Use enter to submit save modals (#34417)
Now you can use the Enter key to submit the form used e.g. when saving a new dashboard Good for a11y + a time saver Adds Form element, migration of Button onClick to Form's onSubmit Adds functional test for Enter key submission Fix #30831
This commit is contained in:
parent
05b842c471
commit
eceeb9fb35
|
@ -8,81 +8,84 @@ exports[`SavedObjectSaveModal should render matching snapshot 1`] = `
|
|||
maxWidth={true}
|
||||
onClose={[Function]}
|
||||
>
|
||||
<EuiModalHeader>
|
||||
<EuiModalHeaderTitle>
|
||||
<FormattedMessage
|
||||
defaultMessage="Save {objectType}"
|
||||
id="common.ui.savedObjects.saveModal.saveTitle"
|
||||
values={
|
||||
Object {
|
||||
"objectType": "visualization",
|
||||
<form
|
||||
onSubmit={[Function]}
|
||||
>
|
||||
<EuiModalHeader>
|
||||
<EuiModalHeaderTitle>
|
||||
<FormattedMessage
|
||||
defaultMessage="Save {objectType}"
|
||||
id="common.ui.savedObjects.saveModal.saveTitle"
|
||||
values={
|
||||
Object {
|
||||
"objectType": "visualization",
|
||||
}
|
||||
}
|
||||
}
|
||||
/>
|
||||
</EuiModalHeaderTitle>
|
||||
</EuiModalHeader>
|
||||
<EuiModalBody>
|
||||
<EuiForm>
|
||||
<EuiFormRow
|
||||
describedByIds={Array []}
|
||||
fullWidth={false}
|
||||
hasEmptyLabelSpace={false}
|
||||
label={
|
||||
<FormattedMessage
|
||||
defaultMessage="Title"
|
||||
id="common.ui.savedObjects.saveModal.titleLabel"
|
||||
values={Object {}}
|
||||
/>
|
||||
}
|
||||
labelType="label"
|
||||
>
|
||||
<EuiFieldText
|
||||
autoFocus={true}
|
||||
compressed={false}
|
||||
data-test-subj="savedObjectTitle"
|
||||
fullWidth={false}
|
||||
isInvalid={false}
|
||||
isLoading={false}
|
||||
onChange={[Function]}
|
||||
value="Saved Object title"
|
||||
/>
|
||||
</EuiFormRow>
|
||||
</EuiForm>
|
||||
</EuiModalBody>
|
||||
<EuiModalFooter>
|
||||
<EuiButton
|
||||
color="primary"
|
||||
data-test-subj="saveCancelButton"
|
||||
fill={false}
|
||||
iconSide="left"
|
||||
onClick={[Function]}
|
||||
size="m"
|
||||
type="button"
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="Cancel"
|
||||
id="common.ui.savedObjects.saveModal.cancelButtonLabel"
|
||||
values={Object {}}
|
||||
/>
|
||||
</EuiButton>
|
||||
<EuiButton
|
||||
color="primary"
|
||||
data-test-subj="confirmSaveSavedObjectButton"
|
||||
fill={true}
|
||||
iconSide="left"
|
||||
isDisabled={false}
|
||||
isLoading={false}
|
||||
onClick={[Function]}
|
||||
size="m"
|
||||
type="button"
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="Confirm Save"
|
||||
id="common.ui.savedObjects.saveModal.confirmSaveButtonLabel"
|
||||
values={Object {}}
|
||||
/>
|
||||
</EuiButton>
|
||||
</EuiModalFooter>
|
||||
</EuiModalHeaderTitle>
|
||||
</EuiModalHeader>
|
||||
<EuiModalBody>
|
||||
<EuiForm>
|
||||
<EuiFormRow
|
||||
describedByIds={Array []}
|
||||
fullWidth={false}
|
||||
hasEmptyLabelSpace={false}
|
||||
label={
|
||||
<FormattedMessage
|
||||
defaultMessage="Title"
|
||||
id="common.ui.savedObjects.saveModal.titleLabel"
|
||||
values={Object {}}
|
||||
/>
|
||||
}
|
||||
labelType="label"
|
||||
>
|
||||
<EuiFieldText
|
||||
autoFocus={true}
|
||||
compressed={false}
|
||||
data-test-subj="savedObjectTitle"
|
||||
fullWidth={false}
|
||||
isInvalid={false}
|
||||
isLoading={false}
|
||||
onChange={[Function]}
|
||||
value="Saved Object title"
|
||||
/>
|
||||
</EuiFormRow>
|
||||
</EuiForm>
|
||||
</EuiModalBody>
|
||||
<EuiModalFooter>
|
||||
<EuiButton
|
||||
color="primary"
|
||||
data-test-subj="saveCancelButton"
|
||||
fill={false}
|
||||
iconSide="left"
|
||||
onClick={[Function]}
|
||||
size="m"
|
||||
type="button"
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="Cancel"
|
||||
id="common.ui.savedObjects.saveModal.cancelButtonLabel"
|
||||
values={Object {}}
|
||||
/>
|
||||
</EuiButton>
|
||||
<EuiButton
|
||||
color="primary"
|
||||
data-test-subj="confirmSaveSavedObjectButton"
|
||||
fill={true}
|
||||
iconSide="left"
|
||||
isDisabled={false}
|
||||
isLoading={false}
|
||||
size="m"
|
||||
type="submit"
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="Confirm Save"
|
||||
id="common.ui.savedObjects.saveModal.confirmSaveButtonLabel"
|
||||
values={Object {}}
|
||||
/>
|
||||
</EuiButton>
|
||||
</EuiModalFooter>
|
||||
</form>
|
||||
</EuiModal>
|
||||
</EuiOverlayMask>
|
||||
`;
|
||||
|
|
|
@ -63,7 +63,7 @@ export class SavedObjectSaveModal extends React.Component {
|
|||
isTitleDuplicateConfirmed: true,
|
||||
hasTitleDuplicate: true,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
saveSavedObject = async () => {
|
||||
if (this.state.isLoading) {
|
||||
|
@ -95,7 +95,12 @@ export class SavedObjectSaveModal extends React.Component {
|
|||
this.setState({
|
||||
copyOnSave: event.target.checked,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
onFormSubmit = (event) => {
|
||||
event.preventDefault();
|
||||
this.saveSavedObject();
|
||||
};
|
||||
|
||||
renderDuplicateTitleCallout = () => {
|
||||
if (!this.state.hasTitleDuplicate) {
|
||||
|
@ -134,7 +139,7 @@ export class SavedObjectSaveModal extends React.Component {
|
|||
<EuiSpacer />
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
renderCopyOnSave = () => {
|
||||
if (!this.props.showCopyOnSave) {
|
||||
|
@ -156,9 +161,11 @@ export class SavedObjectSaveModal extends React.Component {
|
|||
/>
|
||||
</EuiFormRow>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const { isTitleDuplicateConfirmed, hasTitleDuplicate, title, isLoading } = this.state;
|
||||
|
||||
return (
|
||||
<EuiOverlayMask>
|
||||
<EuiModal
|
||||
|
@ -166,74 +173,76 @@ export class SavedObjectSaveModal extends React.Component {
|
|||
className="dshSaveModal"
|
||||
onClose={this.props.onClose}
|
||||
>
|
||||
<EuiModalHeader>
|
||||
<EuiModalHeaderTitle>
|
||||
<FormattedMessage
|
||||
id="common.ui.savedObjects.saveModal.saveTitle"
|
||||
defaultMessage="Save {objectType}"
|
||||
values={{ objectType: this.props.objectType }}
|
||||
/>
|
||||
</EuiModalHeaderTitle>
|
||||
</EuiModalHeader>
|
||||
|
||||
<EuiModalBody>
|
||||
|
||||
{this.renderDuplicateTitleCallout()}
|
||||
|
||||
<EuiForm>
|
||||
|
||||
{this.renderCopyOnSave()}
|
||||
|
||||
<EuiFormRow
|
||||
label={(<FormattedMessage
|
||||
id="common.ui.savedObjects.saveModal.titleLabel"
|
||||
defaultMessage="Title"
|
||||
/>)}
|
||||
>
|
||||
<EuiFieldText
|
||||
autoFocus
|
||||
data-test-subj="savedObjectTitle"
|
||||
value={this.state.title}
|
||||
onChange={this.onTitleChange}
|
||||
isInvalid={this.state.hasTitleDuplicate || this.state.title.length === 0}
|
||||
<form onSubmit={this.onFormSubmit}>
|
||||
<EuiModalHeader>
|
||||
<EuiModalHeaderTitle>
|
||||
<FormattedMessage
|
||||
id="common.ui.savedObjects.saveModal.saveTitle"
|
||||
defaultMessage="Save {objectType}"
|
||||
values={{ objectType: this.props.objectType }}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
</EuiModalHeaderTitle>
|
||||
</EuiModalHeader>
|
||||
|
||||
{this.props.options}
|
||||
<EuiModalBody>
|
||||
|
||||
</EuiForm>
|
||||
{this.renderDuplicateTitleCallout()}
|
||||
|
||||
</EuiModalBody>
|
||||
<EuiForm>
|
||||
|
||||
<EuiModalFooter>
|
||||
<EuiButton
|
||||
data-test-subj="saveCancelButton"
|
||||
onClick={this.props.onClose}
|
||||
>
|
||||
<FormattedMessage
|
||||
id="common.ui.savedObjects.saveModal.cancelButtonLabel"
|
||||
defaultMessage="Cancel"
|
||||
/>
|
||||
</EuiButton>
|
||||
{this.renderCopyOnSave()}
|
||||
|
||||
<EuiButton
|
||||
fill
|
||||
data-test-subj="confirmSaveSavedObjectButton"
|
||||
onClick={this.saveSavedObject}
|
||||
isLoading={this.state.isLoading}
|
||||
isDisabled={this.state.title.length === 0}
|
||||
>
|
||||
{this.props.confirmButtonLabel
|
||||
? this.props.confirmButtonLabel
|
||||
: (
|
||||
<FormattedMessage
|
||||
id="common.ui.savedObjects.saveModal.confirmSaveButtonLabel"
|
||||
defaultMessage="Confirm Save"
|
||||
<EuiFormRow
|
||||
label={(<FormattedMessage
|
||||
id="common.ui.savedObjects.saveModal.titleLabel"
|
||||
defaultMessage="Title"
|
||||
/>)}
|
||||
>
|
||||
<EuiFieldText
|
||||
autoFocus
|
||||
data-test-subj="savedObjectTitle"
|
||||
value={title}
|
||||
onChange={this.onTitleChange}
|
||||
isInvalid={(!isTitleDuplicateConfirmed && hasTitleDuplicate) || title.length === 0}
|
||||
/>
|
||||
)
|
||||
}
|
||||
</EuiButton>
|
||||
</EuiModalFooter>
|
||||
</EuiFormRow>
|
||||
|
||||
{this.props.options}
|
||||
|
||||
</EuiForm>
|
||||
|
||||
</EuiModalBody>
|
||||
|
||||
<EuiModalFooter>
|
||||
<EuiButton
|
||||
data-test-subj="saveCancelButton"
|
||||
onClick={this.props.onClose}
|
||||
>
|
||||
<FormattedMessage
|
||||
id="common.ui.savedObjects.saveModal.cancelButtonLabel"
|
||||
defaultMessage="Cancel"
|
||||
/>
|
||||
</EuiButton>
|
||||
|
||||
<EuiButton
|
||||
fill
|
||||
data-test-subj="confirmSaveSavedObjectButton"
|
||||
isLoading={isLoading}
|
||||
isDisabled={title.length === 0}
|
||||
type="submit"
|
||||
>
|
||||
{this.props.confirmButtonLabel
|
||||
? this.props.confirmButtonLabel
|
||||
: (
|
||||
<FormattedMessage
|
||||
id="common.ui.savedObjects.saveModal.confirmSaveButtonLabel"
|
||||
defaultMessage="Confirm Save"
|
||||
/>
|
||||
)
|
||||
}
|
||||
</EuiButton>
|
||||
</EuiModalFooter>
|
||||
</form>
|
||||
</EuiModal>
|
||||
</EuiOverlayMask>
|
||||
);
|
||||
|
|
|
@ -24,6 +24,7 @@ export default function ({ getPageObjects }) {
|
|||
|
||||
describe('dashboard save', function describeIndexTests() {
|
||||
const dashboardName = 'Dashboard Save Test';
|
||||
const dashboardNameEnterKey = 'Dashboard Save Test with Enter Key';
|
||||
|
||||
before(async function () {
|
||||
await PageObjects.dashboard.initTests();
|
||||
|
@ -102,5 +103,20 @@ export default function ({ getPageObjects }) {
|
|||
|
||||
await PageObjects.dashboard.cancelSave();
|
||||
});
|
||||
|
||||
it('Saves new Dashboard using the Enter key', async function () {
|
||||
await PageObjects.dashboard.gotoDashboardLandingPage();
|
||||
await PageObjects.dashboard.clickNewDashboard();
|
||||
await PageObjects.dashboard.enterDashboardTitleAndPressEnter(dashboardNameEnterKey);
|
||||
|
||||
// This is important since saving a new dashboard will cause a refresh of the page. We have to
|
||||
// wait till it finishes reloading or it might reload the url after simulating the
|
||||
// dashboard landing page click.
|
||||
await PageObjects.header.waitUntilLoadingHasFinished();
|
||||
|
||||
const countOfDashboards = await PageObjects.dashboard.getDashboardCountWithName(dashboardNameEnterKey);
|
||||
expect(countOfDashboards).to.equal(1);
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
|
|
@ -349,6 +349,11 @@ export function DashboardPageProvider({ getService, getPageObjects }) {
|
|||
await testSubjects.clickWhenNotDisabled('confirmSaveSavedObjectButton');
|
||||
}
|
||||
|
||||
async pressEnterKey() {
|
||||
log.debug('DashboardPage.pressEnterKey');
|
||||
await PageObjects.common.pressEnterKey();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param dashboardTitle {String}
|
||||
|
@ -375,6 +380,20 @@ export function DashboardPageProvider({ getService, getPageObjects }) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param dashboardTitle {String}
|
||||
*/
|
||||
async enterDashboardTitleAndPressEnter(dashboardTitle) {
|
||||
await testSubjects.click('dashboardSaveMenuItem');
|
||||
const modalDialog = await testSubjects.find('savedObjectSaveModal');
|
||||
|
||||
log.debug('entering new title');
|
||||
await testSubjects.setValue('savedObjectTitle', dashboardTitle);
|
||||
|
||||
await this.pressEnterKey();
|
||||
await testSubjects.waitForDeleted(modalDialog);
|
||||
}
|
||||
|
||||
async selectDashboard(dashName) {
|
||||
await testSubjects.click(`dashboardListingTitleLink-${dashName.split(' ').join('-')}`);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue