Skip to content

Commit 17540e5

Browse files
committed
feat(dialog): improve docs and add ConfirmDialog component
1 parent d1dd1d5 commit 17540e5

9 files changed

Lines changed: 219 additions & 24 deletions

File tree

doczrc.js

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,32 @@
11
import { reactNative } from 'docz-plugin-react-native';
2-
import merge from 'webpack-merge';
32

4-
const extensionsResolvers = {
5-
resolve: {
6-
extensions: ['.web.js', '.web.ts', '.web.tsx', '.js', '.ts', '.tsx'],
7-
},
8-
};
3+
const overridingExtensions = [
4+
'.web.js',
5+
'.web.jsx',
6+
'.web.ts',
7+
'.web.tsx',
8+
'.js',
9+
'.jsx',
10+
'.ts',
11+
'.tsx',
12+
'.mdx',
13+
'.json',
14+
'.mjs',
15+
];
916

1017
export default {
1118
title: 'Paramount',
1219
typescript: true,
1320
plugins: [reactNative()],
1421
hashRouter: true,
15-
modifyBundlerConfig: config => {
16-
return merge(config, extensionsResolvers);
22+
modifyBundlerConfig: defaultConfig => {
23+
return {
24+
...defaultConfig,
25+
resolve: {
26+
...defaultConfig.resolve,
27+
extensions: overridingExtensions,
28+
},
29+
};
1730
},
1831
themeConfig: {
1932
showPlaygroundEditor: true,

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@
8181
}
8282
},
8383
"lint-staged": {
84-
"*.{js,jsx,ts,tsx,md,mdx}": [
84+
"*.{js,jsx,ts,tsx}": [
8585
"yarn fix",
8686
"git add"
8787
]

src/components/Button/Button.tsx

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,6 @@ import { LoadingDots } from '../Loading';
1212
import { Text } from '../Typography';
1313

1414
export interface IButtonProps {
15-
/**
16-
* Text of the button
17-
*/
1815
children?: string;
1916

2017
/**
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
import * as React from 'react';
2+
import { FiX } from 'react-icons/fi';
3+
import { TouchableOpacity } from 'react-native';
4+
5+
import { withTheme } from '../../theme';
6+
import { ButtonColor } from '../../theme/component-variables/buttonVariables';
7+
import { Button } from '../Button';
8+
import { GridBox } from '../Layout';
9+
import { Heading } from '../Typography';
10+
import Dialog, { IDialogProps } from './Dialog';
11+
12+
export interface IConfirmDialogProps
13+
extends IDialogProps,
14+
IConfirmDialogHeaderProps,
15+
IConfirmDialogFooterProps {}
16+
17+
export interface IConfirmDialogHeaderProps {
18+
/** Title displayed in the header */
19+
title?: string;
20+
onClose?: () => void;
21+
}
22+
23+
const ConfirmDialogHeader = ({ title, onClose }: IConfirmDialogHeaderProps) => (
24+
<GridBox
25+
padding={2}
26+
flexDirection="row"
27+
justifyContent="space-between"
28+
alignItems="center"
29+
>
30+
<Heading>{title}</Heading>
31+
<TouchableOpacity onPress={onClose}>
32+
<FiX size={24} />
33+
</TouchableOpacity>
34+
</GridBox>
35+
);
36+
37+
export interface IConfirmDialogFooterProps {
38+
color?: ButtonColor;
39+
/** Label for cancel button */
40+
cancelLabel?: string;
41+
/** Label for confirm button */
42+
confirmLabel?: string;
43+
/** Handler for confirm button */
44+
onConfirm?: () => void;
45+
/** Handler for cancel button */
46+
onClose?: () => void;
47+
}
48+
49+
const ConfirmDialogFooter = ({
50+
color = 'primary',
51+
onClose,
52+
onConfirm,
53+
cancelLabel,
54+
confirmLabel,
55+
}: IConfirmDialogFooterProps) => (
56+
<GridBox padding={2} flexDirection="row" justifyContent="flex-end">
57+
<Button appearance="minimal" onPress={onClose}>
58+
{cancelLabel}
59+
</Button>
60+
<GridBox paddingLeft={2}>
61+
<Button color={color} onPress={onConfirm}>
62+
{confirmLabel}
63+
</Button>
64+
</GridBox>
65+
</GridBox>
66+
);
67+
68+
const ConfirmDialogWithoutTheme = (props: IConfirmDialogProps) => {
69+
const {
70+
cancelLabel = 'Cancel',
71+
children,
72+
confirmLabel = 'Confirm',
73+
footer,
74+
header,
75+
onClose,
76+
onConfirm,
77+
title,
78+
...dialogProps
79+
} = props;
80+
81+
return (
82+
<Dialog
83+
header={
84+
header === null
85+
? null
86+
: header || <ConfirmDialogHeader onClose={onClose} title={title} />
87+
}
88+
footer={
89+
footer === null
90+
? null
91+
: footer || (
92+
<ConfirmDialogFooter
93+
onClose={onClose}
94+
onConfirm={onConfirm}
95+
cancelLabel={cancelLabel}
96+
confirmLabel={confirmLabel}
97+
/>
98+
)
99+
}
100+
onClose={onClose}
101+
{...dialogProps}
102+
>
103+
<GridBox padding={2}>{children}</GridBox>
104+
</Dialog>
105+
);
106+
};
107+
108+
export const ConfirmDialog = withTheme(ConfirmDialogWithoutTheme);
109+
export default ConfirmDialog;

src/components/Dialog/Dialog.mdx

Lines changed: 60 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,25 @@ import { Toggle } from 'react-powerplug';
77
import { Playground, PropsTable } from 'docz';
88

99
import { Dialog } from '.';
10+
import { ConfirmDialog } from '.';
1011
import { Button } from '../Button';
1112
import { Box } from '../Layout';
1213
import { Text } from '../Typography';
1314

14-
## Usage
15+
## Dialog
1516

16-
### Go
17+
Dialog has no predefined styling and handlers. The content, header and footer is completely in your control. For a more out-of-box usage, you can use `ConfirmDialog` below
1718

1819
<Playground>
1920
<Toggle initial={false}>
2021
{({ on, toggle }) => (
2122
<Box>
22-
<Dialog isVisible={on} onClose={toggle}>
23+
<Dialog
24+
header={<Text>Header</Text>}
25+
footer={<Text>Footer</Text>}
26+
isVisible={on}
27+
onClose={toggle}
28+
>
2329
<Box height={1800}>
2430
<Text>Put any content in the dialog</Text>
2531
</Box>
@@ -30,6 +36,56 @@ import { Text } from '../Typography';
3036
</Toggle>
3137
</Playground>
3238

33-
## Props
39+
## Dialog Props
3440

3541
<PropsTable of={Dialog} />
42+
43+
## ConfirmDialog
44+
45+
Opposed to `Dialog` (above), `ConfirmDialog` has predefined styling and handlers; however, it still can pass props from `Dialog`
46+
47+
<Playground>
48+
<Toggle initial={false}>
49+
{({ on, toggle }) => (
50+
<Box>
51+
<ConfirmDialog
52+
title="Confirm dialog"
53+
isVisible={on}
54+
onClose={toggle}
55+
cancelLabel="Cancel label"
56+
confirmLabel="Confirm label"
57+
>
58+
<Text>Add custom content here</Text>
59+
</ConfirmDialog>
60+
<Button onPress={toggle}>Open dialog</Button>
61+
</Box>
62+
)}
63+
</Toggle>
64+
</Playground>
65+
66+
## ConfirmDialog without header
67+
68+
Opposed to `Dialog` (above), `ConfirmDialog` has predefined styling and handlers; however, it still can pass props from `Dialog`
69+
70+
<Playground>
71+
<Toggle initial={false}>
72+
{({ on, toggle }) => (
73+
<Box>
74+
<ConfirmDialog
75+
header={null}
76+
isVisible={on}
77+
onClose={toggle}
78+
cancelLabel="Cancel label"
79+
confirmLabel="Confirm label"
80+
>
81+
<Text>Add custom content here</Text>
82+
</ConfirmDialog>
83+
<Button onPress={toggle}>Open dialog</Button>
84+
</Box>
85+
)}
86+
</Toggle>
87+
</Playground>
88+
89+
## ConfirmDialog Props
90+
91+
<PropsTable of={ConfirmDialog} />

src/components/Dialog/Dialog.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,15 @@ import Modal from './Modal';
77
// TODO: Import from react-native when react-native-web implementation is ready
88

99
export interface IDialogProps {
10-
isVisible?: boolean;
1110
children: React.ReactNode;
1211
theme: ITheme;
12+
/** To show dialog or not */
13+
isVisible?: boolean;
1314
/** Called when clicking on overlay or pressing Esc */
1415
onClose?: () => void;
16+
/** In ConfirmDialog, you can pass null to render nothing. If it is undefined, it will use default value */
1517
header?: React.ReactNode;
18+
/** In ConfirmDialog, you can pass null to render nothing. If it is undefined, it will use default value */
1619
footer?: React.ReactNode;
1720
}
1821

@@ -29,7 +32,7 @@ const DialogWithoutTheme = (props: IDialogProps) => {
2932
} = theme.getDialogStyles();
3033

3134
return (
32-
<Modal visible={isVisible} transparent>
35+
<Modal visible={isVisible} transparent onDismiss={onClose}>
3336
<View style={modalContainerStyle}>
3437
<View style={containerStyle}>
3538
{header}

src/components/Dialog/Modal.web.tsx

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,12 @@ import * as React from 'react';
44
import * as ReactDOM from 'react-dom';
55
import { ModalProps } from 'react-native';
66

7+
const ESC_KEY = 27;
8+
79
class Modal extends React.Component<ModalProps> {
810
public el: HTMLDivElement;
911
public modalRoot: HTMLBodyElement;
12+
public content: React.RefObject<HTMLDivElement> = React.createRef();
1013

1114
constructor(props: ModalProps) {
1215
super(props);
@@ -16,12 +19,24 @@ class Modal extends React.Component<ModalProps> {
1619

1720
public componentDidMount() {
1821
this.modalRoot.appendChild(this.el);
22+
if (this.content.current) {
23+
this.content.current.focus();
24+
}
1925
}
2026

2127
public componentWillUnmount() {
2228
this.modalRoot.removeChild(this.el);
2329
}
2430

31+
public handleKeyDown = (event: React.KeyboardEvent<HTMLDivElement>) => {
32+
const { onDismiss } = this.props;
33+
34+
if (event.keyCode === ESC_KEY && onDismiss) {
35+
event.stopPropagation();
36+
onDismiss();
37+
}
38+
};
39+
2540
public render() {
2641
const { transparent, visible } = this.props;
2742

@@ -31,13 +46,17 @@ class Modal extends React.Component<ModalProps> {
3146
<div>
3247
{ReactDOM.createPortal(
3348
<div
49+
ref={this.content}
50+
onKeyDown={this.handleKeyDown}
51+
tabIndex={-1}
3452
style={{
3553
backgroundColor: transparent ? 'transparent' : 'white',
3654
bottom: 0,
3755
left: 0,
3856
position: 'fixed',
3957
right: 0,
4058
top: 0,
59+
zIndex: 1000,
4160
}}
4261
>
4362
{this.props.children}

src/components/Dialog/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
export { default as Dialog } from './Dialog';
2+
export { default as ConfirmDialog } from './ConfirmDialog';

src/theme/component-variables/dialogVariables.ts

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ export interface IDialogVariables {
66
body: ViewStyle;
77
container: ViewStyle;
88
modalContainer: ViewStyle;
9-
overlay: ViewStyle;
9+
overlay: ViewStyle & { cursor: string };
1010
}
1111

1212
export const getDialogVariables = (
@@ -16,15 +16,11 @@ export const getDialogVariables = (
1616
body: {
1717
maxHeight: 400,
1818
overflow: 'scroll',
19-
paddingBottom: 16,
20-
paddingLeft: 16,
21-
paddingRight: 16,
22-
paddingTop: 16,
2319
},
2420
container: {
2521
backgroundColor: 'white',
2622
elevation: 1,
27-
marginTop: 200,
23+
marginTop: 300,
2824
maxWidth: 600,
2925
minWidth: 280,
3026
position: 'relative',
@@ -39,6 +35,7 @@ export const getDialogVariables = (
3935
overlay: {
4036
backgroundColor: 'rgba(0,0,0,0.3)',
4137
bottom: 0,
38+
cursor: 'auto',
4239
height: '100%',
4340
left: 0,
4441
position: 'absolute',

0 commit comments

Comments
 (0)