@helsenorge/designsystem-react 12.0.2 → 12.2.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.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,37 @@
1
+ ## [12.2.0](https://github.com/helsenorge/designsystem/branchCompare?baseVersion=GTv12.1.0&targetVersion=GTv12.2.0) (2025-09-11)
2
+
3
+ ### Features
4
+
5
+ - **expanderhierarchy:** eksponer classname prop på expander
6
+ ([8c7f2cd](https://github.com/helsenorge/designsystem/commit/8c7f2cd92d582de3b97a60f849a9a8e599b9935b)), closes
7
+ [#359415](https://github.com/helsenorge/designsystem/issues/359415)
8
+ - **validation:** ny prop visuallyHiddenSummary
9
+ ([d51e566](https://github.com/helsenorge/designsystem/commit/d51e566a2f02d9b0a65cb3cf5ada7c9c8c062014)), closes
10
+ [#358073](https://github.com/helsenorge/designsystem/issues/358073)
11
+
12
+ ### Bug Fixes
13
+
14
+ - **useReturnFocusOnUnmount:** fiks hook for microweb
15
+ ([5d87848](https://github.com/helsenorge/designsystem/commit/5d87848b5adbb4b0765cf67496408ff8948a1f41)), closes
16
+ [#359450](https://github.com/helsenorge/designsystem/issues/359450)
17
+
18
+ ## [12.1.0](https://github.com/helsenorge/designsystem/branchCompare?baseVersion=GTv12.0.2&targetVersion=GTv12.1.0) (2025-09-10)
19
+
20
+ ### Features
21
+
22
+ - **helpexpanderstandalone:** justering av innhold ved tekst over flere linjer
23
+ ([5d3a8ea](https://github.com/helsenorge/designsystem/commit/5d3a8eac4d3cf1ccbd9bd4aac947b3a26a73a120)), closes
24
+ [#354487](https://github.com/helsenorge/designsystem/issues/354487)
25
+ - **notificationpanel:** fjern spesifikk styling av rene anchorlinks
26
+ ([6db68e4](https://github.com/helsenorge/designsystem/commit/6db68e4c5e3c1806f9ae16c2088322d51b0da15e)), closes
27
+ [#358876](https://github.com/helsenorge/designsystem/issues/358876)
28
+
29
+ ### Bug Fixes
30
+
31
+ - **dropdown:** skjermleser leser ikke opp innholdet i safari
32
+ ([a26e4a9](https://github.com/helsenorge/designsystem/commit/a26e4a990a017c95274a4f9bb5b6b5d1c3f02c9c)), closes
33
+ [#356915](https://github.com/helsenorge/designsystem/issues/356915)
34
+
1
35
  ## [12.0.2](https://github.com/helsenorge/designsystem/branchCompare?baseVersion=GTv12.0.1&targetVersion=GTv12.0.2) (2025-09-04)
2
36
 
3
37
  ### Bug Fixes
@@ -1,4 +1,4 @@
1
- import { jsx, jsxs } from "react/jsx-runtime";
1
+ import { jsxs, jsx } from "react/jsx-runtime";
2
2
  import React__default from "react";
3
3
  import classNames from "classnames";
4
4
  import { AnalyticsId } from "./constants.js";
@@ -14,7 +14,7 @@ const HelpTriggerStandalone = React__default.forwardRef(
14
14
  const ariaLabelAttributes = getAriaLabelAttributes({ label: ariaLabel, id: ariaLabelledById });
15
15
  const helpTriggerStandaloneStyles = classNames(styles["help-trigger-standalone"], className);
16
16
  const { hoverRef, isHovered } = useHover(ref, false);
17
- return /* @__PURE__ */ jsx(
17
+ return /* @__PURE__ */ jsxs(
18
18
  "button",
19
19
  {
20
20
  "aria-label": ariaLabel,
@@ -25,10 +25,10 @@ const HelpTriggerStandalone = React__default.forwardRef(
25
25
  ref: mergeRefs([hoverRef, ref]),
26
26
  ...ariaLabelAttributes,
27
27
  ...rest,
28
- children: /* @__PURE__ */ jsxs("span", { className: styles["help-trigger-standalone__inner-container"], children: [
28
+ children: [
29
29
  /* @__PURE__ */ jsx(HelpTriggerIconInternal, { weight, size: isMobile ? "medium" : "large", htmlMarkup: "span", isHovered }),
30
30
  /* @__PURE__ */ jsx("span", { className: styles["help-trigger-standalone__children"], children })
31
- ] })
31
+ ]
32
32
  }
33
33
  );
34
34
  }
@@ -1 +1 @@
1
- {"version":3,"file":"HelpTriggerStandalone.js","sources":["../src/components/HelpTriggerStandalone/HelpTriggerStandalone.tsx"],"sourcesContent":["import React from 'react';\n\nimport classNames from 'classnames';\n\nimport { AnalyticsId } from '../../constants';\nimport { useHover } from '../../hooks/useHover';\nimport { useIsMobileBreakpoint } from '../../hooks/useIsMobileBreakpoint';\nimport { getAriaLabelAttributes } from '../../utils/accessibility';\nimport { mergeRefs } from '../../utils/refs';\nimport { HelpTriggerIconInternal, HelpTriggerWeights } from '../HelpTriggerIcon';\n\nimport styles from './styles.module.scss';\n\nexport interface HelpTriggerStandaloneProps\n extends Pick<React.InputHTMLAttributes<HTMLButtonElement>, 'onClick' | 'aria-haspopup' | 'aria-controls' | 'aria-expanded'> {\n /**\n * Sets aria-label of the trigger. ariaLabel or ariaLabelledById MUST be set!\n */\n ariaLabel?: string;\n /**\n * Sets aria-labelledby of the trigger. ariaLabel or ariaLabelledById MUST be set!\n */\n ariaLabelledById?: string;\n /** Sets the text content of the HelpTriggerStandalone. */\n children: React.ReactNode;\n /**\n * Sets the colors of the help trigger. Default: normal.\n */\n weight?: HelpTriggerWeights;\n /**\n * Classname will be applied to the button element.\n */\n className?: string;\n /**\n * Optional test id.\n */\n testId?: string;\n}\n\nconst HelpTriggerStandalone = React.forwardRef<HTMLButtonElement, HelpTriggerStandaloneProps>(\n ({ ariaLabel, ariaLabelledById, children, className, testId, weight = 'normal', ...rest }, ref) => {\n const isMobile = useIsMobileBreakpoint();\n const ariaLabelAttributes = getAriaLabelAttributes({ label: ariaLabel, id: ariaLabelledById });\n const helpTriggerStandaloneStyles = classNames(styles['help-trigger-standalone'], className);\n const { hoverRef, isHovered } = useHover<HTMLButtonElement>(ref as React.RefObject<HTMLButtonElement>, false);\n\n return (\n <button\n aria-label={ariaLabel}\n type=\"button\"\n data-testid={testId}\n data-analyticsid={AnalyticsId.HelpTriggerStandalone}\n className={helpTriggerStandaloneStyles}\n ref={mergeRefs([hoverRef, ref])}\n {...ariaLabelAttributes}\n {...rest}\n >\n <span className={styles['help-trigger-standalone__inner-container']}>\n <HelpTriggerIconInternal weight={weight} size={isMobile ? 'medium' : 'large'} htmlMarkup={'span'} isHovered={isHovered} />\n <span className={styles['help-trigger-standalone__children']}>{children}</span>\n </span>\n </button>\n );\n }\n);\n\nHelpTriggerStandalone.displayName = 'HelpTriggerStandalone';\n\nexport default HelpTriggerStandalone;\n"],"names":["React"],"mappings":";;;;;;;;;;AAuCA,MAAM,wBAAwBA,eAAM;AAAA,EAClC,CAAC,EAAE,WAAW,kBAAkB,UAAU,WAAW,QAAQ,SAAS,UAAU,GAAG,KAAA,GAAQ,QAAQ;AACjG,UAAM,WAAW,sBAAA;AACjB,UAAM,sBAAsB,uBAAuB,EAAE,OAAO,WAAW,IAAI,kBAAkB;AAC7F,UAAM,8BAA8B,WAAW,OAAO,yBAAyB,GAAG,SAAS;AAC3F,UAAM,EAAE,UAAU,UAAA,IAAc,SAA4B,KAA2C,KAAK;AAE5G,WACE;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,cAAY;AAAA,QACZ,MAAK;AAAA,QACL,eAAa;AAAA,QACb,oBAAkB,YAAY;AAAA,QAC9B,WAAW;AAAA,QACX,KAAK,UAAU,CAAC,UAAU,GAAG,CAAC;AAAA,QAC7B,GAAG;AAAA,QACH,GAAG;AAAA,QAEJ,UAAA,qBAAC,QAAA,EAAK,WAAW,OAAO,0CAA0C,GAChE,UAAA;AAAA,UAAA,oBAAC,yBAAA,EAAwB,QAAgB,MAAM,WAAW,WAAW,SAAS,YAAY,QAAQ,UAAA,CAAsB;AAAA,8BACvH,QAAA,EAAK,WAAW,OAAO,mCAAmC,GAAI,SAAA,CAAS;AAAA,QAAA,EAAA,CAC1E;AAAA,MAAA;AAAA,IAAA;AAAA,EAGN;AACF;AAEA,sBAAsB,cAAc;"}
1
+ {"version":3,"file":"HelpTriggerStandalone.js","sources":["../src/components/HelpTriggerStandalone/HelpTriggerStandalone.tsx"],"sourcesContent":["import React from 'react';\n\nimport classNames from 'classnames';\n\nimport { AnalyticsId } from '../../constants';\nimport { useHover } from '../../hooks/useHover';\nimport { useIsMobileBreakpoint } from '../../hooks/useIsMobileBreakpoint';\nimport { getAriaLabelAttributes } from '../../utils/accessibility';\nimport { mergeRefs } from '../../utils/refs';\nimport { HelpTriggerIconInternal, HelpTriggerWeights } from '../HelpTriggerIcon';\n\nimport styles from './styles.module.scss';\n\nexport interface HelpTriggerStandaloneProps\n extends Pick<React.InputHTMLAttributes<HTMLButtonElement>, 'onClick' | 'aria-haspopup' | 'aria-controls' | 'aria-expanded'> {\n /**\n * Sets aria-label of the trigger. ariaLabel or ariaLabelledById MUST be set!\n */\n ariaLabel?: string;\n /**\n * Sets aria-labelledby of the trigger. ariaLabel or ariaLabelledById MUST be set!\n */\n ariaLabelledById?: string;\n /** Sets the text content of the HelpTriggerStandalone. */\n children: React.ReactNode;\n /**\n * Sets the colors of the help trigger. Default: normal.\n */\n weight?: HelpTriggerWeights;\n /**\n * Classname will be applied to the button element.\n */\n className?: string;\n /**\n * Optional test id.\n */\n testId?: string;\n}\n\nconst HelpTriggerStandalone = React.forwardRef<HTMLButtonElement, HelpTriggerStandaloneProps>(\n ({ ariaLabel, ariaLabelledById, children, className, testId, weight = 'normal', ...rest }, ref) => {\n const isMobile = useIsMobileBreakpoint();\n const ariaLabelAttributes = getAriaLabelAttributes({ label: ariaLabel, id: ariaLabelledById });\n const helpTriggerStandaloneStyles = classNames(styles['help-trigger-standalone'], className);\n const { hoverRef, isHovered } = useHover<HTMLButtonElement>(ref as React.RefObject<HTMLButtonElement>, false);\n\n return (\n <button\n aria-label={ariaLabel}\n type=\"button\"\n data-testid={testId}\n data-analyticsid={AnalyticsId.HelpTriggerStandalone}\n className={helpTriggerStandaloneStyles}\n ref={mergeRefs([hoverRef, ref])}\n {...ariaLabelAttributes}\n {...rest}\n >\n <HelpTriggerIconInternal weight={weight} size={isMobile ? 'medium' : 'large'} htmlMarkup={'span'} isHovered={isHovered} />\n <span className={styles['help-trigger-standalone__children']}>{children}</span>\n </button>\n );\n }\n);\n\nHelpTriggerStandalone.displayName = 'HelpTriggerStandalone';\n\nexport default HelpTriggerStandalone;\n"],"names":["React"],"mappings":";;;;;;;;;;AAuCA,MAAM,wBAAwBA,eAAM;AAAA,EAClC,CAAC,EAAE,WAAW,kBAAkB,UAAU,WAAW,QAAQ,SAAS,UAAU,GAAG,KAAA,GAAQ,QAAQ;AACjG,UAAM,WAAW,sBAAA;AACjB,UAAM,sBAAsB,uBAAuB,EAAE,OAAO,WAAW,IAAI,kBAAkB;AAC7F,UAAM,8BAA8B,WAAW,OAAO,yBAAyB,GAAG,SAAS;AAC3F,UAAM,EAAE,UAAU,UAAA,IAAc,SAA4B,KAA2C,KAAK;AAE5G,WACE;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,cAAY;AAAA,QACZ,MAAK;AAAA,QACL,eAAa;AAAA,QACb,oBAAkB,YAAY;AAAA,QAC9B,WAAW;AAAA,QACX,KAAK,UAAU,CAAC,UAAU,GAAG,CAAC;AAAA,QAC7B,GAAG;AAAA,QACH,GAAG;AAAA,QAEJ,UAAA;AAAA,UAAA,oBAAC,yBAAA,EAAwB,QAAgB,MAAM,WAAW,WAAW,SAAS,YAAY,QAAQ,UAAA,CAAsB;AAAA,8BACvH,QAAA,EAAK,WAAW,OAAO,mCAAmC,GAAI,SAAA,CAAS;AAAA,QAAA;AAAA,MAAA;AAAA,IAAA;AAAA,EAG9E;AACF;AAEA,sBAAsB,cAAc;"}
@@ -1,5 +1,5 @@
1
1
  import { jsx, jsxs } from "react/jsx-runtime";
2
- import React__default, { useRef, useState, useEffect } from "react";
2
+ import React__default, { useRef, useId, useEffect } from "react";
3
3
  import classNames from "classnames";
4
4
  import { theme } from "../../theme/index.js";
5
5
  import "../../hooks/useBreakpoint.js";
@@ -7,16 +7,12 @@ import { useHover } from "../../hooks/useHover.js";
7
7
  import { useToggle } from "../../hooks/useToggle.js";
8
8
  import { useKeyboardEvent } from "../../hooks/useKeyboardEvent.js";
9
9
  import { useOutsideEvent } from "../../hooks/useOutsideEvent.js";
10
- import { useUuid } from "../../hooks/useUuid.js";
11
10
  import { LanguageLocales, ZIndex, KeyboardEventKey, AnalyticsId, IconSize } from "../../constants.js";
12
- import { isComponent } from "../../utils/component.js";
13
11
  import { useLanguage } from "../../utils/language.js";
14
12
  import { mergeRefs } from "../../utils/refs.js";
15
13
  import { B as Button } from "../../Button.js";
16
- import { C as Checkbox } from "../../Checkbox.js";
17
14
  import { I as Icon } from "../../Icon.js";
18
15
  import PlusSmall from "../Icons/PlusSmall.js";
19
- import { R as RadioButton } from "../../RadioButton.js";
20
16
  import styles from "./styles.module.scss";
21
17
  const closeText$1 = "Close";
22
18
  const enGB = {
@@ -65,10 +61,10 @@ const Dropdown = (props) => {
65
61
  const openedByKeyboard = useRef(false);
66
62
  const { value: isOpen, toggleValue: toggleIsOpen } = useToggle(!disabled && open, onToggle);
67
63
  const inputRefList = useRef(React__default.Children.map(children, () => React__default.createRef()));
68
- const [currentIndex, setCurrentIndex] = useState();
69
- const labelId = useUuid();
70
- const toggleLabelId = useUuid();
71
- const optionIdPrefix = useUuid();
64
+ const labelId = useId();
65
+ const toggleLabelId = useId();
66
+ const optionIdPrefix = useId();
67
+ const contentId = useId();
72
68
  const { language } = useLanguage(LanguageLocales.NORWEGIAN);
73
69
  const defaultResources = getResources(language);
74
70
  const mergedResources = {
@@ -86,14 +82,11 @@ const Dropdown = (props) => {
86
82
  (_a = buttonRef.current) == null ? void 0 : _a.focus();
87
83
  };
88
84
  useEffect(() => {
89
- var _a, _b, _c;
85
+ var _a, _b;
90
86
  if (isOpen && openedByKeyboard.current) {
91
- (_c = (_b = (_a = inputRefList.current) == null ? void 0 : _a.find((inputRef, index) => {
92
- if (inputRef.current && !inputRef.current.hasAttribute("disabled")) {
93
- setCurrentIndex(index);
94
- return true;
95
- }
96
- })) == null ? void 0 : _b.current) == null ? void 0 : _c.focus();
87
+ const firstEnabled = (_a = inputRefList.current) == null ? void 0 : _a.find((r) => r.current && !r.current.hasAttribute("disabled"));
88
+ (_b = firstEnabled == null ? void 0 : firstEnabled.current) == null ? void 0 : _b.focus();
89
+ openedByKeyboard.current = false;
97
90
  }
98
91
  }, [isOpen]);
99
92
  const handleKeyboardNavigation = (event) => {
@@ -126,7 +119,6 @@ const Dropdown = (props) => {
126
119
  if (nextIndex !== -1 && event.key !== KeyboardEventKey.Space) {
127
120
  event.preventDefault();
128
121
  (_a = inputRefList.current[nextIndex].current) == null ? void 0 : _a.focus();
129
- setCurrentIndex(nextIndex);
130
122
  }
131
123
  };
132
124
  useKeyboardEvent(dropdownRef, handleKeyboardNavigation, [
@@ -153,8 +145,9 @@ const Dropdown = (props) => {
153
145
  );
154
146
  const contentClasses = classNames(styles.dropdown__content, isOpen && styles["dropdown__content--open"]);
155
147
  const renderChildren = React__default.Children.map(children, (child, index) => {
156
- const role = isComponent(child, RadioButton) ? "menuitemradio" : isComponent(child, Checkbox) ? "menuitemcheckbox" : "menuitem";
157
- return /* @__PURE__ */ jsx("li", { className: styles.dropdown__input, role, id: `${optionIdPrefix}-${index}`, children: React__default.isValidElement(child) && inputRefList.current && inputRefList.current[index] ? React__default.cloneElement(child, { ref: mergeRefs([child.props.ref, inputRefList.current[index]]) }) : child });
148
+ return /* @__PURE__ */ jsx("li", { className: styles.dropdown__input, id: `${optionIdPrefix}-${index}`, children: React__default.isValidElement(child) && inputRefList.current && inputRefList.current[index] ? React__default.cloneElement(child, {
149
+ ref: mergeRefs([child.props.ref, inputRefList.current[index]])
150
+ }) : child });
158
151
  });
159
152
  return /* @__PURE__ */ jsxs("div", { className: styles.dropdown, ref: dropdownRef, children: [
160
153
  /* @__PURE__ */ jsx("span", { id: labelId, className: styles.dropdown__label, children: label }),
@@ -169,7 +162,8 @@ const Dropdown = (props) => {
169
162
  "data-analyticsid": AnalyticsId.Dropdown,
170
163
  disabled,
171
164
  "aria-labelledby": toggleLabelId,
172
- "aria-haspopup": "menu",
165
+ "aria-haspopup": true,
166
+ "aria-controls": contentId,
173
167
  "aria-expanded": isOpen,
174
168
  children: [
175
169
  /* @__PURE__ */ jsx("span", { id: toggleLabelId, className: styles.dropdown__toggle__label, children: placeholder }),
@@ -186,21 +180,18 @@ const Dropdown = (props) => {
186
180
  ]
187
181
  }
188
182
  ),
189
- /* @__PURE__ */ jsxs("div", { className: contentClasses, style: { width: fluid ? "100%" : `auto`, minWidth: dropdownMinWidth ?? "auto", zIndex }, children: [
190
- /* @__PURE__ */ jsx(
191
- "ul",
192
- {
193
- className: styles.dropdown__options,
194
- role: "menu",
195
- "aria-labelledby": labelId,
196
- tabIndex: -1,
197
- "aria-activedescendant": typeof currentIndex !== "undefined" ? `${optionIdPrefix}-${currentIndex}` : void 0,
198
- ref: optionsRef,
199
- children: renderChildren
200
- }
201
- ),
202
- !noCloseButton && /* @__PURE__ */ jsx("div", { className: styles.dropdown__close, children: /* @__PURE__ */ jsx(Button, { onClick: handleClose, "aria-expanded": isOpen, children: mergedResources.closeText }) })
203
- ] })
183
+ /* @__PURE__ */ jsxs(
184
+ "div",
185
+ {
186
+ id: contentId,
187
+ className: contentClasses,
188
+ style: { width: fluid ? "100%" : `auto`, minWidth: dropdownMinWidth ?? "auto", zIndex },
189
+ children: [
190
+ /* @__PURE__ */ jsx("ul", { className: styles.dropdown__options, role: "group", "aria-labelledby": labelId, tabIndex: -1, ref: optionsRef, children: renderChildren }),
191
+ !noCloseButton && /* @__PURE__ */ jsx("div", { className: styles.dropdown__close, children: /* @__PURE__ */ jsx(Button, { onClick: handleClose, children: mergedResources.closeText }) })
192
+ ]
193
+ }
194
+ )
204
195
  ] });
205
196
  };
206
197
  export {
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../../../src/components/Dropdown/resourceHelper.ts","../../../src/components/Dropdown/Dropdown.tsx"],"sourcesContent":["import { LanguageLocales } from '../../constants';\nimport enGB from '../../resources/HN.Designsystem.Dropdown.en-GB.json';\nimport nbNO from '../../resources/HN.Designsystem.Dropdown.nb-NO.json';\nimport { HNDesignsystemDropdown } from '../../resources/Resources';\n\nexport const getResources = (language: LanguageLocales): HNDesignsystemDropdown => {\n switch (language) {\n case LanguageLocales.ENGLISH:\n return enGB;\n case LanguageLocales.NORWEGIAN:\n default:\n return nbNO;\n }\n};\n","import React, { useEffect, useRef, useState } from 'react';\n\nimport classNames from 'classnames';\n\nimport {\n AnalyticsId,\n IconSize,\n KeyboardEventKey,\n LanguageLocales,\n ZIndex,\n theme,\n useHover,\n useKeyboardEvent,\n useOutsideEvent,\n useToggle,\n useUuid,\n} from '../..';\nimport { HNDesignsystemDropdown } from '../../resources/Resources';\nimport { isComponent } from '../../utils/component';\nimport { useLanguage } from '../../utils/language';\nimport { mergeRefs } from '../../utils/refs';\nimport Button from '../Button';\nimport Checkbox, { CheckboxProps } from '../Checkbox';\nimport Icon from '../Icon';\nimport PlusSmall from '../Icons/PlusSmall';\nimport RadioButton, { RadioButtonProps } from '../RadioButton';\nimport { getResources } from './resourceHelper';\n\nimport styles from './styles.module.scss';\n\nexport enum DropdownOnColor {\n onwhite = 'onwhite',\n ongrey = 'ongrey',\n onblueberry = 'onblueberry',\n oncherry = 'oncherry',\n}\n\nexport interface DropdownProps {\n /** Label for dropdown. Visible for screen readers */\n label: string;\n /** Text on the trigger button that opens the dropdown */\n placeholder: string;\n /** Sets the dropdown content */\n children: React.ReactNode;\n /** @deprecated Close button text */\n closeText?: string;\n /** Minimum width for the dropdown in pixels. Does not affect trigger button */\n dropdownMinWidth?: number;\n /** No close button */\n noCloseButton?: boolean;\n /** Called when dropdown is open/closed */\n onToggle?: (isOpen: boolean) => void;\n /** Whether the dropdown is open or not */\n open?: boolean;\n /** Changes the visuals of the dropdown */\n onColor?: keyof typeof DropdownOnColor;\n /** Makes the background of the trigger transparent */\n transparent?: boolean;\n /** Makes the width of the full component adjust to its parent */\n fluid?: boolean;\n /** Makes the dropdown disabled */\n disabled?: boolean;\n /** Sets the data-testid attribute on the dropdown button */\n testId?: string;\n /** Overrides the default z-index of the DropDownContent */\n zIndex?: number;\n /** Resources for component */\n resources?: Partial<HNDesignsystemDropdown>;\n}\n\nconst Dropdown: React.FC<DropdownProps> = props => {\n const {\n label,\n placeholder,\n noCloseButton = false,\n onToggle,\n dropdownMinWidth,\n open = false,\n children,\n onColor = DropdownOnColor.onwhite,\n transparent = false,\n fluid = false,\n testId,\n disabled,\n zIndex = ZIndex.PopOver,\n resources,\n } = props;\n const dropdownRef = useRef<HTMLDivElement>(null);\n const optionsRef = useRef<HTMLUListElement>(null);\n const { hoverRef: buttonRef, isHovered } = useHover<HTMLButtonElement>();\n const openedByKeyboard = useRef<boolean>(false);\n const { value: isOpen, toggleValue: toggleIsOpen } = useToggle(!disabled && open, onToggle);\n const inputRefList = useRef(React.Children.map(children, () => React.createRef<HTMLElement>()));\n const [currentIndex, setCurrentIndex] = useState<number>();\n const labelId = useUuid();\n const toggleLabelId = useUuid();\n const optionIdPrefix = useUuid();\n const { language } = useLanguage<LanguageLocales>(LanguageLocales.NORWEGIAN);\n const defaultResources = getResources(language);\n\n const mergedResources: HNDesignsystemDropdown = {\n ...defaultResources,\n ...resources,\n closeText: props.closeText ?? resources?.closeText ?? defaultResources.closeText,\n };\n\n const handleOpen = (isKeyboard: boolean): void => {\n openedByKeyboard.current = isKeyboard;\n toggleIsOpen();\n };\n\n const handleClose = (): void => {\n toggleIsOpen();\n buttonRef.current?.focus();\n };\n\n useEffect(() => {\n if (isOpen && openedByKeyboard.current) {\n inputRefList.current\n ?.find((inputRef, index) => {\n if (inputRef.current && !inputRef.current.hasAttribute('disabled')) {\n setCurrentIndex(index);\n return true;\n }\n })\n ?.current?.focus();\n }\n }, [isOpen]);\n\n const handleKeyboardNavigation = (event: KeyboardEvent): void => {\n if (!inputRefList.current) {\n return;\n }\n\n if (event.key === KeyboardEventKey.Escape) {\n if (isOpen) handleClose();\n return;\n }\n\n if (!isOpen) {\n handleOpen(true);\n event.preventDefault();\n return;\n }\n\n const index = inputRefList.current.findIndex(x => x.current === event.target);\n let nextIndex = index;\n\n if (event.key === KeyboardEventKey.Home) {\n nextIndex = 0;\n } else if (event.key === KeyboardEventKey.End) {\n nextIndex = inputRefList.current.length - 1;\n } else if (event.key === KeyboardEventKey.ArrowDown && index < inputRefList.current.length - 1) {\n nextIndex = index + 1;\n } else if (event.key === KeyboardEventKey.ArrowUp && index > 0) {\n nextIndex = index - 1;\n } else if (event.key === KeyboardEventKey.Enter && index !== -1) {\n nextIndex = index;\n }\n\n if (nextIndex !== -1 && event.key !== KeyboardEventKey.Space) {\n event.preventDefault();\n\n inputRefList.current[nextIndex].current?.focus();\n setCurrentIndex(nextIndex);\n }\n };\n\n useKeyboardEvent(dropdownRef, handleKeyboardNavigation, [\n KeyboardEventKey.ArrowDown,\n KeyboardEventKey.ArrowUp,\n KeyboardEventKey.End,\n KeyboardEventKey.Enter,\n KeyboardEventKey.Escape,\n KeyboardEventKey.Home,\n KeyboardEventKey.Space,\n ]);\n\n useOutsideEvent(dropdownRef, () => isOpen && handleClose());\n\n const toggleClasses = classNames(\n styles.dropdown__toggle,\n !disabled && {\n [styles['dropdown__toggle--on-white']]: onColor === DropdownOnColor.onwhite,\n [styles['dropdown__toggle--on-grey']]: onColor === DropdownOnColor.ongrey,\n [styles['dropdown__toggle--on-blueberry']]: onColor === DropdownOnColor.onblueberry,\n [styles['dropdown__toggle--on-cherry']]: onColor === DropdownOnColor.oncherry,\n [styles['dropdown__toggle--transparent']]: transparent,\n [styles['dropdown__toggle--fluid']]: fluid,\n [styles['dropdown__toggle--open']]: isOpen,\n }\n );\n\n const contentClasses = classNames(styles.dropdown__content, isOpen && styles['dropdown__content--open']);\n\n const renderChildren = React.Children.map(children, (child, index) => {\n const role = isComponent<RadioButtonProps>(child, RadioButton)\n ? 'menuitemradio'\n : isComponent<CheckboxProps>(child, Checkbox)\n ? 'menuitemcheckbox'\n : 'menuitem';\n\n return (\n <li className={styles.dropdown__input} role={role} id={`${optionIdPrefix}-${index}`}>\n {React.isValidElement(child) && inputRefList.current && inputRefList.current[index]\n ? React.cloneElement(child as React.ReactElement, { ref: mergeRefs([child.props.ref, inputRefList.current[index]]) })\n : child}\n </li>\n );\n });\n\n return (\n <div className={styles.dropdown} ref={dropdownRef}>\n <span id={labelId} className={styles.dropdown__label}>\n {label}\n </span>\n <button\n type=\"button\"\n onClick={(): false | void => handleOpen(false)}\n className={toggleClasses}\n ref={buttonRef}\n data-testid={testId}\n data-analyticsid={AnalyticsId.Dropdown}\n disabled={disabled}\n aria-labelledby={toggleLabelId}\n aria-haspopup=\"menu\"\n aria-expanded={isOpen}\n >\n <span id={toggleLabelId} className={styles.dropdown__toggle__label}>\n {placeholder}\n </span>\n <Icon\n color={disabled ? theme.palette.neutral700 : theme.palette.blueberry600}\n svgIcon={PlusSmall}\n className={styles.dropdown__icon}\n isHovered={!disabled && isHovered}\n size={IconSize.XSmall}\n />\n </button>\n <div className={contentClasses} style={{ width: fluid ? '100%' : `auto`, minWidth: dropdownMinWidth ?? 'auto', zIndex: zIndex }}>\n <ul\n className={styles.dropdown__options}\n role=\"menu\"\n aria-labelledby={labelId}\n tabIndex={-1}\n aria-activedescendant={typeof currentIndex !== 'undefined' ? `${optionIdPrefix}-${currentIndex}` : undefined}\n ref={optionsRef}\n >\n {renderChildren}\n </ul>\n {!noCloseButton && (\n <div className={styles.dropdown__close}>\n <Button onClick={handleClose} aria-expanded={isOpen}>\n {mergedResources.closeText}\n </Button>\n </div>\n )}\n </div>\n </div>\n );\n};\n\nexport default Dropdown;\n"],"names":["DropdownOnColor","React"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sources":["../../../src/components/Dropdown/resourceHelper.ts","../../../src/components/Dropdown/Dropdown.tsx"],"sourcesContent":["import { LanguageLocales } from '../../constants';\nimport enGB from '../../resources/HN.Designsystem.Dropdown.en-GB.json';\nimport nbNO from '../../resources/HN.Designsystem.Dropdown.nb-NO.json';\nimport { HNDesignsystemDropdown } from '../../resources/Resources';\n\nexport const getResources = (language: LanguageLocales): HNDesignsystemDropdown => {\n switch (language) {\n case LanguageLocales.ENGLISH:\n return enGB;\n case LanguageLocales.NORWEGIAN:\n default:\n return nbNO;\n }\n};\n","import React, { useEffect, useRef, useId } from 'react';\n\nimport classNames from 'classnames';\n\nimport {\n AnalyticsId,\n IconSize,\n KeyboardEventKey,\n LanguageLocales,\n ZIndex,\n theme,\n useHover,\n useKeyboardEvent,\n useOutsideEvent,\n useToggle,\n} from '../..';\nimport { getResources } from './resourceHelper';\nimport { HNDesignsystemDropdown } from '../../resources/Resources';\nimport { useLanguage } from '../../utils/language';\nimport { mergeRefs } from '../../utils/refs';\nimport Button from '../Button';\nimport Icon from '../Icon';\nimport PlusSmall from '../Icons/PlusSmall';\n\nimport styles from './styles.module.scss';\n\nexport enum DropdownOnColor {\n onwhite = 'onwhite',\n ongrey = 'ongrey',\n onblueberry = 'onblueberry',\n oncherry = 'oncherry',\n}\n\nexport interface DropdownProps {\n /** Label for dropdown. Visible for screen readers */\n label: string;\n /** Text on the trigger button that opens the dropdown */\n placeholder: string;\n /** Sets the dropdown content */\n children: React.ReactNode;\n /** @deprecated Close button text */\n closeText?: string;\n /** Minimum width for the dropdown in pixels. Does not affect trigger button */\n dropdownMinWidth?: number;\n /** No close button */\n noCloseButton?: boolean;\n /** Called when dropdown is open/closed */\n onToggle?: (isOpen: boolean) => void;\n /** Whether the dropdown is open or not */\n open?: boolean;\n /** Changes the visuals of the dropdown */\n onColor?: keyof typeof DropdownOnColor;\n /** Makes the background of the trigger transparent */\n transparent?: boolean;\n /** Makes the width of the full component adjust to its parent */\n fluid?: boolean;\n /** Makes the dropdown disabled */\n disabled?: boolean;\n /** Sets the data-testid attribute on the dropdown button */\n testId?: string;\n /** Overrides the default z-index of the DropDownContent */\n zIndex?: number;\n /** Resources for component */\n resources?: Partial<HNDesignsystemDropdown>;\n}\n\nconst Dropdown: React.FC<DropdownProps> = props => {\n const {\n label,\n placeholder,\n noCloseButton = false,\n onToggle,\n dropdownMinWidth,\n open = false,\n children,\n onColor = DropdownOnColor.onwhite,\n transparent = false,\n fluid = false,\n testId,\n disabled,\n zIndex = ZIndex.PopOver,\n resources,\n } = props;\n\n const dropdownRef = useRef<HTMLDivElement>(null);\n const optionsRef = useRef<HTMLUListElement>(null);\n const { hoverRef: buttonRef, isHovered } = useHover<HTMLButtonElement>();\n const openedByKeyboard = useRef<boolean>(false);\n const { value: isOpen, toggleValue: toggleIsOpen } = useToggle(!disabled && open, onToggle);\n const inputRefList = useRef(React.Children.map(children, () => React.createRef<HTMLElement>()));\n const labelId = useId();\n const toggleLabelId = useId();\n const optionIdPrefix = useId();\n const contentId = useId();\n const { language } = useLanguage<LanguageLocales>(LanguageLocales.NORWEGIAN);\n const defaultResources = getResources(language);\n\n const mergedResources: HNDesignsystemDropdown = {\n ...defaultResources,\n ...resources,\n closeText: props.closeText ?? resources?.closeText ?? defaultResources.closeText,\n };\n\n const handleOpen = (isKeyboard: boolean): void => {\n openedByKeyboard.current = isKeyboard;\n toggleIsOpen();\n };\n\n const handleClose = (): void => {\n toggleIsOpen();\n buttonRef.current?.focus();\n };\n\n useEffect(() => {\n if (isOpen && openedByKeyboard.current) {\n const firstEnabled = inputRefList.current?.find(r => r.current && !r.current.hasAttribute('disabled'));\n firstEnabled?.current?.focus();\n openedByKeyboard.current = false;\n }\n }, [isOpen]);\n\n const handleKeyboardNavigation = (event: KeyboardEvent): void => {\n if (!inputRefList.current) {\n return;\n }\n\n if (event.key === KeyboardEventKey.Escape) {\n if (isOpen) handleClose();\n return;\n }\n\n if (!isOpen) {\n handleOpen(true);\n event.preventDefault();\n return;\n }\n\n const index = inputRefList.current.findIndex(x => x.current === event.target);\n let nextIndex = index;\n\n if (event.key === KeyboardEventKey.Home) {\n nextIndex = 0;\n } else if (event.key === KeyboardEventKey.End) {\n nextIndex = inputRefList.current.length - 1;\n } else if (event.key === KeyboardEventKey.ArrowDown && index < inputRefList.current.length - 1) {\n nextIndex = index + 1;\n } else if (event.key === KeyboardEventKey.ArrowUp && index > 0) {\n nextIndex = index - 1;\n } else if (event.key === KeyboardEventKey.Enter && index !== -1) {\n nextIndex = index;\n }\n\n if (nextIndex !== -1 && event.key !== KeyboardEventKey.Space) {\n event.preventDefault();\n\n inputRefList.current[nextIndex].current?.focus();\n }\n };\n\n useKeyboardEvent(dropdownRef, handleKeyboardNavigation, [\n KeyboardEventKey.ArrowDown,\n KeyboardEventKey.ArrowUp,\n KeyboardEventKey.End,\n KeyboardEventKey.Enter,\n KeyboardEventKey.Escape,\n KeyboardEventKey.Home,\n KeyboardEventKey.Space,\n ]);\n\n useOutsideEvent(dropdownRef, () => isOpen && handleClose());\n\n const toggleClasses = classNames(\n styles.dropdown__toggle,\n !disabled && {\n [styles['dropdown__toggle--on-white']]: onColor === DropdownOnColor.onwhite,\n [styles['dropdown__toggle--on-grey']]: onColor === DropdownOnColor.ongrey,\n [styles['dropdown__toggle--on-blueberry']]: onColor === DropdownOnColor.onblueberry,\n [styles['dropdown__toggle--on-cherry']]: onColor === DropdownOnColor.oncherry,\n [styles['dropdown__toggle--transparent']]: transparent,\n [styles['dropdown__toggle--fluid']]: fluid,\n [styles['dropdown__toggle--open']]: isOpen,\n }\n );\n\n const contentClasses = classNames(styles.dropdown__content, isOpen && styles['dropdown__content--open']);\n\n const renderChildren = React.Children.map(children, (child, index) => {\n return (\n <li className={styles.dropdown__input} id={`${optionIdPrefix}-${index}`}>\n {React.isValidElement(child) && inputRefList.current && inputRefList.current[index]\n ? React.cloneElement(child as React.ReactElement, {\n ref: mergeRefs([child.props.ref, inputRefList.current[index]]),\n })\n : child}\n </li>\n );\n });\n\n return (\n <div className={styles.dropdown} ref={dropdownRef}>\n <span id={labelId} className={styles.dropdown__label}>\n {label}\n </span>\n <button\n type=\"button\"\n onClick={(): false | void => handleOpen(false)}\n className={toggleClasses}\n ref={buttonRef}\n data-testid={testId}\n data-analyticsid={AnalyticsId.Dropdown}\n disabled={disabled}\n aria-labelledby={toggleLabelId}\n aria-haspopup={true}\n aria-controls={contentId}\n aria-expanded={isOpen}\n >\n <span id={toggleLabelId} className={styles.dropdown__toggle__label}>\n {placeholder}\n </span>\n <Icon\n color={disabled ? theme.palette.neutral700 : theme.palette.blueberry600}\n svgIcon={PlusSmall}\n className={styles.dropdown__icon}\n isHovered={!disabled && isHovered}\n size={IconSize.XSmall}\n />\n </button>\n <div\n id={contentId}\n className={contentClasses}\n style={{ width: fluid ? '100%' : `auto`, minWidth: dropdownMinWidth ?? 'auto', zIndex: zIndex }}\n >\n <ul className={styles.dropdown__options} role=\"group\" aria-labelledby={labelId} tabIndex={-1} ref={optionsRef}>\n {renderChildren}\n </ul>\n {!noCloseButton && (\n <div className={styles.dropdown__close}>\n <Button onClick={handleClose}>{mergedResources.closeText}</Button>\n </div>\n )}\n </div>\n </div>\n );\n};\n\nexport default Dropdown;\n"],"names":["DropdownOnColor","React"],"mappings":"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"}
@@ -11,6 +11,8 @@ export interface ExpanderProps extends ExpanderHierarchyCommonProps {
11
11
  children?: React.ReactNode;
12
12
  /** Sets the data-testid attribute on the expander button. */
13
13
  testId?: string;
14
+ /** Adds custom classes to the element. */
15
+ className?: string;
14
16
  }
15
17
  export type ExpanderType = React.FC<ExpanderProps>;
16
18
  declare const Expander: ExpanderType;
@@ -75,14 +75,15 @@ const Expander = ({
75
75
  expanded = false,
76
76
  onExpand,
77
77
  children,
78
- testId
78
+ testId,
79
+ className
79
80
  }) => {
80
81
  const contentClasses = classNames(
81
82
  styles.expander__content,
82
83
  (level === 1 || level === 2) && styles[`expander__content--${level}`],
83
84
  level > 2 && styles[`expander__content--3-and-lower`]
84
85
  );
85
- return /* @__PURE__ */ jsxs("li", { className: styles.expander, children: [
86
+ return /* @__PURE__ */ jsxs("li", { className: classNames(styles.expander, className), children: [
86
87
  /* @__PURE__ */ jsx(ExpanderButton, { htmlMarkup, level, print, expanded, onExpand, testId, children: title }),
87
88
  /* @__PURE__ */ jsx("div", { className: contentClasses, children: React__default.Children.map(
88
89
  children,
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../../../src/components/ExpanderHierarchy/ExpanderButton.tsx","../../../src/components/ExpanderHierarchy/utils.ts","../../../src/components/ExpanderHierarchy/Expander.tsx","../../../src/components/ExpanderHierarchy/ExpanderHierarchy.tsx"],"sourcesContent":["import React from 'react';\n\nimport classNames from 'classnames';\n\nimport { AnalyticsId } from '../../constants';\nimport { useExpand } from '../../hooks/useExpand';\nimport { useHover } from '../../hooks/useHover';\nimport { ExpanderHierarchyCommonProps } from '../ExpanderHierarchy';\nimport Icon, { IconSize } from '../Icon';\nimport ChevronDown from '../Icons/ChevronDown';\nimport ChevronUp from '../Icons/ChevronUp';\n\nimport styles from './expander.module.scss';\n\ninterface ExpanderButtonProps extends ExpanderHierarchyCommonProps {\n expanded: boolean;\n onExpand?: (isExpanded: boolean) => void;\n children: string;\n testId?: string;\n}\n\nconst ExpanderButton: React.FC<ExpanderButtonProps> = ({ htmlMarkup = 'h2', level = 1, print, expanded, onExpand, children, testId }) => {\n const { hoverRef, isHovered } = useHover<HTMLButtonElement>();\n const [isExpanded, setIsExpanded] = useExpand(expanded, onExpand);\n\n const handleClick = (): void => {\n setIsExpanded(!isExpanded);\n };\n\n const CustomTag = htmlMarkup;\n\n const buttonClasses = classNames(\n styles.expander__button,\n level > 1 && styles[`expander__button--2-and-lower`],\n print && styles['expander__button--print']\n );\n\n const titleClasses = classNames(\n styles.expander__title,\n (level === 1 || level === 2 || level === 3) && styles[`expander__title--${level}`],\n level > 3 && styles[`expander__title--4-and-lower`],\n isExpanded && styles['expander__title--expanded'],\n print && styles['expander__title--print']\n );\n\n const iconClasses = classNames(\n styles.expander__icon,\n level > 1 && styles[`expander__icon--2-and-lower`],\n level > 2 && styles[`expander__icon--3-and-lower`]\n );\n\n return (\n <CustomTag className={titleClasses}>\n <button\n type=\"button\"\n onClick={handleClick}\n className={buttonClasses}\n aria-expanded={isExpanded}\n ref={hoverRef}\n data-testid={testId}\n data-analyticsid={AnalyticsId.ExpanderHierarchyExpander}\n >\n {children}\n <Icon svgIcon={isExpanded ? ChevronUp : ChevronDown} isHovered={isHovered} className={iconClasses} size={IconSize.XSmall} />\n </button>\n </CustomTag>\n );\n};\n\nexport default ExpanderButton;\n","export type HeadingTags = 'h2' | 'h3' | 'h4' | 'h5' | 'h6';\n\nexport const getHeadingTag = (htmlMarkup?: HeadingTags): HeadingTags => {\n switch (htmlMarkup) {\n case 'h2':\n return 'h3';\n case 'h3':\n return 'h4';\n case 'h4':\n return 'h5';\n case 'h5':\n return 'h6';\n case 'h6':\n return 'h6';\n default:\n return 'h2';\n }\n};\n","import React from 'react';\n\nimport classNames from 'classnames';\n\nimport ExpanderButton from './ExpanderButton';\nimport ExpanderHierarchy, { ExpanderHierarchyCommonProps, ExpanderHierarchyProps } from './ExpanderHierarchy';\nimport { getHeadingTag } from './utils';\nimport { isComponent } from '../../utils/component';\n\nimport styles from './expander.module.scss';\n\nexport interface ExpanderProps extends ExpanderHierarchyCommonProps {\n /** Sets the trigger title */\n title: string;\n /** Opens or closes the expander */\n expanded?: boolean;\n /** Called when expander is open/closed. */\n onExpand?: (isExpanded: boolean) => void;\n /** Sets the expanded content */\n children?: React.ReactNode;\n /** Sets the data-testid attribute on the expander button. */\n testId?: string;\n}\n\nexport type ExpanderType = React.FC<ExpanderProps>;\n\nconst Expander: ExpanderType = ({\n title,\n htmlMarkup = 'h2',\n level = 1,\n print,\n expanded = false,\n onExpand,\n children,\n testId,\n}: ExpanderProps) => {\n const contentClasses = classNames(\n styles.expander__content,\n (level === 1 || level === 2) && styles[`expander__content--${level}`],\n level > 2 && styles[`expander__content--3-and-lower`]\n );\n\n return (\n <li className={styles.expander}>\n <ExpanderButton htmlMarkup={htmlMarkup} level={level} print={print} expanded={expanded} onExpand={onExpand} testId={testId}>\n {title}\n </ExpanderButton>\n <div className={contentClasses}>\n {React.Children.map(children, child =>\n isComponent<ExpanderHierarchyProps>(child, ExpanderHierarchy)\n ? React.cloneElement(child, { htmlMarkup: getHeadingTag(htmlMarkup), level: level + 1, print })\n : child\n )}\n </div>\n </li>\n );\n};\n\nexport default Expander;\n","import React from 'react';\n\nimport classNames from 'classnames';\n\nimport Expander, { ExpanderProps, ExpanderType } from './Expander';\nimport { HeadingTags } from './utils';\nimport { AnalyticsId } from '../../constants';\nimport { isComponent } from '../../utils/component';\n\nimport styles from './styles.module.scss';\n\nexport interface ExpanderHierarchyCommonProps {\n /** Changes the underlying element of the expander title. Default: h2. */\n htmlMarkup?: HeadingTags;\n /** Expand all children when printing. */\n print?: boolean;\n /** Expander nesting level. Should not be set manually. */\n level?: number;\n}\n\nexport interface ExpanderHierarchyProps extends ExpanderHierarchyCommonProps {\n children: React.ReactNode;\n /** Sets the data-testid attribute on the expander list. */\n testId?: string;\n}\n\nexport interface ExpanderHierarchyCompound extends React.FC<ExpanderHierarchyProps> {\n Expander: ExpanderType;\n}\n\nconst ExpanderHierarchy: ExpanderHierarchyCompound = ({\n htmlMarkup = 'h2',\n level = 1,\n print,\n children,\n testId,\n}: ExpanderHierarchyProps) => {\n const listClasses = classNames(\n styles.expanderhierarchy,\n level === 2 && styles[`expanderhierarchy--${level}`],\n level > 2 && styles[`expanderhierarchy--3-and-lower`]\n );\n\n return (\n <ul className={listClasses} data-testid={testId} data-analyticsid={AnalyticsId.ExpanderHierarchy}>\n {React.Children.map(children, child =>\n isComponent<ExpanderProps>(child, Expander) ? React.cloneElement(child, { htmlMarkup, level, print }) : child\n )}\n </ul>\n );\n};\n\nExpanderHierarchy.Expander = Expander;\n\nexport default ExpanderHierarchy;\n"],"names":["React","styles"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sources":["../../../src/components/ExpanderHierarchy/ExpanderButton.tsx","../../../src/components/ExpanderHierarchy/utils.ts","../../../src/components/ExpanderHierarchy/Expander.tsx","../../../src/components/ExpanderHierarchy/ExpanderHierarchy.tsx"],"sourcesContent":["import React from 'react';\n\nimport classNames from 'classnames';\n\nimport { AnalyticsId } from '../../constants';\nimport { useExpand } from '../../hooks/useExpand';\nimport { useHover } from '../../hooks/useHover';\nimport { ExpanderHierarchyCommonProps } from '../ExpanderHierarchy';\nimport Icon, { IconSize } from '../Icon';\nimport ChevronDown from '../Icons/ChevronDown';\nimport ChevronUp from '../Icons/ChevronUp';\n\nimport styles from './expander.module.scss';\n\ninterface ExpanderButtonProps extends ExpanderHierarchyCommonProps {\n expanded: boolean;\n onExpand?: (isExpanded: boolean) => void;\n children: string;\n testId?: string;\n}\n\nconst ExpanderButton: React.FC<ExpanderButtonProps> = ({ htmlMarkup = 'h2', level = 1, print, expanded, onExpand, children, testId }) => {\n const { hoverRef, isHovered } = useHover<HTMLButtonElement>();\n const [isExpanded, setIsExpanded] = useExpand(expanded, onExpand);\n\n const handleClick = (): void => {\n setIsExpanded(!isExpanded);\n };\n\n const CustomTag = htmlMarkup;\n\n const buttonClasses = classNames(\n styles.expander__button,\n level > 1 && styles[`expander__button--2-and-lower`],\n print && styles['expander__button--print']\n );\n\n const titleClasses = classNames(\n styles.expander__title,\n (level === 1 || level === 2 || level === 3) && styles[`expander__title--${level}`],\n level > 3 && styles[`expander__title--4-and-lower`],\n isExpanded && styles['expander__title--expanded'],\n print && styles['expander__title--print']\n );\n\n const iconClasses = classNames(\n styles.expander__icon,\n level > 1 && styles[`expander__icon--2-and-lower`],\n level > 2 && styles[`expander__icon--3-and-lower`]\n );\n\n return (\n <CustomTag className={titleClasses}>\n <button\n type=\"button\"\n onClick={handleClick}\n className={buttonClasses}\n aria-expanded={isExpanded}\n ref={hoverRef}\n data-testid={testId}\n data-analyticsid={AnalyticsId.ExpanderHierarchyExpander}\n >\n {children}\n <Icon svgIcon={isExpanded ? ChevronUp : ChevronDown} isHovered={isHovered} className={iconClasses} size={IconSize.XSmall} />\n </button>\n </CustomTag>\n );\n};\n\nexport default ExpanderButton;\n","export type HeadingTags = 'h2' | 'h3' | 'h4' | 'h5' | 'h6';\n\nexport const getHeadingTag = (htmlMarkup?: HeadingTags): HeadingTags => {\n switch (htmlMarkup) {\n case 'h2':\n return 'h3';\n case 'h3':\n return 'h4';\n case 'h4':\n return 'h5';\n case 'h5':\n return 'h6';\n case 'h6':\n return 'h6';\n default:\n return 'h2';\n }\n};\n","import React from 'react';\n\nimport classNames from 'classnames';\n\nimport ExpanderButton from './ExpanderButton';\nimport ExpanderHierarchy, { ExpanderHierarchyCommonProps, ExpanderHierarchyProps } from './ExpanderHierarchy';\nimport { getHeadingTag } from './utils';\nimport { isComponent } from '../../utils/component';\n\nimport styles from './expander.module.scss';\n\nexport interface ExpanderProps extends ExpanderHierarchyCommonProps {\n /** Sets the trigger title */\n title: string;\n /** Opens or closes the expander */\n expanded?: boolean;\n /** Called when expander is open/closed. */\n onExpand?: (isExpanded: boolean) => void;\n /** Sets the expanded content */\n children?: React.ReactNode;\n /** Sets the data-testid attribute on the expander button. */\n testId?: string;\n /** Adds custom classes to the element. */\n className?: string;\n}\n\nexport type ExpanderType = React.FC<ExpanderProps>;\n\nconst Expander: ExpanderType = ({\n title,\n htmlMarkup = 'h2',\n level = 1,\n print,\n expanded = false,\n onExpand,\n children,\n testId,\n className,\n}: ExpanderProps) => {\n const contentClasses = classNames(\n styles.expander__content,\n (level === 1 || level === 2) && styles[`expander__content--${level}`],\n level > 2 && styles[`expander__content--3-and-lower`]\n );\n\n return (\n <li className={classNames(styles.expander, className)}>\n <ExpanderButton htmlMarkup={htmlMarkup} level={level} print={print} expanded={expanded} onExpand={onExpand} testId={testId}>\n {title}\n </ExpanderButton>\n <div className={contentClasses}>\n {React.Children.map(children, child =>\n isComponent<ExpanderHierarchyProps>(child, ExpanderHierarchy)\n ? React.cloneElement(child, { htmlMarkup: getHeadingTag(htmlMarkup), level: level + 1, print })\n : child\n )}\n </div>\n </li>\n );\n};\n\nexport default Expander;\n","import React from 'react';\n\nimport classNames from 'classnames';\n\nimport Expander, { ExpanderProps, ExpanderType } from './Expander';\nimport { HeadingTags } from './utils';\nimport { AnalyticsId } from '../../constants';\nimport { isComponent } from '../../utils/component';\n\nimport styles from './styles.module.scss';\n\nexport interface ExpanderHierarchyCommonProps {\n /** Changes the underlying element of the expander title. Default: h2. */\n htmlMarkup?: HeadingTags;\n /** Expand all children when printing. */\n print?: boolean;\n /** Expander nesting level. Should not be set manually. */\n level?: number;\n}\n\nexport interface ExpanderHierarchyProps extends ExpanderHierarchyCommonProps {\n children: React.ReactNode;\n /** Sets the data-testid attribute on the expander list. */\n testId?: string;\n}\n\nexport interface ExpanderHierarchyCompound extends React.FC<ExpanderHierarchyProps> {\n Expander: ExpanderType;\n}\n\nconst ExpanderHierarchy: ExpanderHierarchyCompound = ({\n htmlMarkup = 'h2',\n level = 1,\n print,\n children,\n testId,\n}: ExpanderHierarchyProps) => {\n const listClasses = classNames(\n styles.expanderhierarchy,\n level === 2 && styles[`expanderhierarchy--${level}`],\n level > 2 && styles[`expanderhierarchy--3-and-lower`]\n );\n\n return (\n <ul className={listClasses} data-testid={testId} data-analyticsid={AnalyticsId.ExpanderHierarchy}>\n {React.Children.map(children, child =>\n isComponent<ExpanderProps>(child, Expander) ? React.cloneElement(child, { htmlMarkup, level, print }) : child\n )}\n </ul>\n );\n};\n\nExpanderHierarchy.Expander = Expander;\n\nexport default ExpanderHierarchy;\n"],"names":["React","styles"],"mappings":"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"}
@@ -19,11 +19,9 @@
19
19
  }
20
20
 
21
21
  &--standalone {
22
- margin-top: -0.35rem;
23
22
  max-width: 32rem;
24
23
 
25
24
  @media (min-width: map.get(breakpoints.$grid-breakpoints, md)) {
26
- margin-top: -0.25rem;
27
25
  max-width: 36rem;
28
26
  }
29
27
  }
@@ -2,4 +2,9 @@
2
2
  // Plasserer HelpTriggerStandalone over HelpExpanderStandalone for at focus-outline skal vises riktig
3
3
  position: relative;
4
4
  z-index: 1;
5
+
6
+ // justerer tekst og chevron til topp ved flere linjer tekst
7
+ & > * {
8
+ align-items: flex-start;
9
+ }
5
10
  }
@@ -13,54 +13,47 @@ $outline-xl-width: 2.5px;
13
13
 
14
14
  .help-trigger-standalone {
15
15
  display: flex;
16
- align-items: center;
16
+ align-items: flex-start;
17
17
  justify-content: center;
18
- padding: 0;
19
18
  cursor: pointer;
20
19
  text-decoration: none;
21
20
  background-color: transparent;
22
21
  outline: none;
23
22
  border: 0;
24
23
  min-width: 4.6rem;
25
- height: 2.75rem;
24
+ min-height: 2.75rem;
25
+ color: var(--color-help-graphics-normal);
26
+ padding: 0 var(--core-space-3xs);
27
+ font-size: 1rem;
28
+ font-weight: 600;
29
+ line-height: 1.2rem;
30
+ text-align: left;
26
31
 
27
- &:focus-visible {
28
- outline: none;
32
+ // juster alle elementer inne i container for center alignment i knapp med én linje
33
+ & > * {
34
+ margin-top: 10px;
29
35
  }
30
36
 
31
- &__inner-container {
32
- display: flex;
33
- align-items: center;
34
- justify-content: center;
35
- min-height: var(--core-space-l);
36
- color: var(--color-help-graphics-normal);
37
- padding: 0 var(--core-space-3xs);
38
- font-size: 1rem;
39
- font-weight: 600;
40
- line-height: 1.2rem;
41
-
42
- :hover > &,
43
- :active > & {
44
- color: var(--color-help-graphics-verydark);
45
- background-color: var(--color-help-background-transparent-onlight-hover);
46
- }
37
+ &:hover,
38
+ &:active {
39
+ color: var(--color-help-graphics-verydark);
40
+ background-color: var(--color-help-background-transparent-onlight-hover);
41
+ }
47
42
 
48
- :focus-visible > & {
49
- outline: 3px solid var(--color-action-border-onlight-focus);
50
- }
43
+ &:focus-visible {
44
+ outline: 3px solid var(--color-action-border-onlight-focus);
45
+ }
51
46
 
52
- :active > & {
53
- text-decoration-line: underline;
54
- text-decoration-style: solid;
55
- text-decoration-thickness: 1px;
56
- text-underline-offset: 4.23px;
57
- }
47
+ &:active {
48
+ text-decoration-line: underline;
49
+ text-decoration-style: solid;
50
+ text-decoration-thickness: 1px;
51
+ text-underline-offset: 4.23px;
52
+ }
58
53
 
59
- @media (min-width: map.get(breakpoints.$grid-breakpoints, md)) {
60
- min-height: 2.25rem;
61
- font-size: 1.125rem;
62
- line-height: 1.463rem;
63
- }
54
+ @media (min-width: map.get(breakpoints.$grid-breakpoints, md)) {
55
+ font-size: 1.125rem;
56
+ line-height: 1.463rem;
64
57
  }
65
58
 
66
59
  &__children {
@@ -1,7 +1,6 @@
1
1
  export type Styles = {
2
2
  'help-trigger-standalone': string;
3
3
  'help-trigger-standalone__children': string;
4
- 'help-trigger-standalone__inner-container': string;
5
4
  };
6
5
 
7
6
  export type ClassNames = keyof Styles;
@@ -2,7 +2,6 @@
2
2
  @use '../../scss/spacers' as spacers;
3
3
  @use '../../scss/font-settings' as font-settings;
4
4
  @use '../../scss/palette' as palette;
5
- @use '../AnchorLink/styles.module' as anchorlink;
6
5
  @use '../../scss/breakpoints' as breakpoints;
7
6
 
8
7
  .notification-panel {
@@ -120,11 +119,6 @@
120
119
 
121
120
  &__content {
122
121
  align-self: center;
123
-
124
- a {
125
- @include anchorlink.anchorlink;
126
- @include anchorlink.anchorlink-icon;
127
- }
128
122
  }
129
123
 
130
124
  &__label,
@@ -1,7 +1,4 @@
1
1
  export type Styles = {
2
- anchorlink: string;
3
- anchorlink__icon: string;
4
- 'anchorlink-wrapper': string;
5
2
  'fluid-wrapper': string;
6
3
  'notification-panel': string;
7
4
  'notification-panel__children': string;
@@ -14,6 +14,8 @@ interface ValidationProps {
14
14
  size?: keyof typeof FormSize;
15
15
  /** Sets the data-testid attribute. */
16
16
  testId?: string;
17
+ /** Hides the ValidationSummary list visually - summary is still announced by screen readers */
18
+ visuallyHiddenSummary?: boolean;
17
19
  }
18
20
  export declare const Validation: React.ForwardRefExoticComponent<ValidationProps & React.RefAttributes<HTMLDivElement>>;
19
21
  export default Validation;
@@ -10,6 +10,8 @@ interface ValidationSummaryProps {
10
10
  errorTitleHtmlMarkup?: TitleTags;
11
11
  /** Will be shown last */
12
12
  children?: React.ReactNode;
13
+ /** Hides the list visually - summary is still announced by screen readers */
14
+ visuallyHidden?: boolean;
13
15
  }
14
16
  declare const ValidationSummary: React.FC<ValidationSummaryProps>;
15
17
  export default ValidationSummary;
@@ -30,7 +30,11 @@ const ValidationSummary = (props) => {
30
30
  const { errorTitleHtmlMarkup = "h2" } = props;
31
31
  const titleId = useUuid();
32
32
  const hasErrors = !!props.errors && Object.entries(props.errors).length > 0;
33
- const summaryClasses = classNames(styles["validation__summary"], hasErrors && styles["validation__summary--visible"]);
33
+ const visuallyHidden = props.visuallyHidden;
34
+ const summaryClasses = classNames(styles["validation__summary"], {
35
+ [styles["validation__summary--visible"]]: hasErrors && !visuallyHidden,
36
+ [styles["validation__summary--sr-only"]]: visuallyHidden
37
+ });
34
38
  return /* @__PURE__ */ jsxs(
35
39
  "div",
36
40
  {
@@ -71,7 +75,7 @@ const Validation = React__default.forwardRef((props, ref) => {
71
75
  };
72
76
  return /* @__PURE__ */ jsxs(Fragment, { children: [
73
77
  /* @__PURE__ */ jsx("div", { "data-testid": props.testId, "data-analyticsid": AnalyticsId.Validation, className: props.className, ref, children: React__default.Children.map(props.children, (child) => renderChild(child)) }),
74
- /* @__PURE__ */ jsx(ValidationSummary, { errorTitle: props.errorTitle, errors: props.errors })
78
+ /* @__PURE__ */ jsx(ValidationSummary, { errorTitle: props.errorTitle, errors: props.errors, visuallyHidden: props.visuallyHiddenSummary })
75
79
  ] });
76
80
  });
77
81
  Validation.displayName = "Validation";
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../../../src/components/Validation/ErrorListItem.tsx","../../../src/components/Validation/ErrorList.tsx","../../../src/components/Validation/ValidationSummary.tsx","../../../src/components/Validation/Validation.tsx"],"sourcesContent":["import React from 'react';\n\nimport { ErrorDetails, FocusableElement } from './types';\nimport AnchorLink, { AnchorLinkOnClickEvent } from '../AnchorLink';\n\ninterface ErrorElementProps {\n name: string;\n error: ErrorDetails;\n}\n\nconst ErrorListItem: React.FC<ErrorElementProps> = props => {\n const handleClick = (event?: AnchorLinkOnClickEvent, element?: FocusableElement): void => {\n event?.preventDefault();\n if (element?.focus) element?.focus();\n };\n\n if (props.error.ref) {\n return (\n <AnchorLink href={`#${props.name}`} onClick={(e): void => handleClick(e, props.error.ref)}>\n {props.error.message}\n </AnchorLink>\n );\n }\n\n return <>{props.error.message}</>;\n};\n\nexport default ErrorListItem;\n","import React from 'react';\n\nimport ErrorListItem from './ErrorListItem';\nimport { ValidationErrors } from './types';\nimport List from '../List';\n\ninterface ErrorListProps {\n errors: ValidationErrors;\n}\n\nconst ErrorList: React.FC<ErrorListProps> = props => (\n <List>\n {Object.entries(props.errors).map(([name, error]) => (\n <List.Item key={name}>\n <ErrorListItem name={name} error={error} />\n </List.Item>\n ))}\n </List>\n);\n\nexport default ErrorList;\n","import React from 'react';\n\nimport classNames from 'classnames';\n\nimport ErrorList from './ErrorList';\nimport { ValidationErrors } from './types';\nimport { useUuid } from '../../hooks/useUuid';\nimport Title, { TitleTags } from '../Title';\n\nimport styles from './styles.module.scss';\n\ninterface ValidationSummaryProps {\n /** Error summary title */\n errorTitle?: string;\n /** Error list */\n errors?: ValidationErrors;\n /** Markup props for error summary title. Default: h2 */\n errorTitleHtmlMarkup?: TitleTags;\n /** Will be shown last */\n children?: React.ReactNode;\n}\n\nconst ValidationSummary: React.FC<ValidationSummaryProps> = props => {\n const { errorTitleHtmlMarkup = 'h2' } = props;\n const titleId = useUuid();\n\n const hasErrors = !!props.errors && Object.entries(props.errors).length > 0;\n\n const summaryClasses = classNames(styles['validation__summary'], hasErrors && styles['validation__summary--visible']);\n\n return (\n <div\n role={'status'}\n aria-atomic={'true'}\n aria-live={'polite'}\n aria-relevant={'all'}\n aria-labelledby={hasErrors && props.errorTitle ? titleId : undefined}\n className={summaryClasses}\n >\n {hasErrors && (\n <>\n {props.errorTitle && (\n <Title appearance=\"title4\" id={titleId} htmlMarkup={errorTitleHtmlMarkup} margin={{ marginTop: 0, marginBottom: 1 }}>\n {props.errorTitle}\n </Title>\n )}\n <ErrorList errors={props.errors!} />\n </>\n )}\n {props.children}\n </div>\n );\n};\n\nexport default ValidationSummary;\n","import React from 'react';\n\nimport { ValidationErrors } from './types';\nimport ValidationSummary from './ValidationSummary';\nimport { AnalyticsId, FormSize } from '../../constants';\nimport { isComponent, isComponentWithDisplayName } from '../../utils/component';\nimport Checkbox, { CheckboxProps } from '../Checkbox';\nimport { ErrorWrapperClassNameProps } from '../ErrorWrapper';\nimport FormGroup, { FormGroupProps } from '../FormGroup/FormGroup';\nimport Input, { InputProps } from '../Input';\nimport RadioButton, { RadioButtonProps } from '../RadioButton';\nimport Select, { SelectProps } from '../Select';\nimport Slider, { SliderProps } from '../Slider';\nimport Textarea, { TextareaProps } from '../Textarea';\n\nimport styles from './styles.module.scss';\n\ninterface ValidationProps {\n /** Error summary title */\n errorTitle?: string;\n /** Validation errors. If errors include references to HTML elements, the errors will be rendered as links with an onClick handler to focus the element. */\n errors?: ValidationErrors;\n /** Items in the Validation compontent */\n children?: React.ReactNode;\n /** Adds custom classes to the element. */\n className?: string;\n /** Changes the visuals of the formgroup */\n size?: keyof typeof FormSize;\n /** Sets the data-testid attribute. */\n testId?: string;\n}\n\nexport const Validation = React.forwardRef((props: ValidationProps, ref: React.ForwardedRef<HTMLDivElement>) => {\n const validationErrorClass = styles['validation__error-wrapper'];\n\n const cloneFormElement = <T extends ErrorWrapperClassNameProps>(child: React.ReactElement<T>): React.ReactElement<T> => {\n return React.cloneElement(child, {\n ...child.props,\n errorWrapperClassName: validationErrorClass,\n });\n };\n\n const renderChild = (child: React.ReactNode): React.ReactNode => {\n if (\n isComponent<FormGroupProps>(child, FormGroup) ||\n isComponent<CheckboxProps>(child, Checkbox) ||\n isComponent<RadioButtonProps>(child, RadioButton) ||\n isComponent<TextareaProps>(child, Textarea) ||\n isComponent<InputProps>(child, Input) ||\n isComponent<SelectProps>(child, Select) ||\n isComponent<SliderProps>(child, Slider) ||\n isComponentWithDisplayName<ErrorWrapperClassNameProps>(child, 'DateTimePickerWrapper') ||\n isComponentWithDisplayName<ErrorWrapperClassNameProps>(child, 'DatePicker') ||\n isComponentWithDisplayName<ErrorWrapperClassNameProps>(child, 'DateTime')\n ) {\n return cloneFormElement<ErrorWrapperClassNameProps>(child);\n }\n if (React.isValidElement(child) && child.type === React.Fragment) {\n return React.Children.map(child.props.children, (child: React.ReactNode) => {\n return renderChild(child);\n });\n }\n\n return child;\n };\n\n return (\n <>\n <div data-testid={props.testId} data-analyticsid={AnalyticsId.Validation} className={props.className} ref={ref}>\n {React.Children.map(props.children, (child: React.ReactNode) => renderChild(child))}\n </div>\n <ValidationSummary errorTitle={props.errorTitle} errors={props.errors} />\n </>\n );\n});\n\nValidation.displayName = 'Validation';\n\nexport default Validation;\n"],"names":["React","child"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sources":["../../../src/components/Validation/ErrorListItem.tsx","../../../src/components/Validation/ErrorList.tsx","../../../src/components/Validation/ValidationSummary.tsx","../../../src/components/Validation/Validation.tsx"],"sourcesContent":["import React from 'react';\n\nimport { ErrorDetails, FocusableElement } from './types';\nimport AnchorLink, { AnchorLinkOnClickEvent } from '../AnchorLink';\n\ninterface ErrorElementProps {\n name: string;\n error: ErrorDetails;\n}\n\nconst ErrorListItem: React.FC<ErrorElementProps> = props => {\n const handleClick = (event?: AnchorLinkOnClickEvent, element?: FocusableElement): void => {\n event?.preventDefault();\n if (element?.focus) element?.focus();\n };\n\n if (props.error.ref) {\n return (\n <AnchorLink href={`#${props.name}`} onClick={(e): void => handleClick(e, props.error.ref)}>\n {props.error.message}\n </AnchorLink>\n );\n }\n\n return <>{props.error.message}</>;\n};\n\nexport default ErrorListItem;\n","import React from 'react';\n\nimport ErrorListItem from './ErrorListItem';\nimport { ValidationErrors } from './types';\nimport List from '../List';\n\ninterface ErrorListProps {\n errors: ValidationErrors;\n}\n\nconst ErrorList: React.FC<ErrorListProps> = props => (\n <List>\n {Object.entries(props.errors).map(([name, error]) => (\n <List.Item key={name}>\n <ErrorListItem name={name} error={error} />\n </List.Item>\n ))}\n </List>\n);\n\nexport default ErrorList;\n","import React from 'react';\n\nimport classNames from 'classnames';\n\nimport ErrorList from './ErrorList';\nimport { ValidationErrors } from './types';\nimport { useUuid } from '../../hooks/useUuid';\nimport Title, { TitleTags } from '../Title';\n\nimport styles from './styles.module.scss';\n\ninterface ValidationSummaryProps {\n /** Error summary title */\n errorTitle?: string;\n /** Error list */\n errors?: ValidationErrors;\n /** Markup props for error summary title. Default: h2 */\n errorTitleHtmlMarkup?: TitleTags;\n /** Will be shown last */\n children?: React.ReactNode;\n /** Hides the list visually - summary is still announced by screen readers */\n visuallyHidden?: boolean;\n}\n\nconst ValidationSummary: React.FC<ValidationSummaryProps> = props => {\n const { errorTitleHtmlMarkup = 'h2' } = props;\n const titleId = useUuid();\n\n const hasErrors = !!props.errors && Object.entries(props.errors).length > 0;\n const visuallyHidden = props.visuallyHidden;\n\n const summaryClasses = classNames(styles['validation__summary'], {\n [styles['validation__summary--visible']]: hasErrors && !visuallyHidden,\n [styles['validation__summary--sr-only']]: visuallyHidden,\n });\n\n return (\n <div\n role={'status'}\n aria-atomic={'true'}\n aria-live={'polite'}\n aria-relevant={'all'}\n aria-labelledby={hasErrors && props.errorTitle ? titleId : undefined}\n className={summaryClasses}\n >\n {hasErrors && (\n <>\n {props.errorTitle && (\n <Title appearance=\"title4\" id={titleId} htmlMarkup={errorTitleHtmlMarkup} margin={{ marginTop: 0, marginBottom: 1 }}>\n {props.errorTitle}\n </Title>\n )}\n <ErrorList errors={props.errors!} />\n </>\n )}\n {props.children}\n </div>\n );\n};\n\nexport default ValidationSummary;\n","import React from 'react';\n\nimport { ValidationErrors } from './types';\nimport ValidationSummary from './ValidationSummary';\nimport { AnalyticsId, FormSize } from '../../constants';\nimport { isComponent, isComponentWithDisplayName } from '../../utils/component';\nimport Checkbox, { CheckboxProps } from '../Checkbox';\nimport { ErrorWrapperClassNameProps } from '../ErrorWrapper';\nimport FormGroup, { FormGroupProps } from '../FormGroup/FormGroup';\nimport Input, { InputProps } from '../Input';\nimport RadioButton, { RadioButtonProps } from '../RadioButton';\nimport Select, { SelectProps } from '../Select';\nimport Slider, { SliderProps } from '../Slider';\nimport Textarea, { TextareaProps } from '../Textarea';\n\nimport styles from './styles.module.scss';\n\ninterface ValidationProps {\n /** Error summary title */\n errorTitle?: string;\n /** Validation errors. If errors include references to HTML elements, the errors will be rendered as links with an onClick handler to focus the element. */\n errors?: ValidationErrors;\n /** Items in the Validation compontent */\n children?: React.ReactNode;\n /** Adds custom classes to the element. */\n className?: string;\n /** Changes the visuals of the formgroup */\n size?: keyof typeof FormSize;\n /** Sets the data-testid attribute. */\n testId?: string;\n /** Hides the ValidationSummary list visually - summary is still announced by screen readers */\n visuallyHiddenSummary?: boolean;\n}\n\nexport const Validation = React.forwardRef((props: ValidationProps, ref: React.ForwardedRef<HTMLDivElement>) => {\n const validationErrorClass = styles['validation__error-wrapper'];\n\n const cloneFormElement = <T extends ErrorWrapperClassNameProps>(child: React.ReactElement<T>): React.ReactElement<T> => {\n return React.cloneElement(child, {\n ...child.props,\n errorWrapperClassName: validationErrorClass,\n });\n };\n\n const renderChild = (child: React.ReactNode): React.ReactNode => {\n if (\n isComponent<FormGroupProps>(child, FormGroup) ||\n isComponent<CheckboxProps>(child, Checkbox) ||\n isComponent<RadioButtonProps>(child, RadioButton) ||\n isComponent<TextareaProps>(child, Textarea) ||\n isComponent<InputProps>(child, Input) ||\n isComponent<SelectProps>(child, Select) ||\n isComponent<SliderProps>(child, Slider) ||\n isComponentWithDisplayName<ErrorWrapperClassNameProps>(child, 'DateTimePickerWrapper') ||\n isComponentWithDisplayName<ErrorWrapperClassNameProps>(child, 'DatePicker') ||\n isComponentWithDisplayName<ErrorWrapperClassNameProps>(child, 'DateTime')\n ) {\n return cloneFormElement<ErrorWrapperClassNameProps>(child);\n }\n if (React.isValidElement(child) && child.type === React.Fragment) {\n return React.Children.map(child.props.children, (child: React.ReactNode) => {\n return renderChild(child);\n });\n }\n\n return child;\n };\n\n return (\n <>\n <div data-testid={props.testId} data-analyticsid={AnalyticsId.Validation} className={props.className} ref={ref}>\n {React.Children.map(props.children, (child: React.ReactNode) => renderChild(child))}\n </div>\n <ValidationSummary errorTitle={props.errorTitle} errors={props.errors} visuallyHidden={props.visuallyHiddenSummary} />\n </>\n );\n});\n\nValidation.displayName = 'Validation';\n\nexport default Validation;\n"],"names":["React","child"],"mappings":"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"}
@@ -3,6 +3,7 @@
3
3
  @use '../../scss/palette' as palette;
4
4
  @use '../../scss/font-settings' as font-settings;
5
5
  @use '../../scss/breakpoints' as breakpoints;
6
+ @use '../../scss/screen-reader' as *;
6
7
 
7
8
  .validation {
8
9
  &__error-wrapper {
@@ -19,6 +20,10 @@
19
20
  &--visible {
20
21
  margin: spacers.getSpacer(l) 0 spacers.getSpacer(s);
21
22
  }
23
+
24
+ &--sr-only {
25
+ @include sr-only;
26
+ }
22
27
  }
23
28
 
24
29
  &__errors {
@@ -3,6 +3,7 @@ export type Styles = {
3
3
  validation__errors: string;
4
4
  'validation__errors--visible': string;
5
5
  validation__summary: string;
6
+ 'validation__summary--sr-only': string;
6
7
  'validation__summary--visible': string;
7
8
  };
8
9
 
@@ -2,6 +2,7 @@ import { FormSize } from '../../constants';
2
2
  export interface FormExampleProps {
3
3
  exampleType: FormExampleVariants;
4
4
  size?: keyof typeof FormSize;
5
+ visuallyHiddenSummary?: boolean;
5
6
  }
6
7
  export declare enum FormExampleVariants {
7
8
  formgroup = "formgroup",
@@ -1,4 +1,5 @@
1
1
  import { useRef, useEffect } from "react";
2
+ import { deepContains } from "../utils/deepContains.js";
2
3
  import { getDocumentActiveElement } from "../utils/focus.js";
3
4
  const useReturnFocusOnUnmount = (ref) => {
4
5
  const previouslyFocusedElementRef = useRef(null);
@@ -8,7 +9,7 @@ const useReturnFocusOnUnmount = (ref) => {
8
9
  previouslyFocusedElementRef.current = activeElement;
9
10
  }
10
11
  return () => {
11
- if (previouslyFocusedElementRef.current instanceof HTMLElement && document.contains(previouslyFocusedElementRef.current)) {
12
+ if (previouslyFocusedElementRef.current instanceof HTMLElement && deepContains(document, previouslyFocusedElementRef.current)) {
12
13
  previouslyFocusedElementRef.current.focus();
13
14
  }
14
15
  };
@@ -1 +1 @@
1
- {"version":3,"file":"useReturnFocusOnUnmount.js","sources":["../../src/hooks/useReturnFocusOnUnmount.ts"],"sourcesContent":["import { useEffect, useRef } from 'react';\n\nimport { getDocumentActiveElement } from '../utils/focus';\n\n/**\n * A hook that stores the currently focused element when the component mounts,\n * and returns focus to it when the component unmounts.\n */\nexport const useReturnFocusOnUnmount = (ref: React.RefObject<HTMLElement>): void => {\n const previouslyFocusedElementRef = useRef<HTMLElement | null>(null);\n\n useEffect(() => {\n // Captures which element was focused on mount\n // const activeElement = document.activeElement;\n const activeElement = ref.current ? getDocumentActiveElement(ref.current) : null;\n if (activeElement instanceof HTMLElement) {\n previouslyFocusedElementRef.current = activeElement;\n }\n\n // Restores focus to the stored element on unmount\n return (): void => {\n if (previouslyFocusedElementRef.current instanceof HTMLElement && document.contains(previouslyFocusedElementRef.current)) {\n previouslyFocusedElementRef.current.focus();\n }\n };\n }, []);\n};\n"],"names":[],"mappings":";;AAQO,MAAM,0BAA0B,CAAC,QAA4C;AAClF,QAAM,8BAA8B,OAA2B,IAAI;AAEnE,YAAU,MAAM;AAGd,UAAM,gBAAgB,IAAI,UAAU,yBAAyB,IAAI,OAAO,IAAI;AAC5E,QAAI,yBAAyB,aAAa;AACxC,kCAA4B,UAAU;AAAA,IACxC;AAGA,WAAO,MAAY;AACjB,UAAI,4BAA4B,mBAAmB,eAAe,SAAS,SAAS,4BAA4B,OAAO,GAAG;AACxH,oCAA4B,QAAQ,MAAA;AAAA,MACtC;AAAA,IACF;AAAA,EACF,GAAG,CAAA,CAAE;AACP;"}
1
+ {"version":3,"file":"useReturnFocusOnUnmount.js","sources":["../../src/hooks/useReturnFocusOnUnmount.ts"],"sourcesContent":["import { useEffect, useRef } from 'react';\n\nimport { deepContains } from '../utils/deepContains';\nimport { getDocumentActiveElement } from '../utils/focus';\n\n/**\n * A hook that stores the currently focused element when the component mounts,\n * and returns focus to it when the component unmounts.\n */\nexport const useReturnFocusOnUnmount = (ref: React.RefObject<HTMLElement>): void => {\n const previouslyFocusedElementRef = useRef<HTMLElement | null>(null);\n\n useEffect(() => {\n // Captures which element was focused on mount\n // const activeElement = document.activeElement;\n const activeElement = ref.current ? getDocumentActiveElement(ref.current) : null;\n if (activeElement instanceof HTMLElement) {\n previouslyFocusedElementRef.current = activeElement;\n }\n\n // Restores focus to the stored element on unmount\n return (): void => {\n if (previouslyFocusedElementRef.current instanceof HTMLElement && deepContains(document, previouslyFocusedElementRef.current)) {\n previouslyFocusedElementRef.current.focus();\n }\n };\n }, []);\n};\n"],"names":[],"mappings":";;;AASO,MAAM,0BAA0B,CAAC,QAA4C;AAClF,QAAM,8BAA8B,OAA2B,IAAI;AAEnE,YAAU,MAAM;AAGd,UAAM,gBAAgB,IAAI,UAAU,yBAAyB,IAAI,OAAO,IAAI;AAC5E,QAAI,yBAAyB,aAAa;AACxC,kCAA4B,UAAU;AAAA,IACxC;AAGA,WAAO,MAAY;AACjB,UAAI,4BAA4B,mBAAmB,eAAe,aAAa,UAAU,4BAA4B,OAAO,GAAG;AAC7H,oCAA4B,QAAQ,MAAA;AAAA,MACtC;AAAA,IACF;AAAA,EACF,GAAG,CAAA,CAAE;AACP;"}
package/package.json CHANGED
@@ -7,7 +7,7 @@
7
7
  "url": "git+https://github.com/helsenorge/designsystem.git"
8
8
  },
9
9
  "homepage": "https://helsenorge.design",
10
- "version": "12.0.2",
10
+ "version": "12.2.0",
11
11
  "author": "Helsenorge",
12
12
  "license": "MIT",
13
13
  "dependencies": {
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Determines whether a given container node contains a specific element, traversing through shadow DOM boundaries if necessary.
3
+ * This function extends the behavior of `Node.contains` to support scenarios involving shadow DOM.
4
+ *
5
+ * @param container - The container node to check, typically the document or a specific DOM element.
6
+ * @param element - The target node to verify if it is contained within the container.
7
+ * @returns `true` if the container node contains the target node, including through shadow DOM boundaries; otherwise, `false`.
8
+ */
9
+ export declare function deepContains(container: Node, element: Node | null): boolean;
@@ -0,0 +1,19 @@
1
+ function deepContains(container, element) {
2
+ let node = element;
3
+ while (node) {
4
+ if (node === container) {
5
+ return true;
6
+ }
7
+ const rootNode = node.getRootNode();
8
+ if (rootNode instanceof ShadowRoot) {
9
+ node = rootNode.host;
10
+ } else {
11
+ node = node.parentNode;
12
+ }
13
+ }
14
+ return false;
15
+ }
16
+ export {
17
+ deepContains
18
+ };
19
+ //# sourceMappingURL=deepContains.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deepContains.js","sources":["../../src/utils/deepContains.ts"],"sourcesContent":["/**\n * Determines whether a given container node contains a specific element, traversing through shadow DOM boundaries if necessary.\n * This function extends the behavior of `Node.contains` to support scenarios involving shadow DOM.\n *\n * @param container - The container node to check, typically the document or a specific DOM element.\n * @param element - The target node to verify if it is contained within the container.\n * @returns `true` if the container node contains the target node, including through shadow DOM boundaries; otherwise, `false`.\n */\nexport function deepContains(container: Node, element: Node | null): boolean {\n let node = element;\n while (node) {\n if (node === container) {\n return true;\n }\n // Step out of shadow roots if necessary\n const rootNode = node.getRootNode();\n if (rootNode instanceof ShadowRoot) {\n node = rootNode.host;\n } else {\n node = node.parentNode;\n }\n }\n return false;\n}\n"],"names":[],"mappings":"AAQO,SAAS,aAAa,WAAiB,SAA+B;AAC3E,MAAI,OAAO;AACX,SAAO,MAAM;AACX,QAAI,SAAS,WAAW;AACtB,aAAO;AAAA,IACT;AAEA,UAAM,WAAW,KAAK,YAAA;AACtB,QAAI,oBAAoB,YAAY;AAClC,aAAO,SAAS;AAAA,IAClB,OAAO;AACL,aAAO,KAAK;AAAA,IACd;AAAA,EACF;AACA,SAAO;AACT;"}
@@ -0,0 +1 @@
1
+ export {};