@discourser/design-system 0.15.0 → 0.17.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/dist/chunk-2P7Z5PVP.cjs +2151 -0
- package/dist/chunk-2P7Z5PVP.cjs.map +1 -0
- package/dist/{chunk-QC44JPCA.cjs → chunk-PFWU7QSM.cjs} +777 -15
- package/dist/chunk-PFWU7QSM.cjs.map +1 -0
- package/dist/chunk-QC7LGFM3.js +2099 -0
- package/dist/chunk-QC7LGFM3.js.map +1 -0
- package/dist/{chunk-M7J7WKJY.js → chunk-SNUJBT5R.js} +778 -16
- package/dist/chunk-SNUJBT5R.js.map +1 -0
- package/dist/components/Accordion.figma.d.ts +2 -0
- package/dist/components/Accordion.figma.d.ts.map +1 -0
- package/dist/components/Breadcrumb.d.ts +11 -0
- package/dist/components/Breadcrumb.d.ts.map +1 -0
- package/dist/components/Breadcrumb.figma.d.ts +2 -0
- package/dist/components/Breadcrumb.figma.d.ts.map +1 -0
- package/dist/components/Checkbox.d.ts +6 -6
- package/dist/components/ContentCard/ContentCard.d.ts +13 -0
- package/dist/components/ContentCard/ContentCard.d.ts.map +1 -0
- package/dist/components/ContentCard/ContentCard.figma.d.ts +2 -0
- package/dist/components/ContentCard/ContentCard.figma.d.ts.map +1 -0
- package/dist/components/ContentCard/index.d.ts +2 -0
- package/dist/components/ContentCard/index.d.ts.map +1 -0
- package/dist/components/{Heading.d.ts → Header.d.ts} +3 -3
- package/dist/components/Header.d.ts.map +1 -0
- package/dist/components/Header.figma.d.ts +2 -0
- package/dist/components/Header.figma.d.ts.map +1 -0
- package/dist/components/Icons/AccountIcon.d.ts +6 -0
- package/dist/components/Icons/AccountIcon.d.ts.map +1 -0
- package/dist/components/Icons/ChevronUpIcon.d.ts +6 -0
- package/dist/components/Icons/ChevronUpIcon.d.ts.map +1 -0
- package/dist/components/Icons/ClockIcon.d.ts +6 -0
- package/dist/components/Icons/ClockIcon.d.ts.map +1 -0
- package/dist/components/Icons/DashboardIcon.d.ts +6 -0
- package/dist/components/Icons/DashboardIcon.d.ts.map +1 -0
- package/dist/components/Icons/DiscourserLogo.d.ts +6 -0
- package/dist/components/Icons/DiscourserLogo.d.ts.map +1 -0
- package/dist/components/Icons/DiscourserLogo.figma.d.ts +2 -0
- package/dist/components/Icons/DiscourserLogo.figma.d.ts.map +1 -0
- package/dist/components/Icons/GripDotsVerticalIcon.d.ts +6 -0
- package/dist/components/Icons/GripDotsVerticalIcon.d.ts.map +1 -0
- package/dist/components/Icons/HelpIcon.d.ts +6 -0
- package/dist/components/Icons/HelpIcon.d.ts.map +1 -0
- package/dist/components/Icons/NotebookIcon.d.ts +6 -0
- package/dist/components/Icons/NotebookIcon.d.ts.map +1 -0
- package/dist/components/Icons/RightArrowIcon.d.ts +6 -0
- package/dist/components/Icons/RightArrowIcon.d.ts.map +1 -0
- package/dist/components/Icons/ScenarioIcon.d.ts +6 -0
- package/dist/components/Icons/ScenarioIcon.d.ts.map +1 -0
- package/dist/components/Icons/index.d.ts +11 -0
- package/dist/components/Icons/index.d.ts.map +1 -0
- package/dist/components/NavigationMenu/NavigationMenu.d.ts +3 -0
- package/dist/components/NavigationMenu/NavigationMenu.d.ts.map +1 -0
- package/dist/components/NavigationMenu/NavigationMenu.figma.d.ts +2 -0
- package/dist/components/NavigationMenu/NavigationMenu.figma.d.ts.map +1 -0
- package/dist/components/NavigationMenu/index.d.ts +3 -0
- package/dist/components/NavigationMenu/index.d.ts.map +1 -0
- package/dist/components/NavigationMenu/types.d.ts +25 -0
- package/dist/components/NavigationMenu/types.d.ts.map +1 -0
- package/dist/components/QuickStartPage/QuickStartPage.d.ts +21 -0
- package/dist/components/QuickStartPage/QuickStartPage.d.ts.map +1 -0
- package/dist/components/QuickStartPage/index.d.ts +3 -0
- package/dist/components/QuickStartPage/index.d.ts.map +1 -0
- package/dist/components/ScenarioQueue/AddScenarioDialog.d.ts +16 -0
- package/dist/components/ScenarioQueue/AddScenarioDialog.d.ts.map +1 -0
- package/dist/components/ScenarioQueue/ScenarioCard.d.ts +10 -0
- package/dist/components/ScenarioQueue/ScenarioCard.d.ts.map +1 -0
- package/dist/components/ScenarioQueue/ScenarioCardDraggable.d.ts +15 -0
- package/dist/components/ScenarioQueue/ScenarioCardDraggable.d.ts.map +1 -0
- package/dist/components/ScenarioQueue/ScenarioQueue.d.ts +3 -0
- package/dist/components/ScenarioQueue/ScenarioQueue.d.ts.map +1 -0
- package/dist/components/ScenarioQueue/ScenarioQueue.figma.d.ts +2 -0
- package/dist/components/ScenarioQueue/ScenarioQueue.figma.d.ts.map +1 -0
- package/dist/components/ScenarioQueue/index.d.ts +6 -0
- package/dist/components/ScenarioQueue/index.d.ts.map +1 -0
- package/dist/components/ScenarioQueue/types.d.ts +56 -0
- package/dist/components/ScenarioQueue/types.d.ts.map +1 -0
- package/dist/components/ScenarioSettings/ScenarioSettings.d.ts +3 -0
- package/dist/components/ScenarioSettings/ScenarioSettings.d.ts.map +1 -0
- package/dist/components/ScenarioSettings/ScenarioSettings.figma.d.ts +2 -0
- package/dist/components/ScenarioSettings/ScenarioSettings.figma.d.ts.map +1 -0
- package/dist/components/ScenarioSettings/index.d.ts +3 -0
- package/dist/components/ScenarioSettings/index.d.ts.map +1 -0
- package/dist/components/ScenarioSettings/types.d.ts +54 -0
- package/dist/components/ScenarioSettings/types.d.ts.map +1 -0
- package/dist/components/index.cjs +110 -34
- package/dist/components/index.d.ts +16 -1
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/index.js +1 -1
- package/dist/figma-codex/config.d.ts +8 -0
- package/dist/figma-codex/config.d.ts.map +1 -0
- package/dist/figma-codex/fixtures/CompoundComponent/CompoundComponent.d.ts +6 -0
- package/dist/figma-codex/fixtures/CompoundComponent/CompoundComponent.d.ts.map +1 -0
- package/dist/figma-codex/fixtures/CompoundComponent/index.d.ts +2 -0
- package/dist/figma-codex/fixtures/CompoundComponent/index.d.ts.map +1 -0
- package/dist/figma-codex/fixtures/CompoundComponent.figma.d.ts +2 -0
- package/dist/figma-codex/fixtures/CompoundComponent.figma.d.ts.map +1 -0
- package/dist/figma-codex/fixtures/SimpleComponent.d.ts +8 -0
- package/dist/figma-codex/fixtures/SimpleComponent.d.ts.map +1 -0
- package/dist/figma-codex/fixtures/SimpleComponent.figma.d.ts +2 -0
- package/dist/figma-codex/fixtures/SimpleComponent.figma.d.ts.map +1 -0
- package/dist/figma-codex/generate.d.ts +6 -0
- package/dist/figma-codex/generate.d.ts.map +1 -0
- package/dist/figma-codex/parser.d.ts +18 -0
- package/dist/figma-codex/parser.d.ts.map +1 -0
- package/dist/figma-codex/resolver.d.ts +5 -0
- package/dist/figma-codex/resolver.d.ts.map +1 -0
- package/dist/figma-codex/schema.d.ts +60 -0
- package/dist/figma-codex/schema.d.ts.map +1 -0
- package/dist/figma-codex/writer.d.ts +8 -0
- package/dist/figma-codex/writer.d.ts.map +1 -0
- package/dist/figma-codex.json +373 -0
- package/dist/index.cjs +114 -38
- package/dist/index.js +2 -2
- package/dist/preset/index.cjs +2 -2
- package/dist/preset/index.d.ts.map +1 -1
- package/dist/preset/index.js +1 -1
- package/dist/preset/recipes/accordion.d.ts.map +1 -1
- package/dist/preset/recipes/avatar.d.ts.map +1 -1
- package/dist/preset/recipes/breadcrumb.d.ts +2 -0
- package/dist/preset/recipes/breadcrumb.d.ts.map +1 -0
- package/dist/preset/recipes/checkbox.d.ts.map +1 -1
- package/dist/preset/recipes/content-card.d.ts +2 -0
- package/dist/preset/recipes/content-card.d.ts.map +1 -0
- package/dist/preset/recipes/field.d.ts.map +1 -1
- package/dist/preset/recipes/index.d.ts +7 -0
- package/dist/preset/recipes/index.d.ts.map +1 -1
- package/dist/preset/recipes/navigation-menu.d.ts +2 -0
- package/dist/preset/recipes/navigation-menu.d.ts.map +1 -0
- package/dist/preset/recipes/progress.d.ts.map +1 -1
- package/dist/preset/recipes/radio-group.d.ts.map +1 -1
- package/dist/preset/recipes/scenario-card.d.ts +2 -0
- package/dist/preset/recipes/scenario-card.d.ts.map +1 -0
- package/dist/preset/recipes/scenario-queue.d.ts +2 -0
- package/dist/preset/recipes/scenario-queue.d.ts.map +1 -0
- package/dist/preset/recipes/scenario-settings.d.ts +2 -0
- package/dist/preset/recipes/scenario-settings.d.ts.map +1 -0
- package/dist/preset/recipes/steps.d.ts.map +1 -1
- package/dist/preset/recipes/toast.d.ts.map +1 -1
- package/dist/preset/recipes/tooltip.d.ts.map +1 -1
- package/dist/preset/semantic-tokens.d.ts +12 -0
- package/dist/preset/semantic-tokens.d.ts.map +1 -1
- package/package.json +35 -2
- package/src/components/Accordion.figma.tsx +20 -0
- package/src/components/Breadcrumb.figma.tsx +18 -0
- package/src/components/Breadcrumb.tsx +52 -0
- package/src/components/ContentCard/ContentCard.figma.tsx +21 -0
- package/src/components/ContentCard/ContentCard.test.tsx +197 -0
- package/src/components/ContentCard/ContentCard.tsx +19 -0
- package/src/components/ContentCard/index.ts +13 -0
- package/src/components/Header.figma.tsx +25 -0
- package/src/components/{Heading.tsx → Header.tsx} +2 -2
- package/src/components/Icons/AccountIcon.tsx +26 -0
- package/src/components/Icons/ChevronUpIcon.tsx +24 -0
- package/src/components/Icons/ClockIcon.tsx +40 -0
- package/src/components/Icons/DashboardIcon.tsx +47 -0
- package/src/components/Icons/Discourser-Logo.svg +14 -0
- package/src/components/Icons/DiscourserLogo.figma.tsx +10 -0
- package/src/components/Icons/DiscourserLogo.tsx +72 -0
- package/src/components/Icons/GripDotsVerticalIcon.tsx +26 -0
- package/src/components/Icons/HelpIcon.tsx +26 -0
- package/src/components/Icons/NotebookIcon.tsx +26 -0
- package/src/components/Icons/RightArrowIcon.tsx +23 -0
- package/src/components/Icons/ScenarioIcon.tsx +26 -0
- package/src/components/Icons/index.ts +13 -0
- package/src/components/NavigationMenu/NavigationMenu.figma.tsx +26 -0
- package/src/components/NavigationMenu/NavigationMenu.test.tsx +524 -0
- package/src/components/NavigationMenu/NavigationMenu.tsx +102 -0
- package/src/components/NavigationMenu/index.ts +2 -0
- package/src/components/NavigationMenu/types.ts +27 -0
- package/src/components/QuickStartPage/QuickStartPage.tsx +627 -0
- package/src/components/QuickStartPage/index.ts +2 -0
- package/src/components/ScenarioQueue/AddScenarioDialog.tsx +137 -0
- package/src/components/ScenarioQueue/ScenarioCard.tsx +120 -0
- package/src/components/ScenarioQueue/ScenarioCardDraggable.tsx +41 -0
- package/src/components/ScenarioQueue/ScenarioQueue.figma.tsx +37 -0
- package/src/components/ScenarioQueue/ScenarioQueue.test.tsx +398 -0
- package/src/components/ScenarioQueue/ScenarioQueue.tsx +162 -0
- package/src/components/ScenarioQueue/index.ts +11 -0
- package/src/components/ScenarioQueue/types.ts +86 -0
- package/src/components/ScenarioSettings/ScenarioSettings.figma.tsx +12 -0
- package/src/components/ScenarioSettings/ScenarioSettings.test.tsx +406 -0
- package/src/components/ScenarioSettings/ScenarioSettings.tsx +386 -0
- package/src/components/ScenarioSettings/index.ts +11 -0
- package/src/components/ScenarioSettings/types.ts +70 -0
- package/src/components/__tests__/Breadcrumb.test.tsx +94 -0
- package/src/components/index.ts +54 -1
- package/src/figma-codex/README.md +186 -0
- package/src/figma-codex/__tests__/config.test.ts +63 -0
- package/src/figma-codex/__tests__/generate.test.ts +78 -0
- package/src/figma-codex/__tests__/parser.test.ts +138 -0
- package/src/figma-codex/__tests__/resolver.test.ts +196 -0
- package/src/figma-codex/__tests__/writer.test.ts +111 -0
- package/src/figma-codex/config.ts +42 -0
- package/src/figma-codex/fixtures/CompoundComponent/CompoundComponent.tsx +17 -0
- package/src/figma-codex/fixtures/CompoundComponent/index.ts +1 -0
- package/src/figma-codex/fixtures/CompoundComponent.figma.tsx +14 -0
- package/src/figma-codex/fixtures/SimpleComponent.figma.tsx +10 -0
- package/src/figma-codex/fixtures/SimpleComponent.tsx +10 -0
- package/src/figma-codex/fixtures/expected-output.json +78 -0
- package/src/figma-codex/generate.ts +106 -0
- package/src/figma-codex/parser.ts +138 -0
- package/src/figma-codex/resolver.ts +280 -0
- package/src/figma-codex/schema.ts +79 -0
- package/src/figma-codex/writer.ts +54 -0
- package/src/preset/index.ts +15 -0
- package/src/preset/recipes/accordion.ts +8 -5
- package/src/preset/recipes/avatar.ts +1 -2
- package/src/preset/recipes/breadcrumb.ts +109 -0
- package/src/preset/recipes/checkbox.ts +1 -2
- package/src/preset/recipes/content-card.ts +124 -0
- package/src/preset/recipes/field.ts +1 -2
- package/src/preset/recipes/index.ts +11 -0
- package/src/preset/recipes/navigation-menu.ts +97 -0
- package/src/preset/recipes/progress.ts +1 -2
- package/src/preset/recipes/radio-group.ts +1 -2
- package/src/preset/recipes/scenario-card.ts +151 -0
- package/src/preset/recipes/scenario-queue.ts +99 -0
- package/src/preset/recipes/scenario-settings.ts +182 -0
- package/src/preset/recipes/steps.ts +1 -2
- package/src/preset/recipes/toast.ts +1 -2
- package/src/preset/recipes/tooltip.ts +1 -2
- package/src/preset/semantic-tokens.ts +4 -0
- package/src/test/setup.ts +15 -0
- package/dist/chunk-F7LHARS4.js +0 -869
- package/dist/chunk-F7LHARS4.js.map +0 -1
- package/dist/chunk-M7J7WKJY.js.map +0 -1
- package/dist/chunk-QC44JPCA.cjs.map +0 -1
- package/dist/chunk-QP4EJI3G.cjs +0 -902
- package/dist/chunk-QP4EJI3G.cjs.map +0 -1
- package/dist/components/Heading.d.ts.map +0 -1
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { ark } from '@ark-ui/react/factory';
|
|
2
|
+
import type { ComponentProps } from 'react';
|
|
3
|
+
import { styled } from 'styled-system/jsx';
|
|
4
|
+
|
|
5
|
+
const StyledSvg = styled(ark.svg);
|
|
6
|
+
|
|
7
|
+
export type HelpIconProps = ComponentProps<typeof StyledSvg>;
|
|
8
|
+
|
|
9
|
+
export const HelpIcon = (props: HelpIconProps) => (
|
|
10
|
+
<StyledSvg
|
|
11
|
+
viewBox="0 0 20 20"
|
|
12
|
+
fill="none"
|
|
13
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
14
|
+
width="1em"
|
|
15
|
+
height="1em"
|
|
16
|
+
{...props}
|
|
17
|
+
>
|
|
18
|
+
<path
|
|
19
|
+
d="M9.16663 10H10.8333C11.2753 10 11.6992 9.82443 12.0118 9.51186C12.3244 9.1993 12.5 8.77538 12.5 8.33335C12.5 7.89133 12.3244 7.4674 12.0118 7.15484C11.6992 6.84228 11.2753 6.66669 10.8333 6.66669H8.33329C7.83329 6.66669 7.41663 6.83335 7.16663 7.16669L2.49996 11.6667M5.83329 15L7.16663 13.8334C7.41663 13.5 7.83329 13.3334 8.33329 13.3334H11.6666C12.5833 13.3334 13.4166 13 14 12.3334L17.8333 8.66671C18.1549 8.36282 18.3426 7.94362 18.3551 7.50135C18.3676 7.05908 18.2039 6.62995 17.9 6.30838C17.5961 5.9868 17.1769 5.79912 16.7346 5.78662C16.2923 5.77411 15.8632 5.93782 15.5416 6.24171L12.0416 9.49171M1.66663 10.8334L6.66663 15.8334"
|
|
20
|
+
stroke="currentColor"
|
|
21
|
+
strokeWidth="2"
|
|
22
|
+
strokeLinecap="round"
|
|
23
|
+
strokeLinejoin="round"
|
|
24
|
+
/>
|
|
25
|
+
</StyledSvg>
|
|
26
|
+
);
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { ark } from '@ark-ui/react/factory';
|
|
2
|
+
import type { ComponentProps } from 'react';
|
|
3
|
+
import { styled } from 'styled-system/jsx';
|
|
4
|
+
|
|
5
|
+
const StyledSvg = styled(ark.svg);
|
|
6
|
+
|
|
7
|
+
export type NotebookIconProps = ComponentProps<typeof StyledSvg>;
|
|
8
|
+
|
|
9
|
+
export const NotebookIcon = (props: NotebookIconProps) => (
|
|
10
|
+
<StyledSvg
|
|
11
|
+
viewBox="0 0 24 24"
|
|
12
|
+
fill="none"
|
|
13
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
14
|
+
width="1em"
|
|
15
|
+
height="1em"
|
|
16
|
+
{...props}
|
|
17
|
+
>
|
|
18
|
+
<path
|
|
19
|
+
d="M12 7.83333C12 6.94928 11.6488 6.10143 11.0236 5.47631C10.3985 4.85119 9.55068 4.5 8.66663 4.5H3.66663V17H9.49996C10.163 17 10.7989 17.2634 11.2677 17.7322C11.7366 18.2011 12 18.837 12 19.5M12 7.83333V19.5M12 7.83333C12 6.94928 12.3511 6.10143 12.9763 5.47631C13.6014 4.85119 14.4492 4.5 15.3333 4.5H20.3333V17H14.5C13.8369 17 13.201 17.2634 12.7322 17.7322C12.2634 18.2011 12 18.837 12 19.5M6.99996 8.66667H8.66663M6.99996 12H8.66663M15.3333 8.66667H17M15.3333 12H17"
|
|
20
|
+
stroke="currentColor"
|
|
21
|
+
strokeWidth="2"
|
|
22
|
+
strokeLinecap="round"
|
|
23
|
+
strokeLinejoin="round"
|
|
24
|
+
/>
|
|
25
|
+
</StyledSvg>
|
|
26
|
+
);
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { ark } from '@ark-ui/react/factory';
|
|
2
|
+
import type { ComponentProps } from 'react';
|
|
3
|
+
import { styled } from 'styled-system/jsx';
|
|
4
|
+
|
|
5
|
+
const StyledSvg = styled(ark.svg);
|
|
6
|
+
|
|
7
|
+
export type RightArrowIconProps = ComponentProps<typeof StyledSvg>;
|
|
8
|
+
|
|
9
|
+
export const RightArrowIcon = (props: RightArrowIconProps) => (
|
|
10
|
+
<StyledSvg
|
|
11
|
+
viewBox="0 0 19 19"
|
|
12
|
+
fill="none"
|
|
13
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
14
|
+
width="1em"
|
|
15
|
+
height="1em"
|
|
16
|
+
{...props}
|
|
17
|
+
>
|
|
18
|
+
<path
|
|
19
|
+
d="M15.9172 13.2266H3.74219V11.2266H15.9172L10.3172 5.62656L11.7422 4.22656L19.7422 12.2266L11.7422 20.2266L10.3172 18.8266L15.9172 13.2266Z"
|
|
20
|
+
fill="currentColor"
|
|
21
|
+
/>
|
|
22
|
+
</StyledSvg>
|
|
23
|
+
);
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { ark } from '@ark-ui/react/factory';
|
|
2
|
+
import type { ComponentProps } from 'react';
|
|
3
|
+
import { styled } from 'styled-system/jsx';
|
|
4
|
+
|
|
5
|
+
const StyledSvg = styled(ark.svg);
|
|
6
|
+
|
|
7
|
+
export type ScenarioIconProps = ComponentProps<typeof StyledSvg>;
|
|
8
|
+
|
|
9
|
+
export const ScenarioIcon = (props: ScenarioIconProps) => (
|
|
10
|
+
<StyledSvg
|
|
11
|
+
viewBox="0 0 24 24"
|
|
12
|
+
fill="none"
|
|
13
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
14
|
+
width="1em"
|
|
15
|
+
height="1em"
|
|
16
|
+
{...props}
|
|
17
|
+
>
|
|
18
|
+
<path
|
|
19
|
+
d="M19.5 14.5C19.5 14.942 19.3244 15.366 19.0118 15.6785C18.6993 15.9911 18.2754 16.1667 17.8333 16.1667H7.83333L4.5 19.5V6.16667C4.5 5.72464 4.67559 5.30072 4.98816 4.98816C5.30072 4.67559 5.72464 4.5 6.16667 4.5H17.8333C18.2754 4.5 18.6993 4.67559 19.0118 4.98816C19.3244 5.30072 19.5 5.72464 19.5 6.16667V14.5Z"
|
|
20
|
+
stroke="currentColor"
|
|
21
|
+
strokeWidth="2"
|
|
22
|
+
strokeLinecap="round"
|
|
23
|
+
strokeLinejoin="round"
|
|
24
|
+
/>
|
|
25
|
+
</StyledSvg>
|
|
26
|
+
);
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export {
|
|
2
|
+
GripDotsVerticalIcon,
|
|
3
|
+
type GripDotsVerticalIconProps,
|
|
4
|
+
} from './GripDotsVerticalIcon';
|
|
5
|
+
export { ClockIcon, type ClockIconProps } from './ClockIcon';
|
|
6
|
+
export { DashboardIcon, type DashboardIconProps } from './DashboardIcon';
|
|
7
|
+
export { NotebookIcon, type NotebookIconProps } from './NotebookIcon';
|
|
8
|
+
export { ScenarioIcon, type ScenarioIconProps } from './ScenarioIcon';
|
|
9
|
+
export { HelpIcon, type HelpIconProps } from './HelpIcon';
|
|
10
|
+
export { AccountIcon, type AccountIconProps } from './AccountIcon';
|
|
11
|
+
export { RightArrowIcon, type RightArrowIconProps } from './RightArrowIcon';
|
|
12
|
+
export { ChevronUpIcon, type ChevronUpIconProps } from './ChevronUpIcon';
|
|
13
|
+
export { DiscourserLogo, type DiscourserLogoProps } from './DiscourserLogo';
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import figma from '@figma/code-connect';
|
|
2
|
+
import { NavigationMenu } from './NavigationMenu';
|
|
3
|
+
|
|
4
|
+
figma.connect(
|
|
5
|
+
NavigationMenu,
|
|
6
|
+
'https://www.figma.com/design/GaHmFfmvO4loUzuZS4TgEz/Discourser.AI--V1?node-id=38-8485',
|
|
7
|
+
{
|
|
8
|
+
example: () => (
|
|
9
|
+
<NavigationMenu
|
|
10
|
+
sections={[
|
|
11
|
+
{
|
|
12
|
+
value: 'dashboard',
|
|
13
|
+
title: 'Dashboard',
|
|
14
|
+
icon: null,
|
|
15
|
+
items: [
|
|
16
|
+
{ label: 'Quick Start', href: '/dashboard/quick-start' },
|
|
17
|
+
{ label: 'Progress', href: '/dashboard/progress' },
|
|
18
|
+
],
|
|
19
|
+
},
|
|
20
|
+
]}
|
|
21
|
+
defaultOpenSections={['dashboard']}
|
|
22
|
+
activeHref="/dashboard/quick-start"
|
|
23
|
+
/>
|
|
24
|
+
),
|
|
25
|
+
},
|
|
26
|
+
);
|
|
@@ -0,0 +1,524 @@
|
|
|
1
|
+
/* global describe, it, expect, vi */
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { render, screen } from '@testing-library/react';
|
|
4
|
+
import userEvent from '@testing-library/user-event';
|
|
5
|
+
import { axe } from 'jest-axe';
|
|
6
|
+
import { NavigationMenu } from './NavigationMenu';
|
|
7
|
+
import type { NavSection } from './types';
|
|
8
|
+
|
|
9
|
+
// ── Mock icon ─────────────────────────────────────────────────────────────────
|
|
10
|
+
// Avoids importing real DS icon SVGs — keeps tests free of styled-system deps.
|
|
11
|
+
|
|
12
|
+
const MockIcon = () =>
|
|
13
|
+
React.createElement('svg', { 'data-testid': 'section-icon' });
|
|
14
|
+
|
|
15
|
+
// ── Fixtures ──────────────────────────────────────────────────────────────────
|
|
16
|
+
// Mirrors the mock data in NavigationMenu.stories.tsx
|
|
17
|
+
|
|
18
|
+
const MOCK_SECTIONS: NavSection[] = [
|
|
19
|
+
{
|
|
20
|
+
value: 'dashboard',
|
|
21
|
+
title: 'Dashboard',
|
|
22
|
+
icon: React.createElement(MockIcon),
|
|
23
|
+
items: [
|
|
24
|
+
{ label: 'Quick Start', href: '/dashboard/quick-start' },
|
|
25
|
+
{ label: 'Resume Session', href: '/dashboard/resume-session' },
|
|
26
|
+
{ label: 'Progress', href: '/dashboard/progress' },
|
|
27
|
+
],
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
value: 'scenarios',
|
|
31
|
+
title: 'Scenarios',
|
|
32
|
+
icon: React.createElement(MockIcon),
|
|
33
|
+
items: [
|
|
34
|
+
{ label: 'MyQueue', href: '/scenarios/my-queue' },
|
|
35
|
+
{ label: 'Conversation Studio', href: '/scenarios/conversation-studio' },
|
|
36
|
+
],
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
value: 'help',
|
|
40
|
+
title: 'Help',
|
|
41
|
+
icon: React.createElement(MockIcon),
|
|
42
|
+
items: [
|
|
43
|
+
{ label: 'How it Works', href: '/help/how-it-works' },
|
|
44
|
+
{ label: 'Contact Support', href: '/help/contact-support' },
|
|
45
|
+
],
|
|
46
|
+
},
|
|
47
|
+
];
|
|
48
|
+
|
|
49
|
+
// ── Tests ─────────────────────────────────────────────────────────────────────
|
|
50
|
+
|
|
51
|
+
describe('NavigationMenu', () => {
|
|
52
|
+
// ── Rendering ───────────────────────────────────────────────────────────────
|
|
53
|
+
|
|
54
|
+
describe('Rendering', () => {
|
|
55
|
+
it('renders a <nav> with the provided aria-label', () => {
|
|
56
|
+
render(
|
|
57
|
+
<NavigationMenu
|
|
58
|
+
sections={MOCK_SECTIONS}
|
|
59
|
+
ariaLabel="Dashboard navigation"
|
|
60
|
+
/>,
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
expect(
|
|
64
|
+
screen.getByRole('navigation', { name: 'Dashboard navigation' }),
|
|
65
|
+
).toBeInTheDocument();
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('defaults ariaLabel to "Navigation" when not provided', () => {
|
|
69
|
+
render(<NavigationMenu sections={MOCK_SECTIONS} />);
|
|
70
|
+
|
|
71
|
+
expect(
|
|
72
|
+
screen.getByRole('navigation', { name: 'Navigation' }),
|
|
73
|
+
).toBeInTheDocument();
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it('renders one trigger button per section', () => {
|
|
77
|
+
render(<NavigationMenu sections={MOCK_SECTIONS} />);
|
|
78
|
+
|
|
79
|
+
expect(
|
|
80
|
+
screen.getByRole('button', { name: /Dashboard/i }),
|
|
81
|
+
).toBeInTheDocument();
|
|
82
|
+
expect(
|
|
83
|
+
screen.getByRole('button', { name: /Scenarios/i }),
|
|
84
|
+
).toBeInTheDocument();
|
|
85
|
+
expect(screen.getByRole('button', { name: /Help/i })).toBeInTheDocument();
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it('renders icons for each section', () => {
|
|
89
|
+
render(<NavigationMenu sections={MOCK_SECTIONS} />);
|
|
90
|
+
|
|
91
|
+
expect(screen.getAllByTestId('section-icon')).toHaveLength(
|
|
92
|
+
MOCK_SECTIONS.length,
|
|
93
|
+
);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it('renders nav items for sections that are open by default', () => {
|
|
97
|
+
render(
|
|
98
|
+
<NavigationMenu
|
|
99
|
+
sections={MOCK_SECTIONS}
|
|
100
|
+
defaultOpenSections={['dashboard']}
|
|
101
|
+
/>,
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
expect(
|
|
105
|
+
screen.getByRole('link', { name: 'Quick Start' }),
|
|
106
|
+
).toBeInTheDocument();
|
|
107
|
+
expect(
|
|
108
|
+
screen.getByRole('link', { name: 'Resume Session' }),
|
|
109
|
+
).toBeInTheDocument();
|
|
110
|
+
expect(
|
|
111
|
+
screen.getByRole('link', { name: 'Progress' }),
|
|
112
|
+
).toBeInTheDocument();
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it('does not render items for sections that are closed by default', () => {
|
|
116
|
+
render(
|
|
117
|
+
<NavigationMenu
|
|
118
|
+
sections={MOCK_SECTIONS}
|
|
119
|
+
defaultOpenSections={['dashboard']}
|
|
120
|
+
/>,
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
// Scenarios section is closed — its items are not in the DOM
|
|
124
|
+
expect(
|
|
125
|
+
screen.queryByRole('link', { name: 'MyQueue' }),
|
|
126
|
+
).not.toBeInTheDocument();
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
// ── Expand / Collapse ───────────────────────────────────────────────────────
|
|
131
|
+
|
|
132
|
+
describe('Expand / Collapse', () => {
|
|
133
|
+
it('closed section trigger has aria-expanded="false"', () => {
|
|
134
|
+
render(
|
|
135
|
+
<NavigationMenu sections={MOCK_SECTIONS} defaultOpenSections={[]} />,
|
|
136
|
+
);
|
|
137
|
+
|
|
138
|
+
const trigger = screen.getByRole('button', { name: /Dashboard/i });
|
|
139
|
+
expect(trigger).toHaveAttribute('aria-expanded', 'false');
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
it('open section trigger has aria-expanded="true"', () => {
|
|
143
|
+
render(
|
|
144
|
+
<NavigationMenu
|
|
145
|
+
sections={MOCK_SECTIONS}
|
|
146
|
+
defaultOpenSections={['dashboard']}
|
|
147
|
+
/>,
|
|
148
|
+
);
|
|
149
|
+
|
|
150
|
+
const trigger = screen.getByRole('button', { name: /Dashboard/i });
|
|
151
|
+
expect(trigger).toHaveAttribute('aria-expanded', 'true');
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
it('clicking a closed section opens it and shows its items', async () => {
|
|
155
|
+
const user = userEvent.setup();
|
|
156
|
+
render(
|
|
157
|
+
<NavigationMenu sections={MOCK_SECTIONS} defaultOpenSections={[]} />,
|
|
158
|
+
);
|
|
159
|
+
|
|
160
|
+
const dashboardTrigger = screen.getByRole('button', {
|
|
161
|
+
name: /Dashboard/i,
|
|
162
|
+
});
|
|
163
|
+
await user.click(dashboardTrigger);
|
|
164
|
+
|
|
165
|
+
expect(dashboardTrigger).toHaveAttribute('aria-expanded', 'true');
|
|
166
|
+
expect(
|
|
167
|
+
screen.getByRole('link', { name: 'Quick Start' }),
|
|
168
|
+
).toBeInTheDocument();
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
it('clicking an open section closes it', async () => {
|
|
172
|
+
const user = userEvent.setup();
|
|
173
|
+
render(
|
|
174
|
+
<NavigationMenu
|
|
175
|
+
sections={MOCK_SECTIONS}
|
|
176
|
+
defaultOpenSections={['dashboard']}
|
|
177
|
+
/>,
|
|
178
|
+
);
|
|
179
|
+
|
|
180
|
+
const dashboardTrigger = screen.getByRole('button', {
|
|
181
|
+
name: /Dashboard/i,
|
|
182
|
+
});
|
|
183
|
+
await user.click(dashboardTrigger);
|
|
184
|
+
|
|
185
|
+
expect(dashboardTrigger).toHaveAttribute('aria-expanded', 'false');
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
it('multiple sections can be open simultaneously', async () => {
|
|
189
|
+
const user = userEvent.setup();
|
|
190
|
+
render(
|
|
191
|
+
<NavigationMenu
|
|
192
|
+
sections={MOCK_SECTIONS}
|
|
193
|
+
defaultOpenSections={['dashboard']}
|
|
194
|
+
/>,
|
|
195
|
+
);
|
|
196
|
+
|
|
197
|
+
// Open Scenarios section too
|
|
198
|
+
await user.click(screen.getByRole('button', { name: /Scenarios/i }));
|
|
199
|
+
|
|
200
|
+
expect(
|
|
201
|
+
screen.getByRole('button', { name: /Dashboard/i }),
|
|
202
|
+
).toHaveAttribute('aria-expanded', 'true');
|
|
203
|
+
expect(
|
|
204
|
+
screen.getByRole('button', { name: /Scenarios/i }),
|
|
205
|
+
).toHaveAttribute('aria-expanded', 'true');
|
|
206
|
+
expect(
|
|
207
|
+
screen.getByRole('link', { name: 'Quick Start' }),
|
|
208
|
+
).toBeInTheDocument();
|
|
209
|
+
expect(screen.getByRole('link', { name: 'MyQueue' })).toBeInTheDocument();
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
it('all sections expand correctly when defaultOpenSections covers all values', () => {
|
|
213
|
+
render(
|
|
214
|
+
<NavigationMenu
|
|
215
|
+
sections={MOCK_SECTIONS}
|
|
216
|
+
defaultOpenSections={['dashboard', 'scenarios', 'help']}
|
|
217
|
+
/>,
|
|
218
|
+
);
|
|
219
|
+
|
|
220
|
+
expect(
|
|
221
|
+
screen.getByRole('link', { name: 'Quick Start' }),
|
|
222
|
+
).toBeInTheDocument();
|
|
223
|
+
expect(screen.getByRole('link', { name: 'MyQueue' })).toBeInTheDocument();
|
|
224
|
+
expect(
|
|
225
|
+
screen.getByRole('link', { name: 'How it Works' }),
|
|
226
|
+
).toBeInTheDocument();
|
|
227
|
+
});
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
// ── Active Item ─────────────────────────────────────────────────────────────
|
|
231
|
+
|
|
232
|
+
describe('Active Item', () => {
|
|
233
|
+
it('active link has aria-current="page"', () => {
|
|
234
|
+
render(
|
|
235
|
+
<NavigationMenu
|
|
236
|
+
sections={MOCK_SECTIONS}
|
|
237
|
+
defaultOpenSections={['dashboard']}
|
|
238
|
+
activeHref="/dashboard/quick-start"
|
|
239
|
+
/>,
|
|
240
|
+
);
|
|
241
|
+
|
|
242
|
+
const activeLink = screen.getByRole('link', { name: 'Quick Start' });
|
|
243
|
+
expect(activeLink).toHaveAttribute('aria-current', 'page');
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
it('active link has data-active="true"', () => {
|
|
247
|
+
render(
|
|
248
|
+
<NavigationMenu
|
|
249
|
+
sections={MOCK_SECTIONS}
|
|
250
|
+
defaultOpenSections={['dashboard']}
|
|
251
|
+
activeHref="/dashboard/quick-start"
|
|
252
|
+
/>,
|
|
253
|
+
);
|
|
254
|
+
|
|
255
|
+
const activeLink = screen.getByRole('link', { name: 'Quick Start' });
|
|
256
|
+
expect(activeLink).toHaveAttribute('data-active', 'true');
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
it('non-active links do not have aria-current', () => {
|
|
260
|
+
render(
|
|
261
|
+
<NavigationMenu
|
|
262
|
+
sections={MOCK_SECTIONS}
|
|
263
|
+
defaultOpenSections={['dashboard']}
|
|
264
|
+
activeHref="/dashboard/quick-start"
|
|
265
|
+
/>,
|
|
266
|
+
);
|
|
267
|
+
|
|
268
|
+
const inactiveLink = screen.getByRole('link', { name: 'Resume Session' });
|
|
269
|
+
expect(inactiveLink).not.toHaveAttribute('aria-current');
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
it('non-active links do not have data-active', () => {
|
|
273
|
+
render(
|
|
274
|
+
<NavigationMenu
|
|
275
|
+
sections={MOCK_SECTIONS}
|
|
276
|
+
defaultOpenSections={['dashboard']}
|
|
277
|
+
activeHref="/dashboard/quick-start"
|
|
278
|
+
/>,
|
|
279
|
+
);
|
|
280
|
+
|
|
281
|
+
const inactiveLink = screen.getByRole('link', { name: 'Resume Session' });
|
|
282
|
+
expect(inactiveLink).not.toHaveAttribute('data-active');
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
it('only the matching href is marked active', () => {
|
|
286
|
+
render(
|
|
287
|
+
<NavigationMenu
|
|
288
|
+
sections={MOCK_SECTIONS}
|
|
289
|
+
defaultOpenSections={['dashboard', 'scenarios']}
|
|
290
|
+
activeHref="/dashboard/progress"
|
|
291
|
+
/>,
|
|
292
|
+
);
|
|
293
|
+
|
|
294
|
+
const links = screen.getAllByRole('link');
|
|
295
|
+
const activeLinks = links.filter((l) => l.hasAttribute('aria-current'));
|
|
296
|
+
expect(activeLinks).toHaveLength(1);
|
|
297
|
+
expect(activeLinks[0]).toHaveAccessibleName('Progress');
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
it('no link is marked active when activeHref does not match any item', () => {
|
|
301
|
+
render(
|
|
302
|
+
<NavigationMenu
|
|
303
|
+
sections={MOCK_SECTIONS}
|
|
304
|
+
defaultOpenSections={['dashboard']}
|
|
305
|
+
activeHref="/unmatched/path"
|
|
306
|
+
/>,
|
|
307
|
+
);
|
|
308
|
+
|
|
309
|
+
const links = screen.getAllByRole('link');
|
|
310
|
+
for (const link of links) {
|
|
311
|
+
expect(link).not.toHaveAttribute('aria-current');
|
|
312
|
+
}
|
|
313
|
+
});
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
// ── Navigation (onNavigate) ─────────────────────────────────────────────────
|
|
317
|
+
|
|
318
|
+
describe('Navigation', () => {
|
|
319
|
+
it('clicking a nav item calls onNavigate with the correct href', async () => {
|
|
320
|
+
const user = userEvent.setup();
|
|
321
|
+
const onNavigate = vi.fn();
|
|
322
|
+
|
|
323
|
+
render(
|
|
324
|
+
<NavigationMenu
|
|
325
|
+
sections={MOCK_SECTIONS}
|
|
326
|
+
defaultOpenSections={['dashboard']}
|
|
327
|
+
onNavigate={onNavigate}
|
|
328
|
+
/>,
|
|
329
|
+
);
|
|
330
|
+
|
|
331
|
+
await user.click(screen.getByRole('link', { name: 'Quick Start' }));
|
|
332
|
+
|
|
333
|
+
expect(onNavigate).toHaveBeenCalledWith('/dashboard/quick-start');
|
|
334
|
+
expect(onNavigate).toHaveBeenCalledTimes(1);
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
it("clicking different items calls onNavigate with each item's href", async () => {
|
|
338
|
+
const user = userEvent.setup();
|
|
339
|
+
const onNavigate = vi.fn();
|
|
340
|
+
|
|
341
|
+
render(
|
|
342
|
+
<NavigationMenu
|
|
343
|
+
sections={MOCK_SECTIONS}
|
|
344
|
+
defaultOpenSections={['dashboard']}
|
|
345
|
+
onNavigate={onNavigate}
|
|
346
|
+
/>,
|
|
347
|
+
);
|
|
348
|
+
|
|
349
|
+
await user.click(screen.getByRole('link', { name: 'Quick Start' }));
|
|
350
|
+
await user.click(screen.getByRole('link', { name: 'Resume Session' }));
|
|
351
|
+
|
|
352
|
+
expect(onNavigate).toHaveBeenNthCalledWith(1, '/dashboard/quick-start');
|
|
353
|
+
expect(onNavigate).toHaveBeenNthCalledWith(
|
|
354
|
+
2,
|
|
355
|
+
'/dashboard/resume-session',
|
|
356
|
+
);
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
it('default link href attribute matches the nav item href', () => {
|
|
360
|
+
render(
|
|
361
|
+
<NavigationMenu
|
|
362
|
+
sections={MOCK_SECTIONS}
|
|
363
|
+
defaultOpenSections={['dashboard']}
|
|
364
|
+
/>,
|
|
365
|
+
);
|
|
366
|
+
|
|
367
|
+
const link = screen.getByRole('link', { name: 'Quick Start' });
|
|
368
|
+
expect(link).toHaveAttribute('href', '/dashboard/quick-start');
|
|
369
|
+
});
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
// ── Custom Link Renderer ────────────────────────────────────────────────────
|
|
373
|
+
|
|
374
|
+
describe('Custom Link Renderer', () => {
|
|
375
|
+
it('renders the element returned by renderLink', () => {
|
|
376
|
+
render(
|
|
377
|
+
<NavigationMenu
|
|
378
|
+
sections={MOCK_SECTIONS}
|
|
379
|
+
defaultOpenSections={['dashboard']}
|
|
380
|
+
renderLink={({ href, children, className }) =>
|
|
381
|
+
React.createElement(
|
|
382
|
+
'a',
|
|
383
|
+
{ href, className, 'data-custom': 'true' },
|
|
384
|
+
children,
|
|
385
|
+
)
|
|
386
|
+
}
|
|
387
|
+
/>,
|
|
388
|
+
);
|
|
389
|
+
|
|
390
|
+
const link = screen.getByRole('link', { name: 'Quick Start' });
|
|
391
|
+
expect(link).toHaveAttribute('data-custom', 'true');
|
|
392
|
+
});
|
|
393
|
+
|
|
394
|
+
it('passes isActive=true to renderLink for the matching href', () => {
|
|
395
|
+
const renderLink = vi.fn(
|
|
396
|
+
({
|
|
397
|
+
href,
|
|
398
|
+
children,
|
|
399
|
+
}: {
|
|
400
|
+
href: string;
|
|
401
|
+
children: React.ReactNode;
|
|
402
|
+
isActive: boolean;
|
|
403
|
+
className: string;
|
|
404
|
+
}) => React.createElement('a', { href }, children),
|
|
405
|
+
);
|
|
406
|
+
|
|
407
|
+
render(
|
|
408
|
+
<NavigationMenu
|
|
409
|
+
sections={MOCK_SECTIONS}
|
|
410
|
+
defaultOpenSections={['dashboard']}
|
|
411
|
+
activeHref="/dashboard/quick-start"
|
|
412
|
+
renderLink={renderLink}
|
|
413
|
+
/>,
|
|
414
|
+
);
|
|
415
|
+
|
|
416
|
+
const quickStartCall = renderLink.mock.calls.find(
|
|
417
|
+
([props]) => props.href === '/dashboard/quick-start',
|
|
418
|
+
);
|
|
419
|
+
expect(quickStartCall?.[0].isActive).toBe(true);
|
|
420
|
+
});
|
|
421
|
+
|
|
422
|
+
it('passes isActive=false to renderLink for non-matching hrefs', () => {
|
|
423
|
+
const renderLink = vi.fn(
|
|
424
|
+
({
|
|
425
|
+
href,
|
|
426
|
+
children,
|
|
427
|
+
}: {
|
|
428
|
+
href: string;
|
|
429
|
+
children: React.ReactNode;
|
|
430
|
+
isActive: boolean;
|
|
431
|
+
className: string;
|
|
432
|
+
}) => React.createElement('a', { href }, children),
|
|
433
|
+
);
|
|
434
|
+
|
|
435
|
+
render(
|
|
436
|
+
<NavigationMenu
|
|
437
|
+
sections={MOCK_SECTIONS}
|
|
438
|
+
defaultOpenSections={['dashboard']}
|
|
439
|
+
activeHref="/dashboard/quick-start"
|
|
440
|
+
renderLink={renderLink}
|
|
441
|
+
/>,
|
|
442
|
+
);
|
|
443
|
+
|
|
444
|
+
const resumeCall = renderLink.mock.calls.find(
|
|
445
|
+
([props]) => props.href === '/dashboard/resume-session',
|
|
446
|
+
);
|
|
447
|
+
expect(resumeCall?.[0].isActive).toBe(false);
|
|
448
|
+
});
|
|
449
|
+
|
|
450
|
+
it('passes the recipe className string to renderLink', () => {
|
|
451
|
+
let receivedClassName = '';
|
|
452
|
+
|
|
453
|
+
render(
|
|
454
|
+
<NavigationMenu
|
|
455
|
+
sections={MOCK_SECTIONS}
|
|
456
|
+
defaultOpenSections={['dashboard']}
|
|
457
|
+
renderLink={({ href, children, className }) => {
|
|
458
|
+
receivedClassName = className;
|
|
459
|
+
return React.createElement('a', { href }, children);
|
|
460
|
+
}}
|
|
461
|
+
/>,
|
|
462
|
+
);
|
|
463
|
+
|
|
464
|
+
expect(typeof receivedClassName).toBe('string');
|
|
465
|
+
expect(receivedClassName.length).toBeGreaterThan(0);
|
|
466
|
+
});
|
|
467
|
+
});
|
|
468
|
+
|
|
469
|
+
// ── Accessibility ───────────────────────────────────────────────────────────
|
|
470
|
+
|
|
471
|
+
describe('Accessibility', () => {
|
|
472
|
+
it('passes axe audit with all sections open', async () => {
|
|
473
|
+
const { container } = render(
|
|
474
|
+
<NavigationMenu
|
|
475
|
+
sections={MOCK_SECTIONS}
|
|
476
|
+
defaultOpenSections={['dashboard', 'scenarios', 'help']}
|
|
477
|
+
activeHref="/dashboard/quick-start"
|
|
478
|
+
ariaLabel="Dashboard navigation"
|
|
479
|
+
/>,
|
|
480
|
+
);
|
|
481
|
+
|
|
482
|
+
const results = await axe(container);
|
|
483
|
+
expect(results).toHaveNoViolations();
|
|
484
|
+
});
|
|
485
|
+
|
|
486
|
+
it('passes axe audit with all sections closed', async () => {
|
|
487
|
+
const { container } = render(
|
|
488
|
+
<NavigationMenu
|
|
489
|
+
sections={MOCK_SECTIONS}
|
|
490
|
+
defaultOpenSections={[]}
|
|
491
|
+
ariaLabel="Dashboard navigation"
|
|
492
|
+
/>,
|
|
493
|
+
);
|
|
494
|
+
|
|
495
|
+
const results = await axe(container);
|
|
496
|
+
expect(results).toHaveNoViolations();
|
|
497
|
+
});
|
|
498
|
+
|
|
499
|
+
it('section triggers are keyboard-focusable buttons', () => {
|
|
500
|
+
render(<NavigationMenu sections={MOCK_SECTIONS} />);
|
|
501
|
+
|
|
502
|
+
const buttons = screen.getAllByRole('button');
|
|
503
|
+
expect(buttons).toHaveLength(MOCK_SECTIONS.length);
|
|
504
|
+
for (const btn of buttons) {
|
|
505
|
+
expect(btn.tagName).toBe('BUTTON');
|
|
506
|
+
}
|
|
507
|
+
});
|
|
508
|
+
|
|
509
|
+
it('nav items are rendered as links', () => {
|
|
510
|
+
render(
|
|
511
|
+
<NavigationMenu
|
|
512
|
+
sections={MOCK_SECTIONS}
|
|
513
|
+
defaultOpenSections={['dashboard']}
|
|
514
|
+
/>,
|
|
515
|
+
);
|
|
516
|
+
|
|
517
|
+
const links = screen.getAllByRole('link');
|
|
518
|
+
expect(links.length).toBeGreaterThan(0);
|
|
519
|
+
for (const link of links) {
|
|
520
|
+
expect(link.tagName).toBe('A');
|
|
521
|
+
}
|
|
522
|
+
});
|
|
523
|
+
});
|
|
524
|
+
});
|