From 86a3cc5db22de4d00021f440742fd3919d82d157 Mon Sep 17 00:00:00 2001 From: Kyler Berry Date: Fri, 30 Jun 2023 13:10:15 -0400 Subject: [PATCH 1/4] initial commit on a new drawer component --- src/components/Drawer/Drawer.story.tsx | 0 src/components/Drawer/Drawer.styles.tsx | 49 +++++++++++++ src/components/Drawer/Drawer.tsx | 34 +++++++++ src/components/Drawer/Drawer.types.ts | 69 +++++++++++++++++++ .../Drawer/Minors/DrawerContainer.tsx | 20 ++++++ src/components/Drawer/util.ts | 14 ++++ 6 files changed, 186 insertions(+) create mode 100644 src/components/Drawer/Drawer.story.tsx create mode 100644 src/components/Drawer/Drawer.styles.tsx create mode 100644 src/components/Drawer/Drawer.tsx create mode 100644 src/components/Drawer/Drawer.types.ts create mode 100644 src/components/Drawer/Minors/DrawerContainer.tsx create mode 100644 src/components/Drawer/util.ts diff --git a/src/components/Drawer/Drawer.story.tsx b/src/components/Drawer/Drawer.story.tsx new file mode 100644 index 000000000..e69de29bb diff --git a/src/components/Drawer/Drawer.styles.tsx b/src/components/Drawer/Drawer.styles.tsx new file mode 100644 index 000000000..3b98e177f --- /dev/null +++ b/src/components/Drawer/Drawer.styles.tsx @@ -0,0 +1,49 @@ +import styled from 'styled-components'; +import { rem } from 'polished'; +import { DrawerSize } from './Drawer.types'; + +export const DRAWER_WIDTH_XS = 276; +export const DRAWER_WIDTH_SM = 308; +export const DRAWER_WIDTH_MD = 340; +export const DRAWER_WIDTH_LG = 372; +export const DRAWER_WIDTH_XL = 404; + +export const DRAWER_SIZE = { + xs: DRAWER_WIDTH_XS, + sm: DRAWER_WIDTH_SM, + md: DRAWER_WIDTH_MD, + lg: DRAWER_WIDTH_LG, + xl: DRAWER_WIDTH_XL, +}; + +function panelSize(size: DrawerSize) { + switch (size) { + case 'xs': + return rem(DRAWER_WIDTH_XS); + case 'md': + return rem(DRAWER_WIDTH_MD); + case 'lg': + return rem(DRAWER_WIDTH_LG); + case 'xl': + return rem(DRAWER_WIDTH_XL); + case 'sm': + default: + return rem(DRAWER_WIDTH_SM); + } +} + +export const WithSize = styled.div<{ size: DrawerSize }>` + width: ${({ size }) => panelSize(size)}; +`; + +export const WithPlacement = styled.div<{ left: boolean }>` + ${({ left }) => + left + ? ` + left: 0; + right: initial;` + : ` + right: 0; + left: initial; + `} +`; diff --git a/src/components/Drawer/Drawer.tsx b/src/components/Drawer/Drawer.tsx new file mode 100644 index 000000000..25792d031 --- /dev/null +++ b/src/components/Drawer/Drawer.tsx @@ -0,0 +1,34 @@ +import React from 'react'; +import { DrawerProps, Minors } from './Drawer.types'; +import { AnimatePresence, motion } from 'framer-motion'; +import { withIris } from '../../utils'; +import { DrawerContainer } from './Minors/DrawerContainer'; +import { getAnimation } from './util'; + +const DrawerComponent = ({ + placement, + isOpen, + size, + // autoFocus, + // initialFocusRef, + // finalFocusRef, + // returnFocusOnClose, + // preserveScrollBarGap, + children, +}: DrawerProps) => { + return ( + + {isOpen && ( + + + {children} + + + )} + + ); +}; + +export const Drawer = withIris( + DrawerComponent +); diff --git a/src/components/Drawer/Drawer.types.ts b/src/components/Drawer/Drawer.types.ts new file mode 100644 index 000000000..4a2aa3a45 --- /dev/null +++ b/src/components/Drawer/Drawer.types.ts @@ -0,0 +1,69 @@ +import React, { HTMLProps } from 'react'; +import { IrisProps, MinorComponent } from '../../utils'; + +export type DrawerSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl'; + +export interface FocusableElement { + focus(options?: FocusOptions): void; +} + +export type DrawerProps = IrisProps<{ + /** + * Whether the drawer is visible or not + * + * @default false + */ + isOpen: boolean; + /** + * The placement of the drawer + * + * @default "left" + */ + placement?: 'left' | 'right'; + /** + * Width of the drawer + * + * @default "sm" + */ + size: DrawerSize; + /** + * If `true`, the modal will autofocus the first enabled and interactive + * element within the `ModalContent` + * + * @default true + */ + autoFocus?: boolean; + /** + * The `ref` of element to receive focus when the modal opens. + */ + initialFocusRef?: React.RefObject; + /** + * The `ref` of element to receive focus when the modal closes. + */ + finalFocusRef?: React.RefObject; + /** + * If `true`, the modal will return focus to the element that triggered it when it closes. + * + * @default true + */ + returnFocusOnClose?: boolean; + /** + * If `true`, a `padding-right` will be applied to the body element + * that's equal to the width of the scrollbar. + * + * This can help prevent some unpleasant flickering effect + * and content adjustment when the modal opens + * + * @default true + */ + preserveScrollBarGap?: boolean; +}>; + +export interface Minors { + DrawerHeader: MinorComponent>; + DrawerContent: MinorComponent>; + DrawerBody: MinorComponent>; + DrawerFooter: MinorComponent>; + DrawerOverlay: MinorComponent>; + DrawerCloseButton: MinorComponent>; +} diff --git a/src/components/Drawer/Minors/DrawerContainer.tsx b/src/components/Drawer/Minors/DrawerContainer.tsx new file mode 100644 index 000000000..2e8a3cd84 --- /dev/null +++ b/src/components/Drawer/Minors/DrawerContainer.tsx @@ -0,0 +1,20 @@ +import React from 'react'; +import { WithPlacement, WithSize } from '../Drawer.styles'; +import { DrawerSize } from '../Drawer.types'; + +interface Props { + size: DrawerSize; + placement: 'left' | 'right'; +} + +export const DrawerContainer = ({ + size, + placement, + children, +}: React.PropsWithChildren) => { + return ( + + {children} + + ); +}; diff --git a/src/components/Drawer/util.ts b/src/components/Drawer/util.ts new file mode 100644 index 000000000..f0faa5b92 --- /dev/null +++ b/src/components/Drawer/util.ts @@ -0,0 +1,14 @@ +import { DRAWER_SIZE } from './Drawer.styles'; +import { DrawerSize } from './Drawer.types'; + +export function getAnimation( + placement: 'left' | 'right', + size: DrawerSize, + pad = 0 +) { + return { + initial: { [`${placement}`]: -DRAWER_SIZE[size] }, + animate: { [`${placement}`]: 0 + pad }, + exit: { [`${placement}`]: -DRAWER_SIZE[size] }, + }; +} From fbdde8612f2010a89f4d0cfc9ca9eeb1deb98556 Mon Sep 17 00:00:00 2001 From: Kyler Berry Date: Fri, 30 Jun 2023 16:24:08 -0400 Subject: [PATCH 2/4] fix animations and make basic story --- src/components/Drawer/Drawer.story.tsx | 25 +++++++++++++++++++++++++ src/components/Drawer/Drawer.styles.tsx | 1 + src/components/Drawer/Drawer.tsx | 4 ++-- src/components/Drawer/Drawer.types.ts | 4 ++-- src/components/Drawer/util.ts | 15 +++++++-------- 5 files changed, 37 insertions(+), 12 deletions(-) diff --git a/src/components/Drawer/Drawer.story.tsx b/src/components/Drawer/Drawer.story.tsx index e69de29bb..fad7baf81 100644 --- a/src/components/Drawer/Drawer.story.tsx +++ b/src/components/Drawer/Drawer.story.tsx @@ -0,0 +1,25 @@ +import React from 'react'; +import { Drawer } from './Drawer'; +import { Story } from '@storybook/react'; +import { DrawerProps } from './Drawer.types'; + +export default { + title: 'components/Drawer', + component: Drawer, + argTypes: { + isOpen: { control: 'boolean', defaultValue: false }, + placement: { control: 'string', defaultValue: 'left' }, + size: { control: 'string', defaultValue: 'md' }, + }, +}; + +const Template: Story = ({ isOpen, placement }) => { + return ( + +
Hi! I'm Drawer Content!
+
+ ); +}; + +export const DrawerTemplate = Template.bind({}); +DrawerTemplate.storyName = 'Drawer'; diff --git a/src/components/Drawer/Drawer.styles.tsx b/src/components/Drawer/Drawer.styles.tsx index 3b98e177f..119163901 100644 --- a/src/components/Drawer/Drawer.styles.tsx +++ b/src/components/Drawer/Drawer.styles.tsx @@ -34,6 +34,7 @@ function panelSize(size: DrawerSize) { export const WithSize = styled.div<{ size: DrawerSize }>` width: ${({ size }) => panelSize(size)}; + height: 100vh; `; export const WithPlacement = styled.div<{ left: boolean }>` diff --git a/src/components/Drawer/Drawer.tsx b/src/components/Drawer/Drawer.tsx index 25792d031..da9caa409 100644 --- a/src/components/Drawer/Drawer.tsx +++ b/src/components/Drawer/Drawer.tsx @@ -8,7 +8,7 @@ import { getAnimation } from './util'; const DrawerComponent = ({ placement, isOpen, - size, + size = 'md', // autoFocus, // initialFocusRef, // finalFocusRef, @@ -19,7 +19,7 @@ const DrawerComponent = ({ return ( {isOpen && ( - + {children} diff --git a/src/components/Drawer/Drawer.types.ts b/src/components/Drawer/Drawer.types.ts index 4a2aa3a45..5852145b1 100644 --- a/src/components/Drawer/Drawer.types.ts +++ b/src/components/Drawer/Drawer.types.ts @@ -23,9 +23,9 @@ export type DrawerProps = IrisProps<{ /** * Width of the drawer * - * @default "sm" + * @default "md" */ - size: DrawerSize; + size?: DrawerSize; /** * If `true`, the modal will autofocus the first enabled and interactive * element within the `ModalContent` diff --git a/src/components/Drawer/util.ts b/src/components/Drawer/util.ts index f0faa5b92..bd2a46c63 100644 --- a/src/components/Drawer/util.ts +++ b/src/components/Drawer/util.ts @@ -1,14 +1,13 @@ import { DRAWER_SIZE } from './Drawer.styles'; import { DrawerSize } from './Drawer.types'; -export function getAnimation( - placement: 'left' | 'right', - size: DrawerSize, - pad = 0 -) { +const TRANSITION_DURATION = 0.2; // 200ms + +export function getAnimation(size: DrawerSize, pad = 0) { return { - initial: { [`${placement}`]: -DRAWER_SIZE[size] }, - animate: { [`${placement}`]: 0 + pad }, - exit: { [`${placement}`]: -DRAWER_SIZE[size] }, + initial: { x: -DRAWER_SIZE[size] }, + animate: { x: 0 + pad }, + exit: { x: -DRAWER_SIZE[size] }, + transition: { type: 'tween', duration: TRANSITION_DURATION }, }; } From 73605fb9438911e3b8626b14d8f867fdca4615bd Mon Sep 17 00:00:00 2001 From: Kyler Berry Date: Fri, 30 Jun 2023 16:44:14 -0400 Subject: [PATCH 3/4] Fix placement prop --- src/components/Drawer/Drawer.story.tsx | 21 ++++++++++++++++++--- src/components/Drawer/Drawer.styles.tsx | 12 +++++++----- src/components/Drawer/Drawer.tsx | 2 +- src/components/Drawer/util.ts | 12 ++++++++---- 4 files changed, 34 insertions(+), 13 deletions(-) diff --git a/src/components/Drawer/Drawer.story.tsx b/src/components/Drawer/Drawer.story.tsx index fad7baf81..07dde247c 100644 --- a/src/components/Drawer/Drawer.story.tsx +++ b/src/components/Drawer/Drawer.story.tsx @@ -2,24 +2,39 @@ import React from 'react'; import { Drawer } from './Drawer'; import { Story } from '@storybook/react'; import { DrawerProps } from './Drawer.types'; +import styled from 'styled-components'; export default { title: 'components/Drawer', component: Drawer, argTypes: { isOpen: { control: 'boolean', defaultValue: false }, - placement: { control: 'string', defaultValue: 'left' }, - size: { control: 'string', defaultValue: 'md' }, + placement: { options: ['left', 'right'], defaultValue: 'left' }, + size: { + options: ['xs', 'sm', 'md', 'lg', 'xl'], + defaultValue: 'md', + }, }, }; const Template: Story = ({ isOpen, placement }) => { + console.log(placement); return ( -
Hi! I'm Drawer Content!
+ Hi! I'm Drawer Content!
); }; +const DrawerContent = styled.div` + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: white; + color: black; +`; + export const DrawerTemplate = Template.bind({}); DrawerTemplate.storyName = 'Drawer'; diff --git a/src/components/Drawer/Drawer.styles.tsx b/src/components/Drawer/Drawer.styles.tsx index 119163901..f579044c9 100644 --- a/src/components/Drawer/Drawer.styles.tsx +++ b/src/components/Drawer/Drawer.styles.tsx @@ -33,18 +33,20 @@ function panelSize(size: DrawerSize) { } export const WithSize = styled.div<{ size: DrawerSize }>` + position: relative; width: ${({ size }) => panelSize(size)}; height: 100vh; `; export const WithPlacement = styled.div<{ left: boolean }>` + position: fixed; ${({ left }) => left ? ` - left: 0; - right: initial;` + left: 0; + right: initial;` : ` - right: 0; - left: initial; - `} + right: 0; + left: initial; + `} `; diff --git a/src/components/Drawer/Drawer.tsx b/src/components/Drawer/Drawer.tsx index da9caa409..c3fbd4067 100644 --- a/src/components/Drawer/Drawer.tsx +++ b/src/components/Drawer/Drawer.tsx @@ -19,7 +19,7 @@ const DrawerComponent = ({ return ( {isOpen && ( - + {children} diff --git a/src/components/Drawer/util.ts b/src/components/Drawer/util.ts index bd2a46c63..74379d174 100644 --- a/src/components/Drawer/util.ts +++ b/src/components/Drawer/util.ts @@ -3,11 +3,15 @@ import { DrawerSize } from './Drawer.types'; const TRANSITION_DURATION = 0.2; // 200ms -export function getAnimation(size: DrawerSize, pad = 0) { +export function getAnimation( + size: DrawerSize, + placement: 'left' | 'right' +) { + const left = placement === 'left'; return { - initial: { x: -DRAWER_SIZE[size] }, - animate: { x: 0 + pad }, - exit: { x: -DRAWER_SIZE[size] }, + initial: { x: left ? -DRAWER_SIZE[size] : DRAWER_SIZE[size] }, + animate: { x: 0 }, + exit: { x: left ? -DRAWER_SIZE[size] : DRAWER_SIZE[size] }, transition: { type: 'tween', duration: TRANSITION_DURATION }, }; } From 1b698541509f67353fe2bbfe5a76fbe13363edee Mon Sep 17 00:00:00 2001 From: Kyler Berry Date: Fri, 30 Jun 2023 16:57:31 -0400 Subject: [PATCH 4/4] fix size prop --- src/components/Drawer/Drawer.story.tsx | 9 ++++++--- src/components/Drawer/util.ts | 7 ++++--- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/components/Drawer/Drawer.story.tsx b/src/components/Drawer/Drawer.story.tsx index 07dde247c..cfc0d53a7 100644 --- a/src/components/Drawer/Drawer.story.tsx +++ b/src/components/Drawer/Drawer.story.tsx @@ -17,10 +17,13 @@ export default { }, }; -const Template: Story = ({ isOpen, placement }) => { - console.log(placement); +const Template: Story = ({ + isOpen, + placement, + size, +}) => { return ( - + Hi! I'm Drawer Content! ); diff --git a/src/components/Drawer/util.ts b/src/components/Drawer/util.ts index 74379d174..89aff38ee 100644 --- a/src/components/Drawer/util.ts +++ b/src/components/Drawer/util.ts @@ -1,8 +1,6 @@ import { DRAWER_SIZE } from './Drawer.styles'; import { DrawerSize } from './Drawer.types'; -const TRANSITION_DURATION = 0.2; // 200ms - export function getAnimation( size: DrawerSize, placement: 'left' | 'right' @@ -12,6 +10,9 @@ export function getAnimation( initial: { x: left ? -DRAWER_SIZE[size] : DRAWER_SIZE[size] }, animate: { x: 0 }, exit: { x: left ? -DRAWER_SIZE[size] : DRAWER_SIZE[size] }, - transition: { type: 'tween', duration: TRANSITION_DURATION }, + transition: { + type: 'tween', + duration: (DRAWER_SIZE[size] / 6 + 90) / 1000, + }, }; }