@navikt/ds-react 7.8.0 → 7.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 (262) hide show
  1. package/cjs/accordion/AccordionContent.js +2 -2
  2. package/cjs/accordion/AccordionContent.js.map +1 -1
  3. package/cjs/accordion/AccordionHeader.js +2 -2
  4. package/cjs/accordion/AccordionHeader.js.map +1 -1
  5. package/cjs/button/Button.js +9 -25
  6. package/cjs/button/Button.js.map +1 -1
  7. package/cjs/date/datepicker/parts/WeekNumber.js +2 -2
  8. package/cjs/date/datepicker/parts/WeekNumber.js.map +1 -1
  9. package/cjs/date/parts/DateInput.js +1 -1
  10. package/cjs/date/parts/DateInput.js.map +1 -1
  11. package/cjs/form/combobox/Combobox.d.ts +1 -1
  12. package/cjs/form/combobox/Combobox.js +1 -1
  13. package/cjs/form/combobox/Combobox.js.map +1 -1
  14. package/cjs/form/combobox/ComboboxWrapper.js +0 -1
  15. package/cjs/form/combobox/ComboboxWrapper.js.map +1 -1
  16. package/cjs/form/combobox/Input/Input.js +9 -9
  17. package/cjs/form/combobox/Input/Input.js.map +1 -1
  18. package/cjs/form/combobox/Input/InputController.d.ts +1 -1
  19. package/cjs/form/combobox/Input/InputController.js +8 -2
  20. package/cjs/form/combobox/Input/InputController.js.map +1 -1
  21. package/cjs/form/combobox/SelectedOptions/selectedOptionsContext.js +5 -0
  22. package/cjs/form/combobox/SelectedOptions/selectedOptionsContext.js.map +1 -1
  23. package/cjs/form/confirmation-panel/ConfirmationPanel.js +1 -1
  24. package/cjs/form/confirmation-panel/ConfirmationPanel.js.map +1 -1
  25. package/cjs/form/fieldset/Fieldset.js +1 -1
  26. package/cjs/form/fieldset/Fieldset.js.map +1 -1
  27. package/cjs/form/file-upload/parts/dropzone/Dropzone.js +1 -1
  28. package/cjs/form/file-upload/parts/dropzone/Dropzone.js.map +1 -1
  29. package/cjs/form/search/Search.d.ts +1 -1
  30. package/cjs/form/search/Search.js +4 -4
  31. package/cjs/form/search/Search.js.map +1 -1
  32. package/cjs/form/select/Select.js +1 -1
  33. package/cjs/form/select/Select.js.map +1 -1
  34. package/cjs/form/switch/Switch.js +1 -7
  35. package/cjs/form/switch/Switch.js.map +1 -1
  36. package/cjs/form/textarea/Textarea.js +1 -1
  37. package/cjs/form/textarea/Textarea.js.map +1 -1
  38. package/cjs/form/textfield/TextField.js +1 -1
  39. package/cjs/form/textfield/TextField.js.map +1 -1
  40. package/cjs/guide-panel/GuidePanel.js +2 -2
  41. package/cjs/guide-panel/GuidePanel.js.map +1 -1
  42. package/cjs/help-text/HelpText.js +2 -2
  43. package/cjs/help-text/HelpText.js.map +1 -1
  44. package/cjs/internal-header/InternalHeader.js +3 -3
  45. package/cjs/internal-header/InternalHeader.js.map +1 -1
  46. package/cjs/layout/base/BasePrimitive.js +2 -2
  47. package/cjs/layout/base/BasePrimitive.js.map +1 -1
  48. package/cjs/layout/bleed/Bleed.js +2 -2
  49. package/cjs/layout/bleed/Bleed.js.map +1 -1
  50. package/cjs/layout/box/Box.d.ts +3 -2
  51. package/cjs/layout/box/Box.darkside.d.ts +10 -6
  52. package/cjs/layout/box/Box.darkside.js.map +1 -1
  53. package/cjs/layout/box/Box.js +2 -2
  54. package/cjs/layout/box/Box.js.map +1 -1
  55. package/cjs/layout/grid/HGrid.js +2 -2
  56. package/cjs/layout/grid/HGrid.js.map +1 -1
  57. package/cjs/layout/page/Page.js +2 -2
  58. package/cjs/layout/page/Page.js.map +1 -1
  59. package/cjs/layout/stack/Stack.js +2 -2
  60. package/cjs/layout/stack/Stack.js.map +1 -1
  61. package/cjs/layout/utilities/types.d.ts +7 -23
  62. package/cjs/overlays/action-menu/ActionMenu.js +2 -2
  63. package/cjs/overlays/action-menu/ActionMenu.js.map +1 -1
  64. package/cjs/pagination/PaginationItem.js +2 -2
  65. package/cjs/pagination/PaginationItem.js.map +1 -1
  66. package/cjs/popover/Popover.js +2 -2
  67. package/cjs/popover/Popover.js.map +1 -1
  68. package/cjs/portal/Portal.js +12 -0
  69. package/cjs/portal/Portal.js.map +1 -1
  70. package/cjs/provider/index.d.ts +0 -1
  71. package/cjs/provider/index.js +1 -4
  72. package/cjs/provider/index.js.map +1 -1
  73. package/cjs/read-more/ReadMore.js +1 -3
  74. package/cjs/read-more/ReadMore.js.map +1 -1
  75. package/cjs/tabs/Tabs.context.d.ts +1 -1
  76. package/cjs/tabs/parts/tablist/useScrollButtons.d.ts +1 -1
  77. package/cjs/tag/Tag.d.ts +1 -1
  78. package/cjs/tag/Tag.js +4 -1
  79. package/cjs/tag/Tag.js.map +1 -1
  80. package/cjs/theme/Theme.d.ts +16 -0
  81. package/cjs/{provider/theme/AkselTheme.js → theme/Theme.js} +17 -18
  82. package/cjs/theme/Theme.js.map +1 -0
  83. package/cjs/theme/index.d.ts +1 -0
  84. package/cjs/theme/index.js +7 -0
  85. package/cjs/theme/index.js.map +1 -0
  86. package/cjs/timeline/Pin.js +5 -2
  87. package/cjs/timeline/Pin.js.map +1 -1
  88. package/cjs/timeline/hooks/useTimelineRows.d.ts +5 -1
  89. package/cjs/timeline/hooks/useTimelineRows.js +14 -3
  90. package/cjs/timeline/hooks/useTimelineRows.js.map +1 -1
  91. package/cjs/timeline/period/ClickablePeriod.js +5 -2
  92. package/cjs/timeline/period/ClickablePeriod.js.map +1 -1
  93. package/cjs/toggle-group/parts/ToggleItem.js +1 -1
  94. package/cjs/toggle-group/parts/ToggleItem.js.map +1 -1
  95. package/cjs/tooltip/Tooltip.js +6 -3
  96. package/cjs/tooltip/Tooltip.js.map +1 -1
  97. package/cjs/typography/ErrorMessage.d.ts +4 -0
  98. package/cjs/typography/ErrorMessage.js +6 -2
  99. package/cjs/typography/ErrorMessage.js.map +1 -1
  100. package/cjs/util/TextareaAutoSize.js +1 -1
  101. package/cjs/util/TextareaAutoSize.js.map +1 -1
  102. package/cjs/util/debounce.d.ts +2 -2
  103. package/cjs/util/debounce.js +5 -1
  104. package/cjs/util/debounce.js.map +1 -1
  105. package/esm/accordion/AccordionContent.js +2 -2
  106. package/esm/accordion/AccordionContent.js.map +1 -1
  107. package/esm/accordion/AccordionHeader.js +2 -2
  108. package/esm/accordion/AccordionHeader.js.map +1 -1
  109. package/esm/button/Button.js +10 -26
  110. package/esm/button/Button.js.map +1 -1
  111. package/esm/date/datepicker/parts/WeekNumber.js +2 -2
  112. package/esm/date/datepicker/parts/WeekNumber.js.map +1 -1
  113. package/esm/date/parts/DateInput.js +1 -1
  114. package/esm/date/parts/DateInput.js.map +1 -1
  115. package/esm/form/combobox/Combobox.d.ts +1 -1
  116. package/esm/form/combobox/Combobox.js +1 -1
  117. package/esm/form/combobox/Combobox.js.map +1 -1
  118. package/esm/form/combobox/ComboboxWrapper.js +0 -1
  119. package/esm/form/combobox/ComboboxWrapper.js.map +1 -1
  120. package/esm/form/combobox/Input/Input.js +9 -9
  121. package/esm/form/combobox/Input/Input.js.map +1 -1
  122. package/esm/form/combobox/Input/InputController.d.ts +1 -1
  123. package/esm/form/combobox/Input/InputController.js +8 -2
  124. package/esm/form/combobox/Input/InputController.js.map +1 -1
  125. package/esm/form/combobox/SelectedOptions/selectedOptionsContext.js +5 -0
  126. package/esm/form/combobox/SelectedOptions/selectedOptionsContext.js.map +1 -1
  127. package/esm/form/confirmation-panel/ConfirmationPanel.js +1 -1
  128. package/esm/form/confirmation-panel/ConfirmationPanel.js.map +1 -1
  129. package/esm/form/fieldset/Fieldset.js +1 -1
  130. package/esm/form/fieldset/Fieldset.js.map +1 -1
  131. package/esm/form/file-upload/parts/dropzone/Dropzone.js +1 -1
  132. package/esm/form/file-upload/parts/dropzone/Dropzone.js.map +1 -1
  133. package/esm/form/search/Search.d.ts +1 -1
  134. package/esm/form/search/Search.js +4 -4
  135. package/esm/form/search/Search.js.map +1 -1
  136. package/esm/form/select/Select.js +1 -1
  137. package/esm/form/select/Select.js.map +1 -1
  138. package/esm/form/switch/Switch.js +1 -7
  139. package/esm/form/switch/Switch.js.map +1 -1
  140. package/esm/form/textarea/Textarea.js +1 -1
  141. package/esm/form/textarea/Textarea.js.map +1 -1
  142. package/esm/form/textfield/TextField.js +1 -1
  143. package/esm/form/textfield/TextField.js.map +1 -1
  144. package/esm/guide-panel/GuidePanel.js +2 -2
  145. package/esm/guide-panel/GuidePanel.js.map +1 -1
  146. package/esm/help-text/HelpText.js +2 -2
  147. package/esm/help-text/HelpText.js.map +1 -1
  148. package/esm/internal-header/InternalHeader.js +3 -3
  149. package/esm/internal-header/InternalHeader.js.map +1 -1
  150. package/esm/layout/base/BasePrimitive.js +2 -2
  151. package/esm/layout/base/BasePrimitive.js.map +1 -1
  152. package/esm/layout/bleed/Bleed.js +2 -2
  153. package/esm/layout/bleed/Bleed.js.map +1 -1
  154. package/esm/layout/box/Box.d.ts +3 -2
  155. package/esm/layout/box/Box.darkside.d.ts +10 -6
  156. package/esm/layout/box/Box.darkside.js.map +1 -1
  157. package/esm/layout/box/Box.js +2 -2
  158. package/esm/layout/box/Box.js.map +1 -1
  159. package/esm/layout/grid/HGrid.js +2 -2
  160. package/esm/layout/grid/HGrid.js.map +1 -1
  161. package/esm/layout/page/Page.js +2 -2
  162. package/esm/layout/page/Page.js.map +1 -1
  163. package/esm/layout/stack/Stack.js +2 -2
  164. package/esm/layout/stack/Stack.js.map +1 -1
  165. package/esm/layout/utilities/types.d.ts +7 -23
  166. package/esm/overlays/action-menu/ActionMenu.js +2 -2
  167. package/esm/overlays/action-menu/ActionMenu.js.map +1 -1
  168. package/esm/pagination/PaginationItem.js +2 -2
  169. package/esm/pagination/PaginationItem.js.map +1 -1
  170. package/esm/popover/Popover.js +2 -2
  171. package/esm/popover/Popover.js.map +1 -1
  172. package/esm/portal/Portal.js +12 -0
  173. package/esm/portal/Portal.js.map +1 -1
  174. package/esm/provider/index.d.ts +0 -1
  175. package/esm/provider/index.js +0 -1
  176. package/esm/provider/index.js.map +1 -1
  177. package/esm/read-more/ReadMore.js +1 -3
  178. package/esm/read-more/ReadMore.js.map +1 -1
  179. package/esm/tabs/Tabs.context.d.ts +1 -1
  180. package/esm/tabs/parts/tablist/useScrollButtons.d.ts +1 -1
  181. package/esm/tag/Tag.d.ts +1 -1
  182. package/esm/tag/Tag.js +4 -1
  183. package/esm/tag/Tag.js.map +1 -1
  184. package/esm/theme/Theme.d.ts +16 -0
  185. package/esm/theme/Theme.js +21 -0
  186. package/esm/theme/Theme.js.map +1 -0
  187. package/esm/theme/index.d.ts +1 -0
  188. package/esm/theme/index.js +3 -0
  189. package/esm/theme/index.js.map +1 -0
  190. package/esm/timeline/Pin.js +5 -2
  191. package/esm/timeline/Pin.js.map +1 -1
  192. package/esm/timeline/hooks/useTimelineRows.d.ts +5 -1
  193. package/esm/timeline/hooks/useTimelineRows.js +14 -3
  194. package/esm/timeline/hooks/useTimelineRows.js.map +1 -1
  195. package/esm/timeline/period/ClickablePeriod.js +5 -2
  196. package/esm/timeline/period/ClickablePeriod.js.map +1 -1
  197. package/esm/toggle-group/parts/ToggleItem.js +1 -1
  198. package/esm/toggle-group/parts/ToggleItem.js.map +1 -1
  199. package/esm/tooltip/Tooltip.js +6 -3
  200. package/esm/tooltip/Tooltip.js.map +1 -1
  201. package/esm/typography/ErrorMessage.d.ts +4 -0
  202. package/esm/typography/ErrorMessage.js +6 -2
  203. package/esm/typography/ErrorMessage.js.map +1 -1
  204. package/esm/util/TextareaAutoSize.js +1 -1
  205. package/esm/util/TextareaAutoSize.js.map +1 -1
  206. package/esm/util/debounce.d.ts +2 -2
  207. package/esm/util/debounce.js +5 -1
  208. package/esm/util/debounce.js.map +1 -1
  209. package/package.json +4 -4
  210. package/src/accordion/AccordionContent.tsx +2 -2
  211. package/src/accordion/AccordionHeader.tsx +2 -2
  212. package/src/button/Button.tsx +20 -50
  213. package/src/date/datepicker/parts/WeekNumber.tsx +2 -2
  214. package/src/date/parts/DateInput.tsx +5 -1
  215. package/src/form/combobox/Combobox.tsx +5 -1
  216. package/src/form/combobox/ComboboxWrapper.tsx +0 -1
  217. package/src/form/combobox/Input/Input.tsx +9 -9
  218. package/src/form/combobox/Input/InputController.tsx +10 -2
  219. package/src/form/combobox/SelectedOptions/selectedOptionsContext.tsx +4 -0
  220. package/src/form/confirmation-panel/ConfirmationPanel.tsx +5 -1
  221. package/src/form/fieldset/Fieldset.tsx +3 -1
  222. package/src/form/file-upload/parts/dropzone/Dropzone.tsx +1 -1
  223. package/src/form/search/Search.tsx +6 -4
  224. package/src/form/select/Select.tsx +3 -1
  225. package/src/form/switch/Switch.tsx +1 -10
  226. package/src/form/textarea/Textarea.tsx +3 -1
  227. package/src/form/textfield/TextField.tsx +3 -1
  228. package/src/guide-panel/GuidePanel.tsx +2 -2
  229. package/src/help-text/HelpText.tsx +2 -2
  230. package/src/internal-header/InternalHeader.tsx +4 -4
  231. package/src/layout/base/BasePrimitive.tsx +2 -2
  232. package/src/layout/bleed/Bleed.tsx +2 -2
  233. package/src/layout/box/Box.darkside.tsx +19 -14
  234. package/src/layout/box/Box.tsx +6 -4
  235. package/src/layout/grid/HGrid.tsx +2 -2
  236. package/src/layout/page/Page.tsx +2 -2
  237. package/src/layout/stack/Stack.tsx +2 -2
  238. package/src/layout/utilities/types.ts +20 -42
  239. package/src/overlays/action-menu/ActionMenu.tsx +2 -2
  240. package/src/pagination/PaginationItem.tsx +2 -2
  241. package/src/popover/Popover.tsx +2 -2
  242. package/src/portal/Portal.tsx +17 -0
  243. package/src/provider/index.ts +0 -4
  244. package/src/read-more/ReadMore.tsx +1 -4
  245. package/src/tag/Tag.tsx +32 -18
  246. package/src/theme/Theme.tsx +60 -0
  247. package/src/theme/index.ts +2 -0
  248. package/src/timeline/Pin.tsx +16 -10
  249. package/src/timeline/hooks/useTimelineRows.ts +25 -9
  250. package/src/timeline/period/ClickablePeriod.tsx +16 -10
  251. package/src/timeline/tests/useTimelineRows.test.ts +131 -0
  252. package/src/toggle-group/parts/ToggleItem.tsx +1 -0
  253. package/src/tooltip/Tooltip.tsx +7 -2
  254. package/src/typography/ErrorMessage.tsx +36 -2
  255. package/src/util/TextareaAutoSize.tsx +15 -11
  256. package/src/util/debounce.ts +11 -3
  257. package/cjs/provider/theme/AkselTheme.d.ts +0 -22
  258. package/cjs/provider/theme/AkselTheme.js.map +0 -1
  259. package/esm/provider/theme/AkselTheme.d.ts +0 -22
  260. package/esm/provider/theme/AkselTheme.js +0 -22
  261. package/esm/provider/theme/AkselTheme.js.map +0 -1
  262. package/src/provider/theme/AkselTheme.tsx +0 -70
@@ -15,6 +15,7 @@ import {
15
15
  } from "@floating-ui/react";
16
16
  import { format } from "date-fns";
17
17
  import React, { forwardRef, useRef, useState } from "react";
18
+ import { useThemeInternal } from "../theme/Theme";
18
19
  import { useMergeRefs } from "../util/hooks/useMergeRefs";
19
20
  import { useI18n } from "../util/i18n/i18n.context";
20
21
  import { useTimelineContext } from "./hooks/useTimelineContext";
@@ -47,6 +48,9 @@ export const Pin = forwardRef<HTMLButtonElement, TimelinePinProps>(
47
48
  const arrowRef = useRef<HTMLDivElement | null>(null);
48
49
  const translate = useI18n("Timeline");
49
50
 
51
+ const themeContext = useThemeInternal(false);
52
+ const showArrow = !themeContext;
53
+
50
54
  const {
51
55
  context,
52
56
  placement,
@@ -59,7 +63,7 @@ export const Pin = forwardRef<HTMLButtonElement, TimelinePinProps>(
59
63
  onOpenChange: (_open) => setOpen(_open),
60
64
  whileElementsMounted: autoUpdate,
61
65
  middleware: [
62
- offset(16),
66
+ offset(showArrow ? 16 : 8),
63
67
  shift(),
64
68
  flip({ padding: 5, fallbackPlacements: ["bottom", "top"] }),
65
69
  flArrow({ element: arrowRef, padding: 5 }),
@@ -135,15 +139,17 @@ export const Pin = forwardRef<HTMLButtonElement, TimelinePinProps>(
135
139
  style={floatingStyles}
136
140
  >
137
141
  {children}
138
- <div
139
- ref={arrowRef}
140
- style={{
141
- ...(arrowX != null ? { left: arrowX } : {}),
142
- ...(arrowY != null ? { top: arrowY } : {}),
143
- ...(staticSide ? { [staticSide]: "-0.5rem" } : {}),
144
- }}
145
- className="navds-timeline__popover-arrow"
146
- />
142
+ {showArrow && (
143
+ <div
144
+ ref={arrowRef}
145
+ style={{
146
+ ...(arrowX != null ? { left: arrowX } : {}),
147
+ ...(arrowY != null ? { top: arrowY } : {}),
148
+ ...(staticSide ? { [staticSide]: "-0.5rem" } : {}),
149
+ }}
150
+ className="navds-timeline__popover-arrow"
151
+ />
152
+ )}
147
153
  </div>
148
154
  </FloatingFocusManager>
149
155
  )}
@@ -134,17 +134,33 @@ export const useTimelineRows = (
134
134
  [rows, startDate, endDate, direction],
135
135
  );
136
136
 
137
- const earliestDate = (earliest: Date, period: Period) =>
138
- period.start < earliest ? period.start : earliest;
137
+ export const useEarliestDate = ({
138
+ startDate,
139
+ rows,
140
+ }: {
141
+ startDate?: Date;
142
+ rows: Pick<Period, "start">[][];
143
+ }) =>
144
+ useMemo(() => {
145
+ if (startDate) {
146
+ return startDate;
147
+ }
139
148
 
140
- const earliestFomDate = (rader: Period[][]) =>
141
- rader.flat().reduce(earliestDate, new Date());
149
+ const startDates = rows
150
+ .flat()
151
+ .filter((period) => period.start)
152
+ .map((period) => period.start);
142
153
 
143
- export const useEarliestDate = ({ startDate, rows }: any) =>
144
- useMemo(
145
- () => (startDate ? startDate : earliestFomDate(rows)),
146
- [startDate, rows],
147
- );
154
+ if (startDates.length === 0) {
155
+ return new Date();
156
+ }
157
+
158
+ const earliestDate = startDates.reduce((earliest, current) =>
159
+ current < earliest ? current : earliest,
160
+ );
161
+
162
+ return earliestDate;
163
+ }, [startDate, rows]);
148
164
 
149
165
  const latestDate = (latest: Date, period: Period) =>
150
166
  period.end > latest ? period.end : latest;
@@ -15,6 +15,7 @@ import {
15
15
  } from "@floating-ui/react";
16
16
  import cl from "clsx";
17
17
  import React, { useRef, useState } from "react";
18
+ import { useThemeInternal } from "../../theme/Theme";
18
19
  import { useMergeRefs } from "../../util/hooks/useMergeRefs";
19
20
  import { useI18n } from "../../util/i18n/i18n.context";
20
21
  import { usePeriodContext } from "../hooks/usePeriodContext";
@@ -55,6 +56,9 @@ const ClickablePeriod = React.memo(
55
56
  const arrowRef = useRef<HTMLDivElement | null>(null);
56
57
  const translate = useI18n("Timeline");
57
58
 
59
+ const themeContext = useThemeInternal(false);
60
+ const showArrow = !themeContext;
61
+
58
62
  const {
59
63
  context,
60
64
  placement,
@@ -67,7 +71,7 @@ const ClickablePeriod = React.memo(
67
71
  onOpenChange: (_open) => setOpen(_open),
68
72
  whileElementsMounted: autoUpdate,
69
73
  middleware: [
70
- offset(16),
74
+ offset(showArrow ? 16 : 8),
71
75
  shift(),
72
76
  flip({ padding: 5, fallbackPlacements: ["bottom", "top"] }),
73
77
  flArrow({ element: arrowRef, padding: 5 }),
@@ -164,15 +168,17 @@ const ClickablePeriod = React.memo(
164
168
  style={floatingStyles}
165
169
  >
166
170
  {children}
167
- <div
168
- ref={arrowRef}
169
- style={{
170
- ...(arrowX != null ? { left: arrowX } : {}),
171
- ...(arrowY != null ? { top: arrowY } : {}),
172
- ...(staticSide ? { [staticSide]: "-0.5rem" } : {}),
173
- }}
174
- className="navds-timeline__popover-arrow"
175
- />
171
+ {showArrow && (
172
+ <div
173
+ ref={arrowRef}
174
+ style={{
175
+ ...(arrowX != null ? { left: arrowX } : {}),
176
+ ...(arrowY != null ? { top: arrowY } : {}),
177
+ ...(staticSide ? { [staticSide]: "-0.5rem" } : {}),
178
+ }}
179
+ className="navds-timeline__popover-arrow"
180
+ />
181
+ )}
176
182
  </div>
177
183
  </FloatingFocusManager>
178
184
  )}
@@ -0,0 +1,131 @@
1
+ import { renderHook } from "@testing-library/react";
2
+ import { addDays, isSameDay } from "date-fns";
3
+ import { describe, expect, test } from "vitest";
4
+ import {
5
+ useEarliestDate,
6
+ useLatestDate,
7
+ useTimelineRows,
8
+ } from "../hooks/useTimelineRows";
9
+
10
+ describe("useEarliestDate", () => {
11
+ test("returns the provided startDate if it exists", () => {
12
+ const startDate = new Date(2023, 0, 1);
13
+ const { result } = renderHook(() =>
14
+ useEarliestDate({ startDate, rows: [] }),
15
+ );
16
+ expect(result.current).toEqual(startDate);
17
+ });
18
+
19
+ test("returns the earliest date from the rows if startDate is not provided", () => {
20
+ const rows = [
21
+ [{ start: new Date(2023, 0, 1) }],
22
+ [{ start: new Date(2022, 0, 1) }],
23
+ ];
24
+
25
+ const { result } = renderHook(() => useEarliestDate({ rows }));
26
+ expect(result.current).toEqual(new Date(2022, 0, 1));
27
+ });
28
+
29
+ test("returns the earliest date from the rows if startDate is not provided and date is later than todays date", () => {
30
+ const earliestDate = addDays(new Date(), 400);
31
+ const rows = [
32
+ [{ start: earliestDate }],
33
+ [{ start: addDays(earliestDate, 40) }],
34
+ ];
35
+
36
+ const { result } = renderHook(() => useEarliestDate({ rows }));
37
+ expect(result.current).toEqual(earliestDate);
38
+ });
39
+
40
+ test("returns the current date if no startDate and rows are empty", () => {
41
+ const { result } = renderHook(() => useEarliestDate({ rows: [] }));
42
+ expect(isSameDay(result.current, new Date())).toBeTruthy();
43
+ });
44
+ });
45
+
46
+ describe("useLatestDate", () => {
47
+ test("returns the provided endDate if it exists", () => {
48
+ const endDate = new Date(2023, 0, 1);
49
+ const { result } = renderHook(() => useLatestDate({ endDate, rows: [] }));
50
+ expect(result.current).toEqual(endDate);
51
+ });
52
+
53
+ test("returns the latest date from the rows plus one day if endDate is not provided", () => {
54
+ const rows = [
55
+ [{ start: new Date(2023, 0, 1), end: new Date(2023, 0, 10) }],
56
+ [{ start: new Date(2022, 0, 1), end: new Date(2022, 0, 5) }],
57
+ ];
58
+ const { result } = renderHook(() => useLatestDate({ rows }));
59
+ expect(result.current).toEqual(addDays(new Date(2023, 0, 10), 1));
60
+ });
61
+
62
+ test("returns the current date plus one day if no endDate and rows are empty", () => {
63
+ const { result } = renderHook(() => useLatestDate({ rows: [] }));
64
+ expect(result.current).toEqual(addDays(new Date(0), 1));
65
+ });
66
+ });
67
+
68
+ describe("useTimelineRows", () => {
69
+ const rows = [
70
+ {
71
+ label: "Row 1",
72
+ periods: [
73
+ {
74
+ start: new Date(2023, 0, 1),
75
+ end: new Date(2023, 0, 10),
76
+ status: "active",
77
+ },
78
+ {
79
+ start: new Date(2023, 0, 15),
80
+ end: new Date(2023, 0, 20),
81
+ status: "inactive",
82
+ },
83
+ ],
84
+ },
85
+ {
86
+ label: "Row 2",
87
+ periods: [
88
+ {
89
+ start: new Date(2022, 0, 1),
90
+ end: new Date(2022, 0, 5),
91
+ status: "active",
92
+ },
93
+ ],
94
+ },
95
+ ];
96
+
97
+ test("returns the correct timeline rows", () => {
98
+ const startDate = new Date(2022, 0, 1);
99
+ const endDate = new Date(2023, 0, 31);
100
+ const direction = "left";
101
+ const { result } = renderHook(() =>
102
+ useTimelineRows(rows, startDate, endDate, direction),
103
+ );
104
+
105
+ expect(result.current).toHaveLength(2);
106
+ expect(result.current[0].periods).toHaveLength(2);
107
+ expect(result.current[1].periods).toHaveLength(1);
108
+ });
109
+
110
+ test("handles empty rows", () => {
111
+ const startDate = new Date(2022, 0, 1);
112
+ const endDate = new Date(2023, 0, 31);
113
+ const direction = "left";
114
+ const { result } = renderHook(() =>
115
+ useTimelineRows([], startDate, endDate, direction),
116
+ );
117
+
118
+ expect(result.current).toHaveLength(0);
119
+ });
120
+
121
+ test("handles different directions", () => {
122
+ const startDate = new Date(2022, 0, 1);
123
+ const endDate = new Date(2023, 0, 31);
124
+ const direction = "right";
125
+ const { result } = renderHook(() =>
126
+ useTimelineRows(rows, startDate, endDate, direction),
127
+ );
128
+
129
+ expect(result.current[0].periods[0].start).toEqual(new Date(2023, 0, 15));
130
+ });
131
+ });
@@ -76,6 +76,7 @@ const ToggleItem = forwardRef<HTMLButtonElement, ToggleGroupItemProps>(
76
76
  type="button"
77
77
  role="radio"
78
78
  aria-checked={itemCtx.isSelected}
79
+ data-selected={itemCtx.isSelected}
79
80
  tabIndex={itemCtx.isFocused ? 0 : -1}
80
81
  onClick={itemCtx.onClick}
81
82
  onFocus={itemCtx.onFocus}
@@ -16,6 +16,7 @@ import React, { HTMLAttributes, forwardRef, useRef } from "react";
16
16
  import { useModalContext } from "../modal/Modal.context";
17
17
  import { Portal } from "../portal";
18
18
  import { Slot } from "../slot/Slot";
19
+ import { useThemeInternal } from "../theme/Theme";
19
20
  import { Detail } from "../typography";
20
21
  import { useId } from "../util/hooks";
21
22
  import { useControllableState } from "../util/hooks/useControllableState";
@@ -123,6 +124,9 @@ export const Tooltip = forwardRef<HTMLDivElement, TooltipProps>(
123
124
  },
124
125
  ref,
125
126
  ) => {
127
+ const themeContext = useThemeInternal(false);
128
+ const showArrow = _arrow && !themeContext;
129
+
126
130
  const [_open, _setOpen] = useControllableState({
127
131
  defaultValue: defaultOpen,
128
132
  value: open,
@@ -149,7 +153,7 @@ export const Tooltip = forwardRef<HTMLDivElement, TooltipProps>(
149
153
  open: _open,
150
154
  onOpenChange: (newState) => _setOpen(newState),
151
155
  middleware: [
152
- offset(_offset ? _offset : _arrow ? 10 : 2),
156
+ offset(_offset ?? (themeContext ? 8 : _arrow ? 16 : 4)),
153
157
  shift(),
154
158
  flip({ padding: 5, fallbackPlacements: ["bottom", "top"] }),
155
159
  flArrow({ element: arrowRef, padding: 5 }),
@@ -228,6 +232,7 @@ export const Tooltip = forwardRef<HTMLDivElement, TooltipProps>(
228
232
  ),
229
233
  })}
230
234
  data-side={placement}
235
+ data-state="open"
231
236
  >
232
237
  {content}
233
238
  {keys && (
@@ -239,7 +244,7 @@ export const Tooltip = forwardRef<HTMLDivElement, TooltipProps>(
239
244
  ))}
240
245
  </span>
241
246
  )}
242
- {_arrow && (
247
+ {showArrow && (
243
248
  <div
244
249
  ref={(node) => {
245
250
  arrowRef.current = node;
@@ -16,6 +16,10 @@ export interface ErrorMessageProps
16
16
  * Error text.
17
17
  */
18
18
  children: React.ReactNode;
19
+ /**
20
+ * Render a triangular warning icon.
21
+ */
22
+ showIcon?: boolean;
19
23
  }
20
24
 
21
25
  /**
@@ -36,7 +40,18 @@ export const ErrorMessage: OverridableComponent<
36
40
  ErrorMessageProps,
37
41
  HTMLParagraphElement
38
42
  > = forwardRef(
39
- ({ className, size, spacing, as: Component = "p", ...rest }, ref) => (
43
+ (
44
+ {
45
+ children,
46
+ className,
47
+ size,
48
+ spacing,
49
+ as: Component = "p",
50
+ showIcon = false,
51
+ ...rest
52
+ },
53
+ ref,
54
+ ) => (
40
55
  <Component
41
56
  {...rest}
42
57
  ref={ref}
@@ -49,9 +64,28 @@ export const ErrorMessage: OverridableComponent<
49
64
  }),
50
65
  {
51
66
  "navds-label--small": size === "small",
67
+ "navds-error-message--show-icon": showIcon,
52
68
  },
53
69
  )}
54
- />
70
+ >
71
+ {showIcon && (
72
+ <svg
73
+ viewBox="0 0 17 16"
74
+ fill="none"
75
+ xmlns="http://www.w3.org/2000/svg"
76
+ focusable={false}
77
+ aria-hidden
78
+ >
79
+ <path
80
+ fillRule="evenodd"
81
+ clipRule="evenodd"
82
+ d="M3.49209 11.534L8.11398 2.7594C8.48895 2.04752 9.50833 2.04743 9.88343 2.75924L14.5073 11.5339C14.8582 12.1998 14.3753 13 13.6226 13H4.37685C3.6242 13 3.14132 12.1999 3.49209 11.534ZM9.74855 10.495C9.74855 10.9092 9.41276 11.245 8.99855 11.245C8.58433 11.245 8.24855 10.9092 8.24855 10.495C8.24855 10.0808 8.58433 9.74497 8.99855 9.74497C9.41276 9.74497 9.74855 10.0808 9.74855 10.495ZM9.49988 5.49997C9.49988 5.22383 9.27602 4.99997 8.99988 4.99997C8.72373 4.99997 8.49988 5.22383 8.49988 5.49997V7.99997C8.49988 8.27611 8.72373 8.49997 8.99988 8.49997C9.27602 8.49997 9.49988 8.27611 9.49988 7.99997V5.49997Z"
83
+ fill="currentColor"
84
+ />
85
+ </svg>
86
+ )}
87
+ {children}
88
+ </Component>
55
89
  ),
56
90
  );
57
91
 
@@ -167,19 +167,23 @@ const TextareaAutosize = forwardRef<HTMLTextAreaElement, TextareaAutosizeProps>(
167
167
  });
168
168
  };
169
169
 
170
- const handleResize = debounce(() => {
171
- renders.current = 0;
172
-
173
- if (inputRef.current?.style.height || inputRef.current?.style.width) {
174
- // User has resized manually
175
- if (inputRef.current?.style.overflow === "hidden") {
176
- setState((oldState) => ({ ...oldState, overflow: false })); // The state update isn't important, we just need to trigger a rerender
170
+ const handleResize = debounce(
171
+ () => {
172
+ renders.current = 0;
173
+
174
+ if (inputRef.current?.style.height || inputRef.current?.style.width) {
175
+ // User has resized manually
176
+ if (inputRef.current?.style.overflow === "hidden") {
177
+ setState((oldState) => ({ ...oldState, overflow: false })); // The state update isn't important, we just need to trigger a rerender
178
+ }
179
+ return;
177
180
  }
178
- return;
179
- }
180
181
 
181
- syncHeightWithFlushSync();
182
- });
182
+ syncHeightWithFlushSync();
183
+ },
184
+ 166,
185
+ true,
186
+ );
183
187
 
184
188
  const input = inputRef.current!;
185
189
  const containerWindow = ownerWindow(input);
@@ -1,11 +1,19 @@
1
1
  "use client";
2
2
  // https://github.com/mui/material-ui/blob/master/packages/mui-utils/src/debounce.js
3
- export default function debounce(func, wait = 166) {
4
- let timeout: ReturnType<typeof setTimeout>;
5
- function debounced(this: any, ...args) {
3
+ export default function debounce<T extends unknown[]>(
4
+ func: (...args: T) => void,
5
+ wait = 166,
6
+ leading = false,
7
+ ) {
8
+ let timeout: ReturnType<typeof setTimeout> | undefined;
9
+ function debounced(this: any, ...args: T) {
6
10
  const later = () => {
11
+ timeout = undefined;
7
12
  func.apply(this, args);
8
13
  };
14
+ if (!timeout && leading) {
15
+ later();
16
+ }
9
17
  clearTimeout(timeout);
10
18
  timeout = setTimeout(later, wait);
11
19
  }
@@ -1,22 +0,0 @@
1
- import React from "react";
2
- type AkselThemeContext = {
3
- /**
4
- * Color theme
5
- * @default "light"
6
- */
7
- theme?: "light" | "dark";
8
- /**
9
- * Brand volume
10
- * @default "low"
11
- * This is experimental and subject to changes
12
- */
13
- volume?: "high" | "low";
14
- };
15
- declare const useAkselTheme: <S extends boolean = true>(strict?: S) => S extends true ? AkselThemeContext : AkselThemeContext | undefined;
16
- declare const AkselTheme: React.ForwardRefExoticComponent<{
17
- children: React.ReactNode;
18
- className?: string;
19
- hasBackground?: boolean;
20
- asChild?: boolean;
21
- } & AkselThemeContext & React.RefAttributes<HTMLDivElement>>;
22
- export { AkselTheme, useAkselTheme };
@@ -1 +0,0 @@
1
- {"version":3,"file":"AkselTheme.js","sourceRoot":"","sources":["../../../src/provider/theme/AkselTheme.tsx"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,gDAAsB;AACtB,+CAA0C;AAC1C,0CAAuC;AACvC,8DAA0D;AAgB1D,MAAM,CAAC,aAAa,EAAE,aAAa,CAAC,GAAG,IAAA,8BAAa,EAAoB;IACtE,YAAY,EAAE,sDAAsD;IACpE,QAAQ,EAAE,eAAe;IACzB,IAAI,EAAE,oBAAoB;IAC1B,YAAY,EAAE,oBAAoB;CACnC,CAAC,CAAC;AA6CkB,sCAAa;AAnClC,MAAM,UAAU,GAAG,IAAA,kBAAU,EAC3B,CAAC,KAAsB,EAAE,GAAG,EAAE,EAAE;;IAC9B,MAAM,OAAO,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;IAErC,MAAM,EACJ,QAAQ,EACR,SAAS,EACT,OAAO,GAAG,KAAK,EACf,KAAK,GAAG,MAAA,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,KAAK,mCAAI,OAAO,EACjC,MAAM,GAAG,MAAA,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,MAAM,mCAAI,KAAK,EACjC,aAAa,EAAE,iBAAiB,GAAG,IAAI,GACxC,GAAG,KAAK,CAAC;IAEV,MAAM,MAAM,GAAG,OAAO,KAAK,SAAS,CAAC;IAErC,MAAM,aAAa,GACjB,iBAAiB,aAAjB,iBAAiB,cAAjB,iBAAiB,GAAI,CAAC,MAAM,IAAI,KAAK,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC;IAE7D,MAAM,WAAW,GAAG,OAAO,CAAC,CAAC,CAAC,WAAI,CAAC,CAAC,CAAC,KAAK,CAAC;IAE3C,OAAO,CACL,8BAAC,aAAa,IAAC,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM;QACzC,8BAAC,WAAW,IACV,GAAG,EAAE,GAAG,EACR,SAAS,EAAE,IAAA,cAAE,EAAC,aAAa,EAAE,SAAS,EAAE,KAAK,CAAC,qBAC7B,aAAa,iBACjB,MAAM,IAElB,QAAQ,CACG,CACA,CACjB,CAAC;AACJ,CAAC,CACF,CAAC;AAEO,gCAAU"}
@@ -1,22 +0,0 @@
1
- import React from "react";
2
- type AkselThemeContext = {
3
- /**
4
- * Color theme
5
- * @default "light"
6
- */
7
- theme?: "light" | "dark";
8
- /**
9
- * Brand volume
10
- * @default "low"
11
- * This is experimental and subject to changes
12
- */
13
- volume?: "high" | "low";
14
- };
15
- declare const useAkselTheme: <S extends boolean = true>(strict?: S) => S extends true ? AkselThemeContext : AkselThemeContext | undefined;
16
- declare const AkselTheme: React.ForwardRefExoticComponent<{
17
- children: React.ReactNode;
18
- className?: string;
19
- hasBackground?: boolean;
20
- asChild?: boolean;
21
- } & AkselThemeContext & React.RefAttributes<HTMLDivElement>>;
22
- export { AkselTheme, useAkselTheme };
@@ -1,22 +0,0 @@
1
- import cl from "clsx";
2
- import React, { forwardRef } from "react";
3
- import { Slot } from "../../slot/Slot.js";
4
- import { createContext } from "../../util/create-context.js";
5
- const [ThemeProvider, useAkselTheme] = createContext({
6
- errorMessage: "useAkselTheme must be used within AkselThemeProvider",
7
- hookName: "useAkselTheme",
8
- name: "AkselThemeProvider",
9
- providerName: "AkselThemeProvider",
10
- });
11
- const AkselTheme = forwardRef((props, ref) => {
12
- var _a, _b;
13
- const context = useAkselTheme(false);
14
- const { children, className, asChild = false, theme = (_a = context === null || context === void 0 ? void 0 : context.theme) !== null && _a !== void 0 ? _a : "light", volume = (_b = context === null || context === void 0 ? void 0 : context.volume) !== null && _b !== void 0 ? _b : "low", hasBackground: hasBackgroundProp = true, } = props;
15
- const isRoot = context === undefined;
16
- const hasBackground = hasBackgroundProp !== null && hasBackgroundProp !== void 0 ? hasBackgroundProp : (isRoot || props.theme !== undefined);
17
- const SlotElement = asChild ? Slot : "div";
18
- return (React.createElement(ThemeProvider, { theme: theme, volume: volume },
19
- React.createElement(SlotElement, { ref: ref, className: cl("navds-theme", className, theme), "data-background": hasBackground, "data-volume": volume }, children)));
20
- });
21
- export { AkselTheme, useAkselTheme };
22
- //# sourceMappingURL=AkselTheme.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"AkselTheme.js","sourceRoot":"","sources":["../../../src/provider/theme/AkselTheme.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,MAAM,CAAC;AACtB,OAAO,KAAK,EAAE,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC;AAC1C,OAAO,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAgB1D,MAAM,CAAC,aAAa,EAAE,aAAa,CAAC,GAAG,aAAa,CAAoB;IACtE,YAAY,EAAE,sDAAsD;IACpE,QAAQ,EAAE,eAAe;IACzB,IAAI,EAAE,oBAAoB;IAC1B,YAAY,EAAE,oBAAoB;CACnC,CAAC,CAAC;AAUH,MAAM,UAAU,GAAG,UAAU,CAC3B,CAAC,KAAsB,EAAE,GAAG,EAAE,EAAE;;IAC9B,MAAM,OAAO,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;IAErC,MAAM,EACJ,QAAQ,EACR,SAAS,EACT,OAAO,GAAG,KAAK,EACf,KAAK,GAAG,MAAA,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,KAAK,mCAAI,OAAO,EACjC,MAAM,GAAG,MAAA,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,MAAM,mCAAI,KAAK,EACjC,aAAa,EAAE,iBAAiB,GAAG,IAAI,GACxC,GAAG,KAAK,CAAC;IAEV,MAAM,MAAM,GAAG,OAAO,KAAK,SAAS,CAAC;IAErC,MAAM,aAAa,GACjB,iBAAiB,aAAjB,iBAAiB,cAAjB,iBAAiB,GAAI,CAAC,MAAM,IAAI,KAAK,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC;IAE7D,MAAM,WAAW,GAAG,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC;IAE3C,OAAO,CACL,oBAAC,aAAa,IAAC,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM;QACzC,oBAAC,WAAW,IACV,GAAG,EAAE,GAAG,EACR,SAAS,EAAE,EAAE,CAAC,aAAa,EAAE,SAAS,EAAE,KAAK,CAAC,qBAC7B,aAAa,iBACjB,MAAM,IAElB,QAAQ,CACG,CACA,CACjB,CAAC;AACJ,CAAC,CACF,CAAC;AAEF,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,CAAC"}
@@ -1,70 +0,0 @@
1
- import cl from "clsx";
2
- import React, { forwardRef } from "react";
3
- import { Slot } from "../../slot/Slot";
4
- import { createContext } from "../../util/create-context";
5
-
6
- type AkselThemeContext = {
7
- /**
8
- * Color theme
9
- * @default "light"
10
- */
11
- theme?: "light" | "dark";
12
- /**
13
- * Brand volume
14
- * @default "low"
15
- * This is experimental and subject to changes
16
- */
17
- volume?: "high" | "low";
18
- };
19
-
20
- const [ThemeProvider, useAkselTheme] = createContext<AkselThemeContext>({
21
- errorMessage: "useAkselTheme must be used within AkselThemeProvider",
22
- hookName: "useAkselTheme",
23
- name: "AkselThemeProvider",
24
- providerName: "AkselThemeProvider",
25
- });
26
-
27
- type AkselThemeProps = {
28
- children: React.ReactNode;
29
- className?: string;
30
- hasBackground?: boolean;
31
- /* TODO: Handle this correctly with types */
32
- asChild?: boolean;
33
- } & AkselThemeContext;
34
-
35
- const AkselTheme = forwardRef<HTMLDivElement, AkselThemeProps>(
36
- (props: AkselThemeProps, ref) => {
37
- const context = useAkselTheme(false);
38
-
39
- const {
40
- children,
41
- className,
42
- asChild = false,
43
- theme = context?.theme ?? "light",
44
- volume = context?.volume ?? "low",
45
- hasBackground: hasBackgroundProp = true,
46
- } = props;
47
-
48
- const isRoot = context === undefined;
49
-
50
- const hasBackground =
51
- hasBackgroundProp ?? (isRoot || props.theme !== undefined);
52
-
53
- const SlotElement = asChild ? Slot : "div";
54
-
55
- return (
56
- <ThemeProvider theme={theme} volume={volume}>
57
- <SlotElement
58
- ref={ref}
59
- className={cl("navds-theme", className, theme)}
60
- data-background={hasBackground}
61
- data-volume={volume}
62
- >
63
- {children}
64
- </SlotElement>
65
- </ThemeProvider>
66
- );
67
- },
68
- );
69
-
70
- export { AkselTheme, useAkselTheme };