@arbor-education/design-system.components 0.10.0 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/.claude/agent-memory/blanche-designspert/MEMORY.md +64 -0
  2. package/.claude/agent-memory/blanche-designspert/token-review-patterns.md +29 -0
  3. package/.claude/agent-memory/dorothy-fact-checker/MEMORY.md +129 -0
  4. package/.claude/agent-memory/rose-storybookspert/MEMORY.md +29 -0
  5. package/.claude/agent-memory/rose-storybookspert/patterns.md +132 -0
  6. package/.claude/agent-memory/sophia-componentspert/MEMORY.md +14 -0
  7. package/.claude/agent-memory/sophia-componentspert/components.md +367 -0
  8. package/.claude/agents/blanche-designspert.md +150 -0
  9. package/.claude/agents/dorothy-fact-checker.md +145 -0
  10. package/.claude/agents/rose-storybookspert.md +148 -0
  11. package/.claude/agents/sophia-componentspert.md +133 -0
  12. package/.claude/component-library.md +1107 -0
  13. package/.claude/design-assessment-daily-attendance-2026-04-10.md +566 -0
  14. package/.claude/figma-assessment-7154-58899.md +404 -0
  15. package/.claude/figma-assessment-UKQfcxnT4rlHHNuiumt4o1-11086-97537.md +392 -0
  16. package/.claude/figma-assessment-UKQfcxnT4rlHHNuiumt4o1-551-41974.md +474 -0
  17. package/.claude/figma-assessment-UKQfcxnT4rlHHNuiumt4o1-551-43094.md +462 -0
  18. package/.claude/figma-assessment-fcFK4CGzkz2fVyY3koX8ZE-7154-59061.md +440 -0
  19. package/.claude/migration-report-custom-report-writer-2026-02-19.md +591 -0
  20. package/.claude/skills/analyze-design/README.md +295 -0
  21. package/.claude/skills/analyze-design/SKILL.md +741 -0
  22. package/.claude/skills/create-page/README.md +246 -0
  23. package/.claude/skills/create-page/SKILL.md +634 -0
  24. package/.claude/skills/create-page/design-analysis-template.md +333 -0
  25. package/.claude/skills/create-page/page-template.scss +118 -0
  26. package/.claude/skills/create-page/page-template.tsx +230 -0
  27. package/.claude/skills/map-legacy/README.md +87 -0
  28. package/.claude/skills/map-legacy/SKILL.md +465 -0
  29. package/.claude/skills/migrate-page/README.md +125 -0
  30. package/.claude/skills/migrate-page/SKILL.md +374 -0
  31. package/.github/CODEOWNERS +1 -0
  32. package/.github/pull_request_template.md +39 -0
  33. package/CHANGELOG.md +6 -0
  34. package/CLAUDE.md +31 -0
  35. package/CONTRIBUTING.md +191 -0
  36. package/README.md +110 -20
  37. package/dist/components/table/DSDefaultColDef.js +2 -2
  38. package/dist/components/table/DSDefaultColDef.js.map +1 -1
  39. package/dist/components/table/Table.d.ts.map +1 -1
  40. package/dist/components/table/Table.js +2 -0
  41. package/dist/components/table/Table.js.map +1 -1
  42. package/dist/components/table/Table.stories.d.ts +1 -0
  43. package/dist/components/table/Table.stories.d.ts.map +1 -1
  44. package/dist/components/table/Table.stories.js +95 -3
  45. package/dist/components/table/Table.stories.js.map +1 -1
  46. package/dist/components/table/Table.test.js +106 -5
  47. package/dist/components/table/Table.test.js.map +1 -1
  48. package/dist/components/table/cellRenderers/CheckboxCellRenderer.d.ts +3 -0
  49. package/dist/components/table/cellRenderers/CheckboxCellRenderer.d.ts.map +1 -0
  50. package/dist/components/table/cellRenderers/CheckboxCellRenderer.js +12 -0
  51. package/dist/components/table/cellRenderers/CheckboxCellRenderer.js.map +1 -0
  52. package/dist/components/table/cellRenderers/CheckboxCellRenderer.test.d.ts +2 -0
  53. package/dist/components/table/cellRenderers/CheckboxCellRenderer.test.d.ts.map +1 -0
  54. package/dist/components/table/cellRenderers/CheckboxCellRenderer.test.js +65 -0
  55. package/dist/components/table/cellRenderers/CheckboxCellRenderer.test.js.map +1 -0
  56. package/dist/index.css +1 -1
  57. package/dist/index.d.ts +1 -0
  58. package/dist/index.d.ts.map +1 -1
  59. package/dist/index.js +1 -0
  60. package/dist/index.js.map +1 -1
  61. package/package.json +1 -1
  62. package/src/components/table/DSDefaultColDef.ts +2 -2
  63. package/src/components/table/Table.stories.tsx +99 -3
  64. package/src/components/table/Table.test.tsx +131 -5
  65. package/src/components/table/Table.tsx +2 -0
  66. package/src/components/table/cellRenderers/CheckboxCellRenderer.test.tsx +74 -0
  67. package/src/components/table/cellRenderers/CheckboxCellRenderer.tsx +28 -0
  68. package/src/components/table/table.scss +1 -1
  69. package/src/index.ts +1 -0
@@ -1487,14 +1487,140 @@ describe('Table', () => {
1487
1487
  });
1488
1488
  });
1489
1489
 
1490
- describe('supressCellFocusAndFocusFirstElement', () => {
1490
+ describe('CheckboxCellRenderer', () => {
1491
+ test('renders checkboxes in table cells', async () => {
1492
+ const columnDefs = [
1493
+ { field: 'name', headerName: 'Name' },
1494
+ {
1495
+ field: 'attended',
1496
+ headerName: 'Attended',
1497
+ cellRenderer: 'dsCheckboxCellRenderer',
1498
+ editable: false,
1499
+ },
1500
+ ];
1501
+ const rowData = [
1502
+ { name: 'Alice', attended: true },
1503
+ { name: 'Bob', attended: false },
1504
+ ];
1505
+
1506
+ render(<Table columnDefs={columnDefs} rowData={rowData} />);
1507
+ await waitFor(() => expect(screen.getByRole('grid')).toBeInTheDocument());
1508
+ await waitFor(() => {
1509
+ const checkboxes = screen.getAllByRole('checkbox');
1510
+ expect(checkboxes.length).toBe(2);
1511
+ });
1512
+ });
1513
+
1514
+ test('checked state reflects row data value', async () => {
1515
+ const columnDefs = [{
1516
+ field: 'active',
1517
+ headerName: 'Active',
1518
+ cellRenderer: 'dsCheckboxCellRenderer',
1519
+ editable: false,
1520
+ }];
1521
+ const rowData = [
1522
+ { active: true },
1523
+ { active: false },
1524
+ ];
1525
+
1526
+ render(<Table columnDefs={columnDefs} rowData={rowData} />);
1527
+ await waitFor(() => expect(screen.getByRole('grid')).toBeInTheDocument());
1528
+ await waitFor(() => {
1529
+ const checkboxes = screen.getAllByRole('checkbox');
1530
+ expect(checkboxes[0]).toBeChecked();
1531
+ expect(checkboxes[1]).not.toBeChecked();
1532
+ });
1533
+ });
1534
+
1535
+ test('clicking checkbox triggers onCellValueChanged', async () => {
1536
+ const onCellValueChanged = vi.fn();
1537
+ const columnDefs = [{
1538
+ field: 'active',
1539
+ headerName: 'Active',
1540
+ cellRenderer: 'dsCheckboxCellRenderer',
1541
+ editable: false,
1542
+ }];
1543
+ const rowData = [{ active: false }];
1544
+
1545
+ render(
1546
+ <Table
1547
+ columnDefs={columnDefs}
1548
+ rowData={rowData}
1549
+ onCellValueChanged={onCellValueChanged}
1550
+ />,
1551
+ );
1552
+ await waitFor(() => expect(screen.getByRole('grid')).toBeInTheDocument());
1553
+ await waitFor(() => expect(screen.getByRole('checkbox')).toBeInTheDocument());
1554
+
1555
+ await userEvent.click(screen.getByRole('checkbox'));
1556
+
1557
+ await waitFor(() => {
1558
+ expect(onCellValueChanged).toHaveBeenCalledWith(expect.objectContaining({ oldValue: false, newValue: true }));
1559
+ });
1560
+ });
1561
+
1562
+ test('works with multiple checkbox columns', async () => {
1563
+ const columnDefs = [
1564
+ { field: 'name', headerName: 'Name' },
1565
+ {
1566
+ field: 'attended',
1567
+ headerName: 'Attended',
1568
+ cellRenderer: 'dsCheckboxCellRenderer',
1569
+ editable: false,
1570
+ },
1571
+ {
1572
+ field: 'consented',
1573
+ headerName: 'Consented',
1574
+ cellRenderer: 'dsCheckboxCellRenderer',
1575
+ editable: false,
1576
+ },
1577
+ ];
1578
+ const rowData = [
1579
+ { name: 'Alice', attended: true, consented: false },
1580
+ ];
1581
+
1582
+ render(<Table columnDefs={columnDefs} rowData={rowData} />);
1583
+ await waitFor(() => expect(screen.getByRole('grid')).toBeInTheDocument());
1584
+ await waitFor(() => {
1585
+ const checkboxes = screen.getAllByRole('checkbox');
1586
+ expect(checkboxes[0]).toBeChecked();
1587
+ expect(checkboxes[1]).not.toBeChecked();
1588
+ });
1589
+ });
1590
+
1591
+ test('disabled flag via cellRendererParams', async () => {
1592
+ const onCellValueChanged = vi.fn();
1593
+ const columnDefs = [{
1594
+ field: 'active',
1595
+ headerName: 'Active',
1596
+ cellRenderer: 'dsCheckboxCellRenderer',
1597
+ editable: false,
1598
+ cellRendererParams: { disabled: true },
1599
+ }];
1600
+ const rowData = [{ active: false }];
1601
+
1602
+ render(
1603
+ <Table
1604
+ columnDefs={columnDefs}
1605
+ rowData={rowData}
1606
+ onCellValueChanged={onCellValueChanged}
1607
+ />,
1608
+ );
1609
+ await waitFor(() => expect(screen.getByRole('grid')).toBeInTheDocument());
1610
+ await waitFor(() => expect(screen.getByRole('checkbox')).toBeInTheDocument());
1611
+
1612
+ expect(screen.getByRole('checkbox')).toBeDisabled();
1613
+ });
1614
+ });
1615
+
1616
+ describe('suppressCellFocusAndFocusFirstElement', () => {
1491
1617
  const handleClick = vi.fn();
1492
1618
  const columnDefs = [{
1493
1619
  field: 'action',
1494
1620
  headerName: 'Action',
1495
1621
  cellRenderer: 'dsButtonCellRenderer',
1496
1622
  cellRendererParams: {
1497
- supressCellFocusAndFocusFirstElement: true,
1623
+ suppressCellFocusAndFocusFirstElement: true,
1498
1624
  },
1499
1625
  }];
1500
1626
  const rowData = [{
@@ -1504,14 +1630,14 @@ describe('Table', () => {
1504
1630
  },
1505
1631
  }];
1506
1632
 
1507
- test('setting supressCellFocusAndFocusFirstElement to true should add the ds-table__cell--supress-focus class', async () => {
1633
+ test('setting suppressCellFocusAndFocusFirstElement to true should add the ds-table__cell--suppress-focus class', async () => {
1508
1634
  const { container } = render(<Table columnDefs={columnDefs} rowData={rowData} />);
1509
1635
  await screen.findByRole('grid', {}, { timeout: 10000 });
1510
- const cellWithClass = container.querySelector('.ds-table__cell--supress-focus');
1636
+ const cellWithClass = container.querySelector('.ds-table__cell--suppress-focus');
1511
1637
  expect(cellWithClass).toBeInTheDocument();
1512
1638
  });
1513
1639
 
1514
- test('setting supressCellFocusAndFocusFirstElement to true should suppress cell focus and focus the first element', async () => {
1640
+ test('setting suppressCellFocusAndFocusFirstElement to true should suppress cell focus and focus the first element', async () => {
1515
1641
  const focusFirstFocusableElementSpy = vi.spyOn(
1516
1642
  focusFirstFocusableElementModule,
1517
1643
  'focusFirstFocusableElement',
@@ -24,6 +24,7 @@ import { focusFirstFocusableElement } from 'Utils/focusFirstFocusableElement';
24
24
  import { BooleanFilter } from './columnFilters/BooleanFilter/BooleanFilter';
25
25
  import { TimeFilter } from './columnFilters/TimeFilter/TimeFilter';
26
26
  import { TableSettingsDropdown } from './TableSettingsDropdown';
27
+ import { CheckboxCellRenderer } from './cellRenderers/CheckboxCellRenderer';
27
28
  import { BooleanCellRenderer } from './cellRenderers/BooleanCellRenderer';
28
29
 
29
30
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -195,6 +196,7 @@ export const Table = (props: TableProps) => {
195
196
  dsSelectDropdownCellRenderer: SelectDropdownCellRenderer,
196
197
  dsBooleanFilter: BooleanFilter,
197
198
  dsTimeFilter: TimeFilter,
199
+ dsCheckboxCellRenderer: CheckboxCellRenderer,
198
200
  dsBooleanCellRenderer: BooleanCellRenderer,
199
201
  ...components,
200
202
  }}
@@ -0,0 +1,74 @@
1
+ import { describe, expect, test, vi } from 'vitest';
2
+ import { render, screen } from '@testing-library/react';
3
+ import '@testing-library/jest-dom/vitest';
4
+ import userEvent from '@testing-library/user-event';
5
+ import { CheckboxCellRenderer } from './CheckboxCellRenderer';
6
+ import type { CustomCellRendererProps } from 'ag-grid-react';
7
+
8
+ const createMockProps = (overrides: Partial<CustomCellRendererProps> = {}): CustomCellRendererProps => ({
9
+ value: false,
10
+ node: {
11
+ setDataValue: vi.fn(),
12
+ },
13
+ column: 'testField',
14
+ ...overrides,
15
+ } as unknown as CustomCellRendererProps);
16
+
17
+ describe('CheckboxCellRenderer', () => {
18
+ test('renders a checkbox', () => {
19
+ const props = createMockProps();
20
+ render(<CheckboxCellRenderer {...props} />);
21
+ expect(screen.getByRole('checkbox')).toBeInTheDocument();
22
+ });
23
+
24
+ test('renders unchecked when value is false', () => {
25
+ const props = createMockProps({ value: false });
26
+ render(<CheckboxCellRenderer {...props} />);
27
+ expect(screen.getByRole('checkbox')).not.toBeChecked();
28
+ });
29
+
30
+ test('renders checked when value is true', () => {
31
+ const props = createMockProps({ value: true });
32
+ render(<CheckboxCellRenderer {...props} />);
33
+ expect(screen.getByRole('checkbox')).toBeChecked();
34
+ });
35
+
36
+ test('calls node.setDataValue with toggled value on change', async () => {
37
+ const setDataValue = vi.fn();
38
+ const props = createMockProps({
39
+ value: false,
40
+ node: { setDataValue } as unknown as CustomCellRendererProps['node'],
41
+ column: 'attended' as unknown as CustomCellRendererProps['column'],
42
+ });
43
+
44
+ render(<CheckboxCellRenderer {...props} />);
45
+ await userEvent.click(screen.getByRole('checkbox'));
46
+ expect(setDataValue).toHaveBeenCalledWith('attended', true);
47
+ });
48
+
49
+ test('toggles from true to false on change', async () => {
50
+ const setDataValue = vi.fn();
51
+ const props = createMockProps({
52
+ value: true,
53
+ node: { setDataValue } as unknown as CustomCellRendererProps['node'],
54
+ column: 'attended' as unknown as CustomCellRendererProps['column'],
55
+ });
56
+
57
+ render(<CheckboxCellRenderer {...props} />);
58
+ await userEvent.click(screen.getByRole('checkbox'));
59
+ expect(setDataValue).toHaveBeenCalledWith('attended', false);
60
+ });
61
+
62
+ test('does not call setDataValue when column is undefined', async () => {
63
+ const setDataValue = vi.fn();
64
+ const props = createMockProps({
65
+ value: false,
66
+ node: { setDataValue } as unknown as CustomCellRendererProps['node'],
67
+ column: undefined as unknown as CustomCellRendererProps['column'],
68
+ });
69
+
70
+ render(<CheckboxCellRenderer {...props} />);
71
+ await userEvent.click(screen.getByRole('checkbox'));
72
+ expect(setDataValue).not.toHaveBeenCalled();
73
+ });
74
+ });
@@ -0,0 +1,28 @@
1
+ import type { CustomCellRendererProps } from 'ag-grid-react';
2
+ import { CheckboxInput } from 'Components/formField/inputs/checkbox/CheckboxInput';
3
+
4
+ export const CheckboxCellRenderer = (props: CustomCellRendererProps) => {
5
+ const {
6
+ value,
7
+ node,
8
+ column,
9
+ colDef = {},
10
+ } = props;
11
+
12
+ const { cellRendererParams, headerName } = colDef;
13
+
14
+ return (
15
+ <div className="ds-checkbox-cell-renderer">
16
+ <CheckboxInput
17
+ checked={value}
18
+ aria-label={headerName && `Checkbox: ${headerName}`}
19
+ onChange={() => {
20
+ if (column) {
21
+ node.setDataValue(column, !value);
22
+ }
23
+ }}
24
+ {...cellRendererParams}
25
+ />
26
+ </div>
27
+ );
28
+ };
@@ -58,6 +58,6 @@
58
58
  }
59
59
  }
60
60
 
61
- .ag-cell-focus.ds-table__cell--supress-focus:focus-within {
61
+ .ag-cell-focus.ds-table__cell--suppress-focus:focus-within {
62
62
  border-color: transparent;
63
63
  }
package/src/index.ts CHANGED
@@ -66,3 +66,4 @@ export type {
66
66
  } from 'Components/combobox/types';
67
67
  export { Toggle } from 'Components/toggle/Toggle';
68
68
  export { SlideoverUtils } from 'Utils/SlideoverUtils';
69
+ export { CheckboxCellRenderer } from 'Components/table/cellRenderers/CheckboxCellRenderer';