@navikt/ds-react 6.13.0 → 6.14.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/cjs/accordion/AccordionContext.d.ts +0 -1
- package/cjs/collapsible/Collapsible.context.d.ts +0 -1
- package/cjs/date/context/useDateInputContext.d.ts +0 -1
- package/cjs/date/datepicker/parts/HeadRow.d.ts +0 -1
- package/cjs/date/datepicker/parts/HeadRow.js +2 -3
- package/cjs/date/datepicker/parts/HeadRow.js.map +1 -1
- package/cjs/date/datepicker/parts/Row.d.ts +0 -1
- package/cjs/date/datepicker/parts/TableHead.d.ts +0 -1
- package/cjs/date/datepicker/parts/WeekNumber.d.ts +0 -1
- package/cjs/date/datepicker/types.d.ts +0 -1
- package/cjs/date/monthpicker/types.d.ts +0 -1
- package/cjs/date/utils/check-dates.js +2 -2
- package/cjs/date/utils/check-dates.js.map +1 -1
- package/cjs/date/utils/get-initial-year.js +1 -2
- package/cjs/date/utils/get-initial-year.js.map +1 -1
- package/cjs/date/utils/get-month-weeks.js +2 -3
- package/cjs/date/utils/get-month-weeks.js.map +1 -1
- package/cjs/date/utils/is-match.js +2 -3
- package/cjs/date/utils/is-match.js.map +1 -1
- package/cjs/dropdown/Menu/index.d.ts +1 -1
- package/cjs/dropdown/context.d.ts +0 -1
- package/cjs/expansion-card/context.d.ts +0 -1
- package/cjs/form/checkbox/useCheckbox.d.ts +3 -3
- package/cjs/form/combobox/Combobox.d.ts +1 -1
- package/cjs/form/combobox/Combobox.js.map +1 -1
- package/cjs/form/combobox/ComboboxProvider.js +3 -1
- package/cjs/form/combobox/ComboboxProvider.js.map +1 -1
- package/cjs/form/combobox/FilteredOptions/filtered-options-util.d.ts +1 -0
- package/cjs/form/combobox/FilteredOptions/filtered-options-util.js +6 -1
- package/cjs/form/combobox/FilteredOptions/filtered-options-util.js.map +1 -1
- package/cjs/form/combobox/FilteredOptions/filteredOptionsContext.js +5 -5
- package/cjs/form/combobox/FilteredOptions/filteredOptionsContext.js.map +1 -1
- package/cjs/form/combobox/Input/Input.context.d.ts +20 -7
- package/cjs/form/combobox/Input/Input.context.js +6 -12
- package/cjs/form/combobox/Input/Input.context.js.map +1 -1
- package/cjs/form/combobox/Input/Input.d.ts +1 -1
- package/cjs/form/combobox/Input/Input.js +42 -18
- package/cjs/form/combobox/Input/Input.js.map +1 -1
- package/cjs/form/combobox/Input/InputController.d.ts +1 -1
- package/cjs/form/combobox/Input/InputController.js.map +1 -1
- package/cjs/form/combobox/types.d.ts +8 -4
- package/cjs/form/fieldset/context.d.ts +0 -1
- package/cjs/form/fieldset/useFieldset.d.ts +1 -1
- package/cjs/form/file-upload/FileUpload.context.d.ts +0 -1
- package/cjs/form/file-upload/parts/dropzone/dropzone.types.d.ts +0 -1
- package/cjs/form/file-upload/parts/item/utils/format-file-size.js +1 -2
- package/cjs/form/file-upload/parts/item/utils/format-file-size.js.map +1 -1
- package/cjs/form/file-upload/useFileUpload.d.ts +1 -2
- package/cjs/form/file-upload/utils/is-accepted-file-type.js +1 -2
- package/cjs/form/file-upload/utils/is-accepted-file-type.js.map +1 -1
- package/cjs/form/file-upload/utils/is-accepted-size.js +1 -2
- package/cjs/form/file-upload/utils/is-accepted-size.js.map +1 -1
- package/cjs/form/radio/useRadio.d.ts +3 -3
- package/cjs/form/search/context.d.ts +0 -1
- package/cjs/layout/base/PrimitiveAsChildProps.d.ts +0 -1
- package/cjs/layout/grid/HGrid.js +4 -1
- package/cjs/layout/grid/HGrid.js.map +1 -1
- package/cjs/layout/stack/Stack.js +7 -2
- package/cjs/layout/stack/Stack.js.map +1 -1
- package/cjs/layout/utilities/css.js +2 -3
- package/cjs/layout/utilities/css.js.map +1 -1
- package/cjs/list/context.d.ts +0 -1
- package/cjs/list/types.d.ts +0 -1
- package/cjs/modal/ModalUtils.js +3 -3
- package/cjs/modal/ModalUtils.js.map +1 -1
- package/cjs/modal/types.d.ts +0 -1
- package/cjs/overlays/dismissablelayer/DismissableLayer.d.ts +1 -1
- package/cjs/overlays/dismissablelayer/util/dispatchCustomEvent.js +2 -2
- package/cjs/overlays/dismissablelayer/util/dispatchCustomEvent.js.map +1 -1
- package/cjs/overlays/dismissablelayer/util/useEscapeKeydown.js +1 -2
- package/cjs/overlays/dismissablelayer/util/useEscapeKeydown.js.map +1 -1
- package/cjs/overlays/dismissablelayer/util/useFocusOutside.js +1 -2
- package/cjs/overlays/dismissablelayer/util/useFocusOutside.js.map +1 -1
- package/cjs/overlays/dismissablelayer/util/usePointerDownOutside.js +1 -2
- package/cjs/overlays/dismissablelayer/util/usePointerDownOutside.js.map +1 -1
- package/cjs/overlays/floating/Floating.utils.js +2 -3
- package/cjs/overlays/floating/Floating.utils.js.map +1 -1
- package/cjs/slot/merge-props.js +1 -2
- package/cjs/slot/merge-props.js.map +1 -1
- package/cjs/stepper/context.d.ts +0 -1
- package/cjs/table/context.d.ts +0 -1
- package/cjs/tabs/Tabs.context.d.ts +1 -2
- package/cjs/tabs/parts/tab/useTab.d.ts +1 -2
- package/cjs/tabs/parts/tab/useTab.js +1 -2
- package/cjs/tabs/parts/tab/useTab.js.map +1 -1
- package/cjs/tabs/parts/tablist/useScrollButtons.d.ts +0 -1
- package/cjs/tabs/parts/tablist/useScrollButtons.js +1 -2
- package/cjs/tabs/parts/tablist/useScrollButtons.js.map +1 -1
- package/cjs/tabs/parts/tablist/useTabList.js +3 -3
- package/cjs/tabs/parts/tablist/useTabList.js.map +1 -1
- package/cjs/tabs/parts/tabpanel/useTabPanel.js +1 -2
- package/cjs/tabs/parts/tabpanel/useTabPanel.js.map +1 -1
- package/cjs/tabs/useTabs.d.ts +0 -1
- package/cjs/tabs/useTabs.js +1 -2
- package/cjs/tabs/useTabs.js.map +1 -1
- package/cjs/timeline/hooks/usePeriodContext.d.ts +0 -1
- package/cjs/timeline/hooks/useRowContext.d.ts +0 -1
- package/cjs/timeline/hooks/useTimelineContext.d.ts +0 -1
- package/cjs/timeline/period/types.d.ts +0 -1
- package/cjs/timeline/zoom/index.d.ts +1 -1
- package/cjs/toggle-group/ToggleGroup.context.d.ts +1 -2
- package/cjs/toggle-group/parts/useToggleItem.d.ts +1 -2
- package/cjs/toggle-group/parts/useToggleItem.js +3 -3
- package/cjs/toggle-group/parts/useToggleItem.js.map +1 -1
- package/cjs/toggle-group/useToggleGroup.d.ts +0 -1
- package/cjs/toggle-group/useToggleGroup.js +1 -2
- package/cjs/toggle-group/useToggleGroup.js.map +1 -1
- package/cjs/util/composeEventHandlers.d.ts +0 -1
- package/cjs/util/composeEventHandlers.js +1 -2
- package/cjs/util/composeEventHandlers.js.map +1 -1
- package/cjs/util/copy.js +1 -1
- package/cjs/util/copy.js.map +1 -1
- package/cjs/util/create-context.js +1 -2
- package/cjs/util/create-context.js.map +1 -1
- package/cjs/util/debounce.js +1 -1
- package/cjs/util/debounce.js.map +1 -1
- package/cjs/util/hooks/descendants/useDescendant.d.ts +1 -1
- package/cjs/util/hooks/descendants/useDescendant.js +1 -2
- package/cjs/util/hooks/descendants/useDescendant.js.map +1 -1
- package/cjs/util/hooks/descendants/utils.js +4 -4
- package/cjs/util/hooks/descendants/utils.js.map +1 -1
- package/cjs/util/hooks/useCallbackRef.d.ts +0 -1
- package/cjs/util/hooks/useCallbackRef.js +1 -2
- package/cjs/util/hooks/useCallbackRef.js.map +1 -1
- package/cjs/util/hooks/useControllableState.js +1 -2
- package/cjs/util/hooks/useControllableState.js.map +1 -1
- package/cjs/util/hooks/useId.js +1 -2
- package/cjs/util/hooks/useId.js.map +1 -1
- package/cjs/util/hooks/useMergeRefs.d.ts +1 -1
- package/cjs/util/hooks/useMergeRefs.js +2 -3
- package/cjs/util/hooks/useMergeRefs.js.map +1 -1
- package/cjs/util/i18n/get.js +1 -2
- package/cjs/util/i18n/get.js.map +1 -1
- package/cjs/util/i18n/i18n.context.d.ts +0 -1
- package/cjs/util/i18n/i18n.context.js +2 -2
- package/cjs/util/i18n/i18n.context.js.map +1 -1
- package/cjs/util/i18n/merge.js +1 -2
- package/cjs/util/i18n/merge.js.map +1 -1
- package/cjs/util/omit.js +1 -2
- package/cjs/util/omit.js.map +1 -1
- package/cjs/util/types/AsChild.d.ts +0 -1
- package/cjs/util/types/AsChildProps.d.ts +0 -1
- package/cjs/util/virtualfocus/Context.d.ts +43 -0
- package/cjs/util/virtualfocus/Context.js +9 -0
- package/cjs/util/virtualfocus/Context.js.map +1 -0
- package/cjs/util/virtualfocus/SlottedDivElement.d.ts +7 -0
- package/cjs/util/virtualfocus/SlottedDivElement.js +46 -0
- package/cjs/util/virtualfocus/SlottedDivElement.js.map +1 -0
- package/cjs/util/virtualfocus/VirtualFocus.d.ts +62 -0
- package/cjs/util/virtualfocus/VirtualFocus.js +90 -0
- package/cjs/util/virtualfocus/VirtualFocus.js.map +1 -0
- package/cjs/util/virtualfocus/parts/VirtualFocusAnchor.d.ts +33 -0
- package/cjs/util/virtualfocus/parts/VirtualFocusAnchor.js +80 -0
- package/cjs/util/virtualfocus/parts/VirtualFocusAnchor.js.map +1 -0
- package/cjs/util/virtualfocus/parts/VirtualFocusContent.d.ts +4 -0
- package/cjs/util/virtualfocus/parts/VirtualFocusContent.js +45 -0
- package/cjs/util/virtualfocus/parts/VirtualFocusContent.js.map +1 -0
- package/cjs/util/virtualfocus/parts/VirtualFocusItem.d.ts +18 -0
- package/cjs/util/virtualfocus/parts/VirtualFocusItem.js +64 -0
- package/cjs/util/virtualfocus/parts/VirtualFocusItem.js.map +1 -0
- package/esm/accordion/AccordionContext.d.ts +0 -1
- package/esm/collapsible/Collapsible.context.d.ts +0 -1
- package/esm/date/context/useDateInputContext.d.ts +0 -1
- package/esm/date/datepicker/parts/HeadRow.d.ts +0 -1
- package/esm/date/datepicker/parts/Row.d.ts +0 -1
- package/esm/date/datepicker/parts/TableHead.d.ts +0 -1
- package/esm/date/datepicker/parts/WeekNumber.d.ts +0 -1
- package/esm/date/datepicker/types.d.ts +0 -1
- package/esm/date/monthpicker/types.d.ts +0 -1
- package/esm/dropdown/Menu/index.d.ts +1 -1
- package/esm/dropdown/context.d.ts +0 -1
- package/esm/expansion-card/context.d.ts +0 -1
- package/esm/form/checkbox/useCheckbox.d.ts +3 -3
- package/esm/form/combobox/Combobox.d.ts +1 -1
- package/esm/form/combobox/Combobox.js.map +1 -1
- package/esm/form/combobox/ComboboxProvider.js +3 -1
- package/esm/form/combobox/ComboboxProvider.js.map +1 -1
- package/esm/form/combobox/FilteredOptions/filtered-options-util.d.ts +1 -0
- package/esm/form/combobox/FilteredOptions/filtered-options-util.js +6 -1
- package/esm/form/combobox/FilteredOptions/filtered-options-util.js.map +1 -1
- package/esm/form/combobox/FilteredOptions/filteredOptionsContext.js +5 -5
- package/esm/form/combobox/FilteredOptions/filteredOptionsContext.js.map +1 -1
- package/esm/form/combobox/Input/Input.context.d.ts +20 -7
- package/esm/form/combobox/Input/Input.context.js +7 -13
- package/esm/form/combobox/Input/Input.context.js.map +1 -1
- package/esm/form/combobox/Input/Input.d.ts +1 -1
- package/esm/form/combobox/Input/Input.js +43 -19
- package/esm/form/combobox/Input/Input.js.map +1 -1
- package/esm/form/combobox/Input/InputController.d.ts +1 -1
- package/esm/form/combobox/Input/InputController.js.map +1 -1
- package/esm/form/combobox/types.d.ts +8 -4
- package/esm/form/fieldset/context.d.ts +0 -1
- package/esm/form/fieldset/useFieldset.d.ts +1 -1
- package/esm/form/file-upload/FileUpload.context.d.ts +0 -1
- package/esm/form/file-upload/parts/dropzone/dropzone.types.d.ts +0 -1
- package/esm/form/file-upload/useFileUpload.d.ts +1 -2
- package/esm/form/radio/useRadio.d.ts +3 -3
- package/esm/form/search/context.d.ts +0 -1
- package/esm/layout/base/PrimitiveAsChildProps.d.ts +0 -1
- package/esm/layout/grid/HGrid.js +4 -1
- package/esm/layout/grid/HGrid.js.map +1 -1
- package/esm/layout/stack/Stack.js +7 -2
- package/esm/layout/stack/Stack.js.map +1 -1
- package/esm/list/context.d.ts +0 -1
- package/esm/list/types.d.ts +0 -1
- package/esm/modal/types.d.ts +0 -1
- package/esm/overlays/dismissablelayer/DismissableLayer.d.ts +1 -1
- package/esm/stepper/context.d.ts +0 -1
- package/esm/table/context.d.ts +0 -1
- package/esm/tabs/Tabs.context.d.ts +1 -2
- package/esm/tabs/parts/tab/useTab.d.ts +1 -2
- package/esm/tabs/parts/tablist/useScrollButtons.d.ts +0 -1
- package/esm/tabs/parts/tablist/useTabList.js +2 -1
- package/esm/tabs/parts/tablist/useTabList.js.map +1 -1
- package/esm/tabs/useTabs.d.ts +0 -1
- package/esm/timeline/hooks/usePeriodContext.d.ts +0 -1
- package/esm/timeline/hooks/useRowContext.d.ts +0 -1
- package/esm/timeline/hooks/useTimelineContext.d.ts +0 -1
- package/esm/timeline/period/types.d.ts +0 -1
- package/esm/timeline/zoom/index.d.ts +1 -1
- package/esm/toggle-group/ToggleGroup.context.d.ts +1 -2
- package/esm/toggle-group/parts/useToggleItem.d.ts +1 -2
- package/esm/toggle-group/parts/useToggleItem.js +2 -1
- package/esm/toggle-group/parts/useToggleItem.js.map +1 -1
- package/esm/toggle-group/useToggleGroup.d.ts +0 -1
- package/esm/util/composeEventHandlers.d.ts +0 -1
- package/esm/util/hooks/descendants/useDescendant.d.ts +1 -1
- package/esm/util/hooks/useCallbackRef.d.ts +0 -1
- package/esm/util/hooks/useMergeRefs.d.ts +1 -1
- package/esm/util/i18n/i18n.context.d.ts +0 -1
- package/esm/util/types/AsChild.d.ts +0 -1
- package/esm/util/types/AsChildProps.d.ts +0 -1
- package/esm/util/virtualfocus/Context.d.ts +43 -0
- package/esm/util/virtualfocus/Context.js +5 -0
- package/esm/util/virtualfocus/Context.js.map +1 -0
- package/esm/util/virtualfocus/SlottedDivElement.d.ts +7 -0
- package/esm/util/virtualfocus/SlottedDivElement.js +20 -0
- package/esm/util/virtualfocus/SlottedDivElement.js.map +1 -0
- package/esm/util/virtualfocus/VirtualFocus.d.ts +62 -0
- package/esm/util/virtualfocus/VirtualFocus.js +63 -0
- package/esm/util/virtualfocus/VirtualFocus.js.map +1 -0
- package/esm/util/virtualfocus/parts/VirtualFocusAnchor.d.ts +33 -0
- package/esm/util/virtualfocus/parts/VirtualFocusAnchor.js +54 -0
- package/esm/util/virtualfocus/parts/VirtualFocusAnchor.js.map +1 -0
- package/esm/util/virtualfocus/parts/VirtualFocusContent.d.ts +4 -0
- package/esm/util/virtualfocus/parts/VirtualFocusContent.js +19 -0
- package/esm/util/virtualfocus/parts/VirtualFocusContent.js.map +1 -0
- package/esm/util/virtualfocus/parts/VirtualFocusItem.d.ts +18 -0
- package/esm/util/virtualfocus/parts/VirtualFocusItem.js +38 -0
- package/esm/util/virtualfocus/parts/VirtualFocusItem.js.map +1 -0
- package/package.json +3 -3
- package/src/form/combobox/Combobox.tsx +4 -1
- package/src/form/combobox/ComboboxProvider.tsx +3 -0
- package/src/form/combobox/FilteredOptions/filtered-options-util.ts +9 -1
- package/src/form/combobox/FilteredOptions/filteredOptionsContext.tsx +8 -5
- package/src/form/combobox/Input/Input.context.tsx +27 -25
- package/src/form/combobox/Input/Input.tsx +56 -27
- package/src/form/combobox/Input/InputController.tsx +1 -0
- package/src/form/combobox/__tests__/combobox.test.tsx +174 -66
- package/src/form/combobox/types.ts +11 -7
- package/src/layout/grid/HGrid.tsx +4 -1
- package/src/layout/stack/Stack.tsx +6 -2
- package/src/tabs/parts/tablist/useTabList.ts +4 -1
- package/src/toggle-group/parts/useToggleItem.ts +4 -1
- package/src/util/virtualfocus/Context.tsx +27 -0
- package/src/util/virtualfocus/SlottedDivElement.tsx +17 -0
- package/src/util/virtualfocus/VirtualFocus.tsx +89 -0
- package/src/util/virtualfocus/parts/VirtualFocusAnchor.tsx +102 -0
- package/src/util/virtualfocus/parts/VirtualFocusContent.tsx +17 -0
- package/src/util/virtualfocus/parts/VirtualFocusItem.tsx +60 -0
|
@@ -8,6 +8,7 @@ import { UNSAFE_Combobox } from "../index";
|
|
|
8
8
|
const options = [
|
|
9
9
|
"banana",
|
|
10
10
|
"apple",
|
|
11
|
+
"apple pie",
|
|
11
12
|
"tangerine",
|
|
12
13
|
"pear",
|
|
13
14
|
"grape",
|
|
@@ -71,86 +72,193 @@ describe("Render combobox", () => {
|
|
|
71
72
|
});
|
|
72
73
|
});
|
|
73
74
|
|
|
74
|
-
|
|
75
|
-
|
|
75
|
+
describe("Combobox state-handling", () => {
|
|
76
|
+
test("Should show loading icon when loading (used for async search)", async () => {
|
|
77
|
+
render(<App options={[]} isListOpen isLoading />);
|
|
76
78
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
});
|
|
79
|
+
expect(await screen.findByText("Søker...")).toBeInTheDocument();
|
|
80
|
+
});
|
|
80
81
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
render(<App options={options} />);
|
|
82
|
+
test("Should not select previous focused element when closes", async () => {
|
|
83
|
+
render(<App options={options} />);
|
|
84
84
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
85
|
+
await act(async () => {
|
|
86
|
+
await userEvent.click(
|
|
87
|
+
screen.getByRole("combobox", {
|
|
88
|
+
name: "Hva er dine favorittfrukter?",
|
|
89
|
+
}),
|
|
90
|
+
);
|
|
91
|
+
});
|
|
92
|
+
await act(async () => {
|
|
93
|
+
await userEvent.type(
|
|
94
|
+
screen.getByRole("combobox", {
|
|
95
|
+
name: "Hva er dine favorittfrukter?",
|
|
96
|
+
}),
|
|
97
|
+
"ban",
|
|
98
|
+
);
|
|
99
|
+
await userEvent.keyboard("{ArrowDown}");
|
|
100
|
+
await userEvent.keyboard("{ArrowUp}");
|
|
101
|
+
await userEvent.keyboard("{Enter}");
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
expect(screen.queryByRole("button", { name: "banana slett" })).toBeNull();
|
|
98
105
|
});
|
|
99
106
|
|
|
100
|
-
|
|
101
|
-
|
|
107
|
+
test("Should reset list when resetting input (ESC)", async () => {
|
|
108
|
+
render(<App options={options} />);
|
|
102
109
|
|
|
103
|
-
|
|
104
|
-
|
|
110
|
+
await act(async () => {
|
|
111
|
+
await userEvent.click(
|
|
112
|
+
screen.getByRole("combobox", {
|
|
113
|
+
name: "Hva er dine favorittfrukter?",
|
|
114
|
+
}),
|
|
115
|
+
);
|
|
116
|
+
});
|
|
117
|
+
await act(async () => {
|
|
118
|
+
await userEvent.type(
|
|
119
|
+
screen.getByRole("combobox", {
|
|
120
|
+
name: "Hva er dine favorittfrukter?",
|
|
121
|
+
}),
|
|
122
|
+
"apple",
|
|
123
|
+
);
|
|
124
|
+
await userEvent.keyboard("{ArrowDown}");
|
|
125
|
+
await userEvent.keyboard("{Escape}");
|
|
126
|
+
await userEvent.keyboard("{ArrowDown}");
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
expect(
|
|
130
|
+
await screen.findByRole("option", { name: "banana" }),
|
|
131
|
+
).toBeInTheDocument();
|
|
132
|
+
});
|
|
105
133
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
134
|
+
test("Should handle complex options with label and value", async () => {
|
|
135
|
+
const onToggleSelected = vi.fn();
|
|
136
|
+
render(
|
|
137
|
+
<App
|
|
138
|
+
options={[
|
|
139
|
+
{ label: "Hjelpemidler [HJE]", value: "HJE" },
|
|
140
|
+
{ label: "Oppfølging [OPP]", value: "OPP" },
|
|
141
|
+
{ label: "Sykepenger [SYK]", value: "SYK" },
|
|
142
|
+
{ label: "Sykemelding [SYM]", value: "SYM" },
|
|
143
|
+
]}
|
|
144
|
+
onToggleSelected={onToggleSelected}
|
|
145
|
+
/>,
|
|
109
146
|
);
|
|
147
|
+
|
|
148
|
+
expect(screen.getByRole("combobox")).toBeInTheDocument();
|
|
149
|
+
const bananaOption = screen.getByRole("option", {
|
|
150
|
+
name: "Hjelpemidler [HJE]",
|
|
151
|
+
selected: false,
|
|
152
|
+
});
|
|
153
|
+
await act(async () => {
|
|
154
|
+
await userEvent.click(bananaOption);
|
|
155
|
+
});
|
|
156
|
+
expect(onToggleSelected).toHaveBeenCalledWith("HJE", true, false);
|
|
157
|
+
expect(
|
|
158
|
+
screen.getByRole("option", {
|
|
159
|
+
name: "Hjelpemidler [HJE]",
|
|
160
|
+
selected: true,
|
|
161
|
+
}),
|
|
162
|
+
).toBeInTheDocument();
|
|
110
163
|
});
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
164
|
+
|
|
165
|
+
test("should trigger onChange for every character typed or removed", async () => {
|
|
166
|
+
const onChange = vi.fn();
|
|
167
|
+
const onToggleSelected = vi.fn();
|
|
168
|
+
render(
|
|
169
|
+
<App
|
|
170
|
+
options={["Apple", "Orange", "Banana", "Lemon"]}
|
|
171
|
+
onChange={onChange}
|
|
172
|
+
onToggleSelected={onToggleSelected}
|
|
173
|
+
shouldAutocomplete
|
|
174
|
+
/>,
|
|
115
175
|
);
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
176
|
+
const combobox = screen.getByRole("combobox");
|
|
177
|
+
expect(combobox).toBeInTheDocument();
|
|
178
|
+
|
|
179
|
+
await act(async () => {
|
|
180
|
+
await userEvent.click(combobox);
|
|
181
|
+
await userEvent.type(combobox, "Lemon");
|
|
182
|
+
});
|
|
183
|
+
expect(onChange).toHaveBeenNthCalledWith(1, "L");
|
|
184
|
+
expect(onChange).toHaveBeenNthCalledWith(2, "Le");
|
|
185
|
+
expect(onChange).toHaveBeenNthCalledWith(3, "Lem");
|
|
186
|
+
expect(onChange).toHaveBeenNthCalledWith(4, "Lemo");
|
|
187
|
+
expect(onChange).toHaveBeenNthCalledWith(5, "Lemon");
|
|
119
188
|
});
|
|
120
189
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
190
|
+
test("should trigger onChange while typing and on accepting autocomplete suggestions", async () => {
|
|
191
|
+
const onChange = vi.fn();
|
|
192
|
+
const onToggleSelected = vi.fn();
|
|
193
|
+
render(
|
|
194
|
+
<App
|
|
195
|
+
options={[
|
|
196
|
+
"Hjelpemidler [HJE]",
|
|
197
|
+
"Oppfølging [OPP]",
|
|
198
|
+
"Sykepenger [SYK]",
|
|
199
|
+
"Sykemelding [SYM]",
|
|
200
|
+
]}
|
|
201
|
+
onChange={onChange}
|
|
202
|
+
onToggleSelected={onToggleSelected}
|
|
203
|
+
shouldAutocomplete
|
|
204
|
+
/>,
|
|
205
|
+
);
|
|
206
|
+
const combobox = screen.getByRole("combobox");
|
|
207
|
+
expect(combobox).toBeInTheDocument();
|
|
125
208
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
209
|
+
await act(async () => {
|
|
210
|
+
await userEvent.click(combobox);
|
|
211
|
+
await userEvent.type(combobox, "Syke");
|
|
212
|
+
await userEvent.keyboard("{ArrowRight}");
|
|
213
|
+
await userEvent.keyboard("{ArrowDown}");
|
|
214
|
+
await userEvent.keyboard("{Enter}");
|
|
215
|
+
});
|
|
216
|
+
expect(onChange).toHaveBeenNthCalledWith(1, "S");
|
|
217
|
+
expect(onChange).toHaveBeenNthCalledWith(2, "Sy");
|
|
218
|
+
expect(onChange).toHaveBeenNthCalledWith(3, "Syk");
|
|
219
|
+
expect(onChange).toHaveBeenNthCalledWith(4, "Syke");
|
|
220
|
+
expect(onChange).toHaveBeenNthCalledWith(5, "Sykepenger [SYK]");
|
|
221
|
+
expect(onChange).toHaveBeenCalledWith("");
|
|
222
|
+
expect(onToggleSelected).toHaveBeenCalledOnce();
|
|
223
|
+
expect(onToggleSelected).toHaveBeenCalledWith(
|
|
224
|
+
"Sykepenger [SYK]",
|
|
225
|
+
true,
|
|
226
|
+
false,
|
|
227
|
+
);
|
|
144
228
|
});
|
|
145
|
-
|
|
146
|
-
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
describe("search", () => {
|
|
232
|
+
test("should find matched anywhere in the label", async () => {
|
|
233
|
+
render(<App options={options} />);
|
|
234
|
+
|
|
235
|
+
const combobox = screen.getByRole("combobox", {
|
|
236
|
+
name: "Hva er dine favorittfrukter?",
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
await act(async () => {
|
|
240
|
+
await userEvent.click(combobox);
|
|
241
|
+
|
|
242
|
+
await userEvent.type(combobox, "p");
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
const searchHits = [
|
|
246
|
+
"apple",
|
|
247
|
+
"apple pie",
|
|
248
|
+
"pear",
|
|
249
|
+
"grape",
|
|
250
|
+
"passion fruit",
|
|
251
|
+
"pineapple",
|
|
252
|
+
"grape fruit",
|
|
253
|
+
];
|
|
254
|
+
searchHits.forEach((label) => {
|
|
255
|
+
expect(screen.getByRole("option", { name: label })).toBeInTheDocument();
|
|
256
|
+
});
|
|
257
|
+
screen.getAllByRole("option").forEach((option) => {
|
|
258
|
+
expect(
|
|
259
|
+
option.textContent && searchHits.includes(option.textContent),
|
|
260
|
+
).toBe(true);
|
|
261
|
+
});
|
|
147
262
|
});
|
|
148
|
-
expect(onToggleSelected).toHaveBeenCalledWith("HJE", true, false);
|
|
149
|
-
expect(
|
|
150
|
-
screen.getByRole("option", {
|
|
151
|
-
name: "Hjelpemidler [HJE]",
|
|
152
|
-
selected: true,
|
|
153
|
-
}),
|
|
154
|
-
).toBeInTheDocument();
|
|
155
263
|
});
|
|
156
264
|
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, {
|
|
1
|
+
import React, { InputHTMLAttributes } from "react";
|
|
2
2
|
import { FormFieldProps } from "../useFormField";
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -29,7 +29,10 @@ export type MaxSelected = {
|
|
|
29
29
|
|
|
30
30
|
export interface ComboboxProps
|
|
31
31
|
extends FormFieldProps,
|
|
32
|
-
Omit<
|
|
32
|
+
Omit<
|
|
33
|
+
InputHTMLAttributes<HTMLInputElement>,
|
|
34
|
+
"size" | "onChange" | "value" | "defaultValue"
|
|
35
|
+
> {
|
|
33
36
|
/**
|
|
34
37
|
* Combobox label.
|
|
35
38
|
*/
|
|
@@ -89,12 +92,9 @@ export interface ComboboxProps
|
|
|
89
92
|
/**
|
|
90
93
|
* Callback function triggered whenever the value of the input field is triggered.
|
|
91
94
|
*
|
|
92
|
-
* @param
|
|
95
|
+
* @param value The value after change
|
|
93
96
|
*/
|
|
94
|
-
onChange?: (
|
|
95
|
-
event: ChangeEvent<HTMLInputElement> | null,
|
|
96
|
-
value?: string,
|
|
97
|
-
) => void;
|
|
97
|
+
onChange?: (value: string) => void;
|
|
98
98
|
/**
|
|
99
99
|
* Callback function triggered whenever the input field is cleared.
|
|
100
100
|
*
|
|
@@ -156,4 +156,8 @@ export interface ComboboxProps
|
|
|
156
156
|
* This converts the input to a controlled input, so you have to use onChange to update the value.
|
|
157
157
|
*/
|
|
158
158
|
value?: string;
|
|
159
|
+
/**
|
|
160
|
+
* Initial value of the input field. Only works when the input is uncontrolled.
|
|
161
|
+
*/
|
|
162
|
+
defaultValue?: string;
|
|
159
163
|
}
|
|
@@ -92,7 +92,10 @@ export const HGrid: OverridableComponent<HGridProps, HTMLDivElement> =
|
|
|
92
92
|
<Comp
|
|
93
93
|
{...omit(rest, PRIMITIVE_PROPS)}
|
|
94
94
|
ref={ref}
|
|
95
|
-
className={cl("navds-hgrid", className
|
|
95
|
+
className={cl("navds-hgrid", className, {
|
|
96
|
+
"navds-hgrid-gap": gap,
|
|
97
|
+
"navds-hgrid-align": align,
|
|
98
|
+
})}
|
|
96
99
|
style={styles}
|
|
97
100
|
>
|
|
98
101
|
{children}
|
|
@@ -73,7 +73,7 @@ export const Stack: OverridableComponent<StackProps, HTMLDivElement> =
|
|
|
73
73
|
children,
|
|
74
74
|
className,
|
|
75
75
|
as: Component = "div",
|
|
76
|
-
align
|
|
76
|
+
align,
|
|
77
77
|
justify,
|
|
78
78
|
wrap = true,
|
|
79
79
|
gap,
|
|
@@ -86,7 +86,6 @@ export const Stack: OverridableComponent<StackProps, HTMLDivElement> =
|
|
|
86
86
|
) => {
|
|
87
87
|
const style: React.CSSProperties = {
|
|
88
88
|
..._style,
|
|
89
|
-
"--__ac-stack-wrap": wrap ? "wrap" : "nowrap",
|
|
90
89
|
...getResponsiveProps(`stack`, "gap", "spacing", gap),
|
|
91
90
|
...getResponsiveValue(`stack`, "direction", direction),
|
|
92
91
|
...getResponsiveValue(`stack`, "align", align),
|
|
@@ -104,6 +103,11 @@ export const Stack: OverridableComponent<StackProps, HTMLDivElement> =
|
|
|
104
103
|
className={cl("navds-stack", className, {
|
|
105
104
|
"navds-vstack": direction === "column",
|
|
106
105
|
"navds-hstack": direction === "row",
|
|
106
|
+
"navds-stack-gap": gap,
|
|
107
|
+
"navds-stack-align": align,
|
|
108
|
+
"navds-stack-justify": justify,
|
|
109
|
+
"navds-stack-direction": direction,
|
|
110
|
+
"navds-stack-wrap": wrap,
|
|
107
111
|
})}
|
|
108
112
|
>
|
|
109
113
|
{children}
|
|
@@ -48,9 +48,12 @@ export function useTabList() {
|
|
|
48
48
|
End: lastTab,
|
|
49
49
|
};
|
|
50
50
|
|
|
51
|
+
const hasModifiers =
|
|
52
|
+
event.shiftKey || event.ctrlKey || event.altKey || event.metaKey;
|
|
53
|
+
|
|
51
54
|
const action = keyMap[event.key];
|
|
52
55
|
|
|
53
|
-
if (action) {
|
|
56
|
+
if (action && !hasModifiers) {
|
|
54
57
|
event.preventDefault();
|
|
55
58
|
action(event);
|
|
56
59
|
} else if (event.key === "Tab") {
|
|
@@ -77,9 +77,12 @@ export function useToggleItem<P extends UseToggleItemProps>(
|
|
|
77
77
|
End: lastTab,
|
|
78
78
|
};
|
|
79
79
|
|
|
80
|
+
const hasModifiers =
|
|
81
|
+
event.shiftKey || event.ctrlKey || event.altKey || event.metaKey;
|
|
82
|
+
|
|
80
83
|
const action = keyMap[event.key];
|
|
81
84
|
|
|
82
|
-
if (action) {
|
|
85
|
+
if (action && !hasModifiers) {
|
|
83
86
|
event.preventDefault();
|
|
84
87
|
action(event);
|
|
85
88
|
} else if (event.key === "Tab") {
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Dispatch, SetStateAction } from "react";
|
|
2
|
+
import { createContext } from "../create-context";
|
|
3
|
+
import { createDescendantContext } from "../hooks/descendants/useDescendant";
|
|
4
|
+
import { SlottedDivElementRef } from "./SlottedDivElement";
|
|
5
|
+
|
|
6
|
+
export const [
|
|
7
|
+
VirtualFocusDescendantsProvider,
|
|
8
|
+
useVirtualFocusDescendantsContext,
|
|
9
|
+
useVirtualFocusDescendantInitializer,
|
|
10
|
+
useVirtualFocusDescendant,
|
|
11
|
+
] = createDescendantContext<
|
|
12
|
+
SlottedDivElementRef,
|
|
13
|
+
{
|
|
14
|
+
handleOnSelect: () => void;
|
|
15
|
+
handleOnActive: () => void;
|
|
16
|
+
}
|
|
17
|
+
>();
|
|
18
|
+
|
|
19
|
+
export const [
|
|
20
|
+
VirtualFocusInternalContextProvider,
|
|
21
|
+
useVirtualFocusInternalContext,
|
|
22
|
+
] = createContext<{
|
|
23
|
+
virtualFocusIdx: number;
|
|
24
|
+
setVirtualFocusIdx: Dispatch<SetStateAction<number>>;
|
|
25
|
+
loop: boolean;
|
|
26
|
+
uniqueId: string;
|
|
27
|
+
}>();
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import React, { forwardRef } from "react";
|
|
2
|
+
import { Slot } from "../../slot/Slot";
|
|
3
|
+
|
|
4
|
+
interface SlottedDivProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
5
|
+
asChild?: boolean;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const SlottedDivElement = forwardRef<HTMLDivElement, SlottedDivProps>(
|
|
9
|
+
({ asChild, ...rest }, forwardedRef) => {
|
|
10
|
+
const Comp = asChild ? Slot : "div";
|
|
11
|
+
return <Comp {...rest} ref={forwardedRef} />;
|
|
12
|
+
},
|
|
13
|
+
);
|
|
14
|
+
|
|
15
|
+
type SlottedDivElementRef = React.ElementRef<typeof SlottedDivElement>;
|
|
16
|
+
|
|
17
|
+
export { SlottedDivElement, type SlottedDivElementRef, type SlottedDivProps };
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import React, { useState } from "react";
|
|
2
|
+
import { useId } from "../../util/hooks";
|
|
3
|
+
import {
|
|
4
|
+
VirtualFocusDescendantsProvider,
|
|
5
|
+
VirtualFocusInternalContextProvider,
|
|
6
|
+
useVirtualFocusDescendantInitializer,
|
|
7
|
+
} from "./Context";
|
|
8
|
+
import { VirtualFocusAnchor } from "./parts/VirtualFocusAnchor";
|
|
9
|
+
import { VirtualFocusContent } from "./parts/VirtualFocusContent";
|
|
10
|
+
import { VirtualFocusItem } from "./parts/VirtualFocusItem";
|
|
11
|
+
|
|
12
|
+
type VirtualFocusProps = {
|
|
13
|
+
children: React.ReactNode;
|
|
14
|
+
/**
|
|
15
|
+
* Whether to cause focus to loop around when it hits the first or last element
|
|
16
|
+
* @default false
|
|
17
|
+
**/
|
|
18
|
+
loop?: boolean;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* A component that manages a virtual focus using the 'up' and 'down'
|
|
23
|
+
* arrow keys as well as selection with 'Return'.
|
|
24
|
+
*
|
|
25
|
+
* @see [📝 Documentation](https://aksel.nav.no/komponenter/core/virtualfocus)
|
|
26
|
+
* @see 🏷️ {@link AccordionProps}
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* ```jsx
|
|
30
|
+
* <VirtualFocus>
|
|
31
|
+
* <VirtualFocus.Anchor
|
|
32
|
+
* role="combobox"
|
|
33
|
+
* onSelect={() => {
|
|
34
|
+
* console.log("you selected the anchor");
|
|
35
|
+
* }}
|
|
36
|
+
* onActive={() => {
|
|
37
|
+
* console.log("the anchor is now virtually focused");
|
|
38
|
+
* }}
|
|
39
|
+
* >
|
|
40
|
+
* <input type="text" />
|
|
41
|
+
* </VirtualFocus.Anchor>
|
|
42
|
+
* <VirtualFocus.Content>
|
|
43
|
+
* <VirtualFocus.Item
|
|
44
|
+
* onSelect={() => {
|
|
45
|
+
* console.log("you selected the item");
|
|
46
|
+
* }}
|
|
47
|
+
* onActive={() => {
|
|
48
|
+
* console.log("the item is now virtually focused");
|
|
49
|
+
* }}
|
|
50
|
+
* >
|
|
51
|
+
* <p>item 1</p>
|
|
52
|
+
* </VirtualFocus.Item>
|
|
53
|
+
* <VirtualFocus.Item
|
|
54
|
+
* onSelect={() => {
|
|
55
|
+
* console.log("you selected the item");
|
|
56
|
+
* }}
|
|
57
|
+
* onActive={() => {
|
|
58
|
+
* console.log("the item is now virtually focused");
|
|
59
|
+
* }}
|
|
60
|
+
* >
|
|
61
|
+
* <p>item 2</p>
|
|
62
|
+
* </VirtualFocus.Item>
|
|
63
|
+
* </VirtualFocus.Content>
|
|
64
|
+
* </VirtualFocus>
|
|
65
|
+
* ```
|
|
66
|
+
*/
|
|
67
|
+
export const VirtualFocus = ({ children, loop = false }: VirtualFocusProps) => {
|
|
68
|
+
const descendants = useVirtualFocusDescendantInitializer();
|
|
69
|
+
const [virtualFocusIdx, setVirtualFocusIdx] = useState(0);
|
|
70
|
+
|
|
71
|
+
return (
|
|
72
|
+
<VirtualFocusInternalContextProvider
|
|
73
|
+
virtualFocusIdx={virtualFocusIdx}
|
|
74
|
+
setVirtualFocusIdx={setVirtualFocusIdx}
|
|
75
|
+
loop={loop}
|
|
76
|
+
uniqueId={useId()}
|
|
77
|
+
>
|
|
78
|
+
<VirtualFocusDescendantsProvider value={descendants}>
|
|
79
|
+
{children}
|
|
80
|
+
</VirtualFocusDescendantsProvider>
|
|
81
|
+
</VirtualFocusInternalContextProvider>
|
|
82
|
+
);
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
VirtualFocus.Anchor = VirtualFocusAnchor;
|
|
86
|
+
VirtualFocus.Item = VirtualFocusItem;
|
|
87
|
+
VirtualFocus.Content = VirtualFocusContent;
|
|
88
|
+
|
|
89
|
+
export default VirtualFocus;
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import React, { forwardRef } from "react";
|
|
2
|
+
import { Slot } from "../../../slot/Slot";
|
|
3
|
+
import { composeEventHandlers } from "../../composeEventHandlers";
|
|
4
|
+
import { useMergeRefs } from "../../hooks";
|
|
5
|
+
import {
|
|
6
|
+
useVirtualFocusDescendant,
|
|
7
|
+
useVirtualFocusInternalContext,
|
|
8
|
+
} from "../Context";
|
|
9
|
+
|
|
10
|
+
export interface VirtualFocusAnchorProps
|
|
11
|
+
extends Omit<React.HTMLAttributes<HTMLDivElement>, "id"> {
|
|
12
|
+
/**
|
|
13
|
+
* The role of the container. This is a limited subset of roles that
|
|
14
|
+
* require manual focus management.
|
|
15
|
+
*
|
|
16
|
+
* Children that are to get focus inside this container element shall be
|
|
17
|
+
* pointed to by `aria-activedescendant`.
|
|
18
|
+
**/
|
|
19
|
+
role:
|
|
20
|
+
| "combobox"
|
|
21
|
+
| "grid"
|
|
22
|
+
| "listbox"
|
|
23
|
+
| "menu"
|
|
24
|
+
| "menubar"
|
|
25
|
+
| "radiogroup"
|
|
26
|
+
| "tree"
|
|
27
|
+
| "treegrid"
|
|
28
|
+
| "tablist";
|
|
29
|
+
/**
|
|
30
|
+
* The function that is run when the focused element
|
|
31
|
+
* is to be selected (eg. do an actual search, change route... etc)
|
|
32
|
+
*/
|
|
33
|
+
onSelect: () => void;
|
|
34
|
+
/**
|
|
35
|
+
* The function that is run when the element gets
|
|
36
|
+
* virtual focus set to it.
|
|
37
|
+
*/
|
|
38
|
+
onActive: () => void;
|
|
39
|
+
children: React.ReactElement;
|
|
40
|
+
/**
|
|
41
|
+
* Set this to `0` if you want the Anchor itself
|
|
42
|
+
* to be focusable. Since this Anchor is hoisted & merged with
|
|
43
|
+
* its first child, you most likely want to keep this as `0`.
|
|
44
|
+
* @default 0
|
|
45
|
+
*/
|
|
46
|
+
tabIndex?: number;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Must have a single child that is an input element.
|
|
51
|
+
*/
|
|
52
|
+
export const VirtualFocusAnchor = forwardRef<
|
|
53
|
+
HTMLInputElement,
|
|
54
|
+
VirtualFocusAnchorProps
|
|
55
|
+
>(({ onSelect, onActive, children, ...rest }, ref) => {
|
|
56
|
+
const { virtualFocusIdx, setVirtualFocusIdx, loop, uniqueId } =
|
|
57
|
+
useVirtualFocusInternalContext();
|
|
58
|
+
|
|
59
|
+
const { register, descendants, index } = useVirtualFocusDescendant({
|
|
60
|
+
handleOnActive: () => {
|
|
61
|
+
setVirtualFocusIdx(0);
|
|
62
|
+
onActive();
|
|
63
|
+
},
|
|
64
|
+
handleOnSelect: onSelect,
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
const mergedRefs = useMergeRefs(ref, register);
|
|
68
|
+
|
|
69
|
+
return (
|
|
70
|
+
<Slot
|
|
71
|
+
ref={mergedRefs}
|
|
72
|
+
tabIndex={0}
|
|
73
|
+
{...rest}
|
|
74
|
+
id={`virtualfocus-${uniqueId}-${index}`}
|
|
75
|
+
aria-owns={`virtualfocus-${uniqueId}-content`}
|
|
76
|
+
aria-controls={`virtualfocus-${uniqueId}-content`}
|
|
77
|
+
aria-activedescendant={`virtualfocus-${uniqueId}-${virtualFocusIdx}`}
|
|
78
|
+
onKeyDown={composeEventHandlers(rest.onKeyDown, (event) => {
|
|
79
|
+
if (event.key === "ArrowDown") {
|
|
80
|
+
event.preventDefault();
|
|
81
|
+
const to_focus_descendant = descendants.next(virtualFocusIdx, loop);
|
|
82
|
+
if (to_focus_descendant) {
|
|
83
|
+
to_focus_descendant.handleOnActive();
|
|
84
|
+
}
|
|
85
|
+
} else if (event.key === "ArrowUp") {
|
|
86
|
+
event.preventDefault();
|
|
87
|
+
const to_focus_descendant = descendants.prev(virtualFocusIdx, loop);
|
|
88
|
+
if (to_focus_descendant) {
|
|
89
|
+
to_focus_descendant.handleOnActive();
|
|
90
|
+
}
|
|
91
|
+
} else if (event.key === "Enter") {
|
|
92
|
+
const curr = descendants.item(virtualFocusIdx);
|
|
93
|
+
if (curr?.handleOnSelect) {
|
|
94
|
+
curr.handleOnSelect();
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
})}
|
|
98
|
+
>
|
|
99
|
+
{children}
|
|
100
|
+
</Slot>
|
|
101
|
+
);
|
|
102
|
+
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import React, { forwardRef } from "react";
|
|
2
|
+
import { useVirtualFocusInternalContext } from "../Context";
|
|
3
|
+
|
|
4
|
+
export interface VirtualFocusContentProps
|
|
5
|
+
extends Omit<React.HTMLAttributes<HTMLDivElement>, "id"> {}
|
|
6
|
+
|
|
7
|
+
export const VirtualFocusContent = forwardRef<
|
|
8
|
+
HTMLDivElement,
|
|
9
|
+
VirtualFocusContentProps
|
|
10
|
+
>(({ children, ...rest }, ref) => {
|
|
11
|
+
const { uniqueId } = useVirtualFocusInternalContext();
|
|
12
|
+
return (
|
|
13
|
+
<div ref={ref} {...rest} id={`virtualfocus-${uniqueId}-content`}>
|
|
14
|
+
{children}
|
|
15
|
+
</div>
|
|
16
|
+
);
|
|
17
|
+
});
|