From f5e716bccb3602fd0a0abcd4c322b0643fa1920d Mon Sep 17 00:00:00 2001 From: github-actions Date: Tue, 19 Jan 2021 00:06:55 -0800 Subject: [PATCH 1/4] feat #190 - Accordion Component Closes #190 --- .../Accordion/Accordion.stories.tsx | 37 ++++++ src/components/Accordion/CollapseButton.tsx | 21 ++++ src/components/Accordion/PanelContent.tsx | 46 ++++++++ src/components/Accordion/index.tsx | 107 ++++++++++++++++++ src/components/Accordion/utils.ts | 18 +++ src/components/NotificationV2/utils.ts | 3 +- src/components/index.ts | 1 + 7 files changed, 231 insertions(+), 2 deletions(-) create mode 100644 src/components/Accordion/Accordion.stories.tsx create mode 100644 src/components/Accordion/CollapseButton.tsx create mode 100644 src/components/Accordion/PanelContent.tsx create mode 100644 src/components/Accordion/index.tsx create mode 100644 src/components/Accordion/utils.ts diff --git a/src/components/Accordion/Accordion.stories.tsx b/src/components/Accordion/Accordion.stories.tsx new file mode 100644 index 00000000..aa5bf98b --- /dev/null +++ b/src/components/Accordion/Accordion.stories.tsx @@ -0,0 +1,37 @@ +import React from 'react' +import { Accordion, AccordionProps, Panel } from './index' +import { Meta, Story } from '@storybook/react/types-6-0' + +const mockPanels: Panel[] = [ + { content: 'Content1', key: 1, title: 'Title 1' }, + { content: 'Content2', key: 2, title: 'Title 2' }, + { content: 'Content3', key: 3, title: 'Title 3' } +] + +export default { + argTypes: { + panels: { control: { disable: true } } + }, + component: Accordion, + parameters: { + // disabled because shallow rendering doesn't work with decorator and hook inside decorator. + storyshots: { disable: true } + }, + title: 'Accordion' +} as Meta + +const Template: Story = args => ( + +) + +export const Default = Template.bind({}) + +export const Exclusive = Template.bind({}) +Exclusive.args = { + exclusive: true +} + +export const ExpandAll = Template.bind({}) +ExpandAll.args = { + expandAllOnMount: true +} diff --git a/src/components/Accordion/CollapseButton.tsx b/src/components/Accordion/CollapseButton.tsx new file mode 100644 index 00000000..f1a56051 --- /dev/null +++ b/src/components/Accordion/CollapseButton.tsx @@ -0,0 +1,21 @@ +import { faChevronDown } from '@fortawesome/free-solid-svg-icons' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import { motion } from 'framer-motion' +import React, { FC } from 'react' + +interface CollapseButtonProps { + isCollapsed: boolean +} + +export const CollapseButton: FC = ({ + isCollapsed +}: CollapseButtonProps) => ( + + + +) diff --git a/src/components/Accordion/PanelContent.tsx b/src/components/Accordion/PanelContent.tsx new file mode 100644 index 00000000..09542785 --- /dev/null +++ b/src/components/Accordion/PanelContent.tsx @@ -0,0 +1,46 @@ +import { createUseStyles } from 'react-jss' +import { styleguide } from '../assets/styles' +import { AnimatePresence, motion } from 'framer-motion' +import React, { FC, ReactNode } from 'react' + +const { spacing } = styleguide + +const useStyles = createUseStyles({ + content: { + paddingTop: spacing.s + } +}) + +interface PanelContentProps { + children: ReactNode + isActive: boolean +} + +export const PanelContent: FC = ({ + children, + isActive +}: PanelContentProps) => { + const classes = useStyles() + + return ( + + {isActive && ( + +
{children}
+
+ )} +
+ ) +} diff --git a/src/components/Accordion/index.tsx b/src/components/Accordion/index.tsx new file mode 100644 index 00000000..14b7c9f6 --- /dev/null +++ b/src/components/Accordion/index.tsx @@ -0,0 +1,107 @@ +import { CollapseButton } from './CollapseButton' +import { createUseStyles } from 'react-jss' +import { generateAccordionPanelStyles } from './utils' +import { PanelContent } from './PanelContent' +import React, { FC, Key, ReactNode, useState } from 'react' +import { styleguide, themes, ThemeType } from '../assets/styles' + +const { flexSpaceBetween, font } = styleguide +const { dark, light } = ThemeType + +const useStyles = createUseStyles({ + header: { + ...font.bodyLarge, + ...flexSpaceBetween, + cursor: 'pointer' + }, + panel: generateAccordionPanelStyles(light), + title: { + color: themes[light].primary + }, + // eslint-disable-next-line sort-keys + '@global': { + [`.${dark}`]: { + '& $panel': generateAccordionPanelStyles(dark), + '& $title': { color: themes[dark].state.hover } + } + } +}) + +export interface Panel { + content: ReactNode + key: Key + title: string +} + +interface SharedAccordionProps { + defaultActiveKey?: Key + panels: Panel[] +} + +interface ExclusiveAccordionProps extends SharedAccordionProps { + exclusive: true + expandAllOnMount?: never +} + +interface NonExclusiveAccordionProps extends SharedAccordionProps { + exclusive: false + expandAllOnMount?: boolean +} + +export type AccordionProps = + | ExclusiveAccordionProps + | NonExclusiveAccordionProps + +export const Accordion: FC = ({ + defaultActiveKey, + exclusive = false, + expandAllOnMount = false, + panels +}: AccordionProps) => { + const getInitialActiveKeys = () => { + const defaultActiveKeys: Key[] = [panels[0].key] + + if (defaultActiveKey) return [defaultActiveKey] + else if (expandAllOnMount) return panels.map(({ key }) => key) + + return defaultActiveKeys + } + + const [activeKeys, setActiveKeys] = useState(getInitialActiveKeys()) + const classes = useStyles() + + const togglePanel = (panelKey: Key) => { + let newActiveKeys = [...activeKeys, panelKey] + + // Close panel if it is open + if (activeKeys.includes(panelKey)) + newActiveKeys = activeKeys.filter(key => panelKey !== key) + // If accordion is exclusive, only one panel can be open at a time + else if (exclusive) newActiveKeys = [panelKey] + + setActiveKeys(newActiveKeys) + } + + return ( +
+ {panels.map(({ content, key, title }) => { + const isActivePanel = activeKeys.includes(key) + + return ( +
+
togglePanel(key)} + > +
{title}
+ +
+ + {content} + +
+ ) + })} +
+ ) +} diff --git a/src/components/Accordion/utils.ts b/src/components/Accordion/utils.ts new file mode 100644 index 00000000..3fa4e896 --- /dev/null +++ b/src/components/Accordion/utils.ts @@ -0,0 +1,18 @@ +import { styleguide, themedStyles, ThemeType } from '../assets/styles' + +const { spacing } = styleguide + +export const generateAccordionPanelStyles = (themeType: ThemeType) => { + const { base } = themedStyles[themeType] + + const borderStyles = `1px solid ${base.borderColor}` + + return { + '&:first-of-type': { + borderTop: borderStyles + }, + borderBottom: borderStyles, + color: base.color, + padding: spacing.m + } +} diff --git a/src/components/NotificationV2/utils.ts b/src/components/NotificationV2/utils.ts index c52acc61..b0ab860a 100644 --- a/src/components/NotificationV2/utils.ts +++ b/src/components/NotificationV2/utils.ts @@ -1,6 +1,5 @@ import { ev as NotificationTypes } from '@dassana-io/web-utils' import omit from 'lodash/omit' -import { styleguide } from 'components/assets/styles' import { v4 as uuidV4 } from 'uuid' import { faCheckCircle, @@ -8,7 +7,7 @@ import { faExclamationTriangle, faTimesCircle } from '@fortawesome/free-solid-svg-icons' -import { themedStyles, themes, ThemeType } from '../assets/styles/themes' +import { styleguide, themedStyles, themes, ThemeType } from '../assets/styles' import { useCallback, useState } from 'react' const { borderRadius, flexSpaceBetween, spacing } = styleguide diff --git a/src/components/index.ts b/src/components/index.ts index cd8ca457..37f029a8 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -1,3 +1,4 @@ +export * from './Accordion' export * from './Avatar' export * from './assets/styles' export * from './Button' From 5e893718a02b5db6231a98cd3baf69b07823b364 Mon Sep 17 00:00:00 2001 From: github-actions Date: Tue, 19 Jan 2021 12:39:27 -0800 Subject: [PATCH 2/4] Update CollapseButton component name to be more semantically correct --- .../Accordion/{CollapseButton.tsx => CollapseIndicator.tsx} | 6 +++--- src/components/Accordion/index.tsx | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) rename src/components/Accordion/{CollapseButton.tsx => CollapseIndicator.tsx} (75%) diff --git a/src/components/Accordion/CollapseButton.tsx b/src/components/Accordion/CollapseIndicator.tsx similarity index 75% rename from src/components/Accordion/CollapseButton.tsx rename to src/components/Accordion/CollapseIndicator.tsx index f1a56051..8ff5230d 100644 --- a/src/components/Accordion/CollapseButton.tsx +++ b/src/components/Accordion/CollapseIndicator.tsx @@ -3,13 +3,13 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { motion } from 'framer-motion' import React, { FC } from 'react' -interface CollapseButtonProps { +interface CollapseIndicatorProps { isCollapsed: boolean } -export const CollapseButton: FC = ({ +export const CollapseIndicator: FC = ({ isCollapsed -}: CollapseButtonProps) => ( +}: CollapseIndicatorProps) => ( = ({ onClick={() => togglePanel(key)} >
{title}
- + {content} From 48c1c161b72dce53a0cdfce2164c84cef0d3e1ba Mon Sep 17 00:00:00 2001 From: github-actions Date: Tue, 19 Jan 2021 12:40:31 -0800 Subject: [PATCH 3/4] Update package.json --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 91871a8c..a3f17929 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@dassana-io/web-components", - "version": "0.8.2", + "version": "0.8.3", "publishConfig": { "registry": "https://npm.pkg.github.com/dassana-io" }, From 297944b8bead124cce32f557048182fea956daa6 Mon Sep 17 00:00:00 2001 From: github-actions Date: Tue, 19 Jan 2021 15:48:54 -0800 Subject: [PATCH 4/4] Address PR comments --- src/components/Accordion/CollapseIndicator.tsx | 1 + src/components/Accordion/PanelContent.tsx | 3 ++- src/components/Accordion/index.tsx | 5 +++-- src/components/Accordion/utils.ts | 7 ++----- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/components/Accordion/CollapseIndicator.tsx b/src/components/Accordion/CollapseIndicator.tsx index 8ff5230d..2ee83231 100644 --- a/src/components/Accordion/CollapseIndicator.tsx +++ b/src/components/Accordion/CollapseIndicator.tsx @@ -14,6 +14,7 @@ export const CollapseIndicator: FC = ({ animate={{ rotate: isCollapsed ? 0 : 180 }} + transition={{ duration: 0.5 }} whileHover={{ scale: 1.1 }} > diff --git a/src/components/Accordion/PanelContent.tsx b/src/components/Accordion/PanelContent.tsx index 09542785..7564bc78 100644 --- a/src/components/Accordion/PanelContent.tsx +++ b/src/components/Accordion/PanelContent.tsx @@ -7,7 +7,8 @@ const { spacing } = styleguide const useStyles = createUseStyles({ content: { - paddingTop: spacing.s + padding: spacing.m, + paddingTop: 0 } }) diff --git a/src/components/Accordion/index.tsx b/src/components/Accordion/index.tsx index 97875c30..4741863f 100644 --- a/src/components/Accordion/index.tsx +++ b/src/components/Accordion/index.tsx @@ -5,14 +5,15 @@ import { PanelContent } from './PanelContent' import React, { FC, Key, ReactNode, useState } from 'react' import { styleguide, themes, ThemeType } from '../assets/styles' -const { flexSpaceBetween, font } = styleguide +const { flexSpaceBetween, font, spacing } = styleguide const { dark, light } = ThemeType const useStyles = createUseStyles({ header: { ...font.bodyLarge, ...flexSpaceBetween, - cursor: 'pointer' + cursor: 'pointer', + padding: spacing.m }, panel: generateAccordionPanelStyles(light), title: { diff --git a/src/components/Accordion/utils.ts b/src/components/Accordion/utils.ts index 3fa4e896..d35561bf 100644 --- a/src/components/Accordion/utils.ts +++ b/src/components/Accordion/utils.ts @@ -1,6 +1,4 @@ -import { styleguide, themedStyles, ThemeType } from '../assets/styles' - -const { spacing } = styleguide +import { themedStyles, ThemeType } from '../assets/styles' export const generateAccordionPanelStyles = (themeType: ThemeType) => { const { base } = themedStyles[themeType] @@ -12,7 +10,6 @@ export const generateAccordionPanelStyles = (themeType: ThemeType) => { borderTop: borderStyles }, borderBottom: borderStyles, - color: base.color, - padding: spacing.m + color: base.color } }