mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com> Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
This commit is contained in:
parent
37c656ab18
commit
80349f282d
32 changed files with 289 additions and 680 deletions
|
@ -40,7 +40,7 @@ import {
|
|||
import { buildTabularInspectorData } from './build_tabular_inspector_data';
|
||||
import { calculateObjectHash } from '../../../../visualizations/public';
|
||||
import { tabifyAggResponse } from '../../../../../core_plugins/data/public';
|
||||
import { PersistedState } from '../../../../../ui/public/persisted_state';
|
||||
import { PersistedState } from '../../../../../../plugins/visualizations/public';
|
||||
import { Adapters } from '../../../../../../plugins/inspector/public';
|
||||
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
|
||||
import { getQueryService, getIndexPatterns } from '../../../../../../plugins/data/public/services';
|
||||
|
|
|
@ -29,7 +29,6 @@ export { State } from 'ui/state_management/state';
|
|||
export { GlobalStateProvider } from 'ui/state_management/global_state';
|
||||
// @ts-ignore
|
||||
export { StateManagementConfigProvider } from 'ui/state_management/config_provider';
|
||||
export { PersistedState } from 'ui/persisted_state';
|
||||
|
||||
export { subscribeWithScope } from 'ui/utils/subscribe_with_scope';
|
||||
// @ts-ignore
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { PersistedState } from '../../../legacy_imports';
|
||||
import { PersistedState } from '../../../../../../../../plugins/visualizations/public';
|
||||
import { ReduxLikeStateContainer } from '../../../../../../../../plugins/kibana_utils/public';
|
||||
import { VisualizeAppState, VisualizeAppStateTransitions } from '../../types';
|
||||
|
||||
|
|
|
@ -19,9 +19,10 @@
|
|||
|
||||
import { TimeRange, Query, Filter, DataPublicPluginStart } from 'src/plugins/data/public';
|
||||
import { IEmbeddableStart } from 'src/plugins/embeddable/public';
|
||||
import { PersistedState } from 'src/plugins/visualizations/public';
|
||||
import { LegacyCoreStart } from 'kibana/public';
|
||||
import { Vis } from 'src/legacy/core_plugins/visualizations/public';
|
||||
import { VisSavedObject, PersistedState } from '../legacy_imports';
|
||||
import { VisSavedObject } from '../legacy_imports';
|
||||
|
||||
export type PureVisState = ReturnType<Vis['getCurrentState']>;
|
||||
|
||||
|
|
|
@ -21,13 +21,13 @@ import React, { useMemo, useState, useCallback, KeyboardEventHandler, useEffect
|
|||
import { get, isEqual } from 'lodash';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { keyCodes, EuiButtonIcon, EuiFlexGroup, EuiFlexItem, EuiTitle } from '@elastic/eui';
|
||||
|
||||
import { Vis } from 'src/legacy/core_plugins/visualizations/public';
|
||||
import { PersistedState, AggGroupNames } from '../../legacy_imports';
|
||||
import { AggGroupNames } from '../../legacy_imports';
|
||||
import { DefaultEditorNavBar, OptionTab } from './navbar';
|
||||
import { DefaultEditorControls } from './controls';
|
||||
import { setStateParamValue, useEditorReducer, useEditorFormState, discardChanges } from './state';
|
||||
import { DefaultEditorAggCommonProps } from '../agg_common_props';
|
||||
import { PersistedState } from '../../../../../../plugins/visualizations/public';
|
||||
|
||||
interface DefaultEditorSideBarProps {
|
||||
isCollapsed: boolean;
|
||||
|
|
|
@ -48,5 +48,4 @@ export { isValidJson, isValidInterval } from 'ui/agg_types';
|
|||
export { AggParamOption } from 'ui/agg_types';
|
||||
export { CidrMask } from 'ui/agg_types';
|
||||
|
||||
export { PersistedState } from 'ui/persisted_state';
|
||||
export * from 'ui/vis/lib';
|
||||
|
|
|
@ -17,7 +17,8 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { IAggConfigs, PersistedState } from './legacy_imports';
|
||||
import { PersistedState } from '../../../../plugins/visualizations/public';
|
||||
import { IAggConfigs } from './legacy_imports';
|
||||
import { Vis } from '../../visualizations/public';
|
||||
|
||||
export interface VisOptionsProps<VisParamType = unknown> {
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
export { PersistedState } from 'ui/persisted_state';
|
||||
// @ts-ignore
|
||||
export { defaultFeedbackMessage } from 'ui/vis/default_feedback_message';
|
||||
// @ts-ignore
|
||||
|
|
|
@ -24,10 +24,10 @@ import {
|
|||
KibanaContext,
|
||||
Render,
|
||||
} from '../../../../plugins/expressions/public';
|
||||
import { PersistedState } from '../../../../plugins/visualizations/public';
|
||||
|
||||
// @ts-ignore
|
||||
import { metricsRequestHandler } from './request_handler';
|
||||
import { PersistedState } from './legacy_imports';
|
||||
|
||||
type Input = KibanaContext | null;
|
||||
type Output = Promise<Render<RenderValue>>;
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
export { PersistedState } from '../../../ui/public/persisted_state';
|
||||
export {
|
||||
AggConfigs,
|
||||
IAggConfig,
|
||||
|
|
|
@ -19,8 +19,7 @@
|
|||
|
||||
import { get } from 'lodash';
|
||||
import React from 'react';
|
||||
|
||||
import { PersistedState } from '../../../legacy_imports';
|
||||
import { PersistedState } from '../../../../../../../plugins/visualizations/public';
|
||||
import { memoizeLast } from '../legacy/memoize';
|
||||
import { VisualizationChart } from './visualization_chart';
|
||||
import { VisualizationNoResults } from './visualization_noresults';
|
||||
|
|
|
@ -20,8 +20,7 @@
|
|||
import React from 'react';
|
||||
import * as Rx from 'rxjs';
|
||||
import { debounceTime, filter, share, switchMap } from 'rxjs/operators';
|
||||
|
||||
import { PersistedState } from '../../../legacy_imports';
|
||||
import { PersistedState } from '../../../../../../../plugins/visualizations/public';
|
||||
import { Vis, VisualizationController } from '../vis';
|
||||
import { getUpdateStatus } from '../legacy/update_status';
|
||||
import { ResizeChecker } from '../../../../../../../plugins/kibana_utils/public';
|
||||
|
|
|
@ -43,10 +43,10 @@ import {
|
|||
IExpressionLoaderParams,
|
||||
ExpressionsStart,
|
||||
} from '../../../../../../../plugins/expressions/public';
|
||||
import { PersistedState } from '../../../../../../../plugins/visualizations/public';
|
||||
import { buildPipeline } from '../legacy/build_pipeline';
|
||||
import { Vis } from '../vis';
|
||||
import { getExpressions, getUiActions } from '../services';
|
||||
import { PersistedState } from '../../../legacy_imports';
|
||||
import { VisSavedObject } from '../types';
|
||||
|
||||
const getKeys = <T extends {}>(o: T): Array<keyof T> => Object.keys(o) as Array<keyof T>;
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
|
||||
import { EventEmitter } from 'events';
|
||||
import _ from 'lodash';
|
||||
import { PersistedState } from '../../../legacy_imports';
|
||||
import { PersistedState } from '../../../../../../../plugins/visualizations/public';
|
||||
|
||||
import { getTypes } from '../services';
|
||||
|
||||
|
|
|
@ -19,12 +19,14 @@
|
|||
|
||||
import { get } from 'lodash';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { VisResponseValue } from '../../../../../../../plugins/visualizations/public';
|
||||
import {
|
||||
VisResponseValue,
|
||||
PersistedState,
|
||||
} from '../../../../../../../plugins/visualizations/public';
|
||||
import {
|
||||
ExpressionFunctionDefinition,
|
||||
Render,
|
||||
} from '../../../../../../../plugins/expressions/public';
|
||||
import { PersistedState } from '../../../legacy_imports';
|
||||
import { getTypes, getIndexPatterns, getFilterManager } from '../services';
|
||||
|
||||
interface Arguments {
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { PersistedState } from '../../../legacy_imports';
|
||||
import { PersistedState } from '../../../../../../../plugins/visualizations/public';
|
||||
import { calculateObjectHash } from './calculate_object_hash';
|
||||
import { Vis } from '../vis';
|
||||
|
||||
|
|
|
@ -29,7 +29,8 @@
|
|||
|
||||
import { EventEmitter } from 'events';
|
||||
import _ from 'lodash';
|
||||
import { AggConfigs, PersistedState } from '../../legacy_imports';
|
||||
import { PersistedState } from '../../../../../../../src/plugins/visualizations/public';
|
||||
import { AggConfigs } from '../../legacy_imports';
|
||||
import { updateVisualizationConfig } from './legacy/vis_update';
|
||||
import { getTypes } from './services';
|
||||
|
||||
|
|
|
@ -20,19 +20,19 @@
|
|||
/**
|
||||
* @name Events
|
||||
*
|
||||
* @extends SimpleEmitter
|
||||
* @extends EventEmitter
|
||||
*/
|
||||
|
||||
import _ from 'lodash';
|
||||
import { EventEmitter } from 'events';
|
||||
import { fatalError } from './notify';
|
||||
import { SimpleEmitter } from './utils/simple_emitter';
|
||||
import { createLegacyClass } from './utils/legacy_class';
|
||||
import { createDefer } from 'ui/promises';
|
||||
|
||||
const location = 'EventEmitter';
|
||||
|
||||
export function EventsProvider(Promise) {
|
||||
createLegacyClass(Events).inherits(SimpleEmitter);
|
||||
createLegacyClass(Events).inherits(EventEmitter);
|
||||
function Events() {
|
||||
Events.Super.call(this);
|
||||
this._listeners = {};
|
||||
|
@ -79,6 +79,7 @@ export function EventsProvider(Promise) {
|
|||
*/
|
||||
Events.prototype.off = function(name, handler) {
|
||||
if (!name && !handler) {
|
||||
this._listeners = {};
|
||||
return this.removeAllListeners();
|
||||
}
|
||||
|
||||
|
|
|
@ -1,26 +0,0 @@
|
|||
/*
|
||||
* 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 { KbnError } from '../../../../plugins/kibana_utils/public';
|
||||
|
||||
export class PersistedStateError extends KbnError {
|
||||
constructor() {
|
||||
super('Error with the persisted state');
|
||||
}
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export { PersistedState } from './persisted_state';
|
|
@ -1,29 +0,0 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
// It's currenty hard to properly type PersistedState, since it dynamically
|
||||
// inherits the class passed into the constructor. These typings are really pretty bad
|
||||
// but needed in the short term to make incremental progress elsewhere. Can't even
|
||||
// just use `any` since then typescript complains about using PersistedState as a
|
||||
// constructor.
|
||||
export class PersistedState {
|
||||
constructor(value?: any, path?: any, EmitterClass?: any);
|
||||
// method you want typed so far
|
||||
[prop: string]: any;
|
||||
}
|
|
@ -1,253 +0,0 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @name PersistedState
|
||||
*
|
||||
* @extends Events
|
||||
*/
|
||||
|
||||
import _ from 'lodash';
|
||||
import toPath from 'lodash/internal/toPath';
|
||||
import { PersistedStateError } from './errors';
|
||||
import { SimpleEmitter } from '../utils/simple_emitter';
|
||||
|
||||
function prepSetParams(key, value, path) {
|
||||
// key must be the value, set the entire state using it
|
||||
if (_.isUndefined(value) && (_.isPlainObject(key) || path.length > 0)) {
|
||||
// setting entire tree, swap the key and value to write to the state
|
||||
value = key;
|
||||
key = undefined;
|
||||
}
|
||||
|
||||
// ensure the value being passed in is never mutated
|
||||
return {
|
||||
value: _.cloneDeep(value),
|
||||
key: key,
|
||||
};
|
||||
}
|
||||
|
||||
export class PersistedState {
|
||||
/**
|
||||
*
|
||||
* @param value
|
||||
* @param path
|
||||
* @param EmitterClass {SimpleEmitter} - a SimpleEmitter class that this class will extend. Can be used to
|
||||
* inherit a custom event emitter. For example, the EventEmitter is an "angular-ized" version
|
||||
* for angular components which automatically triggers a digest loop for every registered
|
||||
* handler. TODO: replace angularized SimpleEmitter and force angular callers to handle digest loops manually ala
|
||||
* https://github.com/elastic/kibana/issues/13855
|
||||
*/
|
||||
constructor(value, path, EmitterClass = SimpleEmitter) {
|
||||
EmitterClass.call(this);
|
||||
|
||||
this._path = this._setPath(path);
|
||||
|
||||
_.forOwn(EmitterClass.prototype, (method, methodName) => {
|
||||
this[methodName] = function() {
|
||||
return EmitterClass.prototype[methodName].apply(this, arguments);
|
||||
};
|
||||
});
|
||||
|
||||
// Some validations
|
||||
if (!this._path.length && value && !_.isPlainObject(value)) {
|
||||
throw new PersistedStateError('State value must be a plain object');
|
||||
}
|
||||
|
||||
value = value || this._getDefault();
|
||||
|
||||
// copy passed state values and create internal trackers
|
||||
this.set(value);
|
||||
this._initialized = true; // used to track state changes
|
||||
}
|
||||
|
||||
get(key, def) {
|
||||
return _.cloneDeep(this._get(key, def));
|
||||
}
|
||||
|
||||
set(key, value) {
|
||||
const params = prepSetParams(key, value, this._path);
|
||||
const val = this._set(params.key, params.value);
|
||||
this.emit('set');
|
||||
return val;
|
||||
}
|
||||
|
||||
setSilent(key, value) {
|
||||
const params = prepSetParams(key, value, this._path);
|
||||
return this._set(params.key, params.value, true);
|
||||
}
|
||||
|
||||
clearAllKeys() {
|
||||
Object.getOwnPropertyNames(this._changedState).forEach(key => {
|
||||
this.set(key, null);
|
||||
});
|
||||
}
|
||||
|
||||
reset(path) {
|
||||
const keyPath = this._getIndex(path);
|
||||
const origValue = _.get(this._defaultState, keyPath);
|
||||
const currentValue = _.get(this._mergedState, keyPath);
|
||||
|
||||
if (_.isUndefined(origValue)) {
|
||||
this._cleanPath(path, this._mergedState);
|
||||
} else {
|
||||
_.set(this._mergedState, keyPath, origValue);
|
||||
}
|
||||
|
||||
// clean up the changedState tree
|
||||
this._cleanPath(path, this._changedState);
|
||||
|
||||
if (!_.isEqual(currentValue, origValue)) this.emit('change');
|
||||
}
|
||||
|
||||
getChanges() {
|
||||
return _.cloneDeep(this._changedState);
|
||||
}
|
||||
|
||||
toJSON() {
|
||||
return this.get();
|
||||
}
|
||||
|
||||
toString() {
|
||||
return JSON.stringify(this.toJSON());
|
||||
}
|
||||
|
||||
fromString(input) {
|
||||
return this.set(JSON.parse(input));
|
||||
}
|
||||
|
||||
_getIndex(key) {
|
||||
if (_.isUndefined(key)) return this._path;
|
||||
return (this._path || []).concat(toPath(key));
|
||||
}
|
||||
|
||||
_getPartialIndex(key) {
|
||||
const keyPath = this._getIndex(key);
|
||||
return keyPath.slice(this._path.length);
|
||||
}
|
||||
|
||||
_cleanPath(path, stateTree) {
|
||||
const partialPath = this._getPartialIndex(path);
|
||||
let remove = true;
|
||||
|
||||
// recursively delete value tree, when no other keys exist
|
||||
while (partialPath.length > 0) {
|
||||
const lastKey = partialPath.splice(partialPath.length - 1, 1)[0];
|
||||
const statePath = this._path.concat(partialPath);
|
||||
const stateVal = statePath.length > 0 ? _.get(stateTree, statePath) : stateTree;
|
||||
|
||||
// if stateVal isn't an object, do nothing
|
||||
if (!_.isPlainObject(stateVal)) return;
|
||||
|
||||
if (remove) delete stateVal[lastKey];
|
||||
if (Object.keys(stateVal).length > 0) remove = false;
|
||||
}
|
||||
}
|
||||
|
||||
_getDefault() {
|
||||
return this._hasPath() ? undefined : {};
|
||||
}
|
||||
|
||||
_setPath(path) {
|
||||
const isString = _.isString(path);
|
||||
const isArray = Array.isArray(path);
|
||||
|
||||
if (!isString && !isArray) return [];
|
||||
return isString ? [this._getIndex(path)] : path;
|
||||
}
|
||||
|
||||
_hasPath() {
|
||||
return this._path.length > 0;
|
||||
}
|
||||
|
||||
_get(key, def) {
|
||||
// no path and no key, get the whole state
|
||||
if (!this._hasPath() && _.isUndefined(key)) {
|
||||
return this._mergedState;
|
||||
}
|
||||
|
||||
return _.get(this._mergedState, this._getIndex(key), def);
|
||||
}
|
||||
|
||||
_set(key, value, silent) {
|
||||
const self = this;
|
||||
let stateChanged = false;
|
||||
const initialState = !this._initialized;
|
||||
const keyPath = this._getIndex(key);
|
||||
const hasKeyPath = keyPath.length > 0;
|
||||
|
||||
// if this is the initial state value, save value as the default
|
||||
if (initialState) {
|
||||
this._changedState = {};
|
||||
if (!this._hasPath() && _.isUndefined(key)) this._defaultState = value;
|
||||
else this._defaultState = _.set({}, keyPath, value);
|
||||
}
|
||||
|
||||
if (!initialState) {
|
||||
// no path and no key, set the whole state
|
||||
if (!this._hasPath() && _.isUndefined(key)) {
|
||||
// compare changedState and new state, emit an event when different
|
||||
stateChanged = !_.isEqual(this._changedState, value);
|
||||
this._changedState = value;
|
||||
this._mergedState = _.cloneDeep(value);
|
||||
} else {
|
||||
// check for changes at path, emit an event when different
|
||||
const curVal = hasKeyPath ? this.get(keyPath) : this._mergedState;
|
||||
stateChanged = !_.isEqual(curVal, value);
|
||||
|
||||
// arrays are merge by index, not desired - ensure they are replaced
|
||||
if (Array.isArray(_.get(this._mergedState, keyPath))) {
|
||||
if (hasKeyPath) _.set(this._mergedState, keyPath, undefined);
|
||||
else this._mergedState = undefined;
|
||||
}
|
||||
|
||||
if (hasKeyPath) {
|
||||
_.set(this._changedState, keyPath, value);
|
||||
} else {
|
||||
this._changedState = _.isPlainObject(value) ? value : {};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// update the merged state value
|
||||
const targetObj = this._mergedState || _.cloneDeep(this._defaultState);
|
||||
const sourceObj = _.merge({}, this._changedState);
|
||||
|
||||
// handler arguments are (targetValue, sourceValue, key, target, source)
|
||||
const mergeMethod = function(targetValue, sourceValue, mergeKey) {
|
||||
// if not initial state, skip default merge method (ie. return value, see note below)
|
||||
if (!initialState && _.isEqual(keyPath, self._getIndex(mergeKey))) {
|
||||
// use the sourceValue or fall back to targetValue
|
||||
return !_.isUndefined(sourceValue) ? sourceValue : targetValue;
|
||||
}
|
||||
};
|
||||
|
||||
// If `mergeMethod` is provided it is invoked to produce the merged values of the
|
||||
// destination and source properties.
|
||||
// If `mergeMethod` returns `undefined` the default merging method is used
|
||||
this._mergedState = _.merge(targetObj, sourceObj, mergeMethod);
|
||||
|
||||
// sanity check; verify that there are actually changes
|
||||
if (_.isEqual(this._mergedState, this._defaultState)) this._changedState = {};
|
||||
|
||||
if (!silent && stateChanged) this.emit('change', key);
|
||||
|
||||
return this;
|
||||
}
|
||||
}
|
|
@ -19,9 +19,9 @@
|
|||
|
||||
import expect from '@kbn/expect';
|
||||
import sinon from 'sinon';
|
||||
import { EventEmitter } from 'events';
|
||||
import { cloneDeep } from 'lodash';
|
||||
import { stateMonitorFactory } from '../state_monitor_factory';
|
||||
import { SimpleEmitter } from '../../utils/simple_emitter';
|
||||
|
||||
describe('stateMonitorFactory', function() {
|
||||
const noop = () => {};
|
||||
|
@ -35,7 +35,7 @@ describe('stateMonitorFactory', function() {
|
|||
}
|
||||
|
||||
function createMockState(state = {}) {
|
||||
const mockState = new SimpleEmitter();
|
||||
const mockState = new EventEmitter();
|
||||
setState(mockState, state, false);
|
||||
return mockState;
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
|
||||
import { uiModules } from '../modules';
|
||||
import { StateProvider } from './state';
|
||||
import { PersistedState } from '../persisted_state';
|
||||
import { PersistedState } from '../../../../plugins/visualizations/public';
|
||||
import { createLegacyClass } from '../utils/legacy_class';
|
||||
|
||||
const urlParam = '_a';
|
||||
|
|
|
@ -1,141 +0,0 @@
|
|||
/*
|
||||
* 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 _ from 'lodash';
|
||||
|
||||
/**
|
||||
* Simple event emitter class used in the vislib. Calls
|
||||
* handlers synchronously and implements a chainable api
|
||||
*
|
||||
* @class
|
||||
*/
|
||||
export function SimpleEmitter() {
|
||||
this._listeners = {};
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an event handler
|
||||
*
|
||||
* @param {string} name
|
||||
* @param {function} handler
|
||||
* @return {SimpleEmitter} - this, for chaining
|
||||
*/
|
||||
SimpleEmitter.prototype.on = function(name, handler) {
|
||||
let handlers = this._listeners[name];
|
||||
if (!handlers) handlers = this._listeners[name] = [];
|
||||
|
||||
handlers.push(handler);
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove an event handler
|
||||
*
|
||||
* @param {string} name
|
||||
* @param {function} [handler] - optional handler to remove, if no handler is
|
||||
* passed then all are removed
|
||||
* @return {SimpleEmitter} - this, for chaining
|
||||
*/
|
||||
SimpleEmitter.prototype.off = function(name, handler) {
|
||||
if (!this._listeners[name]) {
|
||||
return this;
|
||||
}
|
||||
|
||||
// remove a specific handler
|
||||
if (handler) _.pull(this._listeners[name], handler);
|
||||
// or remove all listeners
|
||||
else this._listeners[name] = null;
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove all event listeners bound to this emitter.
|
||||
*
|
||||
* @return {SimpleEmitter} - this, for chaining
|
||||
*/
|
||||
SimpleEmitter.prototype.removeAllListeners = function() {
|
||||
this._listeners = {};
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Emit an event and all arguments to all listeners for an event name
|
||||
*
|
||||
* @param {string} name
|
||||
* @param {*} [arg...] - any number of arguments that will be applied to each handler
|
||||
* @return {SimpleEmitter} - this, for chaining
|
||||
*/
|
||||
SimpleEmitter.prototype.emit = _.restParam(function(name, args) {
|
||||
if (!this._listeners[name]) return this;
|
||||
const listeners = this.listeners(name);
|
||||
let i = -1;
|
||||
|
||||
while (++i < listeners.length) {
|
||||
listeners[i].apply(this, args);
|
||||
}
|
||||
|
||||
return this;
|
||||
});
|
||||
|
||||
/**
|
||||
* Get a list of the event names that currently have listeners
|
||||
*
|
||||
* @return {array[string]}
|
||||
*/
|
||||
SimpleEmitter.prototype.activeEvents = function() {
|
||||
return _.reduce(
|
||||
this._listeners,
|
||||
function(active, listeners, name) {
|
||||
return active.concat(_.size(listeners) ? name : []);
|
||||
},
|
||||
[]
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a list of the handler functions for a specific event
|
||||
*
|
||||
* @param {string} name
|
||||
* @return {array[function]}
|
||||
*/
|
||||
SimpleEmitter.prototype.listeners = function(name) {
|
||||
return this._listeners[name] ? this._listeners[name].slice(0) : [];
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the count of handlers for a specific event
|
||||
*
|
||||
* @param {string} [name] - optional event name to filter by
|
||||
* @return {number}
|
||||
*/
|
||||
SimpleEmitter.prototype.listenerCount = function(name) {
|
||||
if (name) {
|
||||
return _.size(this._listeners[name]);
|
||||
}
|
||||
|
||||
return _.reduce(
|
||||
this._listeners,
|
||||
function(count, handlers) {
|
||||
return count + _.size(handlers);
|
||||
},
|
||||
0
|
||||
);
|
||||
};
|
|
@ -1,176 +0,0 @@
|
|||
/*
|
||||
* 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 { SimpleEmitter } from './simple_emitter';
|
||||
import sinon from 'sinon';
|
||||
|
||||
describe('SimpleEmitter class', () => {
|
||||
let emitter;
|
||||
|
||||
beforeEach(() => {
|
||||
emitter = new SimpleEmitter();
|
||||
});
|
||||
|
||||
it('constructs an event emitter', () => {
|
||||
expect(emitter).toHaveProperty('on');
|
||||
expect(emitter).toHaveProperty('off');
|
||||
expect(emitter).toHaveProperty('emit');
|
||||
expect(emitter).toHaveProperty('listenerCount');
|
||||
expect(emitter).toHaveProperty('removeAllListeners');
|
||||
});
|
||||
|
||||
describe('#listenerCount', () => {
|
||||
it('counts all event listeners without any arg', () => {
|
||||
expect(emitter.listenerCount()).toBe(0);
|
||||
emitter.on('a', () => {});
|
||||
expect(emitter.listenerCount()).toBe(1);
|
||||
emitter.on('b', () => {});
|
||||
expect(emitter.listenerCount()).toBe(2);
|
||||
});
|
||||
|
||||
it('limits to the event that is passed in', () => {
|
||||
expect(emitter.listenerCount()).toBe(0);
|
||||
emitter.on('a', () => {});
|
||||
expect(emitter.listenerCount('a')).toBe(1);
|
||||
emitter.on('a', () => {});
|
||||
expect(emitter.listenerCount('a')).toBe(2);
|
||||
emitter.on('b', () => {});
|
||||
expect(emitter.listenerCount('a')).toBe(2);
|
||||
expect(emitter.listenerCount('b')).toBe(1);
|
||||
expect(emitter.listenerCount()).toBe(3);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#on', () => {
|
||||
it('registers a handler', () => {
|
||||
const handler = sinon.stub();
|
||||
emitter.on('a', handler);
|
||||
expect(emitter.listenerCount('a')).toBe(1);
|
||||
|
||||
expect(handler.callCount).toBe(0);
|
||||
emitter.emit('a');
|
||||
expect(handler.callCount).toBe(1);
|
||||
});
|
||||
|
||||
it('allows multiple event handlers for the same event', () => {
|
||||
emitter.on('a', () => {});
|
||||
emitter.on('a', () => {});
|
||||
expect(emitter.listenerCount('a')).toBe(2);
|
||||
});
|
||||
|
||||
it('allows the same function to be registered multiple times', () => {
|
||||
const handler = () => {};
|
||||
emitter.on('a', handler);
|
||||
expect(emitter.listenerCount()).toBe(1);
|
||||
emitter.on('a', handler);
|
||||
expect(emitter.listenerCount()).toBe(2);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#off', () => {
|
||||
it('removes a listener if it was registered', () => {
|
||||
const handler = sinon.stub();
|
||||
expect(emitter.listenerCount()).toBe(0);
|
||||
emitter.on('a', handler);
|
||||
expect(emitter.listenerCount('a')).toBe(1);
|
||||
emitter.off('a', handler);
|
||||
expect(emitter.listenerCount('a')).toBe(0);
|
||||
});
|
||||
|
||||
it('clears all listeners if no handler is passed', () => {
|
||||
emitter.on('a', () => {});
|
||||
emitter.on('a', () => {});
|
||||
expect(emitter.listenerCount()).toBe(2);
|
||||
emitter.off('a');
|
||||
expect(emitter.listenerCount()).toBe(0);
|
||||
});
|
||||
|
||||
it('does not mind if the listener is not registered', () => {
|
||||
emitter.off('a', () => {});
|
||||
});
|
||||
|
||||
it('does not mind if the event has no listeners', () => {
|
||||
emitter.off('a');
|
||||
});
|
||||
});
|
||||
|
||||
describe('#emit', () => {
|
||||
it('calls the handlers in the order they were defined', () => {
|
||||
let i = 0;
|
||||
const incr = () => ++i;
|
||||
const one = sinon.spy(incr);
|
||||
const two = sinon.spy(incr);
|
||||
const three = sinon.spy(incr);
|
||||
const four = sinon.spy(incr);
|
||||
|
||||
emitter
|
||||
.on('a', one)
|
||||
.on('a', two)
|
||||
.on('a', three)
|
||||
.on('a', four)
|
||||
.emit('a');
|
||||
|
||||
expect(one).toHaveProperty('callCount', 1);
|
||||
expect(one.returned(1)).toBeDefined();
|
||||
|
||||
expect(two).toHaveProperty('callCount', 1);
|
||||
expect(two.returned(2)).toBeDefined();
|
||||
|
||||
expect(three).toHaveProperty('callCount', 1);
|
||||
expect(three.returned(3)).toBeDefined();
|
||||
|
||||
expect(four).toHaveProperty('callCount', 1);
|
||||
expect(four.returned(4)).toBeDefined();
|
||||
});
|
||||
|
||||
it('always emits the handlers that were initially registered', () => {
|
||||
const destructive = sinon.spy(() => {
|
||||
emitter.removeAllListeners();
|
||||
expect(emitter.listenerCount()).toBe(0);
|
||||
});
|
||||
const stub = sinon.stub();
|
||||
|
||||
emitter
|
||||
.on('run', destructive)
|
||||
.on('run', stub)
|
||||
.emit('run');
|
||||
|
||||
expect(destructive).toHaveProperty('callCount', 1);
|
||||
expect(stub).toHaveProperty('callCount', 1);
|
||||
});
|
||||
|
||||
it('applies all arguments except the first', () => {
|
||||
emitter
|
||||
.on('a', (a, b, c) => {
|
||||
expect(a).toBe('foo');
|
||||
expect(b).toBe('bar');
|
||||
expect(c).toBe('baz');
|
||||
})
|
||||
.emit('a', 'foo', 'bar', 'baz');
|
||||
});
|
||||
|
||||
it('uses the SimpleEmitter as the this context', () => {
|
||||
emitter
|
||||
.on('a', function() {
|
||||
expect(this).toBe(emitter);
|
||||
})
|
||||
.emit('a');
|
||||
});
|
||||
});
|
||||
});
|
|
@ -21,7 +21,7 @@ export function registerListenEventListener($rootScope) {
|
|||
* Helper that registers an event listener, and removes that listener when
|
||||
* the $scope is destroyed.
|
||||
*
|
||||
* @param {SimpleEmitter} emitter - the event emitter to listen to
|
||||
* @param {EventEmitter} emitter - the event emitter to listen to
|
||||
* @param {string} eventName - the event name
|
||||
* @param {Function} handler - the event handler
|
||||
* @return {undefined}
|
||||
|
|
|
@ -27,3 +27,5 @@ export function plugin(initializerContext: PluginInitializerContext) {
|
|||
export { VisualizationsPublicPlugin as Plugin };
|
||||
export * from './plugin';
|
||||
export * from './types';
|
||||
|
||||
export { PersistedState } from './persisted_state';
|
||||
|
|
|
@ -0,0 +1,254 @@
|
|||
/*
|
||||
* 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 { EventEmitter } from 'events';
|
||||
|
||||
import { isPlainObject, cloneDeep, get, set, isEqual, isString, merge } from 'lodash';
|
||||
import toPath from 'lodash/internal/toPath';
|
||||
|
||||
function prepSetParams(key: PersistedStateKey, value: any, path: PersistedStatePath) {
|
||||
// key must be the value, set the entire state using it
|
||||
if (value === undefined && (isPlainObject(key) || path.length > 0)) {
|
||||
// setting entire tree, swap the key and value to write to the state
|
||||
return {
|
||||
value: key,
|
||||
key: undefined,
|
||||
};
|
||||
}
|
||||
|
||||
// ensure the value being passed in is never mutated
|
||||
return {
|
||||
value: cloneDeep(value),
|
||||
key,
|
||||
};
|
||||
}
|
||||
|
||||
type PersistedStateKey = string | string[] | undefined;
|
||||
type PersistedStatePath = string | string[];
|
||||
|
||||
export class PersistedState extends EventEmitter {
|
||||
private readonly _path: PersistedStatePath;
|
||||
private readonly _initialized: boolean;
|
||||
|
||||
private _changedState: any;
|
||||
private _defaultState: any;
|
||||
private _mergedState: any;
|
||||
|
||||
constructor(value?: any, path?: PersistedStatePath) {
|
||||
super();
|
||||
|
||||
this._path = this.setPath(path);
|
||||
|
||||
// Some validations
|
||||
if (!this._path.length && value && !isPlainObject(value)) {
|
||||
throw new Error('State value must be a plain object');
|
||||
}
|
||||
|
||||
value = value || this.getDefault();
|
||||
|
||||
// copy passed state values and create internal trackers
|
||||
this.set(value);
|
||||
this._initialized = true; // used to track state changes
|
||||
}
|
||||
|
||||
get(key?: PersistedStateKey, defaultValue?: any) {
|
||||
// no path and no key, get the whole state
|
||||
if (!this.hasPath() && key === undefined) {
|
||||
return this._mergedState;
|
||||
}
|
||||
|
||||
return cloneDeep(get(this._mergedState, this.getIndex(key || ''), defaultValue));
|
||||
}
|
||||
|
||||
set(key: PersistedStateKey | any, value?: any) {
|
||||
const params = prepSetParams(key, value, this._path);
|
||||
const val = this.setValue(params.key, params.value);
|
||||
|
||||
this.emit('set');
|
||||
return val;
|
||||
}
|
||||
|
||||
setSilent(key: PersistedStateKey | any, value?: any) {
|
||||
const params = prepSetParams(key, value, this._path);
|
||||
|
||||
if (params.key) {
|
||||
return this.setValue(params.key, params.value, true);
|
||||
}
|
||||
}
|
||||
|
||||
clearAllKeys() {
|
||||
Object.getOwnPropertyNames(this._changedState).forEach(key => {
|
||||
this.set(key, null);
|
||||
});
|
||||
}
|
||||
|
||||
reset(path: PersistedStatePath) {
|
||||
const keyPath = this.getIndex(path);
|
||||
const origValue = get(this._defaultState, keyPath);
|
||||
const currentValue = get(this._mergedState, keyPath);
|
||||
|
||||
if (origValue === undefined) {
|
||||
this.cleanPath(path, this._mergedState);
|
||||
} else {
|
||||
set(this._mergedState, keyPath, origValue);
|
||||
}
|
||||
|
||||
// clean up the changedState tree
|
||||
this.cleanPath(path, this._changedState);
|
||||
|
||||
if (!isEqual(currentValue, origValue)) this.emit('change');
|
||||
}
|
||||
|
||||
getChanges() {
|
||||
return cloneDeep(this._changedState);
|
||||
}
|
||||
|
||||
toJSON() {
|
||||
return this.get();
|
||||
}
|
||||
|
||||
toString() {
|
||||
return JSON.stringify(this.toJSON());
|
||||
}
|
||||
|
||||
fromString(input: string) {
|
||||
return this.set(JSON.parse(input));
|
||||
}
|
||||
|
||||
private getIndex(key: PersistedStateKey) {
|
||||
if (key === undefined) return this._path;
|
||||
|
||||
return [...(this._path || []), ...toPath(key)];
|
||||
}
|
||||
|
||||
private getPartialIndex(key: PersistedStateKey) {
|
||||
const keyPath = this.getIndex(key);
|
||||
|
||||
return keyPath.slice(this._path.length);
|
||||
}
|
||||
|
||||
private cleanPath(path: PersistedStatePath, stateTree: any) {
|
||||
const partialPath = this.getPartialIndex(path);
|
||||
let remove = true;
|
||||
|
||||
if (Array.isArray(partialPath)) {
|
||||
// recursively delete value tree, when no other keys exist
|
||||
while (partialPath.length > 0) {
|
||||
const lastKey = partialPath.splice(partialPath.length - 1, 1)[0];
|
||||
const statePath = [...this._path, partialPath];
|
||||
const stateVal = statePath.length > 0 ? get(stateTree, statePath) : stateTree;
|
||||
|
||||
// if stateVal isn't an object, do nothing
|
||||
if (!isPlainObject(stateVal)) return;
|
||||
|
||||
if (remove) delete stateVal[lastKey];
|
||||
if (Object.keys(stateVal).length > 0) remove = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private getDefault() {
|
||||
return this.hasPath() ? undefined : {};
|
||||
}
|
||||
|
||||
private setPath(path?: PersistedStatePath): string[] {
|
||||
if (Array.isArray(path)) {
|
||||
return path;
|
||||
}
|
||||
|
||||
if (isString(path)) {
|
||||
return [...this.getIndex(path)];
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
private hasPath() {
|
||||
return this._path.length > 0;
|
||||
}
|
||||
|
||||
private setValue(key: PersistedStateKey, value: any, silent: boolean = false) {
|
||||
const self = this;
|
||||
let stateChanged = false;
|
||||
const initialState = !this._initialized;
|
||||
const keyPath = this.getIndex(key);
|
||||
const hasKeyPath = keyPath.length > 0;
|
||||
|
||||
// if this is the initial state value, save value as the default
|
||||
if (initialState) {
|
||||
this._changedState = {};
|
||||
if (!this.hasPath() && key === undefined) this._defaultState = value;
|
||||
else this._defaultState = set({}, keyPath, value);
|
||||
}
|
||||
|
||||
if (!initialState) {
|
||||
// no path and no key, set the whole state
|
||||
if (!this.hasPath() && key === undefined) {
|
||||
// compare changedState and new state, emit an event when different
|
||||
stateChanged = !isEqual(this._changedState, value);
|
||||
this._changedState = value;
|
||||
this._mergedState = cloneDeep(value);
|
||||
} else {
|
||||
// check for changes at path, emit an event when different
|
||||
const curVal = hasKeyPath ? this.get(keyPath) : this._mergedState;
|
||||
stateChanged = !isEqual(curVal, value);
|
||||
|
||||
// arrays are merge by index, not desired - ensure they are replaced
|
||||
if (Array.isArray(get(this._mergedState, keyPath))) {
|
||||
if (hasKeyPath) {
|
||||
set(this._mergedState, keyPath, undefined);
|
||||
} else {
|
||||
this._mergedState = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
if (hasKeyPath) {
|
||||
set(this._changedState, keyPath, value);
|
||||
} else {
|
||||
this._changedState = isPlainObject(value) ? value : {};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// update the merged state value
|
||||
const targetObj = this._mergedState || cloneDeep(this._defaultState);
|
||||
const sourceObj = merge({}, this._changedState);
|
||||
|
||||
// handler arguments are (targetValue, sourceValue, key, target, source)
|
||||
const mergeMethod = function(targetValue: any, sourceValue: any, mergeKey: string) {
|
||||
// if not initial state, skip default merge method (ie. return value, see note below)
|
||||
if (!initialState && isEqual(keyPath, self.getIndex(mergeKey))) {
|
||||
// use the sourceValue or fall back to targetValue
|
||||
return sourceValue === undefined ? targetValue : sourceValue;
|
||||
}
|
||||
};
|
||||
|
||||
// If `mergeMethod` is provided it is invoked to produce the merged values of the
|
||||
// destination and source properties.
|
||||
// If `mergeMethod` returns `undefined` the default merging method is used
|
||||
this._mergedState = merge(targetObj, sourceObj, mergeMethod);
|
||||
|
||||
// sanity check; verify that there are actually changes
|
||||
if (isEqual(this._mergedState, this._defaultState)) this._changedState = {};
|
||||
|
||||
if (!silent && stateChanged) this.emit('change', key);
|
||||
|
||||
return this;
|
||||
}
|
||||
}
|
|
@ -17,7 +17,6 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { PersistedStateError } from './errors';
|
||||
import { PersistedState } from './persisted_state';
|
||||
|
||||
describe('Persisted State Provider', () => {
|
||||
|
@ -47,7 +46,7 @@ describe('Persisted State Provider', () => {
|
|||
});
|
||||
|
||||
test('should throw if given an invalid value', () => {
|
||||
expect(() => new PersistedState('bananas')).toThrow(PersistedStateError);
|
||||
expect(() => new PersistedState('bananas')).toThrow(Error);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -224,13 +223,13 @@ describe('Persisted State Provider', () => {
|
|||
describe('internal state tracking', () => {
|
||||
test('should be an empty object', () => {
|
||||
const persistedState = new PersistedState();
|
||||
expect(persistedState._defaultState).toEqual({});
|
||||
expect(persistedState).toHaveProperty('_defaultState', {});
|
||||
});
|
||||
|
||||
test('should store the default state value', () => {
|
||||
const val = { one: 1, two: 2 };
|
||||
const persistedState = new PersistedState(val);
|
||||
expect(persistedState._defaultState).toEqual(val);
|
||||
expect(persistedState).toHaveProperty('_defaultState', val);
|
||||
});
|
||||
|
||||
test('should keep track of changes', () => {
|
||||
|
@ -238,8 +237,8 @@ describe('Persisted State Provider', () => {
|
|||
const persistedState = new PersistedState(val);
|
||||
|
||||
persistedState.set('two', 22);
|
||||
expect(persistedState._defaultState).toEqual(val);
|
||||
expect(persistedState._changedState).toEqual({ two: 22 });
|
||||
expect(persistedState).toHaveProperty('_defaultState', val);
|
||||
expect(persistedState).toHaveProperty('_changedState', { two: 22 });
|
||||
});
|
||||
});
|
||||
|
|
@ -17,7 +17,6 @@ export { StateManagementConfigProvider } from 'ui/state_management/config_provid
|
|||
export { AppStateProvider } from 'ui/state_management/app_state';
|
||||
// @ts-ignore
|
||||
export { EventsProvider } from 'ui/events';
|
||||
export { PersistedState } from 'ui/persisted_state';
|
||||
// @ts-ignore
|
||||
export { KbnUrlProvider, RedirectWhenMissingProvider } from 'ui/url';
|
||||
export { registerTimefilterWithGlobalStateFactory } from 'ui/timefilter/setup_router';
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue