@simplybusiness/mobius 5.1.1 → 5.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.
@@ -3,10 +3,10 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { chevronDown } from "@simplybusiness/icons";
4
4
  import classNames from "classnames/dedupe";
5
5
  import { forwardRef, useEffect, useState } from "react";
6
+ import { usePrefersReducedMotion } from "../../hooks/usePrefersReducedMotion";
6
7
  import { Flex } from "../Flex";
7
8
  import { Icon } from "../Icon";
8
- import { usePrefersReducedMotion } from "../../hooks/usePrefersReducedMotion";
9
- const AccordionLink = ({ text, toggle, onClick, headerChildren, ariaExpanded })=>{
9
+ const AccordionLink = ({ text, elementType: LinkElement = "span", toggle, onClick, headerChildren, ariaExpanded })=>{
10
10
  const linkClasses = classNames("mobius-accordion__link", {
11
11
  "--is-open": ariaExpanded
12
12
  });
@@ -38,7 +38,7 @@ const AccordionLink = ({ text, toggle, onClick, headerChildren, ariaExpanded })=
38
38
  tabIndex: 0,
39
39
  "aria-expanded": !!ariaExpanded,
40
40
  children: [
41
- /*#__PURE__*/ _jsx("span", {
41
+ /*#__PURE__*/ _jsx(LinkElement, {
42
42
  className: "mobius-accordion__link-text",
43
43
  children: text
44
44
  }),
@@ -60,7 +60,7 @@ const AccordionLink = ({ text, toggle, onClick, headerChildren, ariaExpanded })=
60
60
  tabIndex: 0,
61
61
  "aria-expanded": !!ariaExpanded,
62
62
  children: [
63
- /*#__PURE__*/ _jsx("span", {
63
+ /*#__PURE__*/ _jsx(LinkElement, {
64
64
  className: "mobius-accordion__link-text",
65
65
  children: text
66
66
  }),
@@ -72,7 +72,7 @@ const AccordionLink = ({ text, toggle, onClick, headerChildren, ariaExpanded })=
72
72
  });
73
73
  };
74
74
  export const Accordion = /*#__PURE__*/ forwardRef((props, ref)=>{
75
- const { showText = "See more", hideText = "See less", headerPosition = "top", startOpen = false, onOpen, onClose, onChange = ()=>{}, headerChildren, ...rest } = props;
75
+ const { showText = "See more", hideText = "See less", headerPosition = "top", startOpen = false, linkElementType, onOpen, onClose, onChange = ()=>{}, headerChildren, ...rest } = props;
76
76
  const [accordionState, setAccordionState] = useState({
77
77
  open: true,
78
78
  withAnimation: false
@@ -120,6 +120,7 @@ export const Accordion = /*#__PURE__*/ forwardRef((props, ref)=>{
120
120
  children: [
121
121
  headerPosition === "top" && /*#__PURE__*/ _jsx(AccordionLink, {
122
122
  text: linkText,
123
+ elementType: linkElementType,
123
124
  toggle: handleChange,
124
125
  ariaExpanded: accordionState.open,
125
126
  headerChildren: headerChildren
@@ -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\";\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-accordion__link\", {\n \"--is-open\": ariaExpanded,\n });\n const iconClasses = classNames(\"mobius-accordion__link-icon\", {\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-accordion__header\"\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-accordion__link-text\">{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-accordion__link-text\">{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-accordion__content-container\",\n {\n \"--is-open\": accordionState.open,\n },\n );\n const contentClasses = classNames(\"mobius-accordion__content\", {\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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"}
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 { usePrefersReducedMotion } from \"../../hooks/usePrefersReducedMotion\";\nimport { ForwardedRefComponent } from \"../../types/components\";\nimport { DOMProps } from \"../../types/dom\";\nimport { Flex } from \"../Flex\";\nimport { Icon } from \"../Icon\";\n\nexport type AccordionLinkElementType =\n | \"h1\"\n | \"h2\"\n | \"h3\"\n | \"h4\"\n | \"h5\"\n | \"h6\"\n | \"span\";\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 elementType?: AccordionLinkElementType;\n onClick?: () => void;\n toggle: () => void;\n headerChildren?: ReactNode;\n ariaExpanded: boolean | undefined;\n}\n\nconst AccordionLink = ({\n text,\n elementType: LinkElement = \"span\",\n toggle,\n onClick,\n headerChildren,\n ariaExpanded,\n}: AccordionLinkProps) => {\n const linkClasses = classNames(\"mobius-accordion__link\", {\n \"--is-open\": ariaExpanded,\n });\n const iconClasses = classNames(\"mobius-accordion__link-icon\", {\n \"--is-open\": ariaExpanded,\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-accordion__header\"\n >\n <div\n className={linkClasses}\n onClick={handleOnClick}\n onKeyDown={handleKeyDown}\n role=\"button\"\n tabIndex={0}\n aria-expanded={!!ariaExpanded}\n >\n <LinkElement className=\"mobius-accordion__link-text\">\n {text}\n </LinkElement>\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 <LinkElement className=\"mobius-accordion__link-text\">{text}</LinkElement>\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 /** Semantic heading for title link (default is span) */\n linkElementType?: AccordionLinkElementType;\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 linkElementType,\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-accordion__content-container\",\n {\n \"--is-open\": accordionState.open,\n },\n );\n const contentClasses = classNames(\"mobius-accordion__content\", {\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 elementType={linkElementType}\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","usePrefersReducedMotion","Flex","Icon","AccordionLink","text","elementType","LinkElement","toggle","onClick","headerChildren","ariaExpanded","linkClasses","iconClasses","handleOnClick","handleKeyDown","e","key","justifyContent","alignItems","className","div","onKeyDown","role","tabIndex","aria-expanded","icon","Accordion","props","ref","showText","hideText","headerPosition","startOpen","linkElementType","onOpen","onClose","onChange","rest","accordionState","setAccordionState","open","withAnimation","prefersReducedMotion","containerClasses","contentContainerClasses","contentClasses","linkText","handleChange","aria-hidden","children"],"mappings":"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"}
@@ -1,11 +1,13 @@
1
1
  import { ReactNode, Ref, RefAttributes } from "react";
2
2
  import { ForwardedRefComponent } from "../../types/components";
3
3
  import { DOMProps } from "../../types/dom";
4
+ export type AccordionLinkElementType = "h1" | "h2" | "h3" | "h4" | "h5" | "h6" | "span";
4
5
  export type AccordionElementType = HTMLDivElement;
5
6
  export type AccordionRef = Ref<AccordionElementType>;
6
7
  export interface AccordionLinkProps extends DOMProps {
7
8
  /** Link text to show accordion content */
8
9
  text?: string;
10
+ elementType?: AccordionLinkElementType;
9
11
  onClick?: () => void;
10
12
  toggle: () => void;
11
13
  headerChildren?: ReactNode;
@@ -22,6 +24,8 @@ export interface AccordionProps extends DOMProps, RefAttributes<AccordionElement
22
24
  headerPosition?: "top" | "bottom";
23
25
  /** Whether to expand the accordion initially */
24
26
  startOpen?: boolean | undefined;
27
+ /** Semantic heading for title link (default is span) */
28
+ linkElementType?: AccordionLinkElementType;
25
29
  /** Callback that fires each time the accordion is opened */
26
30
  onOpen?: () => void;
27
31
  /** Callback that fires each time the accordion is closed */
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@simplybusiness/mobius",
3
3
  "license": "UNLICENSED",
4
- "version": "5.1.1",
4
+ "version": "5.2.0",
5
5
  "description": "Core library of Mobius react components",
6
6
  "repository": {
7
7
  "type": "git",
@@ -23,7 +23,8 @@
23
23
  "require": "./dist/cjs/index.js",
24
24
  "import": "./dist/esm/index.js",
25
25
  "default": "./dist/esm/index.js"
26
- }
26
+ },
27
+ "./src/**/*.css": "./src/**/*.css"
27
28
  },
28
29
  "scripts": {
29
30
  "clean": "rm -rf dist",
@@ -44,31 +45,31 @@
44
45
  "@react-types/button": "^3.9.6",
45
46
  "@react-types/dialog": "^3.5.12",
46
47
  "@react-types/progress": "^3.5.6",
47
- "@swc/cli": "^0.4.0",
48
- "@swc/core": "^1.7.1",
48
+ "@swc/cli": "^0.4.1-nightly.20240914",
49
+ "@swc/core": "^1.7.26",
49
50
  "@swc/jest": "^0.2.36",
50
51
  "@testing-library/dom": "^10.4.0",
51
- "@testing-library/jest-dom": "6.4.8",
52
- "@testing-library/react": "^16.0.0",
52
+ "@testing-library/jest-dom": "6.5.0",
53
+ "@testing-library/react": "^16.0.1",
53
54
  "@testing-library/user-event": "^14.5.2",
54
55
  "@total-typescript/shoehorn": "^0.1.2",
55
- "@types/jest": "^29.5.12",
56
+ "@types/jest": "^29.5.13",
56
57
  "@types/lodash.debounce": "^4.0.9",
57
58
  "@types/node": "^20.14.12",
58
- "@types/react": "^18.3.3",
59
+ "@types/react": "^18.3.8",
59
60
  "@types/react-dom": "^18.3.0",
60
- "@typescript-eslint/eslint-plugin": "^7.17.0",
61
- "@typescript-eslint/parser": "^7.17.0",
61
+ "@typescript-eslint/eslint-plugin": "^8.6.0",
62
+ "@typescript-eslint/parser": "^8.6.0",
62
63
  "csstype": "^3.1.3",
63
- "eslint": "^8.57.0",
64
+ "eslint": "^8.57.1",
64
65
  "eslint-config-airbnb": "^19.0.4",
65
66
  "eslint-config-prettier": "^9.1.0",
66
- "eslint-import-resolver-typescript": "^3.6.1",
67
- "eslint-plugin-import": "^2.29.1",
68
- "eslint-plugin-jsx-a11y": "^6.9.0",
69
- "eslint-plugin-no-only-tests": "^3.1.0",
67
+ "eslint-import-resolver-typescript": "^3.6.3",
68
+ "eslint-plugin-import": "^2.30.0",
69
+ "eslint-plugin-jsx-a11y": "^6.10.0",
70
+ "eslint-plugin-no-only-tests": "^3.3.0",
70
71
  "eslint-plugin-prettier": "^5.2.1",
71
- "eslint-plugin-react": "^7.35.0",
72
+ "eslint-plugin-react": "^7.36.1",
72
73
  "eslint-plugin-react-hooks": "^4.6.2",
73
74
  "eslint-plugin-storybook": "^0.8.0",
74
75
  "eslint-plugin-testing-library": "^6.3.0",
@@ -78,17 +79,17 @@
78
79
  "prettier": "^3.3.3",
79
80
  "react": "^18.3.1",
80
81
  "react-dom": "^18.3.1",
81
- "ts-jest": "^29.2.3",
82
- "tslib": "^2.6.3",
83
- "typescript": "^5.5.4"
82
+ "ts-jest": "^29.2.5",
83
+ "tslib": "^2.7.0",
84
+ "typescript": "^5.6.2"
84
85
  },
85
86
  "peerDependencies": {
86
87
  "react": "^18.2.0",
87
88
  "react-dom": "^18.2.0"
88
89
  },
89
90
  "dependencies": {
90
- "@floating-ui/react": "^0.26.20",
91
- "@simplybusiness/icons": "^4.14.0",
91
+ "@floating-ui/react": "^0.26.24",
92
+ "@simplybusiness/icons": "^4.14.1",
92
93
  "classnames": "^2.5.1",
93
94
  "dialog-polyfill": "^0.5.6",
94
95
  "lodash.debounce": "^4.0.8",
@@ -58,9 +58,18 @@
58
58
  }
59
59
  }
60
60
 
61
+ h1.mobius-accordion__link-text,
62
+ h2.mobius-accordion__link-text,
63
+ h3.mobius-accordion__link-text,
64
+ h4.mobius-accordion__link-text,
65
+ h5.mobius-accordion__link-text,
66
+ h6.mobius-accordion__link-text,
61
67
  .mobius-accordion__link-text {
68
+ margin: 0;
62
69
  margin-right: var(--size-xs);
63
70
  line-height: var(--line-height-normal);
71
+ font-size: var(--font-size-regular);
72
+ font-weight: var(--font-weight-normal);
64
73
  }
65
74
 
66
75
  .mobius-accordion__link-icon {
@@ -31,6 +31,7 @@ export const Default: StoryType = {
31
31
  </Accordion>
32
32
  ),
33
33
  args: {
34
+ linkElementType: "h2",
34
35
  showText: "See more",
35
36
  hideText: "See less",
36
37
  headerPosition: "top",
@@ -1,6 +1,6 @@
1
1
  import { fireEvent, render, screen } from "@testing-library/react";
2
2
  import userEvent from "@testing-library/user-event";
3
- import { Accordion } from ".";
3
+ import { Accordion, AccordionLinkElementType } from ".";
4
4
  import { jestMockMatchMedia } from "../../utils/jestMockMatchMedia";
5
5
 
6
6
  const CONTAINER_CLASS_NAME = "mobius-accordion";
@@ -259,4 +259,39 @@ describe("Accordion", () => {
259
259
  expect(toggle).not.toHaveBeenCalled();
260
260
  });
261
261
  });
262
+
263
+ describe("semantic heading", () => {
264
+ it("uses span by default", async () => {
265
+ const toggle = jest.fn();
266
+ const { container } = render(
267
+ <Accordion onChange={toggle}>Sample text</Accordion>,
268
+ );
269
+
270
+ const linkContainer = container.getElementsByClassName(LINK_CLASS_NAME);
271
+ const link = linkContainer[0].firstChild;
272
+
273
+ expect(link instanceof HTMLSpanElement).toBe(true);
274
+ });
275
+
276
+ it.each(["h1", "h2", "h3", "h4", "h5", "h6"])(
277
+ "uses heading type %s",
278
+ headingType => {
279
+ const toggle = jest.fn();
280
+ const { container } = render(
281
+ <Accordion
282
+ onChange={toggle}
283
+ linkElementType={headingType as AccordionLinkElementType}
284
+ >
285
+ Sample text
286
+ </Accordion>,
287
+ );
288
+
289
+ const linkContainer = container.getElementsByClassName(LINK_CLASS_NAME);
290
+ const link = linkContainer[0].firstChild;
291
+
292
+ expect(link instanceof HTMLHeadingElement).toBe(true);
293
+ expect(link?.nodeName).toBe(headingType.toUpperCase());
294
+ },
295
+ );
296
+ });
262
297
  });
@@ -11,17 +11,26 @@ import {
11
11
  useEffect,
12
12
  useState,
13
13
  } from "react";
14
+ import { usePrefersReducedMotion } from "../../hooks/usePrefersReducedMotion";
14
15
  import { ForwardedRefComponent } from "../../types/components";
15
16
  import { DOMProps } from "../../types/dom";
16
17
  import { Flex } from "../Flex";
17
18
  import { Icon } from "../Icon";
18
- import { usePrefersReducedMotion } from "../../hooks/usePrefersReducedMotion";
19
19
 
20
+ export type AccordionLinkElementType =
21
+ | "h1"
22
+ | "h2"
23
+ | "h3"
24
+ | "h4"
25
+ | "h5"
26
+ | "h6"
27
+ | "span";
20
28
  export type AccordionElementType = HTMLDivElement;
21
29
  export type AccordionRef = Ref<AccordionElementType>;
22
30
  export interface AccordionLinkProps extends DOMProps {
23
31
  /** Link text to show accordion content */
24
32
  text?: string;
33
+ elementType?: AccordionLinkElementType;
25
34
  onClick?: () => void;
26
35
  toggle: () => void;
27
36
  headerChildren?: ReactNode;
@@ -30,6 +39,7 @@ export interface AccordionLinkProps extends DOMProps {
30
39
 
31
40
  const AccordionLink = ({
32
41
  text,
42
+ elementType: LinkElement = "span",
33
43
  toggle,
34
44
  onClick,
35
45
  headerChildren,
@@ -41,7 +51,6 @@ const AccordionLink = ({
41
51
  const iconClasses = classNames("mobius-accordion__link-icon", {
42
52
  "--is-open": ariaExpanded,
43
53
  });
44
-
45
54
  const handleOnClick = (): void => {
46
55
  if (onClick) {
47
56
  onClick();
@@ -70,7 +79,9 @@ const AccordionLink = ({
70
79
  tabIndex={0}
71
80
  aria-expanded={!!ariaExpanded}
72
81
  >
73
- <span className="mobius-accordion__link-text">{text}</span>
82
+ <LinkElement className="mobius-accordion__link-text">
83
+ {text}
84
+ </LinkElement>
74
85
  <Icon icon={chevronDown} className={iconClasses} />
75
86
  </div>
76
87
  {headerChildren}
@@ -87,7 +98,7 @@ const AccordionLink = ({
87
98
  tabIndex={0}
88
99
  aria-expanded={!!ariaExpanded}
89
100
  >
90
- <span className="mobius-accordion__link-text">{text}</span>
101
+ <LinkElement className="mobius-accordion__link-text">{text}</LinkElement>
91
102
  <Icon icon={chevronDown} className={iconClasses} />
92
103
  </div>
93
104
  );
@@ -106,6 +117,8 @@ export interface AccordionProps
106
117
  headerPosition?: "top" | "bottom";
107
118
  /** Whether to expand the accordion initially */
108
119
  startOpen?: boolean | undefined;
120
+ /** Semantic heading for title link (default is span) */
121
+ linkElementType?: AccordionLinkElementType;
109
122
  /** Callback that fires each time the accordion is opened */
110
123
  onOpen?: () => void;
111
124
  /** Callback that fires each time the accordion is closed */
@@ -125,6 +138,7 @@ export const Accordion: ForwardedRefComponent<
125
138
  hideText = "See less",
126
139
  headerPosition = "top",
127
140
  startOpen = false,
141
+ linkElementType,
128
142
  onOpen,
129
143
  onClose,
130
144
  onChange = () => {},
@@ -186,6 +200,7 @@ export const Accordion: ForwardedRefComponent<
186
200
  {headerPosition === "top" && (
187
201
  <AccordionLink
188
202
  text={linkText}
203
+ elementType={linkElementType}
189
204
  toggle={handleChange}
190
205
  ariaExpanded={accordionState.open}
191
206
  headerChildren={headerChildren}
@@ -19,8 +19,8 @@
19
19
  position: fixed;
20
20
  display: flex;
21
21
  flex-direction: column;
22
- width: 50%;
23
- max-height: 80%;
22
+ width: min(90%, 600px);
23
+ max-height: 90%;
24
24
  padding: 0;
25
25
  margin: auto;
26
26
  background-color: var(--color-background);
@@ -28,6 +28,10 @@
28
28
  border-radius: var(--radius-1);
29
29
  overflow: hidden;
30
30
 
31
+ @media (min-width: 768px) {
32
+ max-height: 80%;
33
+ }
34
+
31
35
  &:not([open]) {
32
36
  display: none;
33
37
  }
@@ -127,13 +131,27 @@
127
131
  display: grid;
128
132
  grid-template-columns: 1fr auto;
129
133
  justify-content: space-between;
130
- gap: var(--size-sm);
131
- align-items: center;
134
+ gap: var(--size-md);
135
+ align-items: start;
132
136
  padding: var(--size-xs);
133
- padding-left: var(--size-sm);
137
+ padding-inline: var(--size-sm);
134
138
  font-family: var(--font-family);
135
- font-size: var(--font-size-small-title);
139
+ font-size: var(--font-size-4);
136
140
  margin: 0;
141
+
142
+ @media (min-width: 768px) {
143
+ padding: var(--size-sm);
144
+ padding-inline: var(--size-md);
145
+ font-size: var(--font-size-5);
146
+ }
147
+ }
148
+
149
+ .mobius-modal__close {
150
+ /* negative padding to match the padding of the header, so it aligns
151
+ horizontally */
152
+ padding: calc(var(--size-sm) * -1);
153
+ font-size: var(--font-size-4);
154
+ flex-shrink: 0;
137
155
  }
138
156
 
139
157
  .mobius-modal__content {
@@ -142,4 +160,9 @@
142
160
  margin: var(--size-sm) 0;
143
161
  font-family: var(--font-family);
144
162
  overflow-y: auto;
163
+
164
+ @media (min-width: 768px) {
165
+ padding: 0 var(--size-md);
166
+ margin: var(--size-md) 0;
167
+ }
145
168
  }
@@ -159,7 +159,7 @@
159
159
 
160
160
  /* Colours */
161
161
  &.subtle {
162
- background-color: var(--color-background-highlight);
162
+ background-color: var(--color-border-light);
163
163
  }
164
164
 
165
165
  &.grey {
@@ -11,6 +11,7 @@
11
11
  font-size: var(--font-size-regular);
12
12
  font-weight: var(--font-weight-normal);
13
13
  line-height: var(--line-height-normal);
14
+ text-wrap: pretty;
14
15
  }
15
16
 
16
17
  &:where(.--is-h1) {
@@ -20,6 +21,7 @@
20
21
  font-weight: var(--font-weight-bold);
21
22
  line-height: 1.25; /* 40px */
22
23
  margin: 0.35em 0;
24
+ text-wrap: balance;
23
25
  }
24
26
 
25
27
  &:where(.--is-h2) {
@@ -29,6 +31,7 @@
29
31
  font-weight: var(--font-weight-bold);
30
32
  line-height: var(--line-height-tight); /* 1.333, 32px */
31
33
  margin: 0.45em 0;
34
+ text-wrap: balance;
32
35
  }
33
36
 
34
37
  &:where(.--is-h3) {
@@ -38,6 +41,7 @@
38
41
  font-weight: var(--font-weight-bold);
39
42
  line-height: 1.2; /* 24px */
40
43
  margin: 0.5em 0;
44
+ text-wrap: balance;
41
45
  }
42
46
 
43
47
  &:where(.--is-h4) {
@@ -47,6 +51,7 @@
47
51
  font-weight: var(--font-weight-bold);
48
52
  line-height: var(--line-height-normal); /* 1.5, 24px */
49
53
  margin: 0.55em 0;
54
+ text-wrap: balance;
50
55
  }
51
56
 
52
57
  &:where(.--is-span) {