@helsenorge/designsystem-react 12.1.0 → 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 +17 -0
- package/components/ExpanderHierarchy/Expander.d.ts +2 -0
- package/components/ExpanderHierarchy/index.js +3 -2
- package/components/ExpanderHierarchy/index.js.map +1 -1
- package/components/Validation/Validation.d.ts +2 -0
- package/components/Validation/ValidationSummary.d.ts +2 -0
- package/components/Validation/index.js +6 -2
- package/components/Validation/index.js.map +1 -1
- package/components/Validation/styles.module.scss +5 -0
- package/components/Validation/styles.module.scss.d.ts +1 -0
- package/docs/FormExample/FormExample.d.ts +1 -0
- package/hooks/useReturnFocusOnUnmount.js +2 -1
- package/hooks/useReturnFocusOnUnmount.js.map +1 -1
- package/package.json +1 -1
- package/utils/deepContains.d.ts +9 -0
- package/utils/deepContains.js +19 -0
- package/utils/deepContains.js.map +1 -0
- package/utils/tests/deepContains.test.d.ts +1 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,20 @@
|
|
|
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
|
+
|
|
1
18
|
## [12.1.0](https://github.com/helsenorge/designsystem/branchCompare?baseVersion=GTv12.0.2&targetVersion=GTv12.1.0) (2025-09-10)
|
|
2
19
|
|
|
3
20
|
### Features
|
|
@@ -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":";;;;;;;;;;;;AAqBA,MAAM,iBAAgD,CAAC,EAAE,aAAa,MAAM,QAAQ,GAAG,OAAO,UAAU,UAAU,UAAU,OAAA,MAAa;AACvI,QAAM,EAAE,UAAU,UAAA,IAAc,SAAA;AAChC,QAAM,CAAC,YAAY,aAAa,IAAI,UAAU,UAAU,QAAQ;AAEhE,QAAM,cAAc,MAAY;AAC9B,kBAAc,CAAC,UAAU;AAAA,EAC3B;AAEA,QAAM,YAAY;AAElB,QAAM,gBAAgB;AAAA,IACpB,OAAO;AAAA,IACP,QAAQ,KAAK,OAAO,+BAA+B;AAAA,IACnD,SAAS,OAAO,yBAAyB;AAAA,EAAA;AAG3C,QAAM,eAAe;AAAA,IACnB,OAAO;AAAA,KACN,UAAU,KAAK,UAAU,KAAK,UAAU,MAAM,OAAO,oBAAoB,KAAK,EAAE;AAAA,IACjF,QAAQ,KAAK,OAAO,8BAA8B;AAAA,IAClD,cAAc,OAAO,2BAA2B;AAAA,IAChD,SAAS,OAAO,wBAAwB;AAAA,EAAA;AAG1C,QAAM,cAAc;AAAA,IAClB,OAAO;AAAA,IACP,QAAQ,KAAK,OAAO,6BAA6B;AAAA,IACjD,QAAQ,KAAK,OAAO,6BAA6B;AAAA,EAAA;AAGnD,SACE,oBAAC,WAAA,EAAU,WAAW,cACpB,UAAA;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,MAAK;AAAA,MACL,SAAS;AAAA,MACT,WAAW;AAAA,MACX,iBAAe;AAAA,MACf,KAAK;AAAA,MACL,eAAa;AAAA,MACb,oBAAkB,YAAY;AAAA,MAE7B,UAAA;AAAA,QAAA;AAAA,QACD,oBAAC,MAAA,EAAK,SAAS,aAAa,YAAY,aAAa,WAAsB,WAAW,aAAa,MAAM,SAAS,OAAA,CAAQ;AAAA,MAAA;AAAA,IAAA;AAAA,EAAA,GAE9H;AAEJ;ACjEO,MAAM,gBAAgB,CAAC,eAA0C;AACtE,UAAQ,YAAA;AAAA,IACN,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT;AACE,aAAO;AAAA,EAAA;AAEb;
|
|
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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"}
|
|
@@ -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
|
|
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--
|
|
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 {
|
|
@@ -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
|
|
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
|
|
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
|
@@ -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 {};
|