@navikt/ds-react 6.10.1 → 6.11.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 (143) hide show
  1. package/cjs/collapsible/parts/Collapsible.Content.js +1 -1
  2. package/cjs/collapsible/parts/Collapsible.Trigger.js +1 -1
  3. package/cjs/date/datepicker/types.d.ts +1 -1
  4. package/cjs/date/monthpicker/types.d.ts +1 -1
  5. package/cjs/form/file-upload/parts/Trigger.js +1 -1
  6. package/cjs/form/file-upload/parts/item/Item.d.ts +5 -0
  7. package/cjs/form/file-upload/parts/item/Item.js +2 -2
  8. package/cjs/form/file-upload/parts/item/Item.js.map +1 -1
  9. package/cjs/layout/base/BasePrimitive.d.ts +135 -0
  10. package/cjs/layout/base/BasePrimitive.js +40 -0
  11. package/cjs/layout/base/BasePrimitive.js.map +1 -0
  12. package/cjs/layout/bleed/Bleed.js +1 -1
  13. package/cjs/layout/box/Box.d.ts +10 -4
  14. package/cjs/layout/box/Box.js.map +1 -1
  15. package/cjs/layout/page/Page.d.ts +4 -3
  16. package/cjs/layout/page/Page.js.map +1 -1
  17. package/cjs/layout/responsive/Responsive.js +1 -1
  18. package/cjs/layout/utilities/types.d.ts +2 -1
  19. package/cjs/list/ListItem.js +2 -2
  20. package/cjs/list/ListItem.js.map +1 -1
  21. package/cjs/modal/dialog-polyfill.js +0 -3
  22. package/cjs/modal/dialog-polyfill.js.map +1 -1
  23. package/cjs/overlays/dismissablelayer/DismissableLayer.js +1 -1
  24. package/cjs/overlays/dismissablelayer/util/useFocusOutside.d.ts +2 -2
  25. package/cjs/overlays/dismissablelayer/util/useFocusOutside.js +6 -2
  26. package/cjs/overlays/dismissablelayer/util/useFocusOutside.js.map +1 -1
  27. package/cjs/overlays/dismissablelayer/util/usePointerDownOutside.d.ts +1 -1
  28. package/cjs/overlays/dismissablelayer/util/usePointerDownOutside.js +3 -1
  29. package/cjs/overlays/dismissablelayer/util/usePointerDownOutside.js.map +1 -1
  30. package/cjs/overlays/floating/Floating.js +1 -1
  31. package/cjs/pagination/Pagination.d.ts +7 -0
  32. package/cjs/pagination/Pagination.js +5 -2
  33. package/cjs/pagination/Pagination.js.map +1 -1
  34. package/cjs/pagination/PaginationItem.js +4 -2
  35. package/cjs/pagination/PaginationItem.js.map +1 -1
  36. package/cjs/portal/Portal.js +1 -1
  37. package/cjs/slot/Slot.d.ts +6 -0
  38. package/cjs/{util → slot}/Slot.js +6 -37
  39. package/cjs/slot/Slot.js.map +1 -0
  40. package/cjs/slot/merge-props.d.ts +4 -0
  41. package/cjs/slot/merge-props.js +37 -0
  42. package/cjs/slot/merge-props.js.map +1 -0
  43. package/cjs/toggle-group/ToggleGroup.d.ts +3 -3
  44. package/cjs/toggle-group/ToggleGroup.js +3 -3
  45. package/cjs/toggle-group/parts/ToggleItem.d.ts +32 -5
  46. package/cjs/toggle-group/parts/ToggleItem.js +4 -2
  47. package/cjs/toggle-group/parts/ToggleItem.js.map +1 -1
  48. package/cjs/util/hooks/descendants/useDescendant.js +1 -1
  49. package/cjs/util/hooks/descendants/useDescendant.js.map +1 -1
  50. package/cjs/util/hooks/descendants/utils.js +1 -1
  51. package/cjs/util/hooks/descendants/utils.js.map +1 -1
  52. package/cjs/util/i18n/get.js +3 -2
  53. package/cjs/util/i18n/get.js.map +1 -1
  54. package/esm/collapsible/parts/Collapsible.Content.js +1 -1
  55. package/esm/collapsible/parts/Collapsible.Trigger.js +1 -1
  56. package/esm/date/datepicker/types.d.ts +1 -1
  57. package/esm/date/monthpicker/types.d.ts +1 -1
  58. package/esm/form/file-upload/parts/Trigger.js +1 -1
  59. package/esm/form/file-upload/parts/item/Item.d.ts +5 -0
  60. package/esm/form/file-upload/parts/item/Item.js +2 -2
  61. package/esm/form/file-upload/parts/item/Item.js.map +1 -1
  62. package/esm/layout/base/BasePrimitive.d.ts +135 -0
  63. package/esm/layout/base/BasePrimitive.js +33 -0
  64. package/esm/layout/base/BasePrimitive.js.map +1 -0
  65. package/esm/layout/bleed/Bleed.js +1 -1
  66. package/esm/layout/box/Box.d.ts +10 -4
  67. package/esm/layout/box/Box.js.map +1 -1
  68. package/esm/layout/page/Page.d.ts +4 -3
  69. package/esm/layout/page/Page.js.map +1 -1
  70. package/esm/layout/responsive/Responsive.js +1 -1
  71. package/esm/layout/utilities/types.d.ts +2 -1
  72. package/esm/list/ListItem.js +3 -3
  73. package/esm/list/ListItem.js.map +1 -1
  74. package/esm/modal/dialog-polyfill.js +0 -3
  75. package/esm/modal/dialog-polyfill.js.map +1 -1
  76. package/esm/overlays/dismissablelayer/DismissableLayer.js +1 -1
  77. package/esm/overlays/dismissablelayer/util/useFocusOutside.d.ts +2 -2
  78. package/esm/overlays/dismissablelayer/util/useFocusOutside.js +6 -2
  79. package/esm/overlays/dismissablelayer/util/useFocusOutside.js.map +1 -1
  80. package/esm/overlays/dismissablelayer/util/usePointerDownOutside.d.ts +1 -1
  81. package/esm/overlays/dismissablelayer/util/usePointerDownOutside.js +3 -1
  82. package/esm/overlays/dismissablelayer/util/usePointerDownOutside.js.map +1 -1
  83. package/esm/overlays/floating/Floating.js +1 -1
  84. package/esm/pagination/Pagination.d.ts +7 -0
  85. package/esm/pagination/Pagination.js +6 -3
  86. package/esm/pagination/Pagination.js.map +1 -1
  87. package/esm/pagination/PaginationItem.js +4 -2
  88. package/esm/pagination/PaginationItem.js.map +1 -1
  89. package/esm/portal/Portal.js +1 -1
  90. package/esm/slot/Slot.d.ts +6 -0
  91. package/esm/slot/Slot.js +32 -0
  92. package/esm/slot/Slot.js.map +1 -0
  93. package/esm/slot/merge-props.d.ts +4 -0
  94. package/esm/slot/merge-props.js +34 -0
  95. package/esm/slot/merge-props.js.map +1 -0
  96. package/esm/toggle-group/ToggleGroup.d.ts +3 -3
  97. package/esm/toggle-group/ToggleGroup.js +3 -3
  98. package/esm/toggle-group/parts/ToggleItem.d.ts +32 -5
  99. package/esm/toggle-group/parts/ToggleItem.js +4 -2
  100. package/esm/toggle-group/parts/ToggleItem.js.map +1 -1
  101. package/esm/util/hooks/descendants/useDescendant.js +1 -1
  102. package/esm/util/hooks/descendants/useDescendant.js.map +1 -1
  103. package/esm/util/hooks/descendants/utils.js +1 -1
  104. package/esm/util/hooks/descendants/utils.js.map +1 -1
  105. package/esm/util/i18n/get.js +3 -2
  106. package/esm/util/i18n/get.js.map +1 -1
  107. package/package.json +3 -3
  108. package/src/collapsible/parts/Collapsible.Content.tsx +1 -1
  109. package/src/collapsible/parts/Collapsible.Trigger.tsx +1 -1
  110. package/src/date/datepicker/types.ts +1 -1
  111. package/src/date/monthpicker/types.ts +1 -1
  112. package/src/form/file-upload/parts/Trigger.tsx +1 -1
  113. package/src/form/file-upload/parts/item/Item.tsx +7 -1
  114. package/src/layout/base/BasePrimitive.tsx +232 -0
  115. package/src/layout/bleed/Bleed.tsx +1 -1
  116. package/src/layout/box/Box.tsx +11 -4
  117. package/src/layout/page/Page.tsx +4 -3
  118. package/src/layout/responsive/Responsive.tsx +1 -1
  119. package/src/layout/utilities/types.ts +2 -3
  120. package/src/list/ListItem.tsx +5 -5
  121. package/src/modal/dialog-polyfill.ts +0 -3
  122. package/src/overlays/dismissablelayer/DismissableLayer.tsx +1 -1
  123. package/src/overlays/dismissablelayer/util/useFocusOutside.ts +6 -2
  124. package/src/overlays/dismissablelayer/util/usePointerDownOutside.ts +3 -1
  125. package/src/overlays/floating/Floating.tsx +1 -1
  126. package/src/pagination/Pagination.tsx +26 -1
  127. package/src/pagination/PaginationItem.tsx +4 -0
  128. package/src/portal/Portal.tsx +1 -1
  129. package/src/slot/Slot.tsx +33 -0
  130. package/src/{util/Slot.tsx → slot/merge-props.ts} +2 -34
  131. package/src/slot/tests/merge-props.test.ts +49 -0
  132. package/src/toggle-group/ToggleGroup.test.tsx +3 -9
  133. package/src/toggle-group/ToggleGroup.tsx +3 -3
  134. package/src/toggle-group/parts/ToggleItem.tsx +54 -8
  135. package/src/util/hooks/descendants/useDescendant.tsx +1 -1
  136. package/src/util/hooks/descendants/utils.ts +1 -1
  137. package/src/util/i18n/get.ts +4 -2
  138. package/cjs/util/Slot.d.ts +0 -6
  139. package/cjs/util/Slot.js.map +0 -1
  140. package/esm/util/Slot.d.ts +0 -6
  141. package/esm/util/Slot.js +0 -63
  142. package/esm/util/Slot.js.map +0 -1
  143. /package/src/{util/__tests__ → slot/tests}/Slot.test.tsx +0 -0
@@ -3,19 +3,26 @@ import React, { forwardRef } from "react";
3
3
  import { OverridableComponent } from "../../util/types";
4
4
  import { getResponsiveProps } from "../utilities/css";
5
5
  import {
6
- BackgroundToken,
6
+ BackgroundColorToken,
7
7
  BorderColorToken,
8
8
  BorderRadiiToken,
9
9
  ResponsiveProp,
10
10
  ShadowToken,
11
11
  SpaceDelimitedAttribute,
12
12
  SpacingScale,
13
+ SurfaceColorToken,
13
14
  } from "../utilities/types";
14
15
 
15
16
  export interface BoxProps extends React.HTMLAttributes<HTMLDivElement> {
16
- /** CSS `background-color` property. Accepts a color token. */
17
- background?: BackgroundToken;
18
- /** CSS `border-color` property. Accepts a color token. */
17
+ /**
18
+ * CSS `background-color` property.
19
+ * Accepts a [background/surface color token](https://aksel.nav.no/grunnleggende/styling/design-tokens#afff774dad80).
20
+ */
21
+ background?: BackgroundColorToken | SurfaceColorToken;
22
+ /**
23
+ * CSS `border-color` property.
24
+ * Accepts a [border color token](https://aksel.nav.no/grunnleggende/styling/design-tokens#adb1767e2f87).
25
+ */
19
26
  borderColor?: BorderColorToken;
20
27
  /**
21
28
  * CSS `border-radius` property.
@@ -1,7 +1,7 @@
1
1
  import cl from "clsx";
2
2
  import React, { forwardRef } from "react";
3
- import bgColors from "@navikt/ds-tokens/src/colors-bg.json";
4
3
  import { OverridableComponent } from "../../util";
4
+ import { BackgroundColorToken } from "../utilities/types";
5
5
  import { PageBlock } from "./parts/PageBlock";
6
6
 
7
7
  export interface PageProps extends React.HTMLAttributes<HTMLElement> {
@@ -11,10 +11,11 @@ export interface PageProps extends React.HTMLAttributes<HTMLElement> {
11
11
  */
12
12
  as?: "div" | "body";
13
13
  /**
14
- * Background color. Accepts a color token.
14
+ * Background color.
15
+ * Accepts a [background color token](https://aksel.nav.no/grunnleggende/styling/design-tokens#753d1cf4d1d6).
15
16
  * @default "bg-default"
16
17
  */
17
- background?: keyof typeof bgColors.a | "surface-subtle";
18
+ background?: BackgroundColorToken;
18
19
  /**
19
20
  * Allows better positioning of footer
20
21
  */
@@ -1,6 +1,6 @@
1
1
  import cl from "clsx";
2
2
  import React, { HTMLAttributes, forwardRef } from "react";
3
- import { Slot } from "../../util/Slot";
3
+ import { Slot } from "../../slot/Slot";
4
4
  import { BreakpointsAlias } from "../utilities/types";
5
5
 
6
6
  export interface ResponsiveProps extends HTMLAttributes<HTMLDivElement> {
@@ -5,9 +5,8 @@ import surfaceColors from "@navikt/ds-tokens/src/colors-surface.json";
5
5
  import shadows from "@navikt/ds-tokens/src/shadow.json";
6
6
  import Spacing from "@navikt/ds-tokens/src/spacing.json";
7
7
 
8
- export type BackgroundToken =
9
- | keyof typeof bgColors.a
10
- | keyof typeof surfaceColors.a;
8
+ export type BackgroundColorToken = keyof typeof bgColors.a;
9
+ export type SurfaceColorToken = keyof typeof surfaceColors.a;
11
10
  export type BorderColorToken = keyof typeof borderColors.a;
12
11
  export type BorderRadiiToken =
13
12
  | keyof (typeof borderRadii.a)["border-radius"]
@@ -1,6 +1,6 @@
1
1
  import cl from "clsx";
2
2
  import React, { forwardRef, useContext } from "react";
3
- import { BodyShort, Label } from "../typography";
3
+ import { BodyLong } from "../typography";
4
4
  import { ListContext } from "./context";
5
5
  import { ListItemProps } from "./types";
6
6
 
@@ -45,14 +45,14 @@ export const ListItem = forwardRef<HTMLLIElement, ListItemProps>(
45
45
  </div>
46
46
  )}
47
47
 
48
- <BodyShort as="div" size={size} className="navds-list__item-content">
48
+ <BodyLong as="div" size={size} className="navds-list__item-content">
49
49
  {title && (
50
- <Label as="p" size={size}>
50
+ <BodyLong as="p" size={size} weight="semibold">
51
51
  {title}
52
- </Label>
52
+ </BodyLong>
53
53
  )}
54
54
  {children}
55
- </BodyShort>
55
+ </BodyLong>
56
56
  </li>
57
57
  );
58
58
  },
@@ -631,7 +631,6 @@ dialogPolyfill.DialogManager = function () {
631
631
  this.mo_ = new MutationObserver(function (records) {
632
632
  var removed = [];
633
633
  records.forEach(function (rec) {
634
- // biome-ignore lint/suspicious/noImplicitAnyLet: Reduntant to type c in this scenario
635
634
  for (var i = 0, c; (c = rec.removedNodes[i]); ++i) {
636
635
  if (!(c instanceof Element)) {
637
636
  continue;
@@ -679,7 +678,6 @@ dialogPolyfill.DialogManager.prototype.unblockDocument = function () {
679
678
  dialogPolyfill.DialogManager.prototype.updateStacking = function () {
680
679
  var zIndex = this.zIndexHigh_;
681
680
 
682
- // biome-ignore lint/suspicious/noImplicitAnyLet: Reduntant to type dpi in this scenario
683
681
  for (var i = 0, dpi; (dpi = this.pendingDialogStack[i]); ++i) {
684
682
  dpi.updateZIndex(--zIndex, --zIndex);
685
683
  if (i === 0) {
@@ -705,7 +703,6 @@ dialogPolyfill.DialogManager.prototype.containedByTopDialog_ = function (
705
703
  candidate,
706
704
  ) {
707
705
  while ((candidate = findNearestDialog(candidate))) {
708
- // biome-ignore lint/suspicious/noImplicitAnyLet: Reduntant to type dpi in this scenario
709
706
  for (var i = 0, dpi; (dpi = this.pendingDialogStack[i]); ++i) {
710
707
  if (dpi.dialog === candidate) {
711
708
  return i === 0; // only valid if top-most
@@ -5,7 +5,7 @@ import React, {
5
5
  useRef,
6
6
  useState,
7
7
  } from "react";
8
- import { Slot } from "../../util/Slot";
8
+ import { Slot } from "../../slot/Slot";
9
9
  import { useMergeRefs } from "../../util/hooks";
10
10
  import { createDescendantContext } from "../../util/hooks/descendants/useDescendant";
11
11
  import { AsChild } from "../../util/types/AsChild";
@@ -46,7 +46,11 @@ export function useFocusOutside(
46
46
  * we can eliminate the need for DOM traversal to verify if the focused element is within the react tree.
47
47
  */
48
48
  return {
49
- onFocusCapture: () => (isFocusInsideReactTreeRef.current = true),
50
- onBlurCapture: () => (isFocusInsideReactTreeRef.current = false),
49
+ onFocusCapture: () => {
50
+ isFocusInsideReactTreeRef.current = true;
51
+ },
52
+ onBlurCapture: () => {
53
+ isFocusInsideReactTreeRef.current = false;
54
+ },
51
55
  };
52
56
  }
@@ -90,6 +90,8 @@ export function usePointerDownOutside(
90
90
 
91
91
  return {
92
92
  // ensures we check React component tree (not just DOM tree)
93
- onPointerDownCapture: () => (isPointerInsideReactTreeRef.current = true),
93
+ onPointerDownCapture: () => {
94
+ isPointerInsideReactTreeRef.current = true;
95
+ },
94
96
  };
95
97
  }
@@ -17,7 +17,7 @@ import React, {
17
17
  useRef,
18
18
  useState,
19
19
  } from "react";
20
- import { Slot } from "../../util/Slot";
20
+ import { Slot } from "../../slot/Slot";
21
21
  import { createContext } from "../../util/create-context";
22
22
  import {
23
23
  useCallbackRef,
@@ -1,7 +1,8 @@
1
1
  import cl from "clsx";
2
2
  import React, { forwardRef } from "react";
3
3
  import { ChevronLeftIcon, ChevronRightIcon } from "@navikt/aksel-icons";
4
- import { BodyShort } from "../typography";
4
+ import { BodyShort, Heading } from "../typography";
5
+ import { useId } from "../util";
5
6
  import PaginationItem, {
6
7
  PaginationItemProps,
7
8
  PaginationItemType,
@@ -47,6 +48,13 @@ export interface PaginationProps extends React.HTMLAttributes<HTMLElement> {
47
48
  * @default (item: PaginationItemProps) => <PaginationItem {...item} />
48
49
  */
49
50
  renderItem?: (item: PaginationItemProps) => ReturnType<React.FC>;
51
+ /**
52
+ * Pagination heading. We recommend adding heading instead of `aria-label` to help assistive technologies with an extra navigation-stop.
53
+ */
54
+ srHeading?: {
55
+ tag: "h2" | "h3" | "h4" | "h5" | "h6";
56
+ text: string;
57
+ };
50
58
  }
51
59
 
52
60
  interface PaginationType
@@ -117,6 +125,8 @@ export const Pagination = forwardRef<HTMLElement, PaginationProps>(
117
125
  className,
118
126
  size = "medium",
119
127
  prevNextTexts = false,
128
+ srHeading,
129
+ "aria-labelledby": ariaLabelledBy,
120
130
  renderItem: Item = (item: PaginationItemProps) => (
121
131
  <PaginationItem {...item} />
122
132
  ),
@@ -124,6 +134,8 @@ export const Pagination = forwardRef<HTMLElement, PaginationProps>(
124
134
  },
125
135
  ref,
126
136
  ) => {
137
+ const headingId = useId();
138
+
127
139
  if (page < 1) {
128
140
  console.error("page cannot be less than 1");
129
141
  return null;
@@ -145,12 +157,25 @@ export const Pagination = forwardRef<HTMLElement, PaginationProps>(
145
157
  <nav
146
158
  ref={ref}
147
159
  {...rest}
160
+ aria-labelledby={
161
+ srHeading ? cl(headingId, ariaLabelledBy) : ariaLabelledBy
162
+ }
148
163
  className={cl(
149
164
  "navds-pagination",
150
165
  `navds-pagination--${size}`,
151
166
  className,
152
167
  )}
153
168
  >
169
+ {srHeading && (
170
+ <Heading
171
+ size="xsmall"
172
+ visuallyHidden
173
+ as={srHeading.tag}
174
+ id={headingId}
175
+ >
176
+ {srHeading.text}
177
+ </Heading>
178
+ )}
154
179
  <ul className="navds-pagination__list">
155
180
  <li>
156
181
  <Item
@@ -33,6 +33,7 @@ export const Item: PaginationItemType = forwardRef(
33
33
  as: Component = "button",
34
34
  selected = false,
35
35
  className,
36
+ page,
36
37
  ...rest
37
38
  },
38
39
  ref,
@@ -46,6 +47,9 @@ export const Item: PaginationItemType = forwardRef(
46
47
  className={cl("navds-pagination__item", className, {
47
48
  "navds-pagination__item--selected": selected,
48
49
  })}
50
+ data-page={page}
51
+ /* TODO: Breaking change to remove. Add to future major version. */
52
+ page={page}
49
53
  {...(Component === "button" && { type: "button" })}
50
54
  {...rest}
51
55
  >
@@ -1,7 +1,7 @@
1
1
  import React, { HTMLAttributes, forwardRef } from "react";
2
2
  import ReactDOM from "react-dom";
3
3
  import { useProvider } from "../provider/Provider";
4
- import { Slot } from "../util/Slot";
4
+ import { Slot } from "../slot/Slot";
5
5
  import { AsChildProps } from "../util/types";
6
6
 
7
7
  interface PortalBaseProps extends HTMLAttributes<HTMLDivElement> {
@@ -0,0 +1,33 @@
1
+ import * as React from "react";
2
+ import { mergeRefs } from "../util/hooks/useMergeRefs";
3
+ import { mergeProps } from "./merge-props";
4
+
5
+ interface SlotProps extends React.HTMLAttributes<HTMLElement> {
6
+ children?: React.ReactNode;
7
+ }
8
+
9
+ const Slot = React.forwardRef<HTMLElement, SlotProps>((props, forwardedRef) => {
10
+ const { children, ...slotProps } = props;
11
+
12
+ if (React.isValidElement(children)) {
13
+ return React.cloneElement<any>(children, {
14
+ ...mergeProps(slotProps, children.props),
15
+ ref: forwardedRef
16
+ ? mergeRefs([forwardedRef, (children as any).ref])
17
+ : (children as any).ref,
18
+ });
19
+ }
20
+
21
+ if (React.Children.count(children) > 1) {
22
+ const error = new Error(
23
+ "Aksel: Components using 'asChild' expects to recieve a single React element child.",
24
+ );
25
+ error.name = "SlotError";
26
+ Error.captureStackTrace?.(error, Slot);
27
+ throw error;
28
+ }
29
+
30
+ return null;
31
+ });
32
+
33
+ export { Slot, type SlotProps };
@@ -1,37 +1,3 @@
1
- // https://github.com/radix-ui/primitives/blob/main/packages/react/slot/src/Slot.tsx
2
- import * as React from "react";
3
- import { mergeRefs } from "./hooks/useMergeRefs";
4
-
5
- interface SlotProps extends React.HTMLAttributes<HTMLElement> {
6
- children?: React.ReactNode;
7
- }
8
-
9
- export const Slot = React.forwardRef<HTMLElement, SlotProps>(
10
- (props, forwardedRef) => {
11
- const { children, ...slotProps } = props;
12
-
13
- if (React.isValidElement(children)) {
14
- return React.cloneElement<any>(children, {
15
- ...mergeProps(slotProps, children.props),
16
- ref: forwardedRef
17
- ? mergeRefs([forwardedRef, (children as any).ref])
18
- : (children as any).ref,
19
- });
20
- }
21
-
22
- if (React.Children.count(children) > 1) {
23
- const error = new Error(
24
- "Aksel: Components using 'asChild' expects to recieve a single React element child.",
25
- );
26
- error.name = "SlotError";
27
- Error.captureStackTrace?.(error, Slot);
28
- throw error;
29
- }
30
-
31
- return null;
32
- },
33
- );
34
-
35
1
  function mergeProps(
36
2
  slotProps: Record<string, any>,
37
3
  childProps: Record<string, any>,
@@ -69,3 +35,5 @@ function mergeProps(
69
35
 
70
36
  return { ...slotProps, ...overrideProps };
71
37
  }
38
+
39
+ export { mergeProps };
@@ -0,0 +1,49 @@
1
+ import { describe, expect, test, vi } from "vitest";
2
+ import { mergeProps } from "../merge-props";
3
+
4
+ describe("Testing mergeProps function for Slot", () => {
5
+ test("child props should override slot props", () => {
6
+ const slotProps = { prop1: "slot", prop2: "slot" };
7
+ const childProps = { prop1: "child" };
8
+ const result = mergeProps(slotProps, childProps);
9
+ expect(result).toEqual({ prop1: "child", prop2: "slot" });
10
+ });
11
+
12
+ test("if a handler exists on both slot and child props, the composed function is created", () => {
13
+ const slotClick = vi.fn();
14
+ const childClick = vi.fn();
15
+ const slotProps = { onClick: slotClick };
16
+ const childProps = { onClick: childClick };
17
+ const result = mergeProps(slotProps, childProps);
18
+
19
+ result.onClick();
20
+
21
+ expect(slotClick).toHaveBeenCalledTimes(1);
22
+ expect(childClick).toHaveBeenCalledTimes(1);
23
+ });
24
+
25
+ test("if a handler exists only on the slot props, only the slot handler is used", () => {
26
+ const slotClick = vi.fn();
27
+ const slotProps = { onClick: slotClick };
28
+ const childProps = {};
29
+ const result = mergeProps(slotProps, childProps);
30
+
31
+ result.onClick();
32
+
33
+ expect(slotClick).toHaveBeenCalledTimes(1);
34
+ });
35
+
36
+ test("if the prop is `style`, the styles are merged", () => {
37
+ const slotProps = { style: { color: "red" } };
38
+ const childProps = { style: { backgroundColor: "blue" } };
39
+ const result = mergeProps(slotProps, childProps);
40
+ expect(result.style).toEqual({ color: "red", backgroundColor: "blue" });
41
+ });
42
+
43
+ test("if the prop is `className`, the class names are joined with a space", () => {
44
+ const slotProps = { className: "class1" };
45
+ const childProps = { className: "class2" };
46
+ const result = mergeProps(slotProps, childProps);
47
+ expect(result.className).toBe("class1 class2");
48
+ });
49
+ });
@@ -6,15 +6,9 @@ import { ToggleGroup } from "./ToggleGroup";
6
6
 
7
7
  const TestToggleGroup = ({ value, onChange, defaultValue }: any) => (
8
8
  <ToggleGroup value={value} onChange={onChange} defaultValue={defaultValue}>
9
- <ToggleGroup.Item value="toggle1" data-testid="toggle1">
10
- Toggle 1
11
- </ToggleGroup.Item>
12
- <ToggleGroup.Item value="toggle2" data-testid="toggle2">
13
- Toggle 2
14
- </ToggleGroup.Item>
15
- <ToggleGroup.Item value="toggle3" data-testid="toggle3">
16
- Toggle 3
17
- </ToggleGroup.Item>
9
+ <ToggleGroup.Item value="toggle1" data-testid="toggle1" label="Toggle 1" />
10
+ <ToggleGroup.Item value="toggle2" data-testid="toggle2" label="Toggle 2" />
11
+ <ToggleGroup.Item value="toggle3" data-testid="toggle3" label="Toggle 3" />
18
12
  </ToggleGroup>
19
13
  );
20
14
 
@@ -30,9 +30,9 @@ interface ToggleGroupComponent
30
30
  * @example
31
31
  * ```jsx
32
32
  * <ToggleGroup defaultValue="lest" onChange={console.log} size="small">
33
- * <ToggleGroup.Item value="ulest">Ulest</ToggleGroup.Item>
34
- * <ToggleGroup.Item value="lest">Leste</ToggleGroup.Item>
35
- * <ToggleGroup.Item value="sendt">Sendt</ToggleGroup.Item>
33
+ * <ToggleGroup.Item value="ulest" label="Ulest" />
34
+ * <ToggleGroup.Item value="lest" label="Leste" />
35
+ * <ToggleGroup.Item value="sendt" label="Sendt" />
36
36
  * </ToggleGroup>
37
37
  * ```
38
38
  */
@@ -4,21 +4,62 @@ import { BodyShort } from "../../typography/BodyShort";
4
4
  import { useToggleGroupContext } from "../ToggleGroup.context";
5
5
  import { useToggleItem } from "./useToggleItem";
6
6
 
7
- export interface ToggleGroupItemProps
8
- extends React.HTMLAttributes<HTMLButtonElement> {
7
+ type BaseProps = Omit<React.HTMLAttributes<HTMLButtonElement>, "children"> & {
9
8
  /**
10
- * Content.
9
+ * Value for state-handling.
10
+ */
11
+ value: string;
12
+ };
13
+
14
+ type ChildrenProps = {
15
+ /**
16
+ * @deprecated Use `label` and/or `icon` as replacement.
11
17
  */
12
18
  children: React.ReactNode;
19
+ label?: never;
20
+ icon?: never;
21
+ };
22
+
23
+ type LabelProps = {
24
+ children?: never;
13
25
  /**
14
- * Value for state-handling.
26
+ * Item label.
15
27
  */
16
- value: string;
17
- }
28
+ label: React.ReactNode;
29
+ /**
30
+ * Item Icon.
31
+ */
32
+ icon?: React.ReactNode;
33
+ };
34
+
35
+ type IconProps = {
36
+ children?: never;
37
+ /**
38
+ * Item label.
39
+ */
40
+ label?: React.ReactNode;
41
+ /**
42
+ * Item Icon.
43
+ */
44
+ icon: React.ReactNode;
45
+ };
46
+
47
+ export type ToggleGroupItemProps = BaseProps &
48
+ (ChildrenProps | LabelProps | IconProps);
18
49
 
19
50
  const ToggleItem = forwardRef<HTMLButtonElement, ToggleGroupItemProps>(
20
51
  (
21
- { className, children, value, onClick, onFocus, onKeyDown, ...rest },
52
+ {
53
+ className,
54
+ children,
55
+ icon,
56
+ label,
57
+ value,
58
+ onClick,
59
+ onFocus,
60
+ onKeyDown,
61
+ ...rest
62
+ },
22
63
  forwardedRef,
23
64
  ) => {
24
65
  const itemCtx = useToggleItem(
@@ -45,7 +86,12 @@ const ToggleItem = forwardRef<HTMLButtonElement, ToggleGroupItemProps>(
45
86
  className="navds-toggle-group__button-inner"
46
87
  size={ctx?.size}
47
88
  >
48
- {children}
89
+ {children ?? (
90
+ <>
91
+ {icon}
92
+ {label}
93
+ </>
94
+ )}
49
95
  </BodyShort>
50
96
  </button>
51
97
  );
@@ -53,7 +53,7 @@ export function createDescendantContext<
53
53
  useClientLayoutEffect(() => {
54
54
  if (!ref.current) return;
55
55
  const dataIndex = Number(ref.current.dataset["index"]);
56
- if (index != dataIndex && !Number.isNaN(dataIndex)) {
56
+ if (index !== dataIndex && !Number.isNaN(dataIndex)) {
57
57
  setIndex(dataIndex);
58
58
  }
59
59
  });
@@ -37,7 +37,7 @@ export function sortNodes(nodes: Node[]) {
37
37
  }
38
38
 
39
39
  export const isElement = (el: any): el is HTMLElement =>
40
- typeof el == "object" &&
40
+ typeof el === "object" &&
41
41
  "nodeType" in el &&
42
42
  el.nodeType === Node.ELEMENT_NODE;
43
43
 
@@ -38,10 +38,12 @@ export function get(
38
38
 
39
39
  function getKeypath(str: string) {
40
40
  const path: string[] = [];
41
- let result: RegExpExecArray | null;
42
- while ((result = OBJECT_NOTATION_MATCHER.exec(str))) {
41
+ let result = OBJECT_NOTATION_MATCHER.exec(str);
42
+
43
+ while (result) {
43
44
  const [, first, second] = result;
44
45
  path.push(first || second);
46
+ result = OBJECT_NOTATION_MATCHER.exec(str);
45
47
  }
46
48
 
47
49
  return path;
@@ -1,6 +0,0 @@
1
- import * as React from "react";
2
- interface SlotProps extends React.HTMLAttributes<HTMLElement> {
3
- children?: React.ReactNode;
4
- }
5
- export declare const Slot: React.ForwardRefExoticComponent<SlotProps & React.RefAttributes<HTMLElement>>;
6
- export {};
@@ -1 +0,0 @@
1
- {"version":3,"file":"Slot.js","sourceRoot":"","sources":["../../src/util/Slot.tsx"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,oFAAoF;AACpF,6CAA+B;AAC/B,uDAAiD;AAMpC,QAAA,IAAI,GAAG,KAAK,CAAC,UAAU,CAClC,CAAC,KAAK,EAAE,YAAY,EAAE,EAAE;;IACtB,MAAM,EAAE,QAAQ,KAAmB,KAAK,EAAnB,SAAS,UAAK,KAAK,EAAlC,YAA0B,CAAQ,CAAC;IAEzC,IAAI,KAAK,CAAC,cAAc,CAAC,QAAQ,CAAC,EAAE,CAAC;QACnC,OAAO,KAAK,CAAC,YAAY,CAAM,QAAQ,kCAClC,UAAU,CAAC,SAAS,EAAE,QAAQ,CAAC,KAAK,CAAC,KACxC,GAAG,EAAE,YAAY;gBACf,CAAC,CAAC,IAAA,wBAAS,EAAC,CAAC,YAAY,EAAG,QAAgB,CAAC,GAAG,CAAC,CAAC;gBAClD,CAAC,CAAE,QAAgB,CAAC,GAAG,IACzB,CAAC;IACL,CAAC;IAED,IAAI,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QACvC,MAAM,KAAK,GAAG,IAAI,KAAK,CACrB,oFAAoF,CACrF,CAAC;QACF,KAAK,CAAC,IAAI,GAAG,WAAW,CAAC;QACzB,MAAA,KAAK,CAAC,iBAAiB,sDAAG,KAAK,EAAE,YAAI,CAAC,CAAC;QACvC,MAAM,KAAK,CAAC;IACd,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC,CACF,CAAC;AAEF,SAAS,UAAU,CACjB,SAA8B,EAC9B,UAA+B;IAE/B,kCAAkC;IAClC,MAAM,aAAa,qBAAQ,UAAU,CAAE,CAAC;IAExC,KAAK,MAAM,QAAQ,IAAI,UAAU,EAAE,CAAC;QAClC,MAAM,aAAa,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC;QAC1C,MAAM,cAAc,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC;QAE5C,MAAM,SAAS,GAAG,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC5C,IAAI,SAAS,EAAE,CAAC;YACd,iDAAiD;YACjD,IAAI,aAAa,IAAI,cAAc,EAAE,CAAC;gBACpC,aAAa,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,IAAe,EAAE,EAAE;oBAC/C,cAAc,CAAC,GAAG,IAAI,CAAC,CAAC;oBACxB,aAAa,CAAC,GAAG,IAAI,CAAC,CAAC;gBACzB,CAAC,CAAC;YACJ,CAAC;YACD,0DAA0D;iBACrD,IAAI,aAAa,EAAE,CAAC;gBACvB,aAAa,CAAC,QAAQ,CAAC,GAAG,aAAa,CAAC;YAC1C,CAAC;QACH,CAAC;QACD,iCAAiC;aAC5B,IAAI,QAAQ,KAAK,OAAO,EAAE,CAAC;YAC9B,aAAa,CAAC,QAAQ,CAAC,mCAAQ,aAAa,GAAK,cAAc,CAAE,CAAC;QACpE,CAAC;aAAM,IAAI,QAAQ,KAAK,WAAW,EAAE,CAAC;YACpC,aAAa,CAAC,QAAQ,CAAC,GAAG,CAAC,aAAa,EAAE,cAAc,CAAC;iBACtD,MAAM,CAAC,OAAO,CAAC;iBACf,IAAI,CAAC,GAAG,CAAC,CAAC;QACf,CAAC;IACH,CAAC;IAED,uCAAY,SAAS,GAAK,aAAa,EAAG;AAC5C,CAAC"}
@@ -1,6 +0,0 @@
1
- import * as React from "react";
2
- interface SlotProps extends React.HTMLAttributes<HTMLElement> {
3
- children?: React.ReactNode;
4
- }
5
- export declare const Slot: React.ForwardRefExoticComponent<SlotProps & React.RefAttributes<HTMLElement>>;
6
- export {};
package/esm/util/Slot.js DELETED
@@ -1,63 +0,0 @@
1
- var __rest = (this && this.__rest) || function (s, e) {
2
- var t = {};
3
- for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
4
- t[p] = s[p];
5
- if (s != null && typeof Object.getOwnPropertySymbols === "function")
6
- for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
7
- if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
8
- t[p[i]] = s[p[i]];
9
- }
10
- return t;
11
- };
12
- // https://github.com/radix-ui/primitives/blob/main/packages/react/slot/src/Slot.tsx
13
- import * as React from "react";
14
- import { mergeRefs } from "./hooks/useMergeRefs.js";
15
- export const Slot = React.forwardRef((props, forwardedRef) => {
16
- var _a;
17
- const { children } = props, slotProps = __rest(props, ["children"]);
18
- if (React.isValidElement(children)) {
19
- return React.cloneElement(children, Object.assign(Object.assign({}, mergeProps(slotProps, children.props)), { ref: forwardedRef
20
- ? mergeRefs([forwardedRef, children.ref])
21
- : children.ref }));
22
- }
23
- if (React.Children.count(children) > 1) {
24
- const error = new Error("Aksel: Components using 'asChild' expects to recieve a single React element child.");
25
- error.name = "SlotError";
26
- (_a = Error.captureStackTrace) === null || _a === void 0 ? void 0 : _a.call(Error, error, Slot);
27
- throw error;
28
- }
29
- return null;
30
- });
31
- function mergeProps(slotProps, childProps) {
32
- // all child props should override
33
- const overrideProps = Object.assign({}, childProps);
34
- for (const propName in childProps) {
35
- const slotPropValue = slotProps[propName];
36
- const childPropValue = childProps[propName];
37
- const isHandler = /^on[A-Z]/.test(propName);
38
- if (isHandler) {
39
- // if the handler exists on both, we compose them
40
- if (slotPropValue && childPropValue) {
41
- overrideProps[propName] = (...args) => {
42
- childPropValue(...args);
43
- slotPropValue(...args);
44
- };
45
- }
46
- // but if it exists only on the slot, we use only this one
47
- else if (slotPropValue) {
48
- overrideProps[propName] = slotPropValue;
49
- }
50
- }
51
- // if it's `style`, we merge them
52
- else if (propName === "style") {
53
- overrideProps[propName] = Object.assign(Object.assign({}, slotPropValue), childPropValue);
54
- }
55
- else if (propName === "className") {
56
- overrideProps[propName] = [slotPropValue, childPropValue]
57
- .filter(Boolean)
58
- .join(" ");
59
- }
60
- }
61
- return Object.assign(Object.assign({}, slotProps), overrideProps);
62
- }
63
- //# sourceMappingURL=Slot.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"Slot.js","sourceRoot":"","sources":["../../src/util/Slot.tsx"],"names":[],"mappings":";;;;;;;;;;;AAAA,oFAAoF;AACpF,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAMjD,MAAM,CAAC,MAAM,IAAI,GAAG,KAAK,CAAC,UAAU,CAClC,CAAC,KAAK,EAAE,YAAY,EAAE,EAAE;;IACtB,MAAM,EAAE,QAAQ,KAAmB,KAAK,EAAnB,SAAS,UAAK,KAAK,EAAlC,YAA0B,CAAQ,CAAC;IAEzC,IAAI,KAAK,CAAC,cAAc,CAAC,QAAQ,CAAC,EAAE,CAAC;QACnC,OAAO,KAAK,CAAC,YAAY,CAAM,QAAQ,kCAClC,UAAU,CAAC,SAAS,EAAE,QAAQ,CAAC,KAAK,CAAC,KACxC,GAAG,EAAE,YAAY;gBACf,CAAC,CAAC,SAAS,CAAC,CAAC,YAAY,EAAG,QAAgB,CAAC,GAAG,CAAC,CAAC;gBAClD,CAAC,CAAE,QAAgB,CAAC,GAAG,IACzB,CAAC;IACL,CAAC;IAED,IAAI,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QACvC,MAAM,KAAK,GAAG,IAAI,KAAK,CACrB,oFAAoF,CACrF,CAAC;QACF,KAAK,CAAC,IAAI,GAAG,WAAW,CAAC;QACzB,MAAA,KAAK,CAAC,iBAAiB,sDAAG,KAAK,EAAE,IAAI,CAAC,CAAC;QACvC,MAAM,KAAK,CAAC;IACd,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC,CACF,CAAC;AAEF,SAAS,UAAU,CACjB,SAA8B,EAC9B,UAA+B;IAE/B,kCAAkC;IAClC,MAAM,aAAa,qBAAQ,UAAU,CAAE,CAAC;IAExC,KAAK,MAAM,QAAQ,IAAI,UAAU,EAAE,CAAC;QAClC,MAAM,aAAa,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC;QAC1C,MAAM,cAAc,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC;QAE5C,MAAM,SAAS,GAAG,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC5C,IAAI,SAAS,EAAE,CAAC;YACd,iDAAiD;YACjD,IAAI,aAAa,IAAI,cAAc,EAAE,CAAC;gBACpC,aAAa,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,IAAe,EAAE,EAAE;oBAC/C,cAAc,CAAC,GAAG,IAAI,CAAC,CAAC;oBACxB,aAAa,CAAC,GAAG,IAAI,CAAC,CAAC;gBACzB,CAAC,CAAC;YACJ,CAAC;YACD,0DAA0D;iBACrD,IAAI,aAAa,EAAE,CAAC;gBACvB,aAAa,CAAC,QAAQ,CAAC,GAAG,aAAa,CAAC;YAC1C,CAAC;QACH,CAAC;QACD,iCAAiC;aAC5B,IAAI,QAAQ,KAAK,OAAO,EAAE,CAAC;YAC9B,aAAa,CAAC,QAAQ,CAAC,mCAAQ,aAAa,GAAK,cAAc,CAAE,CAAC;QACpE,CAAC;aAAM,IAAI,QAAQ,KAAK,WAAW,EAAE,CAAC;YACpC,aAAa,CAAC,QAAQ,CAAC,GAAG,CAAC,aAAa,EAAE,cAAc,CAAC;iBACtD,MAAM,CAAC,OAAO,CAAC;iBACf,IAAI,CAAC,GAAG,CAAC,CAAC;QACf,CAAC;IACH,CAAC;IAED,uCAAY,SAAS,GAAK,aAAa,EAAG;AAC5C,CAAC"}