Skip to content

Support merge/col span cells horizontal in grid and hide a column #4050

Open
lassopicasso wants to merge 7 commits intomainfrom
support-merge/colSpan-cells-horizontal-in-grid
Open

Support merge/col span cells horizontal in grid and hide a column #4050
lassopicasso wants to merge 7 commits intomainfrom
support-merge/colSpan-cells-horizontal-in-grid

Conversation

@lassopicasso
Copy link
Contributor

@lassopicasso lassopicasso commented Mar 9, 2026

Description

In this PR we extended GridComponent.tsx to support;

  • - colSpan on both text/label and component cells, so a cell can span multiple columns.
  • - Column-level hidden, so entire columns can be hidden based on expressions.

What is implemented

  1. colSpan support for grid cells
    We added colSpan to IGridColumnProperties in Common.ts
new CG.prop(
        'colSpan',
        new CG.expr(ExprVal.Number)
          .optional()
          .setTitle('Column span')
          .setDescription('Number of columns this cell should span. Defaults to 1 if not set.'),
      ),

This makes it possible to set colSpan directly on the cell: { "text": "Header 1", "colSpan": 2 } or via "gridColumnOptions": { "colSpan": 2 }

colSpan: We merge header columnOptions, per‑cell gridColumnOptions, and any cell‑level colSpan into columnStyleOptions, then evaluate columnStyleOptions.colSpan with useEvalExpression and pass the result as the <th>/<td> colSpan so both text/label and component cells can span multiple columns.

  1. Column-level hidden support
    We added a hidden property to IGridColumnProperties, aligned with ITableColumnProperties.hidden:
new CG.prop(
        'hidden',
        new CG.expr(ExprVal.Boolean)
          .optional()
          .setTitle('Hidden column')
          .setDescription(
            'Expression or boolean indicating whether this column should be hidden. Defaults to false if not set.',
          ),
      ),

This allows header cells to define visibility: "text": "heading2","gridColumnOptions": { "hidden": false }

  • Added also extraction helper in Grid/tools.ts called getGridCellHiddenExpr small helper that It returns the cell’s hidden expression, using gridColumnOptions.hidden if present, otherwise columnOptions.hidden.

NOTE:

  • This will not merge until we add docs (The docs PR will be fixed after review and it will be linked here to merge this PR and the docs one together)

After:

Screen.Recording.2026-03-17.at.12.47.48.mov

Related Issue(s)

Verification/QA

  • Manual functionality testing
    • I have tested these changes manually
    • Creator of the original issue (or service owner) has been contacted for manual testing (or will be contacted when released in alpha)
    • No testing done/necessary
  • Automated tests
    • Unit test(s) have been added/updated
    • Cypress E2E test(s) have been added/updated
    • No automatic tests are needed here (no functional changes/additions)
    • I want someone to help me make some tests
  • UU/WCAG (follow these guidelines until we have our own)
    • I have tested with a screen reader/keyboard navigation/automated wcag validator
    • No testing done/necessary (no DOM/visual changes)
    • I want someone to help me perform accessibility testing
  • User documentation @ altinn-studio-docs
    • Has been added/updated
    • No functionality has been changed/added, so no documentation is needed
    • I will do that later/have created an issue
  • Support in Altinn Studio
    • Issue(s) created for support in Studio
    • This change/feature does not require any changes to Altinn Studio
  • Sprint board
    • The original issue (or this PR itself) has been added to the Team Apps project and to the current sprint board
    • I don't have permissions to do that, please help me out
  • Labels
    • I have added a kind/* and backport* label to this PR for proper release notes grouping
    • I don't have permissions to add labels, please help me out

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 9, 2026

📝 Walkthrough

Walkthrough

This change introduces column-level visibility and span support to the Grid component. A new IGridColumnProperties type with colSpan and hidden properties is added. Grid component types are refactored to use this type via a gridColumnOptions property instead of inheriting from ITableColumnProperties. Implementation adds expression-based evaluation for hidden columns and dynamic colSpan values.

Changes

Cohort / File(s) Summary
Type Definitions
src/codegen/Common.ts
New IGridColumnProperties type with colSpan and hidden properties added. GridComponentRef, GridCellLabelFrom, GridCellText, and GridCell refactored to use gridColumnOptions property instead of inheriting from ITableColumnProperties.
Grid Implementation
src/layout/Grid/GridComponent.tsx
Added expression evaluation for hidden columns and dynamic colSpan. Introduced internal GridColumnOptions type and column filtering logic. Extended cell configuration across text, label, and component cell types to merge gridColumnOptions and apply evaluated colSpan attributes.
Grid Utilities
src/layout/Grid/tools.ts, src/layout/Grid/tools.test.ts
New getGridCellHiddenExpr() helper function safely extracts hidden expressions from grid cells, prioritizing gridColumnOptions.hidden over columnOptions.hidden. Includes unit tests for edge cases and prioritization logic.
Grid Component Tests
src/layout/Grid/GridComponent.test.tsx
Comprehensive test suite covering hidden column logic, invalid boolean handling, colSpan from text cells, and colSpan for component cells. Validates column filtering, header rendering, and colspan attribute application.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ❓ Inconclusive PR description covers main changes (colSpan and hidden support) and testing strategy, but lacks complete verification checklist. No labels, UU/WCAG, or documentation status clearly marked. Clarify which verification items are complete (e.g., confirm unit tests added, mark accessibility testing status, and specify documentation plan with linked PR/issue).
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed Title directly summarizes the main changes: colSpan support for cells and column hiding in grid component.
Linked Issues check ✅ Passed PR implements colSpan support for grid cells [#1187], allowing cells to span multiple columns. Column-level hidden support via gridColumnOptions aligns with feature request but rowSpan not addressed.
Out of Scope Changes check ✅ Passed All changes are focused on colSpan and hidden column support. Test files and helper functions directly support the core features requested in #1187.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch support-merge/colSpan-cells-horizontal-in-grid
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@JamalAlabdullah JamalAlabdullah changed the title Support merge/col span cells horizontal in grid Support merge/col span cells horizontal in grid and hide a column Mar 16, 2026
@JamalAlabdullah JamalAlabdullah added kind/product-feature Pull requests containing new features backport-ignore This PR is a new feature and should not be cherry-picked onto release branches labels Mar 17, 2026
@sonarqubecloud
Copy link

@JamalAlabdullah JamalAlabdullah marked this pull request as ready for review March 17, 2026 11:57
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (3)
src/layout/Grid/tools.ts (1)

109-117: Consider reducing type casting by leveraging existing type guards.

The function uses as type casting which the coding guidelines recommend avoiding. Since all non-null GridCell variants (GridCellText, GridCellLabelFrom, GridComponentRef) have the same optional columnOptions and gridColumnOptions properties, you could use the existing type guards to narrow the type.

♻️ Suggested refactor using type guards
 export function getGridCellHiddenExpr(cell: GridCell) {
   if (!cell || typeof cell !== 'object') {
     return undefined;
   }
-  const options =
-    'columnOptions' in cell ? (cell as { columnOptions?: { hidden?: unknown } }).columnOptions : undefined;
-  const gridOpts =
-    'gridColumnOptions' in cell ? (cell as { gridColumnOptions?: { hidden?: unknown } }).gridColumnOptions : undefined;
-  return gridOpts?.hidden ?? options?.hidden;
+  if (isGridCellText(cell) || isGridCellLabelFrom(cell) || isGridCellNode(cell)) {
+    return cell.gridColumnOptions?.hidden ?? cell.columnOptions?.hidden;
+  }
+  return undefined;
 }

As per coding guidelines: "Avoid using any or type casting (as type) in TypeScript; instead, improve typing by removing such casts and anys to maintain proper type safety."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/layout/Grid/tools.ts` around lines 109 - 117, getGridCellHiddenExpr
currently uses 'as' casts to access columnOptions/gridColumnOptions; instead use
the existing type guards to narrow GridCell (e.g., isGridCellText,
isGridCellLabelFrom, isGridComponentRef or whatever project's guards are named)
and then read cell.columnOptions and cell.gridColumnOptions directly without
casting, returning gridColumnOptions?.hidden ?? columnOptions?.hidden; remove
the two '(cell as {...})' casts and the initial typeof check since the type
guards handle null/undefined checks.
src/layout/Grid/GridComponent.tsx (2)

255-260: Apply the same optional chaining and type safety improvements here.

This section has the same pattern as the text/label cell handling and should be refactored similarly.

♻️ Suggested refactor
-        const cellColSpan = (cell as { colSpan?: number } | null)?.colSpan;
-        if (cellColSpan !== undefined) {
+        const cellColSpan = cell && 'colSpan' in cell ? cell.colSpan : undefined;
+        if (cellColSpan !== undefined) {
           componentCellSettings = componentCellSettings
             ? { ...componentCellSettings, colSpan: cellColSpan }
             : { colSpan: cellColSpan };
         }

As per coding guidelines: "Avoid using any or type casting (as type) in TypeScript."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/layout/Grid/GridComponent.tsx` around lines 255 - 260, The code uses a
type cast and manual null checks for colSpan; change to use optional chaining
and a proper narrow type or type guard instead of "as". Replace the current
block in GridComponent (where cellColSpan and componentCellSettings are set)
with a check using cell?.colSpan (e.g., if (cell?.colSpan !== undefined) {
componentCellSettings = componentCellSettings ? { ...componentCellSettings,
colSpan: cell.colSpan } : { colSpan: cell.colSpan }; }) or introduce a
CellWithColSpan interface and a small type guard function to ensure type-safe
access to colSpan, avoiding any casts and preserving the existing merge logic
for componentCellSettings.

203-208: Use optional chaining and reduce type casting.

The SonarCloud analysis correctly identifies that optional chaining would be cleaner. Additionally, the as type casting could be avoided by using a more type-safe approach.

♻️ Suggested refactor
-          const cellWithColSpan = cell as { colSpan?: number } | null;
-          if (cellWithColSpan && cellWithColSpan.colSpan !== undefined) {
-            textCellSettings = textCellSettings
-              ? { ...textCellSettings, colSpan: cellWithColSpan.colSpan }
-              : { colSpan: cellWithColSpan.colSpan };
-          }
+          const cellColSpan = 'colSpan' in cell ? cell.colSpan : undefined;
+          if (cellColSpan !== undefined) {
+            textCellSettings = textCellSettings
+              ? { ...textCellSettings, colSpan: cellColSpan }
+              : { colSpan: cellColSpan };
+          }

As per coding guidelines: "Avoid using any or type casting (as type) in TypeScript."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/layout/Grid/GridComponent.tsx` around lines 203 - 208, Replace the ad-hoc
type cast and manual null check with a type-safe user-defined type guard and
optional chaining: add a small type guard function (e.g., hasColSpan(obj:
unknown): obj is { colSpan?: number }) that checks obj is non-null object and
has the 'colSpan' property, then use if (hasColSpan(cell) && cell.colSpan !==
undefined) { textCellSettings = textCellSettings ? { ...textCellSettings,
colSpan: cell.colSpan } : { colSpan: cell.colSpan }; } so you remove the "as"
cast and use optional access via the guard when updating textCellSettings in
GridComponent (reference symbols: hasColSpan, textCellSettings, cell).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/layout/Grid/GridComponent.tsx`:
- Around line 255-260: The code uses a type cast and manual null checks for
colSpan; change to use optional chaining and a proper narrow type or type guard
instead of "as". Replace the current block in GridComponent (where cellColSpan
and componentCellSettings are set) with a check using cell?.colSpan (e.g., if
(cell?.colSpan !== undefined) { componentCellSettings = componentCellSettings ?
{ ...componentCellSettings, colSpan: cell.colSpan } : { colSpan: cell.colSpan };
}) or introduce a CellWithColSpan interface and a small type guard function to
ensure type-safe access to colSpan, avoiding any casts and preserving the
existing merge logic for componentCellSettings.
- Around line 203-208: Replace the ad-hoc type cast and manual null check with a
type-safe user-defined type guard and optional chaining: add a small type guard
function (e.g., hasColSpan(obj: unknown): obj is { colSpan?: number }) that
checks obj is non-null object and has the 'colSpan' property, then use if
(hasColSpan(cell) && cell.colSpan !== undefined) { textCellSettings =
textCellSettings ? { ...textCellSettings, colSpan: cell.colSpan } : { colSpan:
cell.colSpan }; } so you remove the "as" cast and use optional access via the
guard when updating textCellSettings in GridComponent (reference symbols:
hasColSpan, textCellSettings, cell).

In `@src/layout/Grid/tools.ts`:
- Around line 109-117: getGridCellHiddenExpr currently uses 'as' casts to access
columnOptions/gridColumnOptions; instead use the existing type guards to narrow
GridCell (e.g., isGridCellText, isGridCellLabelFrom, isGridComponentRef or
whatever project's guards are named) and then read cell.columnOptions and
cell.gridColumnOptions directly without casting, returning
gridColumnOptions?.hidden ?? columnOptions?.hidden; remove the two '(cell as
{...})' casts and the initial typeof check since the type guards handle
null/undefined checks.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 67925669-d359-425d-a239-078444973770

📥 Commits

Reviewing files that changed from the base of the PR and between 5e931c9 and 912170e.

📒 Files selected for processing (5)
  • src/codegen/Common.ts
  • src/layout/Grid/GridComponent.test.tsx
  • src/layout/Grid/GridComponent.tsx
  • src/layout/Grid/tools.test.ts
  • src/layout/Grid/tools.ts

@JamalAlabdullah JamalAlabdullah added the squad/utforming Issues that belongs to the named squad. label Mar 17, 2026
@JamalAlabdullah JamalAlabdullah moved this to 🔎 In review in Team Altinn Studio Mar 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backport-ignore This PR is a new feature and should not be cherry-picked onto release branches kind/product-feature Pull requests containing new features squad/utforming Issues that belongs to the named squad.

Projects

Status: 🔎 In review

Development

Successfully merging this pull request may close these issues.

Grid/rowsBefore/rowsAfter: Støtte for å slå sammen celler (colSpan/rowSpan)

2 participants