-
Notifications
You must be signed in to change notification settings - Fork 120
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(UXPT-6139): add new drawer component (#1460)
- Loading branch information
1 parent
f27b766
commit 07b6bc3
Showing
5 changed files
with
470 additions
and
0 deletions.
There are no files selected for viewing
10 changes: 10 additions & 0 deletions
10
common/changes/pcln-design-system/feat-UXPT-6139-drawer-component_2024-02-23-15-49.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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')}; | ||
} | ||
` |
Oops, something went wrong.