@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.
- package/CHANGELOG.md +21 -0
- package/dist/cjs/components/Accordion/Accordion.js +6 -5
- package/dist/cjs/components/Accordion/Accordion.js.map +1 -1
- package/dist/cjs/tsconfig.tsbuildinfo +1 -1
- package/dist/esm/components/Accordion/Accordion.js +6 -5
- package/dist/esm/components/Accordion/Accordion.js.map +1 -1
- package/dist/types/components/Accordion/Accordion.d.ts +4 -0
- package/package.json +22 -21
- package/src/components/Accordion/Accordion.css +9 -0
- package/src/components/Accordion/Accordion.stories.tsx +1 -0
- package/src/components/Accordion/Accordion.test.tsx +36 -1
- package/src/components/Accordion/Accordion.tsx +19 -4
- package/src/components/Modal/Modal.css +29 -6
- package/src/components/Segment/Segment.css +1 -1
- package/src/components/Text/Text.css +5 -0
|
@@ -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
|
-
|
|
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(
|
|
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(
|
|
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\";\
|
|
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.
|
|
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.
|
|
48
|
-
"@swc/core": "^1.7.
|
|
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.
|
|
52
|
-
"@testing-library/react": "^16.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.
|
|
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.
|
|
59
|
+
"@types/react": "^18.3.8",
|
|
59
60
|
"@types/react-dom": "^18.3.0",
|
|
60
|
-
"@typescript-eslint/eslint-plugin": "^
|
|
61
|
-
"@typescript-eslint/parser": "^
|
|
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.
|
|
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.
|
|
67
|
-
"eslint-plugin-import": "^2.
|
|
68
|
-
"eslint-plugin-jsx-a11y": "^6.
|
|
69
|
-
"eslint-plugin-no-only-tests": "^3.
|
|
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.
|
|
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.
|
|
82
|
-
"tslib": "^2.
|
|
83
|
-
"typescript": "^5.
|
|
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.
|
|
91
|
-
"@simplybusiness/icons": "^4.14.
|
|
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 {
|
|
@@ -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
|
-
<
|
|
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
|
-
<
|
|
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:
|
|
23
|
-
max-height:
|
|
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-
|
|
131
|
-
align-items:
|
|
134
|
+
gap: var(--size-md);
|
|
135
|
+
align-items: start;
|
|
132
136
|
padding: var(--size-xs);
|
|
133
|
-
padding-
|
|
137
|
+
padding-inline: var(--size-sm);
|
|
134
138
|
font-family: var(--font-family);
|
|
135
|
-
font-size: var(--font-size-
|
|
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
|
}
|
|
@@ -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) {
|