Skip to content

feat(table): new TEDI-Ready component #122#624

Open
airikej wants to merge 36 commits into
rcfrom
feat/122-table-new-tedi-ready-component
Open

feat(table): new TEDI-Ready component #122#624
airikej wants to merge 36 commits into
rcfrom
feat/122-table-new-tedi-ready-component

Conversation

@airikej
Copy link
Copy Markdown
Contributor

@airikej airikej commented May 12, 2026

Summary by CodeRabbit

  • New Features

    • Added a full-featured Table (sorting, filtering, pagination, selection, expandable rows), plus toolbar, columns menu, header button, and persistence hook.
    • Collapse: supports externally-controlled mode via external aria-controls.
    • Pagination: mobile page-jump selector.
  • Enhancements

    • New table and pagination responsive styles, sticky header/column options, accessibility labels and live-region page status.
    • Storybook metadata updated to reflect component status.
  • Tests

    • Expanded test coverage for Table, Pagination, Collapse, and header-button behaviors.

Review Change Stack

@airikej airikej linked an issue May 12, 2026 that may be closed by this pull request
21 tasks
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 12, 2026

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

Walkthrough

Adds a TanStack-based generic Table with persistence, subcomponents, styles, tests, and Storybook stories; refactors Pagination for mobile (types, styling, breakpoint-aware rendering, and aria-live status); and includes supporting updates (Collapse external control, select styling, labels, and index re-export).

Changes

Table Component Implementation

Layer / File(s) Summary
Table types, context, and barrel export
src/tedi/components/content/table/table.tsx, src/tedi/components/content/table/table-context.tsx, src/tedi/components/content/table/index.ts
Defines Table public types and TableContextValue, provides TableContext + useTableContext, and adds barrel re-export.
Table state persistence hook
src/tedi/components/content/table/use-table-persistence.ts
useTablePersistence hydrates and persists allowlisted table state slices, overlays controlled state, and exposes merged state + setState (TableStatePatch).
Toolbar, Header Button, Columns Menu & tests
src/tedi/components/content/table/table-toolbar/*, src/tedi/components/content/table/table-header-button/*, src/tedi/components/content/table/table-columns-menu/*
Implements TableToolbar, TableHeaderButton (forwardRef icon-only button with selected/disabled states and tests), and TableColumnsMenu driven by useTableContext, rendering hideable columns with disable-last-visible protection.
Table component core implementation
src/tedi/components/content/table/table.tsx
TableBase<TData> wires useTablePersistence, configures TanStack Table (selection/expansion columns, manual modes), renders grouped headers, optional filter row, empty-state (optional aria-live), clickable/keyboard-activatable rows, expandable sub-rows, footers, and pagination; exports Table with attached subcomponents.
Table styling & variants
src/tedi/components/content/table/table.module.scss
Comprehensive SCSS for layout, scroll wrapper, toolbar, table head/body/footer cells, alignment/valign modifiers, variants (--small, --striped, --vertical-borders, --borderless), sticky header/first-column, and pagination integration.
Table tests & coverage
src/tedi/components/content/table/table.spec.tsx
Jest/RTL coverage for rendering, accessibility, ColumnsMenu, persistence, clickable/selectable/expandable rows, filtering, tfoot conditional rendering, pagination, server-side mode, sorting, column ordering, keyboard edge-cases, and grouped headers.
Table Storybook stories & demos
src/tedi/components/content/table/table.stories.tsx
25+ stories demonstrating editable rows, sizes, merged headers, borders, truncation, actions/custom cells, sorting, filters, collapsible/selectable/clickable rows, sticky variants, footers, columns menu toolbar, draggable rows/columns, and a ServerSide simulation.
Table docs/reference
skills/tedi-react/references/components.md
Adds a TEDI-Ready Table reference section documenting API usage and accessibility guidance.

Pagination Component Enhancements

Layer / File(s) Summary
Pagination types & labels
src/tedi/components/navigation/pagination/pagination.types.ts, src/tedi/providers/label-provider/labels-map.ts
New pagination types and a pagination.page-status label used for aria-live status text.
Pagination responsive styling
src/tedi/components/navigation/pagination/pagination.module.scss
SCSS updates for md-down: collapse main layout, center slot-start, switch nav to 3-column grid, add .tedi-pagination__page-jump and select variant, and adjust mobile button styles and animations.
Pagination component behavior
src/tedi/components/navigation/pagination/pagination.tsx
Adds useBreakpoint/isBreakpointBelow to compute isMobile, hides page-size select on mobile, introduces mobile-only page-jump Select, precomputes previous/next/page items, and uses pageStatus for aria-live text.
Pagination tests
src/tedi/components/navigation/pagination/pagination.spec.tsx
Adds breakpoint mocking, updates DOM queries for page-size selector, adds aria-live current-page test, and mobile layout tests asserting page-jump Select behavior and hidden page-size on mobile.

Supporting Component Updates

Layer / File(s) Summary
Collapse externally-controlled mode
src/tedi/components/buttons/collapse/collapse.tsx, src/tedi/components/buttons/collapse/collapse.spec.tsx
Adds optional controlsId?: string to CollapseProps; when provided, aria-controls targets the external id and internal panel rendering is suppressed; adds keyboard tests for Enter/Space, repeat suppression, and non-triggering keys.
Select small fix, community story metadata, index export, labels
src/tedi/components/form/select/select.module.scss, src/community/components/table/table.stories.tsx, src/tedi/index.ts, src/tedi/providers/label-provider/labels-map.ts
Removes breakpoint-based small-select styling in favor of direct small values; marks community table stories with parameters.status.type: ['deprecated','ExistsInTediReady']; re-exports table barrel from package entrypoint; adds table-related labels and pagination.page-status.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • mart-sessman
  • ly-tempel-bitweb
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 36.36% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The pull request title clearly summarizes the main change: introducing a new Table component to TEDI-Ready. The title is concise, specific, and directly reflects the primary objective of the changeset.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/122-table-new-tedi-ready-component

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.

@codecov
Copy link
Copy Markdown

codecov Bot commented May 12, 2026

Codecov Report

❌ Patch coverage is 98.58357% with 5 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/tedi/components/content/table/table.tsx 98.24% 3 Missing and 1 partial ⚠️
src/tedi/components/buttons/collapse/collapse.tsx 83.33% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

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.

Actionable comments posted: 2

🧹 Nitpick comments (3)
src/tedi/components/content/table/table.stories.tsx (3)

1868-1874: ⚡ Quick win

Drop the CSS var() fallbacks in this style block.

TEDI-ready styles intentionally omit fallback values, but this block reintroduces them for borderRadius and fontFamily. Use the tokens directly so the story stays aligned with the rest of the library. As per coding guidelines, "never use fallback values in CSS var()".

Suggested cleanup
-            borderRadius: 'var(--tedi-borders-radius-default, 4px)',
-            fontFamily: 'var(--family-mono, monospace)',
+            borderRadius: 'var(--tedi-borders-radius-default)',
+            fontFamily: 'var(--family-mono)',
🤖 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 `@src/tedi/components/content/table/table.stories.tsx` around lines 1868 -
1874, The inline style object in the story uses CSS var() fallbacks for
borderRadius and fontFamily (keys: borderRadius and fontFamily inside the style
prop in table.stories.tsx); remove the fallback values so the tokens are used
directly (e.g., change 'var(--tedi-borders-radius-default, 4px)' to
'var(--tedi-borders-radius-default)' and 'var(--family-mono, monospace)' to
'var(--family-mono)') to comply with TEDI guideline "never use fallback values
in CSS var()".

185-198: 🏗️ Heavy lift

Move the story-only presentation rules out of TSX.

This file starts introducing reusable React.CSSProperties blocks here and then keeps layering more inline style literals through the story renderers. For TEDI stories, these styles should live in a CSS module so the examples follow the same styling path as the component itself. As per coding guidelines, "Use CSS Modules with SCSS for styling, avoiding inline styles".

🤖 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 `@src/tedi/components/content/table/table.stories.tsx` around lines 185 - 198,
The story file defines story-only inline style objects editLinkStyle and
nameLinkStyle and uses more inline styles in the renderers; move these
presentation rules into a CSS module (SCSS) and switch the story to use
className instead of React.CSSProperties: create a table.stories.module.scss
with classes (e.g. .editLink, .nameLink and any other story-specific styles),
import that module into the story file, replace usages of editLinkStyle and
nameLinkStyle in the story renderers with the corresponding module.className,
remove the React.CSSProperties declarations, and convert any additional inline
style literals in the render functions to module classes so the story follows
the CSS Modules/SCSS styling guideline.

44-53: ⚡ Quick win

Add status metadata to the story meta export.

design is wired up, but TEDI stories are expected to publish status metadata as well so Storybook shows component maturity consistently alongside the design link. As per coding guidelines, "meta parameters should include status/design info (including status.type array shape)".

🤖 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 `@src/tedi/components/content/table/table.stories.tsx` around lines 44 - 53,
The story meta object named "meta" for the Table component is missing TEDI
"status" metadata; update the meta export (the Meta<typeof Table> object) to
include a status field alongside design under parameters, following the expected
shape (e.g., a status property with a type array such as status: { type:
['stable'|'beta'|'deprecated' as appropriate] }); ensure you pick the correct
maturity value for Table and add it to the meta.parameters so Storybook shows
status and design together.
🤖 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.

Inline comments:
In `@src/tedi/components/content/table/table.spec.tsx`:
- Around line 418-488: The suite uses breakpoint-dependent markup but never
mocks the hook; add a single mock for useBreakpointProps at the top of the
describe('pagination') block (using jest.mock or jest.spyOn to stub
useBreakpointProps) and control its return value per-test (e.g., return
desktop:true for desktop-specific assertions and desktop:false for
mobile-specific assertions) so the rendered DOM is deterministic; update tests
that expect desktop controls (e.g., the page-size select assertions in the
'hides the page-size selector...' case and others like 'renders the pagination
bar...' and 'changes page size...') to rely on the mocked useBreakpointProps
return values.

In `@src/tedi/components/content/table/table.stories.tsx`:
- Around line 1556-1558: The footer currently computes the salary total using
info.table.getRowModel().rows which is post-pagination; change the aggregation
to use info.table.getFilteredRowModel().rows (or aggregate directly from the
people array) so the footer total matches the filtered/headcount scope shown in
the header; update the footer implementation (the footer function that reads
info.table.getRowModel().rows) to sum over info.table.getFilteredRowModel().rows
or people and keep formatting (toLocaleString('et-EE')) as before.

---

Nitpick comments:
In `@src/tedi/components/content/table/table.stories.tsx`:
- Around line 1868-1874: The inline style object in the story uses CSS var()
fallbacks for borderRadius and fontFamily (keys: borderRadius and fontFamily
inside the style prop in table.stories.tsx); remove the fallback values so the
tokens are used directly (e.g., change 'var(--tedi-borders-radius-default, 4px)'
to 'var(--tedi-borders-radius-default)' and 'var(--family-mono, monospace)' to
'var(--family-mono)') to comply with TEDI guideline "never use fallback values
in CSS var()".
- Around line 185-198: The story file defines story-only inline style objects
editLinkStyle and nameLinkStyle and uses more inline styles in the renderers;
move these presentation rules into a CSS module (SCSS) and switch the story to
use className instead of React.CSSProperties: create a table.stories.module.scss
with classes (e.g. .editLink, .nameLink and any other story-specific styles),
import that module into the story file, replace usages of editLinkStyle and
nameLinkStyle in the story renderers with the corresponding module.className,
remove the React.CSSProperties declarations, and convert any additional inline
style literals in the render functions to module classes so the story follows
the CSS Modules/SCSS styling guideline.
- Around line 44-53: The story meta object named "meta" for the Table component
is missing TEDI "status" metadata; update the meta export (the Meta<typeof
Table> object) to include a status field alongside design under parameters,
following the expected shape (e.g., a status property with a type array such as
status: { type: ['stable'|'beta'|'deprecated' as appropriate] }); ensure you
pick the correct maturity value for Table and add it to the meta.parameters so
Storybook shows status and design together.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: e23b7eaf-d0be-4bf3-a557-00598d31154f

📥 Commits

Reviewing files that changed from the base of the PR and between 1740b86 and 02f6043.

📒 Files selected for processing (20)
  • src/community/components/table/table.stories.tsx
  • src/tedi/components/buttons/collapse/collapse.tsx
  • src/tedi/components/content/table/index.ts
  • src/tedi/components/content/table/table-columns-menu/table-columns-menu.tsx
  • src/tedi/components/content/table/table-context.tsx
  • src/tedi/components/content/table/table-header-button/table-header-button.module.scss
  • src/tedi/components/content/table/table-header-button/table-header-button.tsx
  • src/tedi/components/content/table/table.module.scss
  • src/tedi/components/content/table/table.spec.tsx
  • src/tedi/components/content/table/table.stories.tsx
  • src/tedi/components/content/table/table.tsx
  • src/tedi/components/content/table/table.types.ts
  • src/tedi/components/content/table/use-table-persistence.ts
  • src/tedi/components/form/select/select.module.scss
  • src/tedi/components/navigation/pagination/pagination.module.scss
  • src/tedi/components/navigation/pagination/pagination.spec.tsx
  • src/tedi/components/navigation/pagination/pagination.tsx
  • src/tedi/components/navigation/pagination/pagination.types.ts
  • src/tedi/index.ts
  • src/tedi/providers/label-provider/labels-map.ts
💤 Files with no reviewable changes (1)
  • src/tedi/components/form/select/select.module.scss

Comment thread src/tedi/components/content/table/table.spec.tsx
Comment thread src/tedi/components/content/table/table.stories.tsx
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.

Actionable comments posted: 3

🧹 Nitpick comments (7)
src/tedi/components/content/table/table-header-button/table-header-button.spec.tsx (1)

14-17: ⚡ Quick win

Use semantic button queries instead of container.querySelector.

Both tests can query the button with screen.getByRole('button', { name: ... }), which keeps the suite aligned with RTL best practices and avoids DOM-structure coupling.

Suggested test update
   it('applies the selected modifier class when selected is true', () => {
-    const { container } = render(<TableHeaderButton icon="arrow_upward" selected aria-label="Sorted" />);
-    expect(container.querySelector('button')?.className).toMatch(/--selected/);
+    render(<TableHeaderButton icon="arrow_upward" selected aria-label="Sorted" />);
+    const button = screen.getByRole('button', { name: 'Sorted' });
+    expect(button.className).toMatch(/--selected/);
   });
@@
   it('merges custom className with the component class', () => {
-    const { container } = render(<TableHeaderButton icon="unfold_more" aria-label="Custom" className="custom-class" />);
-    const button = container.querySelector('button');
+    render(<TableHeaderButton icon="unfold_more" aria-label="Custom" className="custom-class" />);
+    const button = screen.getByRole('button', { name: 'Custom' });
     expect(button).toHaveClass('custom-class');
-    expect(button?.className).toMatch(/tedi-table-header-button/);
+    expect(button.className).toMatch(/tedi-table-header-button/);
   });

As per coding guidelines, "Use semantic queries in tests (getByRole, getByLabelText) instead of non-semantic queries (getByTestId)."

Also applies to: 54-59

🤖 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
`@src/tedi/components/content/table/table-header-button/table-header-button.spec.tsx`
around lines 14 - 17, Replace non-semantic DOM queries with role-based queries:
in the test "applies the selected modifier class when selected is true" (and the
similar test around lines 54-59), stop using container.querySelector('button')
and instead locate the element with screen.getByRole('button', { name: 'Sorted'
}) after render(<TableHeaderButton ... />); then assert on that button's
className or class list (e.g., expect(button.className).toMatch(/--selected/) or
expect(button).toHaveClass(...)). Ensure you import/use screen from
`@testing-library/react` and update both occurrences to use getByRole with the
aria-label "Sorted".
src/tedi/components/navigation/pagination/pagination.spec.tsx (2)

254-259: ⚡ Quick win

Prefer semantic queries over id-prefix selectors in these assertions.

These checks currently use container.querySelector(...), but the same intent is expressible via accessible-role queries and will be more resilient (e.g., queryByRole('combobox', { name: /Page size/i }), getByRole('combobox', { name: /Pagination/i })).

As per coding guidelines, "Use semantic queries in tests (getByRole, getByLabelText) instead of non-semantic queries (getByTestId)."

Also applies to: 307-309, 316-317

🤖 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 `@src/tedi/components/navigation/pagination/pagination.spec.tsx` around lines
254 - 259, Replace non-semantic
container.querySelector('[id^="tedi-pagination-page-size-"]') checks in the
Pagination tests with accessible role queries: after rendering Pagination
(render(<Pagination ... />)), use queryByRole('combobox', { name: /Page size/i
}) to assert the page-size select is not present and use getByRole('combobox', {
name: /Pagination/i }) or the appropriate accessible name for the page-jump
select where presence is expected; update the other instances referenced (around
lines 307-309 and 316-317) similarly to use queryByRole/getByRole instead of
container.querySelector to follow semantic query guidelines.

20-22: ⚡ Quick win

Avoid any in the breakpoint-props mock.

Line 21 uses props: any. This can be typed generically without losing flexibility.

Suggested change
-      // eslint-disable-next-line `@typescript-eslint/no-explicit-any`
-      getCurrentBreakpointProps: (props: any) => ({ ...props }),
+      getCurrentBreakpointProps: <T extends Record<string, unknown>>(props: T): T => ({ ...props }),

As per coding guidelines, "Use proper TypeScript generics instead of @ts-ignore or any types."

🤖 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 `@src/tedi/components/navigation/pagination/pagination.spec.tsx` around lines
20 - 22, The mock uses an explicit any in getCurrentBreakpointProps which
violates typing rules; change the mock to a generic signature so it preserves
input types (e.g., make getCurrentBreakpointProps generic like <T>(props: T) =>
T or equivalent) so callers retain proper types instead of any; update the mock
declaration for getCurrentBreakpointProps in the pagination spec to use that
generic type (referencing getCurrentBreakpointProps) and adjust any test usages
if needed.
src/tedi/components/buttons/collapse/collapse.spec.tsx (1)

154-165: ⚡ Quick win

Consider verifying DOM state in addition to callback values.

The test correctly verifies that onToggle is called with the appropriate state values (true for Enter, false for Space). To make the test more robust, consider also asserting the actual DOM state changes—for example, checking the aria-expanded attribute on the button or verifying the content element's visibility.

🧪 Suggested enhancement
  it('toggles open state when Enter or Space is pressed on the trigger', () => {
    const onToggle = jest.fn();
    const { getByRole } = getComponent({ id: 'collapse-keyboard', onToggle });
    const button = getByRole('button', { name: /näita rohkem/i });

    fireEvent.keyDown(button, { key: 'Enter' });
    expect(onToggle).toHaveBeenCalledWith(true);
+   expect(button).toHaveAttribute('aria-expanded', 'true');

    fireEvent.keyDown(button, { key: ' ' });
    expect(onToggle).toHaveBeenCalledWith(false);
+   expect(button).toHaveAttribute('aria-expanded', 'false');
    expect(onToggle).toHaveBeenCalledTimes(2);
  });
🤖 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 `@src/tedi/components/buttons/collapse/collapse.spec.tsx` around lines 154 -
165, The test currently asserts the onToggle mock calls but not the resulting
DOM changes; update the spec in collapse.spec.tsx (the test using getComponent
and the button variable) to also assert the button's aria-expanded attribute and
the collapse content visibility after each key press—e.g. after firing Enter
assert button.getAttribute('aria-expanded') === 'true' and that the content
element is visible, then after firing Space assert aria-expanded === 'false' and
the content is hidden—so both callback behavior (onToggle) and actual DOM state
are validated.
src/tedi/components/content/table/table.stories.tsx (2)

44-53: ⚡ Quick win

Storybook meta is missing parameters.status.type.

Per the contributing best-practices, story meta must include a parameters.status.type. Only parameters.design is provided here.

♻️ Suggested addition
 const meta: Meta<typeof Table> = {
   component: Table,
   title: 'TEDI-Ready/Content/Table',
   parameters: {
+    status: {
+      type: /* the appropriate status, e.g. 'beta' | 'stable' */,
+    },
     design: {
       type: 'figma',
       url: 'https://www.figma.com/design/jWiRIXhHRxwVdMSimKX2FF/TEDI-READY-2.45.70?node-id=4514-63761&m=dev',
     },
   },
 };

As per coding guidelines: "Storybook: meta must include parameters.status.type; follow the provided status/design parameter pattern".

🤖 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 `@src/tedi/components/content/table/table.stories.tsx` around lines 44 - 53,
The storybook meta object (meta: Meta<typeof Table> for component Table) is
missing parameters.status.type; update the meta.parameters to include a status
entry (e.g., parameters.status = { type: '<appropriate-status-value>' })
alongside the existing design entry so the meta includes both parameters.design
and parameters.status.type as required by the contributing guidelines.

1420-1428: 💤 Low value

Awkward <></> children on icon-only Buttons.

Several icon-only Button usages pass an empty fragment as children plus an onClick={() => undefined} no-op. If Button supports an icon-only mode (no required visible label / no children), prefer that pattern; otherwise consider rendering an accessible visually-hidden label child instead of an empty fragment. Repeating () => undefined for click handlers also obscures the demo's intent — either omit the handler if the prop is optional, or wire it to a real action (e.g., a console.info placeholder).

Also applies to: 1442-1446, 1510-1518

🤖 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 `@src/tedi/components/content/table/table.stories.tsx` around lines 1420 -
1428, The Button usages rendering icon-only controls (Button with
icon="edit"/"delete" in the row actions span) should not pass an empty fragment
as children or a no-op onClick; either use Button's icon-only mode (remove the
empty fragment children entirely) or provide an accessible visually-hidden label
child, and replace onClick={() => undefined} with either nothing (omit the prop
if optional) or a real placeholder like () => console.info('edit')/() =>
console.info('delete'); apply the same changes to the other occurrences
referenced (the Button instances at the other noted blocks).
src/tedi/components/content/table/table.spec.tsx (1)

217-246: 💤 Low value

pageCount={data.length} is semantically incorrect.

pageCount is the total number of pages, not rows. Here it happens to evaluate to 2, which equals ceil(data.length / pageSize) only because data.length === 2 and pageSize === 1. A future change to data will silently desync the test. Consider expressing it as pageCount={Math.ceil(data.length / pagination.pageSize)} or a literal pageCount={2} with a comment.

🤖 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 `@src/tedi/components/content/table/table.spec.tsx` around lines 217 - 246, The
test passes an incorrect semantic value for the Table prop pageCount (it
currently uses data.length); update the ServerSideWrapper so pageCount reflects
total pages rather than rows — e.g. compute pageCount as Math.ceil(data.length /
pagination.pageSize) using the pagination state from ServerSideWrapper (or
replace with a fixed literal like 2 plus a comment explaining why) so the Table
prop pageCount is correct when calling <Table ... pageCount={...}>.
🤖 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.

Inline comments:
In `@src/tedi/components/content/table/table.spec.tsx`:
- Around line 307-324: The test replaces window.localStorage with a throwing
getter but only restores it when an original descriptor exists; to avoid leaking
the mocked getter into other tests, capture the original via
Object.getOwnPropertyDescriptor(window, 'localStorage') and in the finally block
restore using Object.defineProperty(window, 'localStorage', original) if
original is present, otherwise remove the own property by doing delete (window
as { localStorage?: unknown }).localStorage so prototype-backed localStorage is
re-exposed; adjust the finally in the 'falls back gracefully when
window.localStorage access throws' test around render(<Table... persist={{ key:
'persist-block' }} />) accordingly.

In `@src/tedi/components/content/table/table.stories.tsx`:
- Around line 1870-1879: In the inline style object for the <pre> JSX element in
table.stories.tsx, remove the CSS var() fallback values so they match project
convention; specifically replace occurrences of
var(--tedi-borders-radius-default, 4px) with var(--tedi-borders-radius-default)
and var(--family-mono, monospace) with var(--family-mono) (update any similar
var(..., fallback) usages in that style object).

In `@src/tedi/components/navigation/pagination/pagination.module.scss`:
- Around line 150-157: Replace the hardcoded "transparent" values in the
breakpoints.media-breakpoint-down(md) block and its nested &:hover/:active rules
with the core design token: import the design tokens from
'@tedi-design-system/core' (e.g. as tokens) and use the transparent color token
(e.g. tokens.color.transparent or the project’s transparent token name) for the
background declarations; update both occurrences inside the selector containing
&:hover:not(:disabled, .tedi-pagination__button--selected) and
&:active:not(:disabled, .tedi-pagination__button--selected) so the component
uses the tokenized value instead of the literal "transparent".

---

Nitpick comments:
In `@src/tedi/components/buttons/collapse/collapse.spec.tsx`:
- Around line 154-165: The test currently asserts the onToggle mock calls but
not the resulting DOM changes; update the spec in collapse.spec.tsx (the test
using getComponent and the button variable) to also assert the button's
aria-expanded attribute and the collapse content visibility after each key
press—e.g. after firing Enter assert button.getAttribute('aria-expanded') ===
'true' and that the content element is visible, then after firing Space assert
aria-expanded === 'false' and the content is hidden—so both callback behavior
(onToggle) and actual DOM state are validated.

In
`@src/tedi/components/content/table/table-header-button/table-header-button.spec.tsx`:
- Around line 14-17: Replace non-semantic DOM queries with role-based queries:
in the test "applies the selected modifier class when selected is true" (and the
similar test around lines 54-59), stop using container.querySelector('button')
and instead locate the element with screen.getByRole('button', { name: 'Sorted'
}) after render(<TableHeaderButton ... />); then assert on that button's
className or class list (e.g., expect(button.className).toMatch(/--selected/) or
expect(button).toHaveClass(...)). Ensure you import/use screen from
`@testing-library/react` and update both occurrences to use getByRole with the
aria-label "Sorted".

In `@src/tedi/components/content/table/table.spec.tsx`:
- Around line 217-246: The test passes an incorrect semantic value for the Table
prop pageCount (it currently uses data.length); update the ServerSideWrapper so
pageCount reflects total pages rather than rows — e.g. compute pageCount as
Math.ceil(data.length / pagination.pageSize) using the pagination state from
ServerSideWrapper (or replace with a fixed literal like 2 plus a comment
explaining why) so the Table prop pageCount is correct when calling <Table ...
pageCount={...}>.

In `@src/tedi/components/content/table/table.stories.tsx`:
- Around line 44-53: The storybook meta object (meta: Meta<typeof Table> for
component Table) is missing parameters.status.type; update the meta.parameters
to include a status entry (e.g., parameters.status = { type:
'<appropriate-status-value>' }) alongside the existing design entry so the meta
includes both parameters.design and parameters.status.type as required by the
contributing guidelines.
- Around line 1420-1428: The Button usages rendering icon-only controls (Button
with icon="edit"/"delete" in the row actions span) should not pass an empty
fragment as children or a no-op onClick; either use Button's icon-only mode
(remove the empty fragment children entirely) or provide an accessible
visually-hidden label child, and replace onClick={() => undefined} with either
nothing (omit the prop if optional) or a real placeholder like () =>
console.info('edit')/() => console.info('delete'); apply the same changes to the
other occurrences referenced (the Button instances at the other noted blocks).

In `@src/tedi/components/navigation/pagination/pagination.spec.tsx`:
- Around line 254-259: Replace non-semantic
container.querySelector('[id^="tedi-pagination-page-size-"]') checks in the
Pagination tests with accessible role queries: after rendering Pagination
(render(<Pagination ... />)), use queryByRole('combobox', { name: /Page size/i
}) to assert the page-size select is not present and use getByRole('combobox', {
name: /Pagination/i }) or the appropriate accessible name for the page-jump
select where presence is expected; update the other instances referenced (around
lines 307-309 and 316-317) similarly to use queryByRole/getByRole instead of
container.querySelector to follow semantic query guidelines.
- Around line 20-22: The mock uses an explicit any in getCurrentBreakpointProps
which violates typing rules; change the mock to a generic signature so it
preserves input types (e.g., make getCurrentBreakpointProps generic like
<T>(props: T) => T or equivalent) so callers retain proper types instead of any;
update the mock declaration for getCurrentBreakpointProps in the pagination spec
to use that generic type (referencing getCurrentBreakpointProps) and adjust any
test usages if needed.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 7f4f0af9-1f89-4b64-ba6a-ab119bd3e24b

📥 Commits

Reviewing files that changed from the base of the PR and between 02f6043 and 39b3167.

📒 Files selected for processing (13)
  • src/tedi/components/buttons/collapse/collapse.spec.tsx
  • src/tedi/components/content/table/index.ts
  • src/tedi/components/content/table/table-columns-menu/table-columns-menu.tsx
  • src/tedi/components/content/table/table-header-button/table-header-button.module.scss
  • src/tedi/components/content/table/table-header-button/table-header-button.spec.tsx
  • src/tedi/components/content/table/table.module.scss
  • src/tedi/components/content/table/table.spec.tsx
  • src/tedi/components/content/table/table.stories.tsx
  • src/tedi/components/content/table/table.tsx
  • src/tedi/components/content/table/table.types.ts
  • src/tedi/components/navigation/pagination/pagination.module.scss
  • src/tedi/components/navigation/pagination/pagination.spec.tsx
  • src/tedi/components/navigation/pagination/pagination.tsx
✅ Files skipped from review due to trivial changes (2)
  • src/tedi/components/content/table/table-header-button/table-header-button.module.scss
  • src/tedi/components/content/table/index.ts
🚧 Files skipped from review as they are similar to previous changes (5)
  • src/tedi/components/content/table/table.types.ts
  • src/tedi/components/content/table/table-columns-menu/table-columns-menu.tsx
  • src/tedi/components/content/table/table.tsx
  • src/tedi/components/navigation/pagination/pagination.tsx
  • src/tedi/components/content/table/table.module.scss

Comment thread src/tedi/components/content/table/table.spec.tsx
Comment thread src/tedi/components/content/table/table.stories.tsx
Comment thread src/tedi/components/navigation/pagination/pagination.module.scss
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.

Actionable comments posted: 2

🤖 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.

Inline comments:
In `@src/tedi/components/content/table/table.tsx`:
- Around line 657-677: The top-row standalone headers currently get rowSpan=1
causing blanks under them; update the rowSpan calculation so non-placeholder
standalone headers (where isGroup is false and hasParentGroup is false) span the
remaining header depth: compute rowSpanCount = header.isPlaceholder ?
headerGroups.length - rowIndex : (!isGroup && !hasParentGroup ?
headerGroups.length - rowIndex : 1). Adjust the logic around
header.isPlaceholder / isGroup / hasParentGroup in the block that returns the
<th> (using symbols header, headerGroups, rowIndex, isGroup, hasParentGroup) so
the top-level leaf header spans the full header depth instead of leaving an
empty slot.
- Around line 466-476: The header Checkbox currently uses
table.getIsAllRowsSelected(), table.getIsSomeRowsSelected(), and
table.toggleAllRowsSelected(), which select across the entire dataset; change
these to the page-scoped APIs so pagination only affects the visible page: use
table.getIsAllPageRowsSelected() for checked, table.getIsSomePageRowsSelected()
for indeterminate calculation, and call table.toggleAllPageRowsSelected(checked)
in the onChange handler; keep the same props (id/name/resolvedId/getLabelRef)
but swap the global selection calls to the page-scoped counterparts.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: c1ad6f7b-ea73-4c12-9448-1575616b8cc7

📥 Commits

Reviewing files that changed from the base of the PR and between 39b3167 and da52955.

📒 Files selected for processing (11)
  • src/tedi/components/content/table/index.ts
  • src/tedi/components/content/table/table-context.tsx
  • src/tedi/components/content/table/table-toolbar/table-toolbar.tsx
  • src/tedi/components/content/table/table.module.scss
  • src/tedi/components/content/table/table.spec.tsx
  • src/tedi/components/content/table/table.stories.tsx
  • src/tedi/components/content/table/table.tsx
  • src/tedi/components/content/table/use-table-persistence.ts
  • src/tedi/components/navigation/pagination/pagination.spec.tsx
  • src/tedi/components/navigation/pagination/pagination.tsx
  • src/tedi/providers/label-provider/labels-map.ts
🚧 Files skipped from review as they are similar to previous changes (7)
  • src/tedi/components/content/table/table-context.tsx
  • src/tedi/components/content/table/index.ts
  • src/tedi/providers/label-provider/labels-map.ts
  • src/tedi/components/navigation/pagination/pagination.spec.tsx
  • src/tedi/components/content/table/use-table-persistence.ts
  • src/tedi/components/content/table/table.module.scss
  • src/tedi/components/navigation/pagination/pagination.tsx

Comment thread src/tedi/components/content/table/table.tsx
Comment thread src/tedi/components/content/table/table.tsx Outdated
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)
src/tedi/components/content/table/table.stories.tsx (1)

793-927: ⚡ Quick win

Consider reusing the shared useEditableRows + EditableRowsContext pattern.

This story re-implements the inline editing logic that's already available via the shared utilities (lines 240-267, 218-232). The shared pattern keeps the columns array stable (avoiding TanStack Table reconciliation), whereas this implementation requires useMemo with [draft, editingId] dependencies.

If the duplication is intentional to demonstrate alternative approaches, consider adding a comment explaining the trade-off. Otherwise, refactoring to reuse the shared pattern would reduce maintenance burden.

🤖 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 `@src/tedi/components/content/table/table.stories.tsx` around lines 793 - 927,
The story EditableValues reimplements inline-editing state (beginEdit,
cancelEdit, commitEdit, draft, editingId) and builds columns in a useMemo
dependent on [draft, editingId], duplicating the shared useEditableRows +
EditableRowsContext pattern; refactor this story to consume the existing
useEditableRows hook and/or EditableRowsContext (replace local state and
handlers with the shared hook's API) and move editable cell rendering to the
stable columns built without draft/editingId dependencies so the columns array
stays stable for TanStack Table reconciliation; if you intentionally want to
show an alternative, add a short comment in EditableValues explaining the
trade-off and why it diverges from useEditableRows.
🤖 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 `@src/tedi/components/content/table/table.stories.tsx`:
- Around line 793-927: The story EditableValues reimplements inline-editing
state (beginEdit, cancelEdit, commitEdit, draft, editingId) and builds columns
in a useMemo dependent on [draft, editingId], duplicating the shared
useEditableRows + EditableRowsContext pattern; refactor this story to consume
the existing useEditableRows hook and/or EditableRowsContext (replace local
state and handlers with the shared hook's API) and move editable cell rendering
to the stable columns built without draft/editingId dependencies so the columns
array stays stable for TanStack Table reconciliation; if you intentionally want
to show an alternative, add a short comment in EditableValues explaining the
trade-off and why it diverges from useEditableRows.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 95c02b92-5332-46dd-a2e8-4d6218185a6c

📥 Commits

Reviewing files that changed from the base of the PR and between da52955 and a463629.

📒 Files selected for processing (4)
  • src/tedi/components/content/table/table.module.scss
  • src/tedi/components/content/table/table.spec.tsx
  • src/tedi/components/content/table/table.stories.tsx
  • src/tedi/components/content/table/table.tsx
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/tedi/components/content/table/table.module.scss
  • src/tedi/components/content/table/table.spec.tsx
  • src/tedi/components/content/table/table.tsx

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)
src/tedi/components/content/table/table.stories.tsx (1)

303-315: ⚡ Quick win

Consider using <button> instead of <a href="#"> for non-navigation actions.

When an action doesn't navigate to a new URL, use a <button> element styled as a link rather than an <a> with a # href. This improves semantics and accessibility — screen readers announce it correctly as a button action rather than a navigation link.

♻️ Suggested pattern
- <a
-   href="#"
-   onClick={(event) => {
-     event.preventDefault();
-     editor.beginEdit(row);
-   }}
-   style={editLinkStyle}
- >
+ <button
+   type="button"
+   onClick={() => editor.beginEdit(row)}
+   style={editLinkStyle}
+ >
    <Icon name="edit" color="brand" size={18} />
    Muuda
- </a>
+ </button>

Apply the same change at lines 478 and 1454 where the same pattern appears.

🤖 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 `@src/tedi/components/content/table/table.stories.tsx` around lines 303 - 315,
Replace the non-navigation anchor used for the edit action with a semantic
button: instead of <a href="#"> using editRowActionsStyle/editLinkStyle and
calling editor.beginEdit(row) in the onClick, use a <button type="button">
styled with editLinkStyle (or a className) and keep the same onClick handler
(call editor.beginEdit(row) and preventDefault is no longer needed); ensure
accessibility attributes remain (e.g., aria-label if present) and preserve the
Icon and text; apply the identical replacement for the other occurrences that
use the same pattern.
🤖 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 `@src/tedi/components/content/table/table.stories.tsx`:
- Around line 303-315: Replace the non-navigation anchor used for the edit
action with a semantic button: instead of <a href="#"> using
editRowActionsStyle/editLinkStyle and calling editor.beginEdit(row) in the
onClick, use a <button type="button"> styled with editLinkStyle (or a className)
and keep the same onClick handler (call editor.beginEdit(row) and preventDefault
is no longer needed); ensure accessibility attributes remain (e.g., aria-label
if present) and preserve the Icon and text; apply the identical replacement for
the other occurrences that use the same pattern.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: d3bdfdf6-3969-4066-a200-77c1b8ded643

📥 Commits

Reviewing files that changed from the base of the PR and between a463629 and 94f19b5.

📒 Files selected for processing (11)
  • skills/tedi-react/references/components.md
  • src/tedi/components/content/table/table-columns-menu/table-columns-menu.tsx
  • src/tedi/components/content/table/table.module.scss
  • src/tedi/components/content/table/table.spec.tsx
  • src/tedi/components/content/table/table.stories.tsx
  • src/tedi/components/content/table/table.tsx
  • src/tedi/components/navigation/pagination/pagination.module.scss
  • src/tedi/components/navigation/pagination/pagination.spec.tsx
  • src/tedi/components/navigation/pagination/pagination.tsx
  • src/tedi/index.ts
  • src/tedi/providers/label-provider/labels-map.ts
🚧 Files skipped from review as they are similar to previous changes (7)
  • src/tedi/components/content/table/table-columns-menu/table-columns-menu.tsx
  • src/tedi/components/navigation/pagination/pagination.module.scss
  • src/tedi/components/navigation/pagination/pagination.spec.tsx
  • src/tedi/providers/label-provider/labels-map.ts
  • src/tedi/components/content/table/table.module.scss
  • src/tedi/components/content/table/table.tsx
  • src/tedi/components/content/table/table.spec.tsx

@airikej
Copy link
Copy Markdown
Contributor Author

airikej commented May 18, 2026

@coderabbitai resolve

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 18, 2026

✅ Actions performed

Comments resolved. Approval is disabled; enable reviews.request_changes_workflow to allow explicit top-level @coderabbitai resolve or @coderabbitai approve commands.

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]: New TEDI-Ready component

1 participant