Skip to content

feat(orchestrator): add card height mode config for workflow run page#2386

Open
lokanandaprabhu wants to merge 2 commits intoredhat-developer:mainfrom
lokanandaprabhu:feat/orchestrator-card-height-config
Open

feat(orchestrator): add card height mode config for workflow run page#2386
lokanandaprabhu wants to merge 2 commits intoredhat-developer:mainfrom
lokanandaprabhu:feat/orchestrator-card-height-config

Conversation

@lokanandaprabhu
Copy link
Member

@lokanandaprabhu lokanandaprabhu commented Feb 24, 2026

User description

Hey, I just made a Pull Request!

Fixes:

https://issues.redhat.com/browse/RHDHBUGS-2676

Summary

  • add orchestrator.workflowInstancePage.cardHeightMode config (fixed vs content)
  • introduce a hook to read the config and apply height/overflow styles
  • update workflow instance page to respect the selected mode
  • default behavior: if config is missing or invalid, use fixed (current layout)

Screenshot 2026-02-24 at 1 56 01 PM
Screenshot 2026-02-24 at 1 56 19 PM

✔️ Checklist

  • A changeset describing the change and affected packages. (more info)
  • Added or Updated documentation
  • Tests for new functionality and regression tests for bug fixes
  • Screenshots attached (for UI changes)

PR Type

Enhancement


Description

  • Add configurable card height mode for workflow instance page

  • Introduce useWorkflowInstanceCardHeightMode hook to read config

  • Support "fixed" (current layout) and "content" (expandable) modes

  • Restructure layout dynamically based on selected height mode


Diagram Walkthrough

flowchart LR
  Config["Config: cardHeightMode<br/>fixed | content"]
  Hook["useWorkflowInstanceCardHeightMode<br/>Hook"]
  Layout["WorkflowInstancePageContent<br/>Dynamic Layout"]
  Config -- "reads" --> Hook
  Hook -- "applies" --> Layout
Loading

File Walkthrough

Relevant files
Configuration changes
config.d.ts
Add card height mode configuration interface                         

workspaces/orchestrator/plugins/orchestrator-common/config.d.ts

  • Add workflowInstancePage configuration section to orchestrator config
  • Introduce cardHeightMode property with "fixed" or "content" options
  • Include JSDoc comments explaining behavior and default value
+14/-0   
Enhancement
useWorkflowInstanceCardHeightMode.ts
New hook for card height mode configuration                           

workspaces/orchestrator/plugins/orchestrator/src/hooks/useWorkflowInstanceCardHeightMode.ts

  • Create new hook to read
    orchestrator.workflowInstancePage.cardHeightMode config
  • Define WorkflowInstanceCardHeightMode type with "fixed" and "content"
    values
  • Default to "fixed" mode if config is missing or invalid
+27/-0   
WorkflowInstancePageContent.tsx
Dynamic layout based on card height mode                                 

workspaces/orchestrator/plugins/orchestrator/src/components/WorkflowInstancePage/WorkflowInstancePageContent.tsx

  • Import and use useWorkflowInstanceCardHeightMode hook to determine
    layout mode
  • Conditionally apply fixed-height CSS classes based on card height mode
  • Restructure Grid layout: fixed mode uses 2-column layout, content mode
    uses nested columns
  • Maintain all card components (Details, Result, Inputs, Progress) with
    appropriate styling
+115/-51
Documentation
workflow-instance-card-height.md
Changeset entry for card height feature                                   

workspaces/orchestrator/.changeset/workflow-instance-card-height.md

  • Document patch version bump for orchestrator and orchestrator-common
    packages
  • Describe feature: card height mode config for fixed or content-based
    layouts
+6/-0     

Expose a workflow instance page option to switch between fixed card
heights and content-based sizing, with a new hook and changeset entry.

Co-authored-by: Cursor <cursoragent@cursor.com>
@rhdh-gh-app
Copy link

rhdh-gh-app bot commented Feb 24, 2026

Changed Packages

Package Name Package Path Changeset Bump Current Version
@red-hat-developer-hub/backstage-plugin-orchestrator-common workspaces/orchestrator/plugins/orchestrator-common patch v3.5.1
@red-hat-developer-hub/backstage-plugin-orchestrator workspaces/orchestrator/plugins/orchestrator patch v5.5.1

@rhdh-qodo-merge
Copy link

rhdh-qodo-merge bot commented Feb 24, 2026

PR Compliance Guide 🔍

Below is a summary of compliance checks for this PR:

Security Compliance
🟢
No security concerns identified No security vulnerabilities detected by AI analysis. Human verification advised for critical code.
Ticket Compliance
🎫 No ticket provided
  • Create ticket/issue
Codebase Duplication Compliance
useWorkflowInstanceCardHeightMode Component

Description:
This hook repeats an existing pattern of using useApi(configApiRef) +
getOptionalString(...) to derive a typed UI setting with a fallback (as in useImportFlow).
Consider extracting a small shared helper like useOptionalConfigStringWithFallback(key,
fallback) (and optionally a mapping function) to centralize config lookup + defaulting
logic across these hooks.

PR Code:
useWorkflowInstanceCardHeightMode.ts [20-27]

export function useWorkflowInstanceCardHeightMode(): WorkflowInstanceCardHeightMode {
  const config = useApi(configApiRef);
  const value = config.getOptionalString(
    'orchestrator.workflowInstancePage.cardHeightMode',
  );

  return value === 'content' ? 'content' : 'fixed';
}

Codebase Context Code:
redhat-developer/rhdh-plugins/workspaces/bulk-import/plugins/bulk-import/src/hooks/useImportFlow.ts [20-26]

export const useImportFlow = (): ImportFlow => {
 const configApi = useApi(configApiRef);
 const flowValue =
   configApi.getOptionalString('bulkImport.importAPI') ??
   ImportFlow.OpenPullRequests;
 return flowValue as ImportFlow;
};
Custom Compliance
🟢
Generic: Comprehensive Audit Trails

Objective: To create a detailed and reliable record of critical system actions for security analysis
and compliance.

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Meaningful Naming and Self-Documenting Code

Objective: Ensure all identifiers clearly express their purpose and intent, making code
self-documenting

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Robust Error Handling and Edge Case Management

Objective: Ensure comprehensive error handling that provides meaningful context and graceful
degradation

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Error Handling

Objective: To prevent the leakage of sensitive system information through error messages while
providing sufficient detail for internal debugging.

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Logging Practices

Objective: To ensure logs are useful for debugging and auditing without exposing sensitive
information like PII, PHI, or cardholder data.

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Security-First Input Validation and Data Handling

Objective: Ensure all data inputs are validated, sanitized, and handled securely to prevent
vulnerabilities

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

  • Update
Compliance status legend 🟢 - Fully Compliant
🟡 - Partial Compliant
🔴 - Not Compliant
⚪ - Requires Further Human Verification
🏷️ - Compliance label

@rhdh-qodo-merge
Copy link

rhdh-qodo-merge bot commented Feb 24, 2026

PR Code Suggestions ✨

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
High-level
Refactor component to avoid duplication
Suggestion Impact:The commit extracted the duplicated Details/Result/Inputs/Progress JSX into reusable card variables (detailsCard, resultCard, inputsCard, progressCard) and then reused them in both layout branches, reducing duplication; it also renamed useFixedHeights to isFixedHeightMode.

code diff:

-  const useFixedHeights = cardHeightMode !== 'content';
-  const topRowClassName = useFixedHeights ? classes.topRowCard : '';
-  const bottomRowClassName = useFixedHeights ? classes.bottomRowCard : '';
-  const cardOverflowClassName = useFixedHeights ? classes.cardClassName : '';
+  const isFixedHeightMode = cardHeightMode !== 'content';
+  const topRowClassName = isFixedHeightMode ? classes.topRowCard : '';
+  const bottomRowClassName = isFixedHeightMode ? classes.bottomRowCard : '';
+  const cardOverflowClassName = isFixedHeightMode ? classes.cardClassName : '';
 
   const details = useMemo(
     () => mapProcessInstanceToDetails(instance, t),
@@ -167,6 +167,61 @@
     </Link>
   );
 
+  const detailsCard = (
+    <InfoCard
+      title={
+        <div className={classes.titleContainer}>
+          <Typography
+            component="span"
+            variant="h5"
+            className={classes.detailsTitle}
+          >
+            {t('common.details')}
+          </Typography>
+          {viewVariables}
+        </div>
+      }
+      divider={false}
+      className={topRowClassName}
+      cardClassName={cardOverflowClassName}
+    >
+      <WorkflowRunDetails details={details} />
+    </InfoCard>
+  );
+
+  const resultCard = (
+    <WorkflowResult
+      className={topRowClassName}
+      cardClassName={cardOverflowClassName}
+      instance={instance}
+    />
+  );
+
+  const inputsCard = (
+    <WorkflowInputs
+      className={bottomRowClassName}
+      cardClassName={cardOverflowClassName}
+      value={value}
+      loading={loading}
+      responseError={responseError}
+    />
+  );
+
+  const progressCard = (
+    <InfoCard
+      title={t('workflow.progress')}
+      divider={false}
+      className={bottomRowClassName}
+      cardClassName={cardOverflowClassName}
+    >
+      <WorkflowProgress
+        workflowError={instance.error}
+        workflowNodes={instance.nodes}
+        workflowStatus={instance.state}
+      />
+    </InfoCard>
+  );
+
   return (
     <Content noPadding>
       <VariablesDialog
@@ -174,120 +229,39 @@
         onClose={toggleVariablesDialog}
         instanceVariables={instanceVariables}
       />
-      {useFixedHeights ? (
-        <Grid container spacing={2}>
-          <Grid item xs={6}>
-            <InfoCard
-              title={
-                <div className={classes.titleContainer}>
-                  <Typography
-                    component="span"
-                    variant="h5"
-                    className={classes.detailsTitle}
-                  >
-                    {t('common.details')}
-                  </Typography>
-                  {viewVariables}
-                </div>
-              }
-              divider={false}
-              className={topRowClassName}
-              cardClassName={cardOverflowClassName}
-            >
-              <WorkflowRunDetails details={details} />
-            </InfoCard>
-          </Grid>
-
-          <Grid item xs={6}>
-            <WorkflowResult
-              className={topRowClassName}
-              cardClassName={cardOverflowClassName}
-              instance={instance}
-            />
-          </Grid>
-
-          <Grid item xs={6}>
-            <WorkflowInputs
-              className={bottomRowClassName}
-              cardClassName={cardOverflowClassName}
-              value={value}
-              loading={loading}
-              responseError={responseError}
-            />
-          </Grid>
-
-          <Grid item xs={6}>
-            <InfoCard
-              title={t('workflow.progress')}
-              divider={false}
-              className={bottomRowClassName}
-              cardClassName={cardOverflowClassName}
-            >
-              <WorkflowProgress
-                workflowError={instance.error}
-                workflowNodes={instance.nodes}
-                workflowStatus={instance.state}
-              />
-            </InfoCard>
-          </Grid>
-        </Grid>
-      ) : (
-        <Grid container spacing={2}>
-          <Grid item xs={6}>
-            <Grid container spacing={2} direction="column">
-              <Grid item>
-                <InfoCard
-                  title={
-                    <div className={classes.titleContainer}>
-                      <Typography
-                        component="span"
-                        variant="h5"
-                        className={classes.detailsTitle}
-                      >
-                        {t('common.details')}
-                      </Typography>
-                      {viewVariables}
-                    </div>
-                  }
-                  divider={false}
-                >
-                  <WorkflowRunDetails details={details} />
-                </InfoCard>
+      <Grid container spacing={2}>
+        {isFixedHeightMode ? (
+          <>
+            <Grid item xs={6}>
+              {detailsCard}
+            </Grid>
+            <Grid item xs={6}>
+              {resultCard}
+            </Grid>
+            <Grid item xs={6}>
+              {inputsCard}
+            </Grid>
+            <Grid item xs={6}>
+              {progressCard}
+            </Grid>
+          </>
+        ) : (
+          <>
+            <Grid item xs={6}>
+              <Grid container spacing={2} direction="column">
+                <Grid item>{detailsCard}</Grid>
+                <Grid item>{inputsCard}</Grid>
               </Grid>
-              <Grid item>
-                <WorkflowInputs
-                  className={bottomRowClassName}
-                  cardClassName={cardOverflowClassName}
-                  value={value}
-                  loading={loading}
-                  responseError={responseError}
-                />
+            </Grid>
+            <Grid item xs={6}>
+              <Grid container spacing={2} direction="column">
+                <Grid item>{resultCard}</Grid>
+                <Grid item>{progressCard}</Grid>
               </Grid>
             </Grid>
-          </Grid>
-
-          <Grid item xs={6}>
-            <Grid container spacing={2} direction="column">
-              <Grid item>
-                <WorkflowResult
-                  className={topRowClassName}
-                  cardClassName={cardOverflowClassName}
-                  instance={instance}
-                />
-              </Grid>
-              <Grid item>
-                <InfoCard title={t('workflow.progress')} divider={false}>
-                  <WorkflowProgress
-                    workflowError={instance.error}
-                    workflowNodes={instance.nodes}
-                    workflowStatus={instance.state}
-                  />
-                </InfoCard>
-              </Grid>
-            </Grid>
-          </Grid>
-        </Grid>
-      )}
+          </>
+        )}
+      </Grid>

The WorkflowInstancePageContent component duplicates JSX for its two layout
modes. Refactor by defining the card components once and placing them within the
conditional layout to improve maintainability.

Examples:

workspaces/orchestrator/plugins/orchestrator/src/components/WorkflowInstancePage/WorkflowInstancePageContent.tsx [177-290]
      {useFixedHeights ? (
        <Grid container spacing={2}>
          <Grid item xs={6}>
            <InfoCard
              title={
                <div className={classes.titleContainer}>
                  <Typography
                    component="span"
                    variant="h5"
                    className={classes.detailsTitle}

 ... (clipped 104 lines)

Solution Walkthrough:

Before:

const WorkflowInstancePageContent = ({ instance }) => {
  const useFixedHeights = cardHeightMode !== 'content';
  // ...
  return (
    <Content>
      {useFixedHeights ? (
        <Grid container spacing={2}>
          {/* ... JSX for Details, Result, Inputs, Progress cards ... */}
        </Grid>
      ) : (
        <Grid container spacing={2}>
          <Grid item xs={6}>
            <Grid container direction="column">
              {/* ... Duplicated JSX for Details and Inputs cards ... */}
            </Grid>
          </Grid>
          <Grid item xs={6}>
            <Grid container direction="column">
              {/* ... Duplicated JSX for Result and Progress cards ... */}
            </Grid>
          </Grid>
        </Grid>
      )}
    </Content>
  );
};

After:

const WorkflowInstancePageContent = ({ instance }) => {
  const useFixedHeights = cardHeightMode !== 'content';
  
  const detailsCard = <InfoCard>...</InfoCard>;
  const resultCard = <WorkflowResult>...</WorkflowResult>;
  const inputsCard = <WorkflowInputs>...</WorkflowInputs>;
  const progressCard = <InfoCard>...</InfoCard>;

  return (
    <Content>
      <Grid container spacing={2}>
        {useFixedHeights ? (
          <>
            <Grid item xs={6}>{detailsCard}</Grid>
            <Grid item xs={6}>{resultCard}</Grid>
            <Grid item xs={6}>{inputsCard}</Grid>
            <Grid item xs={6}>{progressCard}</Grid>
          </>
        ) : (
          <>
            <Grid item xs={6} container direction="column" spacing={2}><Grid item>{detailsCard}</Grid><Grid item>{inputsCard}</Grid></Grid>
            <Grid item xs={6} container direction="column" spacing={2}><Grid item>{resultCard}</Grid><Grid item>{progressCard}</Grid></Grid>
          </>
        )}
      </Grid>
    </Content>
  );
};
Suggestion importance[1-10]: 8

__

Why: The suggestion correctly identifies significant JSX duplication in WorkflowInstancePageContent.tsx, which harms maintainability and violates the DRY principle, making it a valuable architectural improvement.

Medium
General
Rename boolean to avoid hook prefix
Suggestion Impact:The commit renames `useFixedHeights` to `isFixedHeightMode` and updates all corresponding conditional usages.

code diff:

   const cardHeightMode = useWorkflowInstanceCardHeightMode();
-  const useFixedHeights = cardHeightMode !== 'content';
-  const topRowClassName = useFixedHeights ? classes.topRowCard : '';
-  const bottomRowClassName = useFixedHeights ? classes.bottomRowCard : '';
-  const cardOverflowClassName = useFixedHeights ? classes.cardClassName : '';
+  const isFixedHeightMode = cardHeightMode !== 'content';
+  const topRowClassName = isFixedHeightMode ? classes.topRowCard : '';
+  const bottomRowClassName = isFixedHeightMode ? classes.bottomRowCard : '';
+  const cardOverflowClassName = isFixedHeightMode ? classes.cardClassName : '';

Rename the useFixedHeights boolean variable to isFixedHeightMode to follow
standard naming conventions and avoid confusion with React Hooks.

workspaces/orchestrator/plugins/orchestrator/src/components/WorkflowInstancePage/WorkflowInstancePageContent.tsx [107-110]

-const useFixedHeights = cardHeightMode !== 'content';
-const topRowClassName = useFixedHeights ? classes.topRowCard : '';
-const bottomRowClassName = useFixedHeights ? classes.bottomRowCard : '';
-const cardOverflowClassName = useFixedHeights ? classes.cardClassName : '';
+const isFixedHeightMode = cardHeightMode !== 'content';
+const topRowClassName = isFixedHeightMode ? classes.topRowCard : '';
+const bottomRowClassName = isFixedHeightMode ? classes.bottomRowCard : '';
+const cardOverflowClassName = isFixedHeightMode ? classes.cardClassName : '';

[Suggestion processed]

Suggestion importance[1-10]: 5

__

Why: The suggestion correctly points out that the variable useFixedHeights violates React's Rules of Hooks naming convention, which can be confusing. Renaming it improves code clarity and adherence to best practices.

Low
  • Update

Refactor the workflow instance layout to reuse card components and
rename the height mode flag for clarity, and warn when config values
are unexpected before falling back to fixed mode.

Made-with: Cursor
@lokanandaprabhu lokanandaprabhu requested review from a team and lholmquist as code owners March 11, 2026 07:31
@sonarqubecloud
Copy link

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants