@simplybusiness/mobius 4.14.1 → 4.15.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 +6 -0
- package/dist/cjs/components/Accordion/Accordion.js +2 -1
- package/dist/cjs/components/Accordion/Accordion.js.map +1 -1
- package/dist/cjs/hooks/index.js +1 -0
- package/dist/cjs/hooks/index.js.map +1 -1
- package/dist/cjs/hooks/usePrefersReducedMotion/index.js +20 -0
- package/dist/cjs/hooks/usePrefersReducedMotion/index.js.map +1 -0
- package/dist/cjs/hooks/usePrefersReducedMotion/usePrefersReducedMotion.js +28 -0
- package/dist/cjs/hooks/usePrefersReducedMotion/usePrefersReducedMotion.js.map +1 -0
- package/dist/esm/components/Accordion/Accordion.js +2 -1
- package/dist/esm/components/Accordion/Accordion.js.map +1 -1
- package/dist/esm/hooks/index.js +1 -0
- package/dist/esm/hooks/index.js.map +1 -1
- package/dist/esm/hooks/usePrefersReducedMotion/index.js +3 -0
- package/dist/esm/hooks/usePrefersReducedMotion/index.js.map +1 -0
- package/dist/esm/hooks/usePrefersReducedMotion/usePrefersReducedMotion.js +18 -0
- package/dist/esm/hooks/usePrefersReducedMotion/usePrefersReducedMotion.js.map +1 -0
- package/dist/types/hooks/index.d.ts +1 -0
- package/dist/types/hooks/usePrefersReducedMotion/index.d.ts +1 -0
- package/dist/types/hooks/usePrefersReducedMotion/usePrefersReducedMotion.d.ts +1 -0
- package/dist/types/hooks/usePrefersReducedMotion/usePrefersReducedMotion.test.d.ts +1 -0
- package/package.json +1 -1
- package/src/components/Accordion/Accordion.tsx +2 -3
- package/src/hooks/index.tsx +1 -0
- package/src/hooks/usePrefersReducedMotion/index.tsx +1 -0
- package/src/hooks/usePrefersReducedMotion/usePrefersReducedMotion.test.tsx +48 -0
- package/src/hooks/usePrefersReducedMotion/usePrefersReducedMotion.tsx +22 -0
- package/src/public-whitelist.test.ts +0 -2
package/CHANGELOG.md
CHANGED
|
@@ -15,6 +15,7 @@ const _dedupe = /*#__PURE__*/ _interop_require_default(require("classnames/dedup
|
|
|
15
15
|
const _react = require("react");
|
|
16
16
|
const _Flex = require("../Flex");
|
|
17
17
|
const _Icon = require("../Icon");
|
|
18
|
+
const _usePrefersReducedMotion = require("../../hooks/usePrefersReducedMotion");
|
|
18
19
|
function _interop_require_default(obj) {
|
|
19
20
|
return obj && obj.__esModule ? obj : {
|
|
20
21
|
default: obj
|
|
@@ -91,7 +92,7 @@ const Accordion = /*#__PURE__*/ (0, _react.forwardRef)((props, ref)=>{
|
|
|
91
92
|
open: true,
|
|
92
93
|
withAnimation: false
|
|
93
94
|
});
|
|
94
|
-
const prefersReducedMotion =
|
|
95
|
+
const prefersReducedMotion = (0, _usePrefersReducedMotion.usePrefersReducedMotion)();
|
|
95
96
|
const containerClasses = (0, _dedupe.default)("mobius", "mobius/Accordion", props.className, {
|
|
96
97
|
"--should-animate": accordionState.withAnimation && !prefersReducedMotion,
|
|
97
98
|
"--is-open": accordionState.open
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/components/Accordion/Accordion.tsx"],"sourcesContent":["\"use client\";\n\nimport { chevronDown } from \"@simplybusiness/icons\";\nimport classNames from \"classnames/dedupe\";\nimport {\n KeyboardEvent,\n ReactNode,\n Ref,\n RefAttributes,\n forwardRef,\n useEffect,\n useState,\n} from \"react\";\nimport { ForwardedRefComponent } from \"../../types/components\";\nimport { DOMProps } from \"../../types/dom\";\nimport { Flex } from \"../Flex\";\nimport { Icon } from \"../Icon\";\n\nexport type AccordionElementType = HTMLDivElement;\nexport type AccordionRef = Ref<AccordionElementType>;\nexport interface AccordionLinkProps extends DOMProps {\n /** Link text to show accordion content */\n text?: string;\n onClick?: () => void;\n toggle: () => void;\n headerChildren?: ReactNode;\n ariaExpanded: boolean | undefined;\n}\n\nconst AccordionLink = ({\n text,\n toggle,\n onClick,\n headerChildren,\n ariaExpanded,\n}: AccordionLinkProps) => {\n const linkClasses = classNames(\"mobius/AccordionLink\", {\n \"--is-open\": ariaExpanded,\n });\n const iconClasses = classNames(\"mobius/AccordionLinkIcon\", {\n \"--is-open\": ariaExpanded,\n });\n\n const handleOnClick = (): void => {\n if (onClick) {\n onClick();\n }\n toggle();\n };\n\n const handleKeyDown = (e: KeyboardEvent) => {\n if (e.key === \" \" || e.key === \"Enter\") {\n toggle();\n }\n };\n\n if (headerChildren) {\n return (\n <Flex\n justifyContent=\"space-between\"\n alignItems=\"center\"\n className=\"mobius/AccordionHeader\"\n >\n <div\n className={linkClasses}\n onClick={handleOnClick}\n onKeyDown={handleKeyDown}\n role=\"button\"\n tabIndex={0}\n aria-expanded={!!ariaExpanded}\n >\n <span className=\"mobius/AccordionLinkText\">{text}</span>\n <Icon icon={chevronDown} className={iconClasses} />\n </div>\n {headerChildren}\n </Flex>\n );\n }\n\n return (\n <div\n className={linkClasses}\n onClick={handleOnClick}\n onKeyDown={handleKeyDown}\n role=\"button\"\n tabIndex={0}\n aria-expanded={!!ariaExpanded}\n >\n <span className=\"mobius/AccordionLinkText\">{text}</span>\n <Icon icon={chevronDown} className={iconClasses} />\n </div>\n );\n};\n\nexport interface AccordionProps\n extends DOMProps,\n RefAttributes<AccordionElementType> {\n /** Custom class name for setting specific CSS */\n className?: string;\n /** Link text to show accordion content */\n showText?: string;\n /** Link text to hide accordion content */\n hideText?: string;\n /** Whether header is above or below content */\n headerPosition?: \"top\" | \"bottom\";\n /** Whether to expand the accordion initially */\n startOpen?: boolean | undefined;\n /** Callback that fires each time the accordion is opened */\n onOpen?: () => void;\n /** Callback that fires each time the accordion is closed */\n onClose?: () => void;\n /** Callback that fires each time the accordion state changes */\n onChange?: (state: boolean) => void;\n children?: ReactNode;\n headerChildren?: ReactNode;\n}\n\nexport const Accordion: ForwardedRefComponent<\n AccordionProps,\n AccordionElementType\n> = forwardRef((props: AccordionProps, ref: AccordionRef) => {\n const {\n showText = \"See more\",\n hideText = \"See less\",\n headerPosition = \"top\",\n startOpen = false,\n onOpen,\n onClose,\n onChange = () => {},\n headerChildren,\n ...rest\n } = props;\n const [accordionState, setAccordionState] = useState({\n open: true,\n withAnimation: false,\n });\n const prefersReducedMotion
|
|
1
|
+
{"version":3,"sources":["../../../../src/components/Accordion/Accordion.tsx"],"sourcesContent":["\"use client\";\n\nimport { chevronDown } from \"@simplybusiness/icons\";\nimport classNames from \"classnames/dedupe\";\nimport {\n KeyboardEvent,\n ReactNode,\n Ref,\n RefAttributes,\n forwardRef,\n useEffect,\n useState,\n} from \"react\";\nimport { ForwardedRefComponent } from \"../../types/components\";\nimport { DOMProps } from \"../../types/dom\";\nimport { Flex } from \"../Flex\";\nimport { Icon } from \"../Icon\";\nimport { usePrefersReducedMotion } from \"../../hooks/usePrefersReducedMotion\";\n\nexport type AccordionElementType = HTMLDivElement;\nexport type AccordionRef = Ref<AccordionElementType>;\nexport interface AccordionLinkProps extends DOMProps {\n /** Link text to show accordion content */\n text?: string;\n onClick?: () => void;\n toggle: () => void;\n headerChildren?: ReactNode;\n ariaExpanded: boolean | undefined;\n}\n\nconst AccordionLink = ({\n text,\n toggle,\n onClick,\n headerChildren,\n ariaExpanded,\n}: AccordionLinkProps) => {\n const linkClasses = classNames(\"mobius/AccordionLink\", {\n \"--is-open\": ariaExpanded,\n });\n const iconClasses = classNames(\"mobius/AccordionLinkIcon\", {\n \"--is-open\": ariaExpanded,\n });\n\n const handleOnClick = (): void => {\n if (onClick) {\n onClick();\n }\n toggle();\n };\n\n const handleKeyDown = (e: KeyboardEvent) => {\n if (e.key === \" \" || e.key === \"Enter\") {\n toggle();\n }\n };\n\n if (headerChildren) {\n return (\n <Flex\n justifyContent=\"space-between\"\n alignItems=\"center\"\n className=\"mobius/AccordionHeader\"\n >\n <div\n className={linkClasses}\n onClick={handleOnClick}\n onKeyDown={handleKeyDown}\n role=\"button\"\n tabIndex={0}\n aria-expanded={!!ariaExpanded}\n >\n <span className=\"mobius/AccordionLinkText\">{text}</span>\n <Icon icon={chevronDown} className={iconClasses} />\n </div>\n {headerChildren}\n </Flex>\n );\n }\n\n return (\n <div\n className={linkClasses}\n onClick={handleOnClick}\n onKeyDown={handleKeyDown}\n role=\"button\"\n tabIndex={0}\n aria-expanded={!!ariaExpanded}\n >\n <span className=\"mobius/AccordionLinkText\">{text}</span>\n <Icon icon={chevronDown} className={iconClasses} />\n </div>\n );\n};\n\nexport interface AccordionProps\n extends DOMProps,\n RefAttributes<AccordionElementType> {\n /** Custom class name for setting specific CSS */\n className?: string;\n /** Link text to show accordion content */\n showText?: string;\n /** Link text to hide accordion content */\n hideText?: string;\n /** Whether header is above or below content */\n headerPosition?: \"top\" | \"bottom\";\n /** Whether to expand the accordion initially */\n startOpen?: boolean | undefined;\n /** Callback that fires each time the accordion is opened */\n onOpen?: () => void;\n /** Callback that fires each time the accordion is closed */\n onClose?: () => void;\n /** Callback that fires each time the accordion state changes */\n onChange?: (state: boolean) => void;\n children?: ReactNode;\n headerChildren?: ReactNode;\n}\n\nexport const Accordion: ForwardedRefComponent<\n AccordionProps,\n AccordionElementType\n> = forwardRef((props: AccordionProps, ref: AccordionRef) => {\n const {\n showText = \"See more\",\n hideText = \"See less\",\n headerPosition = \"top\",\n startOpen = false,\n onOpen,\n onClose,\n onChange = () => {},\n headerChildren,\n ...rest\n } = props;\n const [accordionState, setAccordionState] = useState({\n open: true,\n withAnimation: false,\n });\n const prefersReducedMotion = usePrefersReducedMotion();\n\n const containerClasses = classNames(\n \"mobius\",\n \"mobius/Accordion\",\n props.className,\n {\n \"--should-animate\": accordionState.withAnimation && !prefersReducedMotion,\n \"--is-open\": accordionState.open,\n },\n );\n const contentContainerClasses = classNames(\n \"mobius/AccordionContentContainer\",\n {\n \"--is-open\": accordionState.open,\n },\n );\n const contentClasses = classNames(\"mobius/AccordionContent\", {\n \"--is-open\": accordionState.open,\n });\n const linkText = accordionState.open ? hideText : showText;\n\n const handleChange = () => {\n // Fire events\n if (!accordionState.open && onOpen) {\n onOpen();\n }\n if (accordionState.open && onClose) {\n onClose();\n }\n if (onChange) {\n onChange(!accordionState.open);\n }\n setAccordionState({\n open: !accordionState.open,\n withAnimation: true,\n });\n };\n\n useEffect(() => {\n setAccordionState({\n open: startOpen,\n withAnimation: false,\n });\n }, [startOpen]);\n\n return (\n <div ref={ref} {...rest} className={containerClasses}>\n {headerPosition === \"top\" && (\n <AccordionLink\n text={linkText}\n toggle={handleChange}\n ariaExpanded={accordionState.open}\n headerChildren={headerChildren}\n />\n )}\n <div\n className={contentContainerClasses}\n aria-hidden={!accordionState.open}\n >\n <div className={contentClasses}>{props.children}</div>\n </div>\n {headerPosition === \"bottom\" && (\n <AccordionLink\n text={linkText}\n toggle={handleChange}\n ariaExpanded={accordionState.open}\n headerChildren={headerChildren}\n />\n )}\n </div>\n );\n});\n"],"names":["Accordion","AccordionLink","text","toggle","onClick","headerChildren","ariaExpanded","linkClasses","classNames","iconClasses","handleOnClick","handleKeyDown","e","key","Flex","justifyContent","alignItems","className","div","onKeyDown","role","tabIndex","aria-expanded","span","Icon","icon","chevronDown","forwardRef","props","ref","showText","hideText","headerPosition","startOpen","onOpen","onClose","onChange","rest","accordionState","setAccordionState","useState","open","withAnimation","prefersReducedMotion","usePrefersReducedMotion","containerClasses","contentContainerClasses","contentClasses","linkText","handleChange","useEffect","aria-hidden","children"],"mappings":"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"}
|
package/dist/cjs/hooks/index.js
CHANGED
|
@@ -10,6 +10,7 @@ _export_star(require("./useDialog"), exports);
|
|
|
10
10
|
_export_star(require("./useDialogPolyfill"), exports);
|
|
11
11
|
_export_star(require("./useLabel"), exports);
|
|
12
12
|
_export_star(require("./useOnClickOutside"), exports);
|
|
13
|
+
_export_star(require("./usePrefersReducedMotion"), exports);
|
|
13
14
|
_export_star(require("./useTextField"), exports);
|
|
14
15
|
_export_star(require("./useValidationClasses"), exports);
|
|
15
16
|
_export_star(require("./useWindowEvent"), exports);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/hooks/index.tsx"],"sourcesContent":["export * from \"./useBodyScrollLock\";\nexport * from \"./useBreakpoint\";\nexport * from \"./useButton\";\nexport * from \"./useDeprecationWarning\";\nexport * from \"./useDialog\";\nexport * from \"./useDialogPolyfill\";\nexport * from \"./useLabel\";\nexport * from \"./useOnClickOutside\";\nexport * from \"./useTextField\";\nexport * from \"./useValidationClasses\";\nexport * from \"./useWindowEvent\";\n"],"names":[],"mappings":";;;;qBAAc;qBACA;qBACA;qBACA;qBACA;qBACA;qBACA;qBACA;qBACA;qBACA;qBACA"}
|
|
1
|
+
{"version":3,"sources":["../../../src/hooks/index.tsx"],"sourcesContent":["export * from \"./useBodyScrollLock\";\nexport * from \"./useBreakpoint\";\nexport * from \"./useButton\";\nexport * from \"./useDeprecationWarning\";\nexport * from \"./useDialog\";\nexport * from \"./useDialogPolyfill\";\nexport * from \"./useLabel\";\nexport * from \"./useOnClickOutside\";\nexport * from \"./usePrefersReducedMotion\";\nexport * from \"./useTextField\";\nexport * from \"./useValidationClasses\";\nexport * from \"./useWindowEvent\";\n"],"names":[],"mappings":";;;;qBAAc;qBACA;qBACA;qBACA;qBACA;qBACA;qBACA;qBACA;qBACA;qBACA;qBACA;qBACA"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
_export_star(require("./usePrefersReducedMotion"), exports);
|
|
6
|
+
function _export_star(from, to) {
|
|
7
|
+
Object.keys(from).forEach(function(k) {
|
|
8
|
+
if (k !== "default" && !Object.prototype.hasOwnProperty.call(to, k)) {
|
|
9
|
+
Object.defineProperty(to, k, {
|
|
10
|
+
enumerable: true,
|
|
11
|
+
get: function() {
|
|
12
|
+
return from[k];
|
|
13
|
+
}
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
return from;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../src/hooks/usePrefersReducedMotion/index.tsx"],"sourcesContent":["export * from \"./usePrefersReducedMotion\";\n"],"names":[],"mappings":";;;;qBAAc"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
Object.defineProperty(exports, "usePrefersReducedMotion", {
|
|
6
|
+
enumerable: true,
|
|
7
|
+
get: function() {
|
|
8
|
+
return usePrefersReducedMotion;
|
|
9
|
+
}
|
|
10
|
+
});
|
|
11
|
+
const _react = require("react");
|
|
12
|
+
function usePrefersReducedMotion() {
|
|
13
|
+
const [prefersReducedMotion, setPrefersReducedMotion] = (0, _react.useState)(false);
|
|
14
|
+
(0, _react.useEffect)(()=>{
|
|
15
|
+
const mediaQuery = window.matchMedia("(prefers-reduced-motion: reduce)");
|
|
16
|
+
setPrefersReducedMotion(mediaQuery.matches);
|
|
17
|
+
const listener = (event)=>{
|
|
18
|
+
setPrefersReducedMotion(event.matches);
|
|
19
|
+
};
|
|
20
|
+
mediaQuery.addEventListener("change", listener);
|
|
21
|
+
return ()=>{
|
|
22
|
+
mediaQuery.removeEventListener("change", listener);
|
|
23
|
+
};
|
|
24
|
+
}, []);
|
|
25
|
+
return prefersReducedMotion;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
//# sourceMappingURL=usePrefersReducedMotion.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../src/hooks/usePrefersReducedMotion/usePrefersReducedMotion.tsx"],"sourcesContent":["import { useEffect, useState } from \"react\";\n\nexport function usePrefersReducedMotion(): boolean {\n const [prefersReducedMotion, setPrefersReducedMotion] = useState(false);\n\n useEffect(() => {\n const mediaQuery = window.matchMedia(\"(prefers-reduced-motion: reduce)\");\n setPrefersReducedMotion(mediaQuery.matches);\n\n const listener = (event: MediaQueryListEvent) => {\n setPrefersReducedMotion(event.matches);\n };\n\n mediaQuery.addEventListener(\"change\", listener);\n\n return () => {\n mediaQuery.removeEventListener(\"change\", listener);\n };\n }, []);\n\n return prefersReducedMotion;\n}\n"],"names":["usePrefersReducedMotion","prefersReducedMotion","setPrefersReducedMotion","useState","useEffect","mediaQuery","window","matchMedia","matches","listener","event","addEventListener","removeEventListener"],"mappings":";;;;+BAEgBA;;;eAAAA;;;uBAFoB;AAE7B,SAASA;IACd,MAAM,CAACC,sBAAsBC,wBAAwB,GAAGC,IAAAA,eAAQ,EAAC;IAEjEC,IAAAA,gBAAS,EAAC;QACR,MAAMC,aAAaC,OAAOC,UAAU,CAAC;QACrCL,wBAAwBG,WAAWG,OAAO;QAE1C,MAAMC,WAAW,CAACC;YAChBR,wBAAwBQ,MAAMF,OAAO;QACvC;QAEAH,WAAWM,gBAAgB,CAAC,UAAUF;QAEtC,OAAO;YACLJ,WAAWO,mBAAmB,CAAC,UAAUH;QAC3C;IACF,GAAG,EAAE;IAEL,OAAOR;AACT"}
|
|
@@ -5,6 +5,7 @@ import classNames from "classnames/dedupe";
|
|
|
5
5
|
import { forwardRef, useEffect, useState } from "react";
|
|
6
6
|
import { Flex } from "../Flex";
|
|
7
7
|
import { Icon } from "../Icon";
|
|
8
|
+
import { usePrefersReducedMotion } from "../../hooks/usePrefersReducedMotion";
|
|
8
9
|
const AccordionLink = ({ text, toggle, onClick, headerChildren, ariaExpanded })=>{
|
|
9
10
|
const linkClasses = classNames("mobius/AccordionLink", {
|
|
10
11
|
"--is-open": ariaExpanded
|
|
@@ -76,7 +77,7 @@ export const Accordion = /*#__PURE__*/ forwardRef((props, ref)=>{
|
|
|
76
77
|
open: true,
|
|
77
78
|
withAnimation: false
|
|
78
79
|
});
|
|
79
|
-
const prefersReducedMotion =
|
|
80
|
+
const prefersReducedMotion = usePrefersReducedMotion();
|
|
80
81
|
const containerClasses = classNames("mobius", "mobius/Accordion", props.className, {
|
|
81
82
|
"--should-animate": accordionState.withAnimation && !prefersReducedMotion,
|
|
82
83
|
"--is-open": accordionState.open
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/components/Accordion/Accordion.tsx"],"sourcesContent":["\"use client\";\n\nimport { chevronDown } from \"@simplybusiness/icons\";\nimport classNames from \"classnames/dedupe\";\nimport {\n KeyboardEvent,\n ReactNode,\n Ref,\n RefAttributes,\n forwardRef,\n useEffect,\n useState,\n} from \"react\";\nimport { ForwardedRefComponent } from \"../../types/components\";\nimport { DOMProps } from \"../../types/dom\";\nimport { Flex } from \"../Flex\";\nimport { Icon } from \"../Icon\";\n\nexport type AccordionElementType = HTMLDivElement;\nexport type AccordionRef = Ref<AccordionElementType>;\nexport interface AccordionLinkProps extends DOMProps {\n /** Link text to show accordion content */\n text?: string;\n onClick?: () => void;\n toggle: () => void;\n headerChildren?: ReactNode;\n ariaExpanded: boolean | undefined;\n}\n\nconst AccordionLink = ({\n text,\n toggle,\n onClick,\n headerChildren,\n ariaExpanded,\n}: AccordionLinkProps) => {\n const linkClasses = classNames(\"mobius/AccordionLink\", {\n \"--is-open\": ariaExpanded,\n });\n const iconClasses = classNames(\"mobius/AccordionLinkIcon\", {\n \"--is-open\": ariaExpanded,\n });\n\n const handleOnClick = (): void => {\n if (onClick) {\n onClick();\n }\n toggle();\n };\n\n const handleKeyDown = (e: KeyboardEvent) => {\n if (e.key === \" \" || e.key === \"Enter\") {\n toggle();\n }\n };\n\n if (headerChildren) {\n return (\n <Flex\n justifyContent=\"space-between\"\n alignItems=\"center\"\n className=\"mobius/AccordionHeader\"\n >\n <div\n className={linkClasses}\n onClick={handleOnClick}\n onKeyDown={handleKeyDown}\n role=\"button\"\n tabIndex={0}\n aria-expanded={!!ariaExpanded}\n >\n <span className=\"mobius/AccordionLinkText\">{text}</span>\n <Icon icon={chevronDown} className={iconClasses} />\n </div>\n {headerChildren}\n </Flex>\n );\n }\n\n return (\n <div\n className={linkClasses}\n onClick={handleOnClick}\n onKeyDown={handleKeyDown}\n role=\"button\"\n tabIndex={0}\n aria-expanded={!!ariaExpanded}\n >\n <span className=\"mobius/AccordionLinkText\">{text}</span>\n <Icon icon={chevronDown} className={iconClasses} />\n </div>\n );\n};\n\nexport interface AccordionProps\n extends DOMProps,\n RefAttributes<AccordionElementType> {\n /** Custom class name for setting specific CSS */\n className?: string;\n /** Link text to show accordion content */\n showText?: string;\n /** Link text to hide accordion content */\n hideText?: string;\n /** Whether header is above or below content */\n headerPosition?: \"top\" | \"bottom\";\n /** Whether to expand the accordion initially */\n startOpen?: boolean | undefined;\n /** Callback that fires each time the accordion is opened */\n onOpen?: () => void;\n /** Callback that fires each time the accordion is closed */\n onClose?: () => void;\n /** Callback that fires each time the accordion state changes */\n onChange?: (state: boolean) => void;\n children?: ReactNode;\n headerChildren?: ReactNode;\n}\n\nexport const Accordion: ForwardedRefComponent<\n AccordionProps,\n AccordionElementType\n> = forwardRef((props: AccordionProps, ref: AccordionRef) => {\n const {\n showText = \"See more\",\n hideText = \"See less\",\n headerPosition = \"top\",\n startOpen = false,\n onOpen,\n onClose,\n onChange = () => {},\n headerChildren,\n ...rest\n } = props;\n const [accordionState, setAccordionState] = useState({\n open: true,\n withAnimation: false,\n });\n const prefersReducedMotion
|
|
1
|
+
{"version":3,"sources":["../../../../src/components/Accordion/Accordion.tsx"],"sourcesContent":["\"use client\";\n\nimport { chevronDown } from \"@simplybusiness/icons\";\nimport classNames from \"classnames/dedupe\";\nimport {\n KeyboardEvent,\n ReactNode,\n Ref,\n RefAttributes,\n forwardRef,\n useEffect,\n useState,\n} from \"react\";\nimport { ForwardedRefComponent } from \"../../types/components\";\nimport { DOMProps } from \"../../types/dom\";\nimport { Flex } from \"../Flex\";\nimport { Icon } from \"../Icon\";\nimport { usePrefersReducedMotion } from \"../../hooks/usePrefersReducedMotion\";\n\nexport type AccordionElementType = HTMLDivElement;\nexport type AccordionRef = Ref<AccordionElementType>;\nexport interface AccordionLinkProps extends DOMProps {\n /** Link text to show accordion content */\n text?: string;\n onClick?: () => void;\n toggle: () => void;\n headerChildren?: ReactNode;\n ariaExpanded: boolean | undefined;\n}\n\nconst AccordionLink = ({\n text,\n toggle,\n onClick,\n headerChildren,\n ariaExpanded,\n}: AccordionLinkProps) => {\n const linkClasses = classNames(\"mobius/AccordionLink\", {\n \"--is-open\": ariaExpanded,\n });\n const iconClasses = classNames(\"mobius/AccordionLinkIcon\", {\n \"--is-open\": ariaExpanded,\n });\n\n const handleOnClick = (): void => {\n if (onClick) {\n onClick();\n }\n toggle();\n };\n\n const handleKeyDown = (e: KeyboardEvent) => {\n if (e.key === \" \" || e.key === \"Enter\") {\n toggle();\n }\n };\n\n if (headerChildren) {\n return (\n <Flex\n justifyContent=\"space-between\"\n alignItems=\"center\"\n className=\"mobius/AccordionHeader\"\n >\n <div\n className={linkClasses}\n onClick={handleOnClick}\n onKeyDown={handleKeyDown}\n role=\"button\"\n tabIndex={0}\n aria-expanded={!!ariaExpanded}\n >\n <span className=\"mobius/AccordionLinkText\">{text}</span>\n <Icon icon={chevronDown} className={iconClasses} />\n </div>\n {headerChildren}\n </Flex>\n );\n }\n\n return (\n <div\n className={linkClasses}\n onClick={handleOnClick}\n onKeyDown={handleKeyDown}\n role=\"button\"\n tabIndex={0}\n aria-expanded={!!ariaExpanded}\n >\n <span className=\"mobius/AccordionLinkText\">{text}</span>\n <Icon icon={chevronDown} className={iconClasses} />\n </div>\n );\n};\n\nexport interface AccordionProps\n extends DOMProps,\n RefAttributes<AccordionElementType> {\n /** Custom class name for setting specific CSS */\n className?: string;\n /** Link text to show accordion content */\n showText?: string;\n /** Link text to hide accordion content */\n hideText?: string;\n /** Whether header is above or below content */\n headerPosition?: \"top\" | \"bottom\";\n /** Whether to expand the accordion initially */\n startOpen?: boolean | undefined;\n /** Callback that fires each time the accordion is opened */\n onOpen?: () => void;\n /** Callback that fires each time the accordion is closed */\n onClose?: () => void;\n /** Callback that fires each time the accordion state changes */\n onChange?: (state: boolean) => void;\n children?: ReactNode;\n headerChildren?: ReactNode;\n}\n\nexport const Accordion: ForwardedRefComponent<\n AccordionProps,\n AccordionElementType\n> = forwardRef((props: AccordionProps, ref: AccordionRef) => {\n const {\n showText = \"See more\",\n hideText = \"See less\",\n headerPosition = \"top\",\n startOpen = false,\n onOpen,\n onClose,\n onChange = () => {},\n headerChildren,\n ...rest\n } = props;\n const [accordionState, setAccordionState] = useState({\n open: true,\n withAnimation: false,\n });\n const prefersReducedMotion = usePrefersReducedMotion();\n\n const containerClasses = classNames(\n \"mobius\",\n \"mobius/Accordion\",\n props.className,\n {\n \"--should-animate\": accordionState.withAnimation && !prefersReducedMotion,\n \"--is-open\": accordionState.open,\n },\n );\n const contentContainerClasses = classNames(\n \"mobius/AccordionContentContainer\",\n {\n \"--is-open\": accordionState.open,\n },\n );\n const contentClasses = classNames(\"mobius/AccordionContent\", {\n \"--is-open\": accordionState.open,\n });\n const linkText = accordionState.open ? hideText : showText;\n\n const handleChange = () => {\n // Fire events\n if (!accordionState.open && onOpen) {\n onOpen();\n }\n if (accordionState.open && onClose) {\n onClose();\n }\n if (onChange) {\n onChange(!accordionState.open);\n }\n setAccordionState({\n open: !accordionState.open,\n withAnimation: true,\n });\n };\n\n useEffect(() => {\n setAccordionState({\n open: startOpen,\n withAnimation: false,\n });\n }, [startOpen]);\n\n return (\n <div ref={ref} {...rest} className={containerClasses}>\n {headerPosition === \"top\" && (\n <AccordionLink\n text={linkText}\n toggle={handleChange}\n ariaExpanded={accordionState.open}\n headerChildren={headerChildren}\n />\n )}\n <div\n className={contentContainerClasses}\n aria-hidden={!accordionState.open}\n >\n <div className={contentClasses}>{props.children}</div>\n </div>\n {headerPosition === \"bottom\" && (\n <AccordionLink\n text={linkText}\n toggle={handleChange}\n ariaExpanded={accordionState.open}\n headerChildren={headerChildren}\n />\n )}\n </div>\n );\n});\n"],"names":["chevronDown","classNames","forwardRef","useEffect","useState","Flex","Icon","usePrefersReducedMotion","AccordionLink","text","toggle","onClick","headerChildren","ariaExpanded","linkClasses","iconClasses","handleOnClick","handleKeyDown","e","key","justifyContent","alignItems","className","div","onKeyDown","role","tabIndex","aria-expanded","span","icon","Accordion","props","ref","showText","hideText","headerPosition","startOpen","onOpen","onClose","onChange","rest","accordionState","setAccordionState","open","withAnimation","prefersReducedMotion","containerClasses","contentContainerClasses","contentClasses","linkText","handleChange","aria-hidden","children"],"mappings":"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"}
|
package/dist/esm/hooks/index.js
CHANGED
|
@@ -6,6 +6,7 @@ export * from "./useDialog";
|
|
|
6
6
|
export * from "./useDialogPolyfill";
|
|
7
7
|
export * from "./useLabel";
|
|
8
8
|
export * from "./useOnClickOutside";
|
|
9
|
+
export * from "./usePrefersReducedMotion";
|
|
9
10
|
export * from "./useTextField";
|
|
10
11
|
export * from "./useValidationClasses";
|
|
11
12
|
export * from "./useWindowEvent";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/hooks/index.tsx"],"sourcesContent":["export * from \"./useBodyScrollLock\";\nexport * from \"./useBreakpoint\";\nexport * from \"./useButton\";\nexport * from \"./useDeprecationWarning\";\nexport * from \"./useDialog\";\nexport * from \"./useDialogPolyfill\";\nexport * from \"./useLabel\";\nexport * from \"./useOnClickOutside\";\nexport * from \"./useTextField\";\nexport * from \"./useValidationClasses\";\nexport * from \"./useWindowEvent\";\n"],"names":[],"mappings":"AAAA,cAAc,sBAAsB;AACpC,cAAc,kBAAkB;AAChC,cAAc,cAAc;AAC5B,cAAc,0BAA0B;AACxC,cAAc,cAAc;AAC5B,cAAc,sBAAsB;AACpC,cAAc,aAAa;AAC3B,cAAc,sBAAsB;AACpC,cAAc,iBAAiB;AAC/B,cAAc,yBAAyB;AACvC,cAAc,mBAAmB"}
|
|
1
|
+
{"version":3,"sources":["../../../src/hooks/index.tsx"],"sourcesContent":["export * from \"./useBodyScrollLock\";\nexport * from \"./useBreakpoint\";\nexport * from \"./useButton\";\nexport * from \"./useDeprecationWarning\";\nexport * from \"./useDialog\";\nexport * from \"./useDialogPolyfill\";\nexport * from \"./useLabel\";\nexport * from \"./useOnClickOutside\";\nexport * from \"./usePrefersReducedMotion\";\nexport * from \"./useTextField\";\nexport * from \"./useValidationClasses\";\nexport * from \"./useWindowEvent\";\n"],"names":[],"mappings":"AAAA,cAAc,sBAAsB;AACpC,cAAc,kBAAkB;AAChC,cAAc,cAAc;AAC5B,cAAc,0BAA0B;AACxC,cAAc,cAAc;AAC5B,cAAc,sBAAsB;AACpC,cAAc,aAAa;AAC3B,cAAc,sBAAsB;AACpC,cAAc,4BAA4B;AAC1C,cAAc,iBAAiB;AAC/B,cAAc,yBAAyB;AACvC,cAAc,mBAAmB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../src/hooks/usePrefersReducedMotion/index.tsx"],"sourcesContent":["export * from \"./usePrefersReducedMotion\";\n"],"names":[],"mappings":"AAAA,cAAc,4BAA4B"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { useEffect, useState } from "react";
|
|
2
|
+
export function usePrefersReducedMotion() {
|
|
3
|
+
const [prefersReducedMotion, setPrefersReducedMotion] = useState(false);
|
|
4
|
+
useEffect(()=>{
|
|
5
|
+
const mediaQuery = window.matchMedia("(prefers-reduced-motion: reduce)");
|
|
6
|
+
setPrefersReducedMotion(mediaQuery.matches);
|
|
7
|
+
const listener = (event)=>{
|
|
8
|
+
setPrefersReducedMotion(event.matches);
|
|
9
|
+
};
|
|
10
|
+
mediaQuery.addEventListener("change", listener);
|
|
11
|
+
return ()=>{
|
|
12
|
+
mediaQuery.removeEventListener("change", listener);
|
|
13
|
+
};
|
|
14
|
+
}, []);
|
|
15
|
+
return prefersReducedMotion;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
//# sourceMappingURL=usePrefersReducedMotion.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../src/hooks/usePrefersReducedMotion/usePrefersReducedMotion.tsx"],"sourcesContent":["import { useEffect, useState } from \"react\";\n\nexport function usePrefersReducedMotion(): boolean {\n const [prefersReducedMotion, setPrefersReducedMotion] = useState(false);\n\n useEffect(() => {\n const mediaQuery = window.matchMedia(\"(prefers-reduced-motion: reduce)\");\n setPrefersReducedMotion(mediaQuery.matches);\n\n const listener = (event: MediaQueryListEvent) => {\n setPrefersReducedMotion(event.matches);\n };\n\n mediaQuery.addEventListener(\"change\", listener);\n\n return () => {\n mediaQuery.removeEventListener(\"change\", listener);\n };\n }, []);\n\n return prefersReducedMotion;\n}\n"],"names":["useEffect","useState","usePrefersReducedMotion","prefersReducedMotion","setPrefersReducedMotion","mediaQuery","window","matchMedia","matches","listener","event","addEventListener","removeEventListener"],"mappings":"AAAA,SAASA,SAAS,EAAEC,QAAQ,QAAQ,QAAQ;AAE5C,OAAO,SAASC;IACd,MAAM,CAACC,sBAAsBC,wBAAwB,GAAGH,SAAS;IAEjED,UAAU;QACR,MAAMK,aAAaC,OAAOC,UAAU,CAAC;QACrCH,wBAAwBC,WAAWG,OAAO;QAE1C,MAAMC,WAAW,CAACC;YAChBN,wBAAwBM,MAAMF,OAAO;QACvC;QAEAH,WAAWM,gBAAgB,CAAC,UAAUF;QAEtC,OAAO;YACLJ,WAAWO,mBAAmB,CAAC,UAAUH;QAC3C;IACF,GAAG,EAAE;IAEL,OAAON;AACT"}
|
|
@@ -6,6 +6,7 @@ export * from "./useDialog";
|
|
|
6
6
|
export * from "./useDialogPolyfill";
|
|
7
7
|
export * from "./useLabel";
|
|
8
8
|
export * from "./useOnClickOutside";
|
|
9
|
+
export * from "./usePrefersReducedMotion";
|
|
9
10
|
export * from "./useTextField";
|
|
10
11
|
export * from "./useValidationClasses";
|
|
11
12
|
export * from "./useWindowEvent";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./usePrefersReducedMotion";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function usePrefersReducedMotion(): boolean;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
CHANGED
|
@@ -15,6 +15,7 @@ import { ForwardedRefComponent } from "../../types/components";
|
|
|
15
15
|
import { DOMProps } from "../../types/dom";
|
|
16
16
|
import { Flex } from "../Flex";
|
|
17
17
|
import { Icon } from "../Icon";
|
|
18
|
+
import { usePrefersReducedMotion } from "../../hooks/usePrefersReducedMotion";
|
|
18
19
|
|
|
19
20
|
export type AccordionElementType = HTMLDivElement;
|
|
20
21
|
export type AccordionRef = Ref<AccordionElementType>;
|
|
@@ -134,9 +135,7 @@ export const Accordion: ForwardedRefComponent<
|
|
|
134
135
|
open: true,
|
|
135
136
|
withAnimation: false,
|
|
136
137
|
});
|
|
137
|
-
const prefersReducedMotion =
|
|
138
|
-
typeof window === "object" &&
|
|
139
|
-
window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
138
|
+
const prefersReducedMotion = usePrefersReducedMotion();
|
|
140
139
|
|
|
141
140
|
const containerClasses = classNames(
|
|
142
141
|
"mobius",
|
package/src/hooks/index.tsx
CHANGED
|
@@ -6,6 +6,7 @@ export * from "./useDialog";
|
|
|
6
6
|
export * from "./useDialogPolyfill";
|
|
7
7
|
export * from "./useLabel";
|
|
8
8
|
export * from "./useOnClickOutside";
|
|
9
|
+
export * from "./usePrefersReducedMotion";
|
|
9
10
|
export * from "./useTextField";
|
|
10
11
|
export * from "./useValidationClasses";
|
|
11
12
|
export * from "./useWindowEvent";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./usePrefersReducedMotion";
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { renderHook } from "@testing-library/react";
|
|
2
|
+
import { usePrefersReducedMotion } from "./usePrefersReducedMotion";
|
|
3
|
+
|
|
4
|
+
function mockMatchMedia(matches: boolean) {
|
|
5
|
+
window.matchMedia = jest.fn().mockReturnValue({
|
|
6
|
+
matches,
|
|
7
|
+
addEventListener: jest.fn(),
|
|
8
|
+
removeEventListener: jest.fn(),
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
describe("usePrefersReducedMotion", () => {
|
|
13
|
+
it("should return a boolean", () => {
|
|
14
|
+
mockMatchMedia(false);
|
|
15
|
+
|
|
16
|
+
const { result } = renderHook(() => usePrefersReducedMotion());
|
|
17
|
+
expect(typeof result.current).toBe("boolean");
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it("should return false by default", () => {
|
|
21
|
+
mockMatchMedia(false);
|
|
22
|
+
|
|
23
|
+
const { result } = renderHook(() => usePrefersReducedMotion());
|
|
24
|
+
expect(result.current).toBe(false);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it("should return true when the user prefers reduced motion", () => {
|
|
28
|
+
mockMatchMedia(true);
|
|
29
|
+
|
|
30
|
+
const { result } = renderHook(() => usePrefersReducedMotion());
|
|
31
|
+
expect(result.current).toBe(true);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it("should return false when the user does not prefer reduced motion", () => {
|
|
35
|
+
mockMatchMedia(false);
|
|
36
|
+
|
|
37
|
+
const { result } = renderHook(() => usePrefersReducedMotion());
|
|
38
|
+
expect(result.current).toBe(false);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
// TODO: These tests need better mocking to work
|
|
42
|
+
it.todo(
|
|
43
|
+
"should return true when the user prefers reduced motion after changing",
|
|
44
|
+
);
|
|
45
|
+
it.todo(
|
|
46
|
+
"should return false when the user does not prefer reduced motion after changing",
|
|
47
|
+
);
|
|
48
|
+
});
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { useEffect, useState } from "react";
|
|
2
|
+
|
|
3
|
+
export function usePrefersReducedMotion(): boolean {
|
|
4
|
+
const [prefersReducedMotion, setPrefersReducedMotion] = useState(false);
|
|
5
|
+
|
|
6
|
+
useEffect(() => {
|
|
7
|
+
const mediaQuery = window.matchMedia("(prefers-reduced-motion: reduce)");
|
|
8
|
+
setPrefersReducedMotion(mediaQuery.matches);
|
|
9
|
+
|
|
10
|
+
const listener = (event: MediaQueryListEvent) => {
|
|
11
|
+
setPrefersReducedMotion(event.matches);
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
mediaQuery.addEventListener("change", listener);
|
|
15
|
+
|
|
16
|
+
return () => {
|
|
17
|
+
mediaQuery.removeEventListener("change", listener);
|
|
18
|
+
};
|
|
19
|
+
}, []);
|
|
20
|
+
|
|
21
|
+
return prefersReducedMotion;
|
|
22
|
+
}
|