diff --git a/src/plugins/dashboard/public/dashboard_api/unified_search_manager.ts b/src/plugins/dashboard/public/dashboard_api/unified_search_manager.ts index e1f1d0a4e71d3..1310f0104a5ea 100644 --- a/src/plugins/dashboard/public/dashboard_api/unified_search_manager.ts +++ b/src/plugins/dashboard/public/dashboard_api/unified_search_manager.ts @@ -39,8 +39,8 @@ import { connectToQueryState, syncGlobalQueryStateWithUrl, } from '@kbn/data-plugin/public'; -import { cleanFiltersForSerialize } from '@kbn/presentation-util-plugin/public'; import moment, { Moment } from 'moment'; +import { cleanFiltersForSerialize } from '../utils/clean_filters_for_serialize'; import { dataService } from '../services/kibana_services'; import { DashboardCreationOptions, DashboardState } from './types'; import { DEFAULT_DASHBOARD_INPUT, GLOBAL_STATE_STORAGE_KEY } from '../dashboard_constants'; diff --git a/src/plugins/dashboard/public/dashboard_container/types.ts b/src/plugins/dashboard/public/dashboard_container/types.ts index cf630902cbbd5..eac2a782f11c2 100644 --- a/src/plugins/dashboard/public/dashboard_container/types.ts +++ b/src/plugins/dashboard/public/dashboard_container/types.ts @@ -8,7 +8,6 @@ */ import type { ContainerOutput } from '@kbn/embeddable-plugin/public'; -import type { ReduxEmbeddableState } from '@kbn/presentation-util-plugin/public'; import { SerializableRecord } from '@kbn/utility-types'; import { ControlGroupRuntimeState } from '@kbn/controls-plugin/public'; @@ -19,11 +18,6 @@ export interface UnsavedPanelState { [key: string]: object | undefined; } -export type DashboardReduxState = ReduxEmbeddableState< - DashboardContainerInput, - DashboardContainerOutput ->; - export type DashboardRedirect = (props: RedirectToProps) => void; export type RedirectToProps = | { destination: 'dashboard'; id?: string; useReplace?: boolean; editMode?: boolean } diff --git a/src/plugins/dashboard/public/services/dashboard_content_management_service/lib/load_dashboard_state.ts b/src/plugins/dashboard/public/services/dashboard_content_management_service/lib/load_dashboard_state.ts index 2694411ed001a..25a3192ac82c3 100644 --- a/src/plugins/dashboard/public/services/dashboard_content_management_service/lib/load_dashboard_state.ts +++ b/src/plugins/dashboard/public/services/dashboard_content_management_service/lib/load_dashboard_state.ts @@ -14,8 +14,8 @@ import { injectSearchSourceReferences } from '@kbn/data-plugin/public'; import { ViewMode } from '@kbn/embeddable-plugin/public'; import { Filter, Query } from '@kbn/es-query'; import { SavedObjectNotFound } from '@kbn/kibana-utils-plugin/public'; -import { cleanFiltersForSerialize } from '@kbn/presentation-util-plugin/public'; +import { cleanFiltersForSerialize } from '../../../utils/clean_filters_for_serialize'; import { getDashboardContentManagementCache } from '..'; import { convertPanelsArrayToPanelMap, injectReferences } from '../../../../common'; import type { DashboardGetIn, DashboardGetOut } from '../../../../server/content_management'; diff --git a/src/plugins/dashboard/public/utils/clean_filters_for_serialize.test.ts b/src/plugins/dashboard/public/utils/clean_filters_for_serialize.test.ts new file mode 100644 index 0000000000000..9b0e05515d29b --- /dev/null +++ b/src/plugins/dashboard/public/utils/clean_filters_for_serialize.test.ts @@ -0,0 +1,38 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { Filter } from '@kbn/es-query'; +import { cleanFiltersForSerialize } from './clean_filters_for_serialize'; + +describe('cleanFiltersForSerialize', () => { + test('should return an empty array if filters is not provided', () => { + expect(cleanFiltersForSerialize()).toEqual([]); + }); + + test('should remove "meta.value" property from each filter', () => { + const filters: Filter[] = [ + { query: { a: 'a' }, meta: { value: 'value1' } }, + { query: { b: 'b' }, meta: { value: 'value2' } }, + ]; + + const cleanedFilters = cleanFiltersForSerialize(filters); + + expect(cleanedFilters[0]).toEqual({ query: { a: 'a' }, meta: {} }); + expect(cleanedFilters[1]).toEqual({ query: { b: 'b' }, meta: {} }); + }); + + test('should not fail if meta is missing from filters', () => { + const filters: Filter[] = [{ query: { a: 'a' } }, { query: { b: 'b' } }] as unknown as Filter[]; + + const cleanedFilters = cleanFiltersForSerialize(filters as unknown as Filter[]); + + expect(cleanedFilters[0]).toEqual({ query: { a: 'a' } }); + expect(cleanedFilters[1]).toEqual({ query: { b: 'b' } }); + }); +}); diff --git a/src/plugins/dashboard/public/utils/clean_filters_for_serialize.ts b/src/plugins/dashboard/public/utils/clean_filters_for_serialize.ts new file mode 100644 index 0000000000000..70f1d09eb97fd --- /dev/null +++ b/src/plugins/dashboard/public/utils/clean_filters_for_serialize.ts @@ -0,0 +1,18 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { Filter } from '@kbn/es-query'; + +export function cleanFiltersForSerialize(filters?: Filter[]): Filter[] { + if (!filters) return []; + return filters.map((filter) => { + if (filter.meta?.value) delete filter.meta.value; + return filter; + }); +} diff --git a/src/plugins/presentation_util/README.mdx b/src/plugins/presentation_util/README.mdx index 4a98d90b2de28..b83dc20a02543 100755 --- a/src/plugins/presentation_util/README.mdx +++ b/src/plugins/presentation_util/README.mdx @@ -8,51 +8,4 @@ tags: ['kibana', 'presentation', 'services'] related: [] --- -## Introduction - The Presentation Utility Plugin is a set of common, shared components and toolkits for solutions within the Presentation space, (e.g. Dashboards, Canvas). - -## Redux Embeddables - -The Redux Embeddables system allows embeddable authors to interact with their embeddables in a standardized way using Redux toolkit. This wrapper abstracts away store and slice creation, and embeddable input sync. To use this system, a developer can use CreateReduxEmbeddableTools in the constructor of their embeddable, supplying a collection of reducers. - -### Reducers - -The reducer object expected by the ReduxEmbeddableWrapper is the same type as the reducers expected by [Redux Toolkit's CreateSlice](https://redux-toolkit.js.org/api/createslice). - - -```ts -// my_embeddable_reducers.ts -import { MyEmbeddableInput } from './my_embeddable'; - -export const myEmbeddableReducers = { - setSpecialBoolean: ( - state: WritableDraft, - action: PayloadAction - ) => { - state.specialBoolean = action.payload; - } -} - -``` - - -### Accessing Actions and State - -From components under the embeddable, actions, containerActions, and the current state of the redux store are accessed via the embeddable instance. You can pass the embeddable instance down as a prop, or use a context. - - - ```ts - // my_embeddable_component.tsx - const MyEmbeddableComponent = ({ embeddableInstance }: { embeddableInstance: IEmbeddable }) => { - // current state - const specialBoolean = embeddableInstance.select((state) => state.specialBoolean); - - // change specialBoolean after 5 seconds - setTimeout(() => embeddableInstance.dispatch.setSpecialBoolean(false), 5000); - -} - -``` - -``` diff --git a/src/plugins/presentation_util/public/index.ts b/src/plugins/presentation_util/public/index.ts index 568a691482a72..76e7362c855ed 100644 --- a/src/plugins/presentation_util/public/index.ts +++ b/src/plugins/presentation_util/public/index.ts @@ -30,15 +30,6 @@ export { DEFAULT_DASHBOARD_DRILLDOWN_OPTIONS, } from './components'; -export { - lazyLoadReduxToolsPackage, - cleanFiltersForSerialize, - type ReduxEmbeddableState, - type ReduxEmbeddableTools, - type ReduxTools, - type ReduxToolsPackage, -} from './redux_tools'; - export type { ExpressionInputEditorRef, ExpressionInputProps, diff --git a/src/plugins/presentation_util/public/mocks.ts b/src/plugins/presentation_util/public/mocks.ts index e9eea6790d3df..cfe4bcaee12f5 100644 --- a/src/plugins/presentation_util/public/mocks.ts +++ b/src/plugins/presentation_util/public/mocks.ts @@ -8,9 +8,7 @@ */ import { PresentationUtilPluginStart } from './types'; -import { ReduxToolsPackage, registerExpressionsLanguage } from '.'; -import { createReduxEmbeddableTools } from './redux_tools/redux_embeddables/create_redux_embeddable_tools'; -import { createReduxTools } from './redux_tools/create_redux_tools'; +import { registerExpressionsLanguage } from '.'; import { setStubKibanaServices } from './services/mocks'; const createStartContract = (): PresentationUtilPluginStart => { @@ -31,14 +29,6 @@ export const presentationUtilPluginMock = { createStartContract, }; -/** - * A non async-imported version of the real redux embeddable tools package for mocking purposes. - */ -export const mockedReduxEmbeddablePackage: ReduxToolsPackage = { - createReduxEmbeddableTools, - createReduxTools, -}; - export * from './__stories__/fixtures/flights'; export const setMockedPresentationUtilServices = () => { setStubKibanaServices(); diff --git a/src/plugins/presentation_util/public/redux_tools/create_redux_tools.ts b/src/plugins/presentation_util/public/redux_tools/create_redux_tools.ts deleted file mode 100644 index b067df8daadd7..0000000000000 --- a/src/plugins/presentation_util/public/redux_tools/create_redux_tools.ts +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import { - AnyAction, - Middleware, - createSlice, - configureStore, - SliceCaseReducers, - CaseReducerActions, -} from '@reduxjs/toolkit'; -import { v4 as uuidv4 } from 'uuid'; -import { createContext } from 'react'; -import { createSelectorHook } from 'react-redux'; - -import { ReduxTools, ReduxToolsReducers, ReduxToolsSetters } from './types'; - -export const createReduxTools = < - ReduxStateType extends unknown, - ReducerType extends ReduxToolsReducers = ReduxToolsReducers ->({ - reducers, - additionalMiddleware, - initialState, -}: { - additionalMiddleware?: Array>; - initialState: ReduxStateType; - reducers: ReducerType; -}): ReduxTools => { - const id = uuidv4(); - - /** - * Create slice out of reducers and embeddable initial state. - */ - const slice = createSlice>({ - initialState, - name: id, - reducers, - }); - - const store = configureStore({ - reducer: slice.reducer, - middleware: (getDefaultMiddleware) => - getDefaultMiddleware().concat(...(additionalMiddleware ?? [])), - }); - - /** - * Create an object of setter functions by looping through the reducers, and creating a method that dispatches the related - * action to the appropriate store. - */ - const dispatch: ReduxToolsSetters = Object.keys(reducers).reduce( - (acc, key: keyof ReducerType) => { - const sliceAction = - slice.actions[key as keyof CaseReducerActions, string>]; - acc[key] = (payload) => store.dispatch(sliceAction(payload)); - return acc; - }, - {} as ReduxToolsSetters - ); - - /** - * Create a selector which can be used by react components to get the latest state values and to re-render when state changes. - */ - const select = createSelectorHook( - createContext({ - store, - storeState: store.getState(), - }) - ); - - return { - store, - select, - dispatch, - getState: store.getState, - onStateChange: store.subscribe, - }; -}; diff --git a/src/plugins/presentation_util/public/redux_tools/index.ts b/src/plugins/presentation_util/public/redux_tools/index.ts deleted file mode 100644 index 6c7f8c627babc..0000000000000 --- a/src/plugins/presentation_util/public/redux_tools/index.ts +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import { ReduxToolsPackage } from './types'; - -export type { ReduxEmbeddableState, ReduxEmbeddableTools } from './redux_embeddables/types'; -export { cleanFiltersForSerialize } from './redux_embeddables/clean_redux_embeddable_state'; -export type { ReduxToolsPackage, ReduxTools } from './types'; - -export const lazyLoadReduxToolsPackage = async (): Promise => { - const { createReduxTools } = await import('./create_redux_tools'); - const { createReduxEmbeddableTools } = await import( - './redux_embeddables/create_redux_embeddable_tools' - ); - return { - createReduxTools, - createReduxEmbeddableTools, - }; -}; diff --git a/src/plugins/presentation_util/public/redux_tools/redux_embeddables/clean_redux_embeddable_state.test.ts b/src/plugins/presentation_util/public/redux_tools/redux_embeddables/clean_redux_embeddable_state.test.ts deleted file mode 100644 index 2ecaf9144831e..0000000000000 --- a/src/plugins/presentation_util/public/redux_tools/redux_embeddables/clean_redux_embeddable_state.test.ts +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import { EmbeddableInput } from '@kbn/embeddable-plugin/public'; -import { Filter } from '@kbn/es-query'; -import { - cleanFiltersForSerialize, - cleanInputForRedux, - cleanStateForRedux, - stateContainsFilters, -} from './clean_redux_embeddable_state'; - -type InputWithFilters = Partial & { filters: Filter[] }; - -describe('stateContainsFilters', () => { - test('should return true if state contains filters', () => { - const explicitInput: InputWithFilters = { - id: 'wat', - filters: [{ query: {}, meta: {} }], - }; - - expect(stateContainsFilters(explicitInput)).toBe(true); - }); - - test('should return false if state does not contain filters', () => { - const explicitInput: EmbeddableInput = { - id: 'wat', - }; - - expect(stateContainsFilters(explicitInput)).toBe(false); - }); -}); - -describe('cleanFiltersForSerialize', () => { - test('should return an empty array if filters is not provided', () => { - expect(cleanFiltersForSerialize()).toEqual([]); - }); - - test('should remove "meta.value" property from each filter', () => { - const filters: Filter[] = [ - { query: { a: 'a' }, meta: { value: 'value1' } }, - { query: { b: 'b' }, meta: { value: 'value2' } }, - ]; - - const cleanedFilters = cleanFiltersForSerialize(filters); - - expect(cleanedFilters[0]).toEqual({ query: { a: 'a' }, meta: {} }); - expect(cleanedFilters[1]).toEqual({ query: { b: 'b' }, meta: {} }); - }); - - test('should not fail if meta is missing from filters', () => { - const filters: Filter[] = [{ query: { a: 'a' } }, { query: { b: 'b' } }] as unknown as Filter[]; - - const cleanedFilters = cleanFiltersForSerialize(filters as unknown as Filter[]); - - expect(cleanedFilters[0]).toEqual({ query: { a: 'a' } }); - expect(cleanedFilters[1]).toEqual({ query: { b: 'b' } }); - }); -}); - -describe('cleanInputForRedux', () => { - test('should clean filters to make explicit input serializable', () => { - const explicitInput = { - id: 'wat', - filters: [ - { query: { a: 'a' }, meta: { value: 'value1' } }, - { query: { b: 'b' }, meta: { value: 'value2' } }, - ], - }; - - const cleanedInput = cleanInputForRedux(explicitInput) as InputWithFilters; - - expect(cleanedInput.filters[0]).toEqual({ query: { a: 'a' }, meta: {} }); - expect(cleanedInput.filters[1]).toEqual({ query: { b: 'b' }, meta: {} }); - }); - - test('should not modify input if filters are not present', () => { - const explicitInput = { - id: 'wat', - otherProp: 'value', - }; - - const cleanedInput = cleanInputForRedux(explicitInput); - - expect(cleanedInput).toEqual(explicitInput); - }); -}); - -describe('cleanStateForRedux', () => { - test('should clean explicitInput for serializable state', () => { - const state = { - output: {}, - componentState: {}, - explicitInput: { - id: 'wat', - filters: [ - { query: { a: 'a' }, meta: { value: 'value1' } }, - { query: { b: 'b' }, meta: { value: 'value2' } }, - ], - }, - }; - - const cleanedState = cleanStateForRedux(state) as { explicitInput: InputWithFilters }; - - expect(cleanedState.explicitInput.filters[0]).toEqual({ query: { a: 'a' }, meta: {} }); - expect(cleanedState.explicitInput.filters[1]).toEqual({ query: { b: 'b' }, meta: {} }); - }); - - test('should not modify state if explicitInput filters are not present', () => { - const state = { - output: {}, - componentState: {}, - explicitInput: { - id: 'wat', - otherKey: 'value', - }, - }; - - const cleanedState = cleanStateForRedux(state); - - expect(cleanedState).toEqual(state); - }); -}); diff --git a/src/plugins/presentation_util/public/redux_tools/redux_embeddables/clean_redux_embeddable_state.ts b/src/plugins/presentation_util/public/redux_tools/redux_embeddables/clean_redux_embeddable_state.ts deleted file mode 100644 index 17c97e7edecba..0000000000000 --- a/src/plugins/presentation_util/public/redux_tools/redux_embeddables/clean_redux_embeddable_state.ts +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import { Filter } from '@kbn/es-query'; -import { EmbeddableInput } from '@kbn/embeddable-plugin/public'; - -import { ReduxEmbeddableState } from './types'; - -// TODO: Make filters serializable so we don't need special treatment for them. -type InputWithFilters = Partial & { filters: Filter[] }; -export const stateContainsFilters = ( - state: Partial -): state is InputWithFilters => { - if ((state as InputWithFilters).filters && (state as InputWithFilters).filters.length > 0) - return true; - return false; -}; - -export const cleanFiltersForSerialize = (filters?: Filter[]): Filter[] => { - if (!filters) return []; - return filters.map((filter) => { - if (filter.meta?.value) delete filter.meta.value; - return filter; - }); -}; - -export const cleanInputForRedux = < - ReduxEmbeddableStateType extends ReduxEmbeddableState = ReduxEmbeddableState ->( - explicitInput: ReduxEmbeddableStateType['explicitInput'] -) => { - if (stateContainsFilters(explicitInput)) { - explicitInput.filters = cleanFiltersForSerialize(explicitInput.filters); - } - return explicitInput; -}; - -export const cleanStateForRedux = < - ReduxEmbeddableStateType extends ReduxEmbeddableState = ReduxEmbeddableState ->( - state: ReduxEmbeddableStateType -) => { - // clean explicit input - state.explicitInput = cleanInputForRedux(state.explicitInput); - return state; -}; diff --git a/src/plugins/presentation_util/public/redux_tools/redux_embeddables/create_redux_embeddable_tools.ts b/src/plugins/presentation_util/public/redux_tools/redux_embeddables/create_redux_embeddable_tools.ts deleted file mode 100644 index 3cb0b2c24425c..0000000000000 --- a/src/plugins/presentation_util/public/redux_tools/redux_embeddables/create_redux_embeddable_tools.ts +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import { Draft, AnyAction, Middleware, PayloadAction } from '@reduxjs/toolkit'; - -import { Embeddable } from '@kbn/embeddable-plugin/public'; - -import { ReduxToolsReducers } from '../types'; -import { createReduxTools } from '../create_redux_tools'; -import { syncReduxEmbeddable } from './sync_redux_embeddable'; -import { cleanStateForRedux } from './clean_redux_embeddable_state'; -import { ReduxEmbeddableTools, ReduxEmbeddableState, ReduxEmbeddableSyncSettings } from './types'; - -export const createReduxEmbeddableTools = < - ReduxEmbeddableStateType extends ReduxEmbeddableState = ReduxEmbeddableState, - ReducerType extends ReduxToolsReducers = ReduxToolsReducers ->({ - reducers, - embeddable, - syncSettings, - additionalMiddleware, - initialComponentState, -}: { - embeddable: Embeddable< - ReduxEmbeddableStateType['explicitInput'], - ReduxEmbeddableStateType['output'] - >; - additionalMiddleware?: Array>; - initialComponentState?: ReduxEmbeddableStateType['componentState']; - syncSettings?: ReduxEmbeddableSyncSettings; - reducers: ReducerType; -}): ReduxEmbeddableTools => { - /** - * Build additional generic reducers to aid in embeddable syncing. - */ - const genericReducers = { - replaceEmbeddableReduxInput: ( - state: Draft, - action: PayloadAction - ) => { - state.explicitInput = action.payload; - }, - replaceEmbeddableReduxOutput: ( - state: Draft, - action: PayloadAction - ) => { - state.output = action.payload; - }, - }; - const allReducers = { ...reducers, ...genericReducers }; - - /** - * Create initial state from Embeddable. - */ - let initialState: ReduxEmbeddableStateType = { - output: embeddable.getOutput(), - componentState: initialComponentState ?? {}, - explicitInput: embeddable.getExplicitInput(), - } as ReduxEmbeddableStateType; - - initialState = cleanStateForRedux(initialState); - - const { dispatch, store, select, getState, onStateChange } = createReduxTools< - ReduxEmbeddableStateType, - typeof allReducers - >({ - reducers: allReducers, - additionalMiddleware, - initialState, - }); - - /** - * Sync redux state with embeddable input and output observables. Eventually we can replace the input and output observables - * with redux and remove this sync. - */ - const stopReduxEmbeddableSync = syncReduxEmbeddable({ - replaceEmbeddableReduxInput: dispatch.replaceEmbeddableReduxInput, - replaceEmbeddableReduxOutput: dispatch.replaceEmbeddableReduxOutput, - settings: syncSettings, - embeddable, - store, - }); - - return { - store, - select, - dispatch, - getState, - onStateChange, - cleanup: () => stopReduxEmbeddableSync?.(), - }; -}; diff --git a/src/plugins/presentation_util/public/redux_tools/redux_embeddables/sync_redux_embeddable.ts b/src/plugins/presentation_util/public/redux_tools/redux_embeddables/sync_redux_embeddable.ts deleted file mode 100644 index 4a6a7f177c525..0000000000000 --- a/src/plugins/presentation_util/public/redux_tools/redux_embeddables/sync_redux_embeddable.ts +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import deepEqual from 'fast-deep-equal'; -import { EnhancedStore } from '@reduxjs/toolkit'; - -import { IEmbeddable } from '@kbn/embeddable-plugin/public'; - -import { cleanInputForRedux } from './clean_redux_embeddable_state'; -import { ReduxEmbeddableState, ReduxEmbeddableSyncSettings } from './types'; - -type Writeable = { -readonly [P in keyof T]: T[P] }; - -export const syncReduxEmbeddable = < - ReduxEmbeddableStateType extends ReduxEmbeddableState = ReduxEmbeddableState ->({ - store, - settings, - embeddable, - replaceEmbeddableReduxInput, - replaceEmbeddableReduxOutput, -}: { - settings?: ReduxEmbeddableSyncSettings; - store: EnhancedStore; - replaceEmbeddableReduxInput: (input: ReduxEmbeddableStateType['explicitInput']) => void; - replaceEmbeddableReduxOutput: (output: ReduxEmbeddableStateType['output']) => void; - embeddable: IEmbeddable< - ReduxEmbeddableStateType['explicitInput'], - ReduxEmbeddableStateType['output'] - >; -}) => { - if (settings?.disableSync) { - return; - } - - let embeddableToReduxInProgress = false; - let reduxToEmbeddableInProgress = false; - - const { isInputEqual: inputEqualityCheck, isOutputEqual: outputEqualityCheck } = settings ?? {}; - const inputEqual = ( - inputA: Partial, - inputB: Partial - ) => (inputEqualityCheck ? inputEqualityCheck(inputA, inputB) : deepEqual(inputA, inputB)); - const outputEqual = ( - outputA: ReduxEmbeddableStateType['output'], - outputB: ReduxEmbeddableStateType['output'] - ) => (outputEqualityCheck ? outputEqualityCheck(outputA, outputB) : deepEqual(outputA, outputB)); - - // when the redux store changes, diff, and push updates to the embeddable input or to the output. - const unsubscribeFromStore = store.subscribe(() => { - if (embeddableToReduxInProgress) return; - reduxToEmbeddableInProgress = true; - const reduxState = store.getState(); - if (!inputEqual(reduxState.explicitInput, embeddable.getExplicitInput())) { - embeddable.updateInput(reduxState.explicitInput); - } - if (!outputEqual(reduxState.output, embeddable.getOutput())) { - // updating output is usually not accessible from outside of the embeddable. - // This redux sync utility is meant to be used from inside the embeddable, so we need to workaround the typescript error via casting. - ( - embeddable as unknown as { - updateOutput: (newOutput: ReduxEmbeddableStateType['output']) => void; - } - ).updateOutput(reduxState.output); - } - reduxToEmbeddableInProgress = false; - }); - - // when the embeddable input changes, diff and dispatch to the redux store - const inputSubscription = embeddable.getInput$().subscribe(() => { - if (reduxToEmbeddableInProgress) return; - embeddableToReduxInProgress = true; - const { explicitInput: reduxExplicitInput } = store.getState(); - - // store only explicit input in the store - const embeddableExplictInput = embeddable.getExplicitInput() as Writeable< - ReduxEmbeddableStateType['explicitInput'] - >; - - if (!inputEqual(reduxExplicitInput, embeddableExplictInput)) { - replaceEmbeddableReduxInput(cleanInputForRedux(embeddableExplictInput)); - } - embeddableToReduxInProgress = false; - }); - - // when the embeddable output changes, diff and dispatch to the redux store - const outputSubscription = embeddable.getOutput$().subscribe((embeddableOutput) => { - if (reduxToEmbeddableInProgress) return; - embeddableToReduxInProgress = true; - const reduxState = store.getState(); - if (!outputEqual(reduxState.output, embeddableOutput)) { - replaceEmbeddableReduxOutput(embeddableOutput); - } - embeddableToReduxInProgress = false; - }); - return () => { - unsubscribeFromStore(); - inputSubscription.unsubscribe(); - outputSubscription.unsubscribe(); - }; -}; diff --git a/src/plugins/presentation_util/public/redux_tools/redux_embeddables/types.ts b/src/plugins/presentation_util/public/redux_tools/redux_embeddables/types.ts deleted file mode 100644 index 89408af559311..0000000000000 --- a/src/plugins/presentation_util/public/redux_tools/redux_embeddables/types.ts +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import { EnhancedStore } from '@reduxjs/toolkit'; -import { EmbeddableInput, EmbeddableOutput } from '@kbn/embeddable-plugin/public'; -import { ReduxToolsReducers, ReduxToolsSelect, ReduxToolsSetters } from '../types'; - -export interface ReduxEmbeddableSyncSettings< - ReduxEmbeddableStateType extends ReduxEmbeddableState = ReduxEmbeddableState -> { - disableSync: boolean; - isInputEqual?: ( - a: Partial, - b: Partial - ) => boolean; - isOutputEqual?: ( - a: Partial, - b: Partial - ) => boolean; -} - -/** - * The return type from createReduxEmbeddableTools. Contains tools to get state, select state for react components, - * set state, and react to state changes. - */ -export interface ReduxEmbeddableTools< - ReduxEmbeddableStateType extends ReduxEmbeddableState = ReduxEmbeddableState, - ReducerType extends ReduxToolsReducers = ReduxToolsReducers -> { - cleanup: () => void; - store: EnhancedStore; - select: ReduxToolsSelect; - getState: EnhancedStore['getState']; - dispatch: ReduxToolsSetters; - onStateChange: EnhancedStore['subscribe']; -} - -/** - * The Embeddable Redux store should contain Input, Output and State. Input is serialized and used to create the embeddable, - * Output is used as a communication layer for state that the Embeddable creates, and State is used to store ephemeral state which needs - * to be communicated between an embeddable and its inner React components. - */ -export interface ReduxEmbeddableState< - InputType extends EmbeddableInput = EmbeddableInput, - OutputType extends EmbeddableOutput = EmbeddableOutput, - StateType extends unknown = unknown -> { - explicitInput: InputType; - output: OutputType; - componentState: StateType; -} diff --git a/src/plugins/presentation_util/public/redux_tools/types.ts b/src/plugins/presentation_util/public/redux_tools/types.ts deleted file mode 100644 index 7b2edc1579211..0000000000000 --- a/src/plugins/presentation_util/public/redux_tools/types.ts +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import { CaseReducer, PayloadAction, EnhancedStore } from '@reduxjs/toolkit'; - -/** - * The Redux Tools Selector is a react redux selector function that can be used to grab values from the state, and to make a component - * re-render when those values change. - */ -export type ReduxToolsSelect = ( - selector: (state: ReduxStateType) => Selected, - equalityFn?: ((previous: Selected, next: Selected) => boolean) | undefined -) => Selected; - -/** - * The Redux Embeddable Setters are a collection of functions which dispatch actions to the correct store. - */ -export type ReduxToolsSetters< - ReduxStateType extends unknown, - ReducerType extends ReduxToolsReducers = ReduxToolsReducers -> = { - [ReducerKey in keyof ReducerType]: ( - payload: Parameters[1]['payload'] - ) => void; -}; - -/** - * The return type from createReduxTools. Contains tools to get state, select state for react components, - * set state, and react to state changes. - */ -export interface ReduxTools< - ReduxStateType extends unknown, - ReducerType extends ReduxToolsReducers = ReduxToolsReducers -> { - store: EnhancedStore; - select: ReduxToolsSelect; - getState: EnhancedStore['getState']; - onStateChange: EnhancedStore['subscribe']; - dispatch: ReduxToolsSetters; -} - -/** - * The Redux Tools Reducers are the shape of the Raw reducers which will be passed into createSlice. These will be used to populate the actions - * object which the tools will return. - */ -export interface ReduxToolsReducers { - /** - * PayloadAction of type any is strategic here because we want to allow payloads of any shape in generic reducers. - * This type will be overridden to remove any and be type safe when returned by setupReduxEmbeddable. - */ - [key: string]: CaseReducer>; -} - -/** - * The package type is lazily exported from presentation_util and should contain all methods needed to use the redux embeddable tools. - */ -export interface ReduxToolsPackage { - createReduxTools: typeof import('./create_redux_tools')['createReduxTools']; - createReduxEmbeddableTools: typeof import('./redux_embeddables/create_redux_embeddable_tools')['createReduxEmbeddableTools']; -} diff --git a/src/plugins/presentation_util/tsconfig.json b/src/plugins/presentation_util/tsconfig.json index a794829d9ba52..45b258a2a3733 100644 --- a/src/plugins/presentation_util/tsconfig.json +++ b/src/plugins/presentation_util/tsconfig.json @@ -22,7 +22,6 @@ "@kbn/data-views-plugin", "@kbn/i18n-react", "@kbn/monaco", - "@kbn/es-query", "@kbn/field-formats-plugin", "@kbn/interpreter", "@kbn/react-field",