Fix UI glitch on SOM delete confirmation modal (#87623)
* extract delete confirm modal * extract the export modal * add data-test-subj to confirm modal * add comment on why we can't use EuiConfirmModal
This commit is contained in:
parent
66c8b2a014
commit
a0d33dc3a9
|
@ -1,200 +1,62 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`SavedObjectsTable delete should show a confirm modal 1`] = `
|
||||
<EuiConfirmModal
|
||||
buttonColor="danger"
|
||||
cancelButtonText={
|
||||
<FormattedMessage
|
||||
defaultMessage="Cancel"
|
||||
id="savedObjectsManagement.objectsTable.deleteSavedObjectsConfirmModal.cancelButtonLabel"
|
||||
values={Object {}}
|
||||
/>
|
||||
}
|
||||
confirmButtonText={
|
||||
<FormattedMessage
|
||||
defaultMessage="Delete"
|
||||
id="savedObjectsManagement.objectsTable.deleteSavedObjectsConfirmModal.deleteButtonLabel"
|
||||
values={Object {}}
|
||||
/>
|
||||
}
|
||||
defaultFocusedButton="confirm"
|
||||
<DeleteConfirmModal
|
||||
isDeleting={false}
|
||||
onCancel={[Function]}
|
||||
onConfirm={[Function]}
|
||||
title={
|
||||
<FormattedMessage
|
||||
defaultMessage="Delete saved objects"
|
||||
id="savedObjectsManagement.objectsTable.deleteSavedObjectsConfirmModalTitle"
|
||||
values={Object {}}
|
||||
/>
|
||||
selectedObjects={
|
||||
Array [
|
||||
Object {
|
||||
"id": "1",
|
||||
"type": "index-pattern",
|
||||
},
|
||||
Object {
|
||||
"id": "3",
|
||||
"type": "dashboard",
|
||||
},
|
||||
]
|
||||
}
|
||||
>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
defaultMessage="This action will delete the following saved objects:"
|
||||
id="savedObjectsManagement.deleteSavedObjectsConfirmModalDescription"
|
||||
values={Object {}}
|
||||
/>
|
||||
</p>
|
||||
<EuiInMemoryTable
|
||||
columns={
|
||||
Array [
|
||||
Object {
|
||||
"field": "type",
|
||||
"name": "Type",
|
||||
"render": [Function],
|
||||
"width": "50px",
|
||||
},
|
||||
Object {
|
||||
"field": "id",
|
||||
"name": "Id",
|
||||
},
|
||||
Object {
|
||||
"field": "meta.title",
|
||||
"name": "Title",
|
||||
},
|
||||
]
|
||||
}
|
||||
items={
|
||||
Array [
|
||||
Object {
|
||||
"id": "1",
|
||||
"type": "index-pattern",
|
||||
},
|
||||
Object {
|
||||
"id": "3",
|
||||
"type": "dashboard",
|
||||
},
|
||||
]
|
||||
}
|
||||
pagination={true}
|
||||
responsive={true}
|
||||
sorting={false}
|
||||
tableLayout="fixed"
|
||||
/>
|
||||
</EuiConfirmModal>
|
||||
/>
|
||||
`;
|
||||
|
||||
exports[`SavedObjectsTable export should allow the user to choose when exporting all 1`] = `
|
||||
<EuiModal
|
||||
onClose={[Function]}
|
||||
>
|
||||
<EuiModalHeader>
|
||||
<EuiModalHeaderTitle>
|
||||
<FormattedMessage
|
||||
defaultMessage="Export {filteredItemCount, plural, one{# object} other {# objects}}"
|
||||
id="savedObjectsManagement.objectsTable.exportObjectsConfirmModalTitle"
|
||||
values={
|
||||
Object {
|
||||
"filteredItemCount": 4,
|
||||
}
|
||||
}
|
||||
/>
|
||||
</EuiModalHeaderTitle>
|
||||
</EuiModalHeader>
|
||||
<EuiModalBody>
|
||||
<EuiFormRow
|
||||
describedByIds={Array []}
|
||||
display="row"
|
||||
fullWidth={false}
|
||||
hasChildLabel={true}
|
||||
hasEmptyLabelSpace={false}
|
||||
label={
|
||||
<FormattedMessage
|
||||
defaultMessage="Select which types to export"
|
||||
id="savedObjectsManagement.objectsTable.exportObjectsConfirmModalDescription"
|
||||
values={Object {}}
|
||||
/>
|
||||
}
|
||||
labelType="legend"
|
||||
>
|
||||
<EuiCheckboxGroup
|
||||
idToSelectedMap={
|
||||
Object {
|
||||
"dashboard": true,
|
||||
"index-pattern": true,
|
||||
"search": true,
|
||||
"visualization": true,
|
||||
}
|
||||
}
|
||||
onChange={[Function]}
|
||||
options={
|
||||
Array [
|
||||
Object {
|
||||
"id": "index-pattern",
|
||||
"label": "index-pattern (0)",
|
||||
},
|
||||
Object {
|
||||
"id": "visualization",
|
||||
"label": "visualization (0)",
|
||||
},
|
||||
Object {
|
||||
"id": "dashboard",
|
||||
"label": "dashboard (0)",
|
||||
},
|
||||
Object {
|
||||
"id": "search",
|
||||
"label": "search (0)",
|
||||
},
|
||||
]
|
||||
}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
<EuiSpacer
|
||||
size="m"
|
||||
/>
|
||||
<EuiSwitch
|
||||
checked={true}
|
||||
label={
|
||||
<FormattedMessage
|
||||
defaultMessage="Include related objects"
|
||||
id="savedObjectsManagement.objectsTable.exportObjectsConfirmModal.includeReferencesDeepLabel"
|
||||
values={Object {}}
|
||||
/>
|
||||
}
|
||||
name="includeReferencesDeep"
|
||||
onChange={[Function]}
|
||||
/>
|
||||
</EuiModalBody>
|
||||
<EuiModalFooter>
|
||||
<EuiFlexGroup
|
||||
justifyContent="flexEnd"
|
||||
>
|
||||
<EuiFlexItem
|
||||
grow={false}
|
||||
>
|
||||
<EuiFlexGroup>
|
||||
<EuiFlexItem
|
||||
grow={false}
|
||||
>
|
||||
<EuiButtonEmpty
|
||||
onClick={[Function]}
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="Cancel"
|
||||
id="savedObjectsManagement.objectsTable.exportObjectsConfirmModal.cancelButtonLabel"
|
||||
values={Object {}}
|
||||
/>
|
||||
</EuiButtonEmpty>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem
|
||||
grow={false}
|
||||
>
|
||||
<EuiButton
|
||||
fill={true}
|
||||
onClick={[Function]}
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="Export all"
|
||||
id="savedObjectsManagement.objectsTable.exportObjectsConfirmModal.exportAllButtonLabel"
|
||||
values={Object {}}
|
||||
/>
|
||||
</EuiButton>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiModalFooter>
|
||||
</EuiModal>
|
||||
<ExportModal
|
||||
filteredItemCount={4}
|
||||
includeReferences={true}
|
||||
onCancel={[Function]}
|
||||
onExport={[Function]}
|
||||
onIncludeReferenceChange={[Function]}
|
||||
onSelectedOptionsChange={[Function]}
|
||||
options={
|
||||
Array [
|
||||
Object {
|
||||
"id": "index-pattern",
|
||||
"label": "index-pattern (0)",
|
||||
},
|
||||
Object {
|
||||
"id": "visualization",
|
||||
"label": "visualization (0)",
|
||||
},
|
||||
Object {
|
||||
"id": "dashboard",
|
||||
"label": "dashboard (0)",
|
||||
},
|
||||
Object {
|
||||
"id": "search",
|
||||
"label": "search (0)",
|
||||
},
|
||||
]
|
||||
}
|
||||
selectedOptions={
|
||||
Object {
|
||||
"dashboard": true,
|
||||
"index-pattern": true,
|
||||
"search": true,
|
||||
"visualization": true,
|
||||
}
|
||||
}
|
||||
/>
|
||||
`;
|
||||
|
||||
exports[`SavedObjectsTable should render normally 1`] = `
|
||||
|
|
|
@ -0,0 +1,97 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { mountWithIntl } from '@kbn/test/jest';
|
||||
import { SavedObjectWithMetadata } from '../../../../common';
|
||||
import { DeleteConfirmModal } from './delete_confirm_modal';
|
||||
|
||||
const createObject = (): SavedObjectWithMetadata => ({
|
||||
id: 'foo',
|
||||
type: 'bar',
|
||||
attributes: {},
|
||||
references: [],
|
||||
meta: {},
|
||||
});
|
||||
|
||||
describe('DeleteConfirmModal', () => {
|
||||
let onConfirm: jest.Mock;
|
||||
let onCancel: jest.Mock;
|
||||
|
||||
beforeEach(() => {
|
||||
onConfirm = jest.fn();
|
||||
onCancel = jest.fn();
|
||||
});
|
||||
|
||||
it('displays a loader if `isDeleting` is true', () => {
|
||||
const wrapper = mountWithIntl(
|
||||
<DeleteConfirmModal
|
||||
isDeleting={true}
|
||||
onConfirm={onConfirm}
|
||||
onCancel={onCancel}
|
||||
selectedObjects={[]}
|
||||
/>
|
||||
);
|
||||
expect(wrapper.find('EuiLoadingElastic')).toHaveLength(1);
|
||||
expect(wrapper.find('EuiModal')).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('lists the objects to delete', () => {
|
||||
const objs = [createObject(), createObject(), createObject()];
|
||||
const wrapper = mountWithIntl(
|
||||
<DeleteConfirmModal
|
||||
isDeleting={false}
|
||||
onConfirm={onConfirm}
|
||||
onCancel={onCancel}
|
||||
selectedObjects={objs}
|
||||
/>
|
||||
);
|
||||
expect(wrapper.find('.euiTableRow')).toHaveLength(3);
|
||||
});
|
||||
|
||||
it('calls `onCancel` when clicking on the cancel button', () => {
|
||||
const wrapper = mountWithIntl(
|
||||
<DeleteConfirmModal
|
||||
isDeleting={false}
|
||||
onConfirm={onConfirm}
|
||||
onCancel={onCancel}
|
||||
selectedObjects={[]}
|
||||
/>
|
||||
);
|
||||
wrapper.find('EuiButtonEmpty').simulate('click');
|
||||
|
||||
expect(onCancel).toHaveBeenCalledTimes(1);
|
||||
expect(onConfirm).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('calls `onDelete` when clicking on the delete button', () => {
|
||||
const wrapper = mountWithIntl(
|
||||
<DeleteConfirmModal
|
||||
isDeleting={false}
|
||||
onConfirm={onConfirm}
|
||||
onCancel={onCancel}
|
||||
selectedObjects={[]}
|
||||
/>
|
||||
);
|
||||
wrapper.find('EuiButton').simulate('click');
|
||||
|
||||
expect(onConfirm).toHaveBeenCalledTimes(1);
|
||||
expect(onCancel).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,153 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import React, { FC } from 'react';
|
||||
import {
|
||||
EuiInMemoryTable,
|
||||
EuiLoadingElastic,
|
||||
EuiToolTip,
|
||||
EuiIcon,
|
||||
EuiOverlayMask,
|
||||
EuiModal,
|
||||
EuiModalHeader,
|
||||
EuiModalHeaderTitle,
|
||||
EuiModalBody,
|
||||
EuiModalFooter,
|
||||
EuiFlexGroup,
|
||||
EuiFlexItem,
|
||||
EuiButtonEmpty,
|
||||
EuiButton,
|
||||
EuiSpacer,
|
||||
} from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
import { SavedObjectWithMetadata } from '../../../../common';
|
||||
import { getSavedObjectLabel } from '../../../lib';
|
||||
|
||||
export interface DeleteConfirmModalProps {
|
||||
isDeleting: boolean;
|
||||
onConfirm: () => void;
|
||||
onCancel: () => void;
|
||||
selectedObjects: SavedObjectWithMetadata[];
|
||||
}
|
||||
|
||||
export const DeleteConfirmModal: FC<DeleteConfirmModalProps> = ({
|
||||
isDeleting,
|
||||
onConfirm,
|
||||
onCancel,
|
||||
selectedObjects,
|
||||
}) => {
|
||||
if (isDeleting) {
|
||||
return (
|
||||
<EuiOverlayMask>
|
||||
<EuiLoadingElastic size="xl" />
|
||||
</EuiOverlayMask>
|
||||
);
|
||||
}
|
||||
|
||||
// can't use `EuiConfirmModal` here as the confirm modal body is wrapped
|
||||
// inside a `<p>` element, causing UI glitches with the table.
|
||||
return (
|
||||
<EuiOverlayMask>
|
||||
<EuiModal initialFocus="soDeleteConfirmModalConfirmBtn" onClose={onCancel}>
|
||||
<EuiModalHeader>
|
||||
<EuiModalHeaderTitle>
|
||||
<FormattedMessage
|
||||
id="savedObjectsManagement.objectsTable.deleteSavedObjectsConfirmModalTitle"
|
||||
defaultMessage="Delete saved objects"
|
||||
/>
|
||||
</EuiModalHeaderTitle>
|
||||
</EuiModalHeader>
|
||||
<EuiModalBody>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="savedObjectsManagement.deleteSavedObjectsConfirmModalDescription"
|
||||
defaultMessage="This action will delete the following saved objects:"
|
||||
/>
|
||||
</p>
|
||||
<EuiSpacer size="m" />
|
||||
<EuiInMemoryTable
|
||||
items={selectedObjects}
|
||||
columns={[
|
||||
{
|
||||
field: 'type',
|
||||
name: i18n.translate(
|
||||
'savedObjectsManagement.objectsTable.deleteSavedObjectsConfirmModal.typeColumnName',
|
||||
{ defaultMessage: 'Type' }
|
||||
),
|
||||
width: '50px',
|
||||
render: (type, object) => (
|
||||
<EuiToolTip position="top" content={getSavedObjectLabel(type)}>
|
||||
<EuiIcon type={object.meta.icon || 'apps'} />
|
||||
</EuiToolTip>
|
||||
),
|
||||
},
|
||||
{
|
||||
field: 'id',
|
||||
name: i18n.translate(
|
||||
'savedObjectsManagement.objectsTable.deleteSavedObjectsConfirmModal.idColumnName',
|
||||
{ defaultMessage: 'Id' }
|
||||
),
|
||||
},
|
||||
{
|
||||
field: 'meta.title',
|
||||
name: i18n.translate(
|
||||
'savedObjectsManagement.objectsTable.deleteSavedObjectsConfirmModal.titleColumnName',
|
||||
{ defaultMessage: 'Title' }
|
||||
),
|
||||
},
|
||||
]}
|
||||
pagination={true}
|
||||
sorting={false}
|
||||
/>
|
||||
</EuiModalBody>
|
||||
<EuiModalFooter>
|
||||
<EuiFlexGroup justifyContent="flexEnd">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiFlexGroup>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButtonEmpty onClick={onCancel} data-test-subj="confirmModalCancelButton">
|
||||
<FormattedMessage
|
||||
id="savedObjectsManagement.objectsTable.deleteSavedObjectsConfirmModal.cancelButtonLabel"
|
||||
defaultMessage="Cancel"
|
||||
/>
|
||||
</EuiButtonEmpty>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButton
|
||||
id="soDeleteConfirmModalConfirmBtn"
|
||||
fill
|
||||
color="danger"
|
||||
onClick={onConfirm}
|
||||
data-test-subj="confirmModalConfirmButton"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="savedObjectsManagement.objectsTable.deleteSavedObjectsConfirmModal.deleteButtonLabel"
|
||||
defaultMessage="Delete"
|
||||
/>
|
||||
</EuiButton>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiModalFooter>
|
||||
</EuiModal>
|
||||
</EuiOverlayMask>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,100 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { mountWithIntl } from '@kbn/test/jest';
|
||||
import { ExportModal } from './export_modal';
|
||||
|
||||
describe('ExportModal', () => {
|
||||
let onExport: jest.Mock;
|
||||
let onCancel: jest.Mock;
|
||||
let onSelectedOptionsChange: jest.Mock;
|
||||
let onIncludeReferenceChange: jest.Mock;
|
||||
|
||||
const options = [
|
||||
{ id: '1', label: 'option 1' },
|
||||
{ id: '2', label: 'option 2' },
|
||||
];
|
||||
const selectedOptions = {
|
||||
1: true,
|
||||
2: false,
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
onExport = jest.fn();
|
||||
onCancel = jest.fn();
|
||||
onSelectedOptionsChange = jest.fn();
|
||||
onIncludeReferenceChange = jest.fn();
|
||||
});
|
||||
|
||||
it('Displays a checkbox for each option', () => {
|
||||
const wrapper = mountWithIntl(
|
||||
<ExportModal
|
||||
onExport={onExport}
|
||||
onCancel={onCancel}
|
||||
onSelectedOptionsChange={onSelectedOptionsChange}
|
||||
filteredItemCount={42}
|
||||
options={options}
|
||||
selectedOptions={selectedOptions}
|
||||
includeReferences={false}
|
||||
onIncludeReferenceChange={onIncludeReferenceChange}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(wrapper.find('EuiCheckbox')).toHaveLength(2);
|
||||
});
|
||||
|
||||
it('calls `onCancel` when clicking on the cancel button', () => {
|
||||
const wrapper = mountWithIntl(
|
||||
<ExportModal
|
||||
onExport={onExport}
|
||||
onCancel={onCancel}
|
||||
onSelectedOptionsChange={onSelectedOptionsChange}
|
||||
filteredItemCount={42}
|
||||
options={options}
|
||||
selectedOptions={selectedOptions}
|
||||
includeReferences={false}
|
||||
onIncludeReferenceChange={onIncludeReferenceChange}
|
||||
/>
|
||||
);
|
||||
wrapper.find('EuiButtonEmpty').simulate('click');
|
||||
|
||||
expect(onCancel).toHaveBeenCalledTimes(1);
|
||||
expect(onExport).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('calls `onExport` when clicking on the export button', () => {
|
||||
const wrapper = mountWithIntl(
|
||||
<ExportModal
|
||||
onExport={onExport}
|
||||
onCancel={onCancel}
|
||||
onSelectedOptionsChange={onSelectedOptionsChange}
|
||||
filteredItemCount={42}
|
||||
options={options}
|
||||
selectedOptions={selectedOptions}
|
||||
includeReferences={false}
|
||||
onIncludeReferenceChange={onIncludeReferenceChange}
|
||||
/>
|
||||
);
|
||||
wrapper.find('EuiButton').simulate('click');
|
||||
|
||||
expect(onExport).toHaveBeenCalledTimes(1);
|
||||
expect(onCancel).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,137 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import React, { FC } from 'react';
|
||||
import {
|
||||
EuiOverlayMask,
|
||||
EuiModal,
|
||||
EuiModalHeader,
|
||||
EuiModalHeaderTitle,
|
||||
EuiModalBody,
|
||||
EuiModalFooter,
|
||||
EuiFlexGroup,
|
||||
EuiFlexItem,
|
||||
EuiButtonEmpty,
|
||||
EuiButton,
|
||||
EuiSpacer,
|
||||
EuiFormRow,
|
||||
EuiCheckboxGroup,
|
||||
EuiSwitch,
|
||||
} from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
|
||||
export interface ExportModalProps {
|
||||
onExport: () => void;
|
||||
onCancel: () => void;
|
||||
onSelectedOptionsChange: (newSelectedOptions: Record<string, boolean>) => void;
|
||||
filteredItemCount: number;
|
||||
options: Array<{ id: string; label: string }>;
|
||||
selectedOptions: Record<string, boolean>;
|
||||
includeReferences: boolean;
|
||||
onIncludeReferenceChange: (newIncludeReference: boolean) => void;
|
||||
}
|
||||
|
||||
export const ExportModal: FC<ExportModalProps> = ({
|
||||
onCancel,
|
||||
onExport,
|
||||
onSelectedOptionsChange,
|
||||
options,
|
||||
filteredItemCount,
|
||||
selectedOptions,
|
||||
includeReferences,
|
||||
onIncludeReferenceChange,
|
||||
}) => {
|
||||
return (
|
||||
<EuiOverlayMask>
|
||||
<EuiModal onClose={onCancel}>
|
||||
<EuiModalHeader>
|
||||
<EuiModalHeaderTitle>
|
||||
<FormattedMessage
|
||||
id="savedObjectsManagement.objectsTable.exportObjectsConfirmModalTitle"
|
||||
defaultMessage="Export {filteredItemCount, plural, one{# object} other {# objects}}"
|
||||
values={{
|
||||
filteredItemCount,
|
||||
}}
|
||||
/>
|
||||
</EuiModalHeaderTitle>
|
||||
</EuiModalHeader>
|
||||
<EuiModalBody>
|
||||
<EuiFormRow
|
||||
label={
|
||||
<FormattedMessage
|
||||
id="savedObjectsManagement.objectsTable.exportObjectsConfirmModalDescription"
|
||||
defaultMessage="Select which types to export"
|
||||
/>
|
||||
}
|
||||
labelType="legend"
|
||||
>
|
||||
<EuiCheckboxGroup
|
||||
options={options}
|
||||
idToSelectedMap={selectedOptions}
|
||||
onChange={(optionId) => {
|
||||
onSelectedOptionsChange({
|
||||
...selectedOptions,
|
||||
...{
|
||||
[optionId]: !selectedOptions[optionId],
|
||||
},
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
<EuiSpacer size="m" />
|
||||
<EuiSwitch
|
||||
name="includeReferencesDeep"
|
||||
label={
|
||||
<FormattedMessage
|
||||
id="savedObjectsManagement.objectsTable.exportObjectsConfirmModal.includeReferencesDeepLabel"
|
||||
defaultMessage="Include related objects"
|
||||
/>
|
||||
}
|
||||
checked={includeReferences}
|
||||
onChange={() => onIncludeReferenceChange(!includeReferences)}
|
||||
/>
|
||||
</EuiModalBody>
|
||||
<EuiModalFooter>
|
||||
<EuiFlexGroup justifyContent="flexEnd">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiFlexGroup>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButtonEmpty onClick={onCancel}>
|
||||
<FormattedMessage
|
||||
id="savedObjectsManagement.objectsTable.exportObjectsConfirmModal.cancelButtonLabel"
|
||||
defaultMessage="Cancel"
|
||||
/>
|
||||
</EuiButtonEmpty>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButton fill onClick={onExport}>
|
||||
<FormattedMessage
|
||||
id="savedObjectsManagement.objectsTable.exportObjectsConfirmModal.exportAllButtonLabel"
|
||||
defaultMessage="Export all"
|
||||
/>
|
||||
</EuiButton>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiModalFooter>
|
||||
</EuiModal>
|
||||
</EuiOverlayMask>
|
||||
);
|
||||
};
|
|
@ -21,3 +21,5 @@ export { Header } from './header';
|
|||
export { Table } from './table';
|
||||
export { Flyout } from './flyout';
|
||||
export { Relationships } from './relationships';
|
||||
export { DeleteConfirmModal } from './delete_confirm_modal';
|
||||
export { ExportModal } from './export_modal';
|
||||
|
|
|
@ -325,7 +325,7 @@ describe('SavedObjectsTable', () => {
|
|||
(component.find('Header') as any).prop('onExportAll')();
|
||||
component.update();
|
||||
|
||||
expect(component.find('EuiModal')).toMatchSnapshot();
|
||||
expect(component.find('ExportModal')).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should export all', async () => {
|
||||
|
@ -504,7 +504,7 @@ describe('SavedObjectsTable', () => {
|
|||
await component.instance().onDelete();
|
||||
component.update();
|
||||
|
||||
expect(component.find('EuiConfirmModal')).toMatchSnapshot();
|
||||
expect(component.find('DeleteConfirmModal')).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should delete selected objects', async () => {
|
||||
|
|
|
@ -21,32 +21,8 @@ import React, { Component } from 'react';
|
|||
import { debounce } from 'lodash';
|
||||
// @ts-expect-error
|
||||
import { saveAs } from '@elastic/filesaver';
|
||||
import {
|
||||
EuiSpacer,
|
||||
Query,
|
||||
EuiInMemoryTable,
|
||||
EuiIcon,
|
||||
EuiConfirmModal,
|
||||
EuiLoadingElastic,
|
||||
EuiOverlayMask,
|
||||
EUI_MODAL_CONFIRM_BUTTON,
|
||||
EuiCheckboxGroup,
|
||||
EuiToolTip,
|
||||
EuiPageContent,
|
||||
EuiSwitch,
|
||||
EuiModal,
|
||||
EuiModalHeader,
|
||||
EuiModalBody,
|
||||
EuiModalFooter,
|
||||
EuiButtonEmpty,
|
||||
EuiButton,
|
||||
EuiModalHeaderTitle,
|
||||
EuiFormRow,
|
||||
EuiFlexGroup,
|
||||
EuiFlexItem,
|
||||
} from '@elastic/eui';
|
||||
import { EuiSpacer, Query, EuiPageContent } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
import {
|
||||
SavedObjectsClientContract,
|
||||
SavedObjectsFindOptions,
|
||||
|
@ -62,7 +38,6 @@ import {
|
|||
parseQuery,
|
||||
getSavedObjectCounts,
|
||||
getRelationships,
|
||||
getSavedObjectLabel,
|
||||
fetchExportObjects,
|
||||
fetchExportByTypeAndSearch,
|
||||
findObjects,
|
||||
|
@ -77,7 +52,14 @@ import {
|
|||
SavedObjectsManagementActionServiceStart,
|
||||
SavedObjectsManagementColumnServiceStart,
|
||||
} from '../../services';
|
||||
import { Header, Table, Flyout, Relationships } from './components';
|
||||
import {
|
||||
Header,
|
||||
Table,
|
||||
Flyout,
|
||||
Relationships,
|
||||
DeleteConfirmModal,
|
||||
ExportModal,
|
||||
} from './components';
|
||||
import { DataPublicPluginStart } from '../../../../../plugins/data/public';
|
||||
|
||||
interface ExportAllOption {
|
||||
|
@ -554,114 +536,24 @@ export class SavedObjectsTable extends Component<SavedObjectsTableProps, SavedOb
|
|||
|
||||
renderDeleteConfirmModal() {
|
||||
const { isShowingDeleteConfirmModal, isDeleting, selectedSavedObjects } = this.state;
|
||||
|
||||
if (!isShowingDeleteConfirmModal) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let modal;
|
||||
|
||||
if (isDeleting) {
|
||||
// Block the user from interacting with the table while its contents are being deleted.
|
||||
modal = <EuiLoadingElastic size="xl" />;
|
||||
} else {
|
||||
const onCancel = () => {
|
||||
this.setState({ isShowingDeleteConfirmModal: false });
|
||||
};
|
||||
|
||||
const onConfirm = () => {
|
||||
this.delete();
|
||||
};
|
||||
|
||||
modal = (
|
||||
<EuiConfirmModal
|
||||
title={
|
||||
<FormattedMessage
|
||||
id="savedObjectsManagement.objectsTable.deleteSavedObjectsConfirmModalTitle"
|
||||
defaultMessage="Delete saved objects"
|
||||
/>
|
||||
}
|
||||
onCancel={onCancel}
|
||||
onConfirm={onConfirm}
|
||||
buttonColor="danger"
|
||||
cancelButtonText={
|
||||
<FormattedMessage
|
||||
id="savedObjectsManagement.objectsTable.deleteSavedObjectsConfirmModal.cancelButtonLabel"
|
||||
defaultMessage="Cancel"
|
||||
/>
|
||||
}
|
||||
confirmButtonText={
|
||||
isDeleting ? (
|
||||
<FormattedMessage
|
||||
id="savedObjectsManagement.objectsTable.deleteSavedObjectsConfirmModal.deleteProcessButtonLabel"
|
||||
defaultMessage="Deleting…"
|
||||
/>
|
||||
) : (
|
||||
<FormattedMessage
|
||||
id="savedObjectsManagement.objectsTable.deleteSavedObjectsConfirmModal.deleteButtonLabel"
|
||||
defaultMessage="Delete"
|
||||
/>
|
||||
)
|
||||
}
|
||||
defaultFocusedButton={EUI_MODAL_CONFIRM_BUTTON}
|
||||
>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="savedObjectsManagement.deleteSavedObjectsConfirmModalDescription"
|
||||
defaultMessage="This action will delete the following saved objects:"
|
||||
/>
|
||||
</p>
|
||||
<EuiInMemoryTable
|
||||
items={selectedSavedObjects}
|
||||
columns={[
|
||||
{
|
||||
field: 'type',
|
||||
name: i18n.translate(
|
||||
'savedObjectsManagement.objectsTable.deleteSavedObjectsConfirmModal.typeColumnName',
|
||||
{ defaultMessage: 'Type' }
|
||||
),
|
||||
width: '50px',
|
||||
render: (type, object) => (
|
||||
<EuiToolTip position="top" content={getSavedObjectLabel(type)}>
|
||||
<EuiIcon type={object.meta.icon || 'apps'} />
|
||||
</EuiToolTip>
|
||||
),
|
||||
},
|
||||
{
|
||||
field: 'id',
|
||||
name: i18n.translate(
|
||||
'savedObjectsManagement.objectsTable.deleteSavedObjectsConfirmModal.idColumnName',
|
||||
{ defaultMessage: 'Id' }
|
||||
),
|
||||
},
|
||||
{
|
||||
field: 'meta.title',
|
||||
name: i18n.translate(
|
||||
'savedObjectsManagement.objectsTable.deleteSavedObjectsConfirmModal.titleColumnName',
|
||||
{ defaultMessage: 'Title' }
|
||||
),
|
||||
},
|
||||
]}
|
||||
pagination={true}
|
||||
sorting={false}
|
||||
/>
|
||||
</EuiConfirmModal>
|
||||
);
|
||||
}
|
||||
|
||||
return <EuiOverlayMask>{modal}</EuiOverlayMask>;
|
||||
return (
|
||||
<DeleteConfirmModal
|
||||
isDeleting={isDeleting}
|
||||
onConfirm={() => {
|
||||
this.delete();
|
||||
}}
|
||||
onCancel={() => {
|
||||
this.setState({ isShowingDeleteConfirmModal: false });
|
||||
}}
|
||||
selectedObjects={selectedSavedObjects}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
changeIncludeReferencesDeep = () => {
|
||||
this.setState((state) => ({
|
||||
isIncludeReferencesDeepChecked: !state.isIncludeReferencesDeepChecked,
|
||||
}));
|
||||
};
|
||||
|
||||
closeExportAllModal = () => {
|
||||
this.setState({ isShowingExportAllOptionsModal: false });
|
||||
};
|
||||
|
||||
renderExportAllOptionsModal() {
|
||||
const {
|
||||
isShowingExportAllOptionsModal,
|
||||
|
@ -676,85 +568,26 @@ export class SavedObjectsTable extends Component<SavedObjectsTableProps, SavedOb
|
|||
}
|
||||
|
||||
return (
|
||||
<EuiOverlayMask>
|
||||
<EuiModal onClose={this.closeExportAllModal}>
|
||||
<EuiModalHeader>
|
||||
<EuiModalHeaderTitle>
|
||||
<FormattedMessage
|
||||
id="savedObjectsManagement.objectsTable.exportObjectsConfirmModalTitle"
|
||||
defaultMessage="Export {filteredItemCount, plural, one{# object} other {# objects}}"
|
||||
values={{
|
||||
filteredItemCount,
|
||||
}}
|
||||
/>
|
||||
</EuiModalHeaderTitle>
|
||||
</EuiModalHeader>
|
||||
<EuiModalBody>
|
||||
<EuiFormRow
|
||||
label={
|
||||
<FormattedMessage
|
||||
id="savedObjectsManagement.objectsTable.exportObjectsConfirmModalDescription"
|
||||
defaultMessage="Select which types to export"
|
||||
/>
|
||||
}
|
||||
labelType="legend"
|
||||
>
|
||||
<EuiCheckboxGroup
|
||||
options={exportAllOptions}
|
||||
idToSelectedMap={exportAllSelectedOptions}
|
||||
onChange={(optionId) => {
|
||||
const newExportAllSelectedOptions = {
|
||||
...exportAllSelectedOptions,
|
||||
...{
|
||||
[optionId]: !exportAllSelectedOptions[optionId],
|
||||
},
|
||||
};
|
||||
|
||||
this.setState({
|
||||
exportAllSelectedOptions: newExportAllSelectedOptions,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
<EuiSpacer size="m" />
|
||||
<EuiSwitch
|
||||
name="includeReferencesDeep"
|
||||
label={
|
||||
<FormattedMessage
|
||||
id="savedObjectsManagement.objectsTable.exportObjectsConfirmModal.includeReferencesDeepLabel"
|
||||
defaultMessage="Include related objects"
|
||||
/>
|
||||
}
|
||||
checked={isIncludeReferencesDeepChecked}
|
||||
onChange={this.changeIncludeReferencesDeep}
|
||||
/>
|
||||
</EuiModalBody>
|
||||
<EuiModalFooter>
|
||||
<EuiFlexGroup justifyContent="flexEnd">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiFlexGroup>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButtonEmpty onClick={this.closeExportAllModal}>
|
||||
<FormattedMessage
|
||||
id="savedObjectsManagement.objectsTable.exportObjectsConfirmModal.cancelButtonLabel"
|
||||
defaultMessage="Cancel"
|
||||
/>
|
||||
</EuiButtonEmpty>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButton fill onClick={this.onExportAll}>
|
||||
<FormattedMessage
|
||||
id="savedObjectsManagement.objectsTable.exportObjectsConfirmModal.exportAllButtonLabel"
|
||||
defaultMessage="Export all"
|
||||
/>
|
||||
</EuiButton>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiModalFooter>
|
||||
</EuiModal>
|
||||
</EuiOverlayMask>
|
||||
<ExportModal
|
||||
onExport={this.onExportAll}
|
||||
onCancel={() => {
|
||||
this.setState({ isShowingExportAllOptionsModal: false });
|
||||
}}
|
||||
onSelectedOptionsChange={(newOptions) => {
|
||||
this.setState({
|
||||
exportAllSelectedOptions: newOptions,
|
||||
});
|
||||
}}
|
||||
filteredItemCount={filteredItemCount}
|
||||
options={exportAllOptions}
|
||||
selectedOptions={exportAllSelectedOptions}
|
||||
includeReferences={isIncludeReferencesDeepChecked}
|
||||
onIncludeReferenceChange={(newIncludeReferences) => {
|
||||
this.setState({
|
||||
isIncludeReferencesDeepChecked: newIncludeReferences,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -3198,7 +3198,6 @@
|
|||
"savedObjectsManagement.objects.savedObjectsTitle": "保存されたオブジェクト",
|
||||
"savedObjectsManagement.objectsTable.deleteSavedObjectsConfirmModal.cancelButtonLabel": "キャンセル",
|
||||
"savedObjectsManagement.objectsTable.deleteSavedObjectsConfirmModal.deleteButtonLabel": "削除",
|
||||
"savedObjectsManagement.objectsTable.deleteSavedObjectsConfirmModal.deleteProcessButtonLabel": "削除中…",
|
||||
"savedObjectsManagement.objectsTable.deleteSavedObjectsConfirmModal.idColumnName": "Id",
|
||||
"savedObjectsManagement.objectsTable.deleteSavedObjectsConfirmModal.titleColumnName": "タイトル",
|
||||
"savedObjectsManagement.objectsTable.deleteSavedObjectsConfirmModal.typeColumnName": "型",
|
||||
|
|
|
@ -3202,7 +3202,6 @@
|
|||
"savedObjectsManagement.objects.savedObjectsTitle": "已保存对象",
|
||||
"savedObjectsManagement.objectsTable.deleteSavedObjectsConfirmModal.cancelButtonLabel": "取消",
|
||||
"savedObjectsManagement.objectsTable.deleteSavedObjectsConfirmModal.deleteButtonLabel": "删除",
|
||||
"savedObjectsManagement.objectsTable.deleteSavedObjectsConfirmModal.deleteProcessButtonLabel": "正在删除……",
|
||||
"savedObjectsManagement.objectsTable.deleteSavedObjectsConfirmModal.idColumnName": "ID",
|
||||
"savedObjectsManagement.objectsTable.deleteSavedObjectsConfirmModal.titleColumnName": "标题",
|
||||
"savedObjectsManagement.objectsTable.deleteSavedObjectsConfirmModal.typeColumnName": "类型",
|
||||
|
|
Loading…
Reference in a new issue