@arbor-education/design-system.components 0.18.0 → 0.19.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 (32) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/dist/components/combobox/useComboboxListboxKeyboard.d.ts.map +1 -1
  3. package/dist/components/combobox/useComboboxListboxKeyboard.js +2 -0
  4. package/dist/components/combobox/useComboboxListboxKeyboard.js.map +1 -1
  5. package/dist/components/combobox/useComboboxListboxKeyboard.test.js +1 -0
  6. package/dist/components/combobox/useComboboxListboxKeyboard.test.js.map +1 -1
  7. package/dist/components/table/Table.d.ts +7 -0
  8. package/dist/components/table/Table.d.ts.map +1 -1
  9. package/dist/components/table/Table.js +3 -0
  10. package/dist/components/table/Table.js.map +1 -1
  11. package/dist/components/table/Table.stories.d.ts +2 -0
  12. package/dist/components/table/Table.stories.d.ts.map +1 -1
  13. package/dist/components/table/Table.stories.js +148 -2
  14. package/dist/components/table/Table.stories.js.map +1 -1
  15. package/dist/components/table/cellRenderers/ComboboxCellRenderer.d.ts +13 -0
  16. package/dist/components/table/cellRenderers/ComboboxCellRenderer.d.ts.map +1 -0
  17. package/dist/components/table/cellRenderers/ComboboxCellRenderer.js +72 -0
  18. package/dist/components/table/cellRenderers/ComboboxCellRenderer.js.map +1 -0
  19. package/dist/components/table/cellRenderers/ComboboxCellRenderer.test.d.ts +2 -0
  20. package/dist/components/table/cellRenderers/ComboboxCellRenderer.test.d.ts.map +1 -0
  21. package/dist/components/table/cellRenderers/ComboboxCellRenderer.test.js +324 -0
  22. package/dist/components/table/cellRenderers/ComboboxCellRenderer.test.js.map +1 -0
  23. package/dist/index.css +3 -0
  24. package/dist/index.css.map +1 -1
  25. package/package.json +1 -1
  26. package/src/components/combobox/useComboboxListboxKeyboard.test.tsx +1 -0
  27. package/src/components/combobox/useComboboxListboxKeyboard.ts +2 -0
  28. package/src/components/table/Table.stories.tsx +180 -2
  29. package/src/components/table/Table.tsx +3 -0
  30. package/src/components/table/cellRenderers/ComboboxCellRenderer.test.tsx +437 -0
  31. package/src/components/table/cellRenderers/ComboboxCellRenderer.tsx +135 -0
  32. package/src/components/table/table.scss +4 -0
@@ -0,0 +1,135 @@
1
+ import { useRef } from 'react';
2
+ import type {
3
+ CellKeyDownEvent,
4
+ FullWidthCellKeyDownEvent,
5
+ SuppressKeyboardEventParams,
6
+ } from 'ag-grid-community';
7
+ import type { CustomCellRendererProps } from 'ag-grid-react';
8
+ import { Combobox } from 'Components/combobox/Combobox';
9
+ import type { ComboboxProps } from 'Components/combobox/types';
10
+ import { useComponentDidMount } from 'Utils/hooks/useComponentDidMount';
11
+
12
+ type ComboboxCellRendererProps = CustomCellRendererProps & ComboboxProps;
13
+
14
+ export const ComboboxCellRenderer = (props: ComboboxCellRendererProps) => {
15
+ const {
16
+ value,
17
+ node,
18
+ column,
19
+ api,
20
+ options = [],
21
+ multiple = false,
22
+ placeholder,
23
+ triggerVariant,
24
+ disabled,
25
+ hasError,
26
+ searchType,
27
+ highlightStringMatches,
28
+ loading,
29
+ onSearch,
30
+ id,
31
+ 'aria-describedby': ariaDescribedBy,
32
+ 'aria-invalid': ariaInvalid,
33
+ 'aria-label': ariaLabel,
34
+ } = props;
35
+
36
+ const containerRef = useRef<HTMLDivElement>(null);
37
+
38
+ const normalizedValue: string[] = Array.isArray(value)
39
+ ? value.map(String)
40
+ : value != null && value !== ''
41
+ ? [String(value)]
42
+ : [];
43
+
44
+ useComponentDidMount(() => {
45
+ // open the combobox when the user presses Enter on the ag-grid cell
46
+ const handleCellKeyDown = (
47
+ event: CellKeyDownEvent | FullWidthCellKeyDownEvent,
48
+ ) => {
49
+ const { key } = event.event as KeyboardEvent;
50
+ if (
51
+ 'column' in event
52
+ && event.node === node
53
+ && event.column === column
54
+ && key === 'Enter'
55
+ ) {
56
+ const trigger = containerRef.current?.querySelector<HTMLElement>(
57
+ '[role="combobox"], [role="button"][aria-haspopup="listbox"]',
58
+ );
59
+ if (!trigger) return;
60
+ trigger.focus();
61
+ // button-style triggers (single-select) need an explicit click to open the popover
62
+ if (trigger.getAttribute('role') === 'button') {
63
+ trigger.click();
64
+ }
65
+ }
66
+ };
67
+
68
+ api.addEventListener('cellKeyDown', handleCellKeyDown);
69
+
70
+ // restore ag-grid cell focus when popover closes without focus leaving the cell
71
+ const container = containerRef.current;
72
+ let observer: MutationObserver | undefined;
73
+ if (container) {
74
+ const trigger = container.querySelector<HTMLElement>(
75
+ '[role="combobox"], [role="button"][aria-haspopup="listbox"]',
76
+ );
77
+ if (trigger) {
78
+ observer = new MutationObserver(() => {
79
+ const isOpen = trigger.getAttribute('aria-expanded') === 'true';
80
+ if (isOpen || column == null || node.rowIndex == null) return;
81
+ const active = document.activeElement;
82
+ const focusIsStillInCell = active == null || active === document.body || container.contains(active);
83
+ if (focusIsStillInCell) {
84
+ api.setFocusedCell(node.rowIndex, column);
85
+ }
86
+ });
87
+ observer.observe(trigger, { attributes: true, attributeFilter: ['aria-expanded'] });
88
+ }
89
+ }
90
+
91
+ return () => {
92
+ api.removeEventListener('cellKeyDown', handleCellKeyDown);
93
+ observer?.disconnect();
94
+ };
95
+ });
96
+
97
+ return (
98
+ <div ref={containerRef} className="ds-table__combobox">
99
+ <Combobox
100
+ options={options}
101
+ value={normalizedValue}
102
+ multiple={multiple}
103
+ placeholder={placeholder}
104
+ triggerVariant={triggerVariant}
105
+ disabled={disabled}
106
+ hasError={hasError}
107
+ searchType={searchType}
108
+ highlightStringMatches={highlightStringMatches}
109
+ loading={loading}
110
+ onSearch={onSearch}
111
+ id={id}
112
+ aria-describedby={ariaDescribedBy}
113
+ aria-invalid={ariaInvalid}
114
+ aria-label={ariaLabel}
115
+ onValueChange={(newValues) => {
116
+ if (column) {
117
+ node.setDataValue(column, multiple ? newValues : (newValues[0] ?? null));
118
+ }
119
+ }}
120
+ />
121
+ </div>
122
+ );
123
+ };
124
+
125
+ ComboboxCellRenderer.colDefDefaults = {
126
+ autoHeight: true,
127
+ suppressKeyboardEvent: (params: SuppressKeyboardEventParams) => {
128
+ const cell = (params.event.target as HTMLElement)?.closest('.ag-cell');
129
+ if (!cell) return false;
130
+ const openTrigger = cell.querySelector(
131
+ '[role="combobox"][aria-expanded="true"], [role="button"][aria-expanded="true"]',
132
+ );
133
+ return !!openTrigger && ['ArrowUp', 'ArrowDown', 'Enter', 'Escape'].includes(params.event.key);
134
+ },
135
+ };
@@ -47,6 +47,10 @@
47
47
  // so we just unset it here
48
48
  z-index: unset;
49
49
  }
50
+
51
+ .ag-cell:has(.ds-table__combobox) {
52
+ padding: var(--spacing-xsmall) var(--spacing-small);
53
+ }
50
54
  }
51
55
 
52
56
  &__controls {