Convert Dashboard save dialog to react/EUI (#19531)
* eui save panel * add title and description inputs * copy on save * add jest tests * fix functional and jest tests * another functional test fix * updates from Stacey-Gammon review * remove debounce because it broke functional tests * update jest snapshot
This commit is contained in:
parent
8d1629a7a6
commit
3dae391e89
|
@ -40,6 +40,7 @@ import { VisualizeConstants } from '../visualize/visualize_constants';
|
|||
import { DashboardStateManager } from './dashboard_state_manager';
|
||||
import { saveDashboard } from './lib';
|
||||
import { showCloneModal } from './top_nav/show_clone_modal';
|
||||
import { showSaveModal } from './top_nav/show_save_modal';
|
||||
import { showAddPanel } from './top_nav/show_add_panel';
|
||||
import { migrateLegacyQuery } from 'ui/utils/migrateLegacyQuery';
|
||||
import * as filterActions from 'ui/doc_table/actions/filter';
|
||||
|
@ -219,9 +220,6 @@ app.directive('dashboardApp', function ($injector) {
|
|||
dashboardStateManager.setDarkTheme($scope.model.darkTheme);
|
||||
updateTheme();
|
||||
});
|
||||
$scope.$watch('model.description', () => dashboardStateManager.setDescription($scope.model.description));
|
||||
$scope.$watch('model.title', () => dashboardStateManager.setTitle($scope.model.title));
|
||||
$scope.$watch('model.timeRestore', () => dashboardStateManager.setTimeRestore($scope.model.timeRestore));
|
||||
$scope.indexPatterns = [];
|
||||
|
||||
$scope.onPanelRemoved = (panelIndex) => {
|
||||
|
@ -329,8 +327,41 @@ app.directive('dashboardApp', function ($injector) {
|
|||
dashboardStateManager.setFullScreenMode(true);
|
||||
navActions[TopNavIds.EXIT_EDIT_MODE] = () => onChangeViewMode(DashboardViewMode.VIEW);
|
||||
navActions[TopNavIds.ENTER_EDIT_MODE] = () => onChangeViewMode(DashboardViewMode.EDIT);
|
||||
navActions[TopNavIds.SAVE] = () => {
|
||||
const currentTitle = dashboardStateManager.getTitle();
|
||||
const currentDescription = dashboardStateManager.getDescription();
|
||||
const currentTimeRestore = dashboardStateManager.getTimeRestore();
|
||||
const onSave = ({ newTitle, newDescription, newCopyOnSave, newTimeRestore, isTitleDuplicateConfirmed, onTitleDuplicate }) => {
|
||||
dashboardStateManager.setTitle(newTitle);
|
||||
dashboardStateManager.setDescription(newDescription);
|
||||
dashboardStateManager.savedDashboard.copyOnSave = newCopyOnSave;
|
||||
dashboardStateManager.setTimeRestore(newTimeRestore);
|
||||
const saveOptions = {
|
||||
confirmOverwrite: false,
|
||||
isTitleDuplicateConfirmed,
|
||||
onTitleDuplicate,
|
||||
};
|
||||
return $scope.save(saveOptions).then(id => {
|
||||
// If the save wasn't successful, put the original values back.
|
||||
if (!id) {
|
||||
dashboardStateManager.setTitle(currentTitle);
|
||||
dashboardStateManager.setDescription(currentDescription);
|
||||
dashboardStateManager.setTimeRestore(currentTimeRestore);
|
||||
}
|
||||
return id;
|
||||
});
|
||||
};
|
||||
|
||||
showSaveModal({
|
||||
onSave,
|
||||
title: currentTitle,
|
||||
description: currentDescription,
|
||||
timeRestore: currentTimeRestore,
|
||||
showCopyOnSave: dash.id ? true : false,
|
||||
});
|
||||
};
|
||||
navActions[TopNavIds.CLONE] = () => {
|
||||
const currentTitle = $scope.model.title;
|
||||
const currentTitle = dashboardStateManager.getTitle();
|
||||
const onClone = (newTitle, isTitleDuplicateConfirmed, onTitleDuplicate) => {
|
||||
dashboardStateManager.savedDashboard.copyOnSave = true;
|
||||
dashboardStateManager.setTitle(newTitle);
|
||||
|
@ -342,9 +373,6 @@ app.directive('dashboardApp', function ($injector) {
|
|||
return $scope.save(saveOptions).then(id => {
|
||||
// If the save wasn't successful, put the original title back.
|
||||
if (!id) {
|
||||
$scope.model.title = currentTitle;
|
||||
// There is a watch on $scope.model.title that *should* call this automatically but
|
||||
// angular is failing to trigger it, so do so manually here.
|
||||
dashboardStateManager.setTitle(currentTitle);
|
||||
}
|
||||
return id;
|
||||
|
@ -406,11 +434,9 @@ app.directive('dashboardApp', function ($injector) {
|
|||
kbnUrl.removeParam(DashboardConstants.NEW_VISUALIZATION_ID_PARAM);
|
||||
}
|
||||
|
||||
// TODO remove opts once share has been converted to react
|
||||
$scope.opts = {
|
||||
displayName: dash.getDisplayName(),
|
||||
dashboard: dash,
|
||||
save: $scope.save,
|
||||
timefilter: $scope.timefilter
|
||||
dashboard: dash, // used in share.html
|
||||
};
|
||||
}
|
||||
};
|
||||
|
|
|
@ -109,7 +109,7 @@
|
|||
}
|
||||
|
||||
|
||||
.dashboardCloneModal {
|
||||
.dashboardModal {
|
||||
width: 450px;
|
||||
}
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
exports[`renders DashboardCloneModal 1`] = `
|
||||
<EuiOverlayMask>
|
||||
<EuiModal
|
||||
className="dashboardCloneModal"
|
||||
className="dashboardModal"
|
||||
data-test-subj="dashboardCloneModal"
|
||||
onClose={[Function]}
|
||||
>
|
||||
|
|
|
@ -0,0 +1,100 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`renders DashboardSaveModal 1`] = `
|
||||
<EuiOverlayMask>
|
||||
<EuiModal
|
||||
className="dashboardModal"
|
||||
data-test-subj="dashboardSaveModal"
|
||||
onClose={[Function]}
|
||||
>
|
||||
<EuiModalHeader>
|
||||
<EuiModalHeaderTitle>
|
||||
Save Dashboard
|
||||
</EuiModalHeaderTitle>
|
||||
</EuiModalHeader>
|
||||
<EuiModalBody>
|
||||
<EuiForm>
|
||||
<EuiFormRow
|
||||
describedByIds={Array []}
|
||||
fullWidth={false}
|
||||
hasEmptyLabelSpace={false}
|
||||
label="Save as a new dashboard"
|
||||
>
|
||||
<EuiSwitch
|
||||
checked={false}
|
||||
data-test-subj="saveAsNewCheckbox"
|
||||
onChange={[Function]}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
<EuiFormRow
|
||||
describedByIds={Array []}
|
||||
fullWidth={false}
|
||||
hasEmptyLabelSpace={false}
|
||||
label="Title"
|
||||
>
|
||||
<EuiFieldText
|
||||
autoFocus={true}
|
||||
compressed={false}
|
||||
data-test-subj="dashboardTitle"
|
||||
fullWidth={false}
|
||||
isInvalid={false}
|
||||
isLoading={false}
|
||||
onChange={[Function]}
|
||||
value="dash title"
|
||||
/>
|
||||
</EuiFormRow>
|
||||
<EuiFormRow
|
||||
describedByIds={Array []}
|
||||
fullWidth={false}
|
||||
hasEmptyLabelSpace={false}
|
||||
label="Description"
|
||||
>
|
||||
<EuiTextArea
|
||||
compressed={true}
|
||||
data-test-subj="dashboardDescription"
|
||||
fullWidth={false}
|
||||
onChange={[Function]}
|
||||
value="dash description"
|
||||
/>
|
||||
</EuiFormRow>
|
||||
<EuiFormRow
|
||||
describedByIds={Array []}
|
||||
fullWidth={false}
|
||||
hasEmptyLabelSpace={false}
|
||||
helpText="This changes the time filter to the currently selected time each time this dashboard is loaded."
|
||||
label="Store time with dashboard"
|
||||
>
|
||||
<EuiSwitch
|
||||
checked={true}
|
||||
data-test-subj="storeTimeWithDashboard"
|
||||
onChange={[Function]}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
</EuiForm>
|
||||
</EuiModalBody>
|
||||
<EuiModalFooter>
|
||||
<EuiButton
|
||||
color="primary"
|
||||
data-test-subj="saveCancelButton"
|
||||
fill={false}
|
||||
iconSide="left"
|
||||
onClick={[Function]}
|
||||
type="button"
|
||||
>
|
||||
Cancel
|
||||
</EuiButton>
|
||||
<EuiButton
|
||||
color="primary"
|
||||
data-test-subj="confirmSaveDashboardButton"
|
||||
fill={true}
|
||||
iconSide="left"
|
||||
isLoading={false}
|
||||
onClick={[Function]}
|
||||
type="button"
|
||||
>
|
||||
Confirm Save
|
||||
</EuiButton>
|
||||
</EuiModalFooter>
|
||||
</EuiModal>
|
||||
</EuiOverlayMask>
|
||||
`;
|
|
@ -92,7 +92,7 @@ export class DashboardCloneModal extends React.Component {
|
|||
<EuiCallOut
|
||||
title={`A Dashboard with the title '${this.state.newDashboardName}' already exists.`}
|
||||
color="warning"
|
||||
data-test-subj="cloneModalTitleDupicateWarnMsg"
|
||||
data-test-subj="titleDupicateWarnMsg"
|
||||
>
|
||||
<p>
|
||||
Click <strong>Confirm Clone</strong> to clone the dashboard with the duplicate title.
|
||||
|
@ -108,7 +108,7 @@ export class DashboardCloneModal extends React.Component {
|
|||
<EuiOverlayMask>
|
||||
<EuiModal
|
||||
data-test-subj="dashboardCloneModal"
|
||||
className="dashboardCloneModal"
|
||||
className="dashboardModal"
|
||||
onClose={this.props.onClose}
|
||||
>
|
||||
<EuiModalHeader>
|
||||
|
|
|
@ -45,7 +45,7 @@ export function getTopNavConfig(dashboardMode, actions, hideWriteControls) {
|
|||
);
|
||||
case DashboardViewMode.EDIT:
|
||||
return [
|
||||
getSaveConfig(),
|
||||
getSaveConfig(actions[TopNavIds.SAVE]),
|
||||
getViewConfig(actions[TopNavIds.EXIT_EDIT_MODE]),
|
||||
getAddConfig(actions[TopNavIds.ADD]),
|
||||
getOptionsConfig(),
|
||||
|
@ -79,12 +79,12 @@ function getEditConfig(action) {
|
|||
/**
|
||||
* @returns {kbnTopNavConfig}
|
||||
*/
|
||||
function getSaveConfig() {
|
||||
function getSaveConfig(action) {
|
||||
return {
|
||||
key: 'save',
|
||||
key: TopNavIds.SAVE,
|
||||
description: 'Save your dashboard',
|
||||
testId: 'dashboardSaveMenuItem',
|
||||
template: require('plugins/kibana/dashboard/top_nav/save.html')
|
||||
run: action
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -105,7 +105,7 @@ function getViewConfig(action) {
|
|||
*/
|
||||
function getCloneConfig(action) {
|
||||
return {
|
||||
key: 'clone',
|
||||
key: TopNavIds.CLONE,
|
||||
description: 'Create a copy of your dashboard',
|
||||
testId: 'dashboardClone',
|
||||
run: action
|
||||
|
|
|
@ -1,84 +0,0 @@
|
|||
<form
|
||||
role="form"
|
||||
ng-submit="opts.save()"
|
||||
>
|
||||
<h2 class="kuiLocalDropdownTitle">
|
||||
Save {{opts.displayName}}
|
||||
</h2>
|
||||
|
||||
<div class="kuiLocalDropdownSection">
|
||||
<div class="kuiLocalDropdownHeader">
|
||||
<label
|
||||
class="kuiLocalDropdownHeader__label"
|
||||
for="saveDashboardTitle"
|
||||
>
|
||||
Title
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<input
|
||||
id="saveDashboardTitle"
|
||||
class="kuiLocalDropdownInput"
|
||||
data-test-subj="dashboardTitle"
|
||||
type="text"
|
||||
ng-model="model.title"
|
||||
placeholder="Dashboard title"
|
||||
input-focus="select"
|
||||
>
|
||||
</div>
|
||||
|
||||
<div class="kuiLocalDropdownSection">
|
||||
<div class="kuiLocalDropdownHeader">
|
||||
<label
|
||||
class="kuiLocalDropdownHeader__label"
|
||||
for="saveDashboardDescription"
|
||||
>
|
||||
Description
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<input
|
||||
id="saveDashboardDescription"
|
||||
class="kuiLocalDropdownInput"
|
||||
data-test-subj="dashboardDescription"
|
||||
type="text"
|
||||
ng-model="model.description"
|
||||
placeholder="Dashboard description"
|
||||
>
|
||||
</div>
|
||||
|
||||
<saved-object-save-as-check-box
|
||||
class="kuiVerticalRhythmSmall"
|
||||
saved-object="opts.dashboard"
|
||||
></saved-object-save-as-check-box>
|
||||
|
||||
<div class="kuiVerticalRhythm">
|
||||
<label class="kuiCheckBoxLabel kuiVerticalRhythmSmall">
|
||||
<input
|
||||
class="kuiCheckBox"
|
||||
type="checkbox"
|
||||
ng-model="model.timeRestore"
|
||||
ng-checked="model.timeRestore"
|
||||
data-test-subj="storeTimeWithDashboard"
|
||||
>
|
||||
|
||||
<span class="kuiCheckBoxLabel__text">
|
||||
Store time with {{opts.displayName}}
|
||||
</span>
|
||||
</label>
|
||||
|
||||
<div class="kuiLocalDropdownFormNote kuiVerticalRhythmSmall">
|
||||
This changes the time filter to the currently selected time each time this dashboard is loaded.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button
|
||||
data-test-subj="confirmSaveDashboardButton"
|
||||
type="submit"
|
||||
ng-disabled="!model.title"
|
||||
class="kuiButton kuiButton--primary kuiVerticalRhythm"
|
||||
aria-label="Save dashboard"
|
||||
>
|
||||
Save
|
||||
</button>
|
||||
</form>
|
245
src/core_plugins/kibana/public/dashboard/top_nav/save_modal.js
Normal file
245
src/core_plugins/kibana/public/dashboard/top_nav/save_modal.js
Normal file
|
@ -0,0 +1,245 @@
|
|||
/*
|
||||
* 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, { Fragment } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import {
|
||||
EuiButton,
|
||||
EuiFieldText,
|
||||
EuiModal,
|
||||
EuiModalBody,
|
||||
EuiModalFooter,
|
||||
EuiModalHeader,
|
||||
EuiModalHeaderTitle,
|
||||
EuiOverlayMask,
|
||||
EuiSpacer,
|
||||
EuiCallOut,
|
||||
EuiForm,
|
||||
EuiFormRow,
|
||||
EuiTextArea,
|
||||
EuiSwitch,
|
||||
} from '@elastic/eui';
|
||||
|
||||
export class DashboardSaveModal extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
title: props.title,
|
||||
description: props.description,
|
||||
copyOnSave: false,
|
||||
timeRestore: props.timeRestore,
|
||||
isTitleDuplicateConfirmed: false,
|
||||
hasTitleDuplicate: false,
|
||||
isLoading: false,
|
||||
};
|
||||
}
|
||||
componentDidMount() {
|
||||
this._isMounted = true;
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this._isMounted = false;
|
||||
}
|
||||
|
||||
onTitleDuplicate = () => {
|
||||
this.setState({
|
||||
isLoading: false,
|
||||
isTitleDuplicateConfirmed: true,
|
||||
hasTitleDuplicate: true,
|
||||
});
|
||||
}
|
||||
|
||||
saveDashboard = async () => {
|
||||
if (this.state.isLoading) {
|
||||
// ignore extra clicks
|
||||
return;
|
||||
}
|
||||
|
||||
this.setState({
|
||||
isLoading: true,
|
||||
});
|
||||
|
||||
await this.props.onSave({
|
||||
newTitle: this.state.title,
|
||||
newDescription: this.state.description,
|
||||
newCopyOnSave: this.state.copyOnSave,
|
||||
newTimeRestore: this.state.timeRestore,
|
||||
isTitleDuplicateConfirmed: this.state.isTitleDuplicateConfirmed,
|
||||
onTitleDuplicate: this.onTitleDuplicate,
|
||||
});
|
||||
};
|
||||
|
||||
onTitleChange = (event) => {
|
||||
this.setState({
|
||||
title: event.target.value,
|
||||
isTitleDuplicateConfirmed: false,
|
||||
hasTitleDuplicate: false,
|
||||
});
|
||||
};
|
||||
|
||||
onDescriptionChange = (event) => {
|
||||
this.setState({
|
||||
description: event.target.value,
|
||||
});
|
||||
};
|
||||
|
||||
onCopyOnSaveChange = (event) => {
|
||||
this.setState({
|
||||
copyOnSave: event.target.checked,
|
||||
});
|
||||
}
|
||||
|
||||
onTimeRestoreChange = (event) => {
|
||||
this.setState({
|
||||
timeRestore: event.target.checked,
|
||||
});
|
||||
}
|
||||
|
||||
renderDuplicateTitleCallout = () => {
|
||||
if (!this.state.hasTitleDuplicate) {
|
||||
return;
|
||||
}
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<EuiCallOut
|
||||
title={`A Dashboard with the title '${this.state.title}' already exists.`}
|
||||
color="warning"
|
||||
data-test-subj="titleDupicateWarnMsg"
|
||||
>
|
||||
<p>
|
||||
Click <strong>Confirm Save</strong> to save the dashboard with the duplicate title.
|
||||
</p>
|
||||
</EuiCallOut>
|
||||
<EuiSpacer />
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
renderCopyOnSave = () => {
|
||||
if (!this.props.showCopyOnSave) {
|
||||
return;
|
||||
}
|
||||
|
||||
return (
|
||||
<EuiFormRow
|
||||
label="Save as a new dashboard"
|
||||
>
|
||||
<EuiSwitch
|
||||
data-test-subj="saveAsNewCheckbox"
|
||||
checked={this.state.copyOnSave}
|
||||
onChange={this.onCopyOnSaveChange}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<EuiOverlayMask>
|
||||
<EuiModal
|
||||
data-test-subj="dashboardSaveModal"
|
||||
className="dashboardModal"
|
||||
onClose={this.props.onClose}
|
||||
>
|
||||
<EuiModalHeader>
|
||||
<EuiModalHeaderTitle>
|
||||
Save Dashboard
|
||||
</EuiModalHeaderTitle>
|
||||
</EuiModalHeader>
|
||||
|
||||
<EuiModalBody>
|
||||
|
||||
{this.renderDuplicateTitleCallout()}
|
||||
|
||||
<EuiForm>
|
||||
|
||||
{this.renderCopyOnSave()}
|
||||
|
||||
<EuiFormRow
|
||||
label="Title"
|
||||
>
|
||||
<EuiFieldText
|
||||
autoFocus
|
||||
data-test-subj="dashboardTitle"
|
||||
value={this.state.title}
|
||||
onChange={this.onTitleChange}
|
||||
isInvalid={this.state.hasTitleDuplicate}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
|
||||
<EuiFormRow
|
||||
label="Description"
|
||||
>
|
||||
<EuiTextArea
|
||||
data-test-subj="dashboardDescription"
|
||||
value={this.state.description}
|
||||
onChange={this.onDescriptionChange}
|
||||
compressed
|
||||
/>
|
||||
</EuiFormRow>
|
||||
|
||||
<EuiFormRow
|
||||
label="Store time with dashboard"
|
||||
helpText="This changes the time filter to the currently selected time each time this dashboard is loaded."
|
||||
>
|
||||
<EuiSwitch
|
||||
data-test-subj="storeTimeWithDashboard"
|
||||
checked={this.state.timeRestore}
|
||||
onChange={this.onTimeRestoreChange}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
|
||||
</EuiForm>
|
||||
|
||||
</EuiModalBody>
|
||||
|
||||
<EuiModalFooter>
|
||||
<EuiButton
|
||||
data-test-subj="saveCancelButton"
|
||||
onClick={this.props.onClose}
|
||||
>
|
||||
Cancel
|
||||
</EuiButton>
|
||||
|
||||
<EuiButton
|
||||
fill
|
||||
data-test-subj="confirmSaveDashboardButton"
|
||||
onClick={this.saveDashboard}
|
||||
isLoading={this.state.isLoading}
|
||||
>
|
||||
Confirm Save
|
||||
</EuiButton>
|
||||
</EuiModalFooter>
|
||||
</EuiModal>
|
||||
</EuiOverlayMask>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
DashboardSaveModal.propTypes = {
|
||||
onSave: PropTypes.func.isRequired,
|
||||
onClose: PropTypes.func.isRequired,
|
||||
title: PropTypes.string.isRequired,
|
||||
description: PropTypes.string.isRequired,
|
||||
timeRestore: PropTypes.bool.isRequired,
|
||||
showCopyOnSave: PropTypes.bool.isRequired,
|
||||
};
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* 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 { shallow } from 'enzyme';
|
||||
|
||||
import {
|
||||
DashboardSaveModal,
|
||||
} from './save_modal';
|
||||
|
||||
test('renders DashboardSaveModal', () => {
|
||||
const component = shallow(<DashboardSaveModal
|
||||
onSave={() => {}}
|
||||
onClose={() => {}}
|
||||
title="dash title"
|
||||
description="dash description"
|
||||
timeRestore={true}
|
||||
showCopyOnSave={true}
|
||||
/>);
|
||||
expect(component).toMatchSnapshot(); // eslint-disable-line
|
||||
});
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* 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 { DashboardSaveModal } from './save_modal';
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
|
||||
export function showSaveModal({ onSave, title, description, timeRestore, showCopyOnSave }) {
|
||||
const container = document.createElement('div');
|
||||
const closeModal = () => {
|
||||
ReactDOM.unmountComponentAtNode(container);
|
||||
document.body.removeChild(container);
|
||||
};
|
||||
|
||||
const onSaveConfirmed = (...args) => {
|
||||
onSave(...args).then(id => {
|
||||
if (id) {
|
||||
closeModal();
|
||||
}
|
||||
});
|
||||
};
|
||||
document.body.appendChild(container);
|
||||
const element = (
|
||||
<DashboardSaveModal
|
||||
onSave={onSaveConfirmed}
|
||||
onClose={closeModal}
|
||||
title={title}
|
||||
description={description}
|
||||
timeRestore={timeRestore}
|
||||
showCopyOnSave={showCopyOnSave}
|
||||
/>
|
||||
);
|
||||
ReactDOM.render(element, container);
|
||||
}
|
|
@ -61,7 +61,7 @@ export default function ({ getService, getPageObjects }) {
|
|||
|
||||
it('and warns on duplicate name', async function () {
|
||||
await PageObjects.dashboard.confirmClone();
|
||||
const isWarningDisplayed = await PageObjects.dashboard.isCloneDuplicateTitleWarningDisplayed();
|
||||
const isWarningDisplayed = await PageObjects.dashboard.isDuplicateTitleWarningDisplayed();
|
||||
expect(isWarningDisplayed).to.equal(true);
|
||||
});
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ import expect from 'expect.js';
|
|||
|
||||
export default function ({ getService, getPageObjects }) {
|
||||
const retry = getService('retry');
|
||||
const PageObjects = getPageObjects(['dashboard', 'header', 'common']);
|
||||
const PageObjects = getPageObjects(['dashboard', 'header']);
|
||||
|
||||
describe('dashboard save', function describeIndexTests() {
|
||||
const dashboardName = 'Dashboard Save Test';
|
||||
|
@ -38,19 +38,19 @@ export default function ({ getService, getPageObjects }) {
|
|||
await PageObjects.dashboard.clickNewDashboard();
|
||||
await PageObjects.dashboard.saveDashboard(dashboardName);
|
||||
|
||||
let isConfirmOpen = await PageObjects.common.isConfirmModalOpen();
|
||||
expect(isConfirmOpen).to.equal(false);
|
||||
let isWarningDisplayed = await PageObjects.dashboard.isDuplicateTitleWarningDisplayed();
|
||||
expect(isWarningDisplayed).to.equal(false);
|
||||
|
||||
await PageObjects.dashboard.gotoDashboardLandingPage();
|
||||
await PageObjects.dashboard.clickNewDashboard();
|
||||
await PageObjects.dashboard.enterDashboardTitleAndClickSave(dashboardName);
|
||||
|
||||
isConfirmOpen = await PageObjects.common.isConfirmModalOpen();
|
||||
expect(isConfirmOpen).to.equal(true);
|
||||
isWarningDisplayed = await PageObjects.dashboard.isDuplicateTitleWarningDisplayed();
|
||||
expect(isWarningDisplayed).to.equal(true);
|
||||
});
|
||||
|
||||
it('does not save on reject confirmation', async function () {
|
||||
await PageObjects.common.clickCancelOnModal();
|
||||
await PageObjects.dashboard.cancelSave();
|
||||
|
||||
const countOfDashboards = await PageObjects.dashboard.getDashboardCountWithName(dashboardName);
|
||||
expect(countOfDashboards).to.equal(1);
|
||||
|
@ -61,7 +61,7 @@ export default function ({ getService, getPageObjects }) {
|
|||
await PageObjects.dashboard.clickNewDashboard();
|
||||
await PageObjects.dashboard.enterDashboardTitleAndClickSave(dashboardName);
|
||||
|
||||
await PageObjects.common.clickConfirmOnModal();
|
||||
await PageObjects.dashboard.clickSave();
|
||||
|
||||
// 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
|
||||
|
@ -79,8 +79,8 @@ export default function ({ getService, getPageObjects }) {
|
|||
await PageObjects.dashboard.clickEdit();
|
||||
await PageObjects.dashboard.saveDashboard(dashboardName);
|
||||
|
||||
const isConfirmOpen = await PageObjects.common.isConfirmModalOpen();
|
||||
expect(isConfirmOpen).to.equal(false);
|
||||
const isWarningDisplayed = await PageObjects.dashboard.isDuplicateTitleWarningDisplayed();
|
||||
expect(isWarningDisplayed).to.equal(false);
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -88,30 +88,30 @@ export default function ({ getService, getPageObjects }) {
|
|||
await PageObjects.dashboard.clickEdit();
|
||||
await PageObjects.dashboard.enterDashboardTitleAndClickSave(dashboardName, { saveAsNew: true });
|
||||
|
||||
const isConfirmOpen = await PageObjects.common.isConfirmModalOpen();
|
||||
expect(isConfirmOpen).to.equal(true);
|
||||
const isWarningDisplayed = await PageObjects.dashboard.isDuplicateTitleWarningDisplayed();
|
||||
expect(isWarningDisplayed).to.equal(true);
|
||||
|
||||
await PageObjects.common.clickCancelOnModal();
|
||||
await PageObjects.dashboard.cancelSave();
|
||||
});
|
||||
|
||||
it('Does not warn when only the prefix matches', async function () {
|
||||
await PageObjects.dashboard.saveDashboard(dashboardName.split(' ')[0]);
|
||||
|
||||
const isConfirmOpen = await PageObjects.common.isConfirmModalOpen();
|
||||
expect(isConfirmOpen).to.equal(false);
|
||||
const isWarningDisplayed = await PageObjects.dashboard.isDuplicateTitleWarningDisplayed();
|
||||
expect(isWarningDisplayed).to.equal(false);
|
||||
});
|
||||
|
||||
it('Warns when case is different', async function () {
|
||||
await PageObjects.dashboard.clickEdit();
|
||||
await PageObjects.dashboard.enterDashboardTitleAndClickSave(dashboardName.toUpperCase());
|
||||
|
||||
// We expect isConfirmModalOpen to be open, hence the retry if not found.
|
||||
// We expect isWarningDisplayed to be open, hence the retry if not found.
|
||||
await retry.try(async () => {
|
||||
const isConfirmOpen = await PageObjects.common.isConfirmModalOpen();
|
||||
expect(isConfirmOpen).to.equal(true);
|
||||
const isWarningDisplayed = await PageObjects.dashboard.isDuplicateTitleWarningDisplayed();
|
||||
expect(isWarningDisplayed).to.equal(true);
|
||||
});
|
||||
|
||||
await PageObjects.common.clickCancelOnModal();
|
||||
await PageObjects.dashboard.cancelSave();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -167,8 +167,8 @@ export function DashboardPageProvider({ getService, getPageObjects }) {
|
|||
await testSubjects.setValue('clonedDashboardTitle', title);
|
||||
}
|
||||
|
||||
async isCloneDuplicateTitleWarningDisplayed() {
|
||||
return await testSubjects.exists('cloneModalTitleDupicateWarnMsg');
|
||||
async isDuplicateTitleWarningDisplayed() {
|
||||
return await testSubjects.exists('titleDupicateWarnMsg');
|
||||
}
|
||||
|
||||
async clickEdit() {
|
||||
|
@ -286,7 +286,7 @@ export function DashboardPageProvider({ getService, getPageObjects }) {
|
|||
await this.enterDashboardTitleAndClickSave(dashName, saveOptions);
|
||||
|
||||
if (saveOptions.needsConfirm) {
|
||||
await PageObjects.common.clickConfirmOnModal();
|
||||
await this.clickSave();
|
||||
}
|
||||
|
||||
await PageObjects.header.waitUntilLoadingHasFinished();
|
||||
|
@ -295,6 +295,18 @@ export function DashboardPageProvider({ getService, getPageObjects }) {
|
|||
return await testSubjects.exists('saveDashboardSuccess');
|
||||
}
|
||||
|
||||
async cancelSave() {
|
||||
log.debug('Canceling save');
|
||||
await testSubjects.click('saveCancelButton');
|
||||
}
|
||||
|
||||
async clickSave() {
|
||||
await retry.try(async () => {
|
||||
log.debug('clicking final Save button for named dashboard');
|
||||
return await testSubjects.click('confirmSaveDashboardButton');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param dashboardTitle {String}
|
||||
|
@ -316,10 +328,7 @@ export function DashboardPageProvider({ getService, getPageObjects }) {
|
|||
await this.setSaveAsNewCheckBox(saveOptions.saveAsNew);
|
||||
}
|
||||
|
||||
await retry.try(async () => {
|
||||
log.debug('clicking final Save button for named dashboard');
|
||||
return await testSubjects.click('confirmSaveDashboardButton');
|
||||
});
|
||||
await this.clickSave();
|
||||
}
|
||||
|
||||
async selectDashboard(dashName) {
|
||||
|
|
Loading…
Reference in a new issue