@navikt/ds-react 7.23.2 → 7.25.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 (80) hide show
  1. package/cjs/chat/Chat.js +4 -3
  2. package/cjs/chat/Chat.js.map +1 -1
  3. package/cjs/form/file-upload/parts/item/Item.d.ts +1 -1
  4. package/cjs/form/file-upload/parts/item/Item.js +10 -2
  5. package/cjs/form/file-upload/parts/item/Item.js.map +1 -1
  6. package/cjs/form/search/Search.js +5 -3
  7. package/cjs/form/search/Search.js.map +1 -1
  8. package/cjs/form/switch/Switch.js +23 -2
  9. package/cjs/form/switch/Switch.js.map +1 -1
  10. package/cjs/index.d.ts +1 -0
  11. package/cjs/index.js +3 -1
  12. package/cjs/index.js.map +1 -1
  13. package/cjs/link-card/LinkCard.d.ts +126 -0
  14. package/cjs/link-card/LinkCard.js +141 -0
  15. package/cjs/link-card/LinkCard.js.map +1 -0
  16. package/cjs/link-card/index.d.ts +2 -0
  17. package/cjs/link-card/index.js +13 -0
  18. package/cjs/link-card/index.js.map +1 -0
  19. package/cjs/link-panel/LinkPanel.d.ts +3 -11
  20. package/cjs/link-panel/LinkPanel.js +3 -11
  21. package/cjs/link-panel/LinkPanel.js.map +1 -1
  22. package/cjs/modal/ModalUtils.d.ts +2 -1
  23. package/cjs/modal/ModalUtils.js +21 -12
  24. package/cjs/modal/ModalUtils.js.map +1 -1
  25. package/cjs/table/Table.d.ts +5 -0
  26. package/cjs/table/Table.js +2 -1
  27. package/cjs/table/Table.js.map +1 -1
  28. package/cjs/util/link-anchor/LinkAnchor.d.ts +26 -0
  29. package/cjs/util/link-anchor/LinkAnchor.js +110 -0
  30. package/cjs/util/link-anchor/LinkAnchor.js.map +1 -0
  31. package/cjs/util/link-anchor/index.d.ts +2 -0
  32. package/cjs/util/link-anchor/index.js +9 -0
  33. package/cjs/util/link-anchor/index.js.map +1 -0
  34. package/esm/chat/Chat.js +4 -3
  35. package/esm/chat/Chat.js.map +1 -1
  36. package/esm/form/file-upload/parts/item/Item.d.ts +1 -1
  37. package/esm/form/file-upload/parts/item/Item.js +10 -2
  38. package/esm/form/file-upload/parts/item/Item.js.map +1 -1
  39. package/esm/form/search/Search.js +5 -3
  40. package/esm/form/search/Search.js.map +1 -1
  41. package/esm/form/switch/Switch.js +23 -2
  42. package/esm/form/switch/Switch.js.map +1 -1
  43. package/esm/index.d.ts +1 -0
  44. package/esm/index.js +1 -0
  45. package/esm/index.js.map +1 -1
  46. package/esm/link-card/LinkCard.d.ts +126 -0
  47. package/esm/link-card/LinkCard.js +105 -0
  48. package/esm/link-card/LinkCard.js.map +1 -0
  49. package/esm/link-card/index.d.ts +2 -0
  50. package/esm/link-card/index.js +3 -0
  51. package/esm/link-card/index.js.map +1 -0
  52. package/esm/link-panel/LinkPanel.d.ts +3 -11
  53. package/esm/link-panel/LinkPanel.js +3 -11
  54. package/esm/link-panel/LinkPanel.js.map +1 -1
  55. package/esm/modal/ModalUtils.d.ts +2 -1
  56. package/esm/modal/ModalUtils.js +20 -11
  57. package/esm/modal/ModalUtils.js.map +1 -1
  58. package/esm/table/Table.d.ts +5 -0
  59. package/esm/table/Table.js +2 -1
  60. package/esm/table/Table.js.map +1 -1
  61. package/esm/util/link-anchor/LinkAnchor.d.ts +26 -0
  62. package/esm/util/link-anchor/LinkAnchor.js +72 -0
  63. package/esm/util/link-anchor/LinkAnchor.js.map +1 -0
  64. package/esm/util/link-anchor/index.d.ts +2 -0
  65. package/esm/util/link-anchor/index.js +3 -0
  66. package/esm/util/link-anchor/index.js.map +1 -0
  67. package/package.json +13 -3
  68. package/src/chat/Chat.tsx +15 -13
  69. package/src/form/file-upload/parts/item/Item.tsx +20 -6
  70. package/src/form/search/Search.tsx +5 -1
  71. package/src/form/switch/Switch.tsx +79 -26
  72. package/src/index.ts +10 -0
  73. package/src/link-card/LinkCard.tsx +317 -0
  74. package/src/link-card/index.tsx +20 -0
  75. package/src/link-panel/LinkPanel.tsx +3 -11
  76. package/src/modal/Modal.test.tsx +12 -4
  77. package/src/modal/ModalUtils.ts +24 -7
  78. package/src/table/Table.tsx +7 -0
  79. package/src/util/link-anchor/LinkAnchor.tsx +153 -0
  80. package/src/util/link-anchor/index.tsx +7 -0
@@ -1,4 +1,5 @@
1
1
  import React, { MouseEvent, forwardRef } from "react";
2
+ import { Spacer } from "../../../../layout/stack";
2
3
  import { useRenameCSS } from "../../../../theme/Theme";
3
4
  import { BodyShort, ErrorMessage } from "../../../../typography";
4
5
  import { OverridableComponent } from "../../../../util";
@@ -51,11 +52,13 @@ export interface FileUploadItemProps
51
52
  /**
52
53
  * Props for the action button.
53
54
  */
54
- button?: {
55
- action: "delete" | "retry";
56
- onClick: (event: MouseEvent<HTMLButtonElement>) => void;
57
- id?: string;
58
- };
55
+ button?:
56
+ | {
57
+ action: "delete" | "retry";
58
+ onClick: (event: MouseEvent<HTMLButtonElement>) => void;
59
+ id?: string;
60
+ }
61
+ | React.ReactNode;
59
62
  /**
60
63
  * i18n-API for customizing texts and labels
61
64
  */
@@ -100,6 +103,9 @@ export const Item: OverridableComponent<FileUploadItemProps, HTMLDivElement> =
100
103
  return description ?? formatFileSize(file);
101
104
  }
102
105
 
106
+ const renderButton = status === "idle" && button;
107
+ const renderCustomButton = isCustomButton(button);
108
+
103
109
  return (
104
110
  <Component
105
111
  ref={ref}
@@ -131,8 +137,9 @@ export const Item: OverridableComponent<FileUploadItemProps, HTMLDivElement> =
131
137
  )}
132
138
  </div>
133
139
  </div>
140
+ {renderButton && <Spacer />}
134
141
 
135
- {status === "idle" && button && (
142
+ {renderButton && !renderCustomButton && (
136
143
  <ItemButton
137
144
  {...button}
138
145
  title={translate(
@@ -142,10 +149,17 @@ export const Item: OverridableComponent<FileUploadItemProps, HTMLDivElement> =
142
149
  )}
143
150
  />
144
151
  )}
152
+ {renderButton && renderCustomButton && button}
145
153
  </div>
146
154
  </Component>
147
155
  );
148
156
  },
149
157
  );
150
158
 
159
+ function isCustomButton(
160
+ button: FileUploadItemProps["button"],
161
+ ): button is React.ReactNode {
162
+ return React.isValidElement(button);
163
+ }
164
+
151
165
  export default Item;
@@ -121,6 +121,7 @@ export const Search = forwardRef<HTMLInputElement, SearchProps>(
121
121
  onChange,
122
122
  onSearchClick,
123
123
  htmlSize,
124
+ "data-color": dataColor,
124
125
  ...rest
125
126
  } = props;
126
127
 
@@ -170,6 +171,7 @@ export const Search = forwardRef<HTMLInputElement, SearchProps>(
170
171
  "navds-search--with-size": htmlSize,
171
172
  },
172
173
  )}
174
+ data-color={dataColor}
173
175
  >
174
176
  <Label
175
177
  htmlFor={inputProps.id}
@@ -233,7 +235,9 @@ export const Search = forwardRef<HTMLInputElement, SearchProps>(
233
235
  handleClick,
234
236
  }}
235
237
  >
236
- {children ? children : variant !== "simple" && <SearchButton />}
238
+ {children
239
+ ? children
240
+ : variant !== "simple" && <SearchButton data-color={dataColor} />}
237
241
  </SearchContext.Provider>
238
242
  </div>
239
243
  <div
@@ -117,32 +117,7 @@ export const Switch = forwardRef<HTMLInputElement, SwitchProps>(
117
117
  />
118
118
  <span className={cn("navds-switch__track")}>
119
119
  <span className={cn("navds-switch__thumb")}>
120
- {loading ? (
121
- <Loader
122
- size="xsmall"
123
- aria-live="polite"
124
- variant={checked ? "interaction" : "inverted"}
125
- />
126
- ) : (
127
- <svg
128
- width="12"
129
- height="10"
130
- viewBox="0 0 12 10"
131
- fill="none"
132
- xmlns="http://www.w3.org/2000/svg"
133
- focusable={false}
134
- role="img"
135
- aria-hidden
136
- className={cn("navds-switch__checkmark")}
137
- >
138
- <path
139
- fillRule="evenodd"
140
- clipRule="evenodd"
141
- d="M11.2674 0.647802C11.8762 1.20971 11.9141 2.1587 11.3522 2.76743L5.35221 9.26743C5.07531 9.56739 4.68813 9.74155 4.27998 9.74971C3.87184 9.75787 3.478 9.59933 3.18934 9.31067L0.68934 6.81067C0.103553 6.22488 0.103553 5.27513 0.68934 4.68935C1.27513 4.10356 2.22487 4.10356 2.81066 4.68935L4.20673 6.08541L9.14779 0.732587C9.7097 0.123856 10.6587 0.0858967 11.2674 0.647802Z"
142
- fill="currentColor"
143
- />
144
- </svg>
145
- )}
120
+ <SwitchIcon size={size} checked={checked} loading={loading} />
146
121
  </span>
147
122
  </span>
148
123
  <label
@@ -181,4 +156,82 @@ export const Switch = forwardRef<HTMLInputElement, SwitchProps>(
181
156
  },
182
157
  );
183
158
 
159
+ const SwitchIcon = ({
160
+ size,
161
+ checked,
162
+ loading,
163
+ }: {
164
+ size: SwitchProps["size"];
165
+ checked: SwitchProps["checked"];
166
+ loading: SwitchProps["loading"];
167
+ }) => {
168
+ if (loading) {
169
+ let baseSize = 16;
170
+
171
+ if (size === "small") {
172
+ baseSize = 12;
173
+ }
174
+
175
+ if (checked) {
176
+ baseSize += 2;
177
+ }
178
+
179
+ return (
180
+ <Loader
181
+ size="small"
182
+ aria-live="polite"
183
+ variant={checked ? "interaction" : "inverted"}
184
+ width={`${baseSize / 16}rem`}
185
+ height={`${baseSize / 16}rem`}
186
+ />
187
+ );
188
+ }
189
+
190
+ if (!checked) {
191
+ return null;
192
+ }
193
+
194
+ if (size === "small") {
195
+ return (
196
+ <svg
197
+ width="11"
198
+ height="8"
199
+ viewBox="0 0 11 8"
200
+ fill="none"
201
+ xmlns="http://www.w3.org/2000/svg"
202
+ focusable={false}
203
+ role="img"
204
+ aria-hidden
205
+ >
206
+ <path
207
+ fillRule="evenodd"
208
+ clipRule="evenodd"
209
+ d="M9.62013 0.530226C10.1194 0.952686 10.1817 1.6999 9.7592 2.19917L5.4171 7.33075C5.20318 7.58356 4.89318 7.73525 4.5623 7.74901C4.23142 7.76277 3.90989 7.63735 3.67572 7.40318L1.3073 5.03476C0.844833 4.5723 0.844833 3.8225 1.3073 3.36003C1.76976 2.89757 2.51956 2.89757 2.98202 3.36003L4.4404 4.81841L7.95118 0.669304C8.37364 0.170033 9.12085 0.107765 9.62013 0.530226Z"
210
+ fill="currentColor"
211
+ />
212
+ </svg>
213
+ );
214
+ }
215
+
216
+ return (
217
+ <svg
218
+ width="12"
219
+ height="10"
220
+ viewBox="0 0 12 10"
221
+ fill="none"
222
+ xmlns="http://www.w3.org/2000/svg"
223
+ focusable={false}
224
+ role="img"
225
+ aria-hidden
226
+ >
227
+ <path
228
+ fillRule="evenodd"
229
+ clipRule="evenodd"
230
+ d="M11.2674 0.647802C11.8762 1.20971 11.9141 2.1587 11.3522 2.76743L5.35221 9.26743C5.07531 9.56739 4.68813 9.74155 4.27998 9.74971C3.87184 9.75787 3.478 9.59933 3.18934 9.31067L0.68934 6.81067C0.103553 6.22488 0.103553 5.27513 0.68934 4.68935C1.27513 4.10356 2.22487 4.10356 2.81066 4.68935L4.20673 6.08541L9.14779 0.732587C9.7097 0.123856 10.6587 0.0858967 11.2674 0.647802Z"
231
+ fill="currentColor"
232
+ />
233
+ </svg>
234
+ );
235
+ };
236
+
184
237
  export default Switch;
package/src/index.ts CHANGED
@@ -150,6 +150,16 @@ export { Select, type SelectProps } from "./form/select";
150
150
  export { Switch, type SwitchProps } from "./form/switch";
151
151
  export { Textarea, type TextareaProps } from "./form/textarea";
152
152
  export { TextField, type TextFieldProps } from "./form/textfield";
153
+ export {
154
+ LinkCard,
155
+ type LinkCardProps,
156
+ type LinkCardTitleProps,
157
+ type LinkCardDescriptionProps,
158
+ type LinkCardFooterProps,
159
+ type LinkCardAnchorProps,
160
+ type LinkCardIconProps,
161
+ type LinkCardImageProps,
162
+ } from "./link-card";
153
163
 
154
164
  /**
155
165
  * Theming
@@ -0,0 +1,317 @@
1
+ import React, { HTMLAttributes, forwardRef } from "react";
2
+ import { useRenameCSS } from "../theme/Theme";
3
+ import { BodyLong, Heading } from "../typography";
4
+ import { createContext } from "../util/create-context";
5
+ import {
6
+ LinkAnchor,
7
+ LinkAnchorArrow,
8
+ LinkAnchorOverlay,
9
+ LinkAnchorProps,
10
+ } from "../util/link-anchor";
11
+
12
+ /* ------------------------------ LinkCard Root ----------------------------- */
13
+ interface LinkCardProps extends HTMLAttributes<HTMLDivElement> {
14
+ /**
15
+ * @default true
16
+ */
17
+ arrow?: boolean;
18
+ /**
19
+ * Changes padding and typo sizes.
20
+ * @default "medium"
21
+ */
22
+ size?: "small" | "medium";
23
+ }
24
+
25
+ type LinkCardContextProps = {
26
+ size: LinkCardProps["size"];
27
+ arrow: LinkCardProps["arrow"];
28
+ };
29
+
30
+ const [LinkCardContextProvider, useLinkCardContext] =
31
+ createContext<LinkCardContextProps>({
32
+ name: "LinkCardContextProvider",
33
+ });
34
+
35
+ interface LinkCardComponent
36
+ extends React.ForwardRefExoticComponent<
37
+ LinkCardProps & React.RefAttributes<HTMLDivElement>
38
+ > {
39
+ /**
40
+ * @see 🏷️ {@link LinkCardTitleProps}
41
+ */
42
+ Title: typeof LinkCardTitle;
43
+ /**
44
+ * @see 🏷️ {@link LinkCardAnchorProps}
45
+ */
46
+ Anchor: typeof LinkCardAnchor;
47
+ /**
48
+ * @see 🏷️ {@link LinkCardDescriptionProps}
49
+ */
50
+ Description: typeof LinkCardDescription;
51
+ /**
52
+ * @see 🏷️ {@link LinkCardFooterProps}
53
+ */
54
+ Footer: typeof LinkCardFooter;
55
+ /**
56
+ * @see 🏷️ {@link LinkCardIconProps}
57
+ */
58
+ Icon: typeof LinkCardIcon;
59
+ /**
60
+ * @see 🏷️ {@link LinkCardImageProps}
61
+ */
62
+ Image: typeof LinkCardImage;
63
+ }
64
+
65
+ /**
66
+ * Accessible clickable card as a link.
67
+ *
68
+ * @see [📝 Documentation](https://aksel.nav.no/komponenter/core/linkcard)
69
+ * @see 🏷️ {@link LinkCardProps}
70
+ *
71
+ *
72
+ * @example
73
+ * ```tsx
74
+ * <LinkCard>
75
+ * <LinkCard.Icon>
76
+ * <IconOrPictogram />
77
+ * </LinkCard.Icon>
78
+ * <LinkCard.Title>
79
+ * <LinkCard.Anchor href="/href">
80
+ * LinkCard title
81
+ * </LinkCard.Anchor>
82
+ * </LinkCard.Title>
83
+ * <LinkCard.Description>
84
+ * This is a description of the link card.
85
+ * </LinkCard.Description>
86
+ * <LinkCard.Footer>Footer content</LinkCard.Footer>
87
+ * </LinkCard>
88
+ * ```
89
+ */
90
+ export const LinkCard = forwardRef<HTMLDivElement, LinkCardProps>(
91
+ (
92
+ {
93
+ children,
94
+ className,
95
+ arrow = true,
96
+ size = "medium",
97
+ ...restProps
98
+ }: LinkCardProps,
99
+ forwardedRef,
100
+ ) => {
101
+ const { cn } = useRenameCSS();
102
+
103
+ return (
104
+ <LinkCardContextProvider size={size} arrow={arrow}>
105
+ <LinkAnchorOverlay asChild>
106
+ <BodyLong
107
+ as="div"
108
+ size={size}
109
+ ref={forwardedRef}
110
+ data-color="neutral"
111
+ className={cn(
112
+ "navds-link-card",
113
+ className,
114
+ `navds-link-card--${size}`,
115
+ )}
116
+ {...restProps}
117
+ >
118
+ {children}
119
+ </BodyLong>
120
+ </LinkAnchorOverlay>
121
+ </LinkCardContextProvider>
122
+ );
123
+ },
124
+ ) as LinkCardComponent;
125
+
126
+ /* ---------------------------- LinkCard Title ---------------------------- */
127
+ type LinkCardTitleProps = HTMLAttributes<HTMLHeadingElement> & {
128
+ children: React.ReactNode;
129
+ /**
130
+ * Heading tag. Use "span" if you want a non header defining card
131
+ * (eg. you have a lot of them all at once, such as in a grid)
132
+ * @default "span"
133
+ */
134
+ as?: "span" | "h2" | "h3" | "h4" | "h5" | "h6";
135
+ };
136
+
137
+ /**
138
+ * @see 🏷️ {@link LinkCardTitleProps}
139
+ */
140
+ export const LinkCardTitle = forwardRef<HTMLHeadingElement, LinkCardTitleProps>(
141
+ (
142
+ { children, as = "span", className, ...restProps }: LinkCardTitleProps,
143
+ forwardedRef,
144
+ ) => {
145
+ const { cn } = useRenameCSS();
146
+
147
+ const context = useLinkCardContext();
148
+
149
+ return (
150
+ <Heading
151
+ ref={forwardedRef}
152
+ as={as}
153
+ size={context.size === "medium" ? "small" : "xsmall"}
154
+ className={cn("navds-link-card__title", className)}
155
+ {...restProps}
156
+ >
157
+ {children}
158
+ {context.arrow && (
159
+ <LinkAnchorArrow
160
+ fontSize={context.size === "medium" ? "1.75rem" : "1.5rem"}
161
+ />
162
+ )}
163
+ </Heading>
164
+ );
165
+ },
166
+ );
167
+
168
+ /* ---------------------------- LinkCard Anchor ---------------------------- */
169
+ type LinkCardAnchorProps = LinkAnchorProps;
170
+
171
+ /**
172
+ * @see 🏷️ {@link LinkCardAnchorProps}
173
+ */
174
+ export const LinkCardAnchor = LinkAnchor;
175
+
176
+ /* ---------------------------- LinkCard Description ---------------------------- */
177
+ interface LinkCardDescriptionProps extends HTMLAttributes<HTMLDivElement> {
178
+ children: React.ReactNode;
179
+ }
180
+
181
+ /**
182
+ * @see 🏷️ {@link LinkCardDescriptionProps}
183
+ */
184
+ export const LinkCardDescription = forwardRef<
185
+ HTMLDivElement,
186
+ LinkCardDescriptionProps
187
+ >(
188
+ (
189
+ { children, className, ...restProps }: LinkCardDescriptionProps,
190
+ forwardedRef,
191
+ ) => {
192
+ const { cn } = useRenameCSS();
193
+
194
+ return (
195
+ <div
196
+ ref={forwardedRef}
197
+ className={cn("navds-link-card__description", className)}
198
+ {...restProps}
199
+ >
200
+ {children}
201
+ </div>
202
+ );
203
+ },
204
+ );
205
+
206
+ /* ---------------------------- LinkCard Footer ---------------------------- */
207
+ interface LinkCardFooterProps extends HTMLAttributes<HTMLDivElement> {
208
+ children: React.ReactNode;
209
+ }
210
+
211
+ /**
212
+ * @see 🏷️ {@link LinkCardFooterProps}
213
+ */
214
+ export const LinkCardFooter = forwardRef<HTMLDivElement, LinkCardFooterProps>(
215
+ (
216
+ { children, className, ...restProps }: LinkCardFooterProps,
217
+ forwardedRef,
218
+ ) => {
219
+ const { cn } = useRenameCSS();
220
+
221
+ return (
222
+ <div
223
+ ref={forwardedRef}
224
+ className={cn("navds-link-card__footer", className)}
225
+ {...restProps}
226
+ >
227
+ {children}
228
+ </div>
229
+ );
230
+ },
231
+ );
232
+
233
+ /* ---------------------------- LinkCard Icon ---------------------------- */
234
+ interface LinkCardIconProps extends HTMLAttributes<HTMLDivElement> {
235
+ children: React.ReactNode;
236
+ }
237
+
238
+ /**
239
+ * @see 🏷️ {@link LinkCardIconProps}
240
+ */
241
+ export const LinkCardIcon = forwardRef<HTMLDivElement, LinkCardIconProps>(
242
+ ({ children, className, ...restProps }: LinkCardIconProps, forwardedRef) => {
243
+ const { cn } = useRenameCSS();
244
+
245
+ return (
246
+ <div
247
+ ref={forwardedRef}
248
+ aria-hidden
249
+ className={cn("navds-link-card__icon", className)}
250
+ {...restProps}
251
+ >
252
+ {children}
253
+ </div>
254
+ );
255
+ },
256
+ );
257
+
258
+ /* ---------------------------- LinkCard Image ---------------------------- */
259
+ type ImageAspectRatio = "1/1" | "16/9" | "16/10" | "4/3" | (string & {});
260
+
261
+ interface LinkCardImageProps extends HTMLAttributes<HTMLDivElement> {
262
+ children: React.ReactNode;
263
+ /**
264
+ * The aspect-ratio CSS property allows you to define the desired width-to-height ratio of an element's box.
265
+ * This means that even if the parent container or viewport size changes, the browser will adjust the element's dimensions to maintain the specified width-to-height ratio.
266
+ */
267
+ aspectRatio?: ImageAspectRatio;
268
+ }
269
+
270
+ /**
271
+ * @see 🏷️ {@link LinkCardImageProps}
272
+ */
273
+ export const LinkCardImage = forwardRef<HTMLDivElement, LinkCardImageProps>(
274
+ (
275
+ {
276
+ children,
277
+ className,
278
+ aspectRatio,
279
+ style,
280
+ ...restProps
281
+ }: LinkCardImageProps,
282
+ forwardedRef,
283
+ ) => {
284
+ const { cn } = useRenameCSS();
285
+
286
+ return (
287
+ <div
288
+ ref={forwardedRef}
289
+ className={cn("navds-link-card__image-container", className)}
290
+ style={{
291
+ ...style,
292
+ aspectRatio,
293
+ }}
294
+ {...restProps}
295
+ >
296
+ {children}
297
+ </div>
298
+ );
299
+ },
300
+ );
301
+
302
+ LinkCard.Title = LinkCardTitle;
303
+ LinkCard.Anchor = LinkCardAnchor;
304
+ LinkCard.Description = LinkCardDescription;
305
+ LinkCard.Footer = LinkCardFooter;
306
+ LinkCard.Icon = LinkCardIcon;
307
+ LinkCard.Image = LinkCardImage;
308
+
309
+ export type {
310
+ LinkCardAnchorProps,
311
+ LinkCardDescriptionProps,
312
+ LinkCardFooterProps,
313
+ LinkCardIconProps,
314
+ LinkCardImageProps,
315
+ LinkCardProps,
316
+ LinkCardTitleProps,
317
+ };
@@ -0,0 +1,20 @@
1
+ "use client";
2
+ export {
3
+ LinkCard,
4
+ LinkCardTitle,
5
+ LinkCardDescription,
6
+ LinkCardFooter,
7
+ LinkCardAnchor,
8
+ LinkCardIcon,
9
+ LinkCardImage,
10
+ } from "./LinkCard";
11
+
12
+ export type {
13
+ LinkCardProps,
14
+ LinkCardTitleProps,
15
+ LinkCardDescriptionProps,
16
+ LinkCardFooterProps,
17
+ LinkCardAnchorProps,
18
+ LinkCardIconProps,
19
+ LinkCardImageProps,
20
+ } from "./LinkCard";
@@ -38,21 +38,13 @@ interface LinkPanelComponentType
38
38
  }
39
39
 
40
40
  /**
41
- * A component that displays a link panel.
41
+ * @deprecated Use `LinkCard` instead. Migrations should be straightforward as the API is similar.
42
+ * @see [📝 LinkCard documentation](https://aksel.nav.no/komponenter/core/linkcard)
42
43
  *
43
- * @see [📝 Documentation](https://aksel.nav.no/komponenter/core/linkpanel)
44
+ * @see [📝 Documentation](https://aksel.nav.no/komponenter/legacy/linkpanel)
44
45
  * @see 🏷️ {@link LinkPanelProps}
45
46
  * @see [🤖 OverridableComponent](https://aksel.nav.no/grunnleggende/kode/overridablecomponent) support
46
47
  *
47
- * @example
48
- * ```jsx
49
- * <LinkPanel href="#" border>
50
- * <LinkPanel.Title>Arbeidssøker eller permittert</LinkPanel.Title>
51
- * <LinkPanel.Description>
52
- * Om jobb, registrering, CV, dagpenger og feriepenger av dagpenger
53
- * </LinkPanel.Description>
54
- * </LinkPanel>
55
- * ```
56
48
  */
57
49
  export const LinkPanelComponent: OverridableComponent<
58
50
  LinkPanelProps,
@@ -2,7 +2,7 @@ import { fireEvent, render, screen, waitFor } from "@testing-library/react";
2
2
  import React, { useState } from "react";
3
3
  import { describe, expect, test } from "vitest";
4
4
  import { Button, Modal } from "..";
5
- import { BODY_CLASS } from "./ModalUtils";
5
+ import { BODY_CLASS, BODY_CLASS_LEGACY } from "./ModalUtils";
6
6
 
7
7
  const Test = () => {
8
8
  const [open, setOpen] = useState(true);
@@ -32,11 +32,15 @@ describe("Modal", () => {
32
32
  test("should toggle body class", async () => {
33
33
  render(<Test />);
34
34
  expect(document.body.classList).toContain(BODY_CLASS);
35
+ expect(document.body.classList).toContain(BODY_CLASS_LEGACY);
35
36
 
36
37
  fireEvent.click(screen.getByText("Close"));
37
- await waitFor(() =>
38
- expect(document.body.classList).not.toContain(BODY_CLASS),
39
- );
38
+ await waitFor(() => {
39
+ expect(document.body.classList).not.toContain(BODY_CLASS);
40
+ });
41
+ await waitFor(() => {
42
+ expect(document.body.classList).not.toContain(BODY_CLASS_LEGACY);
43
+ });
40
44
  });
41
45
 
42
46
  test("should toggle body class when using portal", async () => {
@@ -46,10 +50,14 @@ describe("Modal", () => {
46
50
  </Modal>,
47
51
  );
48
52
  expect(document.body.classList).toContain(BODY_CLASS);
53
+ expect(document.body.classList).toContain(BODY_CLASS_LEGACY);
49
54
 
50
55
  fireEvent.click(screen.getByRole("button"));
51
56
  await waitFor(() =>
52
57
  expect(document.body.classList).not.toContain(BODY_CLASS),
53
58
  );
59
+ await waitFor(() =>
60
+ expect(document.body.classList).not.toContain(BODY_CLASS_LEGACY),
61
+ );
54
62
  });
55
63
  });
@@ -27,7 +27,8 @@ export function getCloseHandler(
27
27
  return () => modalRef.current?.close();
28
28
  }
29
29
 
30
- export const BODY_CLASS = "navds-modal__document-body";
30
+ export const BODY_CLASS_LEGACY = "navds-modal__document-body";
31
+ export const BODY_CLASS = "aksel-modal__document-body";
31
32
 
32
33
  export function useBodyScrollLock(
33
34
  modalRef: React.RefObject<HTMLDialogElement>,
@@ -35,21 +36,37 @@ export function useBodyScrollLock(
35
36
  isNested: boolean,
36
37
  ) {
37
38
  React.useEffect(() => {
38
- if (isNested) return;
39
- if (!modalRef.current || !portalNode) return; // We check both to avoid running this twice when not using portal
40
- if (modalRef.current.open) document.body.classList.add(BODY_CLASS); // In case `open` is true initially
39
+ if (isNested) {
40
+ return;
41
+ }
42
+
43
+ // We check both to avoid running this twice when not using portal
44
+ if (!modalRef.current || !portalNode) {
45
+ return;
46
+ }
47
+
48
+ // In case `open` is true initially
49
+ if (modalRef.current.open) {
50
+ document.body.classList.add(BODY_CLASS, BODY_CLASS_LEGACY);
51
+ }
41
52
 
42
53
  const observer = new MutationObserver(() => {
43
- if (modalRef.current?.open) document.body.classList.add(BODY_CLASS);
44
- else document.body.classList.remove(BODY_CLASS);
54
+ if (modalRef.current?.open) {
55
+ document.body.classList.add(BODY_CLASS, BODY_CLASS_LEGACY);
56
+ } else {
57
+ document.body.classList.remove(BODY_CLASS, BODY_CLASS_LEGACY);
58
+ }
45
59
  });
60
+
46
61
  observer.observe(modalRef.current, {
47
62
  attributes: true,
48
63
  attributeFilter: ["open"],
49
64
  });
65
+
50
66
  return () => {
51
67
  observer.disconnect();
52
- document.body.classList.remove(BODY_CLASS); // In case modal is unmounted before it's closed
68
+ // In case modal is unmounted before it's closed
69
+ document.body.classList.remove(BODY_CLASS, BODY_CLASS_LEGACY);
53
70
  };
54
71
  }, [modalRef, portalNode, isNested]);
55
72
  }