@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.
Files changed (28) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/dist/cjs/components/Accordion/Accordion.js +2 -1
  3. package/dist/cjs/components/Accordion/Accordion.js.map +1 -1
  4. package/dist/cjs/hooks/index.js +1 -0
  5. package/dist/cjs/hooks/index.js.map +1 -1
  6. package/dist/cjs/hooks/usePrefersReducedMotion/index.js +20 -0
  7. package/dist/cjs/hooks/usePrefersReducedMotion/index.js.map +1 -0
  8. package/dist/cjs/hooks/usePrefersReducedMotion/usePrefersReducedMotion.js +28 -0
  9. package/dist/cjs/hooks/usePrefersReducedMotion/usePrefersReducedMotion.js.map +1 -0
  10. package/dist/esm/components/Accordion/Accordion.js +2 -1
  11. package/dist/esm/components/Accordion/Accordion.js.map +1 -1
  12. package/dist/esm/hooks/index.js +1 -0
  13. package/dist/esm/hooks/index.js.map +1 -1
  14. package/dist/esm/hooks/usePrefersReducedMotion/index.js +3 -0
  15. package/dist/esm/hooks/usePrefersReducedMotion/index.js.map +1 -0
  16. package/dist/esm/hooks/usePrefersReducedMotion/usePrefersReducedMotion.js +18 -0
  17. package/dist/esm/hooks/usePrefersReducedMotion/usePrefersReducedMotion.js.map +1 -0
  18. package/dist/types/hooks/index.d.ts +1 -0
  19. package/dist/types/hooks/usePrefersReducedMotion/index.d.ts +1 -0
  20. package/dist/types/hooks/usePrefersReducedMotion/usePrefersReducedMotion.d.ts +1 -0
  21. package/dist/types/hooks/usePrefersReducedMotion/usePrefersReducedMotion.test.d.ts +1 -0
  22. package/package.json +1 -1
  23. package/src/components/Accordion/Accordion.tsx +2 -3
  24. package/src/hooks/index.tsx +1 -0
  25. package/src/hooks/usePrefersReducedMotion/index.tsx +1 -0
  26. package/src/hooks/usePrefersReducedMotion/usePrefersReducedMotion.test.tsx +48 -0
  27. package/src/hooks/usePrefersReducedMotion/usePrefersReducedMotion.tsx +22 -0
  28. package/src/public-whitelist.test.ts +0 -2
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## 4.15.0
4
+
5
+ ### Minor Changes
6
+
7
+ - c2f1d0a: Add `usePrefersReducedMotion` hook
8
+
3
9
  ## 4.14.1
4
10
 
5
11
  ### Patch Changes
@@ -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 = typeof window === "object" && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
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 =\n typeof window === \"object\" &&\n window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches;\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","window","matchMedia","matches","containerClasses","contentContainerClasses","contentClasses","linkText","handleChange","useEffect","aria-hidden","children"],"mappings":"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"}
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":"AAAA;;;;;+BAsHaA;;;eAAAA;;;;uBApHe;+DACL;uBAShB;sBAGc;sBACA;yCACmB;;;;;;AAaxC,MAAMC,gBAAgB,CAAC,EACrBC,IAAI,EACJC,MAAM,EACNC,OAAO,EACPC,cAAc,EACdC,YAAY,EACO;IACnB,MAAMC,cAAcC,IAAAA,eAAU,EAAC,wBAAwB;QACrD,aAAaF;IACf;IACA,MAAMG,cAAcD,IAAAA,eAAU,EAAC,4BAA4B;QACzD,aAAaF;IACf;IAEA,MAAMI,gBAAgB;QACpB,IAAIN,SAAS;YACXA;QACF;QACAD;IACF;IAEA,MAAMQ,gBAAgB,CAACC;QACrB,IAAIA,EAAEC,GAAG,KAAK,OAAOD,EAAEC,GAAG,KAAK,SAAS;YACtCV;QACF;IACF;IAEA,IAAIE,gBAAgB;QAClB,qBACE,sBAACS,UAAI;YACHC,gBAAe;YACfC,YAAW;YACXC,WAAU;;8BAEV,sBAACC;oBACCD,WAAWV;oBACXH,SAASM;oBACTS,WAAWR;oBACXS,MAAK;oBACLC,UAAU;oBACVC,iBAAe,CAAC,CAAChB;;sCAEjB,qBAACiB;4BAAKN,WAAU;sCAA4Bf;;sCAC5C,qBAACsB,UAAI;4BAACC,MAAMC,kBAAW;4BAAET,WAAWR;;;;gBAErCJ;;;IAGP;IAEA,qBACE,sBAACa;QACCD,WAAWV;QACXH,SAASM;QACTS,WAAWR;QACXS,MAAK;QACLC,UAAU;QACVC,iBAAe,CAAC,CAAChB;;0BAEjB,qBAACiB;gBAAKN,WAAU;0BAA4Bf;;0BAC5C,qBAACsB,UAAI;gBAACC,MAAMC,kBAAW;gBAAET,WAAWR;;;;AAG1C;AAyBO,MAAMT,0BAGT2B,IAAAA,iBAAU,EAAC,CAACC,OAAuBC;IACrC,MAAM,EACJC,WAAW,UAAU,EACrBC,WAAW,UAAU,EACrBC,iBAAiB,KAAK,EACtBC,YAAY,KAAK,EACjBC,MAAM,EACNC,OAAO,EACPC,WAAW,KAAO,CAAC,EACnB/B,cAAc,EACd,GAAGgC,MACJ,GAAGT;IACJ,MAAM,CAACU,gBAAgBC,kBAAkB,GAAGC,IAAAA,eAAQ,EAAC;QACnDC,MAAM;QACNC,eAAe;IACjB;IACA,MAAMC,uBAAuBC,IAAAA,gDAAuB;IAEpD,MAAMC,mBAAmBrC,IAAAA,eAAU,EACjC,UACA,oBACAoB,MAAMX,SAAS,EACf;QACE,oBAAoBqB,eAAeI,aAAa,IAAI,CAACC;QACrD,aAAaL,eAAeG,IAAI;IAClC;IAEF,MAAMK,0BAA0BtC,IAAAA,eAAU,EACxC,oCACA;QACE,aAAa8B,eAAeG,IAAI;IAClC;IAEF,MAAMM,iBAAiBvC,IAAAA,eAAU,EAAC,2BAA2B;QAC3D,aAAa8B,eAAeG,IAAI;IAClC;IACA,MAAMO,WAAWV,eAAeG,IAAI,GAAGV,WAAWD;IAElD,MAAMmB,eAAe;QACnB,cAAc;QACd,IAAI,CAACX,eAAeG,IAAI,IAAIP,QAAQ;YAClCA;QACF;QACA,IAAII,eAAeG,IAAI,IAAIN,SAAS;YAClCA;QACF;QACA,IAAIC,UAAU;YACZA,SAAS,CAACE,eAAeG,IAAI;QAC/B;QACAF,kBAAkB;YAChBE,MAAM,CAACH,eAAeG,IAAI;YAC1BC,eAAe;QACjB;IACF;IAEAQ,IAAAA,gBAAS,EAAC;QACRX,kBAAkB;YAChBE,MAAMR;YACNS,eAAe;QACjB;IACF,GAAG;QAACT;KAAU;IAEd,qBACE,sBAACf;QAAIW,KAAKA;QAAM,GAAGQ,IAAI;QAAEpB,WAAW4B;;YACjCb,mBAAmB,uBAClB,qBAAC/B;gBACCC,MAAM8C;gBACN7C,QAAQ8C;gBACR3C,cAAcgC,eAAeG,IAAI;gBACjCpC,gBAAgBA;;0BAGpB,qBAACa;gBACCD,WAAW6B;gBACXK,eAAa,CAACb,eAAeG,IAAI;0BAEjC,cAAA,qBAACvB;oBAAID,WAAW8B;8BAAiBnB,MAAMwB,QAAQ;;;YAEhDpB,mBAAmB,0BAClB,qBAAC/B;gBACCC,MAAM8C;gBACN7C,QAAQ8C;gBACR3C,cAAcgC,eAAeG,IAAI;gBACjCpC,gBAAgBA;;;;AAK1B"}
@@ -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 = typeof window === "object" && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
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 =\n typeof window === \"object\" &&\n window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches;\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","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","window","matchMedia","matches","containerClasses","contentContainerClasses","contentClasses","linkText","handleChange","aria-hidden","children"],"mappings":"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"}
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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"}
@@ -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,3 @@
1
+ export * from "./usePrefersReducedMotion";
2
+
3
+ //# sourceMappingURL=index.js.map
@@ -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;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@simplybusiness/mobius",
3
3
  "license": "UNLICENSED",
4
- "version": "4.14.1",
4
+ "version": "4.15.0",
5
5
  "description": "Core library of Mobius react components",
6
6
  "repository": {
7
7
  "type": "git",
@@ -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",
@@ -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
+ }
@@ -12,8 +12,6 @@ const PUBLIC_PACKAGE_WHITELIST = [
12
12
  "@simplybusiness/icons",
13
13
  "@simplybusiness/mobius-datepicker",
14
14
  "@simplybusiness/services",
15
- "@simplybusiness/theme-insurancebee",
16
- "@simplybusiness/theme-ott",
17
15
  "@simplybusiness/theme-sb",
18
16
  ];
19
17