Преглед изворни кода

feat(ui-components) Add Dialog Component (#12260)

factor2
Robert Pintilii пре 3 година
родитељ
комит
bfa88f13dc
No account linked to committer's email address

+ 1
- 1
react/features/app/components/App.web.js Прегледај датотеку

@@ -3,9 +3,9 @@
3 3
 import { AtlasKitThemeProvider } from '@atlaskit/theme';
4 4
 import React from 'react';
5 5
 
6
-import { DialogContainer } from '../../base/dialog';
7 6
 import GlobalStyles from '../../base/ui/components/GlobalStyles';
8 7
 import JitsiThemeProvider from '../../base/ui/components/JitsiThemeProvider.web';
8
+import DialogContainer from '../../base/ui/components/web/DialogContainer';
9 9
 import { ChromeExtensionBanner } from '../../chrome-extension-banner';
10 10
 
11 11
 import { AbstractApp } from './AbstractApp';

react/features/base/dialog/actions.js → react/features/base/dialog/actions.ts Прегледај датотеку

@@ -1,6 +1,6 @@
1
-// @flow
1
+import { ComponentType } from 'react';
2 2
 
3
-import type { Dispatch } from 'redux';
3
+import { IStore } from '../../app/types';
4 4
 
5 5
 import {
6 6
     HIDE_DIALOG,
@@ -22,7 +22,7 @@ import { isDialogOpen } from './functions';
22 22
  *     component: (React.Component | undefined)
23 23
  * }}
24 24
  */
25
-export function hideDialog(component: ?Object) {
25
+export function hideDialog(component?: ComponentType) {
26 26
     return {
27 27
         type: HIDE_DIALOG,
28 28
         component
@@ -54,7 +54,7 @@ export function hideSheet() {
54 54
  *     componentProps: (Object | undefined)
55 55
  * }}
56 56
  */
57
-export function openDialog(component: Object, componentProps: ?Object) {
57
+export function openDialog(component: ComponentType, componentProps?: Object) {
58 58
     return {
59 59
         type: OPEN_DIALOG,
60 60
         component,
@@ -74,7 +74,7 @@ export function openDialog(component: Object, componentProps: ?Object) {
74 74
  *     componentProps: (Object | undefined)
75 75
  * }}
76 76
  */
77
-export function openSheet(component: Object, componentProps: ?Object) {
77
+export function openSheet(component: ComponentType, componentProps?: Object) {
78 78
     return {
79 79
         type: OPEN_SHEET,
80 80
         component,
@@ -92,8 +92,8 @@ export function openSheet(component: Object, componentProps: ?Object) {
92 92
  * specified {@code component}.
93 93
  * @returns {Function}
94 94
  */
95
-export function toggleDialog(component: Object, componentProps: ?Object) {
96
-    return (dispatch: Dispatch<any>, getState: Function) => {
95
+export function toggleDialog(component: ComponentType, componentProps?: Object) {
96
+    return (dispatch: IStore['dispatch'], getState: IStore['getState']) => {
97 97
         if (isDialogOpen(getState, component)) {
98 98
             dispatch(hideDialog(component));
99 99
         } else {

react/features/base/dialog/components/AbstractDialogContainer.js → react/features/base/dialog/components/AbstractDialogContainer.ts Прегледај датотеку

@@ -1,34 +1,33 @@
1
-/* @flow */
1
+import React, { Component, ComponentType } from 'react';
2 2
 
3
-import React, { Component } from 'react';
4
-
5
-import { type ReactionEmojiProps } from '../../../reactions/constants';
3
+import { IState } from '../../../app/types';
4
+import { ReactionEmojiProps } from '../../../reactions/constants';
6 5
 
7 6
 /**
8 7
  * The type of the React {@code Component} props of {@link DialogContainer}.
9 8
  */
10
-type Props = {
9
+interface Props {
11 10
 
12 11
     /**
13 12
      * The component to render.
14 13
      */
15
-    _component: Function,
14
+    _component: ComponentType;
16 15
 
17 16
     /**
18 17
      * The props to pass to the component that will be rendered.
19 18
      */
20
-    _componentProps: Object,
19
+    _componentProps: Object;
21 20
 
22 21
     /**
23
-     * True if the UI is in a compact state where we don't show dialogs.
22
+     * Array of reactions to be displayed.
24 23
      */
25
-    _reducedUI: boolean,
24
+    _reactionsQueue: Array<ReactionEmojiProps>;
26 25
 
27 26
     /**
28
-     * Array of reactions to be displayed.
27
+     * True if the UI is in a compact state where we don't show dialogs.
29 28
      */
30
-    _reactionsQueue: Array<ReactionEmojiProps>
31
-};
29
+    _reducedUI: boolean;
30
+}
32 31
 
33 32
 /**
34 33
  * Implements a DialogContainer responsible for showing all dialogs.
@@ -61,7 +60,7 @@ export default class AbstractDialogContainer extends Component<Props> {
61 60
  * @private
62 61
  * @returns {Props}
63 62
  */
64
-export function abstractMapStateToProps(state: Object): $Shape<Props> {
63
+export function abstractMapStateToProps(state: IState) {
65 64
     const stateFeaturesBaseDialog = state['features/base/dialog'];
66 65
     const { reducedUI } = state['features/base/responsive-ui'];
67 66
 

react/features/base/dialog/components/web/Dialog.js → react/features/base/dialog/components/web/Dialog-old.js Прегледај датотеку


+ 0
- 31
react/features/base/dialog/components/web/DialogContainer.js Прегледај датотеку

@@ -1,31 +0,0 @@
1
-import { ModalTransition } from '@atlaskit/modal-dialog';
2
-import React from 'react';
3
-
4
-import { connect } from '../../../redux';
5
-import AbstractDialogContainer, {
6
-    abstractMapStateToProps
7
-} from '../AbstractDialogContainer';
8
-
9
-/**
10
- * Implements a DialogContainer responsible for showing all dialogs. Necessary
11
- * for supporting @atlaskit's modal animations.
12
- *
13
- * @augments AbstractDialogContainer
14
- */
15
-class DialogContainer extends AbstractDialogContainer {
16
-    /**
17
-     * Implements React's {@link Component#render()}.
18
-     *
19
-     * @inheritdoc
20
-     * @returns {ReactElement}
21
-     */
22
-    render() {
23
-        return (
24
-            <ModalTransition>
25
-                { this._renderDialogContent() }
26
-            </ModalTransition>
27
-        );
28
-    }
29
-}
30
-
31
-export default connect(abstractMapStateToProps)(DialogContainer);

+ 2
- 2
react/features/base/dialog/components/web/index.js Прегледај датотеку

@@ -2,7 +2,7 @@
2 2
 
3 3
 export { default as AbstractDialogTab } from './AbstractDialogTab';
4 4
 export type { Props as AbstractDialogTabProps } from './AbstractDialogTab';
5
-export { default as Dialog } from './Dialog';
6
-export { default as DialogContainer } from './DialogContainer';
5
+export { default as Dialog } from './Dialog-old';
7 6
 export { default as DialogWithTabs } from './DialogWithTabs';
8 7
 export { default as StatelessDialog } from './StatelessDialog';
8
+export { default as DialogContainer } from '../../../ui/components/web/DialogContainer';

react/features/base/dialog/functions.js → react/features/base/dialog/functions.ts Прегледај датотеку

@@ -1,16 +1,20 @@
1
-/* @flow */
1
+import { ComponentType } from 'react';
2 2
 
3
+import { IState } from '../../app/types';
4
+import { IStateful } from '../app/types';
5
+// eslint-disable-next-line lines-around-comment
6
+// @ts-ignore
3 7
 import { ColorSchemeRegistry } from '../color-scheme';
4
-import { toState } from '../redux';
8
+import { toState } from '../redux/functions';
5 9
 
6 10
 /**
7 11
  * Checks if any {@code Dialog} is currently open.
8 12
  *
9
- * @param {Function|Object} stateful - The redux store, the redux
13
+ * @param {IStateful} stateful - The redux store, the redux
10 14
  * {@code getState} function, or the redux state itself.
11 15
  * @returns {boolean}
12 16
  */
13
-export function isAnyDialogOpen(stateful: Function) {
17
+export function isAnyDialogOpen(stateful: IStateful) {
14 18
     return Boolean(toState(stateful)['features/base/dialog'].component);
15 19
 }
16 20
 
@@ -18,25 +22,25 @@ export function isAnyDialogOpen(stateful: Function) {
18 22
  * Checks if a {@code Dialog} with a specific {@code component} is currently
19 23
  * open.
20 24
  *
21
- * @param {Function|Object} stateful - The redux store, the redux
25
+ * @param {IStateful} stateful - The redux store, the redux
22 26
  * {@code getState} function, or the redux state itself.
23 27
  * @param {React.Component} component - The {@code component} of a
24 28
  * {@code Dialog} to be checked.
25 29
  * @returns {boolean}
26 30
  */
27
-export function isDialogOpen(stateful: Function | Object, component: Object) {
31
+export function isDialogOpen(stateful: IStateful, component: ComponentType) {
28 32
     return toState(stateful)['features/base/dialog'].component === component;
29 33
 }
30 34
 
31 35
 /**
32 36
  * Maps part of the Redux state to the props of any Dialog based component.
33 37
  *
34
- * @param {Object} state - The Redux state.
38
+ * @param {IState} state - The Redux state.
35 39
  * @returns {{
36 40
  *     _dialogStyles: StyleType
37 41
  * }}
38 42
  */
39
-export function _abstractMapStateToProps(state: Object): Object {
43
+export function _abstractMapStateToProps(state: IState) {
40 44
     return {
41 45
         _dialogStyles: ColorSchemeRegistry.get(state, 'Dialog')
42 46
     };

+ 5
- 0
react/features/base/ui/components/web/ClickableIcon.tsx Прегледај датотеку

@@ -24,6 +24,11 @@ const useStyles = makeStyles()((theme: Theme) => {
24 24
                 backgroundColor: theme.palette.ui02
25 25
             },
26 26
 
27
+            '&:focus': {
28
+                outline: 0,
29
+                boxShadow: `0px 0px 0px 2px ${theme.palette.focus01}`
30
+            },
31
+
27 32
             '&:active': {
28 33
                 backgroundColor: theme.palette.ui03
29 34
             },

+ 250
- 0
react/features/base/ui/components/web/Dialog.tsx Прегледај датотеку

@@ -0,0 +1,250 @@
1
+import { Theme } from '@mui/material';
2
+import React, { ReactElement, useCallback, useContext, useEffect } from 'react';
3
+import { useTranslation } from 'react-i18next';
4
+import { keyframes } from 'tss-react';
5
+import { makeStyles } from 'tss-react/mui';
6
+
7
+import { IconClose } from '../../../icons/svg';
8
+import { withPixelLineHeight } from '../../../styles/functions.web';
9
+
10
+import Button from './Button';
11
+import ClickableIcon from './ClickableIcon';
12
+import { DialogTransitionContext } from './DialogTransition';
13
+
14
+
15
+const useStyles = makeStyles()((theme: Theme) => {
16
+    return {
17
+        container: {
18
+            width: '100%',
19
+            height: '100%',
20
+            position: 'fixed',
21
+            top: 0,
22
+            left: 0,
23
+            display: 'flex',
24
+            justifyContent: 'center',
25
+            alignItems: 'flex-start',
26
+            animation: `${keyframes`
27
+                0% {
28
+                    opacity: 0.4;
29
+                }
30
+                100% {
31
+                    opacity: 1;
32
+                }
33
+            `} 0.2s forwards ease-out`,
34
+
35
+            '&.unmount': {
36
+                animation: `${keyframes`
37
+                    0% {
38
+                        opacity: 1;
39
+                    }
40
+                    100% {
41
+                        opacity: 0.5;
42
+                    }
43
+                `} 0.15s forwards ease-in`
44
+            }
45
+        },
46
+
47
+        backdrop: {
48
+            position: 'absolute',
49
+            width: '100%',
50
+            height: '100%',
51
+            top: 0,
52
+            left: 0,
53
+            backgroundColor: theme.palette.ui02,
54
+            opacity: 0.75
55
+        },
56
+
57
+        modal: {
58
+            zIndex: 1,
59
+            backgroundColor: theme.palette.ui01,
60
+            border: `1px solid ${theme.palette.ui03}`,
61
+            boxShadow: '0px 4px 25px 4px rgba(20, 20, 20, 0.6)',
62
+            borderRadius: `${theme.shape.borderRadius}px`,
63
+            display: 'flex',
64
+            flexDirection: 'column',
65
+            height: 'auto',
66
+            minHeight: '200px',
67
+            maxHeight: '560px',
68
+            marginTop: '64px',
69
+            animation: `${keyframes`
70
+                0% {
71
+                    margin-top: 85px
72
+                }
73
+                100% {
74
+                    margin-top: 64px
75
+                }
76
+            `} 0.2s forwards ease-out`,
77
+
78
+            '&.medium': {
79
+                width: '400px'
80
+            },
81
+
82
+            '&.large': {
83
+                width: '664px'
84
+            },
85
+
86
+            '&.unmount': {
87
+                animation: `${keyframes`
88
+                    0% {
89
+                        margin-top: 64px
90
+                    }
91
+                    100% {
92
+                        margin-top: 40px
93
+                    }
94
+                `} 0.15s forwards ease-in`
95
+            },
96
+
97
+            '@media (max-width: 448px)': {
98
+                width: '100% !important',
99
+                maxHeight: 'initial',
100
+                height: '100%',
101
+                margin: 0,
102
+                position: 'absolute',
103
+                top: 0,
104
+                left: 0,
105
+                bottom: 0,
106
+                animation: `${keyframes`
107
+                    0% {
108
+                        margin-top: 15px
109
+                    }
110
+                    100% {
111
+                        margin-top: 0
112
+                    }
113
+                `} 0.2s forwards ease-out`,
114
+
115
+                '&.unmount': {
116
+                    animation: `${keyframes`
117
+                        0% {
118
+                            margin-top: 0
119
+                        }
120
+                        100% {
121
+                            margin-top: 15px
122
+                        }
123
+                    `} 0.15s forwards ease-in`
124
+                }
125
+            }
126
+        },
127
+
128
+        header: {
129
+            width: '100%',
130
+            padding: '24px',
131
+            boxSizing: 'border-box',
132
+            display: 'flex',
133
+            alignItems: 'flex-start',
134
+            justifyContent: 'space-between'
135
+        },
136
+
137
+        title: {
138
+            color: theme.palette.text01,
139
+            ...withPixelLineHeight(theme.typography.heading5),
140
+            margin: 0,
141
+            padding: 0
142
+        },
143
+
144
+        content: {
145
+            height: 'auto',
146
+            overflowY: 'auto',
147
+            width: '100%',
148
+            boxSizing: 'border-box',
149
+            padding: '0 24px',
150
+            overflowX: 'hidden',
151
+
152
+            '@media (max-width: 448px)': {
153
+                height: '100%'
154
+            }
155
+        },
156
+
157
+        footer: {
158
+            width: '100%',
159
+            boxSizing: 'border-box',
160
+            display: 'flex',
161
+            alignItems: 'center',
162
+            justifyContent: 'flex-end',
163
+            padding: '24px',
164
+
165
+            '& button:last-child': {
166
+                marginLeft: '16px'
167
+            }
168
+        }
169
+    };
170
+});
171
+
172
+interface DialogProps {
173
+    cancelKey?: string;
174
+    children?: ReactElement | ReactElement[];
175
+    description?: string;
176
+    ok?: {
177
+        disabled?: boolean;
178
+        key: string;
179
+    };
180
+    onCancel: () => void;
181
+    onSubmit?: () => void;
182
+    size?: 'large' | 'medium';
183
+    title?: string;
184
+    titleKey?: string;
185
+}
186
+
187
+const Dialog = ({
188
+    title,
189
+    titleKey,
190
+    description,
191
+    size = 'medium',
192
+    onCancel,
193
+    children,
194
+    ok,
195
+    cancelKey,
196
+    onSubmit
197
+}: DialogProps) => {
198
+    const { classes, cx } = useStyles();
199
+    const { t } = useTranslation();
200
+    const { isUnmounting } = useContext(DialogTransitionContext);
201
+
202
+    const handleKeyDown = useCallback((e: KeyboardEvent) => {
203
+        if (e.key === 'Escape') {
204
+            onCancel();
205
+        }
206
+    }, []);
207
+
208
+    useEffect(() => {
209
+        window.addEventListener('keydown', handleKeyDown);
210
+
211
+        return () => window.removeEventListener('keydown', handleKeyDown);
212
+    }, []);
213
+
214
+    return (
215
+        <div className = { cx(classes.container, isUnmounting && 'unmount') }>
216
+            <div
217
+                className = { classes.backdrop }
218
+                onClick = { onCancel } />
219
+            <div
220
+                aria-describedby = { description }
221
+                aria-labelledby = { title ?? t(titleKey ?? '') }
222
+                aria-modal = { true }
223
+                className = { cx(classes.modal, isUnmounting && 'unmount', size) }
224
+                role = 'dialog'>
225
+                <div className = { classes.header }>
226
+                    <p className = { classes.title }>{title ?? t(titleKey ?? '')}</p>
227
+                    <ClickableIcon
228
+                        accessibilityLabel = { t('dialog.close') }
229
+                        icon = { IconClose }
230
+                        onClick = { onCancel } />
231
+                </div>
232
+                <div className = { classes.content }>{children}</div>
233
+                <div className = { classes.footer }>
234
+                    {cancelKey && <Button
235
+                        accessibilityLabel = { t(cancelKey) }
236
+                        labelKey = { cancelKey }
237
+                        onClick = { onCancel }
238
+                        type = 'tertiary' />}
239
+                    {ok && <Button
240
+                        accessibilityLabel = { t(ok.key) }
241
+                        disabled = { ok.disabled }
242
+                        labelKey = { ok.key }
243
+                        onClick = { onSubmit } />}
244
+                </div>
245
+            </div>
246
+        </div>
247
+    );
248
+};
249
+
250
+export default Dialog;

+ 138
- 0
react/features/base/ui/components/web/DialogContainer.tsx Прегледај датотеку

@@ -0,0 +1,138 @@
1
+import { ModalTransition } from '@atlaskit/modal-dialog';
2
+import React, { Component, ComponentType } from 'react';
3
+
4
+import { IState } from '../../../../app/types';
5
+import KeyboardShortcutsDialog from '../../../../keyboard-shortcuts/components/web/KeyboardShortcutsDialog';
6
+import { ReactionEmojiProps } from '../../../../reactions/constants';
7
+import { connect } from '../../../redux/functions';
8
+
9
+import DialogTransition from './DialogTransition';
10
+
11
+interface Props {
12
+
13
+    /**
14
+     * The component to render.
15
+     */
16
+    _component: ComponentType;
17
+
18
+    /**
19
+     * The props to pass to the component that will be rendered.
20
+     */
21
+    _componentProps: Object;
22
+
23
+    /**
24
+     * Array of reactions to be displayed.
25
+     */
26
+    _reactionsQueue: Array<ReactionEmojiProps>;
27
+
28
+    /**
29
+     * True if the UI is in a compact state where we don't show dialogs.
30
+     */
31
+    _reducedUI: boolean;
32
+}
33
+
34
+// This function is necessary while the transition from @atlaskit dialog to our component is ongoing.
35
+const isNewDialog = (component: any) => {
36
+    const list = [ KeyboardShortcutsDialog ];
37
+
38
+    return Boolean(list.find(comp => comp === component));
39
+};
40
+
41
+// Needed for the transition to our component.
42
+type State = {
43
+    isNewDialog: boolean;
44
+};
45
+
46
+/**
47
+ * Implements a DialogContainer responsible for showing all dialogs. Necessary
48
+ * for supporting @atlaskit's modal animations.
49
+ *
50
+ */
51
+class DialogContainer extends Component<Props, State> {
52
+
53
+    /**
54
+     * Initializes a new {@code DialogContainer} instance.
55
+     *
56
+     * @param {Props} props - The React {@code Component} props to initialize
57
+     * the new {@code DialogContainer} instance with.
58
+     */
59
+    constructor(props: Props) {
60
+        super(props);
61
+        this.state = {
62
+            isNewDialog: false
63
+        };
64
+    }
65
+
66
+    /**
67
+     * Check which Dialog container to render.
68
+     * Needed during transition from atlaskit.
69
+     *
70
+     * @inheritdoc
71
+     * @returns {void}
72
+     */
73
+    componentDidUpdate(prevProps: Props) {
74
+        if (this.props._component && prevProps._component !== this.props._component) {
75
+            // eslint-disable-next-line react/no-did-update-set-state
76
+            this.setState({
77
+                isNewDialog: isNewDialog(this.props._component)
78
+            });
79
+        }
80
+    }
81
+
82
+    /**
83
+     * Returns the dialog to be displayed.
84
+     *
85
+     * @private
86
+     * @returns {ReactElement|null}
87
+     */
88
+    _renderDialogContent() {
89
+        const {
90
+            _component: component,
91
+            _reducedUI: reducedUI
92
+        } = this.props;
93
+
94
+        return (
95
+            component && !reducedUI
96
+                ? React.createElement(component, this.props._componentProps)
97
+                : null);
98
+    }
99
+
100
+    /**
101
+     * Implements React's {@link Component#render()}.
102
+     *
103
+     * @inheritdoc
104
+     * @returns {ReactElement}
105
+     */
106
+    render() {
107
+        return this.state.isNewDialog ? (
108
+            <DialogTransition>
109
+                {this._renderDialogContent()}
110
+            </DialogTransition>
111
+        ) : (
112
+            <ModalTransition>
113
+                { this._renderDialogContent() }
114
+            </ModalTransition>
115
+        );
116
+    }
117
+}
118
+
119
+/**
120
+ * Maps (parts of) the redux state to the associated
121
+ * {@code AbstractDialogContainer}'s props.
122
+ *
123
+ * @param {Object} state - The redux state.
124
+ * @private
125
+ * @returns {Props}
126
+ */
127
+function mapStateToProps(state: IState) {
128
+    const stateFeaturesBaseDialog = state['features/base/dialog'];
129
+    const { reducedUI } = state['features/base/responsive-ui'];
130
+
131
+    return {
132
+        _component: stateFeaturesBaseDialog.component,
133
+        _componentProps: stateFeaturesBaseDialog.componentProps,
134
+        _reducedUI: reducedUI
135
+    };
136
+}
137
+
138
+export default connect(mapStateToProps)(DialogContainer);

+ 28
- 0
react/features/base/ui/components/web/DialogTransition.tsx Прегледај датотеку

@@ -0,0 +1,28 @@
1
+import React, { ReactElement, useEffect, useState } from 'react';
2
+
3
+export const DialogTransitionContext = React.createContext({ isUnmounting: false });
4
+
5
+const DialogTransition = ({ children }: { children: ReactElement | null; }) => {
6
+    const [ childrenToRender, setChildrenToRender ] = useState(children);
7
+    const [ isUnmounting, setIsUnmounting ] = useState(false);
8
+
9
+    useEffect(() => {
10
+        if (children === null) {
11
+            setIsUnmounting(true);
12
+            setTimeout(() => {
13
+                setChildrenToRender(children);
14
+                setIsUnmounting(false);
15
+            }, 150);
16
+        } else {
17
+            setChildrenToRender(children);
18
+        }
19
+    }, [ children ]);
20
+
21
+    return (
22
+        <DialogTransitionContext.Provider value = {{ isUnmounting }}>
23
+            {childrenToRender}
24
+        </DialogTransitionContext.Provider>
25
+    );
26
+};
27
+
28
+export default DialogTransition;

+ 24
- 7
react/features/keyboard-shortcuts/components/web/KeyboardShortcutsDialog.tsx Прегледај датотеку

@@ -3,10 +3,12 @@ import { withStyles } from '@mui/styles';
3 3
 import clsx from 'clsx';
4 4
 import React, { Component } from 'react';
5 5
 import { WithTranslation } from 'react-i18next';
6
+import { connect } from 'react-redux';
6 7
 
7
-// @ts-ignore
8
-import { Dialog } from '../../../base/dialog';
8
+import { IStore } from '../../../app/types';
9
+import { hideDialog } from '../../../base/dialog/actions';
9 10
 import { translate } from '../../../base/i18n/functions';
11
+import Dialog from '../../../base/ui/components/web/Dialog';
10 12
 
11 13
 /**
12 14
  * The type of the React {@code Component} props of
@@ -14,6 +16,11 @@ import { translate } from '../../../base/i18n/functions';
14 16
  */
15 17
 interface Props extends WithTranslation {
16 18
 
19
+    /**
20
+     * Dispatches close dialog.
21
+     */
22
+    _onCloseDialog: () => void;
23
+
17 24
     /**
18 25
      * An object containing the CSS classes.
19 26
      */
@@ -73,10 +80,8 @@ class KeyboardShortcutsDialog extends Component<Props> {
73 80
 
74 81
         return (
75 82
             <Dialog
76
-                cancelKey = { 'dialog.close' }
77
-                submitDisabled = { true }
78
-                titleKey = 'keyboardShortcuts.keyboardShortcuts'
79
-                width = 'small'>
83
+                onCancel = { this.props._onCloseDialog }
84
+                titleKey = 'keyboardShortcuts.keyboardShortcuts'>
80 85
                 <div
81 86
                     id = 'keyboard-shortcuts'>
82 87
                     <ul
@@ -125,4 +130,16 @@ class KeyboardShortcutsDialog extends Component<Props> {
125 130
     }
126 131
 }
127 132
 
128
-export default translate(withStyles(styles)(KeyboardShortcutsDialog));
133
+/**
134
+ * Function that maps parts of Redux actions into component props.
135
+ *
136
+ * @param {Object} dispatch - Redux dispatch.
137
+ * @returns {Object}
138
+ */
139
+function mapDispatchToProps(dispatch: IStore['dispatch']) {
140
+    return {
141
+        _onCloseDialog: () => dispatch(hideDialog())
142
+    };
143
+}
144
+
145
+export default connect(null, mapDispatchToProps)(translate(withStyles(styles)(KeyboardShortcutsDialog)));

Loading…
Откажи
Сачувај