Skip to content

Commit

Permalink
feat(UXPT-6139): add new drawer component (#1460)
Browse files Browse the repository at this point in the history
  • Loading branch information
fareedahmad1 authored Mar 13, 2024
1 parent f27b766 commit 07b6bc3
Show file tree
Hide file tree
Showing 5 changed files with 470 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "pcln-design-system",
"comment": "add new drawer component",
"type": "minor"
}
],
"packageName": "pcln-design-system"
}
51 changes: 51 additions & 0 deletions packages/core/src/Drawer/Drawer.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import React from 'react'
import { render } from '../__test__/testing-library'
import { Drawer } from './Drawer'

describe('Drawer', () => {
test('renders Drawer with no heading', () => {
const { queryByText } = render(<Drawer isOpen={true} placement='right' onCollapse={jest.fn} />)
expect(queryByText('Drawer Heading')).toBeFalsy()
})
test('renders collapsed Drawer', () => {
const { queryByText } = render(
<Drawer isOpen={true} isCollapsed={true} placement='right'>
Drawer Content
</Drawer>
)
expect(queryByText('Drawer Content')).toBeFalsy()
})

test('renders mobile Drawer', () => {
const { queryByText } = render(
<Drawer isOpen={true} isMobile={true} isCollapsed={true}>
Drawer Content
</Drawer>
)
expect(queryByText('Drawer Content')).toBeFalsy()
})

test('renders heading from string prop', () => {
const { queryByText } = render(
<Drawer isOpen={true} isMobile={true} isCollapsed={true} heading='Default Heading'>
Drawer Content
</Drawer>
)
expect(queryByText('Default Heading')).toBeTruthy()
})

test('renders Drawer with custom heading element', () => {
const { getByText } = render(
<Drawer
isOpen={true}
placement='right'
onClose={jest.fn}
onCollapse={jest.fn}
heading={<div>Custom Heading</div>}
>
Content
</Drawer>
)
expect(getByText('Custom Heading')).toBeTruthy()
})
})
182 changes: 182 additions & 0 deletions packages/core/src/Drawer/Drawer.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
import { Box } from '../Box/Box'
import { Flex } from '../Flex/Flex'
import { Sparkle } from 'pcln-icons'
import { Drawer } from './Drawer'
import React, { useState } from 'react'
import { Text } from '../Text/Text'
import { Stamp } from '../Stamp/Stamp'
import { ChatMessageContainer } from '../ChatMessageContainer/ChatMessageContainer'

function DrawerStory(props) {
const [isOpen, setIsOpen] = useState(true)
const [isCollapsed, setIsCollapsed] = useState(false)

const { includeCloseButton = true, includeCollapseButton = true } = props

return (
<Box height='50vh' p={0}>
<Drawer
heading='Header'
isOpen={isOpen}
isCollapsed={isCollapsed}
onClose={includeCloseButton ? () => setIsOpen(false) : undefined}
onCollapse={includeCollapseButton ? () => setIsCollapsed((isCollapsed) => !isCollapsed) : undefined}
{...props}
>
{props.children}
</Drawer>
</Box>
)
}

const CustomPennyHeader = (
<Flex width='100%' alignItems='center'>
<Text color='text.heading' textStyle='heading5'>
Penny
<Sparkle color='promoPrimary' size='16px' />
</Text>
<Stamp color='primary' mx={2}>
Beta
</Stamp>
</Flex>
)

const CustomPennyContent = (
<ChatMessageContainer
messageMaxWidth='90%'
messages={[
{
dateTime: 'Wed Jul 11 2023 11:33:52 GMT-0500',
message: "Hi! I'm Penny, your virtual travel assistant!",
variation: 'initial',
},
{
dateTime: 'Wed Jul 11 2023 11:33:52 GMT-0500',
message:
'I see that you are interested in booking the Hilton Hawaiian Village for 3 nights from Mon, Apr 10, 2023, to Thu, Apr 13, 2023. The total cost for this stay is $796.68, with a total tax and fee of $111.57. There is an additional fee of $130.83 for facilities when you check-in. This hotel features free WiFi and is located in the Waikiki Beach Marina area. The room has 1 king bed and is rated 4 stars.',
variation: 'incoming',
},
{
dateTime: 'Wed Jul 11 2023 11:34:12 GMT-0500',
message: 'Is this hotel in a good location?',
variation: 'outgoing',
},
{
dateTime: 'Wed Jul 11 2023 11:34:30 GMT-0500',
message:
"Yes, the Hilton Hawaiian Village is located in a fantastic area, specifically in the Waikiki Beach Marina - Ala Moana neighborhood. This location is renowned for its beautiful beaches, vibrant nightlife, and a wide variety of shopping and dining options. It's a great spot if you're looking to experience the best of Honolulu. If you have any other questions or if you're ready to book, feel free to ask!",
variation: 'incoming',
},
{
dateTime: 'Wed Jul 11 2023 11:33:52 GMT-0500',
message: "Hi! I'm Penny, your virtual travel assistant!",
variation: 'initial',
},
{
dateTime: 'Wed Jul 11 2023 11:33:52 GMT-0500',
message:
'I see that you are interested in booking the Hilton Hawaiian Village for 3 nights from Mon, Apr 10, 2023, to Thu, Apr 13, 2023. The total cost for this stay is $796.68, with a total tax and fee of $111.57. There is an additional fee of $130.83 for facilities when you check-in. This hotel features free WiFi and is located in the Waikiki Beach Marina area. The room has 1 king bed and is rated 4 stars.',
variation: 'incoming',
},
{
dateTime: 'Wed Jul 11 2023 11:34:12 GMT-0500',
message: 'Is this hotel in a good location?',
variation: 'outgoing',
},
{
dateTime: 'Wed Jul 11 2023 11:34:30 GMT-0500',
message:
"Yes, the Hilton Hawaiian Village is located in a fantastic area, specifically in the Waikiki Beach Marina - Ala Moana neighborhood. This location is renowned for its beautiful beaches, vibrant nightlife, and a wide variety of shopping and dining options. It's a great spot if you're looking to experience the best of Honolulu. If you have any other questions or if you're ready to book, feel free to ask!",
variation: 'incoming',
},
]}
/>
)

export default {
title: 'Drawer',
component: Drawer,
args: {
width: '50%',
height: '100%',
placement: 'right',
isFloating: true,
},
argTypes: {
placement: {
options: ['top', 'right', 'bottom', 'left'],
control: { type: 'select' },
description: 'moves the drawer to the specified position',
},
isFloating: {
description: 'set to false to anchor the drawer to a specific side',
},
},
}

export const Top = (args) => (
<DrawerStory {...args} height='50%' width='100%' placement='top'>
Drawer Content
</DrawerStory>
)

export const Bottom = (args) => (
<DrawerStory {...args} height='50%' width='100%' placement='bottom'>
Drawer Content
</DrawerStory>
)
export const Left = (args) => (
<DrawerStory {...args} placement='left'>
Drawer Content
</DrawerStory>
)
export const Right = (args) => (
<DrawerStory {...args} placement='right'>
Drawer Content
</DrawerStory>
)
export const Anchored = (args) => (
<DrawerStory {...args} isFloating={false} placement='right'>
Drawer Content
</DrawerStory>
)

export const Mobile = (args) => (
<DrawerStory {...args} isMobile isFloating={false} placement='right'>
Drawer Content
</DrawerStory>
)
export const WithNoHeader = (args) => (
<DrawerStory
{...args}
placement='right'
heading={undefined}
includeCloseButton={false}
includeCollapseButton={false}
>
{CustomPennyContent}
</DrawerStory>
)
export const WithCustomHeaderAndContent = (args) => (
<DrawerStory {...args} placement='right' heading={CustomPennyHeader}>
{CustomPennyContent}
</DrawerStory>
)

export const WithCloseButton = (args) => (
<DrawerStory {...args} placement='right' onCollapse={undefined} includeCollapseButton={false}>
Drawer Content
</DrawerStory>
)

export const WithCollapseButton = (args) => (
<DrawerStory {...args} placement='right' includeCloseButton={false}>
Drawer Content
</DrawerStory>
)

export const WithContentOverflow = (args) => (
<DrawerStory {...args} height='50vh' placement='right'>
{CustomPennyContent}
</DrawerStory>
)
66 changes: 66 additions & 0 deletions packages/core/src/Drawer/Drawer.styled.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import styled from 'styled-components'
import { Flex } from '../Flex/Flex'
import { theme } from '../theme'
import { motion } from 'framer-motion'
import React from 'react'
import { Absolute } from '../Absolute/Absolute'
import { getPaletteColor } from '../utils'
import { Box } from '../Box/Box'
import { IconButton } from '../IconButton/IconButton'

const Component = React.forwardRef((props, ref: React.MutableRefObject<HTMLDivElement>) => (
<Box {...props} ref={ref} />
))

const AnimatedAbsolute = motion(Component)

const getBorderRadiusStyles = (placement, isFloating) => {
const themeRadius = theme.borderRadii['2xl']
if (isFloating) {
return themeRadius
}
switch (placement) {
case 'top':
return `0px 0px ${themeRadius} ${themeRadius}`
case 'right':
return `${themeRadius} 0px 0px ${themeRadius}`
case 'bottom':
return `${themeRadius} ${themeRadius} 0px 0px`
case 'left':
return `0px ${themeRadius} ${themeRadius} 0px`
}
}
export type PlacementOptions = 'top' | 'bottom' | 'right' | 'left'
const positions: Record<PlacementOptions, string> = {
top: `top:0; left:0; right:0;`,
right: `right: 0; bottom:0;`,
bottom: `bottom:0; left:0; right:0;`,
left: `left:0;bottom:0;`,
}
const getPlacementStyles = (placement) =>
placement ? `margin-inline:auto; margin-block:auto; ${positions[placement]};` : ``
export const DrawerWrapper = styled(Absolute)`
${({ placement }) =>
`
${getPlacementStyles(placement)}
`}
`
export const DrawerRoot = styled(AnimatedAbsolute)`
background-color: ${getPaletteColor('background.lightest')};
${({ theme, placement, isFloating }) =>
`box-shadow: ${theme.shadows['overlay-md']};
border-radius: ${getBorderRadiusStyles(placement, isFloating)};
`}
`
export const HeadingWrapper = styled(Flex)``

export const HeaderButton = styled(IconButton)`
background-color: ${getPaletteColor('background.lightest')};
color: ${getPaletteColor('text.dark')};
padding: 0;
&:hover {
background-color: ${getPaletteColor('background.light')};
color: ${getPaletteColor('text.dark')};
}
`
Loading

0 comments on commit 07b6bc3

Please sign in to comment.