Skip to content

Commit 0b2b8f2

Browse files
committed
add CustomModal
1 parent 2e8c334 commit 0b2b8f2

File tree

3 files changed

+77
-45
lines changed

3 files changed

+77
-45
lines changed

Diff for: packages/bento-design-system/src/Modal/createModal.tsx

+57-43
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@ type Props = {
2525
size?: "small" | "medium" | "large";
2626
};
2727

28+
type CustomModalProps = Pick<Props, "children" | "isDestructive" | "size"> & {
29+
["aria-label"]: string;
30+
};
31+
2832
export function createModal(
2933
config: ModalConfig,
3034
{
@@ -35,7 +39,7 @@ export function createModal(
3539
IconButton: FunctionComponent<IconButtonProps>;
3640
}
3741
) {
38-
return function Modal(props: Props) {
42+
function CustomModal(props: CustomModalProps) {
3943
const ref = useRef<HTMLDivElement>(null);
4044
const { overlayProps, underlayProps } = useOverlay({ ...props, isOpen: true }, ref);
4145

@@ -45,20 +49,12 @@ export function createModal(
4549

4650
const { dialogProps } = useDialog(
4751
{
48-
"aria-label": props.title,
52+
"aria-label": props["aria-label"],
4953
role: props.isDestructive ? "alertdialog" : "dialog",
5054
},
5155
ref
5256
);
5357

54-
// Trigger the primary action on 'Enter' if there is one
55-
useKeyPressEvent(
56-
(key) => key.code === "Enter",
57-
() => props.primaryAction?.onPress()
58-
);
59-
60-
const { defaultMessages } = useDefaultMessages();
61-
6258
return createPortal(
6359
<Box className={underlay} {...underlayProps} color={undefined}>
6460
<ModalContext.Provider value={true}>
@@ -71,44 +67,62 @@ export function createModal(
7167
color={undefined}
7268
borderRadius={config.radius}
7369
>
74-
<Inset space={config.padding}>
75-
<Columns space={16} alignY="top">
76-
<Title size={config.titleSize}>{props.title}</Title>
77-
<Column width="content">
78-
<IconButton
79-
icon={config.closeIcon}
80-
label={props.closeButtonLabel ?? defaultMessages.Modal.closeButtonLabel}
81-
onPress={props.onClose}
82-
size={config.closeIconSize}
83-
tabIndex={-1}
84-
kind="transparent"
85-
hierarchy="secondary"
86-
/>
87-
</Column>
88-
</Columns>
89-
</Inset>
90-
<Box className={modalBody} paddingX={config.padding}>
91-
{props.children}
92-
</Box>
93-
<Inset space={config.padding}>
94-
<Actions
95-
primaryAction={
96-
props.primaryAction
97-
? { ...props.primaryAction, isDestructive: props.isDestructive }
98-
: undefined
99-
}
100-
secondaryAction={props.secondaryAction}
101-
size="large"
102-
loadingMessage={props.loadingMessage}
103-
error={props.error}
104-
/>
105-
</Inset>
70+
{props.children}
10671
</Box>
10772
</FocusScope>
10873
</ModalContext.Provider>
10974
</Box>
11075
);
111-
};
76+
}
77+
78+
function Modal(props: Props) {
79+
// Trigger the primary action on 'Enter' if there is one
80+
useKeyPressEvent(
81+
(key) => key.code === "Enter",
82+
() => props.primaryAction?.onPress()
83+
);
84+
85+
const { defaultMessages } = useDefaultMessages();
86+
87+
return (
88+
<CustomModal {...props} aria-label={props.title}>
89+
<Inset space={config.padding}>
90+
<Columns space={16} alignY="top">
91+
<Title size={config.titleSize}>{props.title}</Title>
92+
<Column width="content">
93+
<IconButton
94+
icon={config.closeIcon}
95+
label={props.closeButtonLabel ?? defaultMessages.Modal.closeButtonLabel}
96+
onPress={props.onClose}
97+
size={config.closeIconSize}
98+
tabIndex={-1}
99+
kind="transparent"
100+
hierarchy="secondary"
101+
/>
102+
</Column>
103+
</Columns>
104+
</Inset>
105+
<Box className={modalBody} paddingX={config.padding}>
106+
{props.children}
107+
</Box>
108+
<Inset space={config.padding}>
109+
<Actions
110+
primaryAction={
111+
props.primaryAction
112+
? { ...props.primaryAction, isDestructive: props.isDestructive }
113+
: undefined
114+
}
115+
secondaryAction={props.secondaryAction}
116+
size="large"
117+
loadingMessage={props.loadingMessage}
118+
error={props.error}
119+
/>
120+
</Inset>
121+
</CustomModal>
122+
);
123+
}
124+
125+
return { CustomModal, Modal };
112126
}
113127

114128
export type { Props as ModalProps };

Diff for: packages/storybook/stories/Components/Modal.stories.tsx

+19-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Modal, Body, Placeholder, Stack } from "../";
1+
import { Modal, Body, Placeholder, Stack, CustomModal, Feedback, Inset } from "../";
22
import { createComponentStories, formatMessage, textArgType } from "../util";
33
import { action } from "@storybook/addon-actions";
44
import { screen } from "@storybook/testing-library";
@@ -132,3 +132,21 @@ export const Large = createStory({
132132
onPress: action("Cancel"),
133133
},
134134
});
135+
136+
export const Custom = () => {
137+
return (
138+
<CustomModal aria-label="Custom modal" isDestructive size="medium">
139+
<Inset space={24}>
140+
<Stack space={0} align="center">
141+
<Feedback
142+
size="medium"
143+
status="negative"
144+
title={formatMessage("Something went wrong")}
145+
description={formatMessage("Wait a few minutes and retry")}
146+
action={{ label: formatMessage("retry"), onPress: action("onPress") }}
147+
/>
148+
</Stack>
149+
</Inset>
150+
</CustomModal>
151+
);
152+
};

Diff for: packages/storybook/stories/index.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ export const BentoProvider = createBentoProvider(ToastProvider);
8080
export const Card = createCard<24 | 32 | 40>(defaultConfigs.card);
8181
export const Link = createLink(defaultConfigs.link);
8282
export const Breadcrumb = createBreadcrumb(defaultConfigs.breadcrumb, { Link });
83-
export const Modal = createModal(defaultConfigs.modal, { Actions, IconButton });
83+
export const { Modal, CustomModal } = createModal(defaultConfigs.modal, { Actions, IconButton });
8484
export const Chip = createChip(
8585
{
8686
...defaultConfigs.chip,

0 commit comments

Comments
 (0)