diff --git a/apps/website/pages/components/accordion/code.tsx b/apps/website/pages/components/accordion/code.tsx new file mode 100644 index 0000000000..5d11b6205a --- /dev/null +++ b/apps/website/pages/components/accordion/code.tsx @@ -0,0 +1,17 @@ +import Head from "next/head"; +import type { ReactElement } from "react"; +import AccordionPageLayout from "screens/components/accordion/AccordionPageLayout"; +import AccordionCodePage from "screens/components/accordion/code/AccordionCodePage"; + +const Code = () => ( + <> + + Accordion code — Halstack Design System + + + +); + +Code.getLayout = (page: ReactElement) => {page}; + +export default Code; diff --git a/apps/website/pages/components/accordion/index.tsx b/apps/website/pages/components/accordion/index.tsx index d865a8a8f8..8f6d902d48 100644 --- a/apps/website/pages/components/accordion/index.tsx +++ b/apps/website/pages/components/accordion/index.tsx @@ -1,21 +1,17 @@ import Head from "next/head"; import type { ReactElement } from "react"; import AccordionPageLayout from "screens/components/accordion/AccordionPageLayout"; -import AccordionCodePage from "screens/components/accordion/code/AccordionCodePage"; +import AccordionOverviewPage from "screens/components/accordion/overview/AccordionOverviewPage"; -const Index = () => { - return ( - <> - - Accordion — Halstack Design System - - - - ); -}; +const Index = () => ( + <> + + Accordion — Halstack Design System + + + +); -Index.getLayout = function getLayout(page: ReactElement) { - return {page}; -}; +Index.getLayout = (page: ReactElement) => {page}; export default Index; diff --git a/apps/website/pages/components/accordion/specifications.tsx b/apps/website/pages/components/accordion/specifications.tsx deleted file mode 100644 index 595b36f672..0000000000 --- a/apps/website/pages/components/accordion/specifications.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import Head from "next/head"; -import type { ReactElement } from "react"; -import AccordionPageLayout from "screens/components/accordion/AccordionPageLayout"; -import AccordionSpecsPage from "screens/components/accordion/specs/AccordionSpecsPage"; - -const Specifications = () => { - return ( - <> - - Accordion Specs — Halstack Design System - - - - ); -}; - -Specifications.getLayout = function getLayout(page: ReactElement) { - return {page}; -}; - -export default Specifications; diff --git a/apps/website/pages/components/accordion/usage.tsx b/apps/website/pages/components/accordion/usage.tsx deleted file mode 100644 index 2fe0b67f24..0000000000 --- a/apps/website/pages/components/accordion/usage.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import Head from "next/head"; -import type { ReactElement } from "react"; -import AccordionPageLayout from "screens/components/accordion/AccordionPageLayout"; -import AccordionUsagePage from "screens/components/accordion/usage/AccordionUsagePage"; - -const Usage = () => { - return ( - <> - - Accordion Usage — Halstack Design System - - - - ); -}; - -Usage.getLayout = function getLayout(page: ReactElement) { - return {page}; -}; - -export default Usage; diff --git a/apps/website/screens/components/accordion/AccordionPageLayout.tsx b/apps/website/screens/components/accordion/AccordionPageLayout.tsx index 33e4d86743..9cb36985ef 100644 --- a/apps/website/screens/components/accordion/AccordionPageLayout.tsx +++ b/apps/website/screens/components/accordion/AccordionPageLayout.tsx @@ -6,9 +6,8 @@ import { ReactNode } from "react"; const AccordionPageHeading = ({ children }: { children: ReactNode }) => { const tabs = [ - { label: "Code", path: "/components/accordion" }, - { label: "Usage", path: "/components/accordion/usage" }, - { label: "Specifications", path: "/components/accordion/specifications" }, + { label: "Overview", path: "/components/accordion" }, + { label: "Code", path: "/components/accordion/code" }, ]; return ( @@ -22,7 +21,7 @@ const AccordionPageHeading = ({ children }: { children: ReactNode }) => { enhances the user experience by organizing information into smaller, digestible chunks, helping reduce cognitive load and save screen space. - + {children} diff --git a/apps/website/screens/components/accordion/code/AccordionCodePage.tsx b/apps/website/screens/components/accordion/code/AccordionCodePage.tsx index c70438f61b..1e775ee1bc 100644 --- a/apps/website/screens/components/accordion/code/AccordionCodePage.tsx +++ b/apps/website/screens/components/accordion/code/AccordionCodePage.tsx @@ -28,17 +28,17 @@ const sections = [ - independent - - boolean - - When true, limits the user to single-open section at a time. When false, multiple sections can be opened - simultaneously. + + + children + - false + {`ReactElement[] | ReactElement`} + Contains one or more accordion items. + - defaultIndexActive @@ -52,25 +52,27 @@ const sections = [ - - indexActive + independent - number | number[] + boolean - The index of the active accordion. If undefined, the component will be uncontrolled and the active - accordion will be managed internally by the component. If null, the component will be controlled and all - accordions will be closed. If the accordion is not independent, several accordions can be activated. + When true, limits the user to single-open section at a time. When false, multiple sections can be opened + simultaneously. + + + false - - - onActiveChange + indexActive - {`(index: number | number[]) => void`} + number | number[] - This function will be called when the user clicks on an accordion. The index of the clicked accordion will - be passed as a parameter. + The index of the active accordion. If undefined, the component will be uncontrolled and the active + accordion will be managed internally by the component. If null, the component will be controlled and all + accordions will be closed. If the accordion is not independent, several accordions can be activated. - @@ -86,16 +88,14 @@ const sections = [ - + onActiveChange - - - children - + {`(index: number | number[]) => void`} - {`ReactElement[] | ReactElement`} + This function will be called when the user clicks on an accordion. The index of the clicked accordion will + be passed as a parameter. - Contains one or more accordion items. - @@ -120,56 +120,48 @@ const sections = [ - - - - label - - + assistiveText string - The panel label. + Assistive text to be placed on the right side of the panel. - - subLabel + badge - string + {"{ position: 'before' | 'after'; element: ReactNode }"} - Additional info label positioned under the label. + Badge component to add extra value to the accordion. - - - badge + + children - {"{ position: 'before' | 'after'; element: ReactNode }"} + React.ReactNode - Badge component to add extra value to the accordion. + The expanded panel of the accordion. This area can be used to render custom content. - + disabled - - - statusLight - + boolean + If true, the component will be disabled. - React.ReactNode + false - Status light component to add extra value to the accordion. - - icon @@ -187,34 +179,42 @@ const sections = [ - - assistiveText + + + + label + + string - Assistive text to be placed on the right side of the panel. + The panel label. - - disabled - boolean + + + statusLight + - If true, the component will be disabled. - false + React.ReactNode + Status light component to add extra value to the accordion. + - - - children + + subLabel - React.ReactNode + string - The expanded panel of the accordion. This area can be used to render custom content. + Additional info label positioned under the label. - diff --git a/apps/website/screens/components/accordion/overview/AccordionOverviewPage.tsx b/apps/website/screens/components/accordion/overview/AccordionOverviewPage.tsx new file mode 100644 index 0000000000..6a7f8ea18a --- /dev/null +++ b/apps/website/screens/components/accordion/overview/AccordionOverviewPage.tsx @@ -0,0 +1,319 @@ +import { DxcBulletedList, DxcFlex, DxcParagraph } from "@dxc-technology/halstack-react"; +import QuickNavContainer from "@/common/QuickNavContainer"; +import QuickNavContainerLayout from "@/common/QuickNavContainerLayout"; +import DocFooter from "@/common/DocFooter"; +import Image from "@/common/Image"; +import anatomy from "./images/accordion_anatomy.png"; +import Example from "@/common/example/Example"; +import basicExample from "./examples/basicExample"; +import nestedExample from "./examples/nestedExample"; +import placement from "./images/accordion_placement.png"; + +const sections = [ + { + title: "Introduction", + content: ( + + The accordion component is designed to present large amounts of content in a small space by leveraging + progressive disclosure. It helps improve the user experience by breaking down information into manageable + sections, allowing users to focus only on what they need. + + ), + }, + { + title: "Anatomy", + content: ( + <> + Accordion's anatomy + + + Header: serves as the trigger for expanding or collapsing the section. It acts as a summary + of the content, allowing users to decide if they want to interact with it. + + + Content area: the expanded section where detailed information or functionality resides. + + + Title: a concise and descriptive label that summarizes the content of the accordion + section. It helps users understand what type of information they can expect to find inside. + + + Chevron icon (Expand/collapse): A visual indicator of the accordion's current state + (expanded or collapsed). It provides an affordance for interaction and ensures users can toggle the + accordion intuitively. + + + Left secondary element (Optional): Provides additional context or enhances the + user experience. Only one of the following can be used per accordion section to avoid + visual clutter: + + + Icon: Adds visual context or aids recognition by representing the content type or + purpose of the section. + + + Badge: Displays supplemental information, such as a notification count or status, to + give users a quick overview of the section. + + + + + Sublabel (Optional): Offers additional context or instruction. + + + Right secondary element (Optional): Also limited to one element + per section. May include: + + + Helper text: Offers supporting guidance or content summary. + + + Status light: A visual status indicator (e.g., completed, in progress, error). + + + Badge: Similar to the left badge, positioned right for layout flexibility. + + + + + + + ), + }, + { + title: "Using accordions", + subSections: [ + { + title: "Behavior and interaction", + content: ( + + The accordion component has two main states: collapsed and expanded. The chevron icon at the end of the + accordion indicates which state the accordion is in. Accordions begin by default in the collapsed state with + all content panels closed. Starting in a collapsed state gives the user a high level overview of the + available information. + + ), + }, + { + title: "Content area", + content: ( + <> + + The content area can contain other components, images, tables, and every custom feature that can be + supported inside the element container. + + + + ), + }, + { + title: "Allowing multiple sections open vs single-open behavior", + content: ( + <> + + The accordion component can be configured to allow either multiple sections to be open + simultaneously or limit the user to a single-open section at a time. Each approach has + specific use cases, but it's important to prioritise user needs and content hierarchy when deciding which + behavior to implement. + + + + Allow multiple sections open + + This approach gives users full control over the visibility of content, allowing them to open or + collapse multiple sections at the same time. It's particularly useful when: + + + + Users need to compare or reference information across different sections simultaneously. + + + The content in each section is independent and doesn't require strict linear navigation. + + + There is enough vertical space to accommodate multiple expanded sections without overwhelming the + layout. + + + + + Single-open behavior + + Some implementations restrict the accordion to allow only one section to be open at a time. When a + user expands a new section, the previously expanded section collapses automatically. This pattern is + suitable when: + + + + The content is closely related or mutually exclusive, making it logical to view only one section at + a time. + + + Vertical space is limited, and having multiple sections open could cause usability or layout issues. + + + A simplified and more guided user experience is desired, such as in wizards or step-by-step + processes. + + + + + + ), + }, + { + title: "Placement and alignment", + content: ( + <> + + Accordions can be placed with main page content or placed inside of a container such as a side panel or + tile. + + Accordion's placement + + ), + }, + { + title: "Mobile", + content: ( + <> + + In small devices, extremely long pages are detrimental to the user experience. Collapsing information + minimises excessive scrolling and gives an overview of the structure and content available on the page. + + + In mobile use 100% of the available screen width + + + ), + }, + { + title: "Best practices", + content: ( + + To ensure a clean, efficient, and user-friendly experience, follow these best practices when designing and + implementing accordion components: + + ), + subSections: [ + { + title: "Use accordions to manage space and structure", + content: ( + + + Apply accordions when you need to organize large or secondary content into collapsible sections. + + + They are particularly useful for FAQs, optional content, or detailed information nested under + high-level summaries. + + + Collapsing content helps reduce scrolling and offers users control over what they choose to engage + with. + + + ), + }, + { + title: "Keep headers simple and informative", + content: ( + + + Each accordion header must include a clear, concise title that describes the content inside. + + + Use a sublabel only when additional context is necessary —it should not overpower the title. + + + Avoid hiding essential or primary information inside an accordion panel. + + + ), + }, + { + title: "Limit secondary elements for clarity", + content: ( + + + Use only one secondary element per side of the header (left and right) to avoid + clutter. + + + Choose either an icon or a badge for the left side, and a helper text, badge, or status light for the + right side —never more than one per side. + + + Avoid placing two elements of the same type in one header (e.g., two badges), as this reduces + scannability and can confuse users. + + + ), + }, + { + title: "Respect visual hierarchy and semantic meaning", + content: ( + + + Always prioritize the visibility of mandatory elements like the title over optional elements. + + + If both a badge and a status light are present, avoid applying semantic colors (e.g., red, green) to + the badge to prevent visual conflict with the status indicator. + + + Maintain consistency in layout and alignment to support content scanning, especially when multiple + accordion sections are used together. + + + ), + }, + { + title: "Choose the appropriate expand behavior", + content: ( + + + Use single-open behavior when content is interdependent, linear, or when space is + limited. + + + Use multi-open behavior when content is independent or when users may need to view + multiple sections at once. + + + Consider the context of use and user goals when deciding which interaction pattern is most + appropriate. + + + ), + }, + { + title: "Design for mobile and accessibility", + content: ( + + + Accordions should span the full width of the screen on smaller devices for easier interaction. + + + Ensure all interactive elements are large enough to be tapped comfortably on touchscreens. + + + ), + }, + ], + }, + ], + }, +]; + +const AccordionOverviewPage = () => { + return ( + + + + + + + ); +}; + +export default AccordionOverviewPage; diff --git a/apps/website/screens/components/accordion/overview/examples/basicExample.ts b/apps/website/screens/components/accordion/overview/examples/basicExample.ts new file mode 100644 index 0000000000..0d726ff8d8 --- /dev/null +++ b/apps/website/screens/components/accordion/overview/examples/basicExample.ts @@ -0,0 +1,86 @@ +import { DxcAccordion, DxcInset, DxcBadge, DxcStatusLight, DxcFlex } from "@dxc-technology/halstack-react"; +import { useState } from "react"; + +const code = `() => { + return ( + + + + + + Details + + + + + + + Details + + + + + } + subLabel="Jan, 09 2025" + > + + Details + + + + + } + > + + Details + + + + + }} + label="Life Policy" + subLabel="Ref - 1236554546" + > + + Details + + + + + }} + label="Life Policy" + statusLight={} + subLabel="Ref - 1236554546" + > + + Details + + + + + + ); +}`; + +const scope = { + DxcAccordion, + DxcInset, + DxcBadge, + DxcFlex, + DxcStatusLight, + useState, +}; + +export default { code, scope }; diff --git a/apps/website/screens/components/accordion/overview/examples/nestedExample.ts b/apps/website/screens/components/accordion/overview/examples/nestedExample.ts new file mode 100644 index 0000000000..17ab246ea1 --- /dev/null +++ b/apps/website/screens/components/accordion/overview/examples/nestedExample.ts @@ -0,0 +1,37 @@ +import { DxcAccordion, DxcInset, DxcBadge, DxcStatusLight, DxcFlex } from "@dxc-technology/halstack-react"; +import { useState } from "react"; + +const code = `() => { + return ( + + + + + + + + Details + + + + + + + + ); +}`; + +const scope = { + DxcAccordion, + DxcInset, + DxcBadge, + DxcFlex, + DxcStatusLight, + useState, +}; + +export default { code, scope }; diff --git a/apps/website/screens/components/accordion/overview/images/accordion_anatomy.png b/apps/website/screens/components/accordion/overview/images/accordion_anatomy.png new file mode 100644 index 0000000000..a6d49b7f7a Binary files /dev/null and b/apps/website/screens/components/accordion/overview/images/accordion_anatomy.png differ diff --git a/apps/website/screens/components/accordion/overview/images/accordion_placement.png b/apps/website/screens/components/accordion/overview/images/accordion_placement.png new file mode 100644 index 0000000000..f2e19cd4e5 Binary files /dev/null and b/apps/website/screens/components/accordion/overview/images/accordion_placement.png differ diff --git a/apps/website/screens/components/accordion/specs/AccordionSpecsPage.tsx b/apps/website/screens/components/accordion/specs/AccordionSpecsPage.tsx deleted file mode 100644 index 3430294597..0000000000 --- a/apps/website/screens/components/accordion/specs/AccordionSpecsPage.tsx +++ /dev/null @@ -1,611 +0,0 @@ -import { DxcFlex, DxcTable, DxcLink, DxcParagraph, DxcBulletedList } from "@dxc-technology/halstack-react"; -import QuickNavContainer from "@/common/QuickNavContainer"; -import QuickNavContainerLayout from "@/common/QuickNavContainerLayout"; -import Code from "@/common/Code"; -import Figure from "@/common/Figure"; -import DocFooter from "@/common/DocFooter"; -import Image from "@/common/Image"; -import accordionStates from "./images/accordion_states.png"; -import accordionAnatomy from "./images/accordion_anatomy.png"; -import accordionSpecs from "./images/accordion_specs.png"; - -const sections = [ - { - title: "Specifications", - content: ( -
- Accordion design specifications -
- ), - }, - { - title: "States", - content: ( - <> - - The accordion header can get four different states based on user interaction. States: enabled - , hover, focus and disabled. - -
- Accordion states -
- - ), - }, - { - title: "Anatomy", - content: ( - <> - Accordion anatomy - - Header - - Left secondary element (Optional) - - Title - Sublabel - - Right secondary element (Optional) - - - Caret icon (Expand/collapse) - - Content area - - - ), - }, - { - title: "Design tokens", - subSections: [ - { - title: "Color", - content: ( - - - - Component token - Element - Core token - Value - - - - - - accordionSeparatorBorderColor - - Separator - - color-grey-200 - - #e6e6e6 - - - - accordionSeparatorBorderThickness - - Separator - - - 1px - - - - accordionSeparatorBorderStyle - - Separator - - border-style-solid - - solid - - - - activeBackgroundColor - - Header background:active - - color-purple-100 - - #f2eafa - - - - arrowColor - - Caret icon - - color-purple-700 - - #5f249f - - - - assistiveTextFontColor - - Assistive text - - color-grey-700 - - #666666 - - - - backgroundColor - - Container background - - color-white - - #ffffff - - - - disabledArrowColor - - Caret icon:disabled - - color-grey-500 - - #999999 - - - - disabledAssistiveTextFontColor - - Assistive text:disabled - - color-grey-500 - - #999999 - - - - disabledIconColor - - Custom icon:disabled - - color-grey-500 - - #999999 - - - - disabledSubLabelFontColor - - Sublabel:disabled - - color-grey-700 - - #999999 - - - - disabledTitleLabelFontColor - - Title:disabled - - color-grey-500 - - #999999 - - - - focusBackgroundColor - - Header background:focus - - color-transparent - - transparent - - - - focusBorderColor - - Header outline:focus - - color-blue-600 - - #0095ff - - - - hoverBackgroundColor - - Header background:hover - - color-purple-100 - - #f2eafa - - - - iconColor - - Custom icon - - color-purple-700 - - #5f249f - - - - subLabelFontColor - - Sublabel - - color-grey-500 - - #999999 - - - - titleLabelFontColor - - Title - - color-grey-900 - - #333333 - - - - - boxShadowColor - - Container shadow - - color-grey-200-a - - #0000001a - - - - ), - }, - { - title: "Typography", - content: ( - - - - Component token - Element - Core token - Value - - - - - - assistiveTextFontFamily - - Assistive text - - font-family-sans - - 'Open Sans', sans-serif; - - - - assistiveTextFontSize - - Assistive text - - font-scale-01 - - 0.75rem / 12px - - - - assistiveTextFontStyle - - Assistive text - - font-style-normal - - normal - - - - assistiveTextFontWeight - - Assistive text - - font-weight-light - - 400 - - - - subLabelFontFamily - - Sublabel - - font-family-sans - - 'Open Sans', sans-serif - - - - subLabelFontSize - - Sublabel - - font-scale-01 - - 0.75rem / 12px - - - - subLabelFontStyle - - Sublabel - - font-style-normal - - normal - - - - subLabelFontWeight - - Sublabel - - font-weight-normal - - 400 - - - - titleLabelFontFamily - - Title - - font-family-sans - - 'Open Sans', sans-serif - - - - titleLabelFontSize - - Title - - font-scale-03 - - 1rem / 16px - - - - titleLabelFontStyle - - Title - - font-style-normal - - normal - - - - titleLabelFontWeight - - Title - - font-weight-regular - - 400 - - - - ), - }, - { - title: "Iconography", - content: ( - - - - Component token - Element - Core token - Value - - - - - - iconSize - - Custom icon/Caret icon - - - 24x24px - - - - ), - }, - { - title: "Border", - content: ( - - - - Component token - Element - Core token - Value - - - - - - borderRadius - - Accordion container - - border-radius-medium - - 0.25rem / 4px - - - - focusBorderStyle - - Header:focus border - - border-style-solid - - solid - - - - focusBorderThickness - - Header:focus border - - border-width-2 - - 2px - - - - ), - }, - { - title: "Margin", - content: ( - <> - - Margin properties can be applied independently to top, right,{" "} - bottom and left sides of the card container. - - - - - Margin - Value - - - - - - xxsmall - - 4px - - - - xsmall - - 8px - - - - small - - 12px - - - - medium - - 16px - - - - large - - 24px - - - - xlarge - - 32px - - - - xxlarge - - 48px - - - - - ), - }, - ], - }, - { - title: "Accessibility", - subSections: [ - { - title: "WCAG 2.2", - content: ( - - - Understanding WCAG 2.2 -{" "} - - SC 2.1.1 Keyboard - - - - Understanding WCAG 2.2 -{" "} - - SC 4.1.2 Name, Role, Value - - - - ), - }, - { - title: "WAI-ARIA 1.2", - content: ( - - - WAI-ARIA Authoring Practices 1.2 -{" "} - - 3.1 Accordion (Sections With Show/Hide Functionality) - - - - WAI-ARIA Authoring Practices 1.2 -{" "} - - Accordion Design Pattern - - - - ), - }, - { - title: "Usability known issues", - subSections: [ - { - title: "Printing", - content: ( - - Accordions are often not well suited for printing documents and require people to print snippets of - content at a time. - - ), - }, - ], - }, - ], - }, -]; - -const AccordionSpecsPage = () => { - return ( - - - - - - - ); -}; - -export default AccordionSpecsPage; diff --git a/apps/website/screens/components/accordion/specs/images/accordion_anatomy.png b/apps/website/screens/components/accordion/specs/images/accordion_anatomy.png deleted file mode 100644 index 62369e4b95..0000000000 Binary files a/apps/website/screens/components/accordion/specs/images/accordion_anatomy.png and /dev/null differ diff --git a/apps/website/screens/components/accordion/specs/images/accordion_specs.png b/apps/website/screens/components/accordion/specs/images/accordion_specs.png deleted file mode 100644 index ca2d0df214..0000000000 Binary files a/apps/website/screens/components/accordion/specs/images/accordion_specs.png and /dev/null differ diff --git a/apps/website/screens/components/accordion/specs/images/accordion_states.png b/apps/website/screens/components/accordion/specs/images/accordion_states.png deleted file mode 100644 index 6f503ddb48..0000000000 Binary files a/apps/website/screens/components/accordion/specs/images/accordion_states.png and /dev/null differ diff --git a/apps/website/screens/components/accordion/usage/AccordionUsagePage.tsx b/apps/website/screens/components/accordion/usage/AccordionUsagePage.tsx deleted file mode 100644 index 79b4410f13..0000000000 --- a/apps/website/screens/components/accordion/usage/AccordionUsagePage.tsx +++ /dev/null @@ -1,392 +0,0 @@ -import { DxcBulletedList, DxcFlex, DxcParagraph } from "@dxc-technology/halstack-react"; -import QuickNavContainer from "@/common/QuickNavContainer"; -import QuickNavContainerLayout from "@/common/QuickNavContainerLayout"; -import DocFooter from "@/common/DocFooter"; -import Figure from "@/common/Figure"; -import Image from "@/common/Image"; -import accordionMainParts from "./images/accordion_main_parts.png"; -import accordionElements from "./images/accordion_elements.png"; -import accordionExamples from "./images/accordion_examples.png"; -import accordionContent from "./images/accordion_content.png"; -import accordionPlacement from "./images/accordion_placement.png"; - -const sections = [ - { - title: "Usage", - content: ( - - The accordion component is designed to present large amounts of content in a small space by leveraging - progressive disclosure. It helps improve the user experience by breaking down information into manageable - sections, allowing users to focus only on what they need. - - ), - subSections: [ - { - title: "Main parts", - content: ( - <> - Each accordion section consists of two main parts: -
- Main parts -
- - - Header: Serves as the trigger for expanding or collapsing the section. It acts as a - summary of the content, allowing users to decide if they want to interact with it.{" "} - - - Content area: The expanded section where detailed information or functionality resides.{" "} - - - - ), - subSections: [ - { - title: "Header layout, primary and secondary elements", - content: ( - <> - - The accordion header is divided into two sections: left and right, each of which can contain different - UI elements. These elements are categorised as primary or secondary, based on their importance and - role within the accordion's functionality. - - Accordion elements - - ), - subSections: [ - { - title: "Primary Elements", - content: ( - <> - - The primary elements are mandatory and provide the basic functionality of the - accordion. These ensure the component is functional and intuitive for users: - - - - Title: A concise and descriptive label that summarises the content of the - accordion section. It helps users understand what type of information they can expect to find - inside. - - - Chevron Icon: A visual indicator of the accordion’s current state (expanded or - collapsed). It provides an affordance for interaction and ensures users can toggle the accordion - intuitively. - - - - Without these elements, the accordion cannot effectively communicate its purpose or provide a - clear interaction model. - - - ), - }, - { - title: "Secondary Elements", - content: ( - <> - - The secondary elements are optional and provide additional context or enhance the - user experience. While not essential for the accordion's functionality, they add useful details or - visual hierarchy. - - - - Left secondary elements: - - - Icon: Adds visual context or aids recognition by representing the content - type or purpose of the section. - - - Badge: Displays supplemental information, such as a notification count or - status, to give users a quick overview of the section. - - - - - Right secondary elements: - - - Helper text: Provides additional context, such as brief instructions or a - summary of the content within the section. - - - Status light: Displays a visual indicator of the section’s status (e.g., - completed, in progress, or error). - - - Badge: Similar to the left-side badge, but placed on the right for better - alignment in specific layouts. - - - - -
- Accordion examples -
- - ), - }, - ], - }, - { - title: "Content area", - content: ( - <> - - The content area can contain other components, images, tables, and every custom feature that can be - supported inside the element container. - - Accordion content - - Nesting is allowed and icons can be used as a complement to the header label. - - - ), - }, - ], - }, - { - title: "Placement and alignment", - subSections: [ - { - title: "Placement", - content: ( - <> - - Accordions can be placed with main page content or placed inside of a container such as a side panel - or tile. - -
- Accordion placement -
- - ), - }, - { - title: "Alignment", - content: ( - - By default the chevron icon is placed on the end side of the header. This allows for the title on the - start side to align with other type elements in the layout. - - ), - }, - ], - }, - { - title: "Behavior and interaction", - content: ( - <> - - The accordion component has two main states: collapsed and expanded. The chevron icon at the end of the - accordion indicates which state the accordion is in. Accordions begin by default in the collapsed state - with all content panels closed. Starting in a collapsed state gives the user a high level overview of the - available information. - - - - Trigger collapsed and expanded states when clicking on either the header or icon. - - - Use icons and animation to easily reflect collapsed and expanded states. - - - - Use a chevron icon to indicate the expand/collapse behavior. - - - - When the panel expands, the chevron icon rotates 180 degrees counterclockwise. - - - When the panel collapses, the chevron icon rotates 180 degrees clockwise. - - - - - - ), - subSections: [ - { - title: "Allowing multiple sections open vs single-open behavior", - content: ( - - The accordion component can be configured to allow either multiple sections to be open - simultaneously or limit the user to a single-open section at a time. Each approach has - specific use cases, but it’s important to prioritise user needs and content hierarchy when deciding - which behavior to implement. - - ), - subSections: [ - { - title: "Allow multiple sections open", - content: ( - <> - - This approach gives users full control over the visibility of content, allowing them to open or - collapse multiple sections at the same time. It’s particularly useful when: - - - - Users need to compare or reference information across different sections simultaneously. - - - The content in each section is independent and doesn’t require strict linear navigation. - - - There is enough vertical space to accommodate multiple expanded sections without overwhelming - the layout. - - - - ), - }, - { - title: "Single-open behavior", - content: ( - <> - - Some implementations restrict the accordion to allow only one section to be open - at a time. When a user expands a new section, the previously expanded section collapses - automatically. This pattern is suitable when: - - - - The content is closely related or mutually exclusive, making it logical to view only one section - at a time. - - - Vertical space is limited, and having multiple sections open could cause usability or layout - issues. - - - A simplified and more guided user experience is desired, such as in wizards or step-by-step - processes. - - - - ), - }, - ], - }, - ], - }, - { - title: "Best practices", - content: ( - <> - - - One element per side: Each side of the header (left and{" "} - right) should only include one secondary element to maintain a clean - and organised visual hierarchy. - - - No duplicates: Avoid including multiple instances of the same type of element in the - header (e.g., two badges or two status lights), as this can create visual clutter and confuse users. - - - Semantic colors: If both a badge and a status light{" "} - are included, avoid using semantic colors (e.g., red, green) for the badge to prevent it from competing - visually with the status light. - - - Growth priority: Mandatory and descriptive elements, such as the title, are prioritised - over optional elements to ensure that essential information is always visible and accessible. - - - - ), - subSections: [ - { - title: "Mobile", - content: ( - <> - - In small devices, extremely long pages are detrimental to the user experience. Collapsing information - minimises excessive scrolling and gives an overview of the structure and content available on the - page. - - - In mobile use 100% of the available screen width. - - - ), - }, - { - title: "Do's", - content: ( - <> - Use an accordion when: - - - Displaying and grouping additional information that is related or supplemental to - the primary content. - - - Shortening pages and reducing scrolling, especially for optional or non-critical - content. - - - Providing users with granular control over the visibility of information, helping - them interact with the page in a way that meets their specific needs. - - - Organising FAQs or similar repeated structures, where content can be logically - divided into expandable sections. - - - Enhancing content hierarchy by nesting detailed or secondary content under a more - general overview. - - - - ), - }, - { - title: "Don'ts", - content: ( - <> - Don’t use an accordion if: - - - The majority of the content on the page is crucial for the user to see upfront, as - hiding it may increase friction or confusion. - - - You need to display a list of selectable options (e.g., navigation menus or - filters)—a dropdown or other list pattern is more appropriate. - - - Critical system information or actions (like alerts, confirmations, or primary - buttons) need to be visible—these should remain prominent and accessible without requiring user - interaction. - - - The interaction of expanding and collapsing creates{" "} - unnecessary complexity or extra clicks for the user. - - - - ), - }, - ], - }, - ], - }, -]; - -const AccordionUsagePage = () => { - return ( - - - - - - - ); -}; - -export default AccordionUsagePage; diff --git a/apps/website/screens/components/accordion/usage/images/accordion_content.png b/apps/website/screens/components/accordion/usage/images/accordion_content.png deleted file mode 100644 index bf02e664bd..0000000000 Binary files a/apps/website/screens/components/accordion/usage/images/accordion_content.png and /dev/null differ diff --git a/apps/website/screens/components/accordion/usage/images/accordion_elements.png b/apps/website/screens/components/accordion/usage/images/accordion_elements.png deleted file mode 100644 index b508c9908b..0000000000 Binary files a/apps/website/screens/components/accordion/usage/images/accordion_elements.png and /dev/null differ diff --git a/apps/website/screens/components/accordion/usage/images/accordion_examples.png b/apps/website/screens/components/accordion/usage/images/accordion_examples.png deleted file mode 100644 index d4c3afd9e2..0000000000 Binary files a/apps/website/screens/components/accordion/usage/images/accordion_examples.png and /dev/null differ diff --git a/apps/website/screens/components/accordion/usage/images/accordion_main_parts.png b/apps/website/screens/components/accordion/usage/images/accordion_main_parts.png deleted file mode 100644 index 4aee73941a..0000000000 Binary files a/apps/website/screens/components/accordion/usage/images/accordion_main_parts.png and /dev/null differ diff --git a/apps/website/screens/components/accordion/usage/images/accordion_placement.png b/apps/website/screens/components/accordion/usage/images/accordion_placement.png deleted file mode 100644 index d55759d6ec..0000000000 Binary files a/apps/website/screens/components/accordion/usage/images/accordion_placement.png and /dev/null differ diff --git a/packages/lib/src/accordion/Accordion.tsx b/packages/lib/src/accordion/Accordion.tsx index 180777ce19..c811702131 100644 --- a/packages/lib/src/accordion/Accordion.tsx +++ b/packages/lib/src/accordion/Accordion.tsx @@ -1,15 +1,64 @@ -import { Children, useCallback, useContext, useMemo, useState } from "react"; -import styled, { ThemeProvider } from "styled-components"; +import { Children, useCallback, useMemo, useState } from "react"; +import styled from "styled-components"; import { getMargin } from "../common/utils"; import { spaces } from "../common/variables"; import AccordionPropsType from "./types"; import AccordionContext from "./AccordionContext"; -import HalstackContext from "../HalstackContext"; import AccordionItem from "./AccordionItem"; +const calculateWidth = (margin: AccordionPropsType["margin"]) => + `calc(100% - ${getMargin(margin, "left")} - ${getMargin(margin, "right")})`; + +const AccordionContainer = styled.div<{ + margin: AccordionPropsType["margin"]; +}>` + width: ${(props) => calculateWidth(props.margin)}; + margin: ${({ margin }) => (margin && typeof margin !== "object" ? spaces[margin] : "var(--spacing-padding-none)")}; + margin-top: ${({ margin }) => (margin && typeof margin === "object" && margin.top ? spaces[margin.top] : "")}; + margin-right: ${({ margin }) => (margin && typeof margin === "object" && margin.right ? spaces[margin.right] : "")}; + margin-bottom: ${({ margin }) => + margin && typeof margin === "object" && margin.bottom ? spaces[margin.bottom] : ""}; + margin-left: ${({ margin }) => (margin && typeof margin === "object" && margin.left ? spaces[margin.left] : "")}; + cursor: "pointer"; + + // first accordion + > div:first-of-type:not(:only-of-type) { + border-bottom-left-radius: var(--border-radius-none); + border-bottom-right-radius: var(--border-radius-none); + border-top-left-radius: var(--border-radius-s); + border-top-right-radius: var(--border-radius-s); + } + + // first accordion: hover, focus and active + > div:first-of-type:not(:only-of-type) button:is(:hover, :focus, :active) { + border-bottom-left-radius: var(--border-radius-none); + border-bottom-right-radius: var(--border-radius-none); + } + + // middle accordions + > div:first-of-type:not(:only-of-type), + div:first-of-type:not(:only-of-type) button:is(:hover, :focus, :active) { + border-radius: var(--border-radius-none); + } + + // last accordion + > div:last-of-type:not(:only-of-type), + div:last-of-type:not(:only-of-type) button:is(:hover, :focus, :active) { + border-top-left-radius: var(--border-radius-none); + border-top-right-radius: var(--border-radius-none); + border-bottom-left-radius: var(--border-radius-s); + border-bottom-right-radius: var(--border-radius-s); + } + + // last expanded accordion + > div:last-of-type:not(:only-of-type) > button[aria-expanded="true"], + div:last-of-type:not(:only-of-type) > button[aria-expanded="true"]:is(:hover, :focus, :active) { + border-radius: var(--border-radius-none); + } +`; + const DxcAccordion = (props: AccordionPropsType): JSX.Element => { const { children, margin, onActiveChange } = props; - const colorsTheme = useContext(HalstackContext); const [innerIndexActive, setInnerIndexActive] = useState( props.independent @@ -49,98 +98,16 @@ const DxcAccordion = (props: AccordionPropsType): JSX.Element => { ); return ( - - - {Children.map(children, (accordion, index) => ( - - {accordion} - - ))} - - + + {Children.map(children, (accordion, index) => ( + + {accordion} + + ))} + ); }; DxcAccordion.AccordionItem = AccordionItem; -const calculateWidth = (margin: AccordionPropsType["margin"]) => - `calc(100% - ${getMargin(margin, "left")} - ${getMargin(margin, "right")})`; - -const AccordionContainer = styled.div<{ - margin: AccordionPropsType["margin"]; -}>` - width: ${(props) => calculateWidth(props.margin)}; - margin: ${({ margin }) => (margin && typeof margin !== "object" ? spaces[margin] : "0px")}; - margin-top: ${({ margin }) => (margin && typeof margin === "object" && margin.top ? spaces[margin.top] : "")}; - margin-right: ${({ margin }) => (margin && typeof margin === "object" && margin.right ? spaces[margin.right] : "")}; - margin-bottom: ${({ margin }) => - margin && typeof margin === "object" && margin.bottom ? spaces[margin.bottom] : ""}; - margin-left: ${({ margin }) => (margin && typeof margin === "object" && margin.left ? spaces[margin.left] : "")}; - cursor: "pointer"; - - // first and middle accordions (separator) - > div:not(:last-of-type):not(:only-of-type) { - border-bottom: ${(props) => - `${props.theme.accordionSeparatorBorderThickness} ${props.theme.accordionSeparatorBorderStyle}`}; - border-color: ${(props) => props.theme.accordionSeparatorBorderColor}; - } - - // first accordion - > div:first-of-type:not(:only-of-type) { - border-bottom-left-radius: 0; - border-bottom-right-radius: 0; - border-top-left-radius: ${(props) => props.theme.borderRadius}; - border-top-right-radius: ${(props) => props.theme.borderRadius}; - } - - // first accordion: hover, focus and active - > div:first-of-type:not(:only-of-type) button:hover, - div:first-of-type:not(:only-of-type) button:focus, - div:first-of-type:not(:only-of-type) button:active { - border-bottom-left-radius: 0; - border-bottom-right-radius: 0; - } - - // middle accordions - > div:not(:first-of-type):not(:last-of-type):not(:only-of-type) { - border-radius: 0; - } - - // middle accordions: hover, focus and active - > div:not(:first-of-type):not(:last-of-type):not(:only-of-type) button:hover, - div:not(:first-of-type):not(:last-of-type):not(:only-of-type) button:focus, - div:not(:first-of-type):not(:last-of-type):not(:only-of-type) button:active { - border-radius: 0; - } - - // last accordion - > div:last-of-type:not(:only-of-type) { - border-top-left-radius: 0; - border-top-right-radius: 0; - border-bottom-left-radius: ${(props) => props.theme.borderRadius}; - border-bottom-right-radius: ${(props) => props.theme.borderRadius}; - } - - // last accordion: hover, focus and active - > div:last-of-type:not(:only-of-type) button:hover, - div:last-of-type:not(:only-of-type) button:focus, - div:last-of-type:not(:only-of-type) button:active { - border-top-left-radius: 0; - border-top-right-radius: 0; - border-bottom-left-radius: ${(props) => props.theme.borderRadius}; - border-bottom-right-radius: ${(props) => props.theme.borderRadius}; - } - - // last expanded accordion - > div:last-of-type:not(:only-of-type) > button[aria-expanded="true"] { - border-radius: 0; - } - // last expanded accordion: hover, focus and active - > div:last-of-type:not(:only-of-type) > button[aria-expanded="true"]:hover, - div:last-of-type:not(:only-of-type) > button[aria-expanded="true"]:focus, - div:last-of-type:not(:only-of-type) > button[aria-expanded="true"]:active { - border-radius: 0; - } -`; - export default DxcAccordion; diff --git a/packages/lib/src/accordion/AccordionItem.tsx b/packages/lib/src/accordion/AccordionItem.tsx index c2ce6a448b..2901614cac 100644 --- a/packages/lib/src/accordion/AccordionItem.tsx +++ b/packages/lib/src/accordion/AccordionItem.tsx @@ -1,5 +1,5 @@ import { ReactElement, useContext, useId, cloneElement, useMemo } from "react"; -import styled, { ThemeProvider } from "styled-components"; +import styled from "styled-components"; import HalstackContext from "../HalstackContext"; import { AccordionItemProps } from "./types"; import DxcIcon from "../icon/Icon"; @@ -8,104 +8,13 @@ import DxcContainer from "../container/Container"; import React from "react"; import AccordionContext from "./AccordionContext"; -const AccordionItem = ({ - label = "", - subLabel = "", - badge, - statusLight, - icon, - assistiveText = "", - disabled = false, - children, - tabIndex = 0, -}: AccordionItemProps): JSX.Element => { - const id = useId(); - const colorsTheme = useContext(HalstackContext); - const { activeIndex, handlerActiveChange, index, independent } = useContext(AccordionContext) ?? {}; - const isItemExpanded = useMemo(() => { - return independent - ? activeIndex === index - : Array.isArray(activeIndex) && index !== undefined && activeIndex.includes(index); - }, [independent, activeIndex, index]); - - const handleAccordionState = () => { - if (index !== undefined) handlerActiveChange?.(index); - }; - - return ( - - - - - - - - {(icon || badge?.position === "before") && ( - - {icon ? ( - - {typeof icon === "string" ? : icon} - - ) : ( - - {disabled ? cloneElement(badge?.element as ReactElement, { color: "grey" }) : badge?.element} - - )} - - )} - - {label} - {subLabel && {subLabel}} - - - - - {assistiveText && ( - - {assistiveText} - - )} - {badge && badge?.position === "after" && !assistiveText && ( - - {disabled ? React.cloneElement(badge.element as ReactElement, { color: "grey" }) : badge.element} - - )} - {badge?.position !== "after" && statusLight && !assistiveText && ( - - {disabled ? React.cloneElement(statusLight as ReactElement, { mode: "default" }) : statusLight} - - )} - - - - - - - - {isItemExpanded && ( - - {children} - - )} - - - ); -}; - const AccordionContainer = styled.div` display: flex; flex-direction: column; - background-color: ${(props) => props.theme.backgroundColor}; - border-radius: ${(props) => props.theme.borderRadius}; - box-shadow: ${(props) => - `${props.theme.boxShadowOffsetX} ${props.theme.boxShadowOffsetY} ${props.theme.boxShadowBlur} ${props.theme.boxShadowSpread} ${props.theme.boxShadowColor}`}; + background-color: var(--color-bg-neutral-lightest); + border-radius: var(--border-radius-s); + box-shadow: var(--shadow-mid-x-position) var(--shadow-mid-y-position) var(--shadow-mid-blur) var(--shadow-mid-spread) + var(--shadow-light); min-width: 280px; width: 100%; `; @@ -113,40 +22,42 @@ const AccordionContainer = styled.div` const AccordionTrigger = styled.button` display: flex; justify-content: space-between; - gap: 1.5rem; width: 100%; background-color: transparent; border: none; - border-radius: ${(props) => props.theme.borderRadius}; - padding: 8px 16px; + padding: var(--spacing-padding-xs) var(--spacing-padding-m); + border-radius: var(--border-radius-s); cursor: ${(props) => (props.disabled ? "not-allowed" : "pointer")}; - :focus { - background-color: ${(props) => `${props.theme.focusBackgroundColor}`}; - box-shadow: inset 0 0 0 ${(props) => props.theme.focusBorderThickness} ${(props) => props.theme.focusBorderColor}; - } + :focus, :focus-visible { - background-color: ${(props) => `${props.theme.focusBackgroundColor}`}; - box-shadow: inset 0 0 0 ${(props) => props.theme.focusBorderThickness} ${(props) => props.theme.focusBorderColor}; - outline: none; + outline: var(--border-width-m) var(--border-style-default) var(--border-color-secondary-medium); + } + :hover:enabled, + :active:enabled { + background-color: var(--color-bg-primary-lighter); } :active:enabled { - background-color: ${(props) => `${props.theme.activeBackgroundColor}`}; - box-shadow: inset 0 0 0 ${(props) => props.theme.focusBorderThickness} ${(props) => props.theme.focusBorderColor}; + outline: var(--border-width-m) var(--border-style-default) var(--border-color-secondary-medium); } - :hover:enabled { - background-color: ${(props) => `${props.theme.hoverBackgroundColor}`}; + + &[aria-expanded="true"] { + border-bottom-left-radius: var(--border-radius-none); + border-bottom-right-radius: var(--border-radius-none); } `; + const LeftSideContainer = styled.div` flex: 1; overflow: hidden; + display: flex; + gap: var(--spacing-gap-m); `; const RightSideContainer = styled.div` display: flex; overflow: hidden; justify-content: flex-end; - gap: 0.5rem; + gap: var(--spacing-gap-s); max-width: 30%; flex-shrink: 0; `; @@ -165,39 +76,34 @@ const LabelsContainer = styled.div` const StatusContainer = styled.div<{ subLabel: AccordionItemProps["subLabel"] }>` display: flex; align-items: ${(props) => (props.subLabel ? "flex-start" : "center")}; - margin-top: ${(props) => props.subLabel && "4px"}; `; const IconContainer = styled.span<{ disabled: AccordionItemProps["disabled"] }>` display: flex; - color: ${(props) => (props.disabled ? props.theme.disabledIconColor : props.theme.iconColor)}; - font-size: ${(props) => props.theme.iconSize}; + color: ${(props) => (props.disabled ? "var(--color-fg-neutral-medium)" : "var(--color-fg-primary-strong)")}; + font-size: var(--height-s); svg { - height: ${(props) => props.theme.iconSize}; - width: ${(props) => props.theme.iconSize}; + height: var(--height-s); + width: 24px; } `; const AccordionLabel = styled.span<{ disabled: AccordionItemProps["disabled"] }>` - color: ${(props) => (props.disabled ? props.theme.disabledTitleLabelFontColor : props.theme.titleLabelFontColor)}; - font-family: ${(props) => props.theme.titleLabelFontFamily}; - font-size: ${(props) => props.theme.titleLabelFontSize}; - font-style: ${(props) => props.theme.titleLabelFontStyle}; - font-weight: ${(props) => props.theme.titleLabelFontWeight}; - line-height: 1.5em; + color: ${(props) => (props.disabled ? "var(--color-fg-neutral-medium)" : "var(--color-fg-neutral-dark)")}; + font-family: var(--typography-font-family); + font-size: var(--typography-label-l); + font-weight: var(--typography-label-regular); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; + text-align: left; `; const SubLabel = styled.span<{ disabled: AccordionItemProps["disabled"] }>` - height: 20px; - color: ${(props) => (props.disabled ? props.theme.disabledSubLabelFontColor : props.theme.subLabelFontColor)}; - font-family: ${(props) => props.theme.subLabelFontFamily}; - font-size: ${(props) => props.theme.subLabelFontSize}; - font-style: ${(props) => props.theme.subLabelFontStyle}; - font-weight: ${(props) => props.theme.subLabelFontWeight}; - line-height: 1.5em; + color: ${(props) => (props.disabled ? "var(--color-fg-neutral-medium)" : "var(--color-fg-neutral-stronger)")}; + font-family: var(--typography-font-family); + font-size: var(--typography-helper-text-s); + font-weight: var(--typography-helper-text-regular); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; @@ -208,18 +114,14 @@ const AssistiveText = styled.span<{ disabled: AccordionItemProps["disabled"]; subLabel: AccordionItemProps["subLabel"]; }>` - color: ${(props) => - props.disabled ? props.theme.disabledAssistiveTextFontColor : props.theme.assistiveTextFontColor}; - font-family: ${(props) => props.theme.assistiveTextFontFamily}; - font-size: ${(props) => props.theme.assistiveTextFontSize}; - font-style: ${(props) => props.theme.assistiveTextFontStyle}; - font-weight: ${(props) => props.theme.assistiveTextFontWeight}; - line-height: 1.5em; + color: ${(props) => (props.disabled ? "var(--color-fg-neutral-medium)" : "var(--color-fg-neutral-stronger)")}; + font-family: var(--typography-font-family); + font-size: var(--typography-helper-text-s); + font-weight: var(--typography-helper-text-regular); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; align-content: ${(props) => !props.subLabel && "center"}; - margin-top: ${(props) => props.subLabel && "4px"}; `; const CollapseIndicator = styled.span<{ @@ -228,16 +130,103 @@ const CollapseIndicator = styled.span<{ display: flex; flex-wrap: wrap; font-size: 24px; - color: ${(props) => (props.disabled ? props.theme.disabledArrowColor : props.theme.arrowColor)}; + color: ${(props) => (props.disabled ? "var(--color-fg-neutral-medium)" : "var(--color-fg-primary-strong)")}; svg { - height: ${(props) => props.theme.iconSize}; - width: ${(props) => props.theme.iconSize}; + height: var(--height-s); + width: 24px; } `; const AccordionPanel = styled.div` - border-bottom-left-radius: ${(props) => props.theme.borderRadius}; - border-bottom-right-radius: ${(props) => props.theme.borderRadius}; + border-bottom-left-radius: var(--border-radius-s); + border-bottom-right-radius: var(--border-radius-s); + padding: var(--spacing-padding-m); `; +const AccordionItem = ({ + label = "", + subLabel = "", + badge, + statusLight, + icon, + assistiveText = "", + disabled = false, + children, + tabIndex = 0, +}: AccordionItemProps): JSX.Element => { + const id = useId(); + const { activeIndex, handlerActiveChange, index, independent } = useContext(AccordionContext) ?? {}; + const isItemExpanded = useMemo(() => { + return independent + ? activeIndex === index + : Array.isArray(activeIndex) && index !== undefined && activeIndex.includes(index); + }, [independent, activeIndex, index]); + + const handleAccordionState = () => { + if (index !== undefined) handlerActiveChange?.(index); + }; + + return ( + + + + + + {(icon || badge?.position === "before") && ( + + {icon ? ( + + {typeof icon === "string" ? : icon} + + ) : ( + + {disabled ? cloneElement(badge?.element as ReactElement, { color: "grey" }) : badge?.element} + + )} + + )} + + {label} + {subLabel && {subLabel}} + + + + {assistiveText && ( + + {assistiveText} + + )} + {badge && badge?.position === "after" && !assistiveText && ( + + {disabled ? React.cloneElement(badge.element as ReactElement, { color: "grey" }) : badge.element} + + )} + {badge?.position !== "after" && statusLight && !assistiveText && ( + + {disabled ? React.cloneElement(statusLight as ReactElement, { mode: "default" }) : statusLight} + + )} + + + + + + + + {isItemExpanded && ( + + {children} + + )} + + ); +}; + export default AccordionItem;