From bc6dfbdf3c1581d7cdd5e3840cb184c19d9a9d20 Mon Sep 17 00:00:00 2001 From: Fran McDade <18710366+frano-m@users.noreply.github.com> Date: Wed, 7 Jan 2026 17:40:31 +1000 Subject: [PATCH 1/4] feat: add differential expression workflow under feature flag (#1054) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Create shared WorkflowAccordion component for workflow display - Add differential expression workflow constants and configuration - Gate DE workflow behind "de" feature flag - Update AnalysisMethodsCatalog to use new shared component 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- .../components/CustomWorkflow/types.ts | 3 --- .../constants.ts | 13 +++++++++++++ .../components/WorkflowAccordion/types.ts | 7 +++++++ .../workflowAccordion.styles.ts} | 0 .../workflowAccordion.tsx} | 13 ++++++++----- .../analysisMethodsCatalog.tsx | 19 +++++++++++++++++-- .../Main/components/Stepper/steps/utils.ts | 6 ++++++ app/services/workflows/loader.ts | 7 +++++++ .../WorkflowInputsView/workflowInputsView.tsx | 6 ++++++ pages/_app.tsx | 3 +++ .../[entityId]/[trsId]/index.tsx | 10 ++++++++++ 11 files changed, 77 insertions(+), 10 deletions(-) delete mode 100644 app/components/Entity/components/AnalysisMethod/components/CustomWorkflow/types.ts create mode 100644 app/components/Entity/components/AnalysisMethod/components/DifferentialExpressionWorkflow/constants.ts create mode 100644 app/components/Entity/components/AnalysisMethod/components/WorkflowAccordion/types.ts rename app/components/Entity/components/AnalysisMethod/components/{CustomWorkflow/customWorkflow.styles.ts => WorkflowAccordion/workflowAccordion.styles.ts} (100%) rename app/components/Entity/components/AnalysisMethod/components/{CustomWorkflow/customWorkflow.tsx => WorkflowAccordion/workflowAccordion.tsx} (86%) diff --git a/app/components/Entity/components/AnalysisMethod/components/CustomWorkflow/types.ts b/app/components/Entity/components/AnalysisMethod/components/CustomWorkflow/types.ts deleted file mode 100644 index c69c3a20f..000000000 --- a/app/components/Entity/components/AnalysisMethod/components/CustomWorkflow/types.ts +++ /dev/null @@ -1,3 +0,0 @@ -export interface Props { - entityId: string; -} diff --git a/app/components/Entity/components/AnalysisMethod/components/DifferentialExpressionWorkflow/constants.ts b/app/components/Entity/components/AnalysisMethod/components/DifferentialExpressionWorkflow/constants.ts new file mode 100644 index 000000000..83d12427b --- /dev/null +++ b/app/components/Entity/components/AnalysisMethod/components/DifferentialExpressionWorkflow/constants.ts @@ -0,0 +1,13 @@ +import { WORKFLOW_PLOIDY } from "../../../../../../apis/catalog/brc-analytics-catalog/common/schema-entities"; +import { Workflow } from "../../../../../../apis/catalog/brc-analytics-catalog/common/entities"; + +export const DIFFERENTIAL_EXPRESSION_WORKFLOW: Workflow = { + iwcId: "", + parameters: [], + ploidy: WORKFLOW_PLOIDY.ANY, + taxonomyId: null, + trsId: "differential-expression-workflow", + workflowDescription: + "Analyze differential gene expression from raw reads to DE results.", + workflowName: "Differential Expression Workflow", +}; diff --git a/app/components/Entity/components/AnalysisMethod/components/WorkflowAccordion/types.ts b/app/components/Entity/components/AnalysisMethod/components/WorkflowAccordion/types.ts new file mode 100644 index 000000000..f34ce9d22 --- /dev/null +++ b/app/components/Entity/components/AnalysisMethod/components/WorkflowAccordion/types.ts @@ -0,0 +1,7 @@ +import { Workflow } from "../../../../../../apis/catalog/brc-analytics-catalog/common/entities"; + +export interface Props { + buttonText?: string; + entityId: string; + workflow: Workflow; +} diff --git a/app/components/Entity/components/AnalysisMethod/components/CustomWorkflow/customWorkflow.styles.ts b/app/components/Entity/components/AnalysisMethod/components/WorkflowAccordion/workflowAccordion.styles.ts similarity index 100% rename from app/components/Entity/components/AnalysisMethod/components/CustomWorkflow/customWorkflow.styles.ts rename to app/components/Entity/components/AnalysisMethod/components/WorkflowAccordion/workflowAccordion.styles.ts diff --git a/app/components/Entity/components/AnalysisMethod/components/CustomWorkflow/customWorkflow.tsx b/app/components/Entity/components/AnalysisMethod/components/WorkflowAccordion/workflowAccordion.tsx similarity index 86% rename from app/components/Entity/components/AnalysisMethod/components/CustomWorkflow/customWorkflow.tsx rename to app/components/Entity/components/AnalysisMethod/components/WorkflowAccordion/workflowAccordion.tsx index 28757d07b..1a265c912 100644 --- a/app/components/Entity/components/AnalysisMethod/components/CustomWorkflow/customWorkflow.tsx +++ b/app/components/Entity/components/AnalysisMethod/components/WorkflowAccordion/workflowAccordion.tsx @@ -17,12 +17,15 @@ import { ROUTES } from "../../../../../../../routes/constants"; import { replaceParameters } from "@databiosphere/findable-ui/lib/utils/replaceParameters"; import { TYPOGRAPHY_PROPS } from "@databiosphere/findable-ui/lib/styles/common/mui/typography"; import { FluidPaper } from "@databiosphere/findable-ui/lib/components/common/Paper/components/FluidPaper/fluidPaper"; -import { CUSTOM_WORKFLOW } from "./constants"; -import { StyledAccordion } from "./customWorkflow.styles"; +import { StyledAccordion } from "./workflowAccordion.styles"; import { ChevronRightRounded } from "@mui/icons-material"; -export const CustomWorkflow = ({ entityId }: Props): JSX.Element => { - const { trsId, workflowDescription, workflowName } = CUSTOM_WORKFLOW; +export const WorkflowAccordion = ({ + buttonText = "Select Data", + entityId, + workflow, +}: Props): JSX.Element => { + const { trsId, workflowDescription, workflowName } = workflow; return ( { })} rel={REL_ATTRIBUTE.NO_OPENER} > - Select Data + {buttonText} diff --git a/app/components/Entity/components/AnalysisMethodsCatalog/analysisMethodsCatalog.tsx b/app/components/Entity/components/AnalysisMethodsCatalog/analysisMethodsCatalog.tsx index ed427807b..c5ec1ae46 100644 --- a/app/components/Entity/components/AnalysisMethodsCatalog/analysisMethodsCatalog.tsx +++ b/app/components/Entity/components/AnalysisMethodsCatalog/analysisMethodsCatalog.tsx @@ -2,11 +2,14 @@ import { AnalysisMethod } from "../AnalysisMethod/analysisMethod"; import { Props } from "./types"; import { useRouter } from "next/router"; import { Fragment } from "react"; -import { CustomWorkflow } from "../AnalysisMethod/components/CustomWorkflow/customWorkflow"; +import { WorkflowAccordion } from "../AnalysisMethod/components/WorkflowAccordion/workflowAccordion"; +import { CUSTOM_WORKFLOW } from "../AnalysisMethod/components/CustomWorkflow/constants"; +import { DIFFERENTIAL_EXPRESSION_WORKFLOW } from "../AnalysisMethod/components/DifferentialExpressionWorkflow/constants"; import { AnalysisTypeHeader } from "./components/AnalysisTypeHeader/analysisTypeHeader"; import { Stack } from "@mui/material"; import { buildAssemblyWorkflows } from "./utils"; import WORKFLOW_CATEGORIES from "../../../../../catalog/output/workflows.json"; +import { useFeatureFlag } from "@databiosphere/findable-ui/lib/hooks/useFeatureFlag/useFeatureFlag"; export const AnalysisMethodsCatalog = ({ assembly }: Props): JSX.Element => { const workflowCategories = buildAssemblyWorkflows( @@ -18,6 +21,8 @@ export const AnalysisMethodsCatalog = ({ assembly }: Props): JSX.Element => { query: { entityId }, } = useRouter(); + const isDEEnabled = useFeatureFlag("de"); + return ( {/* Custom Analysis */} @@ -26,7 +31,10 @@ export const AnalysisMethodsCatalog = ({ assembly }: Props): JSX.Element => { description="Prefer to explore the data without a predefined workflow?" title="Custom Analysis" /> - + {/* Workflow Analysis */} @@ -49,6 +57,13 @@ export const AnalysisMethodsCatalog = ({ assembly }: Props): JSX.Element => { } title="Select a Workflow" /> + {isDEEnabled && ( + + )} {workflowCategories.map((workflowCategory) => { return ( param.variable) diff --git a/app/services/workflows/loader.ts b/app/services/workflows/loader.ts index 4df537827..0d64840b2 100644 --- a/app/services/workflows/loader.ts +++ b/app/services/workflows/loader.ts @@ -8,6 +8,7 @@ import { } from "../../apis/catalog/brc-analytics-catalog/common/entities"; import { formatTrsId } from "../../components/Entity/components/AnalysisMethodsCatalog/utils"; import { CUSTOM_WORKFLOW } from "../../components/Entity/components/AnalysisMethod/components/CustomWorkflow/constants"; +import { DIFFERENTIAL_EXPRESSION_WORKFLOW } from "../../components/Entity/components/AnalysisMethod/components/DifferentialExpressionWorkflow/constants"; /** * Fetches entities from the API. @@ -81,6 +82,12 @@ export async function loadWorkflows(): Promise { // Add custom workflow. workflowById.set(CUSTOM_WORKFLOW.trsId, CUSTOM_WORKFLOW); + // Add differential expression workflow. + workflowById.set( + DIFFERENTIAL_EXPRESSION_WORKFLOW.trsId, + DIFFERENTIAL_EXPRESSION_WORKFLOW + ); + setEntitiesById("workflows", workflowById); setEntitiesByType("workflows", workflowCategories); } diff --git a/app/views/WorkflowInputsView/workflowInputsView.tsx b/app/views/WorkflowInputsView/workflowInputsView.tsx index f680f4523..2f54a9d8d 100644 --- a/app/views/WorkflowInputsView/workflowInputsView.tsx +++ b/app/views/WorkflowInputsView/workflowInputsView.tsx @@ -9,6 +9,8 @@ import { augmentConfiguredSteps } from "../../components/Entity/components/Confi import { SEQUENCING_STEPS } from "../../components/Entity/components/ConfigureWorkflowInputs/components/Main/components/Stepper/steps/constants"; import { getAssembly } from "../../services/workflows/entities"; import { getWorkflow } from "../../services/workflows/entities"; +import { useFeatureFlag } from "@databiosphere/findable-ui/lib/hooks/useFeatureFlag/useFeatureFlag"; +import Error from "next/error"; export const WorkflowInputsView = ({ entityId, trsId }: Props): JSX.Element => { const genome = getAssembly(entityId); @@ -17,6 +19,10 @@ export const WorkflowInputsView = ({ entityId, trsId }: Props): JSX.Element => { const { configuredInput, onConfigure } = useConfigureInputs(); const { configuredSteps } = useConfiguredSteps(workflow); + const isDEEnabled = useFeatureFlag("de"); + + if (!isDEEnabled) return ; + return ( { // Format the trsId for URL use From 26fe2985477ae774764cb07248c51996348dbe29 Mon Sep 17 00:00:00 2001 From: Fran McDade <18710366+frano-m@users.noreply.github.com> Date: Wed, 7 Jan 2026 17:51:30 +1000 Subject: [PATCH 2/4] refactor: diff exp workflow to analysis (#1054) --- .../constants.ts | 6 +++--- .../AnalysisMethodsCatalog/analysisMethodsCatalog.tsx | 4 ++-- .../components/Main/components/Stepper/steps/utils.ts | 6 +++--- app/services/workflows/loader.ts | 8 ++++---- pages/data/[entityListType]/[entityId]/[trsId]/index.tsx | 6 +++--- 5 files changed, 15 insertions(+), 15 deletions(-) rename app/components/Entity/components/AnalysisMethod/components/{DifferentialExpressionWorkflow => DifferentialExpressionAnalysis}/constants.ts (71%) diff --git a/app/components/Entity/components/AnalysisMethod/components/DifferentialExpressionWorkflow/constants.ts b/app/components/Entity/components/AnalysisMethod/components/DifferentialExpressionAnalysis/constants.ts similarity index 71% rename from app/components/Entity/components/AnalysisMethod/components/DifferentialExpressionWorkflow/constants.ts rename to app/components/Entity/components/AnalysisMethod/components/DifferentialExpressionAnalysis/constants.ts index 83d12427b..5527c803e 100644 --- a/app/components/Entity/components/AnalysisMethod/components/DifferentialExpressionWorkflow/constants.ts +++ b/app/components/Entity/components/AnalysisMethod/components/DifferentialExpressionAnalysis/constants.ts @@ -1,13 +1,13 @@ import { WORKFLOW_PLOIDY } from "../../../../../../apis/catalog/brc-analytics-catalog/common/schema-entities"; import { Workflow } from "../../../../../../apis/catalog/brc-analytics-catalog/common/entities"; -export const DIFFERENTIAL_EXPRESSION_WORKFLOW: Workflow = { +export const DIFFERENTIAL_EXPRESSION_ANALYSIS: Workflow = { iwcId: "", parameters: [], ploidy: WORKFLOW_PLOIDY.ANY, taxonomyId: null, - trsId: "differential-expression-workflow", + trsId: "differential-expression-analysis", workflowDescription: "Analyze differential gene expression from raw reads to DE results.", - workflowName: "Differential Expression Workflow", + workflowName: "Differential Expression Analysis", }; diff --git a/app/components/Entity/components/AnalysisMethodsCatalog/analysisMethodsCatalog.tsx b/app/components/Entity/components/AnalysisMethodsCatalog/analysisMethodsCatalog.tsx index c5ec1ae46..a58ae2ac9 100644 --- a/app/components/Entity/components/AnalysisMethodsCatalog/analysisMethodsCatalog.tsx +++ b/app/components/Entity/components/AnalysisMethodsCatalog/analysisMethodsCatalog.tsx @@ -4,7 +4,7 @@ import { useRouter } from "next/router"; import { Fragment } from "react"; import { WorkflowAccordion } from "../AnalysisMethod/components/WorkflowAccordion/workflowAccordion"; import { CUSTOM_WORKFLOW } from "../AnalysisMethod/components/CustomWorkflow/constants"; -import { DIFFERENTIAL_EXPRESSION_WORKFLOW } from "../AnalysisMethod/components/DifferentialExpressionWorkflow/constants"; +import { DIFFERENTIAL_EXPRESSION_ANALYSIS } from "../AnalysisMethod/components/DifferentialExpressionAnalysis/constants"; import { AnalysisTypeHeader } from "./components/AnalysisTypeHeader/analysisTypeHeader"; import { Stack } from "@mui/material"; import { buildAssemblyWorkflows } from "./utils"; @@ -61,7 +61,7 @@ export const AnalysisMethodsCatalog = ({ assembly }: Props): JSX.Element => { )} {workflowCategories.map((workflowCategory) => { diff --git a/app/components/Entity/components/ConfigureWorkflowInputs/components/Main/components/Stepper/steps/utils.ts b/app/components/Entity/components/ConfigureWorkflowInputs/components/Main/components/Stepper/steps/utils.ts index 3d756e366..906ed473d 100644 --- a/app/components/Entity/components/ConfigureWorkflowInputs/components/Main/components/Stepper/steps/utils.ts +++ b/app/components/Entity/components/ConfigureWorkflowInputs/components/Main/components/Stepper/steps/utils.ts @@ -4,7 +4,7 @@ import { StepConfig } from "../components/Step/types"; import { STEP } from "./constants"; import { CUSTOM_WORKFLOW } from "../../../../../../../../../components/Entity/components/AnalysisMethod/components/CustomWorkflow/constants"; import { ConfiguredInput } from "../../../../../../../../../views/WorkflowInputsView/hooks/UseConfigureInputs/types"; -import { DIFFERENTIAL_EXPRESSION_WORKFLOW } from "../../../../../../../../../components/Entity/components/AnalysisMethod/components/DifferentialExpressionWorkflow/constants"; +import { DIFFERENTIAL_EXPRESSION_ANALYSIS } from "../../../../../../AnalysisMethod/components/DifferentialExpressionAnalysis/constants"; /** * Augment the configured steps with two additional sequencing steps "READ_RUNS_PAIRED" and "READ_RUNS_SINGLE". @@ -43,8 +43,8 @@ export function buildSteps(workflow: Workflow): StepConfig[] { ); } - // Return steps for differential expression workflow - if (workflow.trsId === DIFFERENTIAL_EXPRESSION_WORKFLOW.trsId) { + // Return steps for differential expression analysis + if (workflow.trsId === DIFFERENTIAL_EXPRESSION_ANALYSIS.trsId) { return [STEP.ASSEMBLY_ID, STEP.GENE_MODEL_URL].filter(isStepConfigured); } diff --git a/app/services/workflows/loader.ts b/app/services/workflows/loader.ts index 0d64840b2..8d11eb2b6 100644 --- a/app/services/workflows/loader.ts +++ b/app/services/workflows/loader.ts @@ -8,7 +8,7 @@ import { } from "../../apis/catalog/brc-analytics-catalog/common/entities"; import { formatTrsId } from "../../components/Entity/components/AnalysisMethodsCatalog/utils"; import { CUSTOM_WORKFLOW } from "../../components/Entity/components/AnalysisMethod/components/CustomWorkflow/constants"; -import { DIFFERENTIAL_EXPRESSION_WORKFLOW } from "../../components/Entity/components/AnalysisMethod/components/DifferentialExpressionWorkflow/constants"; +import { DIFFERENTIAL_EXPRESSION_ANALYSIS } from "../../components/Entity/components/AnalysisMethod/components/DifferentialExpressionAnalysis/constants"; /** * Fetches entities from the API. @@ -82,10 +82,10 @@ export async function loadWorkflows(): Promise { // Add custom workflow. workflowById.set(CUSTOM_WORKFLOW.trsId, CUSTOM_WORKFLOW); - // Add differential expression workflow. + // Add differential expression analysis. workflowById.set( - DIFFERENTIAL_EXPRESSION_WORKFLOW.trsId, - DIFFERENTIAL_EXPRESSION_WORKFLOW + DIFFERENTIAL_EXPRESSION_ANALYSIS.trsId, + DIFFERENTIAL_EXPRESSION_ANALYSIS ); setEntitiesById("workflows", workflowById); diff --git a/pages/data/[entityListType]/[entityId]/[trsId]/index.tsx b/pages/data/[entityListType]/[entityId]/[trsId]/index.tsx index 205d2808a..d17368250 100644 --- a/pages/data/[entityListType]/[entityId]/[trsId]/index.tsx +++ b/pages/data/[entityListType]/[entityId]/[trsId]/index.tsx @@ -20,7 +20,7 @@ import { import { WorkflowInputsView } from "../../../../../app/views/WorkflowInputsView/workflowInputsView"; import { GA2AssemblyEntity } from "../../../../../app/apis/catalog/ga2/entities"; import { CUSTOM_WORKFLOW } from "../../../../../app/components/Entity/components/AnalysisMethod/components/CustomWorkflow/constants"; -import { DIFFERENTIAL_EXPRESSION_WORKFLOW } from "../../../../../app/components/Entity/components/AnalysisMethod/components/DifferentialExpressionWorkflow/constants"; +import { DIFFERENTIAL_EXPRESSION_ANALYSIS } from "../../../../../app/components/Entity/components/AnalysisMethod/components/DifferentialExpressionAnalysis/constants"; interface StaticPath { params: PageUrlParams; @@ -111,12 +111,12 @@ function processEntityPaths( }, }); - // Create differential expression workflow path. + // Create differential expression analysis path. paths.push({ params: { entityId, entityListType, - trsId: DIFFERENTIAL_EXPRESSION_WORKFLOW.trsId, + trsId: DIFFERENTIAL_EXPRESSION_ANALYSIS.trsId, }, }); From 432d9fce6c3b51f4c23b0435ede604529b357d9b Mon Sep 17 00:00:00 2001 From: Fran McDade <18710366+frano-m@users.noreply.github.com> Date: Wed, 7 Jan 2026 17:54:21 +1000 Subject: [PATCH 3/4] fix: render workflow under flag conditions (#1054) --- app/views/WorkflowInputsView/workflowInputsView.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/views/WorkflowInputsView/workflowInputsView.tsx b/app/views/WorkflowInputsView/workflowInputsView.tsx index 2f54a9d8d..b40c7cc88 100644 --- a/app/views/WorkflowInputsView/workflowInputsView.tsx +++ b/app/views/WorkflowInputsView/workflowInputsView.tsx @@ -11,6 +11,7 @@ import { getAssembly } from "../../services/workflows/entities"; import { getWorkflow } from "../../services/workflows/entities"; import { useFeatureFlag } from "@databiosphere/findable-ui/lib/hooks/useFeatureFlag/useFeatureFlag"; import Error from "next/error"; +import { DIFFERENTIAL_EXPRESSION_ANALYSIS } from "app/components/Entity/components/AnalysisMethod/components/DifferentialExpressionAnalysis/constants"; export const WorkflowInputsView = ({ entityId, trsId }: Props): JSX.Element => { const genome = getAssembly(entityId); @@ -21,7 +22,8 @@ export const WorkflowInputsView = ({ entityId, trsId }: Props): JSX.Element => { const isDEEnabled = useFeatureFlag("de"); - if (!isDEEnabled) return ; + if (!isDEEnabled && workflow.trsId === DIFFERENTIAL_EXPRESSION_ANALYSIS.trsId) + return ; return ( Date: Wed, 7 Jan 2026 17:59:50 +1000 Subject: [PATCH 4/4] Update app/views/WorkflowInputsView/workflowInputsView.tsx Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- app/views/WorkflowInputsView/workflowInputsView.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/WorkflowInputsView/workflowInputsView.tsx b/app/views/WorkflowInputsView/workflowInputsView.tsx index b40c7cc88..8346f8f7e 100644 --- a/app/views/WorkflowInputsView/workflowInputsView.tsx +++ b/app/views/WorkflowInputsView/workflowInputsView.tsx @@ -11,7 +11,7 @@ import { getAssembly } from "../../services/workflows/entities"; import { getWorkflow } from "../../services/workflows/entities"; import { useFeatureFlag } from "@databiosphere/findable-ui/lib/hooks/useFeatureFlag/useFeatureFlag"; import Error from "next/error"; -import { DIFFERENTIAL_EXPRESSION_ANALYSIS } from "app/components/Entity/components/AnalysisMethod/components/DifferentialExpressionAnalysis/constants"; +import { DIFFERENTIAL_EXPRESSION_ANALYSIS } from "../../components/Entity/components/AnalysisMethod/components/DifferentialExpressionAnalysis/constants"; export const WorkflowInputsView = ({ entityId, trsId }: Props): JSX.Element => { const genome = getAssembly(entityId);