@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.
- package/CHANGELOG.md +6 -0
- package/dist/components/combobox/useComboboxListboxKeyboard.d.ts.map +1 -1
- package/dist/components/combobox/useComboboxListboxKeyboard.js +2 -0
- package/dist/components/combobox/useComboboxListboxKeyboard.js.map +1 -1
- package/dist/components/combobox/useComboboxListboxKeyboard.test.js +1 -0
- package/dist/components/combobox/useComboboxListboxKeyboard.test.js.map +1 -1
- package/dist/components/table/Table.d.ts +7 -0
- package/dist/components/table/Table.d.ts.map +1 -1
- package/dist/components/table/Table.js +3 -0
- package/dist/components/table/Table.js.map +1 -1
- package/dist/components/table/Table.stories.d.ts +2 -0
- package/dist/components/table/Table.stories.d.ts.map +1 -1
- package/dist/components/table/Table.stories.js +148 -2
- package/dist/components/table/Table.stories.js.map +1 -1
- package/dist/components/table/cellRenderers/ComboboxCellRenderer.d.ts +13 -0
- package/dist/components/table/cellRenderers/ComboboxCellRenderer.d.ts.map +1 -0
- package/dist/components/table/cellRenderers/ComboboxCellRenderer.js +72 -0
- package/dist/components/table/cellRenderers/ComboboxCellRenderer.js.map +1 -0
- package/dist/components/table/cellRenderers/ComboboxCellRenderer.test.d.ts +2 -0
- package/dist/components/table/cellRenderers/ComboboxCellRenderer.test.d.ts.map +1 -0
- package/dist/components/table/cellRenderers/ComboboxCellRenderer.test.js +324 -0
- package/dist/components/table/cellRenderers/ComboboxCellRenderer.test.js.map +1 -0
- package/dist/index.css +3 -0
- package/dist/index.css.map +1 -1
- package/package.json +1 -1
- package/src/components/combobox/useComboboxListboxKeyboard.test.tsx +1 -0
- package/src/components/combobox/useComboboxListboxKeyboard.ts +2 -0
- package/src/components/table/Table.stories.tsx +180 -2
- package/src/components/table/Table.tsx +3 -0
- package/src/components/table/cellRenderers/ComboboxCellRenderer.test.tsx +437 -0
- package/src/components/table/cellRenderers/ComboboxCellRenderer.tsx +135 -0
- 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
|
+
};
|