From 25b0c5a39ba12ce150919445ca285ff78f9ecafe Mon Sep 17 00:00:00 2001 From: asimbhdr96 Date: Fri, 16 Sep 2022 15:13:26 -0400 Subject: [PATCH 1/9] feature/Accordion component created --- src/components/Accordion/Accordion.test.tsx | 0 src/components/Accordion/Accordion.tsx | 40 +++++++++++++++ src/components/Accordion/AccordionDetail.scss | 10 ++++ src/components/Accordion/AccordionDetail.tsx | 33 +++++++++++++ src/components/Accordion/AccordionHeader.scss | 49 +++++++++++++++++++ src/components/Accordion/AccordionHeader.tsx | 36 ++++++++++++++ 6 files changed, 168 insertions(+) create mode 100644 src/components/Accordion/Accordion.test.tsx create mode 100644 src/components/Accordion/Accordion.tsx create mode 100644 src/components/Accordion/AccordionDetail.scss create mode 100644 src/components/Accordion/AccordionDetail.tsx create mode 100644 src/components/Accordion/AccordionHeader.scss create mode 100644 src/components/Accordion/AccordionHeader.tsx diff --git a/src/components/Accordion/Accordion.test.tsx b/src/components/Accordion/Accordion.test.tsx new file mode 100644 index 00000000..e69de29b diff --git a/src/components/Accordion/Accordion.tsx b/src/components/Accordion/Accordion.tsx new file mode 100644 index 00000000..8635aed5 --- /dev/null +++ b/src/components/Accordion/Accordion.tsx @@ -0,0 +1,40 @@ +import React, { ReactNode, useState } from "react"; +import AccordionDetail from "./AccordionDetail"; +import AccordionHeader from "./AccordionHeader"; + +export type AccordionData = { + title: string; + content: ReactNode; +}; + +function Accordion({ children }: { children: JSX.Element[] }) { + const [open, setOpen] = useState(false); + const btnOnClick = (a: boolean) => { + setOpen(!a); + }; + return ( +
+ {children.map((elem, idx) => { + if (elem.type.name === "AccordionHeader") { + return ( + btnOnClick(open)} + key={idx} //uid + > + {elem.props.children} + + ); + } else { + return ( + + {elem.props.children} + + ); + } + })} +
+ ); +} + +export default Accordion; diff --git a/src/components/Accordion/AccordionDetail.scss b/src/components/Accordion/AccordionDetail.scss new file mode 100644 index 00000000..6977f7a9 --- /dev/null +++ b/src/components/Accordion/AccordionDetail.scss @@ -0,0 +1,10 @@ +.accordion-detail-container{ + border: 1px solid #ccc; + border-top: none; + transition: height 0.2s ease-in-out; + overflow: hidden; + +} +.accordion-detail-container-content{ + padding: 15px 20px; +} diff --git a/src/components/Accordion/AccordionDetail.tsx b/src/components/Accordion/AccordionDetail.tsx new file mode 100644 index 00000000..f8e319cf --- /dev/null +++ b/src/components/Accordion/AccordionDetail.tsx @@ -0,0 +1,33 @@ +import React, { useRef, useState, useEffect } from "react"; +import "./AccordionDetail.scss"; + + +type AccordionDetailProps = { + children: JSX.Element; + isOpen?: boolean; +}; +function AccordionDetail(props: AccordionDetailProps) { + const [height, setHeight] = useState(0); + const detailRef = useRef(null); + useEffect(() => { + if (props.isOpen) { + const detailEl = detailRef.current as HTMLDivElement; + console.log(detailEl?.scrollHeight); + setHeight(detailEl?.scrollHeight); + } else { + console.log("elsedyizz"); + setHeight(0); + } + }, [props.isOpen]); + return ( +
+
{props.children}
+
+ ); +} + +export default AccordionDetail; diff --git a/src/components/Accordion/AccordionHeader.scss b/src/components/Accordion/AccordionHeader.scss new file mode 100644 index 00000000..3b6256e8 --- /dev/null +++ b/src/components/Accordion/AccordionHeader.scss @@ -0,0 +1,49 @@ +.accordion-header{ + border: 1px solid #ccc; + cursor: pointer; + user-select: none; + + +} +.accordion-header:not(:first-of-type) { + border-top: 0; +} +.accordion-header-title{ + width: 100%; + margin: 0; +} + +.accordion-header-btn{ + width: 100%; + display: flex; + align-items: center; + border: 0; + font-size: 22px; + font-weight: 500; + background-color: #fff; +} + +.accordion-header-btn > p { + width: 100%; + height: 100%; + + padding-left: 15px; + padding-top: 15px; +} + +.accordion-header-btn::after{ + content: ''; + flex-shrink: 0; + width: 22px; + height: 22px; + margin-left: auto; + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23212529'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e"); + background-repeat: no-repeat; + background-size: 18px; + transition: transform 0.2s ease-in-out; +} + +.accordion-header.active .accordion-header-btn::after { + transform: rotate(-180deg); +} + diff --git a/src/components/Accordion/AccordionHeader.tsx b/src/components/Accordion/AccordionHeader.tsx new file mode 100644 index 00000000..426a1803 --- /dev/null +++ b/src/components/Accordion/AccordionHeader.tsx @@ -0,0 +1,36 @@ +import React from "react"; +import "./AccordionHeader.scss"; + +export type AccordionHeaderProps = { + children: JSX.Element | Array; + btnOnClick?: () => void; + isOpen?: boolean; +}; +const AccordionHeader = (props: AccordionHeaderProps) => { + return ( +
+ {Array.isArray(props.children) ? ( + props.children.map((elem, idx) => { + if (idx === 0) + return

{elem}

; + return ( +

+ {elem} +

+ ); + }) + ) : ( +

{props.children}

+ )} +
+ ); +}; + +export default AccordionHeader; From 4ab4f12dd4cb204ab5cd9bdebee2b2ffc8de0c9e Mon Sep 17 00:00:00 2001 From: asimbhdr96 Date: Fri, 16 Sep 2022 17:55:27 -0400 Subject: [PATCH 2/9] accordion borders fixed --- .../Accordion/Accordion.stories.tsx | 51 +++++++++++++++++++ src/components/Accordion/Accordion.tsx | 2 +- src/components/Accordion/AccordionDetail.scss | 10 ---- src/components/Accordion/AccordionDetail.tsx | 6 +-- src/components/Accordion/AccordionHeader.tsx | 6 ++- .../{AccordionHeader.scss => _Accordion.scss} | 46 ++++++++++------- src/components/Accordion/index.tsx | 1 + src/styles/index.scss | 1 + 8 files changed, 89 insertions(+), 34 deletions(-) create mode 100644 src/components/Accordion/Accordion.stories.tsx delete mode 100644 src/components/Accordion/AccordionDetail.scss rename src/components/Accordion/{AccordionHeader.scss => _Accordion.scss} (56%) create mode 100644 src/components/Accordion/index.tsx diff --git a/src/components/Accordion/Accordion.stories.tsx b/src/components/Accordion/Accordion.stories.tsx new file mode 100644 index 00000000..142fbc51 --- /dev/null +++ b/src/components/Accordion/Accordion.stories.tsx @@ -0,0 +1,51 @@ +import React from 'react'; +import { action } from '@storybook/addon-actions'; +import Accordion from './Accordion'; +import AccordionDetail from './AccordionDetail'; +import AccordionHeader from './AccordionHeader'; + +export default { + title: 'Accordion', + component: Accordion, +}; + +export const DefaultAccordion = () => { + return + +

hello

+
+ +

New Accoirdon detail helloooo

+
+
+} + +export const MultipleAccordions = () => { + return <> + +

hello

+
+ +

New Accoirdon detail helloooo

+
+
+ + +

hello

+
+ +

New Accoirdon detail helloooo

+
+
+ + +

hello

+
+ +

New Accoirdon detail helloooo

+
+
+ +} + + diff --git a/src/components/Accordion/Accordion.tsx b/src/components/Accordion/Accordion.tsx index 8635aed5..acce347c 100644 --- a/src/components/Accordion/Accordion.tsx +++ b/src/components/Accordion/Accordion.tsx @@ -13,7 +13,7 @@ function Accordion({ children }: { children: JSX.Element[] }) { setOpen(!a); }; return ( -
+
{children.map((elem, idx) => { if (elem.type.name === "AccordionHeader") { return ( diff --git a/src/components/Accordion/AccordionDetail.scss b/src/components/Accordion/AccordionDetail.scss deleted file mode 100644 index 6977f7a9..00000000 --- a/src/components/Accordion/AccordionDetail.scss +++ /dev/null @@ -1,10 +0,0 @@ -.accordion-detail-container{ - border: 1px solid #ccc; - border-top: none; - transition: height 0.2s ease-in-out; - overflow: hidden; - -} -.accordion-detail-container-content{ - padding: 15px 20px; -} diff --git a/src/components/Accordion/AccordionDetail.tsx b/src/components/Accordion/AccordionDetail.tsx index f8e319cf..51fbbc07 100644 --- a/src/components/Accordion/AccordionDetail.tsx +++ b/src/components/Accordion/AccordionDetail.tsx @@ -1,5 +1,5 @@ import React, { useRef, useState, useEffect } from "react"; -import "./AccordionDetail.scss"; + type AccordionDetailProps = { @@ -12,10 +12,8 @@ function AccordionDetail(props: AccordionDetailProps) { useEffect(() => { if (props.isOpen) { const detailEl = detailRef.current as HTMLDivElement; - console.log(detailEl?.scrollHeight); setHeight(detailEl?.scrollHeight); } else { - console.log("elsedyizz"); setHeight(0); } }, [props.isOpen]); @@ -23,7 +21,7 @@ function AccordionDetail(props: AccordionDetailProps) {
{props.children}
diff --git a/src/components/Accordion/AccordionHeader.tsx b/src/components/Accordion/AccordionHeader.tsx index 426a1803..2dfc95b8 100644 --- a/src/components/Accordion/AccordionHeader.tsx +++ b/src/components/Accordion/AccordionHeader.tsx @@ -1,5 +1,5 @@ import React from "react"; -import "./AccordionHeader.scss"; + export type AccordionHeaderProps = { children: JSX.Element | Array; @@ -27,7 +27,9 @@ const AccordionHeader = (props: AccordionHeaderProps) => { ); }) ) : ( -

{props.children}

+
+ {props.children} +
)}
); diff --git a/src/components/Accordion/AccordionHeader.scss b/src/components/Accordion/_Accordion.scss similarity index 56% rename from src/components/Accordion/AccordionHeader.scss rename to src/components/Accordion/_Accordion.scss index 3b6256e8..df886421 100644 --- a/src/components/Accordion/AccordionHeader.scss +++ b/src/components/Accordion/_Accordion.scss @@ -1,45 +1,37 @@ -.accordion-header{ - border: 1px solid #ccc; +.accordion-header { cursor: pointer; user-select: none; - - } -.accordion-header:not(:first-of-type) { - border-top: 0; -} -.accordion-header-title{ + +.accordion-header-title { width: 100%; margin: 0; } -.accordion-header-btn{ +.accordion-header-btn { width: 100%; display: flex; align-items: center; - border: 0; font-size: 22px; font-weight: 500; - background-color: #fff; + background-color: $white; } .accordion-header-btn > p { - width: 100%; - height: 100%; - - padding-left: 15px; + padding-left: 20px; padding-top: 15px; } -.accordion-header-btn::after{ +.accordion-header-btn::after { content: ''; flex-shrink: 0; width: 22px; height: 22px; margin-left: auto; + margin-right: 10px; background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23212529'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e"); background-repeat: no-repeat; - background-size: 18px; + background-size: 22px; transition: transform 0.2s ease-in-out; } @@ -47,3 +39,23 @@ transform: rotate(-180deg); } +.accordion-container { + border: 1px solid $gray-400; + border-bottom: 0.5px; +} +.accordion-container.active { + border: 1px solid $gray-400; + margin: 16px 0; +} +.accordion-container:last-of-type { + border-bottom: 1px solid $gray-400; +} + +.accordion-detail-container { + transition: height 0.2s ease-in-out; + overflow: hidden; +} + +.accordion-detail-container-content { + padding: 15px 20px; +} diff --git a/src/components/Accordion/index.tsx b/src/components/Accordion/index.tsx new file mode 100644 index 00000000..c3f85ef8 --- /dev/null +++ b/src/components/Accordion/index.tsx @@ -0,0 +1 @@ +export {default} from './Accordion' diff --git a/src/styles/index.scss b/src/styles/index.scss index 33fa969f..a4cb3bac 100644 --- a/src/styles/index.scss +++ b/src/styles/index.scss @@ -12,3 +12,4 @@ @import '../components/Input/Input'; @import '../components/Card/Card'; @import '../components/Progress/Progress'; +@import '../components/Accordion/Accordion' From 48e64d31a091e8fd052622029774cd3c2eba7853 Mon Sep 17 00:00:00 2001 From: asimbhdr96 Date: Fri, 16 Sep 2022 22:52:37 -0400 Subject: [PATCH 3/9] added disabled accordion and borders fixed --- .../Accordion/Accordion.stories.tsx | 28 +++++++++++++++++++ src/components/Accordion/Accordion.tsx | 25 ++++++++++++----- src/components/Accordion/_Accordion.scss | 23 +++++++++++---- 3 files changed, 64 insertions(+), 12 deletions(-) diff --git a/src/components/Accordion/Accordion.stories.tsx b/src/components/Accordion/Accordion.stories.tsx index 142fbc51..a5040b52 100644 --- a/src/components/Accordion/Accordion.stories.tsx +++ b/src/components/Accordion/Accordion.stories.tsx @@ -49,3 +49,31 @@ export const MultipleAccordions = () => { } +export const DisabledAccordion = () => { + return <> + +

hello

+
+ +

New Accoirdon detail helloooo

+
+
+ + +

hello

+
+ +

New Accoirdon detail helloooo

+
+
+ + +

hello

+
+ +

New Accoirdon detail helloooo

+
+
+ +} + diff --git a/src/components/Accordion/Accordion.tsx b/src/components/Accordion/Accordion.tsx index acce347c..e576bd17 100644 --- a/src/components/Accordion/Accordion.tsx +++ b/src/components/Accordion/Accordion.tsx @@ -1,19 +1,30 @@ import React, { ReactNode, useState } from "react"; import AccordionDetail from "./AccordionDetail"; import AccordionHeader from "./AccordionHeader"; +import { classNames } from '../../utils/classNames'; -export type AccordionData = { - title: string; - content: ReactNode; -}; +//export type childrenType = JSX.Element[] | JSX.Element +export interface IAccordionProps { + children : JSX.Element[]; + disabled? : boolean; +} -function Accordion({ children }: { children: JSX.Element[] }) { +function Accordion( props :IAccordionProps ) { + const {children, disabled, ...rest} = props const [open, setOpen] = useState(false); const btnOnClick = (a: boolean) => { - setOpen(!a); + if(!disabled){ + setOpen(!a); + } + }; + let styleClasses = classNames('accordion-container',{ + disabled: !!disabled + }) + console.log(styleClasses) + return ( -
+
{children.map((elem, idx) => { if (elem.type.name === "AccordionHeader") { return ( diff --git a/src/components/Accordion/_Accordion.scss b/src/components/Accordion/_Accordion.scss index df886421..bd690827 100644 --- a/src/components/Accordion/_Accordion.scss +++ b/src/components/Accordion/_Accordion.scss @@ -1,8 +1,7 @@ -.accordion-header { - cursor: pointer; - user-select: none; -} +.accordion-header{ + border-bottom: 0.5px solid $gray-400; +} .accordion-header-title { width: 100%; margin: 0; @@ -14,7 +13,7 @@ align-items: center; font-size: 22px; font-weight: 500; - background-color: $white; + } .accordion-header-btn > p { @@ -42,16 +41,30 @@ .accordion-container { border: 1px solid $gray-400; border-bottom: 0.5px; + cursor: pointer; + user-select: none; + background-color: $white; + &.disabled, + &[disabled] { + cursor: not-allowed; + opacity: $btn-disabled-opacity; + box-shadow: none; + background-color: $gray-600; + } } .accordion-container.active { border: 1px solid $gray-400; margin: 16px 0; + .accordion-header{ + border-bottom: 0; + } } .accordion-container:last-of-type { border-bottom: 1px solid $gray-400; } .accordion-detail-container { + transition: height 0.2s ease-in-out; overflow: hidden; } From df18d057e1b983a37833fd34d5d455110f180dfa Mon Sep 17 00:00:00 2001 From: asimbhdr96 Date: Mon, 19 Sep 2022 03:53:38 -0400 Subject: [PATCH 4/9] controlled component done --- .../Accordion/Accordion.stories.tsx | 51 ++++++++++++++++++- src/components/Accordion/Accordion.tsx | 37 ++++++++++---- src/components/Accordion/AccordionDetail.tsx | 13 +++++ src/components/Accordion/AccordionHeader.tsx | 3 +- src/components/Accordion/_Accordion.scss | 2 +- src/components/Button/Button.tsx | 2 +- 6 files changed, 93 insertions(+), 15 deletions(-) diff --git a/src/components/Accordion/Accordion.stories.tsx b/src/components/Accordion/Accordion.stories.tsx index a5040b52..000b8566 100644 --- a/src/components/Accordion/Accordion.stories.tsx +++ b/src/components/Accordion/Accordion.stories.tsx @@ -10,8 +10,8 @@ export default { }; export const DefaultAccordion = () => { - return - + return +

hello

@@ -77,3 +77,50 @@ export const DisabledAccordion = () => { } +export const ControlledAccordion:React.FC = () => { + const [expanded, setExpanded] = React.useState(false); + + const handleChange = (panel : string) => { + return (open?: boolean) => setExpanded(open ? panel : false) + } + + + + + + // return (event: React.SyntheticEvent) => { + // console.log('sadfsdf',panel,open) + // //setExpanded(expanded ? panel : false); + // ; + // } + + + //ChangeEvent + //MouseEventHandler + + return <> + +

hello

+
+ +

New Accoirdon detail helloooo

+
+
+ + +

hello

+
+ +

New Accoirdon detail helloooo

+
+
+ + +

hello

+
+ +

New Accoirdon detail helloooo

+
+
+ +} diff --git a/src/components/Accordion/Accordion.tsx b/src/components/Accordion/Accordion.tsx index e576bd17..4f0dfeb0 100644 --- a/src/components/Accordion/Accordion.tsx +++ b/src/components/Accordion/Accordion.tsx @@ -1,4 +1,4 @@ -import React, { ReactNode, useState } from "react"; +import React, { ReactNode, ButtonHTMLAttributes, useState,InputHTMLAttributes,FormEventHandler,useRef,useEffect } from "react"; import AccordionDetail from "./AccordionDetail"; import AccordionHeader from "./AccordionHeader"; import { classNames } from '../../utils/classNames'; @@ -7,29 +7,46 @@ import { classNames } from '../../utils/classNames'; export interface IAccordionProps { children : JSX.Element[]; disabled? : boolean; + expanded? : boolean; + onClick?: (a?: boolean) => void; } -function Accordion( props :IAccordionProps ) { - const {children, disabled, ...rest} = props - const [open, setOpen] = useState(false); +export type bhdrProps = InputHTMLAttributes +type InputArgs = IAccordionProps & Omit +function Accordion( props :InputArgs ) { + const {children, disabled,onClick,expanded, ...rest} = props + const [open, setOpen] = useState(expanded as boolean); + + useEffect(()=>{ + setOpen(expanded as boolean); + },[expanded]) + const btnOnClick = (a: boolean) => { if(!disabled){ - setOpen(!a); + console.log('ex',open) + setOpen(!open); } - }; + + const checkOpenorNot = () => { + if(typeof onClick === 'function'){ + onClick?.(!open) + } + + + } let styleClasses = classNames('accordion-container',{ disabled: !!disabled }) - console.log(styleClasses) + // {...(rest as bhdrProps)} return ( -
+
{children.map((elem, idx) => { if (elem.type.name === "AccordionHeader") { return ( btnOnClick(open)} key={idx} //uid > @@ -38,7 +55,7 @@ function Accordion( props :IAccordionProps ) { ); } else { return ( - + {elem.props.children} ); diff --git a/src/components/Accordion/AccordionDetail.tsx b/src/components/Accordion/AccordionDetail.tsx index 51fbbc07..dde1d525 100644 --- a/src/components/Accordion/AccordionDetail.tsx +++ b/src/components/Accordion/AccordionDetail.tsx @@ -5,10 +5,12 @@ import React, { useRef, useState, useEffect } from "react"; type AccordionDetailProps = { children: JSX.Element; isOpen?: boolean; + expanded?: boolean; }; function AccordionDetail(props: AccordionDetailProps) { const [height, setHeight] = useState(0); const detailRef = useRef(null); + useEffect(() => { if (props.isOpen) { const detailEl = detailRef.current as HTMLDivElement; @@ -17,6 +19,17 @@ function AccordionDetail(props: AccordionDetailProps) { setHeight(0); } }, [props.isOpen]); + useEffect(() => { + + if (props.expanded) { + console.log('useffectt') + const detailEl = detailRef.current as HTMLDivElement; + setHeight(detailEl?.scrollHeight); + } else { + setHeight(0); + } + }, [props.expanded]); + return (
void; isOpen?: boolean; }; -const AccordionHeader = (props: AccordionHeaderProps) => { +const AccordionHeader = (props: AccordionHeaderProps ) => { + return (
Date: Wed, 21 Sep 2022 11:06:53 -0400 Subject: [PATCH 5/9] fixed bugs --- .../Accordion/Accordion.stories.tsx | 39 ++-- src/components/Accordion/Accordion.test.tsx | 172 ++++++++++++++++++ src/components/Accordion/Accordion.tsx | 64 +++---- src/components/Accordion/AccordionDetail.tsx | 5 +- src/components/Accordion/AccordionHeader.tsx | 36 ++-- src/components/Accordion/_Accordion.scss | 21 ++- .../__snapshots__/Accordion.test.tsx.snap | 33 ++++ 7 files changed, 290 insertions(+), 80 deletions(-) create mode 100644 src/components/Accordion/__snapshots__/Accordion.test.tsx.snap diff --git a/src/components/Accordion/Accordion.stories.tsx b/src/components/Accordion/Accordion.stories.tsx index 000b8566..c720fa75 100644 --- a/src/components/Accordion/Accordion.stories.tsx +++ b/src/components/Accordion/Accordion.stories.tsx @@ -10,9 +10,12 @@ export default { }; export const DefaultAccordion = () => { - return + return

hello

+

hello

+

hello

+

asdasdfasdfsadfsadfsadfsadfasdfsadfsadfsadfsadffsadfsadfsadfsadf

New Accoirdon detail helloooo

@@ -83,21 +86,6 @@ export const ControlledAccordion:React.FC = () => { const handleChange = (panel : string) => { return (open?: boolean) => setExpanded(open ? panel : false) } - - - - - - // return (event: React.SyntheticEvent) => { - // console.log('sadfsdf',panel,open) - // //setExpanded(expanded ? panel : false); - // ; - // } - - - //ChangeEvent - //MouseEventHandler - return <>

hello

@@ -124,3 +112,22 @@ export const ControlledAccordion:React.FC = () => {
} + +export const StyledAccordion = () => { + return + +

hello

+
+ + + + +

hello

+
+ + + +
+
+
+} diff --git a/src/components/Accordion/Accordion.test.tsx b/src/components/Accordion/Accordion.test.tsx index e69de29b..a5401fb8 100644 --- a/src/components/Accordion/Accordion.test.tsx +++ b/src/components/Accordion/Accordion.test.tsx @@ -0,0 +1,172 @@ +import React from 'react'; +import { render, fireEvent,screen } from '@testing-library/react'; +import Accordion from "./Accordion"; + +import AccordionHeader from "./AccordionHeader"; +import AccordionDetail from "./AccordionDetail"; + +describe('Accordion', () => { + + it('should match snapshot', () => { + const { asFragment } = render( + + +

hello

+
+ +

New Accoirdon detail helloooo

+
+
) + expect(asFragment()).toMatchSnapshot(); + }); + + it('should render items', () => { + const headerSec = 'HelloFromHeader' + const detailSec = 'HelloFromDetail' + render( + + +

{headerSec}

+
+ +

{detailSec}

+
+
) + const headerEl = screen.queryByText(headerSec) + const detailEl = screen.queryByText(detailSec) + expect(headerEl).toBeInTheDocument(); + expect(detailEl).toBeInTheDocument(); + }); + + it('should open when the expanded props passed', () => { + const headerSec = 'HelloFromHeader' + const detailSec = 'HelloFromDetail' + render( + + +

{headerSec}

+
+ +

{detailSec}

+
+
) + const activeDiv = document.querySelector('.accordion-container.active') + expect(activeDiv).toHaveClass('accordion-container active') + }) + + it('should open when the header clicked', () => { + const headerSec = 'HelloFromHeader' + const detailSec = 'HelloFromDetail' + render( + + +

{headerSec}

+
+ +

{detailSec}

+
+
) + const headerEl = screen.queryByText(headerSec) as HTMLDivElement + fireEvent.click(headerEl) + const activeDiv = document.querySelector('.accordion-container.active') + expect(activeDiv).toHaveClass('accordion-container active') + }) + it('should close when the accordion already open', () => { + const headerSec = 'HelloFromHeader' + const detailSec = 'HelloFromDetail' + render( + + +

{headerSec}

+
+ +

{detailSec}

+
+
) + const headerEl = screen.queryByText(headerSec) as HTMLDivElement + const currentDiv = headerEl.closest('.accordion-container') + + fireEvent.click(headerEl) + fireEvent.click(headerEl) + expect(currentDiv).not.toHaveClass('active') + }) + it('should have a style when props passed', () => { + const headerSec = 'HelloFromHeader' + const detailSec = 'HelloFromDetail' + render( + + +

{headerSec}

+
+ +

{detailSec}

+
+
) + + const accordionDiv = document.querySelector('.accordion-container') + expect(accordionDiv).toHaveStyle('background-color : red;') + }) + it('should have a render multiple child elements', () => { + const headerSec = 'HelloFromHeader' + const headerSec2 = 'HelloFromHeader2' + const detailSec = 'HelloFromDetail' + render( + + +

{headerSec}

+

{headerSec2}

+

{headerSec2}

+

{headerSec2}

+
+ +

{detailSec}

+
+
) + + const accordionHeaderDiv = document.querySelector('.accordion-header-btn') as HTMLDivElement + + expect(Object.values(accordionHeaderDiv)).toHaveLength(3) + }) + // it('should open one at a time', () => { + // const headerSec = 'HelloFromHeader' + // const detailSec = 'HelloFromDetail' + // const headerSec2 = 'HelloFromHeader' + // const detailSec2 = 'HelloFromDetail' + // const [expanded, setExpanded] = React.useState(false); + + // const handleChange = (panel : string) => { + // return (open?: boolean) => setExpanded(open ? panel : false) + // } + // render( + // <> + // + // + //

{headerSec}

+ //
+ // + //

{detailSec}

+ //
+ //
+ // + // + //

{headerSec2}

+ //
+ // + //

{detailSec2}

+ //
+ //
+ // ) + // const headerEl = screen.queryByText(headerSec) as HTMLDivElement + // const headerEl2 = screen.queryByText(headerSec2) as HTMLDivElement + // fireEvent.click(headerEl) + // fireEvent.click(headerEl2) + // const activeDiv = document.querySelectorAll('.accordion-container.active') + // expect(activeDiv).toHaveClass('accordion-container active') + // }) + + + + + + +}) diff --git a/src/components/Accordion/Accordion.tsx b/src/components/Accordion/Accordion.tsx index 4f0dfeb0..288acc04 100644 --- a/src/components/Accordion/Accordion.tsx +++ b/src/components/Accordion/Accordion.tsx @@ -1,54 +1,54 @@ -import React, { ReactNode, ButtonHTMLAttributes, useState,InputHTMLAttributes,FormEventHandler,useRef,useEffect } from "react"; -import AccordionDetail from "./AccordionDetail"; -import AccordionHeader from "./AccordionHeader"; +import React, { useState, InputHTMLAttributes, useEffect } from 'react'; +import AccordionDetail from './AccordionDetail'; +import AccordionHeader from './AccordionHeader'; import { classNames } from '../../utils/classNames'; -//export type childrenType = JSX.Element[] | JSX.Element export interface IAccordionProps { - children : JSX.Element[]; - disabled? : boolean; - expanded? : boolean; - onClick?: (a?: boolean) => void; + children: JSX.Element[]; + disabled?: boolean; + expanded?: boolean; + sx?: {}; + rounded?: boolean; + onClick?: (a?: boolean) => void; } -export type bhdrProps = InputHTMLAttributes -type InputArgs = IAccordionProps & Omit -function Accordion( props :InputArgs ) { - const {children, disabled,onClick,expanded, ...rest} = props - const [open, setOpen] = useState(expanded as boolean); - - useEffect(()=>{ +export type bhdrProps = InputHTMLAttributes; +type InputArgs = IAccordionProps & Omit; +function Accordion(props: InputArgs) { + const { children, disabled, onClick, expanded, sx, ...rest } = props; + const [open, setOpen] = useState(false); + useEffect(() => { setOpen(expanded as boolean); - },[expanded]) + }, [expanded]); const btnOnClick = (a: boolean) => { - if(!disabled){ - console.log('ex',open) - setOpen(!open); + if (!disabled) { + setOpen(!a); } }; const checkOpenorNot = () => { - if(typeof onClick === 'function'){ - onClick?.(!open) + if (typeof onClick === 'function') { + onClick?.(!open); } + }; + let styleClasses = classNames('accordion-container', { + disabled: !!disabled, + }); - - } - let styleClasses = classNames('accordion-container',{ - disabled: !!disabled - }) - - // {...(rest as bhdrProps)} return ( -
+
{children.map((elem, idx) => { - if (elem.type.name === "AccordionHeader") { + if (elem.type.name === 'AccordionHeader') { return ( btnOnClick(open)} - key={idx} //uid + key={`${elem.type.name}${idx}`} //uid > {elem.props.children} diff --git a/src/components/Accordion/AccordionDetail.tsx b/src/components/Accordion/AccordionDetail.tsx index dde1d525..7485ea82 100644 --- a/src/components/Accordion/AccordionDetail.tsx +++ b/src/components/Accordion/AccordionDetail.tsx @@ -1,9 +1,7 @@ import React, { useRef, useState, useEffect } from "react"; - - type AccordionDetailProps = { - children: JSX.Element; + children: React.ReactNode; isOpen?: boolean; expanded?: boolean; }; @@ -22,7 +20,6 @@ function AccordionDetail(props: AccordionDetailProps) { useEffect(() => { if (props.expanded) { - console.log('useffectt') const detailEl = detailRef.current as HTMLDivElement; setHeight(detailEl?.scrollHeight); } else { diff --git a/src/components/Accordion/AccordionHeader.tsx b/src/components/Accordion/AccordionHeader.tsx index 4c6154eb..ce673e97 100644 --- a/src/components/Accordion/AccordionHeader.tsx +++ b/src/components/Accordion/AccordionHeader.tsx @@ -1,36 +1,30 @@ -import React from "react"; - +import React from 'react'; export type AccordionHeaderProps = { - children: JSX.Element | Array; + children: JSX.Element | JSX.Element[]; btnOnClick?: () => void; isOpen?: boolean; }; -const AccordionHeader = (props: AccordionHeaderProps ) => { - +const AccordionHeader = (props: AccordionHeaderProps) => { + const childArray = Object.values(props.children); return (
{Array.isArray(props.children) ? ( - props.children.map((elem, idx) => { - if (idx === 0) - return

{elem}

; - return ( -

- {elem} + <> +

{childArray[0]}
+
+

+ {childArray.slice(1).map((elem, idx) => { + return elem.props.children; + })}

- ); - }) +
+ ) : ( -
- {props.children} -
+
{props.children}
)}
); diff --git a/src/components/Accordion/_Accordion.scss b/src/components/Accordion/_Accordion.scss index bb37f172..66542067 100644 --- a/src/components/Accordion/_Accordion.scss +++ b/src/components/Accordion/_Accordion.scss @@ -1,29 +1,36 @@ - -.accordion-header{ +.accordion-header { border-bottom: 0.5px solid $gray-400; + display: flex; } .accordion-header-title { - width: 100%; + width: 20%; margin: 0; + padding-left: 20px; + padding-top: 15px; + font-size: 22px; + font-weight: 500; } .accordion-header-btn { width: 100%; + flex-grow: 3; + overflow: hidden; display: flex; align-items: center; font-size: 22px; font-weight: 500; - } .accordion-header-btn > p { padding-left: 20px; padding-top: 15px; + max-width: 70%; + word-wrap: break-word; } .accordion-header-btn::after { content: ''; - flex-shrink: 0; + flex-shrink: 1; width: 22px; height: 22px; margin-left: auto; @@ -43,6 +50,7 @@ border-bottom: 0.5px; cursor: pointer; user-select: none; + overflow: hidden; background-color: $white; &.disabled, &[disabled] { @@ -55,7 +63,7 @@ .accordion-container.active { border: 1px solid $gray-400; margin: 16px 0; - .accordion-header{ + .accordion-header { border-bottom: 0.5px solid $gray-400; } } @@ -64,7 +72,6 @@ } .accordion-detail-container { - transition: height 0.2s ease-in-out; overflow: hidden; } diff --git a/src/components/Accordion/__snapshots__/Accordion.test.tsx.snap b/src/components/Accordion/__snapshots__/Accordion.test.tsx.snap new file mode 100644 index 00000000..24d58748 --- /dev/null +++ b/src/components/Accordion/__snapshots__/Accordion.test.tsx.snap @@ -0,0 +1,33 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Accordion should match snapshot 1`] = ` + +
+
+
+

+ hello +

+
+
+
+
+

+ New Accoirdon detail helloooo +

+
+
+
+
+`; From 7762db7481eac3165e4c2c329393fbd6b0e10322 Mon Sep 17 00:00:00 2001 From: asimbhdr96 Date: Thu, 22 Sep 2022 13:02:01 -0400 Subject: [PATCH 6/9] edited index.ts --- .../Accordion/Accordion.stories.tsx | 10 ++- src/components/Accordion/Accordion.tsx | 4 +- src/components/Accordion/AccordionDetail.tsx | 41 +++++++--- src/components/Accordion/_Accordion.scss | 78 ++++++++++++------- src/components/Accordion/index.tsx | 17 +++- 5 files changed, 105 insertions(+), 45 deletions(-) diff --git a/src/components/Accordion/Accordion.stories.tsx b/src/components/Accordion/Accordion.stories.tsx index c720fa75..a2503319 100644 --- a/src/components/Accordion/Accordion.stories.tsx +++ b/src/components/Accordion/Accordion.stories.tsx @@ -81,10 +81,10 @@ export const DisabledAccordion = () => { } export const ControlledAccordion:React.FC = () => { - const [expanded, setExpanded] = React.useState(false); + const [expanded, setExpanded] = React.useState(''); const handleChange = (panel : string) => { - return (open?: boolean) => setExpanded(open ? panel : false) + return (open?: boolean) => setExpanded(open ? panel : '') } return <> @@ -119,13 +119,15 @@ export const StyledAccordion = () => {

hello

- +

sadfsdf

hello

- +

hello

+

hello

+

hello

diff --git a/src/components/Accordion/Accordion.tsx b/src/components/Accordion/Accordion.tsx index 288acc04..acb83155 100644 --- a/src/components/Accordion/Accordion.tsx +++ b/src/components/Accordion/Accordion.tsx @@ -10,12 +10,14 @@ export interface IAccordionProps { sx?: {}; rounded?: boolean; onClick?: (a?: boolean) => void; + } export type bhdrProps = InputHTMLAttributes; type InputArgs = IAccordionProps & Omit; function Accordion(props: InputArgs) { const { children, disabled, onClick, expanded, sx, ...rest } = props; + const [open, setOpen] = useState(false); useEffect(() => { setOpen(expanded as boolean); @@ -55,7 +57,7 @@ function Accordion(props: InputArgs) { ); } else { return ( - + {elem.props.children} ); diff --git a/src/components/Accordion/AccordionDetail.tsx b/src/components/Accordion/AccordionDetail.tsx index 7485ea82..b033ca5c 100644 --- a/src/components/Accordion/AccordionDetail.tsx +++ b/src/components/Accordion/AccordionDetail.tsx @@ -1,27 +1,35 @@ -import React, { useRef, useState, useEffect } from "react"; - -type AccordionDetailProps = { - children: React.ReactNode; +import React, { useRef,ReactElement,Children,cloneElement, useState, useEffect } from "react"; +import { IAccordionProps } from "./Accordion"; +export type AccordionDetailProps = { + children: ReactElement | ReactElement[]; isOpen?: boolean; expanded?: boolean; + }; function AccordionDetail(props: AccordionDetailProps) { - const [height, setHeight] = useState(0); + const [height, setHeight] = useState(0); const detailRef = useRef(null); - + let detailEl = detailRef.current as HTMLDivElement; + console.log(height) useEffect(() => { if (props.isOpen) { - const detailEl = detailRef.current as HTMLDivElement; + // const detailEl = detailRef.current as HTMLDivElement; + + setHeight(detailEl?.scrollHeight); } else { setHeight(0); } }, [props.isOpen]); + + + + useEffect(() => { if (props.expanded) { const detailEl = detailRef.current as HTMLDivElement; - setHeight(detailEl?.scrollHeight); + setHeight(prev => prev + detailEl?.scrollHeight); } else { setHeight(0); } @@ -29,11 +37,22 @@ function AccordionDetail(props: AccordionDetailProps) { return (
-
{props.children}
+
{props.children + ? Children.map(props.children, (child: ReactElement) => { + + return cloneElement(child) + } + + ) + : props.children}
); } diff --git a/src/components/Accordion/_Accordion.scss b/src/components/Accordion/_Accordion.scss index 66542067..83291c4e 100644 --- a/src/components/Accordion/_Accordion.scss +++ b/src/components/Accordion/_Accordion.scss @@ -1,3 +1,32 @@ +.accordion-container { + border: 1px solid $gray-400; + border-bottom: 0.5px; + cursor: pointer; + user-select: none; + //overflow: hidden; + background-color: $white; + &.disabled, + &[disabled] { + cursor: not-allowed; + opacity: $btn-disabled-opacity; + box-shadow: none; + background-color: $gray-600; + } +} + +.accordion-container.active { + border: 1px solid $gray-400; + margin: 16px 0; + .accordion-header { + border-bottom: 0.5px solid $gray-400; + } +} + +.accordion-container:last-of-type { + border-bottom: 1px solid $gray-400; +} + + .accordion-header { border-bottom: 0.5px solid $gray-400; display: flex; @@ -38,44 +67,37 @@ background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23212529'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e"); background-repeat: no-repeat; background-size: 22px; - transition: transform 0.2s ease-in-out; + transition: transform 0.4s ease-in-out; } .accordion-header.active .accordion-header-btn::after { transform: rotate(-180deg); -} -.accordion-container { - border: 1px solid $gray-400; - border-bottom: 0.5px; - cursor: pointer; - user-select: none; - overflow: hidden; - background-color: $white; - &.disabled, - &[disabled] { - cursor: not-allowed; - opacity: $btn-disabled-opacity; - box-shadow: none; - background-color: $gray-600; - } -} -.accordion-container.active { - border: 1px solid $gray-400; - margin: 16px 0; - .accordion-header { - border-bottom: 0.5px solid $gray-400; - } -} -.accordion-container:last-of-type { - border-bottom: 1px solid $gray-400; } + .accordion-detail-container { - transition: height 0.2s ease-in-out; - overflow: hidden; + transition: max-height 1.4s ease-in-out; + //overflow: hidden; + //height : 0; + opacity: 0; + transform: scaleY(0); + max-height: 0px; + &.active{ + transition: max-height 1.4s ease-in-out; + opacity: 1; + transform: scaleY(1); + max-height: 1000px; + // transition: height 0.4s ease-in-out; + // height : auto; + } } + + .accordion-detail-container-content { padding: 15px 20px; + //display : block; + + } diff --git a/src/components/Accordion/index.tsx b/src/components/Accordion/index.tsx index c3f85ef8..adee4907 100644 --- a/src/components/Accordion/index.tsx +++ b/src/components/Accordion/index.tsx @@ -1 +1,16 @@ -export {default} from './Accordion' +import { FC } from 'react'; +import Accordion, { IAccordionProps } from './Accordion'; +import AccordionDetail, { AccordionDetailProps } from './AccordionDetail'; +import AccordionHeader, { AccordionHeaderProps } from './AccordionHeader'; + +export type PatAccordionComponent = FC & { + Header: FC; + Detail: FC; +}; + +const TransAccordion = Accordion as PatAccordionComponent; + +TransAccordion.Header = AccordionHeader; +TransAccordion.Detail = AccordionDetail; + +export default TransAccordion; From db7426c12ee3a9bbffc86e6afa588f9a12d60863 Mon Sep 17 00:00:00 2001 From: asimbhdr96 Date: Mon, 26 Sep 2022 03:06:17 -0400 Subject: [PATCH 7/9] 'fixed several style issues' --- .../Accordion/Accordion.stories.tsx | 416 +++++++++++++----- src/components/Accordion/Accordion.test.tsx | 173 +++----- src/components/Accordion/Accordion.tsx | 63 ++- src/components/Accordion/AccordionDetail.tsx | 61 +-- src/components/Accordion/AccordionHeader.tsx | 24 +- src/components/Accordion/_Accordion.scss | 88 ++-- .../__snapshots__/Accordion.test.tsx.snap | 5 +- src/index.tsx | 1 + 8 files changed, 472 insertions(+), 359 deletions(-) diff --git a/src/components/Accordion/Accordion.stories.tsx b/src/components/Accordion/Accordion.stories.tsx index a2503319..c477f17f 100644 --- a/src/components/Accordion/Accordion.stories.tsx +++ b/src/components/Accordion/Accordion.stories.tsx @@ -1,135 +1,319 @@ import React from 'react'; import { action } from '@storybook/addon-actions'; -import Accordion from './Accordion'; -import AccordionDetail from './AccordionDetail'; -import AccordionHeader from './AccordionHeader'; +import { default as Accordion } from './index'; export default { title: 'Accordion', component: Accordion, }; export const DefaultAccordion = () => { - return - -

hello

-

hello

-

hello

-

asdasdfasdfsadfsadfsadfsadfasdfsadfsadfsadfsadffsadfsadfsadfsadf

-
- -

New Accoirdon detail helloooo

-
-
-} + return ( + + +

+ Default Accordion +

+
+ +

+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse + malesuada lacus ex, sit amet blandit leo lobortis eget. +

+
+
+ ); +}; export const MultipleAccordions = () => { - return <> - -

hello

-
- -

New Accoirdon detail helloooo

-
-
- - -

hello

-
- -

New Accoirdon detail helloooo

-
-
- - -

hello

-
- -

New Accoirdon detail helloooo

-
-
- -} - + return ( + <> + + +

Accordion 1

+
+ +

+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse + malesuada lacus ex, sit amet blandit leo lobortis eget. +

+
+
+ + +

Accordion 2

+
+ +

+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse + malesuada lacus ex, sit amet blandit leo lobortis eget. +

+
+
+ + +

Accordion 3

+
+ +

+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse + malesuada lacus ex, sit amet blandit leo lobortis eget. +

+
+
+ + ); +}; export const DisabledAccordion = () => { - return <> - -

hello

-
- -

New Accoirdon detail helloooo

-
-
- - -

hello

-
- -

New Accoirdon detail helloooo

-
-
- - -

hello

-
- -

New Accoirdon detail helloooo

-
-
- -} + return ( + <> + + +

Accordion 1

+
+ +

+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse + malesuada lacus ex, sit amet blandit leo lobortis eget. +

+
+
+ + +

Accordion 2

+
+ +

+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse + malesuada lacus ex, sit amet blandit leo lobortis eget. +

+
+
+ + +

Accordion 3

+
+ +

+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse + malesuada lacus ex, sit amet blandit leo lobortis eget. +

+
+
+ + ); +}; -export const ControlledAccordion:React.FC = () => { +export const ControlledAccordion: React.FC = () => { const [expanded, setExpanded] = React.useState(''); - const handleChange = (panel : string) => { - return (open?: boolean) => setExpanded(open ? panel : '') - } - return <> - -

hello

-
- -

New Accoirdon detail helloooo

-
-
- - -

hello

-
- -

New Accoirdon detail helloooo

-
-
- - -

hello

-
- -

New Accoirdon detail helloooo

-
-
- -} + const handleChange = (panel: string) => { + return (open?: boolean) => setExpanded(open ? panel : ''); + }; + return ( + <> + + +

Accordion 1

+
+ +

+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse + malesuada lacus ex, sit amet blandit leo lobortis eget. +

+
+
+ + +

Accordion 2

+
+ +

+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse + malesuada lacus ex, sit amet blandit leo lobortis eget. +

+
+
+ + +

Accordion 3

+
+ +

+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse + malesuada lacus ex, sit amet blandit leo lobortis eget. +

+
+
+ + ); +}; export const StyledAccordion = () => { - return - -

hello

-
- -

sadfsdf

- - -

hello

-
- -

hello

-

hello

-

hello

-
-
-
+ return ( + <> + + +

Styled Accordion

+
+ +

+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse + malesuada lacus ex, sit amet blandit leo lobortis eget. +

+ + +

Nested Accordion

+
+ +

+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. + Vestibulum dapibus justo erat. Lorem ipsum dolor sit amet, + consectetur adipiscing elit. Donec ut turpis cursus, tempus ex + in, dictum ante. Vivamus consequat, justo imperdiet ultricies + cursus, neque nisl elementum ligula, a pharetra velit nibh vel + purus. Nam ornare leo non purus fringilla, id lacinia libero + rhoncus. Cras dolor nulla, porta sed neque nec, auctor dapibus + ligula. Etiam at interdum neque. Suspendisse imperdiet odio + nisi, lobortis lacinia turpis porttitor eu. Nam fermentum neque + nulla, ut dapibus ante dignissim at. Nam sodales, sem sed + pulvinar scelerisque, arcu orci luctus diam, ut luctus ipsum ex + eu nunc. Curabitur at purus cursus, fermentum nisi nec, varius + orci. Quisque in eros dictum, imperdiet sapien eu, vulputate + sapien. +

+
-} +
+
+ + + +

+ Styled Accordion +

+

+ Secondary Text +

+
+ +

+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse + malesuada lacus ex, sit amet blandit leo lobortis eget. +

+
+
+ + + +

Click Me

+
+ +

+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse + malesuada lacus ex, sit amet blandit leo lobortis eget. +

+
+
+ + ); +}; + +export const DiffTypeAccordion = () => { + return ( +
+ + +

Primary Accordion

+
+ +

+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse + malesuada lacus ex, sit amet blandit leo lobortis eget. +

+
+
+ + + +

Secondary Accordion

+
+ +

+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse + malesuada lacus ex, sit amet blandit leo lobortis eget. +

+
+
+ + + +

Danger Accordion

+
+ +

+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse + malesuada lacus ex, sit amet blandit leo lobortis eget. +

+
+
+ + +

Info Accordion

+
+ +

+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse + malesuada lacus ex, sit amet blandit leo lobortis eget. +

+
+
+
+ ); +}; diff --git a/src/components/Accordion/Accordion.test.tsx b/src/components/Accordion/Accordion.test.tsx index a5401fb8..343d83f7 100644 --- a/src/components/Accordion/Accordion.test.tsx +++ b/src/components/Accordion/Accordion.test.tsx @@ -1,89 +1,88 @@ import React from 'react'; import { render, fireEvent,screen } from '@testing-library/react'; -import Accordion from "./Accordion"; - -import AccordionHeader from "./AccordionHeader"; -import AccordionDetail from "./AccordionDetail"; +import Accordion from './index' describe('Accordion', () => { it('should match snapshot', () => { const { asFragment } = render( - +

hello

-
- + +

New Accoirdon detail helloooo

-
+
) expect(asFragment()).toMatchSnapshot(); }); it('should render items', () => { - const headerSec = 'HelloFromHeader' - const detailSec = 'HelloFromDetail' + const headerText = 'HelloFromHeader' + const detailText = 'HelloFromDetail' render( - -

{headerSec}

-
- -

{detailSec}

-
+ +

{headerText}

+
+ +

{detailText}

+
) - const headerEl = screen.queryByText(headerSec) - const detailEl = screen.queryByText(detailSec) + const headerEl = screen.queryByText(headerText) + const detailEl = screen.queryByText(detailText) expect(headerEl).toBeInTheDocument(); expect(detailEl).toBeInTheDocument(); }); it('should open when the expanded props passed', () => { - const headerSec = 'HelloFromHeader' - const detailSec = 'HelloFromDetail' + const headerText = 'HelloFromHeader' + const detailText = 'HelloFromDetail' render( - -

{headerSec}

-
- -

{detailSec}

-
+ +

{headerText}

+
+ +

{detailText}

+
) const activeDiv = document.querySelector('.accordion-container.active') expect(activeDiv).toHaveClass('accordion-container active') }) it('should open when the header clicked', () => { - const headerSec = 'HelloFromHeader' - const detailSec = 'HelloFromDetail' + const headerText = 'HelloFromHeader' + const detailText = 'HelloFromDetail' render( - -

{headerSec}

-
- -

{detailSec}

-
+ +

{headerText}

+
+ +

{detailText}

+
) - const headerEl = screen.queryByText(headerSec) as HTMLDivElement + const headerEl = screen.queryByText(headerText) as HTMLDivElement + const inActiveDiv = document.querySelector('.accordion-container') + expect(inActiveDiv).not.toHaveClass('accordion-container active') fireEvent.click(headerEl) const activeDiv = document.querySelector('.accordion-container.active') expect(activeDiv).toHaveClass('accordion-container active') }) it('should close when the accordion already open', () => { - const headerSec = 'HelloFromHeader' - const detailSec = 'HelloFromDetail' + const headerText = 'HelloFromHeader' + const detailText = 'HelloFromDetail' render( - -

{headerSec}

-
- -

{detailSec}

-
+ +

{headerText}

+
+ +

{detailText}

+
) - const headerEl = screen.queryByText(headerSec) as HTMLDivElement + const headerEl = screen.queryByText(headerText) as HTMLDivElement const currentDiv = headerEl.closest('.accordion-container') fireEvent.click(headerEl) @@ -91,82 +90,42 @@ describe('Accordion', () => { expect(currentDiv).not.toHaveClass('active') }) it('should have a style when props passed', () => { - const headerSec = 'HelloFromHeader' - const detailSec = 'HelloFromDetail' + const headerText = 'HelloFromHeader' + const detailText = 'HelloFromDetail' render( - -

{headerSec}

-
- -

{detailSec}

-
+ +

{headerText}

+
+ +

{detailText}

+
) const accordionDiv = document.querySelector('.accordion-container') expect(accordionDiv).toHaveStyle('background-color : red;') }) it('should have a render multiple child elements', () => { - const headerSec = 'HelloFromHeader' - const headerSec2 = 'HelloFromHeader2' - const detailSec = 'HelloFromDetail' + const headerText = 'HelloFromHeader' + const headerText2 = 'HelloFromHeader2' + const detailText = 'HelloFromDetail' render( - -

{headerSec}

-

{headerSec2}

-

{headerSec2}

-

{headerSec2}

-
- -

{detailSec}

-
+ +

{headerText}

+ +
+ +

{detailText}

+

{headerText2}

+

{headerText2}

+

{headerText2}

+
) - const accordionHeaderDiv = document.querySelector('.accordion-header-btn') as HTMLDivElement + const accordionHeaderDiv = document.querySelector('.accordion-detail-container-content') as HTMLDivElement - expect(Object.values(accordionHeaderDiv)).toHaveLength(3) + expect(accordionHeaderDiv.children).toHaveLength(4) }) - // it('should open one at a time', () => { - // const headerSec = 'HelloFromHeader' - // const detailSec = 'HelloFromDetail' - // const headerSec2 = 'HelloFromHeader' - // const detailSec2 = 'HelloFromDetail' - // const [expanded, setExpanded] = React.useState(false); - - // const handleChange = (panel : string) => { - // return (open?: boolean) => setExpanded(open ? panel : false) - // } - // render( - // <> - // - // - //

{headerSec}

- //
- // - //

{detailSec}

- //
- //
- // - // - //

{headerSec2}

- //
- // - //

{detailSec2}

- //
- //
- // ) - // const headerEl = screen.queryByText(headerSec) as HTMLDivElement - // const headerEl2 = screen.queryByText(headerSec2) as HTMLDivElement - // fireEvent.click(headerEl) - // fireEvent.click(headerEl2) - // const activeDiv = document.querySelectorAll('.accordion-container.active') - // expect(activeDiv).toHaveClass('accordion-container active') - // }) - - - - - }) diff --git a/src/components/Accordion/Accordion.tsx b/src/components/Accordion/Accordion.tsx index acb83155..216158d2 100644 --- a/src/components/Accordion/Accordion.tsx +++ b/src/components/Accordion/Accordion.tsx @@ -1,22 +1,41 @@ -import React, { useState, InputHTMLAttributes, useEffect } from 'react'; +import React, { + useState, + InputHTMLAttributes, + useEffect, + ReactElement, +} from 'react'; import AccordionDetail from './AccordionDetail'; import AccordionHeader from './AccordionHeader'; import { classNames } from '../../utils/classNames'; +import { AccordionDetailProps } from './AccordionDetail'; +import { AccordionHeaderProps } from './AccordionHeader'; + +export type AccordionType = + | 'primary' + | 'secondary' + | 'danger' + | 'info' + | 'default'; export interface IAccordionProps { - children: JSX.Element[]; + children: [ + ReactElement, + ReactElement + ]; disabled?: boolean; expanded?: boolean; sx?: {}; rounded?: boolean; onClick?: (a?: boolean) => void; - + key?: number | string; + accordionType?: AccordionType; } export type bhdrProps = InputHTMLAttributes; type InputArgs = IAccordionProps & Omit; function Accordion(props: InputArgs) { - const { children, disabled, onClick, expanded, sx, ...rest } = props; + const { children, disabled, accordionType, onClick, expanded, sx, key } = + props; const [open, setOpen] = useState(false); useEffect(() => { @@ -35,6 +54,7 @@ function Accordion(props: InputArgs) { } }; let styleClasses = classNames('accordion-container', { + [`acc-${accordionType}`]: true, disabled: !!disabled, }); @@ -44,25 +64,22 @@ function Accordion(props: InputArgs) { className={`${styleClasses} ${expanded || open ? 'active' : ''}`} style={sx} > - {children.map((elem, idx) => { - if (elem.type.name === 'AccordionHeader') { - return ( - btnOnClick(open)} - key={`${elem.type.name}${idx}`} //uid - > - {elem.props.children} - - ); - } else { - return ( - - {elem.props.children} - - ); - } - })} + btnOnClick(open)} + key={`header${key}`} + expandedStyle={children[0]?.props['expandedStyle']} + > + {children[0].props.children} + + + + {children[1].props.children} +
); } diff --git a/src/components/Accordion/AccordionDetail.tsx b/src/components/Accordion/AccordionDetail.tsx index b033ca5c..bf8759b2 100644 --- a/src/components/Accordion/AccordionDetail.tsx +++ b/src/components/Accordion/AccordionDetail.tsx @@ -1,58 +1,27 @@ -import React, { useRef,ReactElement,Children,cloneElement, useState, useEffect } from "react"; -import { IAccordionProps } from "./Accordion"; +import React, { ReactElement, Children, cloneElement } from 'react'; +import { IAccordionProps } from './Accordion'; export type AccordionDetailProps = { children: ReactElement | ReactElement[]; isOpen?: boolean; expanded?: boolean; - + sx?: {}; }; function AccordionDetail(props: AccordionDetailProps) { - const [height, setHeight] = useState(0); - const detailRef = useRef(null); - let detailEl = detailRef.current as HTMLDivElement; - console.log(height) - useEffect(() => { - if (props.isOpen) { - // const detailEl = detailRef.current as HTMLDivElement; - - - setHeight(detailEl?.scrollHeight); - } else { - setHeight(0); - } - }, [props.isOpen]); - - - - - useEffect(() => { - - if (props.expanded) { - const detailEl = detailRef.current as HTMLDivElement; - setHeight(prev => prev + detailEl?.scrollHeight); - } else { - setHeight(0); - } - }, [props.expanded]); - return (
-
{props.children - ? Children.map(props.children, (child: ReactElement) => { - - return cloneElement(child) - } - - ) - : props.children}
+
+ {props.children + ? Children.map( + props.children, + (child: ReactElement) => { + return cloneElement(child); + } + ) + : props.children} +
); } diff --git a/src/components/Accordion/AccordionHeader.tsx b/src/components/Accordion/AccordionHeader.tsx index ce673e97..5dc24829 100644 --- a/src/components/Accordion/AccordionHeader.tsx +++ b/src/components/Accordion/AccordionHeader.tsx @@ -4,28 +4,22 @@ export type AccordionHeaderProps = { children: JSX.Element | JSX.Element[]; btnOnClick?: () => void; isOpen?: boolean; + sx?: {}; + expandedStyle?: {}; }; const AccordionHeader = (props: AccordionHeaderProps) => { - const childArray = Object.values(props.children); return (
- {Array.isArray(props.children) ? ( - <> -
{childArray[0]}
-
-

- {childArray.slice(1).map((elem, idx) => { - return elem.props.children; - })} -

-
- - ) : ( -
{props.children}
- )} +
+ {props.children} +
); }; diff --git a/src/components/Accordion/_Accordion.scss b/src/components/Accordion/_Accordion.scss index 83291c4e..9d7e9b6a 100644 --- a/src/components/Accordion/_Accordion.scss +++ b/src/components/Accordion/_Accordion.scss @@ -1,9 +1,8 @@ .accordion-container { - border: 1px solid $gray-400; - border-bottom: 0.5px; + border-top: 1px solid $gray-300; + border-bottom: 1px solid $gray-300; cursor: pointer; user-select: none; - //overflow: hidden; background-color: $white; &.disabled, &[disabled] { @@ -12,37 +11,43 @@ box-shadow: none; background-color: $gray-600; } + &.acc-primary { + background-color: $primary; + color: white; + } + &.acc-secondary { + background-color: $secondary; + color: white; + } + &.acc-danger { + background-color: $danger; + color: white; + } + &.acc-info { + background-color: $info; + color: white; + } } .accordion-container.active { - border: 1px solid $gray-400; margin: 16px 0; - .accordion-header { - border-bottom: 0.5px solid $gray-400; + .accordion-container:nth-of-type(n + 2) { + border-top: 1px solid $cyan; } -} - -.accordion-container:last-of-type { - border-bottom: 1px solid $gray-400; -} - -.accordion-header { - border-bottom: 0.5px solid $gray-400; - display: flex; -} -.accordion-header-title { - width: 20%; - margin: 0; - padding-left: 20px; - padding-top: 15px; - font-size: 22px; - font-weight: 500; + &.acc-primary { + border: 1px solid $cyan; + &.acc-primary:last-of-type { + border-bottom: 1px solid $cyan; + } + &.acc-primary:nth-of-type(n + 2) { + border-top: 1px solid $cyan; + } + } } -.accordion-header-btn { +.accordion-header__btn { width: 100%; - flex-grow: 3; overflow: hidden; display: flex; align-items: center; @@ -50,18 +55,18 @@ font-weight: 500; } -.accordion-header-btn > p { +.accordion-header__btn > p { padding-left: 20px; padding-top: 15px; max-width: 70%; word-wrap: break-word; } -.accordion-header-btn::after { +.accordion-header__btn::after { content: ''; flex-shrink: 1; - width: 22px; - height: 22px; + width: 20px; + height: 20px; margin-left: auto; margin-right: 10px; background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23212529'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e"); @@ -70,34 +75,19 @@ transition: transform 0.4s ease-in-out; } -.accordion-header.active .accordion-header-btn::after { +.accordion-header.active .accordion-header__btn::after { transform: rotate(-180deg); - } - .accordion-detail-container { - transition: max-height 1.4s ease-in-out; - //overflow: hidden; - //height : 0; - opacity: 0; - transform: scaleY(0); + transition: max-height 1.8s all; + overflow: hidden; max-height: 0px; - &.active{ - transition: max-height 1.4s ease-in-out; - opacity: 1; - transform: scaleY(1); + &.active { + transition: max-height 1.8s all; max-height: 1000px; - // transition: height 0.4s ease-in-out; - // height : auto; } } - - - .accordion-detail-container-content { padding: 15px 20px; - //display : block; - - } diff --git a/src/components/Accordion/__snapshots__/Accordion.test.tsx.snap b/src/components/Accordion/__snapshots__/Accordion.test.tsx.snap index 24d58748..85d79279 100644 --- a/src/components/Accordion/__snapshots__/Accordion.test.tsx.snap +++ b/src/components/Accordion/__snapshots__/Accordion.test.tsx.snap @@ -9,7 +9,7 @@ exports[`Accordion should match snapshot 1`] = ` class="accordion-header " >

hello @@ -17,8 +17,7 @@ exports[`Accordion should match snapshot 1`] = `

Date: Mon, 26 Sep 2022 16:00:19 -0400 Subject: [PATCH 8/9] changed the spanshot --- src/components/Accordion/__snapshots__/Accordion.test.tsx.snap | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Accordion/__snapshots__/Accordion.test.tsx.snap b/src/components/Accordion/__snapshots__/Accordion.test.tsx.snap index 85d79279..2d63aeeb 100644 --- a/src/components/Accordion/__snapshots__/Accordion.test.tsx.snap +++ b/src/components/Accordion/__snapshots__/Accordion.test.tsx.snap @@ -3,7 +3,7 @@ exports[`Accordion should match snapshot 1`] = `
Date: Wed, 28 Sep 2022 16:20:29 -0400 Subject: [PATCH 9/9] fixed severa test cases --- src/components/Accordion/Accordion.test.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/Accordion/Accordion.test.tsx b/src/components/Accordion/Accordion.test.tsx index 343d83f7..cb1be523 100644 --- a/src/components/Accordion/Accordion.test.tsx +++ b/src/components/Accordion/Accordion.test.tsx @@ -61,6 +61,7 @@ describe('Accordion', () => {

{detailText}

+

{detailText}

) const headerEl = screen.queryByText(headerText) as HTMLDivElement