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.
This commit is contained in:
parent
bf7415e6b5
commit
8e3dfcf0d0
|
@ -7,6 +7,18 @@ import { translate } from '../../i18n';
|
||||||
|
|
||||||
import { DIALOG_PROP_TYPES } from '../constants';
|
import { DIALOG_PROP_TYPES } from '../constants';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The ID to be used for the cancel button if enabled.
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
|
const CANCEL_BUTTON_ID = 'modal-dialog-cancel-button';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The ID to be used for the ok button if enabled.
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
|
const OK_BUTTON_ID = 'modal-dialog-ok-button';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Web dialog that uses atlaskit modal-dialog to display dialogs.
|
* Web dialog that uses atlaskit modal-dialog to display dialogs.
|
||||||
*/
|
*/
|
||||||
|
@ -63,7 +75,34 @@ class StatelessDialog extends Component {
|
||||||
// Bind event handlers so they are only bound once for every instance.
|
// Bind event handlers so they are only bound once for every instance.
|
||||||
this._onCancel = this._onCancel.bind(this);
|
this._onCancel = this._onCancel.bind(this);
|
||||||
this._onDialogDismissed = this._onDialogDismissed.bind(this);
|
this._onDialogDismissed = this._onDialogDismissed.bind(this);
|
||||||
|
this._onKeyDown = this._onKeyDown.bind(this);
|
||||||
this._onSubmit = this._onSubmit.bind(this);
|
this._onSubmit = this._onSubmit.bind(this);
|
||||||
|
this._setDialogElement = this._setDialogElement.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* React Component method that executes once component is mounted.
|
||||||
|
*
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
componentDidMount() {
|
||||||
|
this._updateButtonFocus();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* React Component method that executes once component is updated.
|
||||||
|
*
|
||||||
|
* @param {Object} prevProps - The previous properties, before the update.
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
componentDidUpdate(prevProps) {
|
||||||
|
// if there is an update in any of the buttons enable/disable props
|
||||||
|
// update the focus if needed
|
||||||
|
if (prevProps.okDisabled !== this.props.okDisabled
|
||||||
|
|| prevProps.cancelDisabled !== this.props.cancelDisabled
|
||||||
|
|| prevProps.submitDisabled !== this.props.submitDisabled) {
|
||||||
|
this._updateButtonFocus();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -74,21 +113,25 @@ class StatelessDialog extends Component {
|
||||||
*/
|
*/
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<ModalDialog
|
<div
|
||||||
footer = { this._renderFooter() }
|
onKeyDown = { this._onKeyDown }
|
||||||
header = { this._renderHeader() }
|
ref = { this._setDialogElement }>
|
||||||
isOpen = { true }
|
<ModalDialog
|
||||||
onDialogDismissed = { this._onDialogDismissed }
|
footer = { this._renderFooter() }
|
||||||
width = { this.props.width || 'medium' }>
|
header = { this._renderHeader() }
|
||||||
<div>
|
isOpen = { true }
|
||||||
<form
|
onDialogDismissed = { this._onDialogDismissed }
|
||||||
className = 'modal-dialog-form'
|
width = { this.props.width || 'medium' }>
|
||||||
id = 'modal-dialog-form'
|
<div>
|
||||||
onSubmit = { this._onSubmit }>
|
<form
|
||||||
{ this.props.children }
|
className = 'modal-dialog-form'
|
||||||
</form>
|
id = 'modal-dialog-form'
|
||||||
</div>
|
onSubmit = { this._onSubmit }>
|
||||||
</ModalDialog>
|
{ this.props.children }
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</ModalDialog>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -139,7 +182,7 @@ class StatelessDialog extends Component {
|
||||||
return (
|
return (
|
||||||
<AKButton
|
<AKButton
|
||||||
appearance = 'subtle'
|
appearance = 'subtle'
|
||||||
id = 'modal-dialog-cancel-button'
|
id = { CANCEL_BUTTON_ID }
|
||||||
onClick = { this._onCancel }>
|
onClick = { this._onCancel }>
|
||||||
{ this.props.t(this.props.cancelTitleKey || 'dialog.Cancel') }
|
{ this.props.t(this.props.cancelTitleKey || 'dialog.Cancel') }
|
||||||
</AKButton>
|
</AKButton>
|
||||||
|
@ -196,13 +239,75 @@ class StatelessDialog extends Component {
|
||||||
<AKButton
|
<AKButton
|
||||||
appearance = 'primary'
|
appearance = 'primary'
|
||||||
form = 'modal-dialog-form'
|
form = 'modal-dialog-form'
|
||||||
id = 'modal-dialog-ok-button'
|
id = { OK_BUTTON_ID }
|
||||||
isDisabled = { this.props.okDisabled }
|
isDisabled = { this.props.okDisabled }
|
||||||
onClick = { this._onSubmit }>
|
onClick = { this._onSubmit }>
|
||||||
{ this.props.t(this.props.okTitleKey || 'dialog.Ok') }
|
{ this.props.t(this.props.okTitleKey || 'dialog.Ok') }
|
||||||
</AKButton>
|
</AKButton>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the instance variable for the div containing the component's dialog
|
||||||
|
* element so it can be accessed directly.
|
||||||
|
*
|
||||||
|
* @param {Object} element - The DOM element for the component's dialog.
|
||||||
|
* @private
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
_setDialogElement(element) {
|
||||||
|
this._dialogElement = element;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles 'Enter' key in the dialog to submit/hide dialog depending on
|
||||||
|
* the available buttons and their disabled state.
|
||||||
|
*
|
||||||
|
* @param {Object} event - the key event.
|
||||||
|
* @private
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
_onKeyDown(event) {
|
||||||
|
if (event.key === 'Enter') {
|
||||||
|
if (this.props.submitDisabled && !this.props.cancelDisabled) {
|
||||||
|
this._onCancel();
|
||||||
|
} else if (!this.props.okDisabled) {
|
||||||
|
this._onSubmit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates focused button, if we have a reference to the dialog element.
|
||||||
|
* Focus on available button if there is no focus already.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
_updateButtonFocus() {
|
||||||
|
if (this._dialogElement) {
|
||||||
|
|
||||||
|
// if we have a focused element inside the dialog, skip changing
|
||||||
|
// the focus
|
||||||
|
if (this._dialogElement.contains(document.activeElement)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let buttonToFocus;
|
||||||
|
|
||||||
|
if (this.props.submitDisabled) {
|
||||||
|
buttonToFocus = this._dialogElement
|
||||||
|
.querySelector(`[id=${CANCEL_BUTTON_ID}]`);
|
||||||
|
} else if (!this.props.okDisabled) {
|
||||||
|
buttonToFocus = this._dialogElement
|
||||||
|
.querySelector(`[id=${OK_BUTTON_ID}]`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buttonToFocus) {
|
||||||
|
buttonToFocus.focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default translate(StatelessDialog);
|
export default translate(StatelessDialog);
|
||||||
|
|
Loading…
Reference in New Issue