Skip to content

Commit 45655a7

Browse files
authored
DrawerDialog: Add support for zeroed out padding (#2773)
## Summary: The DrawerDialog needs an update to support zeroed-out padding where borders and content go all the way to the edge. There is padding set in the `ModalContent` component, a nested child deep in the structure. In this PR, plumbing is created to allow a `styles.content` object to override the padding in `ModalContent`. Issue: WB-2079 ## Test plan: 1. Review stories for DrawerDialog, FlexibleDialog, and Modal Testing snapshot stories 2. Ensure ModalContent has no unexpected layout shifts in OG modals 3. Determine whether media query usage in ModalContent can be deferred until later (WB-2080) Author: marcysutton Reviewers: beaesguerra, marcysutton Required Reviewers: Approved By: beaesguerra Checks: ✅ 12 checks were successful, ⏭️ 2 checks have been skipped Pull Request URL: #2773
1 parent 4546204 commit 45655a7

File tree

11 files changed

+188
-7
lines changed

11 files changed

+188
-7
lines changed

.changeset/thin-candles-nail.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@khanacademy/wonder-blocks-modal": minor
3+
---
4+
5+
Add support for no padding to DrawerDialog and FlexibleDialog

__docs__/wonder-blocks-modal/drawer-dialog.argtypes.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,10 @@ export default {
6969
styles: {
7070
control: {type: undefined},
7171
table: {
72-
type: {summary: "DrawerDialogStyles"},
72+
type: {
73+
summary:
74+
"{root?: StyleType, dialog?: StyleType, panel?: StyleType, content?: StyleType, closeButton?: StyleType}",
75+
},
7376
},
7477
},
7578

__docs__/wonder-blocks-modal/drawer-dialog.stories.tsx

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,16 @@ import DrawerDialogArgTypes from "./drawer-dialog.argtypes";
2323
*
2424
* The component automatically receives alignment, animation, and timing props from `DrawerLauncher`
2525
* via React Context, eliminating the need for manual prop passing in nested components.
26+
*
27+
* ### Custom styling
28+
*
29+
* You can optionally pass in the `styles` prop to override various parts of a DrawerDialog.
30+
*
31+
* - `styles.root` - The outermost container of the dialog itself: alignment styles, box shadow, minWidth, maxWidth, width, height, maxHeight, etc.
32+
* - `styles.dialog` - The actual dialog element with minWidth/minHeight, mostly to override View default styles
33+
* - `styles.panel` - The inner dialog panel, targeting the internal `FlexiblePanel` component
34+
* - `styles.content` - The internal `ModalContent` component, which sets padding
35+
* - `styles.closeButton` - The close button, including absolute positioning
2636
*/
2737
export default {
2838
title: "Packages / Modal / DrawerLauncher / DrawerDialog",
@@ -33,6 +43,55 @@ export default {
3343
// Drawer stories are behavior-based, disable visual regression testing
3444
disableSnapshot: true,
3545
},
46+
docs: {
47+
description: {
48+
component: `\`DrawerDialog\` is the modal content component designed specifically for use with \`DrawerLauncher\`.
49+
It provides a consistent drawer interface with proper animations, positioning, and accessibility features.
50+
51+
**IMPORTANT**: This component should only be used with \`DrawerLauncher\`. Using it with other
52+
modal launchers may result in incorrect animations, positioning, and styling.
53+
54+
The component automatically receives alignment, animation, and timing props from \`DrawerLauncher\`
55+
via React Context, eliminating the need for manual prop passing in nested components.
56+
57+
### Usage
58+
59+
\`\`\`jsx
60+
import {DrawerLauncher} from "@khanacademy/wonder-blocks-modal";
61+
import {DrawerDialog} from "@khanacademy/wonder-blocks-modal";
62+
import {BodyText} from "@khanacademy/wonder-blocks-typography";
63+
64+
<DrawerLauncher
65+
onClose={handleClose}
66+
opened={opened}
67+
animated={animated}
68+
alignment="inlineStart"
69+
modal={({closeModal}) => (
70+
<DrawerDialog
71+
title="Assign Mastery Mission"
72+
styles={{content: {padding: 0}}}
73+
content={
74+
<View>
75+
<BodyText>
76+
Hello, world
77+
</BodyText>
78+
</View>
79+
}
80+
/>
81+
)}
82+
/>
83+
\`\`\`
84+
85+
### Custom styling
86+
You can optionally pass in the \`styles\` prop to override various parts of a DrawerDialog.
87+
88+
- \`styles.root\` - The outermost container of the dialog itself: alignment styles, box shadow, minWidth, maxWidth, width, height, maxHeight, etc.
89+
- \`styles.dialog\` - The actual dialog element with minWidth/minHeight, mostly to override View default styles
90+
- \`styles.panel\` - The inner dialog panel, targeting the internal \`FlexiblePanel\` component
91+
- \`styles.content\` - The internal \`ModalContent\` component, which sets padding
92+
- \`styles.closeButton\` - The close button, including absolute positioning`,
93+
},
94+
},
3695
},
3796
argTypes: DrawerDialogArgTypes,
3897
} satisfies Meta<typeof DrawerDialog>;
@@ -83,6 +142,44 @@ export const Default: StoryComponentType = {
83142
),
84143
};
85144

145+
/**
146+
* Basic drawer dialog with no padding. This allows content to go to the edges.
147+
*/
148+
// TODO (WB-2080): Use media query tokens here and in ModalContent
149+
const small = "@media (max-width: 767px)" as any;
150+
151+
export const WithNoPadding: StoryComponentType = {
152+
render: () => (
153+
<DrawerLauncher
154+
alignment="inlineEnd"
155+
modal={
156+
<DrawerDialog
157+
title="Default Drawer"
158+
styles={{
159+
content: {
160+
padding: 0,
161+
[small]: {
162+
paddingInline: 0,
163+
},
164+
},
165+
}}
166+
content={
167+
<View>
168+
<BodyText>
169+
This is a basic drawer dialog with no padding.
170+
</BodyText>
171+
</View>
172+
}
173+
/>
174+
}
175+
>
176+
{({openModal}) => (
177+
<Button onClick={openModal}>Open Default Drawer</Button>
178+
)}
179+
</DrawerLauncher>
180+
),
181+
};
182+
86183
/**
87184
* Drawer with rich content including form elements and actions.
88185
* Demonstrates how to create more complex drawer interfaces.

__docs__/wonder-blocks-modal/drawer-launcher-testing-snapshots.stories.tsx

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,9 @@ export const InlineEnd: StoryComponentType = {
112112
/**
113113
* InlineEnd drawer
114114
*/
115+
// TODO (WB-2080): Use media query tokens here and in ModalContent
116+
const small = "@media (max-width: 767px)" as any;
117+
115118
export const InlineEndCustomStyle: StoryComponentType = {
116119
render: () => (
117120
<DrawerLauncher
@@ -120,9 +123,15 @@ export const InlineEndCustomStyle: StoryComponentType = {
120123
title="Single-line title"
121124
styles={{
122125
root: {
123-
minWidth: "320px",
126+
minWidth: "320px", // style only applies to desktop
124127
width: "unset",
125128
},
129+
content: {
130+
paddingInline: 0,
131+
[small]: {
132+
paddingInline: 0,
133+
},
134+
},
126135
}}
127136
content={
128137
<View>

__docs__/wonder-blocks-modal/drawer-launcher.stories.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,11 +75,18 @@ export default {
7575
),
7676
docs: {
7777
description: {
78-
component: `A drawer modal launcher intended for the DrawerDialog component. It can align a dialog on the left (inlineStart), right (inlineEnd), or bottom of the screen.
78+
component: `A drawer modal launcher intended for the \`DrawerDialog\` component.
79+
80+
It can align a dialog on the \`inlineStart\` (left), \`inlineEnd\` (right), or \`blockEnd\` (bottom).
7981
8082
- Slide animations can be turned off with the \`animated\` prop.
8183
- Timing of animations can be fine-tuned with the \`timingDuration\` prop, used on enter and exit animations. It is also used to coordinate timing of focus management on open and close.
8284
85+
**IMPORTANT**: This component should only be used with \`DrawerDialog\`. Using it with other
86+
dialog components may result in incorrect animations, positioning, and styling.
87+
88+
See available styling customizations in \`DrawerDialog\` docs.
89+
8390
### Usage
8491
8592
\`\`\`jsx

__docs__/wonder-blocks-modal/flexible-dialog.argtypes.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ export default {
4848
table: {
4949
type: {
5050
summary:
51-
"{root?: StyleType, dialog?: StyleType, panel?: StyleType, closeButton?: StyleType}",
51+
"{root?: StyleType, dialog?: StyleType, panel?: StyleType, content?: StyleType, closeButton?: StyleType}",
5252
},
5353
},
5454
},

__docs__/wonder-blocks-modal/flexible-dialog.stories.tsx

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import {allModes} from "../../.storybook/modes";
1616

1717
import celebrationBg from "../../static/celebration_bg.svg";
1818
import celebrationChest from "../../static/celebration-chest.svg";
19-
import {reallyLongText} from "../components/text-for-testing";
19+
import {longText, reallyLongText} from "../components/text-for-testing";
2020

2121
const customViewports = {
2222
phone: {
@@ -170,6 +170,40 @@ export const WithBackgroundImage: StoryComponentType = {
170170
),
171171
};
172172

173+
/**
174+
*
175+
* A FlexibleDialog can have zeroed out padding.
176+
*/
177+
// TODO (WB-2080): Use media query tokens here and in ModalContent
178+
const small = "@media (max-width: 767px)" as any;
179+
export const WithNoPadding: StoryComponentType = {
180+
render: () => (
181+
<View style={styles.previewSizer}>
182+
<View style={styles.modalPositioner}>
183+
<FlexibleDialog
184+
styles={{
185+
content: {
186+
padding: 0,
187+
maxWidth: "90%",
188+
[small]: {
189+
paddingInline: 0,
190+
},
191+
},
192+
}}
193+
title="Dogz are the best"
194+
content={
195+
<View>
196+
<BodyText>{longText}</BodyText>
197+
<BodyText>{longText}</BodyText>
198+
<BodyText>{longText}</BodyText>
199+
</View>
200+
}
201+
/>
202+
</View>
203+
</View>
204+
),
205+
};
206+
173207
/**
174208
*
175209
* A FlexibleDialog can have a movable title via the

packages/wonder-blocks-modal/src/components/drawer-dialog.tsx

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ export type DrawerDialogStyles = {
7171
root?: StyleType;
7272
dialog?: StyleType;
7373
panel?: StyleType;
74+
content?: StyleType;
7475
closeButton?: StyleType;
7576
};
7677

@@ -112,6 +113,16 @@ type RenderProps = {
112113
* }
113114
* />
114115
* ```
116+
*
117+
* ### Custom styling
118+
*
119+
* You can optionally pass in the `styles` prop to override various parts of a DrawerDialog.
120+
*
121+
* - `styles.root` - The outermost container of the dialog itself: alignment styles, box shadow, minWidth, maxWidth, width, height, maxHeight, etc.
122+
* - `styles.dialog` - The actual dialog element with minWidth/minHeight, mostly to override View default styles
123+
* - `styles.panel` - The inner dialog panel, targeting the internal `FlexiblePanel` component
124+
* - `styles.content` - The internal `ModalContent` component, which sets padding
125+
* - `styles.closeButton` - The close button, including absolute positioning
115126
*/
116127
const DrawerDialog = React.forwardRef(function DrawerDialog(
117128
props: Props,
@@ -156,6 +167,7 @@ const DrawerDialog = React.forwardRef(function DrawerDialog(
156167
Boolean,
157168
),
158169
panel: styles?.panel,
170+
content: styles?.content,
159171
closeButton: styles?.closeButton,
160172
}}
161173
/>

packages/wonder-blocks-modal/src/components/flexible-dialog.tsx

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ export type FlexibleDialogStyles = {
6666
root?: StyleType;
6767
dialog?: StyleType;
6868
panel?: StyleType;
69+
content?: StyleType;
6970
closeButton?: StyleType;
7071
};
7172

@@ -109,6 +110,16 @@ type RenderProps = {
109110
* }
110111
* />
111112
* ```
113+
*
114+
* ### Custom styling
115+
*
116+
* You can optionally pass in the `styles` prop to override various parts of a DrawerDialog.
117+
*
118+
* - `styles.root` - The outermost container of the dialog: box shadow, minWidth, maxWidth, width, height, maxHeight, etc.
119+
* - `styles.dialog` - The actual dialog element with minWidth/minHeight, mostly to override View default styles
120+
* - `styles.panel` - The inner dialog flex panel, targeting the internal `FlexiblePanel` component
121+
* - `styles.content` - The internal `ModalContent` component, which sets padding
122+
* - `styles.closeButton` - The close button, including absolute positioning
112123
*/
113124
const FlexibleDialog = React.forwardRef(function FlexibleDialog(
114125
props: Props,
@@ -155,6 +166,7 @@ const FlexibleDialog = React.forwardRef(function FlexibleDialog(
155166
<FlexiblePanel
156167
styles={{
157168
panel: styles?.panel,
169+
content: styles?.content,
158170
closeButton: styles?.closeButton,
159171
}}
160172
onClose={onClose}

packages/wonder-blocks-modal/src/components/flexible-panel.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ type Props = {
3535
*/
3636
styles?: {
3737
panel?: StyleType;
38+
content?: StyleType;
3839
closeButton?: StyleType;
3940
};
4041
/**
@@ -95,7 +96,7 @@ export default function FlexiblePanel({
9596
const mainContent = ModalContent.isComponentOf(contentNode) ? (
9697
(contentNode as React.ReactElement<PropsFor<typeof ModalContent>>)
9798
) : (
98-
<ModalContent>{contentNode}</ModalContent>
99+
<ModalContent style={styles?.content}>{contentNode}</ModalContent>
99100
);
100101
if (!mainContent) {
101102
return mainContent;
@@ -104,7 +105,7 @@ export default function FlexiblePanel({
104105
return React.cloneElement(mainContent, {
105106
style: [mainContent.props.style],
106107
});
107-
}, [title, content]);
108+
}, [title, content, styles?.content]);
108109

109110
const mainContent = renderMainContent();
110111

0 commit comments

Comments
 (0)