From 58e0071d2ef57de7ded5f633024a868decb8e833 Mon Sep 17 00:00:00 2001 From: Eric Olkowski <70952936+thatblindgeye@users.noreply.github.com> Date: Wed, 20 Dec 2023 15:02:27 -0500 Subject: [PATCH] feat(Accordion): added toggle alignment functionality (#9877) * feat(Accordion): added toggle alignment functionality * Updated prop name and description --- .../src/components/Accordion/Accordion.tsx | 7 +- .../components/Accordion/AccordionContext.ts | 1 + .../components/Accordion/AccordionToggle.tsx | 55 +++++---- .../Accordion/__tests__/Accordion.test.tsx | 12 ++ .../__tests__/AccordionToggle.test.tsx | 28 +++++ .../Accordion/examples/Accordion.md | 5 + .../examples/AccordionToggleIconAtStart.tsx | 107 ++++++++++++++++++ 7 files changed, 191 insertions(+), 24 deletions(-) create mode 100644 packages/react-core/src/components/Accordion/examples/AccordionToggleIconAtStart.tsx diff --git a/packages/react-core/src/components/Accordion/Accordion.tsx b/packages/react-core/src/components/Accordion/Accordion.tsx index 386106770ed..fedc1e21ee0 100644 --- a/packages/react-core/src/components/Accordion/Accordion.tsx +++ b/packages/react-core/src/components/Accordion/Accordion.tsx @@ -18,6 +18,8 @@ export interface AccordionProps extends React.HTMLProps { isBordered?: boolean; /** Display size variant. */ displaySize?: 'default' | 'lg'; + /** Sets the toggle icon position for all accordion toggles. */ + togglePosition?: 'start' | 'end'; } export const Accordion: React.FunctionComponent = ({ @@ -28,6 +30,7 @@ export const Accordion: React.FunctionComponent = ({ asDefinitionList = true, isBordered = false, displaySize = 'default', + togglePosition = 'end', ...props }: AccordionProps) => { const AccordionList: any = asDefinitionList ? 'dl' : 'div'; @@ -36,6 +39,7 @@ export const Accordion: React.FunctionComponent = ({ className={css( styles.accordion, isBordered && styles.modifiers.bordered, + togglePosition === 'start' && styles.modifiers.toggleStart, displaySize === 'lg' && styles.modifiers.displayLg, className )} @@ -46,7 +50,8 @@ export const Accordion: React.FunctionComponent = ({ {children} diff --git a/packages/react-core/src/components/Accordion/AccordionContext.ts b/packages/react-core/src/components/Accordion/AccordionContext.ts index 8df38f401fa..99ba8a5b6c4 100644 --- a/packages/react-core/src/components/Accordion/AccordionContext.ts +++ b/packages/react-core/src/components/Accordion/AccordionContext.ts @@ -3,6 +3,7 @@ import * as React from 'react'; interface AccordionContextProps { ContentContainer: React.ElementType; ToggleContainer: React.ElementType; + togglePosition: 'start' | 'end'; } export const AccordionContext = React.createContext>({}); diff --git a/packages/react-core/src/components/Accordion/AccordionToggle.tsx b/packages/react-core/src/components/Accordion/AccordionToggle.tsx index a2ec5b28124..dd9438cb0a9 100644 --- a/packages/react-core/src/components/Accordion/AccordionToggle.tsx +++ b/packages/react-core/src/components/Accordion/AccordionToggle.tsx @@ -25,27 +25,36 @@ export const AccordionToggle: React.FunctionComponent = ({ children = null, component, ...props -}: AccordionToggleProps) => ( - - {({ ToggleContainer }) => { - const Container = component || ToggleContainer; - return ( - - - - ); - }} - -); +}: AccordionToggleProps) => { + const renderToggleIcon = () => ( + + + + ); + + return ( + + {({ ToggleContainer, togglePosition }) => { + const Container = component || ToggleContainer; + const isToggleStartPositioned = togglePosition === 'start'; + + return ( + + + + ); + }} + + ); +}; AccordionToggle.displayName = 'AccordionToggle'; diff --git a/packages/react-core/src/components/Accordion/__tests__/Accordion.test.tsx b/packages/react-core/src/components/Accordion/__tests__/Accordion.test.tsx index c5abf0c8c68..4d347f5d0eb 100644 --- a/packages/react-core/src/components/Accordion/__tests__/Accordion.test.tsx +++ b/packages/react-core/src/components/Accordion/__tests__/Accordion.test.tsx @@ -135,6 +135,18 @@ test('Renders with pf-m-display-lg when displaySize="lg"', () => { expect(screen.getByText('Test')).toHaveClass('pf-m-display-lg'); }); +test(`Renders without class ${styles.modifiers.toggleStart} by default`, () => { + render(Test); + + expect(screen.getByText('Test')).not.toHaveClass(styles.modifiers.toggleStart); +}); + +test(`Renders with class ${styles.modifiers.toggleStart} when togglePosition='start'`, () => { + render(Test); + + expect(screen.getByText('Test')).toHaveClass(styles.modifiers.toggleStart); +}); + test('Matches the snapshot', () => { const { asFragment } = render(); expect(asFragment()).toMatchSnapshot(); diff --git a/packages/react-core/src/components/Accordion/__tests__/AccordionToggle.test.tsx b/packages/react-core/src/components/Accordion/__tests__/AccordionToggle.test.tsx index dd39f0359ea..9610a0b5af3 100644 --- a/packages/react-core/src/components/Accordion/__tests__/AccordionToggle.test.tsx +++ b/packages/react-core/src/components/Accordion/__tests__/AccordionToggle.test.tsx @@ -155,6 +155,34 @@ test('Renders the toggle with pf-m-expanded and aria-expanded=true when isExpand expect(toggle).toHaveAttribute('aria-expanded', 'true'); }); +test('Renders toggle text before toggle icon by default', () => { + render( + + + Test + + + ); + + const toggle = screen.getByRole('button'); + + expect(toggle.firstChild).toHaveClass(styles.accordionToggleText); +}); + +test('Renders toggle icon before toggle text when togglePosition from context = "start"', () => { + render( + + + Test + + + ); + + const toggle = screen.getByRole('button'); + + expect(toggle.firstChild).toHaveClass(styles.accordionToggleIcon); +}); + test('Matches the snapshot', () => { const { asFragment } = render( diff --git a/packages/react-core/src/components/Accordion/examples/Accordion.md b/packages/react-core/src/components/Accordion/examples/Accordion.md index aea98f99b8e..cac3b5cc699 100644 --- a/packages/react-core/src/components/Accordion/examples/Accordion.md +++ b/packages/react-core/src/components/Accordion/examples/Accordion.md @@ -28,3 +28,8 @@ import ArrowRightIcon from '@patternfly/react-icons/dist/esm/icons/arrow-right-i ```ts file="./AccordionBordered.tsx" ``` + +### Toggle icon at start + +```ts file="./AccordionToggleIconAtStart.tsx" +``` \ No newline at end of file diff --git a/packages/react-core/src/components/Accordion/examples/AccordionToggleIconAtStart.tsx b/packages/react-core/src/components/Accordion/examples/AccordionToggleIconAtStart.tsx new file mode 100644 index 00000000000..9815346c0bc --- /dev/null +++ b/packages/react-core/src/components/Accordion/examples/AccordionToggleIconAtStart.tsx @@ -0,0 +1,107 @@ +import React from 'react'; +import { Accordion, AccordionItem, AccordionContent, AccordionToggle } from '@patternfly/react-core'; + +export const AccordionToggleIconAtStart: React.FunctionComponent = () => { + const [expanded, setExpanded] = React.useState('start-toggle-toggle2'); + + const onToggle = (id: string) => { + if (id === expanded) { + setExpanded(''); + } else { + setExpanded(id); + } + }; + + return ( + + + { + onToggle('start-toggle-toggle1'); + }} + isExpanded={expanded === 'start-toggle-toggle1'} + id="start-toggle-toggle1" + > + Item one + + +

+ Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et + dolore magna aliqua. +

+
+
+ + + { + onToggle('start-toggle-toggle2'); + }} + isExpanded={expanded === 'start-toggle-toggle2'} + id="start-toggle-toggle2" + > + Item two + + +

+ Vivamus et tortor sed arcu congue vehicula eget et diam. Praesent nec dictum lorem. Aliquam id diam + ultrices, faucibus erat id, maximus nunc. +

+
+
+ + + { + onToggle('start-toggle-toggle3'); + }} + isExpanded={expanded === 'start-toggle-toggle3'} + id="start-toggle-toggle3" + > + Item three + + +

Morbi vitae urna quis nunc convallis hendrerit. Aliquam congue orci quis ultricies tempus.

+
+
+ + + { + onToggle('start-toggle-toggle4'); + }} + isExpanded={expanded === 'start-toggle-toggle4'} + id="start-toggle-toggle4" + > + Item four + + +

+ Donec vel posuere orci. Phasellus quis tortor a ex hendrerit efficitur. Aliquam lacinia ligula pharetra, + sagittis ex ut, pellentesque diam. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere + cubilia Curae; Vestibulum ultricies nulla nibh. Etiam vel dui fermentum ligula ullamcorper eleifend non quis + tortor. Morbi tempus ornare tempus. Orci varius natoque penatibus et magnis dis parturient montes, nascetur + ridiculus mus. Mauris et velit neque. Donec ultricies condimentum mauris, pellentesque imperdiet libero + convallis convallis. Aliquam erat volutpat. Donec rutrum semper tempus. Proin dictum imperdiet nibh, quis + dapibus nulla. Integer sed tincidunt lectus, sit amet auctor eros. +

+
+
+ + + { + onToggle('start-toggle-toggle5'); + }} + isExpanded={expanded === 'start-toggle-toggle5'} + id="start-toggle-toggle5" + > + Item five + + +

Vivamus finibus dictum ex id ultrices. Mauris dictum neque a iaculis blandit.

+
+
+
+ ); +};