Skip to content

Commit

Permalink
Merge pull request #8082 from w33ble/fix/save-update-status
Browse files Browse the repository at this point in the history
Update appStatus when saving changed to savedObjects
  • Loading branch information
epixa authored Aug 26, 2016
2 parents a89947e + f21054c commit c68bf9d
Show file tree
Hide file tree
Showing 5 changed files with 89 additions and 27 deletions.
12 changes: 7 additions & 5 deletions src/plugins/kibana/public/dashboard/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ define(function (require) {
const angular = require('angular');
const ConfigTemplate = require('ui/ConfigTemplate');
const chrome = require('ui/chrome');
const stateMonitor = require('ui/state_management/state_monitor');
const stateMonitorFactory = require('ui/state_management/state_monitor_factory');

require('ui/directives/config');
require('ui/courier');
Expand Down Expand Up @@ -90,9 +90,10 @@ define(function (require) {
filters: _.reject(dash.searchSource.getOwn('filter'), matchQueryFilter),
};

let stateMonitor;
const $appStatus = this.appStatus = $scope.appStatus = {};
const $state = $scope.state = new AppState(stateDefaults);
const $uiState = $scope.uiState = $state.makeStateful('uiState');
const $appStatus = this.appStatus = $scope.appStatus = {};

$scope.$watchCollection('state.options', function (newVal, oldVal) {
if (!angular.equals(newVal, oldVal)) $state.save();
Expand Down Expand Up @@ -126,11 +127,11 @@ define(function (require) {
initPanelIndices();

// watch for state changes and update the appStatus.dirty value
const monitor = stateMonitor.create($state, stateDefaults);
monitor.onChange((status) => {
stateMonitor = stateMonitorFactory.create($state, stateDefaults);
stateMonitor.onChange((status) => {
$appStatus.dirty = status.dirty;
});
$scope.$on('$destroy', () => monitor.destroy());
$scope.$on('$destroy', () => stateMonitor.destroy());

$scope.$emit('application.load');
}
Expand Down Expand Up @@ -203,6 +204,7 @@ define(function (require) {

dash.save()
.then(function (id) {
stateMonitor.setInitialState($state.toJSON());
$scope.configTemplate.close('save');
if (id) {
notify.info('Saved Dashboard as "' + dash.title + '"');
Expand Down
10 changes: 6 additions & 4 deletions src/plugins/kibana/public/discover/controllers/discover.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ define(function (require) {
const rison = require('ui/utils/rison');

const dateMath = require('ui/utils/dateMath');
const stateMonitor = require('ui/state_management/state_monitor');
const stateMonitorFactory = require('ui/state_management/state_monitor_factory');

require('ui/doc_table');
require('ui/visualize');
Expand Down Expand Up @@ -119,6 +119,7 @@ define(function (require) {
docTitle.change(savedSearch.title);
}

let stateMonitor;
const $appStatus = $scope.appStatus = this.appStatus = {};
const $state = $scope.state = new AppState(getStateDefaults());
$scope.uiState = $state.makeStateful('uiState');
Expand Down Expand Up @@ -161,11 +162,11 @@ define(function (require) {
$scope.failuresShown = showTotal;
};

const monitor = stateMonitor.create($state, getStateDefaults());
monitor.onChange((status) => {
stateMonitor = stateMonitorFactory.create($state, getStateDefaults());
stateMonitor.onChange((status) => {
$appStatus.dirty = status.dirty;
});
$scope.$on('$destroy', () => monitor.destroy());
$scope.$on('$destroy', () => stateMonitor.destroy());

$scope.updateDataSource()
.then(function () {
Expand Down Expand Up @@ -292,6 +293,7 @@ define(function (require) {

return savedSearch.save()
.then(function (id) {
stateMonitor.setInitialState($state.toJSON());
$scope.configTemplate.close('save');

if (id) {
Expand Down
10 changes: 6 additions & 4 deletions src/plugins/kibana/public/visualize/editor/editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ define(function (require) {
require('plugins/kibana/visualize/editor/sidebar');
require('plugins/kibana/visualize/editor/agg_filter');

const stateMonitor = require('ui/state_management/state_monitor');
const stateMonitorFactory = require('ui/state_management/state_monitor_factory');
require('ui/navbar_extensions');
require('ui/visualize');
require('ui/collapsible_sidebar');
Expand Down Expand Up @@ -68,6 +68,7 @@ define(function (require) {
location: 'Visualization Editor'
});

let stateMonitor;
const $appStatus = this.appStatus = {};

const savedVis = $route.current.locals.savedVis;
Expand Down Expand Up @@ -132,11 +133,11 @@ define(function (require) {
$scope.conf = _.pick($scope, 'doSave', 'savedVis', 'shareData');
$scope.configTemplate = configTemplate;

const monitor = stateMonitor.create($state, stateDefaults);
monitor.ignoreProps([ 'vis.listeners' ]).onChange((status) => {
stateMonitor = stateMonitorFactory.create($state, stateDefaults);
stateMonitor.ignoreProps([ 'vis.listeners' ]).onChange((status) => {
$appStatus.dirty = status.dirty;
});
$scope.$on('$destroy', () => monitor.destroy());
$scope.$on('$destroy', () => stateMonitor.destroy());

editableVis.listeners.click = vis.listeners.click = filterBarClickHandler($state);
editableVis.listeners.brush = vis.listeners.brush = brushEvent;
Expand Down Expand Up @@ -242,6 +243,7 @@ define(function (require) {

savedVis.save()
.then(function (id) {
stateMonitor.setInitialState($state.toJSON());
configTemplate.close('save');

if (id) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import expect from 'expect.js';
import sinon from 'sinon';
import { cloneDeep } from 'lodash';
import stateMonitor from 'ui/state_management/state_monitor';
import stateMonitor from 'ui/state_management/state_monitor_factory';
import SimpleEmitter from 'ui/utils/SimpleEmitter';

describe('stateMonitor', function () {
describe('stateMonitorFactory', function () {
const noop = () => {};
const eventTypes = [
'save_with_changes',
Expand Down Expand Up @@ -135,6 +135,45 @@ describe('stateMonitor', function () {
});
});

describe('setInitialState', function () {
let changeStub;

beforeEach(() => {
changeStub = sinon.stub();
monitor.onChange(changeStub);
sinon.assert.notCalled(changeStub);
});

it('should throw if no state is provided', function () {
const fn = () => monitor.setInitialState();
expect(fn).to.throwException(/must be an object/);
});

it('should throw if given the wrong type', function () {
const fn = () => monitor.setInitialState([]);
expect(fn).to.throwException(/must be an object/);
});

it('should trigger the onChange handler', function () {
monitor.setInitialState({ new: 'state' });
sinon.assert.calledOnce(changeStub);
});

it('should change the status with differing state', function () {
monitor.setInitialState({ new: 'state' });
sinon.assert.calledOnce(changeStub);

const status = changeStub.firstCall.args[0];
expect(status).to.have.property('clean', false);
expect(status).to.have.property('dirty', true);
});

it('should not trigger the onChange handler without state change', function () {
monitor.setInitialState(cloneDeep(mockState.toJSON()));
sinon.assert.notCalled(changeStub);
});
});

describe('status object', function () {
let handlerFn;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,23 @@
import { cloneDeep, isEqual, set } from 'lodash';
import { cloneDeep, isEqual, set, isPlainObject } from 'lodash';

export default {
create: (state, defaultState) => stateMonitor(state, defaultState)
create: (state, customInitialState) => stateMonitor(state, customInitialState)
};

function stateMonitor(state, defaultState) {
function stateMonitor(state, customInitialState) {
let destroyed = false;
let ignoredProps = [];
let changeHandlers = [];
let initialState;

// state.toJSON returns a reference, clone so we can mutate it safely
const originalState = cloneDeep(defaultState) || cloneDeep(state.toJSON());
setInitialState(customInitialState);

function filterState(state) {
function setInitialState(customInitialState) {
// state.toJSON returns a reference, clone so we can mutate it safely
initialState = cloneDeep(customInitialState) || cloneDeep(state.toJSON());
}

function removeIgnoredProps(state) {
ignoredProps.forEach(path => {
set(state, path, true);
});
Expand All @@ -21,8 +26,8 @@ function stateMonitor(state, defaultState) {

function getStatus() {
// state.toJSON returns a reference, clone so we can mutate it safely
const currentState = filterState(cloneDeep(state.toJSON()));
const isClean = isEqual(currentState, originalState);
const currentState = removeIgnoredProps(cloneDeep(state.toJSON()));
const isClean = isEqual(currentState, initialState);

return {
clean: isClean,
Expand Down Expand Up @@ -50,9 +55,23 @@ function stateMonitor(state, defaultState) {
};

return {
setInitialState(customInitialState) {
if (!isPlainObject(customInitialState)) throw new TypeError('The default state must be an object');

// check the current status
const previousStatus = getStatus();

// update the initialState and apply ignoredProps
setInitialState(customInitialState);
removeIgnoredProps(initialState);

// fire the change handler if the status has changed
if (!isEqual(previousStatus, getStatus())) dispatchChange();
},

ignoreProps(props) {
ignoredProps = ignoredProps.concat(props);
filterState(originalState);
removeIgnoredProps(initialState);
return this;
},

Expand All @@ -69,9 +88,7 @@ function stateMonitor(state, defaultState) {

// if the state is already dirty, fire the change handler immediately
const status = getStatus();
if (status.dirty) {
dispatchChange();
}
if (status.dirty) dispatchChange();

return this;
},
Expand Down

0 comments on commit c68bf9d

Please sign in to comment.