Browse Source

Handles Enter key to submit dialogs.

If there is no focused node inside the dialog we focus any of the available buttons, submit button is first.
master
damencho 8 years ago
parent
commit
8e3dfcf0d0
1 changed files with 122 additions and 17 deletions
  1. 122
    17
      react/features/base/dialog/components/StatelessDialog.web.js

+ 122
- 17
react/features/base/dialog/components/StatelessDialog.web.js View File

7
 
7
 
8
 import { DIALOG_PROP_TYPES } from '../constants';
8
 import { DIALOG_PROP_TYPES } from '../constants';
9
 
9
 
10
+/**
11
+ * The ID to be used for the cancel button if enabled.
12
+ * @type {string}
13
+ */
14
+const CANCEL_BUTTON_ID = 'modal-dialog-cancel-button';
15
+
16
+/**
17
+ * The ID to be used for the ok button if enabled.
18
+ * @type {string}
19
+ */
20
+const OK_BUTTON_ID = 'modal-dialog-ok-button';
21
+
10
 /**
22
 /**
11
  * Web dialog that uses atlaskit modal-dialog to display dialogs.
23
  * Web dialog that uses atlaskit modal-dialog to display dialogs.
12
  */
24
  */
63
         // Bind event handlers so they are only bound once for every instance.
75
         // Bind event handlers so they are only bound once for every instance.
64
         this._onCancel = this._onCancel.bind(this);
76
         this._onCancel = this._onCancel.bind(this);
65
         this._onDialogDismissed = this._onDialogDismissed.bind(this);
77
         this._onDialogDismissed = this._onDialogDismissed.bind(this);
78
+        this._onKeyDown = this._onKeyDown.bind(this);
66
         this._onSubmit = this._onSubmit.bind(this);
79
         this._onSubmit = this._onSubmit.bind(this);
80
+        this._setDialogElement = this._setDialogElement.bind(this);
81
+    }
82
+
83
+    /**
84
+     * React Component method that executes once component is mounted.
85
+     *
86
+     * @inheritdoc
87
+     */
88
+    componentDidMount() {
89
+        this._updateButtonFocus();
90
+    }
91
+
92
+    /**
93
+     * React Component method that executes once component is updated.
94
+     *
95
+     * @param {Object} prevProps - The previous properties, before the update.
96
+     * @returns {void}
97
+     */
98
+    componentDidUpdate(prevProps) {
99
+        // if there is an update in any of the buttons enable/disable props
100
+        // update the focus if needed
101
+        if (prevProps.okDisabled !== this.props.okDisabled
102
+            || prevProps.cancelDisabled !== this.props.cancelDisabled
103
+            || prevProps.submitDisabled !== this.props.submitDisabled) {
104
+            this._updateButtonFocus();
105
+        }
67
     }
106
     }
68
 
107
 
69
     /**
108
     /**
74
      */
113
      */
75
     render() {
114
     render() {
76
         return (
115
         return (
77
-            <ModalDialog
78
-                footer = { this._renderFooter() }
79
-                header = { this._renderHeader() }
80
-                isOpen = { true }
81
-                onDialogDismissed = { this._onDialogDismissed }
82
-                width = { this.props.width || 'medium' }>
83
-                <div>
84
-                    <form
85
-                        className = 'modal-dialog-form'
86
-                        id = 'modal-dialog-form'
87
-                        onSubmit = { this._onSubmit }>
88
-                        { this.props.children }
89
-                    </form>
90
-                </div>
91
-            </ModalDialog>
116
+            <div
117
+                onKeyDown = { this._onKeyDown }
118
+                ref = { this._setDialogElement }>
119
+                <ModalDialog
120
+                    footer = { this._renderFooter() }
121
+                    header = { this._renderHeader() }
122
+                    isOpen = { true }
123
+                    onDialogDismissed = { this._onDialogDismissed }
124
+                    width = { this.props.width || 'medium' }>
125
+                    <div>
126
+                        <form
127
+                            className = 'modal-dialog-form'
128
+                            id = 'modal-dialog-form'
129
+                            onSubmit = { this._onSubmit }>
130
+                            { this.props.children }
131
+                        </form>
132
+                    </div>
133
+                </ModalDialog>
134
+            </div>
92
         );
135
         );
93
     }
136
     }
94
 
137
 
139
         return (
182
         return (
140
             <AKButton
183
             <AKButton
141
                 appearance = 'subtle'
184
                 appearance = 'subtle'
142
-                id = 'modal-dialog-cancel-button'
185
+                id = { CANCEL_BUTTON_ID }
143
                 onClick = { this._onCancel }>
186
                 onClick = { this._onCancel }>
144
                 { this.props.t(this.props.cancelTitleKey || 'dialog.Cancel') }
187
                 { this.props.t(this.props.cancelTitleKey || 'dialog.Cancel') }
145
             </AKButton>
188
             </AKButton>
196
             <AKButton
239
             <AKButton
197
                 appearance = 'primary'
240
                 appearance = 'primary'
198
                 form = 'modal-dialog-form'
241
                 form = 'modal-dialog-form'
199
-                id = 'modal-dialog-ok-button'
242
+                id = { OK_BUTTON_ID }
200
                 isDisabled = { this.props.okDisabled }
243
                 isDisabled = { this.props.okDisabled }
201
                 onClick = { this._onSubmit }>
244
                 onClick = { this._onSubmit }>
202
                 { this.props.t(this.props.okTitleKey || 'dialog.Ok') }
245
                 { this.props.t(this.props.okTitleKey || 'dialog.Ok') }
203
             </AKButton>
246
             </AKButton>
204
         );
247
         );
205
     }
248
     }
249
+
250
+    /**
251
+     * Sets the instance variable for the div containing the component's dialog
252
+     * element so it can be accessed directly.
253
+     *
254
+     * @param {Object} element - The DOM element for the component's dialog.
255
+     * @private
256
+     * @returns {void}
257
+     */
258
+    _setDialogElement(element) {
259
+        this._dialogElement = element;
260
+    }
261
+
262
+    /**
263
+     * Handles 'Enter' key in the dialog to submit/hide dialog depending on
264
+     * the available buttons and their disabled state.
265
+     *
266
+     * @param {Object} event - the key event.
267
+     * @private
268
+     * @returns {void}
269
+     */
270
+    _onKeyDown(event) {
271
+        if (event.key === 'Enter') {
272
+            if (this.props.submitDisabled && !this.props.cancelDisabled) {
273
+                this._onCancel();
274
+            } else if (!this.props.okDisabled) {
275
+                this._onSubmit();
276
+            }
277
+        }
278
+    }
279
+
280
+    /**
281
+     * Updates focused button, if we have a reference to the dialog element.
282
+     * Focus on available button if there is no focus already.
283
+     *
284
+     * @private
285
+     * @returns {void}
286
+     */
287
+    _updateButtonFocus() {
288
+        if (this._dialogElement) {
289
+
290
+            // if we have a focused element inside the dialog, skip changing
291
+            // the focus
292
+            if (this._dialogElement.contains(document.activeElement)) {
293
+                return;
294
+            }
295
+
296
+            let buttonToFocus;
297
+
298
+            if (this.props.submitDisabled) {
299
+                buttonToFocus = this._dialogElement
300
+                    .querySelector(`[id=${CANCEL_BUTTON_ID}]`);
301
+            } else if (!this.props.okDisabled) {
302
+                buttonToFocus = this._dialogElement
303
+                    .querySelector(`[id=${OK_BUTTON_ID}]`);
304
+            }
305
+
306
+            if (buttonToFocus) {
307
+                buttonToFocus.focus();
308
+            }
309
+        }
310
+    }
206
 }
311
 }
207
 
312
 
208
 export default translate(StatelessDialog);
313
 export default translate(StatelessDialog);

Loading…
Cancel
Save