@navikt/ds-react 6.12.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/alert/Alert.d.ts +4 -4
- 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/Modal.js +3 -2
- package/cjs/modal/Modal.js.map +1 -1
- package/cjs/modal/ModalUtils.js +3 -3
- package/cjs/modal/ModalUtils.js.map +1 -1
- package/cjs/modal/types.d.ts +5 -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/alert/Alert.d.ts +4 -4
- 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/Modal.js +3 -2
- package/esm/modal/Modal.js.map +1 -1
- package/esm/modal/types.d.ts +5 -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/alert/Alert.tsx +4 -4
- 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/modal/Modal.tsx +3 -1
- package/src/modal/types.ts +5 -0
- 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
|
@@ -1,11 +1,4 @@
|
|
|
1
|
-
import React, {
|
|
2
|
-
ChangeEvent,
|
|
3
|
-
ChangeEventHandler,
|
|
4
|
-
useCallback,
|
|
5
|
-
useMemo,
|
|
6
|
-
useRef,
|
|
7
|
-
useState,
|
|
8
|
-
} from "react";
|
|
1
|
+
import React, { useCallback, useMemo, useRef, useState } from "react";
|
|
9
2
|
import { createContext } from "../../../util/create-context";
|
|
10
3
|
import { useClientLayoutEffect } from "../../../util/hooks";
|
|
11
4
|
import { FormFieldType, useFormField } from "../../useFormField";
|
|
@@ -13,12 +6,12 @@ import { ComboboxProps } from "../types";
|
|
|
13
6
|
|
|
14
7
|
interface InputContextValue extends FormFieldType {
|
|
15
8
|
clearInput: NonNullable<ComboboxProps["onClear"]>;
|
|
16
|
-
error?:
|
|
9
|
+
error?: ComboboxProps["error"];
|
|
17
10
|
focusInput: () => void;
|
|
18
11
|
inputRef: React.RefObject<HTMLInputElement>;
|
|
19
12
|
value: string;
|
|
20
13
|
setValue: (text: string) => void;
|
|
21
|
-
onChange:
|
|
14
|
+
onChange: (newValue: string) => void;
|
|
22
15
|
searchTerm: string;
|
|
23
16
|
setSearchTerm: React.Dispatch<React.SetStateAction<string>>;
|
|
24
17
|
shouldAutocomplete?: boolean;
|
|
@@ -31,7 +24,24 @@ const [InputContextProvider, useInputContext] =
|
|
|
31
24
|
errorMessage: "useInputContext must be used within an InputContextProvider",
|
|
32
25
|
});
|
|
33
26
|
|
|
34
|
-
|
|
27
|
+
interface Props {
|
|
28
|
+
children: React.ReactNode;
|
|
29
|
+
value: {
|
|
30
|
+
defaultValue: ComboboxProps["defaultValue"];
|
|
31
|
+
description: ComboboxProps["description"];
|
|
32
|
+
disabled: ComboboxProps["disabled"];
|
|
33
|
+
error: ComboboxProps["error"];
|
|
34
|
+
errorId: ComboboxProps["errorId"];
|
|
35
|
+
id: ComboboxProps["id"];
|
|
36
|
+
value: ComboboxProps["value"];
|
|
37
|
+
onChange: ComboboxProps["onChange"];
|
|
38
|
+
onClear: ComboboxProps["onClear"];
|
|
39
|
+
shouldAutocomplete: ComboboxProps["shouldAutocomplete"];
|
|
40
|
+
size: ComboboxProps["size"];
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const InputProvider = ({ children, value: props }: Props) => {
|
|
35
45
|
const {
|
|
36
46
|
defaultValue = "",
|
|
37
47
|
description,
|
|
@@ -68,30 +78,22 @@ const InputProvider = ({ children, value: props }) => {
|
|
|
68
78
|
const [searchTerm, setSearchTerm] = useState(value);
|
|
69
79
|
|
|
70
80
|
const onChange = useCallback(
|
|
71
|
-
(
|
|
72
|
-
const newValue = event.currentTarget.value;
|
|
81
|
+
(newValue: string) => {
|
|
73
82
|
externalValue ?? setInternalValue(newValue);
|
|
74
|
-
externalOnChange?.(event);
|
|
75
83
|
setSearchTerm(newValue);
|
|
84
|
+
externalOnChange?.(newValue);
|
|
76
85
|
},
|
|
77
86
|
[externalValue, externalOnChange],
|
|
78
87
|
);
|
|
79
88
|
|
|
80
|
-
const setValue = useCallback(
|
|
81
|
-
(text) => {
|
|
82
|
-
setInternalValue(text);
|
|
83
|
-
},
|
|
84
|
-
[setInternalValue],
|
|
85
|
-
);
|
|
86
|
-
|
|
87
89
|
const clearInput = useCallback(
|
|
88
90
|
(event: React.PointerEvent | React.KeyboardEvent | React.MouseEvent) => {
|
|
89
91
|
onClear?.(event);
|
|
90
|
-
externalOnChange?.(
|
|
91
|
-
|
|
92
|
+
externalOnChange?.("");
|
|
93
|
+
setInternalValue("");
|
|
92
94
|
setSearchTerm("");
|
|
93
95
|
},
|
|
94
|
-
[externalOnChange, onClear,
|
|
96
|
+
[externalOnChange, onClear, setInternalValue],
|
|
95
97
|
);
|
|
96
98
|
|
|
97
99
|
const focusInput = useCallback(() => {
|
|
@@ -111,7 +113,7 @@ const InputProvider = ({ children, value: props }) => {
|
|
|
111
113
|
focusInput,
|
|
112
114
|
inputRef,
|
|
113
115
|
value,
|
|
114
|
-
setValue,
|
|
116
|
+
setValue: setInternalValue,
|
|
115
117
|
onChange,
|
|
116
118
|
searchTerm,
|
|
117
119
|
setSearchTerm,
|
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
import cl from "clsx";
|
|
2
2
|
import React, {
|
|
3
|
-
ChangeEvent,
|
|
4
3
|
InputHTMLAttributes,
|
|
5
4
|
forwardRef,
|
|
6
5
|
useCallback,
|
|
6
|
+
useRef,
|
|
7
7
|
} from "react";
|
|
8
8
|
import { omit } from "../../../util";
|
|
9
|
+
import { useMergeRefs } from "../../../util/hooks";
|
|
9
10
|
import filteredOptionsUtil from "../FilteredOptions/filtered-options-util";
|
|
10
11
|
import { useFilteredOptionsContext } from "../FilteredOptions/filteredOptionsContext";
|
|
11
12
|
import { useSelectedOptionsContext } from "../SelectedOptions/selectedOptionsContext";
|
|
12
13
|
import { useInputContext } from "./Input.context";
|
|
13
14
|
|
|
14
15
|
interface InputProps
|
|
15
|
-
extends Omit<InputHTMLAttributes<HTMLInputElement>, "value"> {
|
|
16
|
+
extends Omit<InputHTMLAttributes<HTMLInputElement>, "value" | "disabled"> {
|
|
16
17
|
ref: React.Ref<HTMLInputElement>;
|
|
17
18
|
inputClassName?: string;
|
|
18
19
|
value?: string;
|
|
@@ -20,7 +21,17 @@ interface InputProps
|
|
|
20
21
|
|
|
21
22
|
const Input = forwardRef<HTMLInputElement, InputProps>(
|
|
22
23
|
({ inputClassName, ...rest }, ref) => {
|
|
23
|
-
const
|
|
24
|
+
const internalRef = useRef<HTMLInputElement>(null);
|
|
25
|
+
const mergedRefs = useMergeRefs(ref, internalRef);
|
|
26
|
+
const {
|
|
27
|
+
clearInput,
|
|
28
|
+
inputProps,
|
|
29
|
+
onChange,
|
|
30
|
+
size,
|
|
31
|
+
value,
|
|
32
|
+
searchTerm,
|
|
33
|
+
setValue,
|
|
34
|
+
} = useInputContext();
|
|
24
35
|
const {
|
|
25
36
|
selectedOptions,
|
|
26
37
|
removeSelectedOption,
|
|
@@ -56,7 +67,7 @@ const Input = forwardRef<HTMLInputElement, InputProps>(
|
|
|
56
67
|
if (!isMultiSelect && !isTextInSelectedOptions(currentOption.label)) {
|
|
57
68
|
toggleIsListOpen(false);
|
|
58
69
|
}
|
|
59
|
-
} else if (
|
|
70
|
+
} else if (isTextInSelectedOptions(value)) {
|
|
60
71
|
event.preventDefault();
|
|
61
72
|
// Trying to set the same value that is already set, so just clearing the input
|
|
62
73
|
clearInput(event);
|
|
@@ -67,13 +78,13 @@ const Input = forwardRef<HTMLInputElement, InputProps>(
|
|
|
67
78
|
allowNewValues && isValueNew
|
|
68
79
|
? { label: value, value }
|
|
69
80
|
: filteredOptions[0];
|
|
81
|
+
|
|
82
|
+
if (!selectedValue) {
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
|
|
70
86
|
toggleOption(selectedValue, event);
|
|
71
|
-
if (
|
|
72
|
-
!isMultiSelect &&
|
|
73
|
-
!isTextInSelectedOptions(
|
|
74
|
-
filteredOptions[0].label || selectedValue.label,
|
|
75
|
-
)
|
|
76
|
-
) {
|
|
87
|
+
if (!isMultiSelect && !isTextInSelectedOptions(selectedValue.label)) {
|
|
77
88
|
toggleIsListOpen(false);
|
|
78
89
|
}
|
|
79
90
|
}
|
|
@@ -96,10 +107,6 @@ const Input = forwardRef<HTMLInputElement, InputProps>(
|
|
|
96
107
|
const handleKeyUp = (e: React.KeyboardEvent<HTMLInputElement>) => {
|
|
97
108
|
e.preventDefault();
|
|
98
109
|
switch (e.key) {
|
|
99
|
-
case "Escape":
|
|
100
|
-
clearInput(e);
|
|
101
|
-
toggleIsListOpen(false);
|
|
102
|
-
break;
|
|
103
110
|
case "Enter":
|
|
104
111
|
case "Accept":
|
|
105
112
|
onEnter(e);
|
|
@@ -118,7 +125,7 @@ const Input = forwardRef<HTMLInputElement, InputProps>(
|
|
|
118
125
|
};
|
|
119
126
|
|
|
120
127
|
const handleKeyDown = useCallback(
|
|
121
|
-
(e) => {
|
|
128
|
+
(e: React.KeyboardEvent<HTMLInputElement>) => {
|
|
122
129
|
setIsMouseLastUsedInputDevice(false);
|
|
123
130
|
if (e.key === "Backspace") {
|
|
124
131
|
if (value === "") {
|
|
@@ -132,17 +139,34 @@ const Input = forwardRef<HTMLInputElement, InputProps>(
|
|
|
132
139
|
if (activeDecendantId || value) {
|
|
133
140
|
e.preventDefault();
|
|
134
141
|
}
|
|
142
|
+
} else if (e.key === "Escape") {
|
|
143
|
+
if (isListOpen || value) {
|
|
144
|
+
e.preventDefault(); // Prevents closing an encasing Modal, as Combobox reacts on keyup.
|
|
145
|
+
clearInput(e);
|
|
146
|
+
toggleIsListOpen(false);
|
|
147
|
+
}
|
|
148
|
+
} else if (["ArrowLeft", "ArrowRight"].includes(e.key)) {
|
|
149
|
+
/**
|
|
150
|
+
* In case user has an active selection and 'completes' the selection with ArrowLeft or ArrowRight
|
|
151
|
+
* we need to make sure to update the filter.
|
|
152
|
+
*/
|
|
153
|
+
if (value !== "" && value !== searchTerm) {
|
|
154
|
+
onChange(value);
|
|
155
|
+
}
|
|
135
156
|
} else if (e.key === "ArrowDown") {
|
|
136
|
-
//
|
|
137
|
-
//
|
|
138
|
-
if (
|
|
139
|
-
|
|
140
|
-
if (virtualFocus.activeElement === null || !isListOpen) {
|
|
141
|
-
toggleIsListOpen(true);
|
|
142
|
-
}
|
|
143
|
-
virtualFocus.moveFocusDown();
|
|
157
|
+
// Reset the value to the search term to cancel autocomplete
|
|
158
|
+
// if the user moves focus down to the FilteredOptions
|
|
159
|
+
if (value !== searchTerm) {
|
|
160
|
+
setValue(searchTerm);
|
|
144
161
|
}
|
|
162
|
+
if (virtualFocus.activeElement === null || !isListOpen) {
|
|
163
|
+
toggleIsListOpen(true);
|
|
164
|
+
}
|
|
165
|
+
virtualFocus.moveFocusDown();
|
|
145
166
|
} else if (e.key === "ArrowUp") {
|
|
167
|
+
if (value !== "" && value !== searchTerm) {
|
|
168
|
+
onChange(value);
|
|
169
|
+
}
|
|
146
170
|
// Check that the FilteredOptions list is open and has virtual focus.
|
|
147
171
|
// Otherwise ignore keystrokes, so it doesn't interfere with text editing
|
|
148
172
|
if (isListOpen && activeDecendantId) {
|
|
@@ -161,13 +185,17 @@ const Input = forwardRef<HTMLInputElement, InputProps>(
|
|
|
161
185
|
isListOpen,
|
|
162
186
|
activeDecendantId,
|
|
163
187
|
setIsMouseLastUsedInputDevice,
|
|
188
|
+
clearInput,
|
|
164
189
|
toggleIsListOpen,
|
|
190
|
+
onChange,
|
|
165
191
|
virtualFocus,
|
|
192
|
+
setValue,
|
|
193
|
+
searchTerm,
|
|
166
194
|
],
|
|
167
195
|
);
|
|
168
196
|
|
|
169
197
|
const onChangeHandler = useCallback(
|
|
170
|
-
(event: ChangeEvent<HTMLInputElement>) => {
|
|
198
|
+
(event: React.ChangeEvent<HTMLInputElement>) => {
|
|
171
199
|
const newValue = event.target.value;
|
|
172
200
|
if (newValue && newValue !== "") {
|
|
173
201
|
toggleIsListOpen(true);
|
|
@@ -175,7 +203,7 @@ const Input = forwardRef<HTMLInputElement, InputProps>(
|
|
|
175
203
|
toggleIsListOpen(false);
|
|
176
204
|
}
|
|
177
205
|
virtualFocus.moveFocusToTop();
|
|
178
|
-
onChange(
|
|
206
|
+
onChange(newValue);
|
|
179
207
|
},
|
|
180
208
|
[filteredOptions.length, virtualFocus, onChange, toggleIsListOpen],
|
|
181
209
|
);
|
|
@@ -184,10 +212,11 @@ const Input = forwardRef<HTMLInputElement, InputProps>(
|
|
|
184
212
|
<input
|
|
185
213
|
{...rest}
|
|
186
214
|
{...omit(inputProps, ["aria-invalid"])}
|
|
187
|
-
ref={
|
|
215
|
+
ref={mergedRefs}
|
|
188
216
|
value={value}
|
|
189
217
|
onBlur={() => virtualFocus.moveFocusToTop()}
|
|
190
|
-
|
|
218
|
+
onClick={() => value !== searchTerm && onChange(value)}
|
|
219
|
+
onInput={onChangeHandler}
|
|
191
220
|
type="text"
|
|
192
221
|
role="combobox"
|
|
193
222
|
onKeyUp={handleKeyUp}
|
|
@@ -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}
|
package/src/modal/Modal.tsx
CHANGED
|
@@ -88,6 +88,7 @@ export const Modal = forwardRef<HTMLDialogElement, ModalProps>(
|
|
|
88
88
|
onCancel,
|
|
89
89
|
closeOnBackdropClick,
|
|
90
90
|
width,
|
|
91
|
+
placement,
|
|
91
92
|
portal,
|
|
92
93
|
className,
|
|
93
94
|
"aria-labelledby": ariaLabelledby,
|
|
@@ -147,9 +148,10 @@ export const Modal = forwardRef<HTMLDialogElement, ModalProps>(
|
|
|
147
148
|
typeof width === "string" && ["small", "medium"].includes(width);
|
|
148
149
|
|
|
149
150
|
const mergedClassName = cl("navds-modal", className, {
|
|
150
|
-
polyfillClassName: needPolyfill,
|
|
151
|
+
[polyfillClassName]: needPolyfill,
|
|
151
152
|
"navds-modal--autowidth": !width,
|
|
152
153
|
[`navds-modal--${width}`]: isWidthPreset,
|
|
154
|
+
"navds-modal--top": placement === "top" && !needPolyfill,
|
|
153
155
|
});
|
|
154
156
|
|
|
155
157
|
const mergedStyle = {
|
package/src/modal/types.ts
CHANGED
|
@@ -59,6 +59,11 @@ interface ModalPropsBase extends React.DialogHTMLAttributes<HTMLDialogElement> {
|
|
|
59
59
|
* @default fit-content (up to 700px)
|
|
60
60
|
* */
|
|
61
61
|
width?: "medium" | "small" | number | `${number}${string}`;
|
|
62
|
+
/**
|
|
63
|
+
* Where to place the modal in the viewport. (Will always be centered on mobile and old browsers.)
|
|
64
|
+
* @default "center"
|
|
65
|
+
*/
|
|
66
|
+
placement?: "top" | "center";
|
|
62
67
|
/**
|
|
63
68
|
* Lets you render the modal into a different part of the DOM.
|
|
64
69
|
* Will use `rootElement` from `Provider` if defined, otherwise `document.body`.
|