Skip to content

Commit 36d68c4

Browse files
committed
feat: Add optional width and zIndex props to SideSheet component
1 parent 8058032 commit 36d68c4

4 files changed

Lines changed: 109 additions & 6 deletions

File tree

src/organisms/SideSheet/SideSheet.stories.tsx

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,62 @@ export const StatefulModal: Story = {
335335
},
336336
};
337337

338+
export const CustomWidth: Story = {
339+
args: {
340+
type: 'standard',
341+
width: 800,
342+
},
343+
play: async ({ canvasElement, context }) => {
344+
const canvas = within(canvasElement);
345+
const { title } = context.args;
346+
347+
const heading = await canvas.findByRole('heading', {
348+
name: title as string,
349+
level: 2,
350+
});
351+
const sheet = heading.closest('aside') ?? heading.parentElement;
352+
await expect(sheet).not.toBeNull();
353+
await expect(getComputedStyle(sheet!).width).toBe('800px');
354+
},
355+
};
356+
357+
export const CustomZIndex: Story = {
358+
args: {
359+
type: 'modal',
360+
zIndex: 1234,
361+
},
362+
parameters: {
363+
layout: 'fullscreen',
364+
},
365+
decorators: (Story) => (
366+
<FloatingStoryWrapper>
367+
<Story />
368+
</FloatingStoryWrapper>
369+
),
370+
play: async ({ canvasElement, context }) => {
371+
const canvas = within(canvasElement);
372+
const { title } = context.args;
373+
374+
const heading = await canvas.findByRole('heading', {
375+
name: title as string,
376+
level: 2,
377+
});
378+
379+
let element: HTMLElement | null = heading;
380+
let foundZIndex: string | null = null;
381+
while (element) {
382+
const computed = getComputedStyle(element).zIndex;
383+
if (computed && computed !== 'auto') {
384+
foundZIndex = computed;
385+
break;
386+
}
387+
element = element.parentElement;
388+
}
389+
390+
await expect(foundZIndex).toBe('1234');
391+
},
392+
};
393+
338394
export const StatefulModalWithScrim: Story = {
339395
args: {
340396
type: 'modal',

src/organisms/SideSheet/SideSheet.styles.ts

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { CSSProperties } from 'react';
2+
13
import { colors, elevation, shape, spacings } from 'src/atoms/style';
24
import type { SideSheetProps } from 'src/organisms';
35

@@ -7,11 +9,12 @@ import { css, styled } from 'styled-components';
79
interface WrapperProps {
810
$type: NonNullable<SideSheetProps['type']>;
911
$withShadow?: boolean;
12+
$zIndex?: CSSProperties['zIndex'];
1013
}
1114

1215
export const Wrapper = styled(motion.div)<WrapperProps>`
1316
overflow: hidden;
14-
${({ $type, $withShadow }) => {
17+
${({ $type, $withShadow, $zIndex }) => {
1518
if ($type === 'standard') return '';
1619
1720
if ($type === 'floating') {
@@ -21,6 +24,10 @@ export const Wrapper = styled(motion.div)<WrapperProps>`
2124
right: ${spacings.medium};
2225
box-shadow: ${elevation.sticky};
2326
border-radius: ${shape.corners.borderRadius};
27+
${$zIndex !== undefined &&
28+
css`
29+
z-index: ${$zIndex};
30+
`}
2431
`;
2532
}
2633
@@ -29,18 +36,27 @@ export const Wrapper = styled(motion.div)<WrapperProps>`
2936
top: 64px;
3037
right: 0;
3138
box-shadow: ${$withShadow ? elevation.above_scrim : 'none'};
39+
${$zIndex !== undefined &&
40+
css`
41+
z-index: ${$zIndex};
42+
`}
3243
`;
3344
}}
3445
`;
3546

3647
interface SheetProps {
3748
$type: NonNullable<SideSheetProps['type']>;
49+
$width?: CSSProperties['width'];
3850
}
3951

4052
export const Sheet = styled.div<SheetProps>`
4153
display: flex;
4254
flex-direction: column;
43-
width: 500px;
55+
width: ${({ $width }) => {
56+
if ($width === undefined) return '500px';
57+
if (typeof $width === 'number') return `${$width}px`;
58+
return $width;
59+
}};
4460
background: ${colors.ui.background__default.rgba};
4561
${({ $type }) => {
4662
if ($type === 'standard') {
@@ -64,12 +80,16 @@ export const Header = styled.div`
6480
padding: ${spacings.medium} ${spacings.large};
6581
`;
6682

67-
export const ScrimWrapper = styled(motion.div)`
83+
interface ScrimWrapperProps {
84+
$zIndex?: CSSProperties['zIndex'];
85+
}
86+
87+
export const ScrimWrapper = styled(motion.div)<ScrimWrapperProps>`
6888
position: absolute;
6989
top: 0;
7090
left: 0;
7191
width: 100%;
7292
overflow: hidden;
7393
height: calc(100vh - 64px);
74-
z-index: 10000;
94+
z-index: ${({ $zIndex }) => $zIndex ?? 10000};
7595
`;

src/organisms/SideSheet/SideSheet.tsx

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import { AnimatePresence } from 'motion/react';
1616
* @param title - Title to display in the header
1717
* @param type - How the side sheet should be position, default is standard
1818
* @param headerElements - Optional spot to put elements in the header, for example more action buttons
19+
* @param width - Optional width override (number is treated as px)
1920
* @param children - Content in the side sheet
2021
*/
2122
export const SideSheet: FC<SideSheetProps> = ({
@@ -24,6 +25,7 @@ export const SideSheet: FC<SideSheetProps> = ({
2425
title,
2526
type = 'standard',
2627
headerElements,
28+
width,
2729
children,
2830
...rest
2931
}) => {
@@ -32,6 +34,7 @@ export const SideSheet: FC<SideSheetProps> = ({
3234
<ScrimWrapper
3335
data-testid="side-sheet-scrim"
3436
onClick={open ? onClose : undefined}
37+
$zIndex={'zIndex' in rest ? rest.zIndex : undefined}
3538
initial={{
3639
display: 'none',
3740
background: 'rgba(111,111,111,0)',
@@ -47,6 +50,8 @@ export const SideSheet: FC<SideSheetProps> = ({
4750
title={title}
4851
type="modal"
4952
headerElements={headerElements}
53+
width={width}
54+
zIndex={'zIndex' in rest ? rest.zIndex : undefined}
5055
withScrim
5156
>
5257
{children}
@@ -55,13 +60,30 @@ export const SideSheet: FC<SideSheetProps> = ({
5560
);
5661
}
5762

63+
if (type === 'modal' || type === 'floating') {
64+
return (
65+
<SideSheetContent
66+
open={open}
67+
onClose={onClose}
68+
title={title}
69+
type={type}
70+
headerElements={headerElements}
71+
width={width}
72+
zIndex={'zIndex' in rest ? rest.zIndex : undefined}
73+
>
74+
{children}
75+
</SideSheetContent>
76+
);
77+
}
78+
5879
return (
5980
<SideSheetContent
6081
open={open}
6182
onClose={onClose}
6283
title={title}
6384
type={type}
6485
headerElements={headerElements}
86+
width={width}
6587
>
6688
{children}
6789
</SideSheetContent>
@@ -74,6 +96,7 @@ function SideSheetContent({
7496
title,
7597
type = 'standard',
7698
headerElements,
99+
width,
77100
children,
78101
...rest
79102
}: SideSheetProps) {
@@ -83,6 +106,7 @@ function SideSheetContent({
83106
<Wrapper
84107
$type={type}
85108
$withShadow={!('withScrim' in rest && rest.withScrim)}
109+
$zIndex={'zIndex' in rest ? rest.zIndex : undefined}
86110
initial={{
87111
x: type !== 'standard' ? '110%' : undefined,
88112
width: type === 'standard' ? 0 : undefined,
@@ -99,7 +123,7 @@ function SideSheetContent({
99123
bounce: 0.25,
100124
}}
101125
>
102-
<Sheet $type={type}>
126+
<Sheet $type={type} $width={width}>
103127
<Header>
104128
<Typography variant="h2">{title}</Typography>
105129
{headerElements && <section>{headerElements}</section>}

src/organisms/SideSheet/SideSheet.types.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1-
import { ReactElement } from 'react';
1+
import { CSSProperties, ReactElement } from 'react';
22

33
interface BaseSideSheetProps {
44
open: boolean;
55
onClose: () => void;
66
title: string;
77
headerElements?: ReactElement | ReactElement[];
8+
width?: CSSProperties['width'];
89
children: ReactElement | ReactElement[];
910
}
1011

@@ -15,10 +16,12 @@ interface StandardSideSheetProps extends BaseSideSheetProps {
1516
interface ModalSideSheetProps extends BaseSideSheetProps {
1617
type: 'modal';
1718
withScrim?: boolean;
19+
zIndex?: CSSProperties['zIndex'];
1820
}
1921

2022
interface FloatingSideSheetProps extends BaseSideSheetProps {
2123
type: 'floating';
24+
zIndex?: CSSProperties['zIndex'];
2225
}
2326

2427
export type SideSheetProps =

0 commit comments

Comments
 (0)