From b2284087f1dee699158180e501953f289adb516c Mon Sep 17 00:00:00 2001 From: Tyler Rudy Date: Fri, 24 May 2024 20:20:11 -0600 Subject: [PATCH 1/4] feat(accordion): clickable title --- src/components/Accordion.stories.tsx | 5 ++++ src/components/Accordion.test.tsx | 34 +++++++++++++++++++++++++++- src/components/Accordion.tsx | 28 +++++++++++++++++++---- 3 files changed, 61 insertions(+), 6 deletions(-) diff --git a/src/components/Accordion.stories.tsx b/src/components/Accordion.stories.tsx index a2056da81..ba01de0cc 100644 --- a/src/components/Accordion.stories.tsx +++ b/src/components/Accordion.stories.tsx @@ -31,6 +31,11 @@ export function AccordionVariations() { Our modern approach to homebuilding makes the process easier and more personal than ever before. + {}}> +
+ Our modern approach to homebuilding makes the process easier and more personal than ever before. +
+
); } diff --git a/src/components/Accordion.test.tsx b/src/components/Accordion.test.tsx index 63e51c4b0..203aec286 100644 --- a/src/components/Accordion.test.tsx +++ b/src/components/Accordion.test.tsx @@ -1,4 +1,4 @@ -import { render } from "src/utils/rtl"; +import { click, render } from "src/utils/rtl"; import { Accordion } from "./Accordion"; describe(Accordion, () => { @@ -72,4 +72,36 @@ describe(Accordion, () => { // And the content is not displayed expect(r.query.accordion_content).not.toBeInTheDocument(); }); + + it("calls the titleOnClick function when the title is clicked", async () => { + // Given an accordion component with titleOnClick set + const titleOnClick = jest.fn(); + // When rendered + const r = await render( + + Test description + , + ); + + // Then the titleOnClick function is called when the title is clicked + click(r.accordion_titleText); + expect(titleOnClick).toHaveBeenCalled(); + }); + + it("alters expando behavior when titleOnClick is provided", async () => { + // When rendered with a titleOnClick set + const r = await render( + {}}> + Test description + , + ); + + click(r.accordion_titleText); + // And the content is not displayed + expect(r.query.accordion_content).not.toBeInTheDocument(); + + // Then the content is displayed when the title is clicked + click(r.accordion_title); + expect(r.accordion_content).toHaveTextContent("Test description"); + }); }); diff --git a/src/components/Accordion.tsx b/src/components/Accordion.tsx index 5fd43e152..c5eb29b52 100644 --- a/src/components/Accordion.tsx +++ b/src/components/Accordion.tsx @@ -23,6 +23,8 @@ export interface AccordionProps { */ index?: number; setExpandedIndex?: Dispatch>; + /** Turns the title into a button. If provided, disables expand/collapse on title text */ + titleOnClick?: VoidFunction; /** Used by Accordion list. Sets default padding to 0 for nested accordions */ omitPadding?: boolean; /** Styles overrides for padding */ @@ -43,6 +45,7 @@ export function Accordion>(props: AccordionProps bottomBorder = false, index, setExpandedIndex, + titleOnClick, omitPadding = false, xss, } = props; @@ -76,6 +79,11 @@ export function Accordion>(props: AccordionProps }, [expanded, setContentHeight]); useResizeObserver({ ref: contentRef, onResize }); + const expandOrCollapse = useCallback(() => { + setExpanded((prior) => !prior); + if (setExpandedIndex) setExpandedIndex(index); + }, [index, setExpandedIndex]); + return (
>(props: AccordionProps ...(isFocusVisible && Css.boxShadow(`inset 0 0 0 2px ${Palette.Blue700}`).$), ...xss, }} - onClick={() => { - setExpanded(!expanded); - if (setExpandedIndex) setExpandedIndex(index); - }} + onClick={expandOrCollapse} > - {title} + {titleOnClick ? ( + + ) : ( + {title} + )} Date: Fri, 24 May 2024 20:23:57 -0600 Subject: [PATCH 2/4] selfnit --- src/components/Accordion.tsx | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/components/Accordion.tsx b/src/components/Accordion.tsx index c5eb29b52..0cc9a2b23 100644 --- a/src/components/Accordion.tsx +++ b/src/components/Accordion.tsx @@ -79,11 +79,6 @@ export function Accordion>(props: AccordionProps }, [expanded, setContentHeight]); useResizeObserver({ ref: contentRef, onResize }); - const expandOrCollapse = useCallback(() => { - setExpanded((prior) => !prior); - if (setExpandedIndex) setExpandedIndex(index); - }, [index, setExpandedIndex]); - return (
>(props: AccordionProps ...(isFocusVisible && Css.boxShadow(`inset 0 0 0 2px ${Palette.Blue700}`).$), ...xss, }} - onClick={expandOrCollapse} + onClick={() => { + setExpanded((prior) => !prior); + if (setExpandedIndex) setExpandedIndex(index); + }} > {titleOnClick ? (
+ +
+ Our modern approach to homebuilding makes the process easier and more personal than ever before. +
+
+ {}} compact> +
+ Our modern approach to homebuilding makes the process easier and more personal than ever before. +
+
); } diff --git a/src/components/Accordion.test.tsx b/src/components/Accordion.test.tsx index 203aec286..cd69c2750 100644 --- a/src/components/Accordion.test.tsx +++ b/src/components/Accordion.test.tsx @@ -84,7 +84,7 @@ describe(Accordion, () => { ); // Then the titleOnClick function is called when the title is clicked - click(r.accordion_titleText); + click(r.accordion_title); expect(titleOnClick).toHaveBeenCalled(); }); @@ -96,12 +96,14 @@ describe(Accordion, () => { , ); - click(r.accordion_titleText); - // And the content is not displayed + // when the title is clicked + click(r.accordion_title); + // then the content is not displayed expect(r.query.accordion_content).not.toBeInTheDocument(); - // Then the content is displayed when the title is clicked - click(r.accordion_title); + // when bar is clicked + click(r.accordion_toggle); + // then the content is displayed expect(r.accordion_content).toHaveTextContent("Test description"); }); }); diff --git a/src/components/Accordion.tsx b/src/components/Accordion.tsx index 0cc9a2b23..a230978d3 100644 --- a/src/components/Accordion.tsx +++ b/src/components/Accordion.tsx @@ -1,5 +1,5 @@ import { useId, useResizeObserver } from "@react-aria/utils"; -import { Dispatch, ReactNode, SetStateAction, useCallback, useEffect, useRef, useState } from "react"; +import { Dispatch, ReactNode, SetStateAction, useCallback, useEffect, useMemo, useRef, useState } from "react"; import { useFocusRing } from "react-aria"; import { Icon } from "src/components/Icon"; import { Css, Only, Padding, Palette, Xss } from "src/Css"; @@ -49,7 +49,7 @@ export function Accordion>(props: AccordionProps omitPadding = false, xss, } = props; - const testIds = useTestIds(props, "accordion"); + const tid = useTestIds(props, "accordion"); const id = useId(); const [expanded, setExpanded] = useState(defaultExpanded && !disabled); const { isFocusVisible, focusProps } = useFocusRing(); @@ -79,64 +79,62 @@ export function Accordion>(props: AccordionProps }, [expanded, setContentHeight]); useResizeObserver({ ref: contentRef, onResize }); + const toggle = useCallback(() => { + setExpanded((prev) => !prev); + if (setExpandedIndex) setExpandedIndex(index); + }, [index, setExpandedIndex]); + + const touchableStyle = useMemo( + () => ({ + ...Css.df.jcsb.gapPx(12).aic.p2.baseMd.outline("none").onHover.bgGray100.if(!!titleOnClick).baseSb.$, + ...(compact && Css.smMd.pl2.prPx(10).py1.bgGray100.mbPx(4).br8.onHover.bgGray200.$), + ...(compact && !!titleOnClick && Css.br0.$), + ...(disabled && Css.gray500.$), + ...(isFocusVisible && Css.boxShadow(`inset 0 0 0 2px ${Palette.Blue700}`).$), + ...xss, + }), + [compact, disabled, isFocusVisible, titleOnClick, xss], + ); + return (
- - ) : ( - {title} - )} - + + +
+ ) : ( + + {title} + + + )}
{expanded && ( -
+
{children}
)} @@ -145,6 +143,20 @@ export function Accordion>(props: AccordionProps ); } +function RotatingChevronIcon(props: { expanded: boolean }) { + return ( + + + + ); +} + export type AccordionSize = "xs" | "sm" | "md" | "lg"; const accordionSizes: Record = { From bd676852034ff4af0a552e8a4d8db85df189afbf Mon Sep 17 00:00:00 2001 From: Tyler Rudy Date: Wed, 29 May 2024 13:04:59 -0600 Subject: [PATCH 4/4] rm jcsb --- src/components/Accordion.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Accordion.tsx b/src/components/Accordion.tsx index a230978d3..435f0b0ad 100644 --- a/src/components/Accordion.tsx +++ b/src/components/Accordion.tsx @@ -105,7 +105,7 @@ export function Accordion>(props: AccordionProps }} > {titleOnClick ? ( -
+