Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
37fd2cf
register custom widget for variable type resources
interim17 Feb 19, 2026
5e1c540
use resourceDetails in nested fields on resource markdown
interim17 Feb 19, 2026
75fbc17
query new resources collection
interim17 Feb 19, 2026
9f43b2b
Update src/cms/widgets/VariableResourceWidget/copyResourceNameHandler.ts
interim17 Feb 20, 2026
a345633
Update static/admin/config.yml
interim17 Feb 20, 2026
478c946
reference resourceDetails in relational widget
interim17 Feb 20, 2026
e0fd5b2
Merge branch 'main' of https://github.com/AllenCell/idea-board into f…
interim17 Feb 23, 2026
cc336ee
remove redundant fields from config
interim17 Feb 23, 2026
6653ea5
fix typo
interim17 Feb 23, 2026
eb6b692
run format
interim17 Feb 23, 2026
3e89540
Merge branch 'run-format' of https://github.com/AllenCell/idea-board …
interim17 Feb 23, 2026
211c55a
update comment
interim17 Feb 23, 2026
ee27bd4
Merge branch 'feature/resource' of https://github.com/AllenCell/idea-…
interim17 Feb 24, 2026
00d073e
add resources to schema
interim17 Feb 24, 2026
2bdcb98
add pages for resources
interim17 Feb 24, 2026
879d577
format resource template
interim17 Feb 24, 2026
1a50470
add ResourceDetailsField fragment
interim17 Feb 24, 2026
1e1ee25
build resource pages separately from MarkdownRemark
interim17 Feb 24, 2026
079639a
Merge branch 'main' of https://github.com/AllenCell/idea-board into f…
interim17 Feb 25, 2026
f9283a8
format and lint
interim17 Feb 25, 2026
be3ef5c
Merge branch 'feature/resource' of https://github.com/AllenCell/idea-…
interim17 Feb 25, 2026
971039c
import resources path
interim17 Feb 25, 2026
c12a1aa
add a resources index page and a second dev example tool
interim17 Feb 25, 2026
72038d2
make programs an array and next steps a markdown string
interim17 Mar 9, 2026
fc2b31f
Merge branch 'main' of https://github.com/AllenCell/idea-board into f…
interim17 Mar 9, 2026
f793be6
Merge branch 'fix/next-steps' of https://github.com/AllenCell/idea-bo…
interim17 Mar 9, 2026
89d19e2
fix duplicated fields in dev-example
interim17 Mar 9, 2026
a517a79
flatten resource details when building gatsby nodes
interim17 Mar 10, 2026
666a21a
fix typos
interim17 Mar 10, 2026
137179e
add reporter to resource resolver
interim17 Mar 11, 2026
0c37800
update tests to cover resourceQuery
interim17 Mar 11, 2026
8d88a8d
Update gatsby-node.js
interim17 Mar 11, 2026
8264972
remove tags fields from createPages calls
interim17 Mar 11, 2026
0d4765e
Merge branch 'feature/resource-query' of https://github.com/AllenCell…
interim17 Mar 11, 2026
2b14e68
Update gatsby/utils/gatsby-resolver-utils.js
interim17 Mar 11, 2026
2c25a81
Update gatsby/schema/base.gql
interim17 Mar 11, 2026
2127cba
update comments
interim17 Mar 11, 2026
8411c40
remove extra layout
interim17 Mar 11, 2026
8d3b7b9
Merge branch 'feature/resource-query' of https://github.com/AllenCell…
interim17 Mar 11, 2026
fbe2502
fix authors schema typing
interim17 Mar 11, 2026
23a5c69
Merge branch 'main' of https://github.com/AllenCell/idea-board into f…
interim17 Mar 11, 2026
1a233ec
Merge branch 'main' of https://github.com/AllenCell/idea-board into f…
interim17 Mar 19, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions gatsby-browser.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React from "react";

import { ConfigProvider } from "antd";

import Layout from "./src/components/Layout";
Expand All @@ -8,6 +9,4 @@ export const wrapRootElement = ({ element }) => (
<ConfigProvider theme={theme}>{element}</ConfigProvider>
);

export const wrapPageElement = ({ element }) => (
<Layout>{element}</Layout>
);
export const wrapPageElement = ({ element }) => <Layout>{element}</Layout>;
162 changes: 135 additions & 27 deletions gatsby-node.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,14 @@ const {
resolveToArray,
resolveSlug,
resolveSoftwareTools,
resourceQuery,
} = require("./gatsby/utils/gatsby-resolver-utils");
const { DATASET_PATH } = require("./gatsby/constants");
const {
DATASET_PATH,
RESOURCES_GATSBY_NODE_KEY,
MARKDOWN_REMARK_GATSBY_NODE_KEY,
TEMPLATE_KEY_TO_TYPE,
} = require("./gatsby/constants");
Comment on lines +12 to +17
Copy link
Copy Markdown

@frasercl frasercl Mar 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another optional clarifying question: any rhyme or reason to what gets CommonJS-style vs ESM-style imports? I guess these and the files in gatsby/ are either config or server-side...?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Historical anachronism from gatsby's age and the template this repo was started from.

I think webpack handles src and gatsby-browser.js and Node directly handles the other gatsby configuration files. I think we would need to migrate everything at once and it's just been low on my list, but it is starting to get annoying. Maybe I should do that next. Gatsby supports ESM now, it didn't always.

https://www.gatsbyjs.com/docs/how-to/custom-configuration/es-modules/


const read = (p) => fs.readFileSync(path.join(__dirname, p), "utf8");

Expand All @@ -18,13 +24,10 @@ const read = (p) => fs.readFileSync(path.join(__dirname, p), "utf8");
* They serve as single source of truth, can be added/edited via CMS,
* and are referenced by other markdown files.
*/
const DATA_ONLY_PAGES = [
"software",
"dataset",
"allenite",
"program",
"resource",
];

// TODO: use constants here when this gets revised when we migrate old resources
// to new collection
const DATA_ONLY_PAGES = ["software", "dataset", "allenite", "program"];

exports.createSchemaCustomization = ({ actions }) => {
const { createTypes } = actions;
Expand All @@ -39,9 +42,9 @@ exports.createSchemaCustomization = ({ actions }) => {
software: [SoftwareTool!]!
}

type ProtocolItem {
protocol: String!
}
type ProtocolItem {
protocol: String!
}

type CellLineItem {
name: String!
Expand All @@ -60,13 +63,14 @@ exports.createSchemaCustomization = ({ actions }) => {
caption: String
}

"""
Software tool reference with optional custom description.
"""
type SoftwareTool {
softwareTool: MarkdownRemark @link(by: "fields.slug")
customDescription: String
}`,
"""
Software tool reference with optional custom description.
"""
type SoftwareTool {
softwareTool: MarkdownRemark @link(by: "fields.slug")
customDescription: String
}
`,
];
createTypes(read("gatsby/schema/base.gql"));
createTypes(typeDefs);
Expand All @@ -77,7 +81,7 @@ exports.createSchemaCustomization = ({ actions }) => {
* custom resolution logic. Takes the place of downstream data unpacking
* functions where possible
*/
exports.createResolvers = ({ createResolvers }) => {
exports.createResolvers = ({ reporter, createResolvers }) => {
createResolvers({
MarkdownRemark: {
fields: {
Expand All @@ -101,6 +105,23 @@ exports.createResolvers = ({ createResolvers }) => {
resolve: (source) =>
stringWithDefault(source.title, "No title provided."),
},
resources: {
resolve: async (source, _args, context) => {
const names = resolveToArray(source.resources);
const results = await Promise.all(
names.map((name) =>
context.nodeModel.findOne(resourceQuery(name)),
),
Comment on lines +112 to +114

This comment was marked as outdated.

);
results.forEach((result, i) => {
if (!result) {
const msg = `Resource "${names[i]}" not found for idea "${source.title}". Check for typos and ensure the resource file exists with the correct templateKey.`;
reporter.error(msg, new Error(msg));
}
});
return results.filter(Boolean);
},
},
Comment on lines +108 to +124
Copy link
Copy Markdown
Contributor Author

@interim17 interim17 Mar 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A different way to handle the resolution, using context.nodeModel.findOne, where before we would use a @link directive in the schema. Both are valid ways of doing things. I think I prefer this way because we get a bit more color, as in the example of doing error handling with the reporter.
https://www.gatsbyjs.com/docs/reference/graphql-data-layer/node-model/#findOne

Do we really need error handling here? We have some now.

materialsAndMethods: {
resolve: (source) => {
const raw = source.materialsAndMethods;
Expand Down Expand Up @@ -160,7 +181,46 @@ exports.createResolvers = ({ createResolvers }) => {
exports.createPages = ({ actions, graphql }) => {
const { createPage } = actions;

return graphql(`
// Create pages for any markdown files that are configured to have their
// own node type (e.g. Resource) based on their templateKey.
const typedNodePages = Object.keys(TEMPLATE_KEY_TO_TYPE).map(
(templateKey) => {
const nodeKey = TEMPLATE_KEY_TO_TYPE[templateKey];
const allKeyString = `all${nodeKey}`;
return graphql(`
{
${allKeyString} {
nodes {
id
slug
}
}
}
`).then((result) => {
if (result.errors) {
result.errors.forEach((e) => console.error(e.toString()));
return Promise.reject(result.errors);
}

result.data[allKeyString].nodes.forEach((node) => {
createPage({
path: node.slug,
component: path.resolve(
`src/templates/${templateKey}.tsx`,
),
context: { id: node.id },
});
});
});
},
);

/**
* We make pages from all markdown files that are consumed by gatsby-transformer-remark,
* unless they are specified in DATA_ONLY_PAGES, or TEMPLATE_KEY_TO_TYPE.
* In practice this block makes pages for idea posts and tags.
*/
const markdownPages = graphql(`
{
allMarkdownRemark(limit: 1000) {
edges {
Expand Down Expand Up @@ -191,7 +251,10 @@ exports.createPages = ({ actions, graphql }) => {
const templateKey = edge.node.frontmatter.templateKey;

// Skip creating pages for data-only pages (software, dataset, etc.)
if (DATA_ONLY_PAGES.includes(templateKey)) {
if (
DATA_ONLY_PAGES.includes(templateKey) ||
templateKey in TEMPLATE_KEY_TO_TYPE
) {
return;
}

Expand All @@ -203,7 +266,6 @@ exports.createPages = ({ actions, graphql }) => {

createPage({
path: edge.node.fields.slug,
tags: edge.node.frontmatter.tags,
component: path.resolve(
`src/templates/${String(templateKey)}.tsx`,
),
Expand Down Expand Up @@ -238,17 +300,63 @@ exports.createPages = ({ actions, graphql }) => {
});
});
});

return Promise.all([...typedNodePages, markdownPages]);
};

exports.onCreateNode = ({ node, actions, getNode }) => {
const { createNodeField } = actions;
exports.onCreateNode = ({
node,
actions,
getNode,
createNodeId,
createContentDigest,
}) => {
const { createNodeField, createNode } = actions;

if (node.internal.type === `MarkdownRemark`) {
const value = createFilePath({ node, getNode });
// By default all markdown files are transformed into MarkdownRemark nodes by gatsby-transformer-remark.
// We add a slug field to these nodes.
if (node.internal.type === MARKDOWN_REMARK_GATSBY_NODE_KEY) {
const slug = createFilePath({ node, getNode });
createNodeField({
name: `slug`,
node,
value,
value: slug,
});

// Here me make nodes for any types defined in TEMPLATE_KEY_TO_TYPE
// Once these nodes are in the data layer, we can query them directly by their type (e.g. allResource)
// This type of query is used when mapping over the same TEMPLATE_KEY_TO_TYPE object
// in the createPages.
if (
node.frontmatter?.templateKey &&
Object.keys(TEMPLATE_KEY_TO_TYPE).includes(
node.frontmatter?.templateKey,
)
) {
const nodeType = TEMPLATE_KEY_TO_TYPE[node.frontmatter.templateKey];

let fields = { ...node.frontmatter };

// The structure of our variable type widget leads to a nested field
// that we can flatten out here.
if (nodeType === RESOURCES_GATSBY_NODE_KEY) {
fields = {
...node.frontmatter,
...node.frontmatter.resourceDetails,
};
delete fields.resourceDetails; // avoid duplication in GraphQL node
}
createNode({
...fields,
id: createNodeId(`${node.id}-${nodeType}`),
parent: node.id,
children: [],
slug,
Comment on lines +350 to +354

This comment was marked as resolved.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think how we are handling it is fine because there are not parent child relationships here.

internal: {
type: nodeType,
contentDigest: createContentDigest(node.frontmatter || {}),
},
});
}
}
};
1 change: 0 additions & 1 deletion gatsby-ssr.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

// Gatsby advises implementing the same things in gatsby-ssr.js and
// gatsby-browser.js even for sites that aren't implementing server-side rendering,
// to avoid hydration mismatches.
Expand Down
19 changes: 19 additions & 0 deletions gatsby/constants.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,26 @@
const DATASET_PATH = `dataset`;
const SOFTWARE_PATH = `software`;

const RESOURCES_TEMPLATE_KEY = `resource`;

const RESOURCES_GATSBY_NODE_KEY = `Resource`;
const MARKDOWN_REMARK_GATSBY_NODE_KEY = `MarkdownRemark`;

/**
* Maps frontmatter templateKey → a distinct GraphQL node type.
* gatsby-transformer-remark collapses every .md file into one flat MarkdownRemark
* type regardless of which collection it lives in. Adding entries here causes
* onCreateNode to emit a second, collection-specific node so you can query
* allAllenite, allIdeaPost, etc. with their own inferred schemas.
*/
const TEMPLATE_KEY_TO_TYPE = {
[RESOURCES_TEMPLATE_KEY]: RESOURCES_GATSBY_NODE_KEY,
};
module.exports = {
DATASET_PATH,
SOFTWARE_PATH,
RESOURCES_TEMPLATE_KEY,
MARKDOWN_REMARK_GATSBY_NODE_KEY,
TEMPLATE_KEY_TO_TYPE,
RESOURCES_GATSBY_NODE_KEY,
};
15 changes: 14 additions & 1 deletion gatsby/schema/base.gql
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,18 @@ type Frontmatter {
program: [String!]
publication: String
introduction: String
Comment thread
interim17 marked this conversation as resolved.
preliminaryFindings: PreliminaryFindings!
resources: [Resource!]!
}

type Resource implements Node {
slug: String!
name: String
type: String
description: String
link: String
readmeLink: String
status: String
date: Date @dateformat
tags: [String!]
draft: Boolean
}
22 changes: 21 additions & 1 deletion gatsby/utils/gatsby-resolver-utils.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
const { SOFTWARE_PATH } = require("../constants");
const {
SOFTWARE_PATH,
RESOURCES_TEMPLATE_KEY,
TEMPLATE_KEY_TO_TYPE,
} = require("../constants");
const slugify = require("slugify");

/**
Expand Down Expand Up @@ -71,9 +75,25 @@ const resolveSlug = (id, directory) => {
return `/${directory}/${slugPart}/`;
};

/**
* Builds a nodeModel query for a single Resource node by display name.
* Returns null if the name can't be slugified (falsy input).
* @param {string|null|undefined} name - The resource's name (e.g., "Software Y")
* @returns {{ query: object, type: string } | null}
*/
const resourceQuery = (name) => {
const slug = resolveSlug(name, RESOURCES_TEMPLATE_KEY);
if (!slug) return null;
return {
query: { filter: { slug: { eq: slug } } },
Comment thread
interim17 marked this conversation as resolved.
type: TEMPLATE_KEY_TO_TYPE[RESOURCES_TEMPLATE_KEY], // "Resource"
};
};

module.exports = {
stringWithDefault,
resolveToArray,
resolveSlug,
resolveSoftwareTools,
resourceQuery,
};
Loading
Loading