@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.
- package/.claude/agent-memory/blanche-designspert/MEMORY.md +64 -0
- package/.claude/agent-memory/blanche-designspert/token-review-patterns.md +29 -0
- package/.claude/agent-memory/dorothy-fact-checker/MEMORY.md +129 -0
- package/.claude/agent-memory/rose-storybookspert/MEMORY.md +29 -0
- package/.claude/agent-memory/rose-storybookspert/patterns.md +132 -0
- package/.claude/agent-memory/sophia-componentspert/MEMORY.md +14 -0
- package/.claude/agent-memory/sophia-componentspert/components.md +367 -0
- package/.claude/agents/blanche-designspert.md +150 -0
- package/.claude/agents/dorothy-fact-checker.md +145 -0
- package/.claude/agents/rose-storybookspert.md +148 -0
- package/.claude/agents/sophia-componentspert.md +133 -0
- package/.claude/component-library.md +1107 -0
- package/.claude/design-assessment-daily-attendance-2026-04-10.md +566 -0
- package/.claude/figma-assessment-7154-58899.md +404 -0
- package/.claude/figma-assessment-UKQfcxnT4rlHHNuiumt4o1-11086-97537.md +392 -0
- package/.claude/figma-assessment-UKQfcxnT4rlHHNuiumt4o1-551-41974.md +474 -0
- package/.claude/figma-assessment-UKQfcxnT4rlHHNuiumt4o1-551-43094.md +462 -0
- package/.claude/figma-assessment-fcFK4CGzkz2fVyY3koX8ZE-7154-59061.md +440 -0
- package/.claude/migration-report-custom-report-writer-2026-02-19.md +591 -0
- package/.claude/skills/analyze-design/README.md +295 -0
- package/.claude/skills/analyze-design/SKILL.md +741 -0
- package/.claude/skills/create-page/README.md +246 -0
- package/.claude/skills/create-page/SKILL.md +634 -0
- package/.claude/skills/create-page/design-analysis-template.md +333 -0
- package/.claude/skills/create-page/page-template.scss +118 -0
- package/.claude/skills/create-page/page-template.tsx +230 -0
- package/.claude/skills/map-legacy/README.md +87 -0
- package/.claude/skills/map-legacy/SKILL.md +465 -0
- package/.claude/skills/migrate-page/README.md +125 -0
- package/.claude/skills/migrate-page/SKILL.md +374 -0
- package/.github/CODEOWNERS +1 -0
- package/.github/pull_request_template.md +39 -0
- package/CHANGELOG.md +6 -0
- package/CLAUDE.md +31 -0
- package/CONTRIBUTING.md +191 -0
- package/README.md +110 -20
- package/dist/components/table/DSDefaultColDef.js +2 -2
- package/dist/components/table/DSDefaultColDef.js.map +1 -1
- package/dist/components/table/Table.d.ts.map +1 -1
- package/dist/components/table/Table.js +2 -0
- package/dist/components/table/Table.js.map +1 -1
- package/dist/components/table/Table.stories.d.ts +1 -0
- package/dist/components/table/Table.stories.d.ts.map +1 -1
- package/dist/components/table/Table.stories.js +95 -3
- package/dist/components/table/Table.stories.js.map +1 -1
- package/dist/components/table/Table.test.js +106 -5
- package/dist/components/table/Table.test.js.map +1 -1
- package/dist/components/table/cellRenderers/CheckboxCellRenderer.d.ts +3 -0
- package/dist/components/table/cellRenderers/CheckboxCellRenderer.d.ts.map +1 -0
- package/dist/components/table/cellRenderers/CheckboxCellRenderer.js +12 -0
- package/dist/components/table/cellRenderers/CheckboxCellRenderer.js.map +1 -0
- package/dist/components/table/cellRenderers/CheckboxCellRenderer.test.d.ts +2 -0
- package/dist/components/table/cellRenderers/CheckboxCellRenderer.test.d.ts.map +1 -0
- package/dist/components/table/cellRenderers/CheckboxCellRenderer.test.js +65 -0
- package/dist/components/table/cellRenderers/CheckboxCellRenderer.test.js.map +1 -0
- package/dist/index.css +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/components/table/DSDefaultColDef.ts +2 -2
- package/src/components/table/Table.stories.tsx +99 -3
- package/src/components/table/Table.test.tsx +131 -5
- package/src/components/table/Table.tsx +2 -0
- package/src/components/table/cellRenderers/CheckboxCellRenderer.test.tsx +74 -0
- package/src/components/table/cellRenderers/CheckboxCellRenderer.tsx +28 -0
- package/src/components/table/table.scss +1 -1
- package/src/index.ts +1 -0
|
@@ -1487,14 +1487,140 @@ describe('Table', () => {
|
|
|
1487
1487
|
});
|
|
1488
1488
|
});
|
|
1489
1489
|
|
|
1490
|
-
describe('
|
|
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
|
-
|
|
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
|
|
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--
|
|
1636
|
+
const cellWithClass = container.querySelector('.ds-table__cell--suppress-focus');
|
|
1511
1637
|
expect(cellWithClass).toBeInTheDocument();
|
|
1512
1638
|
});
|
|
1513
1639
|
|
|
1514
|
-
test('setting
|
|
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
|
+
};
|
package/src/index.ts
CHANGED