@navikt/ds-react 5.7.6 → 5.9.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 (164) hide show
  1. package/_docs.json +1824 -1758
  2. package/cjs/accordion/AccordionHeader.js +2 -2
  3. package/cjs/date/context/useDateInputContext.js +1 -5
  4. package/cjs/date/datepicker/DatePicker.js +26 -25
  5. package/cjs/date/hooks/useDatepicker.js +9 -17
  6. package/cjs/date/hooks/useMonthPicker.js +9 -17
  7. package/cjs/date/hooks/useRangeDatepicker.js +9 -20
  8. package/cjs/date/monthpicker/MonthPicker.js +11 -6
  9. package/cjs/date/{DateInput.js → parts/DateInput.js} +14 -10
  10. package/cjs/date/parts/DateWrapper.js +55 -0
  11. package/cjs/date/utils/labels.js +77 -1
  12. package/cjs/form/combobox/Combobox.js +2 -2
  13. package/cjs/form/combobox/ComboboxProvider.js +1 -2
  14. package/cjs/form/combobox/FilteredOptions/FilteredOptions.js +15 -14
  15. package/cjs/form/combobox/FilteredOptions/filtered-options-util.js +24 -0
  16. package/cjs/form/combobox/FilteredOptions/filteredOptionsContext.js +24 -108
  17. package/cjs/form/combobox/FilteredOptions/useVirtualFocus.js +55 -0
  18. package/cjs/form/combobox/Input/Input.js +33 -16
  19. package/cjs/form/combobox/customOptionsContext.js +2 -3
  20. package/cjs/layout/sidemal-test/Sidebar.js +1 -1
  21. package/cjs/loader/Loader.js +1 -1
  22. package/cjs/modal/Modal.js +39 -15
  23. package/cjs/popover/Popover.js +5 -7
  24. package/cjs/tooltip/Tooltip.js +14 -3
  25. package/cjs/util/useMedia.js +30 -0
  26. package/esm/accordion/AccordionHeader.js +2 -2
  27. package/esm/accordion/AccordionHeader.js.map +1 -1
  28. package/esm/date/context/useDateInputContext.d.ts +6 -2
  29. package/esm/date/context/useDateInputContext.js +1 -5
  30. package/esm/date/context/useDateInputContext.js.map +1 -1
  31. package/esm/date/datepicker/DatePicker.d.ts +1 -1
  32. package/esm/date/datepicker/DatePicker.js +28 -27
  33. package/esm/date/datepicker/DatePicker.js.map +1 -1
  34. package/esm/date/datepicker/types.d.ts +0 -5
  35. package/esm/date/hooks/useDatepicker.d.ts +8 -5
  36. package/esm/date/hooks/useDatepicker.js +10 -18
  37. package/esm/date/hooks/useDatepicker.js.map +1 -1
  38. package/esm/date/hooks/useMonthPicker.d.ts +7 -4
  39. package/esm/date/hooks/useMonthPicker.js +10 -18
  40. package/esm/date/hooks/useMonthPicker.js.map +1 -1
  41. package/esm/date/hooks/useRangeDatepicker.d.ts +9 -3
  42. package/esm/date/hooks/useRangeDatepicker.js +10 -21
  43. package/esm/date/hooks/useRangeDatepicker.js.map +1 -1
  44. package/esm/date/index.d.ts +1 -1
  45. package/esm/date/index.js.map +1 -1
  46. package/esm/date/monthpicker/MonthPicker.d.ts +1 -1
  47. package/esm/date/monthpicker/MonthPicker.js +13 -8
  48. package/esm/date/monthpicker/MonthPicker.js.map +1 -1
  49. package/esm/date/monthpicker/types.d.ts +0 -5
  50. package/esm/date/{DateInput.d.ts → parts/DateInput.d.ts} +5 -1
  51. package/esm/date/{DateInput.js → parts/DateInput.js} +15 -11
  52. package/esm/date/parts/DateInput.js.map +1 -0
  53. package/esm/date/parts/DateWrapper.d.ts +15 -0
  54. package/esm/date/parts/DateWrapper.js +26 -0
  55. package/esm/date/parts/DateWrapper.js.map +1 -0
  56. package/esm/date/utils/labels.d.ts +2 -0
  57. package/esm/date/utils/labels.js +74 -0
  58. package/esm/date/utils/labels.js.map +1 -1
  59. package/esm/form/combobox/Combobox.js +2 -2
  60. package/esm/form/combobox/Combobox.js.map +1 -1
  61. package/esm/form/combobox/ComboboxProvider.js +1 -2
  62. package/esm/form/combobox/ComboboxProvider.js.map +1 -1
  63. package/esm/form/combobox/FilteredOptions/FilteredOptions.js +15 -14
  64. package/esm/form/combobox/FilteredOptions/FilteredOptions.js.map +1 -1
  65. package/esm/form/combobox/FilteredOptions/filtered-options-util.d.ts +12 -0
  66. package/esm/form/combobox/FilteredOptions/filtered-options-util.js +23 -0
  67. package/esm/form/combobox/FilteredOptions/filtered-options-util.js.map +1 -0
  68. package/esm/form/combobox/FilteredOptions/filteredOptionsContext.d.ts +10 -13
  69. package/esm/form/combobox/FilteredOptions/filteredOptionsContext.js +25 -109
  70. package/esm/form/combobox/FilteredOptions/filteredOptionsContext.js.map +1 -1
  71. package/esm/form/combobox/FilteredOptions/useVirtualFocus.d.ts +15 -0
  72. package/esm/form/combobox/FilteredOptions/useVirtualFocus.js +54 -0
  73. package/esm/form/combobox/FilteredOptions/useVirtualFocus.js.map +1 -0
  74. package/esm/form/combobox/Input/Input.js +33 -16
  75. package/esm/form/combobox/Input/Input.js.map +1 -1
  76. package/esm/form/combobox/customOptionsContext.d.ts +4 -1
  77. package/esm/form/combobox/customOptionsContext.js +2 -3
  78. package/esm/form/combobox/customOptionsContext.js.map +1 -1
  79. package/esm/layout/bleed/Bleed.d.ts +1 -1
  80. package/esm/layout/bleed/Bleed.js +1 -1
  81. package/esm/layout/bleed/Bleed.js.map +1 -1
  82. package/esm/layout/box/Box.d.ts +1 -2
  83. package/esm/layout/box/Box.js +1 -1
  84. package/esm/layout/box/Box.js.map +1 -1
  85. package/esm/layout/grid/HGrid.d.ts +1 -1
  86. package/esm/layout/grid/HGrid.js +1 -1
  87. package/esm/layout/grid/HGrid.js.map +1 -1
  88. package/esm/layout/responsive/Responsive.d.ts +1 -1
  89. package/esm/layout/sidemal-test/Sidebar.js +1 -1
  90. package/esm/layout/sidemal-test/Sidebar.js.map +1 -1
  91. package/esm/layout/stack/Stack.d.ts +1 -1
  92. package/esm/layout/stack/Stack.js +1 -1
  93. package/esm/layout/stack/Stack.js.map +1 -1
  94. package/esm/layout/utilities/css.d.ts +1 -8
  95. package/esm/layout/utilities/css.js.map +1 -1
  96. package/esm/layout/utilities/types.d.ts +9 -0
  97. package/esm/loader/Loader.d.ts +1 -1
  98. package/esm/loader/Loader.js +1 -1
  99. package/esm/modal/Modal.js +39 -15
  100. package/esm/modal/Modal.js.map +1 -1
  101. package/esm/modal/ModalContext.d.ts +1 -0
  102. package/esm/modal/ModalContext.js.map +1 -1
  103. package/esm/modal/types.d.ts +7 -0
  104. package/esm/popover/Popover.d.ts +0 -5
  105. package/esm/popover/Popover.js +5 -7
  106. package/esm/popover/Popover.js.map +1 -1
  107. package/esm/tooltip/Tooltip.js +16 -5
  108. package/esm/tooltip/Tooltip.js.map +1 -1
  109. package/esm/util/useMedia.d.ts +8 -0
  110. package/esm/util/useMedia.js +27 -0
  111. package/esm/util/useMedia.js.map +1 -0
  112. package/package.json +3 -3
  113. package/src/accordion/AccordionHeader.tsx +3 -3
  114. package/src/date/context/useDateInputContext.tsx +5 -5
  115. package/src/date/datepicker/DatePicker.tsx +58 -65
  116. package/src/date/datepicker/datepicker.stories.tsx +37 -46
  117. package/src/date/datepicker/types.ts +0 -5
  118. package/src/date/hooks/useDatepicker.tsx +20 -25
  119. package/src/date/hooks/useMonthPicker.tsx +18 -24
  120. package/src/date/hooks/useRangeDatepicker.tsx +27 -30
  121. package/src/date/index.ts +1 -1
  122. package/src/date/monthpicker/MonthPicker.tsx +39 -43
  123. package/src/date/monthpicker/types.ts +0 -5
  124. package/src/date/{DateInput.tsx → parts/DateInput.tsx} +23 -12
  125. package/src/date/parts/DateWrapper.tsx +80 -0
  126. package/src/date/utils/labels.ts +83 -0
  127. package/src/form/combobox/Combobox.tsx +2 -2
  128. package/src/form/combobox/ComboboxProvider.tsx +1 -2
  129. package/src/form/combobox/FilteredOptions/FilteredOptions.tsx +28 -16
  130. package/src/form/combobox/FilteredOptions/filtered-options-util.ts +38 -0
  131. package/src/form/combobox/FilteredOptions/filteredOptionsContext.tsx +71 -142
  132. package/src/form/combobox/FilteredOptions/useVirtualFocus.ts +87 -0
  133. package/src/form/combobox/Input/Input.tsx +40 -21
  134. package/src/form/combobox/combobox.stories.tsx +44 -0
  135. package/src/form/combobox/customOptionsContext.tsx +10 -5
  136. package/src/guide-panel/guidepanel.stories.tsx +2 -2
  137. package/src/layout/bleed/Bleed.tsx +2 -5
  138. package/src/layout/box/Box.tsx +1 -3
  139. package/src/layout/grid/HGrid.tsx +2 -6
  140. package/src/layout/responsive/Responsive.tsx +1 -1
  141. package/src/layout/sidemal-test/Sidebar.tsx +1 -1
  142. package/src/layout/stack/Stack.tsx +2 -6
  143. package/src/layout/utilities/css.ts +1 -36
  144. package/src/layout/utilities/types.ts +16 -0
  145. package/src/loader/Loader.tsx +1 -1
  146. package/src/modal/Modal.tsx +54 -21
  147. package/src/modal/ModalContext.ts +1 -0
  148. package/src/modal/modal.stories.tsx +30 -2
  149. package/src/modal/types.ts +7 -0
  150. package/src/popover/Popover.tsx +4 -12
  151. package/src/tooltip/Tooltip.tsx +18 -6
  152. package/src/util/__tests__/useMedia.test.tsx +19 -0
  153. package/src/util/useMedia.ts +38 -0
  154. package/cjs/date/hooks/useEscape.js +0 -23
  155. package/cjs/date/hooks/useOutsideClickHandler.js +0 -26
  156. package/esm/date/DateInput.js.map +0 -1
  157. package/esm/date/hooks/useEscape.d.ts +0 -2
  158. package/esm/date/hooks/useEscape.js +0 -20
  159. package/esm/date/hooks/useEscape.js.map +0 -1
  160. package/esm/date/hooks/useOutsideClickHandler.d.ts +0 -1
  161. package/esm/date/hooks/useOutsideClickHandler.js +0 -23
  162. package/esm/date/hooks/useOutsideClickHandler.js.map +0 -1
  163. package/src/date/hooks/useEscape.tsx +0 -30
  164. package/src/date/hooks/useOutsideClickHandler.tsx +0 -34
@@ -78,7 +78,7 @@ const ComboboxProvider = (0, react_1.forwardRef)((props, ref) => {
78
78
  shouldAutocomplete,
79
79
  size,
80
80
  } },
81
- react_1.default.createElement(customOptionsContext_1.CustomOptionsProvider, null,
81
+ react_1.default.createElement(customOptionsContext_1.CustomOptionsProvider, { value: { isMultiSelect } },
82
82
  react_1.default.createElement(selectedOptionsContext_1.SelectedOptionsProvider, { value: {
83
83
  allowNewValues,
84
84
  isMultiSelect,
@@ -91,7 +91,6 @@ const ComboboxProvider = (0, react_1.forwardRef)((props, ref) => {
91
91
  filteredOptions,
92
92
  isListOpen,
93
93
  isLoading,
94
- isMultiSelect,
95
94
  options,
96
95
  } },
97
96
  react_1.default.createElement(Combobox_1.default, Object.assign({ ref: ref }, rest), children))))));
@@ -11,27 +11,28 @@ const selectedOptionsContext_1 = require("../SelectedOptions/selectedOptionsCont
11
11
  const inputContext_1 = require("../Input/inputContext");
12
12
  const loader_1 = require("../../../loader");
13
13
  const typography_1 = require("../../../typography");
14
+ const filtered_options_util_1 = __importDefault(require("./filtered-options-util"));
14
15
  const FilteredOptions = () => {
15
16
  const { inputProps: { id }, size, value, } = (0, inputContext_1.useInputContext)();
16
- const { allowNewValues, isLoading, isListOpen, filteredOptions, filteredOptionsIndex, filteredOptionsRef, isMouseLastUsedInputDevice, setIsMouseLastUsedInputDevice, isValueNew, setFilteredOptionsIndex, toggleIsListOpen, } = (0, filteredOptionsContext_1.useFilteredOptionsContext)();
17
+ const { allowNewValues, isLoading, isListOpen, filteredOptions, setFilteredOptionsRef, isMouseLastUsedInputDevice, setIsMouseLastUsedInputDevice, isValueNew, toggleIsListOpen, activeDecendantId, virtualFocus, } = (0, filteredOptionsContext_1.useFilteredOptionsContext)();
17
18
  const { isMultiSelect, selectedOptions, toggleOption } = (0, selectedOptionsContext_1.useSelectedOptionsContext)();
18
- return (react_1.default.createElement("ul", { ref: filteredOptionsRef, className: (0, clsx_1.default)("navds-combobox__list", {
19
+ return (react_1.default.createElement("ul", { ref: setFilteredOptionsRef, className: (0, clsx_1.default)("navds-combobox__list", {
19
20
  "navds-combobox__list--closed": !isListOpen,
20
21
  "navds-combobox__list--with-hover": isMouseLastUsedInputDevice,
21
- }), id: `${id}-filtered-options`, role: "listbox", tabIndex: -1 },
22
- isLoading && (react_1.default.createElement("li", { className: "navds-combobox__list-item--loading", role: "option", "aria-selected": false, id: `${id}-is-loading` },
22
+ }), id: filtered_options_util_1.default.getFilteredOptionsId(id), role: "listbox", tabIndex: -1 },
23
+ isLoading && (react_1.default.createElement("li", { className: "navds-combobox__list-item--loading", role: "option", "aria-selected": false, id: filtered_options_util_1.default.getIsLoadingId(id), "data-no-focus": "true" },
23
24
  react_1.default.createElement(loader_1.Loader, { "aria-label": "S\u00F8ker..." }))),
24
25
  isValueNew && allowNewValues && (react_1.default.createElement("li", { tabIndex: -1, onMouseMove: () => {
25
- if (filteredOptionsIndex !== -1) {
26
- setFilteredOptionsIndex(-1);
26
+ if (activeDecendantId !== filtered_options_util_1.default.getAddNewOptionId(id)) {
27
+ virtualFocus.moveFocusToElement(filtered_options_util_1.default.getAddNewOptionId(id));
27
28
  setIsMouseLastUsedInputDevice(true);
28
29
  }
29
30
  }, onPointerUp: (event) => {
30
31
  toggleOption(value, event);
31
32
  if (!isMultiSelect && !selectedOptions.includes(value))
32
33
  toggleIsListOpen(false);
33
- }, id: `${id}-combobox-new-option`, className: (0, clsx_1.default)("navds-combobox__list-item__new-option", {
34
- "navds-combobox__list-item__new-option--focus": filteredOptionsIndex === -1,
34
+ }, id: filtered_options_util_1.default.getAddNewOptionId(id), className: (0, clsx_1.default)("navds-combobox__list-item__new-option", {
35
+ "navds-combobox__list-item__new-option--focus": activeDecendantId === filtered_options_util_1.default.getAddNewOptionId(id),
35
36
  }), role: "option", "aria-selected": false },
36
37
  react_1.default.createElement(aksel_icons_1.PlusIcon, { "aria-hidden": true }),
37
38
  react_1.default.createElement(typography_1.BodyShort, { size: size },
@@ -41,13 +42,13 @@ const FilteredOptions = () => {
41
42
  "\u201C",
42
43
  value,
43
44
  "\u201D")))),
44
- !isLoading && filteredOptions.length === 0 && (react_1.default.createElement("li", { className: "navds-combobox__list-item__no-options", role: "option", "aria-selected": false, id: `${id}-no-hits` }, "Ingen s\u00F8ketreff")),
45
- filteredOptions.map((option, index) => (react_1.default.createElement("li", { className: (0, clsx_1.default)("navds-combobox__list-item", {
46
- "navds-combobox__list-item--focus": index === filteredOptionsIndex,
45
+ !isLoading && filteredOptions.length === 0 && (react_1.default.createElement("li", { className: "navds-combobox__list-item__no-options", role: "option", "aria-selected": false, id: filtered_options_util_1.default.getNoHitsId(id), "data-no-focus": "true" }, "Ingen s\u00F8ketreff")),
46
+ filteredOptions.map((option) => (react_1.default.createElement("li", { className: (0, clsx_1.default)("navds-combobox__list-item", {
47
+ "navds-combobox__list-item--focus": activeDecendantId === filtered_options_util_1.default.getOptionId(id, option),
47
48
  "navds-combobox__list-item--selected": selectedOptions.includes(option),
48
- }), id: `${id}-option-${option.replace(" ", "-")}`, key: option, tabIndex: -1, onMouseMove: () => {
49
- if (filteredOptionsIndex !== index) {
50
- setFilteredOptionsIndex(index);
49
+ }), id: filtered_options_util_1.default.getOptionId(id, option), key: option, tabIndex: -1, onMouseMove: () => {
50
+ if (activeDecendantId !== filtered_options_util_1.default.getOptionId(id, option)) {
51
+ virtualFocus.moveFocusToElement(filtered_options_util_1.default.getOptionId(id, option));
51
52
  setIsMouseLastUsedInputDevice(true);
52
53
  }
53
54
  }, onPointerUp: (event) => {
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const normalizeText = (text) => typeof text === "string" ? text.toLocaleLowerCase().trim() : "";
4
+ const isPartOfText = (value, text) => normalizeText(text).startsWith(normalizeText(value !== null && value !== void 0 ? value : ""));
5
+ const isValueInList = (value, list) => list === null || list === void 0 ? void 0 : list.find((listItem) => normalizeText(value) === normalizeText(listItem));
6
+ const getMatchingValuesFromList = (value, list) => list === null || list === void 0 ? void 0 : list.filter((listItem) => isPartOfText(value, listItem));
7
+ const getFilteredOptionsId = (comboboxId) => `${comboboxId}-filtered-options`;
8
+ const getOptionId = (comboboxId, option) => `${comboboxId.toLocaleLowerCase()}-option-${option
9
+ .replace(" ", "-")
10
+ .toLocaleLowerCase()}`;
11
+ const getAddNewOptionId = (comboboxId) => `${comboboxId}-combobox-new-option`;
12
+ const getIsLoadingId = (comboboxId) => `${comboboxId}-is-loading`;
13
+ const getNoHitsId = (comboboxId) => `${comboboxId}-no-hits`;
14
+ exports.default = {
15
+ normalizeText,
16
+ isPartOfText,
17
+ isValueInList,
18
+ getMatchingValuesFromList,
19
+ getFilteredOptionsId,
20
+ getAddNewOptionId,
21
+ getOptionId,
22
+ getIsLoadingId,
23
+ getNoHitsId,
24
+ };
@@ -33,16 +33,14 @@ const customOptionsContext_1 = require("../customOptionsContext");
33
33
  const inputContext_1 = require("../Input/inputContext");
34
34
  const usePrevious_1 = __importDefault(require("../../../util/usePrevious"));
35
35
  const util_1 = require("../../../util");
36
- const normalizeText = (text) => typeof text === "string" ? `${text}`.toLowerCase().trim() : "";
37
- const isPartOfText = (value, text) => normalizeText(text).startsWith(normalizeText(value !== null && value !== void 0 ? value : ""));
38
- const isValueInList = (value, list) => list === null || list === void 0 ? void 0 : list.find((listItem) => normalizeText(value) === normalizeText(listItem));
39
- const getMatchingValuesFromList = (value, list) => list === null || list === void 0 ? void 0 : list.filter((listItem) => isPartOfText(value, listItem));
36
+ const filtered_options_util_1 = __importDefault(require("./filtered-options-util"));
37
+ const useVirtualFocus_1 = __importDefault(require("./useVirtualFocus"));
40
38
  const FilteredOptionsContext = (0, react_1.createContext)({});
41
- const FilteredOptionsProvider = ({ children, value: props }) => {
39
+ const FilteredOptionsProvider = ({ children, value: props, }) => {
42
40
  const { allowNewValues, filteredOptions: externalFilteredOptions, isListOpen: isExternalListOpen, isLoading, options, } = props;
43
- const filteredOptionsRef = (0, react_1.useRef)(null);
41
+ const [filteredOptionsRef, setFilteredOptionsRef] = (0, react_1.useState)(null);
42
+ const virtualFocus = (0, useVirtualFocus_1.default)(filteredOptionsRef);
44
43
  const { inputProps: { "aria-describedby": partialAriaDescribedBy, id }, value, searchTerm, setValue, setSearchTerm, shouldAutocomplete, } = (0, inputContext_1.useInputContext)();
45
- const [filteredOptionsIndex, setFilteredOptionsIndex] = (0, react_1.useState)(null);
46
44
  const [isInternalListOpen, setInternalListOpen] = (0, react_1.useState)(false);
47
45
  const { customOptions } = (0, customOptionsContext_1.useCustomOptionsContext)();
48
46
  const filteredOptions = (0, react_1.useMemo)(() => {
@@ -50,17 +48,20 @@ const FilteredOptionsProvider = ({ children, value: props }) => {
50
48
  return externalFilteredOptions;
51
49
  }
52
50
  const opts = [...customOptions, ...options];
53
- setFilteredOptionsIndex(null);
54
- return getMatchingValuesFromList(searchTerm, opts);
51
+ return filtered_options_util_1.default.getMatchingValuesFromList(searchTerm, opts);
55
52
  }, [customOptions, externalFilteredOptions, options, searchTerm]);
56
53
  const previousSearchTerm = (0, usePrevious_1.default)(searchTerm);
57
54
  const [isMouseLastUsedInputDevice, setIsMouseLastUsedInputDevice] = (0, react_1.useState)(false);
55
+ const filteredOptionsMap = (0, react_1.useMemo)(() => options.reduce((map, _option) => (Object.assign(Object.assign({}, map), { [filtered_options_util_1.default.getOptionId(id, _option)]: _option })), {
56
+ [filtered_options_util_1.default.getAddNewOptionId(id)]: allowNewValues
57
+ ? value
58
+ : undefined,
59
+ }), [allowNewValues, id, options, value]);
58
60
  (0, util_1.useClientLayoutEffect)(() => {
59
61
  if (shouldAutocomplete &&
60
- normalizeText(searchTerm) !== "" &&
62
+ filtered_options_util_1.default.normalizeText(searchTerm) !== "" &&
61
63
  ((previousSearchTerm === null || previousSearchTerm === void 0 ? void 0 : previousSearchTerm.length) || 0) < searchTerm.length &&
62
- filteredOptions.length > 0 &&
63
- !isValueInList(searchTerm, filteredOptions)) {
64
+ filteredOptions.length > 0) {
64
65
  setValue(`${searchTerm}${filteredOptions[0].substring(searchTerm.length)}`);
65
66
  setSearchTerm(searchTerm);
66
67
  }
@@ -76,24 +77,22 @@ const FilteredOptionsProvider = ({ children, value: props }) => {
76
77
  return isExternalListOpen !== null && isExternalListOpen !== void 0 ? isExternalListOpen : isInternalListOpen;
77
78
  }, [isExternalListOpen, isInternalListOpen]);
78
79
  const toggleIsListOpen = (0, react_1.useCallback)((newState) => {
79
- setFilteredOptionsIndex(null);
80
+ virtualFocus.moveFocusToTop();
80
81
  setInternalListOpen((oldState) => newState !== null && newState !== void 0 ? newState : !oldState);
81
- }, []);
82
- const isValueNew = (0, react_1.useMemo)(() => Boolean(value) && !isValueInList(value, filteredOptions), [value, filteredOptions]);
83
- const getMinimumIndex = (0, react_1.useCallback)(() => {
84
- return isValueNew && allowNewValues ? -1 : 0;
85
- }, [allowNewValues, isValueNew]);
82
+ }, [virtualFocus]);
83
+ const isValueNew = (0, react_1.useMemo)(() => Boolean(value) &&
84
+ !filteredOptionsMap[filtered_options_util_1.default.getOptionId(id, value)], [filteredOptionsMap, id, value]);
86
85
  const ariaDescribedBy = (0, react_1.useMemo)(() => {
87
86
  let activeOption;
88
87
  if (!isLoading && filteredOptions.length === 0) {
89
- activeOption = `${id}-no-hits`;
88
+ activeOption = filtered_options_util_1.default.getNoHitsId(id);
90
89
  }
91
90
  else if ((value && value !== "") || isLoading) {
92
91
  if (shouldAutocomplete && filteredOptions[0]) {
93
- activeOption = `${id}-option-${filteredOptions[0].replace(" ", "-")}`;
92
+ activeOption = filtered_options_util_1.default.getOptionId(id, filteredOptions[0]);
94
93
  }
95
94
  else if (isListOpen && isLoading) {
96
- activeOption = `${id}-is-loading`;
95
+ activeOption = filtered_options_util_1.default.getIsLoadingId(id);
97
96
  }
98
97
  }
99
98
  return (0, clsx_1.default)(activeOption, partialAriaDescribedBy) || undefined;
@@ -106,91 +105,12 @@ const FilteredOptionsProvider = ({ children, value: props }) => {
106
105
  filteredOptions,
107
106
  id,
108
107
  ]);
109
- const currentOption = (0, react_1.useMemo)(() => {
110
- if (filteredOptionsIndex == null) {
111
- return null;
112
- }
113
- if (filteredOptionsIndex === -1) {
114
- return value;
115
- }
116
- return filteredOptions[filteredOptionsIndex];
117
- }, [filteredOptionsIndex, filteredOptions, value]);
118
- const resetFilteredOptionsIndex = () => {
119
- setFilteredOptionsIndex(getMinimumIndex());
120
- };
121
- const scrollToOption = (0, react_1.useCallback)((newIndex) => {
122
- if (filteredOptionsRef.current &&
123
- filteredOptionsRef.current.children[newIndex]) {
124
- const child = filteredOptionsRef.current.children[newIndex];
125
- const { top, bottom } = child.getBoundingClientRect();
126
- const parentRect = filteredOptionsRef.current.getBoundingClientRect();
127
- if (top < parentRect.top || bottom > parentRect.bottom) {
128
- child.scrollIntoView({ block: "nearest" });
129
- }
130
- }
131
- }, []);
132
- (0, react_1.useEffect)(() => {
133
- if (filteredOptionsIndex !== null && isListOpen) {
134
- scrollToOption(filteredOptionsIndex);
135
- }
136
- }, [filteredOptionsIndex, isListOpen, scrollToOption]);
137
- const moveFocusToInput = (0, react_1.useCallback)(() => {
138
- setFilteredOptionsIndex(null);
139
- toggleIsListOpen(false);
140
- }, [toggleIsListOpen]);
141
- const moveFocusToEnd = (0, react_1.useCallback)(() => {
142
- const lastIndex = filteredOptions.length - 1;
143
- toggleIsListOpen(true);
144
- setFilteredOptionsIndex(lastIndex);
145
- }, [filteredOptions.length, toggleIsListOpen]);
146
- const moveFocusUp = (0, react_1.useCallback)(() => {
147
- if (filteredOptionsIndex === null) {
148
- return;
149
- }
150
- if (filteredOptionsIndex === getMinimumIndex()) {
151
- toggleIsListOpen(false);
152
- setFilteredOptionsIndex(null);
153
- }
154
- else {
155
- const newIndex = Math.max(getMinimumIndex(), filteredOptionsIndex - 1);
156
- setFilteredOptionsIndex(newIndex);
157
- }
158
- }, [filteredOptionsIndex, getMinimumIndex, toggleIsListOpen]);
159
- const moveFocusDown = (0, react_1.useCallback)(() => {
160
- if (filteredOptionsIndex === null || !isListOpen) {
161
- toggleIsListOpen(true);
162
- if (allowNewValues || filteredOptions.length >= 1) {
163
- setFilteredOptionsIndex(getMinimumIndex());
164
- }
165
- return;
166
- }
167
- const newIndex = Math.min(filteredOptionsIndex + 1, Math.max(getMinimumIndex(), filteredOptions.length - 1));
168
- setFilteredOptionsIndex(newIndex);
169
- }, [
170
- allowNewValues,
171
- filteredOptions.length,
172
- filteredOptionsIndex,
173
- getMinimumIndex,
174
- isListOpen,
175
- toggleIsListOpen,
176
- ]);
177
- const activeDecendantId = (0, react_1.useMemo)(() => {
178
- if (filteredOptionsIndex === null) {
179
- return undefined;
180
- }
181
- else if (filteredOptionsIndex === -1) {
182
- return `${id}-combobox-new-option`;
183
- }
184
- else {
185
- return `${id}-option-${currentOption === null || currentOption === void 0 ? void 0 : currentOption.replace(" ", "-")}`;
186
- }
187
- }, [filteredOptionsIndex, currentOption, id]);
108
+ const currentOption = (0, react_1.useMemo)(() => { var _a; return filteredOptionsMap[((_a = virtualFocus.activeElement) === null || _a === void 0 ? void 0 : _a.getAttribute("id")) || -1]; }, [filteredOptionsMap, virtualFocus]);
109
+ const activeDecendantId = (0, react_1.useMemo)(() => { var _a; return ((_a = virtualFocus.activeElement) === null || _a === void 0 ? void 0 : _a.getAttribute("id")) || undefined; }, [virtualFocus.activeElement]);
188
110
  const filteredOptionsState = {
189
111
  activeDecendantId,
190
112
  allowNewValues,
191
- filteredOptionsRef,
192
- filteredOptionsIndex,
193
- setFilteredOptionsIndex,
113
+ setFilteredOptionsRef,
194
114
  shouldAutocomplete,
195
115
  isListOpen,
196
116
  isLoading,
@@ -200,11 +120,7 @@ const FilteredOptionsProvider = ({ children, value: props }) => {
200
120
  isValueNew,
201
121
  toggleIsListOpen,
202
122
  currentOption,
203
- resetFilteredOptionsIndex,
204
- moveFocusUp,
205
- moveFocusDown,
206
- moveFocusToInput,
207
- moveFocusToEnd,
123
+ virtualFocus,
208
124
  ariaDescribedBy,
209
125
  };
210
126
  return (react_1.default.createElement(FilteredOptionsContext.Provider, { value: filteredOptionsState }, children));
@@ -0,0 +1,55 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const react_1 = require("react");
4
+ const useVirtualFocus = (containerRef) => {
5
+ const [index, setIndex] = (0, react_1.useState)(-1);
6
+ const listOfAllChildren = (containerRef === null || containerRef === void 0 ? void 0 : containerRef.children)
7
+ ? Array.prototype.slice.call(containerRef === null || containerRef === void 0 ? void 0 : containerRef.children)
8
+ : [];
9
+ const elementsAbleToReceiveFocus = listOfAllChildren.filter((child) => child.getAttribute("data-no-focus") !== "true");
10
+ const activeElement = elementsAbleToReceiveFocus[index];
11
+ const getElementById = (id) => listOfAllChildren.find((element) => element.id === id);
12
+ const isFocusOnTheTop = index === 0;
13
+ const isFocusOnTheBottom = index === elementsAbleToReceiveFocus.length - 1;
14
+ const scrollToOption = (newIndex) => {
15
+ const indexOfElementToScrollTo = Math.min(Math.max(newIndex, 0), (containerRef === null || containerRef === void 0 ? void 0 : containerRef.children.length) || 0);
16
+ if (containerRef === null || containerRef === void 0 ? void 0 : containerRef.children[indexOfElementToScrollTo]) {
17
+ const child = containerRef.children[indexOfElementToScrollTo];
18
+ const { top, bottom } = child.getBoundingClientRect();
19
+ const parentRect = containerRef.getBoundingClientRect();
20
+ if (top < parentRect.top || bottom > parentRect.bottom) {
21
+ child.scrollIntoView({ block: "nearest" });
22
+ }
23
+ }
24
+ };
25
+ const _moveFocusAndScrollTo = (_index) => {
26
+ setIndex(_index);
27
+ scrollToOption(_index);
28
+ };
29
+ const moveFocusUp = () => _moveFocusAndScrollTo(Math.max(index - 1, -1));
30
+ const moveFocusDown = () => _moveFocusAndScrollTo(Math.min(index + 1, elementsAbleToReceiveFocus.length - 1));
31
+ const moveFocusToTop = () => _moveFocusAndScrollTo(-1);
32
+ const moveFocusToBottom = () => _moveFocusAndScrollTo(elementsAbleToReceiveFocus.length - 1);
33
+ const moveFocusToElement = (id) => {
34
+ const thisElement = elementsAbleToReceiveFocus.find((_element) => _element.getAttribute("id") === id);
35
+ const indexOfElement = thisElement
36
+ ? elementsAbleToReceiveFocus.indexOf(thisElement)
37
+ : -1;
38
+ if (indexOfElement >= 0) {
39
+ setIndex(indexOfElement);
40
+ }
41
+ };
42
+ return {
43
+ activeElement,
44
+ getElementById,
45
+ isFocusOnTheTop,
46
+ isFocusOnTheBottom,
47
+ setIndex,
48
+ moveFocusUp,
49
+ moveFocusDown,
50
+ moveFocusToElement,
51
+ moveFocusToTop,
52
+ moveFocusToBottom,
53
+ };
54
+ };
55
+ exports.default = useVirtualFocus;
@@ -43,20 +43,25 @@ const clsx_1 = __importDefault(require("clsx"));
43
43
  const selectedOptionsContext_1 = require("../SelectedOptions/selectedOptionsContext");
44
44
  const filteredOptionsContext_1 = require("../FilteredOptions/filteredOptionsContext");
45
45
  const inputContext_1 = require("./inputContext");
46
+ const filtered_options_util_1 = __importDefault(require("../FilteredOptions/filtered-options-util"));
46
47
  const Input = (0, react_1.forwardRef)((_a, ref) => {
47
48
  var { inputClassName } = _a, rest = __rest(_a, ["inputClassName"]);
48
49
  const { clearInput, inputProps, onChange, size, value } = (0, inputContext_1.useInputContext)();
49
50
  const { selectedOptions, removeSelectedOption, toggleOption, isMultiSelect, } = (0, selectedOptionsContext_1.useSelectedOptionsContext)();
50
- const { activeDecendantId, allowNewValues, currentOption, filteredOptions, toggleIsListOpen, isListOpen, filteredOptionsIndex, moveFocusUp, moveFocusDown, ariaDescribedBy, moveFocusToInput, moveFocusToEnd, setFilteredOptionsIndex, setIsMouseLastUsedInputDevice, shouldAutocomplete, } = (0, filteredOptionsContext_1.useFilteredOptionsContext)();
51
+ const { activeDecendantId, allowNewValues, currentOption, filteredOptions, isValueNew, toggleIsListOpen, isListOpen, ariaDescribedBy, setIsMouseLastUsedInputDevice, shouldAutocomplete, virtualFocus, } = (0, filteredOptionsContext_1.useFilteredOptionsContext)();
51
52
  const onEnter = (0, react_1.useCallback)((event) => {
53
+ const isTextInSelectedOptions = (text) => {
54
+ return selectedOptions.find((item) => item.toLocaleLowerCase() === text.toLocaleLowerCase());
55
+ };
52
56
  if (currentOption) {
53
57
  event.preventDefault();
54
58
  // Selecting a value from the dropdown / FilteredOptions
55
59
  toggleOption(currentOption, event);
56
- if (!isMultiSelect && !selectedOptions.includes(currentOption))
60
+ if (!isMultiSelect && !isTextInSelectedOptions(currentOption)) {
57
61
  toggleIsListOpen(false);
62
+ }
58
63
  }
59
- else if (shouldAutocomplete && selectedOptions.includes(value)) {
64
+ else if (shouldAutocomplete && isTextInSelectedOptions(value)) {
60
65
  event.preventDefault();
61
66
  // Trying to set the same value that is already set, so just clearing the input
62
67
  clearInput(event);
@@ -64,15 +69,20 @@ const Input = (0, react_1.forwardRef)((_a, ref) => {
64
69
  else if ((allowNewValues || shouldAutocomplete) && value !== "") {
65
70
  event.preventDefault();
66
71
  // Autocompleting or adding a new value
67
- toggleOption(value, event);
68
- if (!isMultiSelect && !selectedOptions.includes(value))
72
+ const selectedValue = allowNewValues && isValueNew ? value : filteredOptions[0];
73
+ toggleOption(selectedValue, event);
74
+ if (!isMultiSelect &&
75
+ !isTextInSelectedOptions(filteredOptions[0] || selectedValue)) {
69
76
  toggleIsListOpen(false);
77
+ }
70
78
  }
71
79
  }, [
72
80
  allowNewValues,
73
81
  clearInput,
74
82
  currentOption,
83
+ filteredOptions,
75
84
  isMultiSelect,
85
+ isValueNew,
76
86
  selectedOptions,
77
87
  shouldAutocomplete,
78
88
  toggleIsListOpen,
@@ -91,10 +101,10 @@ const Input = (0, react_1.forwardRef)((_a, ref) => {
91
101
  onEnter(e);
92
102
  break;
93
103
  case "Home":
94
- moveFocusToInput();
104
+ virtualFocus.moveFocusToTop();
95
105
  break;
96
106
  case "End":
97
- moveFocusToEnd();
107
+ virtualFocus.moveFocusToBottom();
98
108
  break;
99
109
  default:
100
110
  break;
@@ -113,26 +123,32 @@ const Input = (0, react_1.forwardRef)((_a, ref) => {
113
123
  // so we don't interfere with text editing
114
124
  if (e.target.selectionStart === (value === null || value === void 0 ? void 0 : value.length)) {
115
125
  e.preventDefault();
116
- moveFocusDown();
126
+ if (virtualFocus.activeElement === null || !isListOpen) {
127
+ toggleIsListOpen(true);
128
+ }
129
+ virtualFocus.moveFocusDown();
117
130
  }
118
131
  }
119
132
  else if (e.key === "ArrowUp") {
120
133
  // Check that the FilteredOptions list is open and has virtual focus.
121
134
  // Otherwise ignore keystrokes, so it doesn't interfere with text editing
122
- if (isListOpen && filteredOptionsIndex !== null) {
135
+ if (isListOpen && activeDecendantId) {
123
136
  e.preventDefault();
124
- moveFocusUp();
137
+ if (virtualFocus.isFocusOnTheTop) {
138
+ toggleIsListOpen(false);
139
+ }
140
+ virtualFocus.moveFocusUp();
125
141
  }
126
142
  }
127
143
  }, [
128
144
  value,
129
145
  selectedOptions,
130
146
  removeSelectedOption,
131
- moveFocusDown,
132
147
  isListOpen,
133
- filteredOptionsIndex,
134
- moveFocusUp,
148
+ activeDecendantId,
135
149
  setIsMouseLastUsedInputDevice,
150
+ toggleIsListOpen,
151
+ virtualFocus,
136
152
  ]);
137
153
  const onChangeHandler = (0, react_1.useCallback)((event) => {
138
154
  const newValue = event.target.value;
@@ -142,11 +158,12 @@ const Input = (0, react_1.forwardRef)((_a, ref) => {
142
158
  else if (filteredOptions.length === 0) {
143
159
  toggleIsListOpen(false);
144
160
  }
161
+ virtualFocus.moveFocusToTop();
145
162
  onChange(event);
146
- }, [filteredOptions.length, onChange, toggleIsListOpen]);
163
+ }, [filteredOptions.length, virtualFocus, onChange, toggleIsListOpen]);
147
164
  const onBlur = () => {
148
- setFilteredOptionsIndex(-1);
165
+ virtualFocus.moveFocusToTop();
149
166
  };
150
- return (react_1.default.createElement("input", Object.assign({}, rest, (0, util_1.omit)(inputProps, ["aria-invalid"]), { ref: ref, value: value, onChange: onChangeHandler, type: "text", role: "combobox", onBlur: onBlur, onKeyUp: handleKeyUp, onKeyDown: handleKeyDown, "aria-controls": `${inputProps.id}-filtered-options`, "aria-expanded": !!isListOpen, autoComplete: "off", "aria-autocomplete": shouldAutocomplete ? "both" : "list", "aria-activedescendant": activeDecendantId, "aria-describedby": ariaDescribedBy, "aria-invalid": inputProps["aria-invalid"], className: (0, clsx_1.default)(inputClassName, "navds-combobox__input", "navds-body-short", `navds-body-short--${size}`) })));
167
+ return (react_1.default.createElement("input", Object.assign({}, rest, (0, util_1.omit)(inputProps, ["aria-invalid"]), { ref: ref, value: value, onChange: onChangeHandler, type: "text", role: "combobox", onBlur: onBlur, onKeyUp: handleKeyUp, onKeyDown: handleKeyDown, "aria-controls": filtered_options_util_1.default.getFilteredOptionsId(inputProps.id), "aria-expanded": !!isListOpen, autoComplete: "off", "aria-autocomplete": shouldAutocomplete ? "both" : "list", "aria-activedescendant": activeDecendantId, "aria-describedby": ariaDescribedBy, "aria-invalid": inputProps["aria-invalid"], className: (0, clsx_1.default)(inputClassName, "navds-combobox__input", "navds-body-short", `navds-body-short--${size}`) })));
151
168
  });
152
169
  exports.default = Input;
@@ -26,12 +26,11 @@ Object.defineProperty(exports, "__esModule", { value: true });
26
26
  exports.useCustomOptionsContext = exports.CustomOptionsProvider = void 0;
27
27
  const react_1 = __importStar(require("react"));
28
28
  const inputContext_1 = require("./Input/inputContext");
29
- const selectedOptionsContext_1 = require("./SelectedOptions/selectedOptionsContext");
30
29
  const CustomOptionsContext = (0, react_1.createContext)({});
31
- const CustomOptionsProvider = ({ children }) => {
30
+ const CustomOptionsProvider = ({ children, value, }) => {
32
31
  const [customOptions, setCustomOptions] = (0, react_1.useState)([]);
33
32
  const { focusInput } = (0, inputContext_1.useInputContext)();
34
- const { isMultiSelect } = (0, selectedOptionsContext_1.useSelectedOptionsContext)();
33
+ const { isMultiSelect } = value;
35
34
  const removeCustomOption = (0, react_1.useCallback)((option) => {
36
35
  setCustomOptions((prevCustomOptions) => prevCustomOptions.filter((o) => o !== option));
37
36
  focusInput();
@@ -5,10 +5,10 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.NewsPanel = exports.ContentPanel = exports.Sidebar = void 0;
7
7
  const react_1 = __importDefault(require("react"));
8
+ const link_1 = require("../../link");
8
9
  const typography_1 = require("../../typography");
9
10
  const box_1 = require("../box");
10
11
  const stack_1 = require("../stack");
11
- const link_1 = require("../../link");
12
12
  const LinkElement = ({ children }) => {
13
13
  return (react_1.default.createElement(typography_1.Label, { as: "li", className: "sidebarlink" }, children));
14
14
  };
@@ -49,7 +49,7 @@ const util_1 = require("../util");
49
49
  *
50
50
  * @example
51
51
  * ```jsx
52
- * <Loader size="3xlarge" title="venter..." />
52
+ * <Loader size="3xlarge" title="Venter..." />
53
53
  * ```
54
54
  */
55
55
  exports.Loader = (0, react_1.forwardRef)((_a, ref) => {
@@ -42,6 +42,7 @@ const react_1 = require("@floating-ui/react");
42
42
  const clsx_1 = __importDefault(require("clsx"));
43
43
  const react_2 = __importStar(require("react"));
44
44
  const react_dom_1 = require("react-dom");
45
+ const context_1 = require("../date/context");
45
46
  const provider_1 = require("../provider");
46
47
  const typography_1 = require("../typography");
47
48
  const util_1 = require("../util");
@@ -99,13 +100,15 @@ const dialog_polyfill_1 = __importStar(require("./dialog-polyfill"));
99
100
  */
100
101
  exports.Modal = (0, react_2.forwardRef)((_a, ref) => {
101
102
  var _b, _c;
102
- var { header, children, open, onBeforeClose, onCancel, width, portal, className, "aria-labelledby": ariaLabelledby, style } = _a, rest = __rest(_a, ["header", "children", "open", "onBeforeClose", "onCancel", "width", "portal", "className", "aria-labelledby", "style"]);
103
+ var { header, children, open, onBeforeClose, onCancel, closeOnBackdropClick, width, portal, className, "aria-labelledby": ariaLabelledby, style, onClick } = _a, rest = __rest(_a, ["header", "children", "open", "onBeforeClose", "onCancel", "closeOnBackdropClick", "width", "portal", "className", "aria-labelledby", "style", "onClick"]);
103
104
  const modalRef = (0, react_2.useRef)(null);
104
105
  const mergedRef = (0, react_2.useMemo)(() => (0, util_1.mergeRefs)([modalRef, ref]), [ref]);
105
106
  const ariaLabelId = (0, util_1.useId)();
106
107
  const rootElement = (_b = (0, provider_1.useProvider)()) === null || _b === void 0 ? void 0 : _b.rootElement;
107
108
  const portalNode = (0, react_1.useFloatingPortalNode)({ root: rootElement });
108
- if ((0, react_2.useContext)(ModalContext_1.ModalContext)) {
109
+ const dateContext = (0, react_2.useContext)(context_1.DateContext);
110
+ const modalContext = (0, react_2.useContext)(ModalContext_1.ModalContext);
111
+ if (modalContext && !dateContext) {
109
112
  console.error("Modals should not be nested");
110
113
  }
111
114
  (0, react_2.useEffect)(() => {
@@ -115,6 +118,12 @@ exports.Modal = (0, react_2.forwardRef)((_a, ref) => {
115
118
  if (dialog_polyfill_1.needPolyfill && modalRef.current && portalNode) {
116
119
  dialog_polyfill_1.default.registerDialog(modalRef.current);
117
120
  }
121
+ // We set autofocus on the dialog element to prevent the default behavior where first focusable element gets focus when modal is opened.
122
+ // This is mainly to fix an edge case where having a Tooltip as the first focusable element would make it activate when you open the modal.
123
+ // We have to use JS because it doesn't work to set it with a prop (React bug?)
124
+ // Currently doesn't seem to work in Chrome. See also Tooltip.tsx
125
+ if (modalRef.current && portalNode)
126
+ modalRef.current.autofocus = true;
118
127
  }, [modalRef, portalNode]);
119
128
  (0, react_2.useEffect)(() => {
120
129
  // We need to have this in a useEffect so that the content renders before the modal is displayed,
@@ -131,22 +140,37 @@ exports.Modal = (0, react_2.forwardRef)((_a, ref) => {
131
140
  }, [modalRef, portalNode, open]);
132
141
  (0, ModalUtils_1.useBodyScrollLock)(modalRef, portalNode);
133
142
  const isWidthPreset = typeof width === "string" && ["small", "medium"].includes(width);
134
- const component = (react_2.default.createElement("dialog", Object.assign({}, rest, { ref: mergedRef, className: (0, clsx_1.default)("navds-modal", className, {
135
- "navds-modal--polyfilled": dialog_polyfill_1.needPolyfill,
136
- "navds-modal--autowidth": !width,
137
- [`navds-modal--${width}`]: isWidthPreset,
138
- }), style: Object.assign(Object.assign({}, style), (!isWidthPreset ? { width } : {})), onCancel: (event) => {
139
- // FYI: onCancel fires when you press Esc
140
- if (onBeforeClose && onBeforeClose() === false) {
141
- event.preventDefault();
143
+ const mergedClassName = (0, clsx_1.default)("navds-modal", className, {
144
+ "navds-modal--polyfilled": dialog_polyfill_1.needPolyfill,
145
+ "navds-modal--autowidth": !width,
146
+ [`navds-modal--${width}`]: isWidthPreset,
147
+ });
148
+ const mergedStyle = Object.assign(Object.assign({}, style), (!isWidthPreset ? { width } : {}));
149
+ const mergedOnCancel = (event) => {
150
+ if (onBeforeClose && onBeforeClose() === false) {
151
+ event.preventDefault();
152
+ }
153
+ else if (onCancel)
154
+ onCancel(event);
155
+ };
156
+ const mergedOnClick = closeOnBackdropClick && !dialog_polyfill_1.needPolyfill // closeOnBackdropClick has issues on polyfill when nesting modals (DatePicker)
157
+ ? (event) => {
158
+ onClick && onClick(event);
159
+ if (event.target === modalRef.current &&
160
+ (!onBeforeClose || onBeforeClose() !== false)) {
161
+ modalRef.current.close();
142
162
  }
143
- else if (onCancel)
144
- onCancel(event);
145
- }, "aria-labelledby": !ariaLabelledby && !rest["aria-label"] && header
146
- ? ariaLabelId
147
- : ariaLabelledby }),
163
+ }
164
+ : onClick;
165
+ const mergedAriaLabelledBy = !ariaLabelledby && !rest["aria-label"] && header
166
+ ? ariaLabelId
167
+ : ariaLabelledby;
168
+ const component = (
169
+ // eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-noninteractive-element-interactions
170
+ react_2.default.createElement("dialog", Object.assign({}, rest, { ref: mergedRef, className: mergedClassName, style: mergedStyle, onCancel: mergedOnCancel, onClick: mergedOnClick, "aria-labelledby": mergedAriaLabelledBy }),
148
171
  react_2.default.createElement(ModalContext_1.ModalContext.Provider, { value: {
149
172
  closeHandler: (0, ModalUtils_1.getCloseHandler)(modalRef, header, onBeforeClose),
173
+ ref: modalRef,
150
174
  } },
151
175
  header && (react_2.default.createElement(ModalHeader_1.default, null,
152
176
  header.label && (react_2.default.createElement(typography_1.Detail, { className: "navds-modal__label" }, header.label)),
@@ -41,6 +41,7 @@ exports.Popover = void 0;
41
41
  const react_1 = require("@floating-ui/react");
42
42
  const clsx_1 = __importDefault(require("clsx"));
43
43
  const react_2 = __importStar(require("react"));
44
+ const context_1 = require("../date/context");
44
45
  const ModalContext_1 = require("../modal/ModalContext");
45
46
  const util_1 = require("../util");
46
47
  const PopoverContent_1 = __importDefault(require("./PopoverContent"));
@@ -65,11 +66,12 @@ const PopoverContent_1 = __importDefault(require("./PopoverContent"));
65
66
  * ```
66
67
  */
67
68
  exports.Popover = (0, react_2.forwardRef)((_a, ref) => {
68
- var { className, children, anchorEl, arrow = true, open, onClose, placement = "top", offset, strategy: userStrategy, bubbleEscape = false, flip: _flip = true } = _a, rest = __rest(_a, ["className", "children", "anchorEl", "arrow", "open", "onClose", "placement", "offset", "strategy", "bubbleEscape", "flip"]);
69
+ var { className, children, anchorEl, arrow = true, open, onClose, placement = "top", offset, strategy: userStrategy, flip: _flip = true } = _a, rest = __rest(_a, ["className", "children", "anchorEl", "arrow", "open", "onClose", "placement", "offset", "strategy", "flip"]);
69
70
  const arrowRef = (0, react_2.useRef)(null);
70
71
  const isInModal = (0, react_2.useContext)(ModalContext_1.ModalContext) !== null;
72
+ const isInDatepicker = (0, react_2.useContext)(context_1.DateContext) !== null;
71
73
  const chosenStrategy = userStrategy !== null && userStrategy !== void 0 ? userStrategy : (isInModal ? "fixed" : "absolute");
72
- const chosenFlip = isInModal ? true : _flip;
74
+ const chosenFlip = isInDatepicker ? false : _flip;
73
75
  const { x, y, strategy, context, update, refs, placement: flPlacement, middlewareData: { arrow: { x: arrowX, y: arrowY } = {} }, } = (0, react_1.useFloating)({
74
76
  strategy: chosenStrategy,
75
77
  placement,
@@ -85,11 +87,7 @@ exports.Popover = (0, react_2.forwardRef)((_a, ref) => {
85
87
  });
86
88
  const { getFloatingProps } = (0, react_1.useInteractions)([
87
89
  (0, react_1.useClick)(context),
88
- (0, react_1.useDismiss)(context, {
89
- bubbles: {
90
- escapeKey: bubbleEscape,
91
- },
92
- }),
90
+ (0, react_1.useDismiss)(context),
93
91
  ]);
94
92
  (0, util_1.useClientLayoutEffect)(() => {
95
93
  refs.setReference(anchorEl);