UNPKG

@visa/nova-react

Version:

Visa Product Design System Nova React library. Compatible with React ^19.

359 lines 2.47 MB
{ "library": { "version": "3.0.0", "name": "@visa/nova-react" }, "components": [ { "name": "accordion", "version": "0.0.1", "description": "Sets of vertical headers that reveal or hide the accordion panel.", "libraryId": null, "category": "components", "exampleSections": [ { "name": "Individual accordions", "description": "", "order": 1 }, { "name": "Multi-select accordion groups", "description": "", "order": 2 }, { "name": "Single-select accordion groups", "description": "", "order": 3 }, { "name": "Custom accordion groups", "description": "", "order": 4 } ], "examples": [ { "description": "", "order": 1, "libraryId": null, "componentId": null, "section": "Individual accordions", "url": { "iframe": "components/accordion/collapsed-accordion", "github": "apps/workshop/src/examples/components/accordion/collapsed-accordion.tsx" }, "tags": [ "docs" ], "snippets": { "tsx": "import { VisaChevronDownTiny, VisaChevronRightTiny } from '@visa/nova-icons-react';\nimport { Accordion, AccordionHeading, AccordionPanel, AccordionToggleIcon, Typography } from '@visa/nova-react';\n\nexport const CollapsedAccordion = () => {\n return (\n <Accordion>\n <AccordionHeading buttonSize=\"large\" colorScheme=\"secondary\">\n <AccordionToggleIcon elementClosed={<VisaChevronRightTiny rtl />} elementOpen={<VisaChevronDownTiny />} />\n Accordion title\n </AccordionHeading>\n <AccordionPanel>\n <Typography>This is required text that describes the accordion section in more detail.</Typography>\n </AccordionPanel>\n </Accordion>\n );\n};\n" }, "name": "Default accordion" }, { "description": "", "order": 2, "libraryId": null, "componentId": null, "section": "Individual accordions", "url": { "iframe": "components/accordion/collapsed-disabled-accordion", "github": "apps/workshop/src/examples/components/accordion/collapsed-disabled-accordion.tsx" }, "tags": [ "docs" ], "snippets": { "tsx": "import { VisaChevronDownTiny, VisaChevronRightTiny } from '@visa/nova-icons-react';\nimport { Accordion, AccordionHeading, AccordionPanel, AccordionToggleIcon, Typography } from '@visa/nova-react';\n\n// TIP: Customize this ID, pass it as a prop, or auto-generate it with useId() from @react\nconst id = 'collapsed-disabled-accordion';\n\nexport const CollapsedDisabledAccordion = () => {\n const expanded = false;\n\n return (\n <Accordion id={id} tag=\"div\">\n <AccordionHeading\n aria-controls={`${id}-accordion-panel`}\n aria-expanded={expanded}\n disabled\n buttonSize=\"large\"\n colorScheme=\"secondary\"\n id={`${id}-accordion-header`}\n tag=\"button\"\n >\n <AccordionToggleIcon\n accordionOpen={expanded}\n elementClosed={<VisaChevronRightTiny rtl />}\n elementOpen={<VisaChevronDownTiny />}\n />\n Accordion title\n </AccordionHeading>\n <AccordionPanel aria-hidden={!expanded} id={`${id}-accordion-panel`}>\n <Typography>This is required text that describes the accordion section in more detail.</Typography>\n </AccordionPanel>\n </Accordion>\n );\n};\n" }, "name": "Disabled accordion" }, { "description": "", "order": 3, "libraryId": null, "componentId": null, "section": "Individual accordions", "url": { "iframe": "components/accordion/with-icon-accordion", "github": "apps/workshop/src/examples/components/accordion/with-icon-accordion.tsx" }, "tags": [ "docs" ], "snippets": { "tsx": "import { VisaChevronDownTiny, VisaChevronRightTiny, VisaCloudLow } from '@visa/nova-icons-react';\nimport { Accordion, AccordionHeading, AccordionPanel, AccordionToggleIcon, Typography } from '@visa/nova-react';\n\nexport const WithIconAccordion = () => {\n return (\n <Accordion>\n <AccordionHeading buttonSize=\"large\" colorScheme=\"secondary\">\n <AccordionToggleIcon elementClosed={<VisaChevronRightTiny rtl />} elementOpen={<VisaChevronDownTiny />} />\n <VisaCloudLow />\n Accordion title\n </AccordionHeading>\n <AccordionPanel>\n <Typography>This is required text that describes the accordion section in more detail.</Typography>\n </AccordionPanel>\n </Accordion>\n );\n};\n" }, "name": "Accordion with icon" }, { "description": "", "order": 4, "libraryId": null, "componentId": null, "section": "Individual accordions", "url": { "iframe": "components/accordion/with-badge-accordion", "github": "apps/workshop/src/examples/components/accordion/with-badge-accordion.tsx" }, "tags": [ "docs" ], "snippets": { "tsx": "import { VisaChevronDownTiny, VisaChevronRightTiny, VisaSuccessTiny } from '@visa/nova-icons-react';\nimport {\n Accordion,\n AccordionHeading,\n AccordionPanel,\n AccordionToggleIcon,\n Badge,\n Typography,\n UtilityFragment,\n} from '@visa/nova-react';\n\nexport const WithBadgeAccordion = () => {\n return (\n <Accordion>\n <UtilityFragment vAlignItems=\"center\">\n <AccordionHeading buttonSize=\"large\" colorScheme=\"secondary\">\n {/* TODO: Remove this style tag after nova-styles fix */}\n <AccordionToggleIcon\n elementClosed={<VisaChevronRightTiny rtl />}\n elementOpen={<VisaChevronDownTiny />}\n style={{ alignSelf: 'center' }}\n />\n Accordion title\n <UtilityFragment vMarginLeft=\"auto\">\n <Badge badgeType=\"stable\">\n <VisaSuccessTiny />\n Label\n </Badge>\n </UtilityFragment>\n </AccordionHeading>\n </UtilityFragment>\n <AccordionPanel>\n <Typography>This is required text that describes the accordion section in more detail.</Typography>\n </AccordionPanel>\n </Accordion>\n );\n};\n" }, "name": "Accordion with badge" }, { "description": "", "order": 5, "libraryId": null, "componentId": null, "section": "Individual accordions", "url": { "iframe": "components/accordion/subtle-accordion", "github": "apps/workshop/src/examples/components/accordion/subtle-accordion.tsx" }, "tags": [ "docs" ], "snippets": { "tsx": "import { VisaChevronDownTiny, VisaChevronRightTiny } from '@visa/nova-icons-react';\nimport {\n Accordion,\n AccordionHeading,\n AccordionPanel,\n AccordionToggleIcon,\n Typography,\n UtilityFragment,\n} from '@visa/nova-react';\nimport type { CSSProperties } from 'react';\n\nexport const SubtleAccordion = () => {\n return (\n <Accordion>\n <UtilityFragment vGap={2}>\n <AccordionHeading\n className=\"v-typography-body-2-medium\"\n colorScheme=\"tertiary\"\n style={\n {\n '--v-accordion-foreground-initial': 'var(--palette-default-active)',\n '--v-button-default-background': 'transparent',\n } as CSSProperties\n }\n >\n <AccordionToggleIcon elementClosed={<VisaChevronRightTiny rtl />} elementOpen={<VisaChevronDownTiny />} />\n Accordion title\n </AccordionHeading>\n </UtilityFragment>\n <UtilityFragment vPaddingHorizontal={32}>\n <AccordionPanel\n style={\n {\n '--v-accordion-panel-background-color': 'transparent',\n '--v-accordion-panel-border-size': '0px',\n } as CSSProperties\n }\n >\n <Typography>This is required text that describes the accordion section in more detail.</Typography>\n </AccordionPanel>\n </UtilityFragment>\n </Accordion>\n );\n};\n" }, "name": "Subtle accordion" }, { "description": "", "order": 6, "libraryId": null, "componentId": null, "section": "Individual accordions", "url": { "iframe": "components/accordion/disabled-subtle-accordion", "github": "apps/workshop/src/examples/components/accordion/disabled-subtle-accordion.tsx" }, "tags": [ "docs" ], "snippets": { "tsx": "import { VisaChevronDownTiny, VisaChevronRightTiny } from '@visa/nova-icons-react';\nimport {\n Accordion,\n AccordionHeading,\n AccordionPanel,\n AccordionToggleIcon,\n Typography,\n UtilityFragment,\n} from '@visa/nova-react';\nimport type { CSSProperties } from 'react';\n\n// TIP: Customize this ID, pass it as a prop, or auto-generate it with useId() from @react\nconst id = 'disabled-subtle-accordion';\n\nexport const DisabledSubtleAccordion = () => {\n const expanded = false;\n\n return (\n <Accordion tag=\"div\">\n <UtilityFragment vGap={2}>\n <AccordionHeading\n aria-controls={`${id}-accordion-panel`}\n aria-expanded={expanded}\n buttonSize=\"large\"\n disabled\n colorScheme=\"tertiary\"\n id={`${id}-accordion-header`}\n tag=\"button\"\n >\n <AccordionToggleIcon\n accordionOpen={expanded}\n elementClosed={<VisaChevronRightTiny rtl />}\n elementOpen={<VisaChevronDownTiny />}\n />\n <Typography variant=\"body-2-medium\">Accordion title</Typography>\n </AccordionHeading>\n </UtilityFragment>\n <UtilityFragment vPaddingHorizontal={32}>\n <AccordionPanel\n aria-hidden={!expanded}\n id={`${id}-accordion-panel`}\n style={\n {\n '--v-accordion-panel-background-color': 'transparent',\n '--v-accordion-panel-border-size': '0px',\n } as CSSProperties\n }\n >\n <Typography>This is required text that describes the accordion section in more detail.</Typography>\n </AccordionPanel>\n </UtilityFragment>\n </Accordion>\n );\n};\n" }, "name": "Disabled subtle accordion" }, { "description": "", "order": 7, "libraryId": null, "componentId": null, "section": "Individual accordions", "url": { "iframe": "components/accordion/subtle-accordion-with-icon", "github": "apps/workshop/src/examples/components/accordion/subtle-accordion-with-icon.tsx" }, "tags": [], "snippets": { "tsx": "import type { CSSProperties } from 'react';\nimport {\n Accordion,\n AccordionHeading,\n AccordionPanel,\n AccordionToggleIcon,\n Typography,\n UtilityFragment,\n} from '@visa/nova-react';\nimport { VisaChevronDownTiny, VisaChevronRightTiny, VisaCloudLow } from '@visa/nova-icons-react';\n\nexport const SubtleAccordionWithIcon = () => {\n return (\n <Accordion>\n <UtilityFragment vGap={2}>\n <AccordionHeading\n className=\"v-typography-body-2-medium\"\n colorScheme=\"tertiary\"\n style={\n {\n '--v-accordion-foreground-initial': 'var(--palette-default-active)',\n '--v-button-default-background': 'transparent',\n } as CSSProperties\n }\n >\n <AccordionToggleIcon elementClosed={<VisaChevronRightTiny rtl />} elementOpen={<VisaChevronDownTiny />} />\n <VisaCloudLow />\n Accordion title\n </AccordionHeading>\n </UtilityFragment>\n <UtilityFragment vPaddingHorizontal={32}>\n <AccordionPanel\n style={\n {\n '--v-accordion-panel-background-color': 'transparent',\n '--v-accordion-panel-border-size': '0px',\n } as CSSProperties\n }\n >\n <Typography>This is required text that describes the accordion section in more detail.</Typography>\n </AccordionPanel>\n </UtilityFragment>\n </Accordion>\n );\n};\n" }, "name": "Subtle accordion with icon" }, { "description": "", "order": 8, "libraryId": null, "componentId": null, "section": "Multi-select accordion groups", "url": { "iframe": "components/accordion/default-multi-select-accordion-group", "github": "apps/workshop/src/examples/components/accordion/default-multi-select-accordion-group.tsx" }, "tags": [ "docs" ], "snippets": { "tsx": "import { VisaChevronRightTiny, VisaChevronDownTiny } from '@visa/nova-icons-react';\nimport {\n Accordion,\n AccordionHeading,\n AccordionPanel,\n AccordionToggleIcon,\n Typography,\n Utility,\n} from '@visa/nova-react';\n\nconst accordions = [\n {\n content: 'This is required text that describes the accordion section in more detail.',\n header: 'Accordion title 1',\n },\n {\n content: 'This is required text that describes the accordion section in more detail.',\n header: 'Accordion title 2',\n },\n {\n content: 'This is required text that describes the accordion section in more detail.',\n header: 'Accordion title 3',\n },\n];\n\n// TIP: Customize this ID, pass it as a prop, or auto-generate it with useId() from @react\nconst id = 'default-multi-select-accordion-group-example';\n\nexport const DefaultMultiSelectAccordionGroup = () => {\n return (\n <Utility vFlex vFlexCol vGap={6}>\n {accordions.map((accordion, index) => (\n <Accordion key={`${id}-${index}`}>\n <AccordionHeading buttonSize=\"large\" colorScheme=\"secondary\">\n <AccordionToggleIcon elementClosed={<VisaChevronRightTiny rtl />} elementOpen={<VisaChevronDownTiny />} />\n {accordion.header}\n </AccordionHeading>\n <AccordionPanel>\n <Typography>{accordion.content}</Typography>\n </AccordionPanel>\n </Accordion>\n ))}\n </Utility>\n );\n};\n" }, "name": "Default multi-select accordion group" }, { "description": "", "order": 9, "libraryId": null, "componentId": null, "section": "Multi-select accordion groups", "url": { "iframe": "components/accordion/multi-select-accordion-group-with-expanded", "github": "apps/workshop/src/examples/components/accordion/multi-select-accordion-group-with-expanded.tsx" }, "tags": [], "snippets": { "tsx": "import { VisaChevronDownTiny, VisaChevronRightTiny } from '@visa/nova-icons-react';\nimport {\n Accordion,\n AccordionHeading,\n AccordionPanel,\n AccordionToggleIcon,\n Typography,\n Utility,\n} from '@visa/nova-react';\n\nconst accordions = [\n {\n content: 'This is required text that describes the accordion section in more detail.',\n header: 'Accordion title 1',\n },\n {\n content: 'This is required text that describes the accordion section in more detail.',\n header: 'Accordion title 2',\n },\n {\n content: 'This is required text that describes the accordion section in more detail.',\n header: 'Accordion title 3',\n },\n];\n\n// TIP: Customize this ID, pass it as a prop, or auto-generate it with useId() from @react\nconst id = 'multi-select-accordion-group-with-expanded';\n\nexport const MultiSelectAccordionGroupWithExpanded = () => {\n return (\n <Utility vFlex vFlexCol vGap={6}>\n {accordions.map((accordion, index) => (\n <Accordion key={`${id}-${index}`} open={index === 0}>\n <AccordionHeading buttonSize=\"large\" colorScheme=\"secondary\">\n <AccordionToggleIcon elementClosed={<VisaChevronRightTiny rtl />} elementOpen={<VisaChevronDownTiny />} />\n {accordion.header}\n </AccordionHeading>\n <AccordionPanel>\n <Typography>{accordion.content}</Typography>\n </AccordionPanel>\n </Accordion>\n ))}\n </Utility>\n );\n};\n" }, "name": "Multi-select accordion group with accordion expanded by default" }, { "description": "", "order": 10, "libraryId": null, "componentId": null, "section": "Multi-select accordion groups", "url": { "iframe": "components/accordion/multi-select-accordion-group-with-disabled", "github": "apps/workshop/src/examples/components/accordion/multi-select-accordion-group-with-disabled.tsx" }, "tags": [], "snippets": { "tsx": "import { VisaChevronDownTiny, VisaChevronRightTiny } from '@visa/nova-icons-react';\nimport {\n Accordion,\n AccordionHeading,\n AccordionPanel,\n AccordionToggleIcon,\n Typography,\n Utility,\n} from '@visa/nova-react';\nimport { useState } from 'react';\n\n// TIP: Customize this ID, pass it as a prop, or auto-generate it with useId() from @react\nconst id = 'multi-select-accordion-group-with-disabled';\n\nconst accordions = [\n {\n id: `${id}-panel-1`,\n content: 'This is required text that describes the accordion section in more detail.',\n header: 'Accordion title 1',\n disabled: true,\n },\n {\n id: `${id}-panel-2`,\n content: 'This is required text that describes the accordion section in more detail.',\n header: 'Accordion title 2',\n },\n {\n id: `${id}-panel-3`,\n content: 'This is required text that describes the accordion section in more detail.',\n header: 'Accordion title 3',\n },\n];\n\nexport const MultiSelectAccordionGroupWithDisabled = () => {\n const [expandedPanels, setExpandedPanels] = useState<{\n [key: string]: boolean;\n }>({\n panel1: false,\n panel2: false,\n panel3: false,\n });\n\n const handleToggle = (panel: string) => {\n setExpandedPanels(prevState => ({\n ...prevState,\n [panel]: !prevState[panel],\n }));\n };\n\n return (\n <Utility vFlex vFlexCol vGap={6}>\n {accordions.map((accordion, index) => {\n const panelKey = `panel${index + 1}`;\n\n return (\n <Accordion id={accordion.id} tag=\"div\" key={accordion.id}>\n <AccordionHeading\n aria-controls={`${accordion.id}-accordion-panel`}\n aria-expanded={expandedPanels[panelKey]}\n disabled={accordion.disabled}\n buttonSize=\"large\"\n colorScheme=\"secondary\"\n id={`${accordion.id}-accordion-header`}\n onClick={() => handleToggle(panelKey)}\n tag=\"button\"\n >\n <AccordionToggleIcon\n accordionOpen={expandedPanels[panelKey]}\n elementClosed={<VisaChevronRightTiny rtl />}\n elementOpen={<VisaChevronDownTiny />}\n />\n {accordion.header}\n </AccordionHeading>\n <AccordionPanel aria-hidden={!expandedPanels[panelKey]} id={`${accordion.id}-accordion-panel`}>\n <Typography>{accordion.content}</Typography>\n </AccordionPanel>\n </Accordion>\n );\n })}\n </Utility>\n );\n};\n" }, "name": "Multi-select accordion group with disabled accordion" }, { "description": "", "order": 11, "libraryId": null, "componentId": null, "section": "Multi-select accordion groups", "url": { "iframe": "components/accordion/subtle-multi-select-accordion-group", "github": "apps/workshop/src/examples/components/accordion/subtle-multi-select-accordion-group.tsx" }, "tags": [], "snippets": { "tsx": "import { VisaChevronDownTiny, VisaChevronRightTiny } from '@visa/nova-icons-react';\nimport {\n Accordion,\n AccordionHeading,\n AccordionPanel,\n AccordionToggleIcon,\n Typography,\n Utility,\n UtilityFragment,\n} from '@visa/nova-react';\nimport type { CSSProperties } from 'react';\n\nconst accordions = [\n {\n content: 'This is required text that describes the accordion section in more detail.',\n header: 'Accordion title 1',\n },\n {\n content: 'This is required text that describes the accordion section in more detail.',\n header: 'Accordion title 2',\n },\n {\n content: 'This is required text that describes the accordion section in more detail.',\n header: 'Accordion title 3',\n },\n];\n\n// TIP: Customize this ID, pass it as a prop, or auto-generate it with useId() from @react\nconst id = 'subtle-multi-select-accordion-group';\n\nexport const SubtleMultiSelectAccordionGroup = () => {\n return (\n <Utility vFlex vFlexCol vGap={6}>\n {accordions.map((accordion, index) => (\n <Accordion key={`${id}-${index}`}>\n <UtilityFragment vGap={2}>\n <AccordionHeading\n className=\"v-typography-body-2-medium\"\n colorScheme=\"tertiary\"\n style={\n {\n '--v-accordion-foreground-initial': 'var(--palette-default-active)',\n '--v-button-default-background': 'transparent',\n } as CSSProperties\n }\n >\n <AccordionToggleIcon elementClosed={<VisaChevronRightTiny rtl />} elementOpen={<VisaChevronDownTiny />} />\n {accordion.header}\n </AccordionHeading>\n </UtilityFragment>\n <UtilityFragment vPaddingHorizontal={32}>\n <AccordionPanel\n style={\n {\n '--v-accordion-panel-background-color': 'transparent',\n '--v-accordion-panel-border-size': '0px',\n } as CSSProperties\n }\n >\n <Typography>{accordion.content}</Typography>\n </AccordionPanel>\n </UtilityFragment>\n </Accordion>\n ))}\n </Utility>\n );\n};\n" }, "name": "Subtle multi-select accordion group" }, { "description": "", "order": 12, "libraryId": null, "componentId": null, "section": "Single-select accordion groups", "url": { "iframe": "components/accordion/default-single-select-accordion-group", "github": "apps/workshop/src/examples/components/accordion/default-single-select-accordion-group.tsx" }, "tags": [], "snippets": { "tsx": "import { VisaChevronDownTiny, VisaChevronRightTiny } from '@visa/nova-icons-react';\nimport {\n Accordion,\n AccordionHeading,\n AccordionPanel,\n AccordionToggleIcon,\n Typography,\n Utility,\n} from '@visa/nova-react';\nimport { useState } from 'react';\n\nconst accordions = [\n {\n content: 'This is required text that describes the accordion section in more detail.',\n header: 'Accordion title 1',\n },\n {\n content: 'This is required text that describes the accordion section in more detail.',\n header: 'Accordion title 2',\n },\n {\n content: 'This is required text that describes the accordion section in more detail.',\n header: 'Accordion title 3',\n },\n];\n\n// TIP: Customize this ID, pass it as a prop, or auto-generate it with useId() from @react\nconst id = 'default-single-select-accordion-group';\n\nexport const DefaultSingleSelectAccordionGroup = () => {\n const [openIndex, setOpenIndex] = useState(-1);\n return (\n <Utility vFlex vFlexCol vGap={6}>\n {accordions.map((accordion, index) => (\n <Accordion key={`${id}-${index}`} open={openIndex === index}>\n <AccordionHeading\n buttonSize=\"large\"\n colorScheme=\"secondary\"\n onClick={event => {\n event.preventDefault();\n // If open, close accordion, else open accordion\n setOpenIndex(openIndex === index ? -1 : index);\n }}\n >\n <AccordionToggleIcon elementClosed={<VisaChevronRightTiny rtl />} elementOpen={<VisaChevronDownTiny />} />\n {accordion.header}\n </AccordionHeading>\n <AccordionPanel>\n <Typography tag=\"span\">{accordion.content}</Typography>\n </AccordionPanel>\n </Accordion>\n ))}\n </Utility>\n );\n};\n" }, "name": "Default single-select accordion group" }, { "description": "", "order": 13, "libraryId": null, "componentId": null, "section": "Single-select accordion groups", "url": { "iframe": "components/accordion/single-select-accordion-group-with-expanded", "github": "apps/workshop/src/examples/components/accordion/single-select-accordion-group-with-expanded.tsx" }, "tags": [], "snippets": { "tsx": "import { VisaChevronDownTiny, VisaChevronRightTiny } from '@visa/nova-icons-react';\nimport {\n Accordion,\n AccordionHeading,\n AccordionPanel,\n AccordionToggleIcon,\n Typography,\n Utility,\n} from '@visa/nova-react';\nimport { useState } from 'react';\n\nconst accordions = [\n {\n content: 'This is required text that describes the accordion section in more detail.',\n header: 'Accordion title 1',\n },\n {\n content: 'This is required text that describes the accordion section in more detail.',\n header: 'Accordion title 2',\n },\n {\n content: 'This is required text that describes the accordion section in more detail.',\n header: 'Accordion title 3',\n },\n];\n\n// TIP: Customize this ID, pass it as a prop, or auto-generate it with useId() from @react\nconst id = 'single-select-accordion-group-with-expanded';\n\nexport const SingleSelectAccordionGroupWithExpanded = () => {\n const [openIndex, setOpenIndex] = useState(0);\n return (\n <Utility vFlex vFlexCol vGap={6}>\n {accordions.map((accordion, index) => (\n <Accordion key={`${id}-${index}`} open={openIndex === index}>\n <AccordionHeading\n buttonSize=\"large\"\n colorScheme=\"secondary\"\n onClick={event => {\n event.preventDefault();\n // If open, close accordion, else open accordion\n setOpenIndex(openIndex === index ? -1 : index);\n }}\n >\n <AccordionToggleIcon elementClosed={<VisaChevronRightTiny rtl />} elementOpen={<VisaChevronDownTiny />} />\n {accordion.header}\n </AccordionHeading>\n <AccordionPanel>\n <Typography tag=\"span\">{accordion.content}</Typography>\n </AccordionPanel>\n </Accordion>\n ))}\n </Utility>\n );\n};\n" }, "name": "Single-select accordion group with accordion expanded by default" }, { "description": "", "order": 14, "libraryId": null, "componentId": null, "section": "Single-select accordion groups", "url": { "iframe": "components/accordion/subtle-single-select-accordion-group", "github": "apps/workshop/src/examples/components/accordion/subtle-single-select-accordion-group.tsx" }, "tags": [], "snippets": { "tsx": "import { VisaChevronDownTiny, VisaChevronRightTiny } from '@visa/nova-icons-react';\nimport {\n Accordion,\n AccordionHeading,\n AccordionPanel,\n AccordionToggleIcon,\n Typography,\n Utility,\n UtilityFragment,\n} from '@visa/nova-react';\nimport { useState, type CSSProperties } from 'react';\n\nconst accordions = [\n {\n content: 'This is required text that describes the accordion section in more detail.',\n header: 'Accordion title 1',\n },\n {\n content: 'This is required text that describes the accordion section in more detail.',\n header: 'Accordion title 2',\n },\n {\n content: 'This is required text that describes the accordion section in more detail.',\n header: 'Accordion title 3',\n },\n];\n\n// TIP: Customize this ID, pass it as a prop, or auto-generate it with useId() from @react\nconst id = 'subtle-single-select-accordion-group';\n\nexport const SubtleSingleSelectAccordionGroup = () => {\n const [openIndex, setOpenIndex] = useState(-1);\n return (\n <Utility vFlex vFlexCol vGap={6}>\n {accordions.map((accordion, index) => (\n <Accordion key={`${id}-${index}`} open={index === openIndex}>\n <UtilityFragment vGap={2}>\n <AccordionHeading\n className=\"v-typography-body-2-medium\"\n colorScheme=\"tertiary\"\n onClick={event => {\n event.preventDefault();\n // If open, close accordion, else open accordion\n setOpenIndex(openIndex === index ? -1 : index);\n }}\n style={\n {\n '--v-accordion-foreground-initial': 'var(--palette-default-active)',\n '--v-button-default-background': 'transparent',\n } as CSSProperties\n }\n >\n <AccordionToggleIcon elementClosed={<VisaChevronRightTiny rtl />} elementOpen={<VisaChevronDownTiny />} />\n {accordion.header}\n </AccordionHeading>\n </UtilityFragment>\n <UtilityFragment vPaddingHorizontal={32}>\n <AccordionPanel\n style={\n {\n '--v-accordion-panel-background-color': 'transparent',\n '--v-accordion-panel-border-size': '0px',\n } as CSSProperties\n }\n >\n <Typography>{accordion.content}</Typography>\n </AccordionPanel>\n </UtilityFragment>\n </Accordion>\n ))}\n </Utility>\n );\n};\n" }, "name": "Subtle single-select accordion group" }, { "description": "", "order": 15, "libraryId": null, "componentId": null, "section": "Custom accordion groups", "url": { "iframe": "components/accordion/native-single-select-accordion-group", "github": "apps/workshop/src/examples/components/accordion/native-single-select-accordion-group.tsx" }, "tags": [], "snippets": { "tsx": "import { VisaChevronDownTiny, VisaChevronRightTiny } from '@visa/nova-icons-react';\nimport {\n Accordion,\n AccordionHeading,\n AccordionPanel,\n AccordionToggleIcon,\n Typography,\n Utility,\n} from '@visa/nova-react';\n\nconst accordions = [\n {\n content: 'This is required text that describes the accordion section in more detail.',\n header: 'Accordion title 1',\n },\n {\n content: 'This is required text that describes the accordion section in more detail.',\n header: 'Accordion title 2',\n },\n {\n content: 'This is required text that describes the accordion section in more detail.',\n header: 'Accordion title 3',\n },\n];\n\n// TIP: Customize this ID, pass it as a prop, or auto-generate it with useId() from @react\nconst id = 'native-single-select-accordion-group';\n\nexport const NativeSingleSelectAccordionGroup = () => {\n return (\n <Utility vFlex vFlexCol vGap={6}>\n {accordions.map((accordion, index) => (\n <Accordion key={`${id}-${index}`} name={id}>\n <AccordionHeading buttonSize=\"large\" colorScheme=\"secondary\">\n <AccordionToggleIcon elementClosed={<VisaChevronRightTiny rtl />} elementOpen={<VisaChevronDownTiny />} />\n {accordion.header}\n </AccordionHeading>\n <AccordionPanel>\n <Typography tag=\"span\">{accordion.content}</Typography>\n </AccordionPanel>\n </Accordion>\n ))}\n </Utility>\n );\n};\n" }, "name": "Native single-select accordion group" }, { "description": "", "order": 16, "libraryId": null, "componentId": null, "section": "Custom accordion groups", "url": { "iframe": "components/accordion/default-with-item-open-accordion", "github": "apps/workshop/src/examples/components/accordion/default-with-item-open-accordion.tsx" }, "tags": [ "custom" ], "snippets": { "tsx": "import { VisaChevronDownTiny, VisaChevronRightTiny } from '@visa/nova-icons-react';\nimport {\n Accordion,\n AccordionHeading,\n AccordionPanel,\n AccordionToggleIcon,\n Typography,\n useAccordion,\n} from '@visa/nova-react';\nimport { Fragment } from 'react';\n\nconst accordions = [\n {\n content: 'This is required text that describes the accordion section in more detail.',\n header: 'Accordion title 1',\n },\n {\n content: 'This is required text that describes the accordion section in more detail.',\n header: 'Accordion title 2',\n },\n {\n content: 'This is required text that describes the accordion section in more detail.',\n header: 'Accordion title 3',\n },\n];\n\nexport const DefaultWithItemOpenAccordion = () => {\n const { isIndexExpanded, toggleIndexExpanded } = useAccordion({ defaultExpanded: 0 });\n\n return (\n <Accordion id=\"accordion-default-open-group\" tag=\"div\">\n {accordions.map((accordion, index) => (\n <Fragment key={`accordion-default-open-group-${index}`}>\n <AccordionHeading\n aria-controls={`accordion-default-open-panel-${index}`}\n aria-expanded={isIndexExpanded(index)}\n buttonSize=\"large\"\n colorScheme=\"secondary\"\n id={`accordion-default-open-header-${index}`}\n onClick={() => toggleIndexExpanded(index)}\n tag=\"button\"\n >\n <AccordionToggleIcon\n accordionOpen={isIndexExpanded(index)}\n elementClosed={<VisaChevronRightTiny rtl />}\n elementOpen={<VisaChevronDownTiny />}\n />\n {accordion.header}\n </AccordionHeading>\n <AccordionPanel aria-hidden={!isIndexExpanded(index)} id={`accordion-default-open-panel-${index}`}>\n <Typography>{accordion.content}</Typography>\n </AccordionPanel>\n </Fragment>\n ))}\n </Accordion>\n );\n};\n" }, "name": "Custom single-select accordion group with accordion expanded by default" }, { "description": "", "order": 17, "libraryId": null, "componentId": null, "section": "Custom accordion groups", "url": { "iframe": "components/accordion/disclosure-group-accordion", "github": "apps/workshop/src/examples/components/accordion/disclosure-group-accordion.tsx" }, "tags": [ "custom" ], "snippets": { "tsx": "import { VisaChevronDownTiny, VisaChevronRightTiny } from '@visa/nova-icons-react';\nimport {\n Accordion,\n AccordionHeading,\n AccordionPanel,\n AccordionToggleIcon,\n Typography,\n useAccordion,\n} from '@visa/nova-react';\nimport { Fragment } from 'react';\n\nconst accordions = [\n {\n content: 'This is required text that describes the accordion section in more detail.',\n header: 'Accordion title 1',\n },\n {\n content: 'This is required text that describes the accordion section in more detail.',\n header: 'Accordion title 2',\n },\n {\n content: 'This is required text that describes the accordion section in more detail.',\n header: 'Accordion title 3',\n },\n];\n\nexport const DisclosureGroupAccordion = () => {\n const { isIndexExpanded, toggleIndexExpanded } = useAccordion({ defaultExpanded: [0, 1] });\n\n return (\n <Accordion id=\"accordion-disclosure-group\" tag=\"div\">\n {accordions.map((accordion, index) => (\n <Fragment key={`accordion-disclosure-group-${index}`}>\n <AccordionHeading\n aria-controls={`accordion-disclosure-group-panel-${index}`}\n aria-expanded={isIndexExpanded(index)}\n buttonSize=\"large\"\n colorScheme=\"secondary\"\n id={`accordion-disclosure-group-header-${index}`}\n onClick={() => toggleIndexExpanded(index)}\n tag=\"button\"\n >\n <AccordionToggleIcon\n accordionOpen={isIndexExpanded(index)}\n elementClosed={<VisaChevronRightTiny rtl />}\n elementOpen={<VisaChevronDownTiny />}\n />\n {accordion.header}\n </AccordionHeading>\n <AccordionPanel aria-hidden={!isIndexExpanded(index)} id={`accordion-disclosure-group-panel-${index}`}>\n <Typography>{accordion.content}</Typography>\n </AccordionPanel>\n </Fragment>\n ))}\n </Accordion>\n );\n};\n" }, "name": "Custom multi-select accordion group with accordion expanded by default" }, { "description": "", "order": 18, "libraryId": null, "componentId": null, "section": "Custom accordion groups", "url": { "iframe": "components/accordion/key-nav-group-accordion", "github": "apps/workshop/src/examples/components/accordion/key-nav-group-accordion.tsx" }, "tags": [ "custom" ], "snippets": { "tsx": "import { VisaChevronDownTiny, VisaChevronRightTiny } from '@visa/nova-icons-react';\nimport {\n Accordion,\n AccordionHeading,\n AccordionPanel,\n AccordionToggleIcon,\n Typography,\n useAccordion,\n} from '@visa/nova-react';\nimport { Fragment } from 'react';\n\nconst accordions = [\n {\n content: 'This is required text that describes the accordion section in more detail.',\n header: 'Accordion title 1',\n },\n {\n content: 'This is required text that describes the accordion section in more detail.',\n header: 'Accordion title 2',\n },\n {\n content: 'This is required text that describes the accordion section in more detail.',\n header: 'Accordion title 3',\n },\n];\n\nexport const KeyNavGroupAccordion = () => {\n const { isIndexExpanded, onKeyNavigation, ref: accordionRef, toggleIndexExpanded } = useAccordion();\n\n return (\n <Accordion id=\"accordion-key-nav-group\" onKeyDown={onKeyNavigation} tag=\"div\">\n {accordions.map((accordion, i) => (\n <Fragment key={i}>\n <AccordionHeading\n aria-controls={`accordion-key-nav-group-panel-${i}`}\n aria-expanded={isIndexExpanded(i)}\n buttonSize=\"large\"\n colorScheme=\"secondary\"\n id={`accordion-key-nav-group-header-${i}`}\n onClick={() => toggleIndexExpanded(i)}\n ref={el => {\n accordionRef.current[i] = el;\n }}\n tag=\"button\"\n >\n <AccordionToggleIcon\n accordionOpen={isIndexExpanded(i)}\n elementClosed={<VisaChevronRightTiny rtl />}\n elementOpen={<VisaChevronDownTiny />}\n />\n {accordion.header}\n </AccordionHeading>\n <AccordionPanel aria-hidden={!isIndexExpanded(i)} id={`accordion-key-nav-group-panel-${i}`}>\n <Typography>{accordion.content}</Typography>\n </AccordionPanel>\n </Fragment>\n ))}\n </Accordion>\n );\n};\n" }, "name": "Accordion group with arrow key navigation" }, { "description": "", "order": 19, "libraryId": null, "componentId": null, "section": "Custom accordion groups", "url": { "iframe": "components/accordion/reusable", "github": "apps/workshop/src/examples/components/accordion/reusable.tsx" }, "tags": [ "custom", "AI-first" ], "snippets": { "tsx": "import { VisaChevronDownTiny, VisaChevronRightTiny, VisaCloudLow, VisaSuccessTiny } from '@visa/nova-icons-react';\nimport {\n Accordion,\n AccordionHeading,\n AccordionPanel,\n AccordionToggleIcon,\n Badge,\n Button,\n Checkbox,\n Input,\n InputContainer,\n Label,\n Typography,\n Utility,\n UtilityFragment,\n type AccordionProperties,\n} from '@visa/nova-react';\nimport { type CSSProperties, type ReactNode, useId, useState } from 'react';\n\n// Nova Accordion Component Props\n// Note: Omit 'prefix' because it's a native HTML attribute on <details> that expects a string,\n// but we're overriding it to accept ReactNode for icon/badge support\nexport type NovaAccordionProps = Omit<AccordionProperties, 'prefix'> & {\n description?: string;\n disabled?: boolean;\n padding?: boolean;\n prefix?: ReactNode;\n showIcon?: boolean;\n subtle?: boolean;\n suffix?: ReactNode;\n title?: string;\n toggleIcon?: ReactNode;\n};\n\n// Main Nova Accordion Component\nexport const NovaAccordion = ({\n children,\n description,\n disabled = false,\n id: idProp,\n name,\n padding = true,\n prefix,\n showIcon = true,\n subtle = false,\n suffix,\n title,\n toggleIcon,\n ...remainingProps\n}: NovaAccordionProps) => {\n const generatedId = useId();\n const id = idProp ?? generatedId;\n\n return (\n <Accordion id={id} name={name ?? undefined} {...remainingProps}>\n <UtilityFragment vGap={2}>\n <AccordionHeading\n buttonSize={subtle ? undefined : 'large'}\n className=\"v-typography-body-2-medium\"\n colorScheme={subtle ? 'tertiary' : 'secondary'}\n disabled={disabled}\n style={\n subtle\n ? ({\n '--v-accordion-foreground-initial': 'var(--palette-default-active)',\n '--v-button-default-background': 'transparent',\n } as CSSProperties)\n : undefined\n }\n >\n {toggleIcon}\n {showIcon && (\n <AccordionToggleIcon elementClosed={<VisaChevronRightTiny rtl />} elementOpen={<VisaChevronDownTiny />} />\n )}\n {prefix}\n {title && (\n <Utility vFlex vAlignItems=\"center\" vGap=\"6\">\n {title}\n </Utility>\n )}\n {suffix}\n </AccordionHeading>\n </UtilityFragment>\n <UtilityFragment vPaddingHorizontal={padding ? 32 : undefined}>\n <AccordionPanel\n style={\n subtle\n ? ({\n '--v-accordion-panel-background-color': 'transparent',\n '--v-accordion-panel-border-size': '0px',\n } as CSSProperties)\n : undefined\n }\n >\n {description && <Typography>{description}</Typography>}\n {children}\n </AccordionPanel>\n </UtilityFragment>\n </Accordion>\n );\n};\n\n// export default NovaAccordion;\n\n/** !!! DELETE ME START !!! */\n\n// Demo Component Types\ninterface DemoCustomizations {\n accordions: number;\n description: string;\n disabled: boolean;\n multiselect: boolean;\n padding: boolean;\n showBadge: boolean;\n showIcon: boolean;\n showToggleIcon: boolean;\n subtle: boolean;\n title: string;\n}\n\n// Demo Component\nexport const NovaAccordionDemo = () => {\n const name = 'reusable-accordion-demo';\n\n const defaultCustomizations: DemoCustomizations = {\n accordions: 1,\n description: 'This is required text that describes the accordion in more detail.',\n disabled: false,\n multiselect: false,\n padding: true,\n showBadge: false,\n showIcon: false,\n showToggleIcon: true,\n subtle: false,\n title: 'Success title',\n };\n\n const [customizations, setCustomizations] = useState<DemoCustomizations>(defaultCustomizations);\n const [formValues, setFormValues] = useState<DemoCustomizations>(defaultCustomizations);\n\n const accordions = Array.from({ length: Math.max(1, Math.floor(customizations.accordions)) }, (_, i) => i);\n\n const handleInputChange = (field: keyof DemoCustomizations, value: string | boolean | number) => {\n setFormValues(prev => ({\n ...prev,\n [field]: value,\n }));\n };\n\n const handleApply = (e: React.FormEvent) => {\n e.preventDefault();\n setCustomizations(formValues);\n };\n\n const handleReset = () => {\n setFormValues(defaultCustomizations);\n setCustomizations(defaultCustomizations);\n };\n\n return (\n <div>\n <Utility vFlex vFlexCol vGap={6}>\n {accordions.map(i => (\n <NovaAccordion\n key={`reusable-accordion-${i}`}\n description={customizations.description || ''}\n disabled={customizations.disabled}\n name={customizations.multiselect ? undefined : name}\n padding={customizations.padding}\n showIcon={customizations.showToggleIcon}\n subtle={customizations.subtle}\n title={`${customizations.title || ''} ${i + 1}`}\n prefix={customizations.showIcon ? <VisaCloudLow /> : undefined}\n suffix={\n customizations.showBadge ? (\n <Badge badgeType=\"stable\" style={{ marginLeft: 'auto' }}>\n <VisaSuccessTiny style={{ margin: 0 }} />\n <span>Label</span>\n </Badge>\n ) : undefined\n }\n />\n ))}\n </Utility>\n\n <div style={{ marginTop: '24px' }} />\n\n <NovaAccordion title=\"Customize demo\">\n <form onSubmit={handleApply}>\n <Utility vFlex vFlexCol vGap={16} style={{ marginBottom: '32px' }}>\n <div>\n <Label htmlFor=\"accordions\">Accordions</Label>\n <InputContainer>\n <Input\n id=\"accordions\"\n type=\"number\"\n value={formValues.accordions}\n onChange={e => handleInputChange('accordions', parseInt(e.target.value) || 1)}\n />\n </InputContainer>\n </div>\n\n <div>\n <Label htmlFor=\"description\">Description</Label>\n <InputContainer>\n <Input\n id=\"description\"\n type=\"text\"\n value={formValues.description}\n onChange={e => handleInputChange('description', e.target.value)}\n />\n </InputContainer>\n </div>\n\n <div>\n <Label htmlFor=\"title\">Title</Label>\n <InputContainer>\n <Input\n id=\"title\"\n type=\"text\"\n value={formValues.title}\n onChange={e => handleInputChange('title', e.target.value)}\n />\n </InputContainer>\n </div>\n\n <Label>\n <Checkbox checked={formValues.disabled} onChange={e => handleInputChange('disabled', e.target.checked)} />\n Disabled\n </Label>\n\n <Label>\n <Checkbox\n checked={formValues.multiselect}\n onChange={e => handleInputChange('multiselect', e.target.checked)}\n />\n Multiselect\n </Label>\n\n <Label>\n <Checkbox checked={formValues.padding} onChange={e => handleInputChange('padding', e.target.checked)} />\n Padding\n </Label>\n\n <Label>\n <Checkbox\n checked={formValues.showBadge}\n onChange={e => handleInputChange('showBadge', e.target.checked)}\n />\n Show badge\n </Label>\n\n <Label>\n <Checkbox checked={formValues.showIcon} onChange={e => handleInputChange('showIcon', e.target.checked)} />\n Show icon\n </Label>\n\n <Label>\n <Checkbox\n checked={formValues.showToggleIcon}\n onChange={e => handleInputChange('showToggleIcon', e.target.checked)}\n />\n Show toggle icon\n </Label>\n\n <Label>\n <Checkbox checked={formValues.subtle} onChange={e => handleInputChange('subtle', e.target.checked)} />\n Subtle\n </Label>\n </Utility>\n\n <Utility vFlex vGap={16} style={{ marginBottom: '16px' }}>\n <Button type=\"submit\">Apply</Button>\n <Button colorScheme=\"secondary\" type=\"button\" onClick={handleReset}>\n Reset\n </Button>\n </Utility>\n </form>\n </NovaAccordion>\n </div>\n );\n};\n\nexport default NovaAccordionDemo;\n/** !!! DELETE ME END !!! */\n"