Skip to content

feat(Table): add indeterminate checkbox state support for select-all header#12411

Merged
thatblindgeye merged 2 commits intopatternfly:mainfrom
rhamilto:feat/table-indeterminate-checkbox
May 8, 2026
Merged

feat(Table): add indeterminate checkbox state support for select-all header#12411
thatblindgeye merged 2 commits intopatternfly:mainfrom
rhamilto:feat/table-indeterminate-checkbox

Conversation

@rhamilto
Copy link
Copy Markdown
Member

@rhamilto rhamilto commented May 5, 2026

Summary

Adds support for indeterminate state to the Table component's select-all checkbox in the header, following PatternFly's bulk selection design guidelines.

The select-all checkbox now properly supports three states:

  • Unchecked - no items selected
  • Indeterminate (dash/minus icon) - some items selected
  • Checked - all items selected

Changes

  • Added isIndeterminate?: boolean property to ThSelectType interface
  • Added isIndeterminate?: boolean to IColumn extraParams type
  • Updated Th component to pass isIndeterminate to the selectable decorator
  • Updated selectable decorator to pass indeterminate state to SelectColumn
  • Updated SelectColumn to use PatternFly Checkbox's native indeterminate support (isChecked: null)
  • Added new TableSelectableIndeterminate example showcasing the feature

Technical implementation

The indeterminate state is implemented using the PatternFly Checkbox component's native support by setting isChecked: null when isIndeterminate is true. This leverages PatternFly's built-in indeterminate checkbox handling.

<Checkbox
  isChecked={isIndeterminate ? null : isSelected}
  // ... other props
/>

Usage example

const areSomeReposSelected = selectedRepoNames.length > 0 && selectedRepoNames.length < selectableRepos.length;

<Th
  select={{
    onSelect: (_event, isSelecting) => selectAllRepos(isSelecting),
    isSelected: areAllReposSelected,
    isIndeterminate: areSomeReposSelected  // NEW: shows dash when some selected
  }}
  aria-label="Row select"
/>

Related issues

Test plan

  • Build passes without TypeScript errors
  • Visual testing of TableSelectableIndeterminate example shows indeterminate state when some rows selected
  • Accessibility: Screen readers properly announce the checkbox state
  • Checkbox toggles correctly between all three states

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Tables support an indeterminate select-all state in the header, showing a dash when only some selectable rows are chosen; row selection honors shift+click range selection.
  • Documentation
    • Added example and docs describing indeterminate header selection behavior and accessibility notes.
  • Tests
    • Added tests verifying header checkbox indeterminate/checked/unchecked states.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 5, 2026

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

This PR adds an optional isIndeterminate prop through table selection types, the selectable decorator, and SelectColumn, enabling the header select-all checkbox to render an indeterminate (dash) state; it also adds an example, docs, and tests.

Changes

Indeterminate Checkbox State Support

Indeterminate header select DAG

Layer / File(s) Summary
Type Shape
packages/react-table/src/components/Table/base/types.tsx, packages/react-table/src/components/Table/TableTypes.tsx, packages/react-table/src/components/Table/SelectColumn.tsx
ThSelectType adds optional isIndeterminate?: boolean. IColumn.extraParams gains isIndeterminate?: boolean. SelectColumnProps declares isIndeterminate?: boolean.
Core Checkbox Rendering
packages/react-table/src/components/Table/SelectColumn.tsx
SelectColumn destructures isIndeterminate and builds checkboxProps from commonProps, setting isChecked: null when isIndeterminate is truthy for the checkbox variant.
Integration & Wiring
packages/react-table/src/components/Table/utils/decorators/selectable.tsx, packages/react-table/src/components/Table/Th.tsx
selectable now reads isIndeterminate from column.extraParams and forwards it to SelectColumn only for header rows (rowId === -1). Th supplies select?.isIndeterminate into column.extraParams.
Example & Documentation
packages/react-table/src/components/Table/examples/TableSelectableIndeterminate.tsx, packages/react-table/src/components/Table/examples/Table.md
New TableSelectableIndeterminate example demonstrates header indeterminate state with selection state and shift+click range selection; docs subsection added describing indeterminate dash/minus behavior.
Tests
packages/react-table/src/components/Table/__tests__/Th.test.tsx
Added tests asserting header checkbox indeterminate and checked properties under different select prop combinations.

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant Th
  participant selectable
  participant SelectColumn
  participant Checkbox
  User->>Th: user toggles selection
  Th->>selectable: pass column.extraParams (isIndeterminate, isSelected, ...)
  selectable->>SelectColumn: props (isIndeterminate only for header)
  SelectColumn->>Checkbox: render checkboxProps (isChecked:null when indeterminate)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested labels

Needs design review

Suggested reviewers

  • thatblindgeye
  • wise-king-sullyman
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the primary feature added: indeterminate checkbox state support for the table's select-all header.
Linked Issues check ✅ Passed The PR fully implements all coding requirements from issue #12404: added isIndeterminate to ThSelectType and IColumn.extraParams, propagated it through Th and selectable, and implemented indeterminate UI in SelectColumn.
Out of Scope Changes check ✅ Passed All changes are directly scoped to implementing indeterminate checkbox support. No unrelated modifications to other components or functionality are present.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


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.

@patternfly-build
Copy link
Copy Markdown
Collaborator

patternfly-build commented May 5, 2026

@rhamilto rhamilto force-pushed the feat/table-indeterminate-checkbox branch from 3ab7def to 1b9740e Compare May 5, 2026 15:05
Copy link
Copy Markdown

@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 (1)
packages/react-table/src/components/Table/SelectColumn.tsx (1)

47-61: 💤 Low value

checkboxProps duplicates all four fields from commonProps — consider eliminating the separate object.

PatternFly's Checkbox supports isChecked?: boolean | null, where null means the checkbox will be indeterminate (partially checked). The override is correct, but checkboxProps replicates all four fields (...props, id, ref, onChange) already present in commonProps. You can eliminate the duplication by building the conditional override directly at the call-site:

♻️ Proposed refactor
-  // PatternFly Checkbox supports indeterminate via isChecked: null
-  const checkboxProps = {
-    ...props,
-    id,
-    ref: inputRef,
-    onChange: handleChange,
-    ...(isIndeterminate && { isChecked: null })
-  };
-
   const commonProps = {
     ...props,
     id,
     ref: inputRef,
     onChange: handleChange
   };
-        <Checkbox {...checkboxProps} />
+        <Checkbox {...commonProps} {...(isIndeterminate && { isChecked: null })} />
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/react-table/src/components/Table/SelectColumn.tsx` around lines 47 -
61, Remove the duplicated object by using commonProps as the base and applying
the indeterminate override only where Checkbox is rendered: delete the separate
checkboxProps declaration and, at the Checkbox render site, spread commonProps
and conditionally include isChecked: null when isIndeterminate is true
(preserving ...props, id, ref: inputRef, onChange: handleChange from
commonProps). Keep the identifier names isIndeterminate, commonProps and
checkbox behavior consistent with PatternFly's Checkbox which accepts
isChecked?: boolean | null.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@packages/react-table/src/components/Table/SelectColumn.tsx`:
- Around line 47-61: Remove the duplicated object by using commonProps as the
base and applying the indeterminate override only where Checkbox is rendered:
delete the separate checkboxProps declaration and, at the Checkbox render site,
spread commonProps and conditionally include isChecked: null when
isIndeterminate is true (preserving ...props, id, ref: inputRef, onChange:
handleChange from commonProps). Keep the identifier names isIndeterminate,
commonProps and checkbox behavior consistent with PatternFly's Checkbox which
accepts isChecked?: boolean | null.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 407847d8-4ecd-4bbc-80dc-1d367e75dd63

📥 Commits

Reviewing files that changed from the base of the PR and between 90f963f and 1b9740e.

📒 Files selected for processing (8)
  • packages/react-table/src/components/Table/SelectColumn.tsx
  • packages/react-table/src/components/Table/TableTypes.tsx
  • packages/react-table/src/components/Table/Th.tsx
  • packages/react-table/src/components/Table/base/types.tsx
  • packages/react-table/src/components/Table/examples/Table.md
  • packages/react-table/src/components/Table/examples/TableSelectable.tsx
  • packages/react-table/src/components/Table/examples/TableSelectableIndeterminate.tsx
  • packages/react-table/src/components/Table/utils/decorators/selectable.tsx
✅ Files skipped from review due to trivial changes (2)
  • packages/react-table/src/components/Table/examples/TableSelectable.tsx
  • packages/react-table/src/components/Table/examples/Table.md
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/react-table/src/components/Table/utils/decorators/selectable.tsx
  • packages/react-table/src/components/Table/Th.tsx

@rhamilto
Copy link
Copy Markdown
Member Author

rhamilto commented May 5, 2026

cc: @nicolethoen, this is needed to complete openshift/console#16203

@rhamilto rhamilto force-pushed the feat/table-indeterminate-checkbox branch from 1b9740e to a99a368 Compare May 5, 2026 20:30
…header

Add support for indeterminate state to the Table component's select-all
checkbox, following PatternFly's bulk selection design guidelines.

The select-all checkbox now supports three states:
- Unchecked: no items selected
- Indeterminate: some items selected (shows dash/minus icon)
- Checked: all items selected

## Changes

- Add `isIndeterminate?: boolean` property to `ThSelectType` interface
- Add `isIndeterminate?: boolean` to `IColumn` extraParams type
- Update `Th` component to pass `isIndeterminate` to the selectable decorator
- Update `selectable` decorator to pass indeterminate state to `SelectColumn`
- Update `SelectColumn` to use PatternFly Checkbox's native indeterminate support (isChecked: null)
- Add new `TableSelectableIndeterminate` example showcasing the feature

## Implementation

The indeterminate state is implemented using the PatternFly Checkbox component's
native support by setting `isChecked: null` when `isIndeterminate` is true.

## Usage

```typescript
const areSomeReposSelected = selectedRepoNames.length > 0 && selectedRepoNames.length < selectableRepos.length;

<Th
  select={{
    onSelect: (_event, isSelecting) => selectAllRepos(isSelecting),
    isSelected: areAllReposSelected,
    isIndeterminate: areSomeReposSelected
  }}
  aria-label="Row select"
/>
```

Fixes patternfly#12404

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
@rhamilto rhamilto force-pushed the feat/table-indeterminate-checkbox branch from a99a368 to 6308f98 Compare May 5, 2026 20:31
@andrew-ronaldson
Copy link
Copy Markdown
Collaborator

This update looks good to me. Not sure why the "a" row in our examples has a disabled check but not a blocker for you.

@rhamilto
Copy link
Copy Markdown
Member Author

rhamilto commented May 7, 2026

@nicolethoen, who else can review?


### Selectable with indeterminate state

This example demonstrates the indeterminate state support for the select-all checkbox. When some (but not all) rows are selected, the header checkbox displays a dash/minus icon to indicate partial selection.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Just to match the tone we typically try to have for example descriptions:

Suggested change
This example demonstrates the indeterminate state support for the select-all checkbox. When some (but not all) rows are selected, the header checkbox displays a dash/minus icon to indicate partial selection.
To indicate partial selection, use `isIndeterminate` on the header's `select` prop. When some (but not all) selectable rows are selected, the header checkbox will convey this information to assistive technologies and also display a dash instead of a checkmark.

Copy link
Copy Markdown
Collaborator

@wise-king-sullyman wise-king-sullyman left a comment

Choose a reason for hiding this comment

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

Thanks for this work! Couple small requests:

Comment thread packages/react-table/src/components/Table/SelectColumn.tsx Outdated
Comment thread packages/react-table/src/components/Table/Th.tsx
- Refactor SelectColumn to reduce duplication by building checkboxProps from commonProps
- Update example description to match PatternFly tone and emphasize accessibility
- Add comprehensive unit tests for isIndeterminate prop in Th component

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
@rhamilto
Copy link
Copy Markdown
Member Author

rhamilto commented May 8, 2026

@wise-king-sullyman, @thatblindgeye, thanks for the review. Comments addressed.

Copy link
Copy Markdown
Collaborator

@wise-king-sullyman wise-king-sullyman left a comment

Choose a reason for hiding this comment

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

🎉

Copy link
Copy Markdown

@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 (1)
packages/react-table/src/components/Table/__tests__/Th.test.tsx (1)

66-71: ⚡ Quick win

Add explicit unchecked assertion in the indeterminate test.

Line 70 verifies mixed state, but adding not.toBeChecked() would lock the intended header semantics more tightly.

Proposed test tweak
 test('Renders checkbox with indeterminate state when isIndeterminate is true', () => {
   render(<Th select={{ onSelect: jest.fn(), isSelected: false, isIndeterminate: true }} aria-label="Select all" />);

   const checkbox = screen.getByRole('checkbox') as HTMLInputElement;
   expect(checkbox.indeterminate).toBe(true);
+  expect(checkbox).not.toBeChecked();
 });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/react-table/src/components/Table/__tests__/Th.test.tsx` around lines
66 - 71, Update the test "Renders checkbox with indeterminate state when
isIndeterminate is true" in Th.test.tsx to also assert the checkbox is not
checked: after getting the checkbox (screen.getByRole('checkbox')) add an
expectation using expect(checkbox).not.toBeChecked() so the Th component's
header semantics (indeterminate but unchecked) are explicitly verified alongside
checkbox.indeterminate for the select prop used in <Th select={{ ...
isIndeterminate: true }} />.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@packages/react-table/src/components/Table/__tests__/Th.test.tsx`:
- Around line 66-71: Update the test "Renders checkbox with indeterminate state
when isIndeterminate is true" in Th.test.tsx to also assert the checkbox is not
checked: after getting the checkbox (screen.getByRole('checkbox')) add an
expectation using expect(checkbox).not.toBeChecked() so the Th component's
header semantics (indeterminate but unchecked) are explicitly verified alongside
checkbox.indeterminate for the select prop used in <Th select={{ ...
isIndeterminate: true }} />.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: b0902b39-4905-41d0-a250-54f914329065

📥 Commits

Reviewing files that changed from the base of the PR and between 6308f98 and a03fbd1.

📒 Files selected for processing (3)
  • packages/react-table/src/components/Table/SelectColumn.tsx
  • packages/react-table/src/components/Table/__tests__/Th.test.tsx
  • packages/react-table/src/components/Table/examples/Table.md
✅ Files skipped from review due to trivial changes (1)
  • packages/react-table/src/components/Table/examples/Table.md

@thatblindgeye thatblindgeye merged commit b1c906d into patternfly:main May 8, 2026
14 checks passed
@rhamilto rhamilto deleted the feat/table-indeterminate-checkbox branch May 8, 2026 17:59
@patternfly-build
Copy link
Copy Markdown
Collaborator

Your changes have been released in:

  • @patternfly/react-docs@7.5.0-prerelease.85
  • @patternfly/react-table@6.5.0-prerelease.77

Thanks for your contribution! 🎉

rhamilto added a commit to rhamilto/console that referenced this pull request May 8, 2026
…eckbox support

Upgrade to @patternfly/react-table@6.5.0-prerelease.77 which includes native
indeterminate checkbox support via PR patternfly/patternfly-react#12411.

Replace custom DOM manipulation hook with inline useEffect that sets checkbox
indeterminate state directly. This avoids React controlled/uncontrolled input
warnings that occur when passing isIndeterminate as a prop.

Changes:
- Upgrade PatternFly packages to v6.5 prerelease with peer dependency resolutions
- Delete useIndeterminateCheckbox custom hook
- Add inline useEffect in ConsoleDataView for indeterminate state via DOM manipulation
- Fix icon-utils to handle both IconDefinition and IconData formats from PF v6.5
- Add Boolean coercion for checkbox isSelected prop to prevent controlled warnings
- Wrap getDataViewRows in useCallback for performance
- Remove @ts-expect-error comments now that NotificationBadge types variant prop
- Document known reselect dev warning from legacy connect() HOC in kinds.ts

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Table: Add indeterminate checkbox state support for select-all header

6 participants