@navikt/ds-react 8.8.0 → 8.9.1

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 (228) hide show
  1. package/cjs/data/drag-and-drop/drag-handler/DragAndDropDragHandler.d.ts +13 -0
  2. package/cjs/data/drag-and-drop/drag-handler/DragAndDropDragHandler.js +59 -0
  3. package/cjs/data/drag-and-drop/drag-handler/DragAndDropDragHandler.js.map +1 -0
  4. package/cjs/data/drag-and-drop/item/DragAndDropItem.d.ts +31 -0
  5. package/cjs/data/drag-and-drop/item/DragAndDropItem.js +48 -0
  6. package/cjs/data/drag-and-drop/item/DragAndDropItem.js.map +1 -0
  7. package/cjs/data/drag-and-drop/root/DragAndDrop.context.d.ts +17 -0
  8. package/cjs/data/drag-and-drop/root/DragAndDrop.context.js +10 -0
  9. package/cjs/data/drag-and-drop/root/DragAndDrop.context.js.map +1 -0
  10. package/cjs/data/drag-and-drop/root/DragAndDropRoot.d.ts +38 -0
  11. package/cjs/data/drag-and-drop/root/DragAndDropRoot.js +219 -0
  12. package/cjs/data/drag-and-drop/root/DragAndDropRoot.js.map +1 -0
  13. package/cjs/data/drag-and-drop/types.d.ts +4 -0
  14. package/cjs/data/drag-and-drop/types.js +3 -0
  15. package/cjs/data/drag-and-drop/types.js.map +1 -0
  16. package/cjs/data/table/helpers/selection/getMultipleSelectProps.d.ts +14 -0
  17. package/cjs/data/table/helpers/selection/getMultipleSelectProps.js +48 -0
  18. package/cjs/data/table/helpers/selection/getMultipleSelectProps.js.map +1 -0
  19. package/cjs/data/table/helpers/selection/getSingleSelectProps.d.ts +10 -0
  20. package/cjs/data/table/helpers/selection/getSingleSelectProps.js +26 -0
  21. package/cjs/data/table/helpers/selection/getSingleSelectProps.js.map +1 -0
  22. package/cjs/data/table/helpers/selection/selection.types.d.ts +42 -0
  23. package/cjs/data/table/helpers/selection/selection.types.js +3 -0
  24. package/cjs/data/table/helpers/selection/selection.types.js.map +1 -0
  25. package/cjs/data/table/{root → hooks}/useTableKeyboardNav.js +1 -1
  26. package/cjs/data/table/hooks/useTableKeyboardNav.js.map +1 -0
  27. package/cjs/data/table/hooks/useTableSelection.d.ts +8 -0
  28. package/cjs/data/table/hooks/useTableSelection.js +49 -0
  29. package/cjs/data/table/hooks/useTableSelection.js.map +1 -0
  30. package/cjs/data/table/root/DataTableAuto.d.ts +4 -4
  31. package/cjs/data/table/root/DataTableAuto.js +23 -11
  32. package/cjs/data/table/root/DataTableAuto.js.map +1 -1
  33. package/cjs/data/table/root/DataTableRoot.d.ts +1 -1
  34. package/cjs/data/table/root/DataTableRoot.js +1 -1
  35. package/cjs/data/table/root/DataTableRoot.js.map +1 -1
  36. package/cjs/data/table/td/DataTableTd.d.ts +4 -0
  37. package/cjs/data/table/td/DataTableTd.js +4 -2
  38. package/cjs/data/table/td/DataTableTd.js.map +1 -1
  39. package/cjs/data/table/th/DataTableTh.d.ts +4 -0
  40. package/cjs/data/table/th/DataTableTh.js +6 -3
  41. package/cjs/data/table/th/DataTableTh.js.map +1 -1
  42. package/cjs/data/token-filter/AutoSuggest.js +37 -4
  43. package/cjs/data/token-filter/AutoSuggest.js.map +1 -1
  44. package/cjs/data/token-filter/FilterChip.d.ts +10 -0
  45. package/cjs/data/token-filter/FilterChip.js +65 -0
  46. package/cjs/data/token-filter/FilterChip.js.map +1 -0
  47. package/cjs/data/token-filter/TokenFilter.d.ts +1 -0
  48. package/cjs/data/token-filter/TokenFilter.js +4 -10
  49. package/cjs/data/token-filter/TokenFilter.js.map +1 -1
  50. package/cjs/data/toolbar/root/DataToolbarRoot.d.ts +6 -23
  51. package/cjs/data/toolbar/root/DataToolbarRoot.js +42 -7
  52. package/cjs/data/toolbar/root/DataToolbarRoot.js.map +1 -1
  53. package/cjs/date/Date.Input.js +8 -9
  54. package/cjs/date/Date.Input.js.map +1 -1
  55. package/cjs/date/datepicker/hooks/useDatepicker.js +4 -3
  56. package/cjs/date/datepicker/hooks/useDatepicker.js.map +1 -1
  57. package/cjs/date/monthpicker/hooks/useMonthPicker.js +3 -2
  58. package/cjs/date/monthpicker/hooks/useMonthPicker.js.map +1 -1
  59. package/cjs/form/checkbox/Checkbox.js +19 -33
  60. package/cjs/form/checkbox/Checkbox.js.map +1 -1
  61. package/cjs/form/checkbox/checkbox-input/CheckboxInput.d.ts +21 -0
  62. package/cjs/form/checkbox/checkbox-input/CheckboxInput.js +65 -0
  63. package/cjs/form/checkbox/checkbox-input/CheckboxInput.js.map +1 -0
  64. package/cjs/form/checkbox/types.d.ts +1 -1
  65. package/cjs/form/fieldset/Fieldset.js +2 -6
  66. package/cjs/form/fieldset/Fieldset.js.map +1 -1
  67. package/cjs/form/radio/Radio.js +9 -7
  68. package/cjs/form/radio/Radio.js.map +1 -1
  69. package/cjs/form/radio/radio-input/RadioInput.d.ts +19 -0
  70. package/cjs/{data/drag-and-drop/root/DataDragAndDropRoot.js → form/radio/radio-input/RadioInput.js} +17 -23
  71. package/cjs/form/radio/radio-input/RadioInput.js.map +1 -0
  72. package/cjs/internal-header/InternalHeaderButton.d.ts +5 -0
  73. package/cjs/internal-header/InternalHeaderButton.js +2 -2
  74. package/cjs/internal-header/InternalHeaderButton.js.map +1 -1
  75. package/cjs/utils/components/Listbox/group/ListboxGroup.js +2 -1
  76. package/cjs/utils/components/Listbox/group/ListboxGroup.js.map +1 -1
  77. package/esm/data/drag-and-drop/drag-handler/DragAndDropDragHandler.d.ts +13 -0
  78. package/esm/data/drag-and-drop/drag-handler/DragAndDropDragHandler.js +53 -0
  79. package/esm/data/drag-and-drop/drag-handler/DragAndDropDragHandler.js.map +1 -0
  80. package/esm/data/drag-and-drop/item/DragAndDropItem.d.ts +31 -0
  81. package/esm/data/drag-and-drop/item/DragAndDropItem.js +42 -0
  82. package/esm/data/drag-and-drop/item/DragAndDropItem.js.map +1 -0
  83. package/esm/data/drag-and-drop/root/DragAndDrop.context.d.ts +17 -0
  84. package/esm/data/drag-and-drop/root/DragAndDrop.context.js +6 -0
  85. package/esm/data/drag-and-drop/root/DragAndDrop.context.js.map +1 -0
  86. package/esm/data/drag-and-drop/root/DragAndDropRoot.d.ts +38 -0
  87. package/esm/data/drag-and-drop/root/DragAndDropRoot.js +179 -0
  88. package/esm/data/drag-and-drop/root/DragAndDropRoot.js.map +1 -0
  89. package/esm/data/drag-and-drop/types.d.ts +4 -0
  90. package/esm/data/drag-and-drop/types.js +2 -0
  91. package/esm/data/drag-and-drop/types.js.map +1 -0
  92. package/esm/data/table/helpers/selection/getMultipleSelectProps.d.ts +14 -0
  93. package/esm/data/table/helpers/selection/getMultipleSelectProps.js +46 -0
  94. package/esm/data/table/helpers/selection/getMultipleSelectProps.js.map +1 -0
  95. package/esm/data/table/helpers/selection/getSingleSelectProps.d.ts +10 -0
  96. package/esm/data/table/helpers/selection/getSingleSelectProps.js +24 -0
  97. package/esm/data/table/helpers/selection/getSingleSelectProps.js.map +1 -0
  98. package/esm/data/table/helpers/selection/selection.types.d.ts +42 -0
  99. package/esm/data/table/helpers/selection/selection.types.js +2 -0
  100. package/esm/data/table/helpers/selection/selection.types.js.map +1 -0
  101. package/esm/data/table/{root → hooks}/useTableKeyboardNav.js +1 -1
  102. package/esm/data/table/hooks/useTableKeyboardNav.js.map +1 -0
  103. package/esm/data/table/hooks/useTableSelection.d.ts +8 -0
  104. package/esm/data/table/hooks/useTableSelection.js +47 -0
  105. package/esm/data/table/hooks/useTableSelection.js.map +1 -0
  106. package/esm/data/table/root/DataTableAuto.d.ts +4 -4
  107. package/esm/data/table/root/DataTableAuto.js +23 -11
  108. package/esm/data/table/root/DataTableAuto.js.map +1 -1
  109. package/esm/data/table/root/DataTableRoot.d.ts +1 -1
  110. package/esm/data/table/root/DataTableRoot.js +1 -1
  111. package/esm/data/table/root/DataTableRoot.js.map +1 -1
  112. package/esm/data/table/td/DataTableTd.d.ts +4 -0
  113. package/esm/data/table/td/DataTableTd.js +4 -2
  114. package/esm/data/table/td/DataTableTd.js.map +1 -1
  115. package/esm/data/table/th/DataTableTh.d.ts +4 -0
  116. package/esm/data/table/th/DataTableTh.js +6 -3
  117. package/esm/data/table/th/DataTableTh.js.map +1 -1
  118. package/esm/data/token-filter/AutoSuggest.js +38 -5
  119. package/esm/data/token-filter/AutoSuggest.js.map +1 -1
  120. package/esm/data/token-filter/FilterChip.d.ts +10 -0
  121. package/esm/data/token-filter/FilterChip.js +30 -0
  122. package/esm/data/token-filter/FilterChip.js.map +1 -0
  123. package/esm/data/token-filter/TokenFilter.d.ts +1 -0
  124. package/esm/data/token-filter/TokenFilter.js +4 -10
  125. package/esm/data/token-filter/TokenFilter.js.map +1 -1
  126. package/esm/data/toolbar/root/DataToolbarRoot.d.ts +6 -23
  127. package/esm/data/toolbar/root/DataToolbarRoot.js +9 -7
  128. package/esm/data/toolbar/root/DataToolbarRoot.js.map +1 -1
  129. package/esm/date/Date.Input.js +9 -10
  130. package/esm/date/Date.Input.js.map +1 -1
  131. package/esm/date/datepicker/hooks/useDatepicker.js +4 -3
  132. package/esm/date/datepicker/hooks/useDatepicker.js.map +1 -1
  133. package/esm/date/monthpicker/hooks/useMonthPicker.js +3 -2
  134. package/esm/date/monthpicker/hooks/useMonthPicker.js.map +1 -1
  135. package/esm/form/checkbox/Checkbox.js +19 -33
  136. package/esm/form/checkbox/Checkbox.js.map +1 -1
  137. package/esm/form/checkbox/checkbox-input/CheckboxInput.d.ts +21 -0
  138. package/esm/form/checkbox/checkbox-input/CheckboxInput.js +29 -0
  139. package/esm/form/checkbox/checkbox-input/CheckboxInput.js.map +1 -0
  140. package/esm/form/checkbox/types.d.ts +1 -1
  141. package/esm/form/fieldset/Fieldset.js +2 -6
  142. package/esm/form/fieldset/Fieldset.js.map +1 -1
  143. package/esm/form/radio/Radio.js +9 -7
  144. package/esm/form/radio/Radio.js.map +1 -1
  145. package/esm/form/radio/radio-input/RadioInput.d.ts +19 -0
  146. package/esm/form/radio/radio-input/RadioInput.js +19 -0
  147. package/esm/form/radio/radio-input/RadioInput.js.map +1 -0
  148. package/esm/internal-header/InternalHeaderButton.d.ts +5 -0
  149. package/esm/internal-header/InternalHeaderButton.js +2 -2
  150. package/esm/internal-header/InternalHeaderButton.js.map +1 -1
  151. package/esm/utils/components/Listbox/group/ListboxGroup.js +2 -1
  152. package/esm/utils/components/Listbox/group/ListboxGroup.js.map +1 -1
  153. package/package.json +4 -4
  154. package/src/data/drag-and-drop/drag-handler/DragAndDropDragHandler.tsx +94 -0
  155. package/src/data/drag-and-drop/item/DragAndDropItem.tsx +75 -0
  156. package/src/data/drag-and-drop/root/DragAndDrop.context.tsx +27 -0
  157. package/src/data/drag-and-drop/root/DragAndDropRoot.tsx +294 -0
  158. package/src/data/drag-and-drop/types.ts +4 -0
  159. package/src/data/table/helpers/selection/getMultipleSelectProps.ts +70 -0
  160. package/src/data/table/helpers/selection/getSingleSelectProps.ts +36 -0
  161. package/src/data/table/helpers/selection/selection.types.ts +56 -0
  162. package/src/data/table/hooks/__tests__/useTableSelection.test.ts +327 -0
  163. package/src/data/table/{root → hooks}/useTableKeyboardNav.ts +1 -1
  164. package/src/data/table/hooks/useTableSelection.ts +78 -0
  165. package/src/data/table/root/DataTableAuto.tsx +58 -25
  166. package/src/data/table/root/DataTableRoot.tsx +2 -2
  167. package/src/data/table/td/DataTableTd.tsx +15 -2
  168. package/src/data/table/th/DataTableTh.tsx +13 -2
  169. package/src/data/token-filter/AutoSuggest.tsx +65 -3
  170. package/src/data/token-filter/FilterChip.tsx +100 -0
  171. package/src/data/token-filter/TokenFilter.tsx +9 -24
  172. package/src/data/toolbar/root/DataToolbarRoot.tsx +29 -32
  173. package/src/date/Date.Input.tsx +17 -16
  174. package/src/date/datepicker/hooks/useDatepicker.tsx +4 -5
  175. package/src/date/monthpicker/hooks/useMonthPicker.tsx +3 -4
  176. package/src/form/checkbox/Checkbox.tsx +37 -64
  177. package/src/form/checkbox/checkbox-input/CheckboxInput.tsx +69 -0
  178. package/src/form/checkbox/types.ts +1 -1
  179. package/src/form/fieldset/Fieldset.tsx +4 -6
  180. package/src/form/radio/Radio.tsx +43 -38
  181. package/src/form/radio/radio-input/RadioInput.tsx +32 -0
  182. package/src/internal-header/InternalHeaderButton.tsx +18 -9
  183. package/src/utils/components/Listbox/group/ListboxGroup.tsx +9 -2
  184. package/cjs/data/action-bar/root/DataActionBarRoot.d.ts +0 -27
  185. package/cjs/data/action-bar/root/DataActionBarRoot.js +0 -49
  186. package/cjs/data/action-bar/root/DataActionBarRoot.js.map +0 -1
  187. package/cjs/data/drag-and-drop/drag-handler/DataDragAndDropDragHandler.d.ts +0 -21
  188. package/cjs/data/drag-and-drop/drag-handler/DataDragAndDropDragHandler.js +0 -24
  189. package/cjs/data/drag-and-drop/drag-handler/DataDragAndDropDragHandler.js.map +0 -1
  190. package/cjs/data/drag-and-drop/item/DataDragAndDropItem.d.ts +0 -27
  191. package/cjs/data/drag-and-drop/item/DataDragAndDropItem.js +0 -41
  192. package/cjs/data/drag-and-drop/item/DataDragAndDropItem.js.map +0 -1
  193. package/cjs/data/drag-and-drop/root/DataDragAndDrop.context.d.ts +0 -8
  194. package/cjs/data/drag-and-drop/root/DataDragAndDrop.context.js +0 -10
  195. package/cjs/data/drag-and-drop/root/DataDragAndDrop.context.js.map +0 -1
  196. package/cjs/data/drag-and-drop/root/DataDragAndDropRoot.d.ts +0 -34
  197. package/cjs/data/drag-and-drop/root/DataDragAndDropRoot.js.map +0 -1
  198. package/cjs/data/table/root/useTableKeyboardNav.js.map +0 -1
  199. package/cjs/data/table/root/useTableSelection.d.ts +0 -55
  200. package/cjs/data/table/root/useTableSelection.js +0 -79
  201. package/cjs/data/table/root/useTableSelection.js.map +0 -1
  202. package/esm/data/action-bar/root/DataActionBarRoot.d.ts +0 -27
  203. package/esm/data/action-bar/root/DataActionBarRoot.js +0 -43
  204. package/esm/data/action-bar/root/DataActionBarRoot.js.map +0 -1
  205. package/esm/data/drag-and-drop/drag-handler/DataDragAndDropDragHandler.d.ts +0 -21
  206. package/esm/data/drag-and-drop/drag-handler/DataDragAndDropDragHandler.js +0 -18
  207. package/esm/data/drag-and-drop/drag-handler/DataDragAndDropDragHandler.js.map +0 -1
  208. package/esm/data/drag-and-drop/item/DataDragAndDropItem.d.ts +0 -27
  209. package/esm/data/drag-and-drop/item/DataDragAndDropItem.js +0 -35
  210. package/esm/data/drag-and-drop/item/DataDragAndDropItem.js.map +0 -1
  211. package/esm/data/drag-and-drop/root/DataDragAndDrop.context.d.ts +0 -8
  212. package/esm/data/drag-and-drop/root/DataDragAndDrop.context.js +0 -6
  213. package/esm/data/drag-and-drop/root/DataDragAndDrop.context.js.map +0 -1
  214. package/esm/data/drag-and-drop/root/DataDragAndDropRoot.d.ts +0 -34
  215. package/esm/data/drag-and-drop/root/DataDragAndDropRoot.js +0 -21
  216. package/esm/data/drag-and-drop/root/DataDragAndDropRoot.js.map +0 -1
  217. package/esm/data/table/root/useTableKeyboardNav.js.map +0 -1
  218. package/esm/data/table/root/useTableSelection.d.ts +0 -55
  219. package/esm/data/table/root/useTableSelection.js +0 -77
  220. package/esm/data/table/root/useTableSelection.js.map +0 -1
  221. package/src/data/action-bar/root/DataActionBarRoot.tsx +0 -59
  222. package/src/data/drag-and-drop/drag-handler/DataDragAndDropDragHandler.tsx +0 -63
  223. package/src/data/drag-and-drop/item/DataDragAndDropItem.tsx +0 -54
  224. package/src/data/drag-and-drop/root/DataDragAndDrop.context.tsx +0 -14
  225. package/src/data/drag-and-drop/root/DataDragAndDropRoot.tsx +0 -54
  226. package/src/data/table/root/useTableSelection.ts +0 -126
  227. /package/cjs/data/table/{root → hooks}/useTableKeyboardNav.d.ts +0 -0
  228. /package/esm/data/table/{root → hooks}/useTableKeyboardNav.d.ts +0 -0
@@ -1,7 +1,14 @@
1
+ /** biome-ignore-all lint/correctness/useHookAtTopLevel: False positive because of the way forwardRef() is added */
1
2
  import React, { forwardRef, useState } from "react";
2
- import { Checkbox } from "../../../form/checkbox";
3
+ import { CheckboxInput } from "../../../form/checkbox/checkbox-input/CheckboxInput";
4
+ import { RadioInput } from "../../../form/radio/radio-input/RadioInput";
3
5
  import { cl } from "../../../utils/helpers";
4
6
  import { useMergeRefs } from "../../../utils/hooks";
7
+ import { useTableKeyboardNav } from "../hooks/useTableKeyboardNav";
8
+ import {
9
+ type SelectionProps,
10
+ useTableSelection,
11
+ } from "../hooks/useTableSelection";
5
12
  import { DataTableTbody } from "../tbody/DataTableTbody";
6
13
  import { DataTableTd } from "../td/DataTableTd";
7
14
  import { DataTableTh } from "../th/DataTableTh";
@@ -9,8 +16,6 @@ import { DataTableThead } from "../thead/DataTableThead";
9
16
  import { DataTableTr } from "../tr/DataTableTr";
10
17
  import type { ColumnDefinitions } from "./DataTable.types";
11
18
  import { DataTableContextProvider } from "./DataTableRoot.context";
12
- import { useTableKeyboardNav } from "./useTableKeyboardNav";
13
- import { type SelectionProps, useTableSelection } from "./useTableSelection";
14
19
 
15
20
  interface DataTableProps<T>
16
21
  extends React.HTMLAttributes<HTMLTableElement>, SelectionProps {
@@ -62,7 +67,8 @@ interface DataTableProps<T>
62
67
  *
63
68
  */
64
69
  columnDefinitions: ColumnDefinitions<T>;
65
- data: (T & { id: string | number })[];
70
+ data: T[];
71
+ getRowId?: (rowData: T, index: number) => string | number;
66
72
  }
67
73
 
68
74
  function DataTableAutoInner<T>(
@@ -81,6 +87,7 @@ function DataTableAutoInner<T>(
81
87
  disabledKeys = [],
82
88
  data,
83
89
  columnDefinitions,
90
+ getRowId,
84
91
  ...rest
85
92
  }: DataTableProps<T>,
86
93
  forwardedRef: React.ForwardedRef<HTMLTableElement>,
@@ -93,15 +100,19 @@ function DataTableAutoInner<T>(
93
100
  shouldBlockNavigation,
94
101
  });
95
102
 
96
- const { getTheadCheckboxProps, selectionMode, getRowCheckboxProps } =
97
- useTableSelection({
98
- selectionMode: selectionModeProp,
99
- selectedKeys,
100
- defaultSelectedKeys,
101
- onSelectionChange,
102
- disabledKeys,
103
- data,
104
- });
103
+ const resolvedGetRowId =
104
+ getRowId ??
105
+ (((_row: T, index: number) => index) as (rowData: T) => string | number);
106
+
107
+ const selection = useTableSelection({
108
+ selectionMode: selectionModeProp,
109
+ selectedKeys,
110
+ defaultSelectedKeys,
111
+ onSelectionChange,
112
+ disabledKeys,
113
+ data,
114
+ getRowId: resolvedGetRowId,
115
+ });
105
116
 
106
117
  return (
107
118
  <DataTableContextProvider layout={layout} withKeyboardNav={withKeyboardNav}>
@@ -119,14 +130,25 @@ function DataTableAutoInner<T>(
119
130
  >
120
131
  <DataTableThead>
121
132
  <DataTableTr>
122
- {getTheadCheckboxProps && (
123
- <DataTableTd align="center" width="60px">
124
- <Checkbox {...getTheadCheckboxProps()} />
125
- </DataTableTd>
133
+ {selection.selectionMode === "multiple" && (
134
+ <DataTableTh
135
+ textAlign="center"
136
+ width="60px"
137
+ UNSAFE_isSelection
138
+ >
139
+ <CheckboxInput
140
+ {...selection.getTheadCheckboxProps()}
141
+ compact
142
+ />
143
+ </DataTableTh>
144
+ )}
145
+ {selection.selectionMode === "single" && (
146
+ <DataTableTh width="64px" UNSAFE_isSelection />
126
147
  )}
127
148
  {columnDefinitions.map((colDef, colDefIndex) => {
128
149
  return (
129
150
  <DataTableTh
151
+ /* TODO: Make these user-changable */
130
152
  maxWidth="400px"
131
153
  minWidth="100px"
132
154
  defaultWidth="100%"
@@ -141,15 +163,26 @@ function DataTableAutoInner<T>(
141
163
  </DataTableThead>
142
164
  <DataTableTbody>
143
165
  {data.map((rowData, rowIndex) => {
166
+ const rowId = selection.allKeys[rowIndex];
144
167
  return (
145
- <DataTableTr
146
- key={
147
- rowIndex /* TODO: Should be more flexible to allow user to define the key? */
148
- }
149
- >
150
- {selectionMode !== "none" && getRowCheckboxProps && (
151
- <DataTableTd align="center" width="60px">
152
- <Checkbox {...getRowCheckboxProps(rowData.id)} />
168
+ <DataTableTr key={rowId}>
169
+ {selection.selectionMode === "multiple" && (
170
+ <DataTableTd
171
+ align="center"
172
+ width="60px"
173
+ UNSAFE_isSelection
174
+ >
175
+ <CheckboxInput
176
+ {...selection.getRowCheckboxProps(rowId)}
177
+ compact
178
+ />
179
+ </DataTableTd>
180
+ )}
181
+
182
+ {selection.selectionMode === "single" && (
183
+ <DataTableTd width="64px" UNSAFE_isSelection>
184
+ {/* used with keyboard nav is funky, no longer auto-selects on keyboard-nav. Probably preventDefault somewhere breaking it. */}
185
+ <RadioInput {...selection.getRowRadioProps(rowId)} />
153
186
  </DataTableTd>
154
187
  )}
155
188
  {columnDefinitions.map((colDef, colDefIndex) => {
@@ -9,6 +9,8 @@ import {
9
9
  DataTableEmptyState,
10
10
  type DataTableEmptyStateProps,
11
11
  } from "../empty-state/DataTableEmptyState";
12
+ import { useTableKeyboardNav } from "../hooks/useTableKeyboardNav";
13
+ import { type SelectionProps } from "../hooks/useTableSelection";
12
14
  import {
13
15
  DataTableLoadingState,
14
16
  type DataTableLoadingStateProps,
@@ -29,8 +31,6 @@ import {
29
31
  } from "../thead/DataTableThead";
30
32
  import { DataTableTr, type DataTableTrProps } from "../tr/DataTableTr";
31
33
  import { DataTableContextProvider } from "./DataTableRoot.context";
32
- import { useTableKeyboardNav } from "./useTableKeyboardNav";
33
- import { type SelectionProps } from "./useTableSelection";
34
34
 
35
35
  interface DataTableProps
36
36
  extends React.HTMLAttributes<HTMLTableElement>, SelectionProps {
@@ -20,11 +20,22 @@ interface DataTableTdProps extends React.TdHTMLAttributes<HTMLTableCellElement>
20
20
  * @default "left"
21
21
  */
22
22
  textAlign?: "left" | "center" | "right";
23
+ /**
24
+ * Temp hack to solve overflow and alignment
25
+ */
26
+ UNSAFE_isSelection?: boolean;
23
27
  }
24
28
 
25
29
  const DataTableTd = forwardRef<HTMLTableCellElement, DataTableTdProps>(
26
30
  (
27
- { className, children, contentMaxWidth, textAlign = "left", ...rest },
31
+ {
32
+ className,
33
+ children,
34
+ contentMaxWidth,
35
+ textAlign = "left",
36
+ UNSAFE_isSelection,
37
+ ...rest
38
+ },
28
39
  forwardedRef,
29
40
  ) => {
30
41
  const { withKeyboardNav } = useDataTableContext();
@@ -33,7 +44,9 @@ const DataTableTd = forwardRef<HTMLTableCellElement, DataTableTdProps>(
33
44
  <td
34
45
  {...rest}
35
46
  ref={forwardedRef}
36
- className={cl("aksel-data-table__td", className)}
47
+ className={cl("aksel-data-table__td", className, {
48
+ "aksel-data-table--UNSAFE_isSelection": UNSAFE_isSelection,
49
+ })}
37
50
  tabIndex={withKeyboardNav ? -1 : undefined}
38
51
  data-align={textAlign}
39
52
  >
@@ -52,6 +52,10 @@ interface DataTableThProps
52
52
  */
53
53
  colSpan?: number;
54
54
  rowSpan?: number;
55
+ /**
56
+ * Temp hack to solve overflow and alignment
57
+ */
58
+ UNSAFE_isSelection?: boolean;
55
59
  }
56
60
 
57
61
  const SORT_ICON: Record<SortDirection, React.ElementType | null> = {
@@ -82,6 +86,7 @@ const DataTableTh = forwardRef<HTMLTableCellElement, DataTableThProps>(
82
86
  onWidthChange,
83
87
  defaultWidth,
84
88
  colSpan,
89
+ UNSAFE_isSelection,
85
90
  ...rest
86
91
  },
87
92
  forwardedRef,
@@ -143,12 +148,18 @@ const DataTableTh = forwardRef<HTMLTableCellElement, DataTableThProps>(
143
148
  )}
144
149
  </button>
145
150
  ) : (
146
- <div ref={contentRef} className="aksel-data-table__th-content">
151
+ <div
152
+ ref={contentRef}
153
+ className={cl({
154
+ "aksel-data-table__th-content": !UNSAFE_isSelection,
155
+ "aksel-data-table--UNSAFE_isSelection": UNSAFE_isSelection,
156
+ })}
157
+ >
147
158
  {children}
148
159
  </div>
149
160
  )}
150
161
 
151
- {resizeResult.enabled && (
162
+ {resizeResult.enabled && !UNSAFE_isSelection && (
152
163
  <button
153
164
  {...resizeResult.resizeHandlerProps}
154
165
  className="aksel-data-table__th-resize-handle"
@@ -1,7 +1,7 @@
1
- import React, { forwardRef, useState } from "react";
1
+ import React, { type JSX, forwardRef, useState } from "react";
2
2
  import { Search } from "../../form/search";
3
3
  import { VStack } from "../../primitives/stack";
4
- import { Detail, Label } from "../../typography";
4
+ import { BodyShort, Detail } from "../../typography";
5
5
  import Listbox from "../../utils/components/Listbox/root/ListboxRoot";
6
6
  import { DismissableLayer } from "../../utils/components/dismissablelayer/DismissableLayer";
7
7
  import { Floating } from "../../utils/components/floating/Floating";
@@ -63,6 +63,8 @@ const AutoSuggest = forwardRef<HTMLInputElement, AutoSuggestProps>(
63
63
  setOpen(true);
64
64
  }}
65
65
  onFocus={() => setOpen(true)}
66
+ size="small"
67
+ autoComplete="off"
66
68
  /* onKeyDown={(e) => {
67
69
  if (e.key === "Enter") {
68
70
  createToken(filterText);
@@ -79,6 +81,7 @@ const AutoSuggest = forwardRef<HTMLInputElement, AutoSuggestProps>(
79
81
  setFocusedValue={setVirtuallyFocusedOptionId}
80
82
  onClose={handleClose}
81
83
  safeZoneAnchor={inputRef}
84
+ autoSuggestValue={value}
82
85
  />
83
86
  )}
84
87
  </Listbox>
@@ -94,6 +97,7 @@ type AutoSuggestPopupProps = {
94
97
  setFocusedValue: (value: string) => void;
95
98
  onClose: () => void;
96
99
  safeZoneAnchor: HTMLInputElement | null;
100
+ autoSuggestValue: string;
97
101
  };
98
102
 
99
103
  const AutoSuggestPopup = forwardRef<HTMLDivElement, AutoSuggestPopupProps>(
@@ -105,6 +109,7 @@ const AutoSuggestPopup = forwardRef<HTMLDivElement, AutoSuggestPopupProps>(
105
109
  setFocusedValue,
106
110
  onClose,
107
111
  safeZoneAnchor,
112
+ autoSuggestValue,
108
113
  },
109
114
  ref,
110
115
  ) => {
@@ -134,7 +139,12 @@ const AutoSuggestPopup = forwardRef<HTMLDivElement, AutoSuggestPopupProps>(
134
139
  hasVirtualFocus={focusedValue === item.value}
135
140
  >
136
141
  <VStack gap="space-0">
137
- <Label as="div">{item.label}</Label>
142
+ <BodyShort as="div" size="small">
143
+ <HighlightText
144
+ text={item.label}
145
+ highlightText={autoSuggestValue}
146
+ />
147
+ </BodyShort>
138
148
  {item.description && (
139
149
  <Detail as="div">{item.description}</Detail>
140
150
  )}
@@ -158,4 +168,56 @@ const AutoSuggestPopup = forwardRef<HTMLDivElement, AutoSuggestPopupProps>(
158
168
  },
159
169
  );
160
170
 
171
+ function HighlightText({
172
+ text,
173
+ highlightText,
174
+ }: {
175
+ text: string;
176
+ highlightText: string;
177
+ }) {
178
+ if (!text || !highlightText) {
179
+ return <span>{text}</span>;
180
+ }
181
+
182
+ if (text === highlightText) {
183
+ return <Highlight text={text} />;
184
+ }
185
+
186
+ const { noMatches, matches } = highlightSplit(text, highlightText);
187
+
188
+ const highlighted: (string | JSX.Element)[] = [];
189
+
190
+ noMatches.forEach((noMatch, idx) => {
191
+ highlighted.push(<span key={`noMatch-${idx}`}>{noMatch}</span>);
192
+
193
+ if (matches && idx < matches.length) {
194
+ highlighted.push(<Highlight key={`match-${idx}`} text={matches[idx]} />);
195
+ }
196
+ });
197
+
198
+ return <span>{highlighted}</span>;
199
+ }
200
+
201
+ function Highlight({ text }: { text: string }) {
202
+ return <mark className="aksel-listbox__highlight">{text}</mark>;
203
+ }
204
+
205
+ function highlightSplit(text: string, highlightText: string) {
206
+ /* Skip loooong texts */
207
+ if (highlightText.length > 1000) {
208
+ return { noMatches: [text], matches: null };
209
+ }
210
+
211
+ /* Case insensitive filtering */
212
+ const filteringPattern = highlightText.replace(
213
+ /[-[\]/{}()*+?.\\^$|]/g,
214
+ "\\$&",
215
+ );
216
+ const regexp = new RegExp(filteringPattern, "gi");
217
+ const noMatches = text.split(regexp);
218
+ const matches = text.match(regexp);
219
+
220
+ return { noMatches, matches };
221
+ }
222
+
161
223
  export { AutoSuggest };
@@ -0,0 +1,100 @@
1
+ import React, { useState } from "react";
2
+ import { XMarkIcon } from "@navikt/aksel-icons";
3
+ import { ActionMenu } from "../../action-menu";
4
+ import { Popover } from "../../popover";
5
+ import type { ExternalToken, OperationT } from "./TokenFilter.types";
6
+
7
+ type TokenFilterChipsProps = {
8
+ tokens: ExternalToken[];
9
+ removeToken: (index: number) => void;
10
+ operation: OperationT;
11
+ updateOperation: (operation: OperationT) => void;
12
+ };
13
+
14
+ function TokenFilterChips(props: TokenFilterChipsProps) {
15
+ const { tokens, removeToken, operation, updateOperation } = props;
16
+
17
+ if (tokens.length === 0) {
18
+ return null;
19
+ }
20
+
21
+ return (
22
+ <ul className="aksel-property-filter__chips">
23
+ {tokens.map((token, index) => (
24
+ <TokenFilterChip
25
+ key={index}
26
+ onRemove={() => removeToken(index)}
27
+ token={token}
28
+ showOperation={index > 0}
29
+ operation={operation}
30
+ updateOperation={updateOperation}
31
+ />
32
+ ))}
33
+ </ul>
34
+ );
35
+ }
36
+
37
+ type TokenFilterChipProps = {
38
+ token: ExternalToken;
39
+ onRemove: () => void;
40
+ showOperation: boolean;
41
+ operation?: OperationT;
42
+ updateOperation?: (operation: OperationT) => void;
43
+ };
44
+
45
+ function TokenFilterChip(props: TokenFilterChipProps) {
46
+ const { token, onRemove, showOperation, operation } = props;
47
+ const [popupAnchor, setPopupAnchor] = useState<HTMLButtonElement | null>(
48
+ null,
49
+ );
50
+ const [isPopupOpen, setIsPopupOpen] = useState(false);
51
+
52
+ return (
53
+ <li className="aksel-property-filter__chip">
54
+ {showOperation && (
55
+ <ActionMenu>
56
+ <ActionMenu.Trigger>
57
+ <button
58
+ className="aksel-property-filter__chip-button"
59
+ data-type="operation"
60
+ /* onClick={onRemove} */
61
+ >
62
+ {operation === "and" ? "og" : "eller"}
63
+ </button>
64
+ </ActionMenu.Trigger>
65
+ <ActionMenu.Content>
66
+ <ActionMenu.Item onSelect={() => props.updateOperation?.("and")}>
67
+ AND
68
+ </ActionMenu.Item>
69
+ <ActionMenu.Item onSelect={() => props.updateOperation?.("or")}>
70
+ OR
71
+ </ActionMenu.Item>
72
+ </ActionMenu.Content>
73
+ </ActionMenu>
74
+ )}
75
+ <button
76
+ data-type="value"
77
+ className="aksel-property-filter__chip-button"
78
+ ref={setPopupAnchor}
79
+ onClick={() => setIsPopupOpen((open) => !open)}
80
+ >{`${token.propertyKey} ${token.operator} ${token.value}`}</button>
81
+ <Popover
82
+ open={isPopupOpen}
83
+ onClose={() => setIsPopupOpen(false)}
84
+ anchorEl={popupAnchor}
85
+ placement="bottom-start"
86
+ >
87
+ <Popover.Content>Edit filter</Popover.Content>
88
+ </Popover>
89
+ <button
90
+ data-type="remove"
91
+ className="aksel-property-filter__chip-button"
92
+ onClick={onRemove}
93
+ >
94
+ <XMarkIcon aria-hidden fontSize="1.25rem" />
95
+ </button>
96
+ </li>
97
+ );
98
+ }
99
+
100
+ export { TokenFilterChips };
@@ -1,9 +1,8 @@
1
1
  import React, { forwardRef, useState } from "react";
2
- import { Chips } from "../../chips";
3
- import { HStack } from "../../primitives/stack";
4
2
  import { cl } from "../../utils/helpers";
5
3
  import { AutoSuggest } from "./AutoSuggest";
6
4
  import { AutoCompleteOption } from "./AutoSuggest.types";
5
+ import { TokenFilterChips } from "./FilterChip";
7
6
  import type {
8
7
  ExternalOptions,
9
8
  ExternalPropertyDefinitions,
@@ -28,6 +27,7 @@ type TokenFilterProps = {
28
27
  * TODO:
29
28
  * - Implement onChange handler to update query state when user selects an autocomplete option.
30
29
  * - Handle token rendering and editing (e.g., show tokens for matched properties/operators/values, allow deleting tokens).
30
+ * - Writing "stance" still shows status and hostname options
31
31
  */
32
32
  export const TokenFilter = forwardRef<HTMLDivElement, TokenFilterProps>(
33
33
  ({ query, className, propertyDefinitions, options, onChange }, ref) => {
@@ -45,7 +45,7 @@ export const TokenFilter = forwardRef<HTMLDivElement, TokenFilterProps>(
45
45
  parsedPropertyOptions,
46
46
  );
47
47
 
48
- const { addToken, removeToken } = createActionHandlers({
48
+ const { addToken, removeToken, updateOperation } = createActionHandlers({
49
49
  query,
50
50
  onChange,
51
51
  });
@@ -115,27 +115,12 @@ export const TokenFilter = forwardRef<HTMLDivElement, TokenFilterProps>(
115
115
  open={open}
116
116
  setOpen={setOpen}
117
117
  />
118
- <HStack marginBlock="space-8" gap="space-8">
119
- {query.tokens.map((token, index) => {
120
- return (
121
- <React.Fragment
122
- key={`${token.propertyKey}-${token.operator}-${token.value}-${index}`}
123
- >
124
- <Chips.Removable
125
- key={index}
126
- onClick={() => {
127
- removeToken(index);
128
- }}
129
- >
130
- {`${token.propertyKey} ${token.operator} ${token.value}`}
131
- </Chips.Removable>
132
- {index < query.tokens.length - 1 && (
133
- <span>{query.operation}</span>
134
- )}
135
- </React.Fragment>
136
- );
137
- })}
138
- </HStack>
118
+ <TokenFilterChips
119
+ tokens={query.tokens}
120
+ removeToken={removeToken}
121
+ updateOperation={updateOperation}
122
+ operation={query.operation}
123
+ />
139
124
  </div>
140
125
  );
141
126
  },
@@ -1,4 +1,4 @@
1
- import React from "react";
1
+ import React, { forwardRef } from "react";
2
2
  import { cl } from "../../../utils/helpers";
3
3
  import DataToolbarButton, {
4
4
  DataToolbarButtonProps,
@@ -12,23 +12,15 @@ import DataToolbarToggleButton, {
12
12
  } from "../toggle-button/DataToolbarToggleButton";
13
13
 
14
14
  interface DataToolbarProps extends React.HTMLAttributes<HTMLDivElement> {
15
- children: React.ReactNode;
15
+ children?: never;
16
+ renderInput?: React.ReactNode;
17
+ renderPreferences?: React.ReactNode;
18
+ renderPagination?: React.ReactNode;
16
19
  }
17
20
 
18
21
  interface DataToolbarRootComponent extends React.ForwardRefExoticComponent<
19
22
  DataToolbarProps & React.RefAttributes<HTMLDivElement>
20
23
  > {
21
- /**
22
- * @see 🏷️ {@link DataToolbarSearchFieldProps}
23
- * @example
24
- * ```tsx
25
- * <DataToolbar>
26
- * <DataToolbar.SearchField />
27
- * </DataToolbar>
28
- * ```
29
- */
30
- SearchField: typeof DataToolbarSearchField;
31
-
32
24
  /**
33
25
  * @see 🏷️ {@link DataToolbarButtonProps}
34
26
  * @example
@@ -39,48 +31,53 @@ interface DataToolbarRootComponent extends React.ForwardRefExoticComponent<
39
31
  * ```
40
32
  */
41
33
  Button: typeof DataToolbarButton;
42
-
43
- /**
44
- * @see 🏷️ {@link DataToolbarToggleButtonProps}
45
- * @example
46
- * ```tsx
47
- * <DataToolbar>
48
- * <DataToolbar.ToggleButton />
49
- * </DataToolbar>
50
- * ```
51
- */
52
- ToggleButton: typeof DataToolbarToggleButton;
53
34
  }
54
35
 
55
- const DataToolbar = React.forwardRef<HTMLDivElement, DataToolbarProps>(
56
- ({ children, className, ...rest }, forwardRef) => {
36
+ const DataToolbar = forwardRef<HTMLDivElement, DataToolbarProps>(
37
+ (
38
+ { className, renderInput, renderPreferences, renderPagination, ...rest },
39
+ forwardedRef,
40
+ ) => {
57
41
  return (
58
42
  <div
59
- ref={forwardRef}
43
+ ref={forwardedRef}
60
44
  {...rest}
61
45
  className={cl("aksel-data-toolbar", className)}
62
46
  role="toolbar"
63
47
  >
64
- {children}
48
+ {renderInput && (
49
+ <div className="aksel-data-toolbar__input">{renderInput}</div>
50
+ )}
51
+
52
+ <div className="aksel-data-toolbar__actions">
53
+ {renderPagination && (
54
+ <div className="aksel-data-toolbar__pagination">
55
+ {renderPagination}
56
+ </div>
57
+ )}
58
+ {renderPreferences && (
59
+ <div className="aksel-data-toolbar__preferences">
60
+ {renderPreferences}
61
+ </div>
62
+ )}
63
+ </div>
65
64
  </div>
66
65
  );
67
66
  },
68
67
  ) as DataToolbarRootComponent;
69
68
 
70
- DataToolbar.SearchField = DataToolbarSearchField;
71
69
  DataToolbar.Button = DataToolbarButton;
72
- DataToolbar.ToggleButton = DataToolbarToggleButton;
73
70
 
74
71
  export {
75
72
  DataToolbar,
76
- DataToolbarSearchField,
77
73
  DataToolbarButton,
74
+ DataToolbarSearchField,
78
75
  DataToolbarToggleButton,
79
76
  };
80
77
  export default DataToolbar;
81
78
  export type {
79
+ DataToolbarButtonProps,
82
80
  DataToolbarProps,
83
81
  DataToolbarSearchFieldProps,
84
- DataToolbarButtonProps,
85
82
  DataToolbarToggleButtonProps,
86
83
  };
@@ -1,5 +1,6 @@
1
- import React, { InputHTMLAttributes, forwardRef, useRef } from "react";
1
+ import React, { InputHTMLAttributes, forwardRef } from "react";
2
2
  import { CalendarIcon } from "@navikt/aksel-icons";
3
+ import { Button } from "../button";
3
4
  import { ReadOnlyIcon } from "../form/ReadOnlyIcon";
4
5
  import { FormFieldProps, useFormField } from "../form/useFormField";
5
6
  import { BodyShort, ErrorMessage, Label } from "../typography";
@@ -68,10 +69,10 @@ const DateInput = forwardRef<HTMLInputElement, DateInputProps>((props, ref) => {
68
69
  description,
69
70
  variant = "datepicker",
70
71
  setAnchorRef,
72
+ "data-color": dataColor,
71
73
  ...rest
72
74
  } = props;
73
75
 
74
- const buttonRef = useRef<HTMLButtonElement>(null);
75
76
  const translate = useDateTranslationContext().translate;
76
77
 
77
78
  const isDatepickerVariant = variant === "datepicker";
@@ -113,6 +114,7 @@ const DateInput = forwardRef<HTMLInputElement, DateInputProps>((props, ref) => {
113
114
  "aksel-date__field--readonly": readOnly,
114
115
  },
115
116
  )}
117
+ data-color={dataColor}
116
118
  >
117
119
  <Label
118
120
  htmlFor={inputProps.id}
@@ -142,7 +144,7 @@ const DateInput = forwardRef<HTMLInputElement, DateInputProps>((props, ref) => {
142
144
  {...omit(rest, ["error", "errorId", "size"])}
143
145
  {...inputProps}
144
146
  autoComplete="off"
145
- aria-controls={context?.open ? context.ariaId : undefined}
147
+ aria-controls={context.open ? context.ariaId : undefined}
146
148
  readOnly={readOnly}
147
149
  className={cl(
148
150
  "aksel-date__field-input",
@@ -152,23 +154,22 @@ const DateInput = forwardRef<HTMLInputElement, DateInputProps>((props, ref) => {
152
154
  )}
153
155
  size={isDatepickerVariant ? 11 : 14}
154
156
  />
155
- <button
157
+ <Button
156
158
  disabled={inputProps.disabled || readOnly}
157
159
  tabIndex={readOnly ? -1 : undefined}
158
- onClick={() => {
159
- context?.onOpen();
160
- setAnchorRef?.(buttonRef.current);
161
- }}
160
+ onClick={context.onOpen}
162
161
  type="button"
163
162
  className="aksel-date__field-button"
164
- ref={buttonRef}
165
- >
166
- <CalendarIcon
167
- title={translate(
168
- conditionalVariables.iconTitle[context?.open ? "close" : "open"],
169
- )}
170
- />
171
- </button>
163
+ ref={setAnchorRef}
164
+ icon={<CalendarIcon aria-hidden />}
165
+ // If we have the title on the icon, NVDA will read "close" when focusing
166
+ // the button after closing the popup, even if we wait a tick.
167
+ title={translate(
168
+ conditionalVariables.iconTitle[context.open ? "close" : "open"],
169
+ )}
170
+ variant="secondary"
171
+ size={size}
172
+ />
172
173
  </div>
173
174
  <div
174
175
  className="aksel-form-field__error"