@navikt/ds-react 8.5.0 → 8.5.2

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 (135) hide show
  1. package/cjs/data/table/helpers/table-grid-nav.d.ts +9 -15
  2. package/cjs/data/table/helpers/table-grid-nav.js +18 -25
  3. package/cjs/data/table/helpers/table-grid-nav.js.map +1 -1
  4. package/cjs/data/table/helpers/table-keyboard.d.ts +1 -1
  5. package/cjs/data/table/helpers/table-keyboard.js +1 -6
  6. package/cjs/data/table/helpers/table-keyboard.js.map +1 -1
  7. package/cjs/data/table/root/DataTableRoot.d.ts +41 -4
  8. package/cjs/data/table/root/DataTableRoot.js +10 -6
  9. package/cjs/data/table/root/DataTableRoot.js.map +1 -1
  10. package/cjs/data/table/root/useTableKeyboardNav.d.ts +1 -1
  11. package/cjs/data/table/root/useTableKeyboardNav.js +32 -19
  12. package/cjs/data/table/root/useTableKeyboardNav.js.map +1 -1
  13. package/cjs/data/table/td/DataTableTd.d.ts +5 -4
  14. package/cjs/data/table/td/DataTableTd.js +2 -2
  15. package/cjs/data/table/td/DataTableTd.js.map +1 -1
  16. package/cjs/data/token-filter/AutoSuggest.d.ts +9 -0
  17. package/cjs/data/token-filter/AutoSuggest.js +56 -0
  18. package/cjs/data/token-filter/AutoSuggest.js.map +1 -0
  19. package/cjs/data/token-filter/AutoSuggest.types.d.ts +12 -0
  20. package/cjs/data/token-filter/AutoSuggest.types.js +3 -0
  21. package/cjs/data/token-filter/AutoSuggest.types.js.map +1 -0
  22. package/cjs/data/token-filter/TokenFilter.d.ts +11 -0
  23. package/cjs/data/token-filter/TokenFilter.js +102 -0
  24. package/cjs/data/token-filter/TokenFilter.js.map +1 -0
  25. package/cjs/data/token-filter/TokenFilter.types.d.ts +52 -0
  26. package/cjs/data/token-filter/TokenFilter.types.js +3 -0
  27. package/cjs/data/token-filter/TokenFilter.types.js.map +1 -0
  28. package/cjs/data/token-filter/helpers/generate-autocomplete-options.d.ts +24 -0
  29. package/cjs/data/token-filter/helpers/generate-autocomplete-options.js +197 -0
  30. package/cjs/data/token-filter/helpers/generate-autocomplete-options.js.map +1 -0
  31. package/cjs/data/token-filter/helpers/grouping.d.ts +28 -0
  32. package/cjs/data/token-filter/helpers/grouping.js +61 -0
  33. package/cjs/data/token-filter/helpers/grouping.js.map +1 -0
  34. package/cjs/data/token-filter/helpers/operators.d.ts +22 -0
  35. package/cjs/data/token-filter/helpers/operators.js +66 -0
  36. package/cjs/data/token-filter/helpers/operators.js.map +1 -0
  37. package/cjs/data/token-filter/helpers/parse-query-text.d.ts +25 -0
  38. package/cjs/data/token-filter/helpers/parse-query-text.js +46 -0
  39. package/cjs/data/token-filter/helpers/parse-query-text.js.map +1 -0
  40. package/cjs/data/token-filter/helpers/query-builder.d.ts +20 -0
  41. package/cjs/data/token-filter/helpers/query-builder.js +38 -0
  42. package/cjs/data/token-filter/helpers/query-builder.js.map +1 -0
  43. package/cjs/data/token-filter/helpers/text-matching.d.ts +16 -0
  44. package/cjs/data/token-filter/helpers/text-matching.js +47 -0
  45. package/cjs/data/token-filter/helpers/text-matching.js.map +1 -0
  46. package/cjs/form/combobox/Input/InputController.js +1 -1
  47. package/cjs/form/combobox/Input/InputController.js.map +1 -1
  48. package/cjs/form/file-upload/dropzone/FileUploadDropzone.js +1 -1
  49. package/cjs/form/file-upload/dropzone/FileUploadDropzone.js.map +1 -1
  50. package/cjs/tooltip/Tooltip.js +1 -1
  51. package/cjs/tooltip/Tooltip.js.map +1 -1
  52. package/cjs/utils/i18n/locales/nb.d.ts +75 -154
  53. package/cjs/utils/i18n/locales/nb.js +75 -154
  54. package/cjs/utils/i18n/locales/nb.js.map +1 -1
  55. package/esm/data/table/helpers/table-grid-nav.d.ts +9 -15
  56. package/esm/data/table/helpers/table-grid-nav.js +18 -25
  57. package/esm/data/table/helpers/table-grid-nav.js.map +1 -1
  58. package/esm/data/table/helpers/table-keyboard.d.ts +1 -1
  59. package/esm/data/table/helpers/table-keyboard.js +1 -6
  60. package/esm/data/table/helpers/table-keyboard.js.map +1 -1
  61. package/esm/data/table/root/DataTableRoot.d.ts +41 -4
  62. package/esm/data/table/root/DataTableRoot.js +10 -6
  63. package/esm/data/table/root/DataTableRoot.js.map +1 -1
  64. package/esm/data/table/root/useTableKeyboardNav.d.ts +1 -1
  65. package/esm/data/table/root/useTableKeyboardNav.js +32 -19
  66. package/esm/data/table/root/useTableKeyboardNav.js.map +1 -1
  67. package/esm/data/table/td/DataTableTd.d.ts +5 -4
  68. package/esm/data/table/td/DataTableTd.js +2 -2
  69. package/esm/data/table/td/DataTableTd.js.map +1 -1
  70. package/esm/data/token-filter/AutoSuggest.d.ts +9 -0
  71. package/esm/data/token-filter/AutoSuggest.js +20 -0
  72. package/esm/data/token-filter/AutoSuggest.js.map +1 -0
  73. package/esm/data/token-filter/AutoSuggest.types.d.ts +12 -0
  74. package/esm/data/token-filter/AutoSuggest.types.js +2 -0
  75. package/esm/data/token-filter/AutoSuggest.types.js.map +1 -0
  76. package/esm/data/token-filter/TokenFilter.d.ts +11 -0
  77. package/esm/data/token-filter/TokenFilter.js +66 -0
  78. package/esm/data/token-filter/TokenFilter.js.map +1 -0
  79. package/esm/data/token-filter/TokenFilter.types.d.ts +52 -0
  80. package/esm/data/token-filter/TokenFilter.types.js +2 -0
  81. package/esm/data/token-filter/TokenFilter.types.js.map +1 -0
  82. package/esm/data/token-filter/helpers/generate-autocomplete-options.d.ts +24 -0
  83. package/esm/data/token-filter/helpers/generate-autocomplete-options.js +195 -0
  84. package/esm/data/token-filter/helpers/generate-autocomplete-options.js.map +1 -0
  85. package/esm/data/token-filter/helpers/grouping.d.ts +28 -0
  86. package/esm/data/token-filter/helpers/grouping.js +59 -0
  87. package/esm/data/token-filter/helpers/grouping.js.map +1 -0
  88. package/esm/data/token-filter/helpers/operators.d.ts +22 -0
  89. package/esm/data/token-filter/helpers/operators.js +60 -0
  90. package/esm/data/token-filter/helpers/operators.js.map +1 -0
  91. package/esm/data/token-filter/helpers/parse-query-text.d.ts +25 -0
  92. package/esm/data/token-filter/helpers/parse-query-text.js +44 -0
  93. package/esm/data/token-filter/helpers/parse-query-text.js.map +1 -0
  94. package/esm/data/token-filter/helpers/query-builder.d.ts +20 -0
  95. package/esm/data/token-filter/helpers/query-builder.js +34 -0
  96. package/esm/data/token-filter/helpers/query-builder.js.map +1 -0
  97. package/esm/data/token-filter/helpers/text-matching.d.ts +16 -0
  98. package/esm/data/token-filter/helpers/text-matching.js +45 -0
  99. package/esm/data/token-filter/helpers/text-matching.js.map +1 -0
  100. package/esm/form/combobox/Input/InputController.js +1 -1
  101. package/esm/form/combobox/Input/InputController.js.map +1 -1
  102. package/esm/form/file-upload/dropzone/FileUploadDropzone.js +1 -1
  103. package/esm/form/file-upload/dropzone/FileUploadDropzone.js.map +1 -1
  104. package/esm/tooltip/Tooltip.js +2 -2
  105. package/esm/tooltip/Tooltip.js.map +1 -1
  106. package/esm/utils/i18n/locales/nb.d.ts +75 -154
  107. package/esm/utils/i18n/locales/nb.js +75 -154
  108. package/esm/utils/i18n/locales/nb.js.map +1 -1
  109. package/package.json +3 -3
  110. package/src/data/table/helpers/table-grid-nav.test.ts +659 -0
  111. package/src/data/table/helpers/table-grid-nav.ts +19 -38
  112. package/src/data/table/helpers/table-keyboard.ts +1 -10
  113. package/src/data/table/root/DataTableRoot.tsx +50 -10
  114. package/src/data/table/root/useTableKeyboardNav.ts +35 -23
  115. package/src/data/table/td/DataTableTd.tsx +13 -6
  116. package/src/data/token-filter/AutoSuggest.tsx +55 -0
  117. package/src/data/token-filter/AutoSuggest.types.ts +14 -0
  118. package/src/data/token-filter/TokenFilter.tsx +129 -0
  119. package/src/data/token-filter/TokenFilter.types.ts +85 -0
  120. package/src/data/token-filter/helpers/generate-autocomplete-options.test.ts +896 -0
  121. package/src/data/token-filter/helpers/generate-autocomplete-options.ts +289 -0
  122. package/src/data/token-filter/helpers/grouping.test.ts +206 -0
  123. package/src/data/token-filter/helpers/grouping.ts +73 -0
  124. package/src/data/token-filter/helpers/operators.test.ts +281 -0
  125. package/src/data/token-filter/helpers/operators.ts +91 -0
  126. package/src/data/token-filter/helpers/parse-query-text.test.ts +201 -0
  127. package/src/data/token-filter/helpers/parse-query-text.ts +86 -0
  128. package/src/data/token-filter/helpers/query-builder.test.ts +126 -0
  129. package/src/data/token-filter/helpers/query-builder.ts +41 -0
  130. package/src/data/token-filter/helpers/text-matching.test.ts +125 -0
  131. package/src/data/token-filter/helpers/text-matching.ts +58 -0
  132. package/src/form/combobox/Input/InputController.tsx +0 -1
  133. package/src/form/file-upload/dropzone/FileUploadDropzone.tsx +0 -1
  134. package/src/tooltip/Tooltip.tsx +3 -3
  135. package/src/utils/i18n/locales/nb.ts +4 -83
@@ -0,0 +1,126 @@
1
+ import { describe, expect, test } from "vitest";
2
+ import { buildQueryString } from "./query-builder";
3
+
4
+ describe("buildQueryString", () => {
5
+ describe("basic query building", () => {
6
+ test("builds complete query with all parts", () => {
7
+ expect(buildQueryString("Status", "=", "active")).toBe("Status = active");
8
+ });
9
+
10
+ test("builds query with property and operator only", () => {
11
+ expect(buildQueryString("Status", "=", "")).toBe("Status =");
12
+ });
13
+
14
+ test("builds query with property only", () => {
15
+ expect(buildQueryString("Status", "", "")).toBe("Status");
16
+ });
17
+
18
+ test("returns empty string when all parts are empty", () => {
19
+ expect(buildQueryString("", "", "")).toBe("");
20
+ });
21
+ });
22
+
23
+ describe("operator variations", () => {
24
+ test("builds query with contains operator", () => {
25
+ expect(buildQueryString("Name", ":", "test")).toBe("Name : test");
26
+ });
27
+
28
+ test("builds query with not equal operator", () => {
29
+ expect(buildQueryString("Status", "!=", "inactive")).toBe(
30
+ "Status != inactive",
31
+ );
32
+ });
33
+
34
+ test("builds query with starts with operator", () => {
35
+ expect(buildQueryString("ID", "^", "prefix")).toBe("ID ^ prefix");
36
+ });
37
+
38
+ test("builds query with greater than or equal operator", () => {
39
+ expect(buildQueryString("Count", ">=", "10")).toBe("Count >= 10");
40
+ });
41
+
42
+ test("builds query with all comparison operators", () => {
43
+ expect(buildQueryString("Value", ">", "5")).toBe("Value > 5");
44
+ expect(buildQueryString("Value", "<", "5")).toBe("Value < 5");
45
+ expect(buildQueryString("Value", ">=", "5")).toBe("Value >= 5");
46
+ expect(buildQueryString("Value", "<=", "5")).toBe("Value <= 5");
47
+ });
48
+ });
49
+
50
+ describe("whitespace handling", () => {
51
+ test("joins parts with single space", () => {
52
+ expect(buildQueryString("Property", "=", "value")).toBe(
53
+ "Property = value",
54
+ );
55
+ });
56
+
57
+ test("does not add extra spaces for missing parts", () => {
58
+ expect(buildQueryString("Property", "", "value")).toBe("Property value");
59
+ });
60
+
61
+ test("handles value with spaces", () => {
62
+ expect(buildQueryString("Region", "=", "US East")).toBe(
63
+ "Region = US East",
64
+ );
65
+ });
66
+
67
+ test("handles property with spaces", () => {
68
+ expect(buildQueryString("Instance ID", "=", "12345")).toBe(
69
+ "Instance ID = 12345",
70
+ );
71
+ });
72
+ });
73
+
74
+ describe("edge cases", () => {
75
+ test("handles numeric values", () => {
76
+ expect(buildQueryString("Count", "=", "123")).toBe("Count = 123");
77
+ });
78
+
79
+ test("handles special characters in value", () => {
80
+ expect(buildQueryString("Path", "=", "/var/log")).toBe("Path = /var/log");
81
+ });
82
+
83
+ test("handles hyphenated values", () => {
84
+ expect(buildQueryString("Region", "=", "us-east-1")).toBe(
85
+ "Region = us-east-1",
86
+ );
87
+ });
88
+
89
+ test("omits operator when undefined", () => {
90
+ expect(buildQueryString("Status", undefined as any, "active")).toBe(
91
+ "Status active",
92
+ );
93
+ });
94
+
95
+ test("omits value when null", () => {
96
+ expect(buildQueryString("Status", "=", null as any)).toBe("Status =");
97
+ });
98
+
99
+ test("filters out falsy values correctly", () => {
100
+ // 0 is falsy but should be filtered by Boolean()
101
+ expect(buildQueryString("Count", "=", "0")).toBe("Count = 0");
102
+ // Empty string is falsy and should be filtered
103
+ expect(buildQueryString("Test", "", "")).toBe("Test");
104
+ });
105
+ });
106
+
107
+ describe("real-world examples", () => {
108
+ test("builds property-only queries for operator selection", () => {
109
+ expect(buildQueryString("Status", "!", "")).toBe("Status !");
110
+ });
111
+
112
+ test("builds complete filter queries", () => {
113
+ expect(buildQueryString("Availability Zone", ":", "east")).toBe(
114
+ "Availability Zone : east",
115
+ );
116
+ });
117
+
118
+ test("builds negation queries", () => {
119
+ expect(buildQueryString("Status", "!=", "terminated")).toBe(
120
+ "Status != terminated",
121
+ );
122
+ expect(buildQueryString("Name", "!:", "test")).toBe("Name !: test");
123
+ expect(buildQueryString("ID", "!^", "prod")).toBe("ID !^ prod");
124
+ });
125
+ });
126
+ });
@@ -0,0 +1,41 @@
1
+ import type { QueryFilterOperator } from "../TokenFilter.types";
2
+
3
+ /**
4
+ * Human-readable labels for query filter operators.
5
+ * Used for displaying operator descriptions in autocomplete suggestions.
6
+ * TODO: Support i18n
7
+ */
8
+ const OPERATOR_LABELS: Record<QueryFilterOperator, string> = {
9
+ ":": "contains",
10
+ "!:": "does not contain",
11
+ "=": "is",
12
+ "!=": "is not",
13
+ "^": "starts with",
14
+ "!^": "does not start with",
15
+ ">=": "is greater than or equal to",
16
+ "<=": "is less than or equal to",
17
+ ">": "is greater than",
18
+ "<": "is less than",
19
+ };
20
+
21
+ /**
22
+ * Builds a query string from property label, operator, and value.
23
+ * Only includes non-empty parts, joined by spaces.
24
+ * @returns Space-joined query string
25
+ *
26
+ * @example
27
+ * buildQueryString("Status", "=", "active") // "Status = active"
28
+ * buildQueryString("Status", "=", "") // "Status ="
29
+ * buildQueryString("Status", "", "") // "Status"
30
+ * buildQueryString("", "", "") // ""
31
+ */
32
+ function buildQueryString(
33
+ propertyLabel: string,
34
+ operator: string,
35
+ value: string,
36
+ ): string {
37
+ const parts = [propertyLabel, operator, value].filter(Boolean);
38
+ return parts.join(" ");
39
+ }
40
+
41
+ export { buildQueryString, OPERATOR_LABELS };
@@ -0,0 +1,125 @@
1
+ import { describe, expect, test } from "vitest";
2
+ import { matchesFilterText } from "./text-matching";
3
+
4
+ describe("matchesFilterText", () => {
5
+ describe("basic matching", () => {
6
+ test("returns true for empty filter text", () => {
7
+ expect(matchesFilterText(["hello", "world"], "")).toBe(true);
8
+ });
9
+
10
+ test("returns true for whitespace-only filter text", () => {
11
+ expect(matchesFilterText(["hello", "world"], " ")).toBe(true);
12
+ });
13
+
14
+ test("returns true when single word matches", () => {
15
+ expect(matchesFilterText(["hello", "world"], "hello")).toBe(true);
16
+ });
17
+
18
+ test("returns true when match is case-insensitive", () => {
19
+ expect(matchesFilterText(["Hello World"], "hello")).toBe(true);
20
+ expect(matchesFilterText(["hello world"], "HELLO")).toBe(true);
21
+ });
22
+
23
+ test("returns true when partial match exists", () => {
24
+ expect(matchesFilterText(["testing"], "test")).toBe(true);
25
+ });
26
+
27
+ test("returns false when no match exists", () => {
28
+ expect(matchesFilterText(["hello", "world"], "foo")).toBe(false);
29
+ });
30
+ });
31
+
32
+ describe("multi-word matching", () => {
33
+ test("returns true when all words match across fields", () => {
34
+ expect(matchesFilterText(["hello", "world"], "hello world")).toBe(true);
35
+ });
36
+
37
+ test("returns true when all words match in single field", () => {
38
+ expect(matchesFilterText(["hello world"], "hello world")).toBe(true);
39
+ });
40
+
41
+ test("returns true when words match in any order", () => {
42
+ expect(matchesFilterText(["world hello"], "hello world")).toBe(true);
43
+ });
44
+
45
+ test("returns false when not all words match", () => {
46
+ expect(matchesFilterText(["hello"], "hello world")).toBe(false);
47
+ });
48
+
49
+ test("handles multiple spaces in filter text", () => {
50
+ expect(matchesFilterText(["hello world"], "hello world")).toBe(true);
51
+ });
52
+ });
53
+
54
+ describe("null/undefined handling", () => {
55
+ test("returns false for null searchFieldValues", () => {
56
+ expect(matchesFilterText(null as any, "test")).toBe(false);
57
+ });
58
+
59
+ test("returns false for undefined searchFieldValues", () => {
60
+ expect(matchesFilterText(undefined as any, "test")).toBe(false);
61
+ });
62
+
63
+ test("returns true for null filterText", () => {
64
+ expect(matchesFilterText(["test"], null as any)).toBe(true);
65
+ });
66
+
67
+ test("returns true for undefined filterText", () => {
68
+ expect(matchesFilterText(["test"], undefined as any)).toBe(true);
69
+ });
70
+
71
+ test("filters out null values in searchFieldValues", () => {
72
+ expect(matchesFilterText(["hello", null as any, "world"], "world")).toBe(
73
+ true,
74
+ );
75
+ });
76
+
77
+ test("filters out undefined values in searchFieldValues", () => {
78
+ expect(
79
+ matchesFilterText(["hello", undefined as any, "world"], "world"),
80
+ ).toBe(true);
81
+ });
82
+
83
+ test("returns false when all searchFieldValues are null/undefined", () => {
84
+ expect(matchesFilterText([null as any, undefined as any], "test")).toBe(
85
+ false,
86
+ );
87
+ });
88
+ });
89
+
90
+ describe("edge cases", () => {
91
+ test("returns true for empty searchFieldValues with empty filter", () => {
92
+ expect(matchesFilterText([], "")).toBe(true);
93
+ });
94
+
95
+ test("returns false for empty searchFieldValues with non-empty filter", () => {
96
+ expect(matchesFilterText([], "test")).toBe(false);
97
+ });
98
+
99
+ test("handles special characters in filter text", () => {
100
+ expect(matchesFilterText(["test-value"], "test-value")).toBe(true);
101
+ expect(matchesFilterText(["test.value"], "test.value")).toBe(true);
102
+ });
103
+
104
+ test("handles numbers in filter text", () => {
105
+ expect(matchesFilterText(["version 123"], "123")).toBe(true);
106
+ });
107
+
108
+ test("handles unicode characters", () => {
109
+ expect(matchesFilterText(["café"], "café")).toBe(true);
110
+ expect(matchesFilterText(["🎉 party"], "party")).toBe(true);
111
+ });
112
+
113
+ test("trims whitespace from filter text", () => {
114
+ expect(matchesFilterText(["hello"], " hello ")).toBe(true);
115
+ });
116
+
117
+ test("handles array with single empty string", () => {
118
+ expect(matchesFilterText([""], "test")).toBe(false);
119
+ });
120
+
121
+ test("matches when filter is substring of field", () => {
122
+ expect(matchesFilterText(["prefix-test-suffix"], "test")).toBe(true);
123
+ });
124
+ });
125
+ });
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Checks if search field values match the given filter text.
3
+ *
4
+ * @param searchFieldValues - Array of strings to search within (e.g., labels, tags, descriptions)
5
+ * @param filterText - The search text to match against
6
+ * @returns true if all space-separated parts of filterText are found in at least one searchFieldValue
7
+ *
8
+ * @example
9
+ * matchesFilterText(['Hello World', 'foo'], 'hello') // true
10
+ * matchesFilterText(['Hello World', 'foo'], 'hello bar') // false
11
+ * matchesFilterText(['Hello World', 'bar'], 'hello bar') // true
12
+ * matchesFilterText([], 'test') // false
13
+ * matchesFilterText(['test'], '') // true (empty filter matches all)
14
+ */
15
+ function matchesFilterText(
16
+ searchFieldValues: string[],
17
+ filterText: string,
18
+ ): boolean {
19
+ /* Guard against null/undefined inputs */
20
+ if (!searchFieldValues || !Array.isArray(searchFieldValues)) {
21
+ return false;
22
+ }
23
+
24
+ if (filterText === null || filterText === undefined) {
25
+ return true;
26
+ }
27
+
28
+ const normalizedFilter = filterText.trim().toLowerCase();
29
+
30
+ /* Empty filter matches everything */
31
+ if (!normalizedFilter) {
32
+ return true;
33
+ }
34
+
35
+ /* Split filter into parts ("nord land" -> ["nord", "land"]) */
36
+ const parts = normalizedFilter.split(/\s+/).filter(Boolean);
37
+
38
+ if (parts.length === 0) {
39
+ return true;
40
+ }
41
+
42
+ /* Normalize and filter out nullish values */
43
+ const normalizedFields = searchFieldValues
44
+ .map((value) => value?.toLowerCase())
45
+ .filter(Boolean);
46
+
47
+ /* If no valid fields to search, no match */
48
+ if (normalizedFields.length === 0) {
49
+ return false;
50
+ }
51
+
52
+ /* Every part of the filter must be found in at least one field */
53
+ return parts.every((part) =>
54
+ normalizedFields.some((field) => field.includes(part)),
55
+ );
56
+ }
57
+
58
+ export { matchesFilterText };
@@ -1,5 +1,4 @@
1
1
  /* eslint-disable jsx-a11y/click-events-have-key-events */
2
-
3
2
  /* eslint-disable jsx-a11y/no-static-element-interactions */
4
3
  import React, { forwardRef } from "react";
5
4
  import { Floating } from "../../../utils/components/floating/Floating";
@@ -1,5 +1,4 @@
1
1
  /* eslint-disable jsx-a11y/no-static-element-interactions */
2
-
3
2
  /* eslint-disable jsx-a11y/click-events-have-key-events */
4
3
  import React, { forwardRef } from "react";
5
4
  import { CircleSlashIcon, CloudUpIcon } from "@navikt/aksel-icons";
@@ -11,7 +11,7 @@ import {
11
11
  useHover,
12
12
  useInteractions,
13
13
  } from "@floating-ui/react";
14
- import React, { HTMLAttributes, forwardRef, useRef } from "react";
14
+ import React, { Fragment, HTMLAttributes, forwardRef, useRef } from "react";
15
15
  import { useModalContext } from "../modal/Modal.context";
16
16
  import { Portal } from "../portal";
17
17
  import { HStack } from "../primitives/stack";
@@ -296,7 +296,7 @@ function TooltipShortcuts({ shortcuts }: { shortcuts: TooltipProps["keys"] }) {
296
296
  return (
297
297
  <span className="aksel-tooltip__keys" aria-hidden>
298
298
  {shortcuts.map((key, index) => (
299
- <>
299
+ <Fragment key={key.join("+")}>
300
300
  <HStack gap="space-4">
301
301
  {key.map((k, i) => (
302
302
  <Detail as="kbd" key={i} className="aksel-tooltip__key">
@@ -307,7 +307,7 @@ function TooltipShortcuts({ shortcuts }: { shortcuts: TooltipProps["keys"] }) {
307
307
  {index < shortcuts.length - 1 && (
308
308
  <span> {translate("shortcutSeparator")} </span>
309
309
  )}
310
- </>
310
+ </Fragment>
311
311
  ))}
312
312
  </span>
313
313
  );
@@ -7,218 +7,139 @@ interface TranslationMap {
7
7
  export default {
8
8
  global: {
9
9
  dateLocale: nb,
10
- /** @default "Vis mer" */
11
10
  showMore: "Vis mer",
12
- /** @default "Vis mindre" */
13
11
  showLess: "Vis mindre",
14
- /** @default "Skrivebeskyttet" */
15
12
  readOnly: "Skrivebeskyttet",
16
- /** @default "Lukk" */
17
13
  close: "Lukk",
18
- /** @default "Feil" */
19
14
  error: "Feil",
20
- /** @default "Informasjon" */
21
15
  info: "Informasjon",
22
- /** @default "Suksess" */
23
16
  success: "Suksess",
24
- /** @default "Advarsel" */
25
17
  warning: "Advarsel",
26
- /** @default "Kunngjøring" */
27
18
  announcement: "Kunngjøring",
28
19
  },
29
20
  Chips: {
30
21
  Removable: {
31
- /** Will be appended to the accessible name for the button.
32
- * @default "slett" */
22
+ /** Will be appended to the accessible name for the button. */
33
23
  labelSuffix: "slett",
34
24
  },
35
25
  },
36
26
  Combobox: {
37
- /** The input value will be appended to the end of this text, e.g. `Legg til "input value"`.
38
- * @default "Legg til" */
27
+ /** The input value will be appended to the end of this text, e.g. `Legg til "input value"`. */
39
28
  addOption: "Legg til",
40
- /** @default "Ingen søketreff" */
41
29
  noMatches: "Ingen søketreff",
42
- /** Loader title
43
- * @default "Søker…" */
30
+ /** Loader title */
44
31
  loading: "Søker…",
45
- /** @default "{selected} av maks {limit} er valgt." */
46
32
  maxSelected: "{selected} av maks {limit} er valgt.",
47
33
  },
48
34
  CopyButton: {
49
- /** @default "Kopier" */
50
35
  title: "Kopier",
51
- /** @default "Kopiert!" */
52
36
  activeText: "Kopiert!",
53
37
  },
54
38
  DatePicker: {
55
- /** @default "Velg dato" */
56
39
  chooseDate: "Velg dato",
57
- /** @default "Velg datoer" */
58
40
  chooseDates: "Velg datoer",
59
- /** @default "Velg start- og sluttdato" */
60
41
  chooseDateRange: "Velg start- og sluttdato",
61
- /** @default "Velg måned" */
62
42
  chooseMonth: "Velg måned",
63
- /** @default "Uke" */
64
43
  week: "Uke",
65
- /** @default "Uke {week}" */
66
44
  weekNumber: "Uke {week}",
67
- /** @default "Velg uke {week}" */
68
45
  selectWeekNumber: "Velg uke {week}",
69
- /** @default "Måned" */
70
46
  month: "Måned",
71
- /** @default "Gå til neste måned" */
72
47
  goToNextMonth: "Gå til neste måned",
73
- /** @default "Gå til forrige måned" */
74
48
  goToPreviousMonth: "Gå til forrige måned",
75
- /** @default "År" */
76
49
  year: "År",
77
- /** @default "Gå til neste år" */
78
50
  goToNextYear: "Gå til neste år",
79
- /** @default "Gå til forrige år" */
80
51
  goToPreviousYear: "Gå til forrige år",
81
- /** @default "Åpne datovelger" */
82
52
  openDatePicker: "Åpne datovelger",
83
- /** @default "Åpne månedsvelger" */
84
53
  openMonthPicker: "Åpne månedsvelger",
85
- /** @default "Lukk datovelger" */
86
54
  closeDatePicker: "Lukk datovelger",
87
- /** @default "Lukk månedsvelger" */
88
55
  closeMonthPicker: "Lukk månedsvelger",
89
56
  },
90
57
  ErrorSummary: {
91
- /** @default "Du må rette disse feilene før du kan fortsette:" */
92
58
  heading: "Du må rette disse feilene før du kan fortsette:",
93
59
  },
94
60
  FileUpload: {
95
61
  dropzone: {
96
- /** @default "Velg fil" */
97
62
  button: "Velg fil",
98
- /** @default "Velg filer" */
99
63
  buttonMultiple: "Velg filer",
100
- /** @default "Dra og slipp filen her" */
101
64
  dragAndDrop: "Dra og slipp filen her",
102
- /** @default "Dra og slipp filer her" */
103
65
  dragAndDropMultiple: "Dra og slipp filer her",
104
- /** @default "Slipp" */
105
66
  drop: "Slipp",
106
- /** @default "eller" */
107
67
  or: "eller",
108
- /** @default "Filopplasting er deaktivert" */
109
68
  disabled: "Filopplasting er deaktivert",
110
- /** @default "Du kan ikke laste opp flere filer" */
111
69
  disabledFilelimit: "Du kan ikke laste opp flere filer",
112
70
  },
113
71
  item: {
114
- /** @default "Prøv å laste opp filen på nytt" */
115
72
  retryButtonTitle: "Prøv å laste opp filen på nytt",
116
- /** @default "Slett filen" */
117
73
  deleteButtonTitle: "Slett filen",
118
- /** @default "Laster opp…" */
119
74
  uploading: "Laster opp…",
120
- /** @default "Laster ned…" */
121
75
  downloading: "Laster ned…",
122
76
  },
123
77
  },
124
78
  FormProgress: {
125
- /** @default "Steg {activeStep} av {totalSteps}" */
126
79
  step: "Steg {activeStep} av {totalSteps}",
127
- /** @default "Vis alle steg" */
128
80
  showAllSteps: "Vis alle steg",
129
- /** @default "Skjul alle steg" */
130
81
  hideAllSteps: "Skjul alle steg",
131
82
  },
132
83
  FormSummary: {
133
- /** @default "Endre svar" */
134
84
  editAnswer: "Endre svar",
135
85
  },
136
86
  GuidePanel: {
137
- /** @default "Illustrasjon av veileder" */
138
87
  illustrationLabel: "Illustrasjon av veileder",
139
88
  },
140
89
  HelpText: {
141
- /** @default "Mer informasjon" */
142
90
  title: "Mer informasjon",
143
91
  },
144
92
  Loader: {
145
- /** @default "Venter…" */
146
93
  title: "Venter…",
147
94
  },
148
95
  Pagination: {
149
- /** @default "Forrige" */
150
96
  previous: "Forrige",
151
- /** @default "Neste" */
152
97
  next: "Neste",
153
98
  },
154
99
  Process: {
155
- /** @default "Aktiv" */
156
100
  active: "Aktiv",
157
101
  },
158
102
  ProgressBar: {
159
- /** @default "{current} av {max}" */
160
103
  progress: "{current} av {max}",
161
- /** @default "Fremdrift kan ikke beregnes, antatt tid er {seconds} sekunder." */
162
104
  progressUnknown:
163
105
  "Fremdrift kan ikke beregnes, antatt tid er {seconds} sekunder.",
164
106
  },
165
107
  Search: {
166
- /** @default "Tøm feltet" */
167
108
  clear: "Tøm feltet",
168
- /** @default "Søk" */
169
109
  search: "Søk",
170
110
  },
171
111
  Textarea: {
172
- /** Screen readers only
173
- * @default "Tekstområde med plass til {maxLength} tegn." */
112
+ /** Screen readers only */
174
113
  maxLength: "Tekstområde med plass til {maxLength} tegn.",
175
- /** @default "{chars} tegn for mye" */
176
114
  charsTooMany: "{chars} tegn for mye",
177
- /** @default "{chars} tegn igjen" */
178
115
  charsLeft: "{chars} tegn igjen",
179
116
  },
180
117
  Timeline: {
181
- /** @default "dd.MM.yyyy" */
182
118
  dateFormat: "dd.MM.yyyy",
183
- /** @default "dd.MM" */
184
119
  dayFormat: "dd.MM",
185
- /** @default "MMM yy" */
186
120
  monthFormat: "MMM yy",
187
- /** @default "yyyy" */
188
121
  yearFormat: "yyyy",
189
122
  Row: {
190
- /** @default "Ingen perioder" */
191
123
  noPeriods: "Ingen perioder",
192
- /** @default "{start} til {end}" */
193
124
  period: "{start} til {end}",
194
125
  },
195
126
  Period: {
196
- /** @default "Suksess" */
197
127
  success: "Suksess",
198
- /** @default "Advarsel" */
199
128
  warning: "Advarsel",
200
- /** @default "Fare" */
201
129
  danger: "Fare",
202
- /** @default "Info" */
203
130
  info: "Info",
204
- /** @default "Nøytral" */
205
131
  neutral: "Nøytral",
206
- /** @default "{status} fra {start} til {end}" */
207
132
  period: "{status} fra {start} til {end}",
208
133
  },
209
134
  Pin: {
210
- /** @default "Pin: {date}" */
211
135
  pin: "Pin: {date}",
212
136
  },
213
137
  Zoom: {
214
- /** @default "Zoom tidslinjen {start} til {end}" */
215
138
  zoom: "Zoom tidslinjen {start} til {end}",
216
- /** @default "Tilbakestill tidsperspektiv" */
217
139
  reset: "Tilbakestill tidsperspektiv",
218
140
  },
219
141
  },
220
142
  Tooltip: {
221
- /** @default "eller" */
222
143
  shortcutSeparator: "eller",
223
144
  },
224
145
  } satisfies TranslationMap;