[Data Views] Show loading indicators when submitting a data view form to prevent multiple requests (#150576)

Closes https://github.com/elastic/kibana/issues/146125

This PR adds loading indicators to the form buttons and also disables
them when submitting the form.

* Creating an ad-hoc data view
![Feb-08-2023
16-00-20](https://user-images.githubusercontent.com/1415710/217567470-53e0c614-d12d-4849-90e1-f157c6c36e79.gif)

* Editing an ad-hoc data view
![Feb-08-2023
16-01-38](https://user-images.githubusercontent.com/1415710/217567599-0066393c-4bae-4364-a568-b54c6509d8ad.gif)

* Creating a persisted data view
![Feb-08-2023
16-00-57](https://user-images.githubusercontent.com/1415710/217567411-67dd1b3c-99b9-455b-adbe-be21d61344ee.gif)

* Editing a persisted data view
![Feb-08-2023
16-02-08](https://user-images.githubusercontent.com/1415710/217567541-a6314611-544f-45f2-944d-f49c7a654a43.gif)
This commit is contained in:
Julia Rechkunova 2023-02-16 09:50:02 +01:00 committed by GitHub
parent f399dd094e
commit a6073e8458
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 83 additions and 30 deletions

View file

@ -15,31 +15,39 @@ interface EditDataViewDeps {
onEdit: () => void;
}
export const editDataViewModal = ({ dataViewName, overlays, onEdit }: EditDataViewDeps) =>
overlays &&
export const editDataViewModal = ({
dataViewName,
overlays,
onEdit,
}: EditDataViewDeps): Promise<void> =>
overlays
.openConfirm(
i18n.translate('indexPatternEditor.editDataView.editConfirmationModal.modalDescription', {
defaultMessage: 'Changing this data view can break other objects that depend on it.',
}),
{
confirmButtonText: i18n.translate(
'indexPatternEditor.editDataView.editConfirmationModal.confirmButton',
? overlays
.openConfirm(
i18n.translate('indexPatternEditor.editDataView.editConfirmationModal.modalDescription', {
defaultMessage: 'Changing this data view can break other objects that depend on it.',
}),
{
defaultMessage: 'Confirm',
confirmButtonText: i18n.translate(
'indexPatternEditor.editDataView.editConfirmationModal.confirmButton',
{
defaultMessage: 'Confirm',
}
),
title: i18n.translate(
'indexPatternEditor.editDataView.editConfirmationModal.editHeader',
{
defaultMessage: `Edit '{name}'`,
values: {
name: dataViewName,
},
}
),
buttonColor: 'danger',
}
),
title: i18n.translate('indexPatternEditor.editDataView.editConfirmationModal.editHeader', {
defaultMessage: `Edit '{name}'`,
values: {
name: dataViewName,
},
}),
buttonColor: 'danger',
}
)
.then(async (isConfirmed) => {
if (isConfirmed) {
onEdit();
}
});
)
.then(async (isConfirmed) => {
if (isConfirmed) {
await onEdit();
}
})
: Promise.resolve();

View file

@ -48,6 +48,7 @@ import {
NameField,
schema,
Footer,
SubmittingType,
AdvancedParamsContent,
PreviewPanel,
RollupBetaWarning,
@ -136,7 +137,7 @@ const IndexPatternEditorFlyoutContentComponent = ({
}
if (editData && editData.getIndexPattern() !== formData.title) {
editDataViewModal({
await editDataViewModal({
dataViewName: formData.name || formData.title,
overlays,
onEdit: async () => {
@ -301,7 +302,14 @@ const IndexPatternEditorFlyoutContentComponent = ({
form.setFieldValue('isAdHoc', adhoc || false);
form.submit();
}}
submitDisabled={form.isSubmitted && !form.isValid}
submitDisabled={(form.isSubmitted && !form.isValid) || form.isSubmitting}
submittingType={
form.isSubmitting
? form.getFormData().isAdHoc
? SubmittingType.savingAsAdHoc
: SubmittingType.persisting
: undefined
}
isEdit={!!editData}
isPersisted={Boolean(editData && editData.isPersisted())}
allowAdHoc={allowAdHoc}

View file

@ -17,9 +17,15 @@ import {
EuiButton,
} from '@elastic/eui';
export enum SubmittingType {
savingAsAdHoc = 'savingAsAdHoc',
persisting = 'persisting',
}
interface FooterProps {
onCancel: () => void;
onSubmit: (isAdHoc?: boolean) => void;
submittingType: SubmittingType | undefined;
submitDisabled: boolean;
isEdit: boolean;
isPersisted: boolean;
@ -53,12 +59,14 @@ const exploreButtonLabel = i18n.translate('indexPatternEditor.editor.flyoutExplo
export const Footer = ({
onCancel,
onSubmit,
submittingType,
submitDisabled,
isEdit,
allowAdHoc,
isPersisted,
canSave,
}: FooterProps) => {
const isEditingAdHoc = isEdit && !isPersisted;
const submitPersisted = () => {
onSubmit(false);
};
@ -89,6 +97,7 @@ export const Footer = ({
onClick={submitAdHoc}
data-test-subj="exploreIndexPatternButton"
disabled={submitDisabled}
isLoading={submittingType === SubmittingType.savingAsAdHoc}
title={i18n.translate('indexPatternEditor.editor.flyoutExploreButtonTitle', {
defaultMessage: 'Use this data view without creating a saved object',
})}
@ -98,7 +107,7 @@ export const Footer = ({
</EuiFlexItem>
)}
{(canSave || (isEdit && !isPersisted)) && (
{(canSave || isEditingAdHoc) && (
<EuiFlexItem grow={false}>
<EuiButton
color="primary"
@ -106,6 +115,10 @@ export const Footer = ({
data-test-subj="saveIndexPatternButton"
fill
disabled={submitDisabled}
isLoading={
submittingType === SubmittingType.persisting ||
(submittingType === SubmittingType.savingAsAdHoc && isEditingAdHoc)
}
>
{isEdit
? isPersisted

View file

@ -6,4 +6,4 @@
* Side Public License, v 1.
*/
export { Footer } from './footer';
export { Footer, SubmittingType } from './footer';

View file

@ -15,6 +15,6 @@ export { schema } from './form_schema';
export { NameField, TimestampField, TypeField, TitleField } from './form_fields';
export { PreviewPanel } from './preview_panel';
export { LoadingIndices } from './loading_indices';
export { Footer } from './footer';
export { Footer, SubmittingType } from './footer';
export { AdvancedParamsContent } from './advanced_params_content';
export { RollupBetaWarning } from './rollup_beta_warning';

View file

@ -186,6 +186,30 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
expect(await testSubjects.exists('field-name-agent')).to.be(true);
});
});
it('should disable Save button after pressing', async function () {
await PageObjects.settings.clickEditIndexButton();
await PageObjects.header.waitUntilLoadingHasFinished();
await retry.try(async () => {
await PageObjects.settings.setIndexPatternField('logs*');
});
await PageObjects.settings.selectTimeFieldOption('@timestamp');
expect(await testSubjects.isEnabled('saveIndexPatternButton')).to.be(true);
await (await PageObjects.settings.getSaveDataViewButtonActive()).click();
// wait for the confirmation modal to open
await retry.waitFor('confirmation modal', async () => {
return await testSubjects.exists('confirmModalConfirmButton');
});
// while the confirmation modal is open, we can check that the form button has actually become disabled
expect(await testSubjects.isEnabled('saveIndexPatternButton')).to.be(false);
await testSubjects.click('confirmModalConfirmButton');
await PageObjects.header.waitUntilLoadingHasFinished();
});
});
describe('index pattern deletion', function indexDelete() {