diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 74d2138a9404f..37b0763c5e96e 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -163,6 +163,7 @@ /src/plugins/input_control_vis/ @elastic/kibana-presentation /src/plugins/vis_type_markdown/ @elastic/kibana-presentation /src/plugins/presentation_util/ @elastic/kibana-presentation +/src/plugins/controls/ @elastic/kibana-presentation /test/functional/apps/dashboard/ @elastic/kibana-presentation /test/functional/apps/dashboard_elements/ @elastic/kibana-presentation /x-pack/plugins/canvas/ @elastic/kibana-presentation diff --git a/.i18nrc.json b/.i18nrc.json index 207e1778213bb..c348fc2c2b60c 100644 --- a/.i18nrc.json +++ b/.i18nrc.json @@ -9,6 +9,7 @@ "discover": "src/plugins/discover", "bfetch": "src/plugins/bfetch", "dashboard": "src/plugins/dashboard", + "controls": "src/plugins/controls", "data": "src/plugins/data", "dataViews": "src/plugins/data_views", "embeddableApi": "src/plugins/embeddable", diff --git a/docs/developer/plugin-list.asciidoc b/docs/developer/plugin-list.asciidoc index eb4fd6e30f304..f94b68fe9ab36 100644 --- a/docs/developer/plugin-list.asciidoc +++ b/docs/developer/plugin-list.asciidoc @@ -40,6 +40,10 @@ as uiSettings within the code. |Console provides the user with tools for storing and executing requests against Elasticsearch. +|{kib-repo}blob/{branch}/src/plugins/controls/README.mdx[controls] +|The Controls plugin contains Embeddables which can be used to add user-friendly interactivity to apps. + + |{kib-repo}blob/{branch}/src/plugins/custom_integrations/README.md[customIntegrations] |Register add-data cards diff --git a/packages/kbn-optimizer/limits.yml b/packages/kbn-optimizer/limits.yml index dccf2f2c14169..8865258b36d1b 100644 --- a/packages/kbn-optimizer/limits.yml +++ b/packages/kbn-optimizer/limits.yml @@ -75,7 +75,7 @@ pageLoadAssetSize: watcher: 43598 runtimeFields: 41752 stackAlerts: 29684 - presentationUtil: 84606 + presentationUtil: 58834 osquery: 107090 fileUpload: 25664 dataVisualizer: 27530 @@ -119,4 +119,5 @@ pageLoadAssetSize: visTypeHeatmap: 25340 screenshotting: 17017 expressionGauge: 25000 + controls: 34788 expressionPie: 26338 diff --git a/src/dev/storybook/aliases.ts b/src/dev/storybook/aliases.ts index d6f7fedccd2a2..5043312d0b25c 100644 --- a/src/dev/storybook/aliases.ts +++ b/src/dev/storybook/aliases.ts @@ -33,5 +33,6 @@ export const storybookAliases = { ui_actions_enhanced: 'x-pack/plugins/ui_actions_enhanced/.storybook', observability: 'x-pack/plugins/observability/.storybook', presentation: 'src/plugins/presentation_util/storybook', + controls: 'src/plugins/controls/storybook', lists: 'x-pack/plugins/lists/.storybook', }; diff --git a/src/plugins/controls/README.mdx b/src/plugins/controls/README.mdx new file mode 100644 index 0000000000000..46ba1ed3ba9e7 --- /dev/null +++ b/src/plugins/controls/README.mdx @@ -0,0 +1,25 @@ +--- +id: controls +slug: /kibana-dev-docs/controls +title: Controls Plugin +summary: Introduction to the Controls Plugin. +date: 2020-01-12 +tags: ['kibana', 'controls', 'dashboard'] +related: [] +--- + +## Introduction + +The Controls plugin contains Embeddables which can be used to add user-friendly interactivity to apps. + +## The Control Group + +The Control group is an embeddable container which provides the ability to add, remove, reorder, and edit multiple types of control embeddable. In any implementation, the control group embeddable should be the main point of contact between the application and the controls. The list of filters it sends to its output observable should be considered the final output of the current state of the controls, and can then be sent to other embeddables, or combined with filters from other sources. + +## Control Types + +Multiple types of controls can be registered to work with the Control Group. The current implementations are as follows: + +### Options List + +The options list is the most basic, and most used control type. It allows the dashboard author to specify a data view and field, and create a searchable dropdown. diff --git a/src/plugins/presentation_util/common/controls/control_group/control_group_persistable_state.ts b/src/plugins/controls/common/control_group/control_group_persistable_state.ts similarity index 96% rename from src/plugins/presentation_util/common/controls/control_group/control_group_persistable_state.ts rename to src/plugins/controls/common/control_group/control_group_persistable_state.ts index 2da488acdc436..0fd24bd234327 100644 --- a/src/plugins/presentation_util/common/controls/control_group/control_group_persistable_state.ts +++ b/src/plugins/controls/common/control_group/control_group_persistable_state.ts @@ -10,9 +10,9 @@ import { EmbeddableInput, EmbeddablePersistableStateService, EmbeddableStateWithType, -} from '../../../../embeddable/common/types'; +} from '../../../embeddable/common/types'; import { ControlGroupInput, ControlPanelState } from './types'; -import { SavedObjectReference } from '../../../../../core/types'; +import { SavedObjectReference } from '../../../../core/types'; type ControlGroupInputWithType = Partial & { type: string }; diff --git a/src/plugins/presentation_util/common/controls/control_group/types.ts b/src/plugins/controls/common/control_group/types.ts similarity index 91% rename from src/plugins/presentation_util/common/controls/control_group/types.ts rename to src/plugins/controls/common/control_group/types.ts index da1cec0391102..4e1bddc08143f 100644 --- a/src/plugins/presentation_util/common/controls/control_group/types.ts +++ b/src/plugins/controls/common/control_group/types.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { EmbeddableInput, PanelState } from '../../../../embeddable/common/types'; +import { EmbeddableInput, PanelState } from '../../../embeddable/common/types'; import { ControlInput, ControlStyle, ControlWidth } from '../types'; export const CONTROL_GROUP_TYPE = 'control_group'; diff --git a/src/plugins/presentation_util/common/controls/control_types/options_list/options_list_persistable_state.ts b/src/plugins/controls/common/control_types/options_list/options_list_persistable_state.ts similarity index 89% rename from src/plugins/presentation_util/common/controls/control_types/options_list/options_list_persistable_state.ts rename to src/plugins/controls/common/control_types/options_list/options_list_persistable_state.ts index 90390256325ae..a41eb788d71e2 100644 --- a/src/plugins/presentation_util/common/controls/control_types/options_list/options_list_persistable_state.ts +++ b/src/plugins/controls/common/control_types/options_list/options_list_persistable_state.ts @@ -9,10 +9,10 @@ import { EmbeddableStateWithType, EmbeddablePersistableStateService, -} from '../../../../../embeddable/common/types'; +} from '../../../../embeddable/common'; import { OptionsListEmbeddableInput } from './types'; -import { SavedObjectReference } from '../../../../../../core/types'; -import { DATA_VIEW_SAVED_OBJECT_TYPE } from '../../../../../data_views/common'; +import { SavedObjectReference } from '../../../../../core/types'; +import { DATA_VIEW_SAVED_OBJECT_TYPE } from '../../../../data_views/common'; type OptionsListInputWithType = Partial & { type: string }; const dataViewReferenceName = 'optionsListDataView'; diff --git a/src/plugins/presentation_util/common/controls/control_types/options_list/types.ts b/src/plugins/controls/common/control_types/options_list/types.ts similarity index 100% rename from src/plugins/presentation_util/common/controls/control_types/options_list/types.ts rename to src/plugins/controls/common/control_types/options_list/types.ts diff --git a/src/plugins/controls/common/index.ts b/src/plugins/controls/common/index.ts new file mode 100644 index 0000000000000..aa06259cf855e --- /dev/null +++ b/src/plugins/controls/common/index.ts @@ -0,0 +1,14 @@ +/* + * 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 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 or the Server + * Side Public License, v 1. + */ + +export type { ControlPanelState, ControlsPanels, ControlGroupInput } from './control_group/types'; +export type { OptionsListEmbeddableInput } from './control_types/options_list/types'; +export type { ControlWidth } from './types'; + +export { OPTIONS_LIST_CONTROL } from './control_types/options_list/types'; +export { CONTROL_GROUP_TYPE } from './control_group/types'; diff --git a/src/plugins/presentation_util/common/controls/types.ts b/src/plugins/controls/common/types.ts similarity index 87% rename from src/plugins/presentation_util/common/controls/types.ts rename to src/plugins/controls/common/types.ts index 288324e30b47c..ad03bb642f798 100644 --- a/src/plugins/presentation_util/common/controls/types.ts +++ b/src/plugins/controls/common/types.ts @@ -7,8 +7,8 @@ */ import { Filter, Query } from '@kbn/es-query'; -import { TimeRange } from '../../../data/common'; -import { EmbeddableInput } from '../../../embeddable/common/types'; +import { TimeRange } from '../../data/common'; +import { EmbeddableInput } from '../../embeddable/common/types'; export type ControlWidth = 'auto' | 'small' | 'medium' | 'large'; export type ControlStyle = 'twoLine' | 'oneLine'; diff --git a/src/plugins/controls/kibana.json b/src/plugins/controls/kibana.json new file mode 100644 index 0000000000000..20afd63505a73 --- /dev/null +++ b/src/plugins/controls/kibana.json @@ -0,0 +1,23 @@ +{ + "id": "controls", + "owner": { + "name": "Kibana Presentation", + "githubTeam": "kibana-presentation" + }, + "description": "The Controls Plugin contains embeddable components intended to create a simple query interface for end users, and a powerful editing suite that allows dashboard authors to build controls", + "version": "1.0.0", + "kibanaVersion": "kibana", + "server": true, + "ui": true, + "extraPublicDirs": ["common"], + "requiredPlugins": [ + "presentationUtil", + "savedObjects", + "kibanaReact", + "expressions", + "embeddable", + "dataViews", + "data" + ], + "optionalPlugins": [] +} diff --git a/src/plugins/presentation_util/public/components/controls/__stories__/controls.stories.tsx b/src/plugins/controls/public/__stories__/controls.stories.tsx similarity index 83% rename from src/plugins/presentation_util/public/components/controls/__stories__/controls.stories.tsx rename to src/plugins/controls/public/__stories__/controls.stories.tsx index e6fa5ef630904..ac181f2ab32dd 100644 --- a/src/plugins/presentation_util/public/components/controls/__stories__/controls.stories.tsx +++ b/src/plugins/controls/public/__stories__/controls.stories.tsx @@ -9,19 +9,28 @@ import { EuiFlexGroup, EuiFlexItem, EuiSwitch, EuiTextAlign } from '@elastic/eui'; import React, { useEffect, useMemo, useState, useCallback, FC } from 'react'; import useEffectOnce from 'react-use/lib/useEffectOnce'; - import uuid from 'uuid'; + +import { + getFlightOptionsAsync, + storybookFlightsDataView, +} from '../../../presentation_util/public/mocks'; +import { + ControlGroupContainerFactory, + OptionsListEmbeddableInput, + OPTIONS_LIST_CONTROL, +} from '../'; + +import { ViewMode } from '../../../embeddable/public'; +import { EmbeddablePersistableStateService } from '../../../embeddable/common'; + import { decorators } from './decorators'; import { ControlsPanels } from '../control_group/types'; -import { ViewMode } from '../../../../../embeddable/public'; -import { getFlightOptionsAsync, storybookFlightsDataView } from './fixtures/flights'; -import { pluginServices, registry } from '../../../services/storybook'; -import { OptionsListEmbeddableInput, OPTIONS_LIST_CONTROL } from '../../..'; -import { replaceValueSuggestionMethod } from '../../../services/storybook/data'; -import { injectStorybookDataView } from '../../../services/storybook/data_views'; +import { ControlGroupContainer } from '../control_group'; +import { pluginServices, registry } from '../services/storybook'; +import { replaceValueSuggestionMethod } from '../services/storybook/data'; +import { injectStorybookDataView } from '../services/storybook/data_views'; import { populateStorybookControlFactories } from './storybook_control_factories'; -import { EmbeddablePersistableStateService } from '../../../../../embeddable/common'; -import { ControlGroupContainerFactory } from '../control_group/embeddable/control_group_container_factory'; export default { title: 'Controls', @@ -29,8 +38,6 @@ export default { decorators, }; -type EmbeddableType = Awaited>; - injectStorybookDataView(storybookFlightsDataView); replaceValueSuggestionMethod(getFlightOptionsAsync); @@ -39,7 +46,7 @@ const ControlGroupStoryComponent: FC<{ edit?: boolean; }> = ({ panels, edit }) => { const embeddableRoot: React.RefObject = useMemo(() => React.createRef(), []); - const [embeddable, setEmbeddable] = useState(); + const [embeddable, setEmbeddable] = useState(); const [viewMode, setViewMode] = useState( edit === undefined || edit ? ViewMode.EDIT : ViewMode.VIEW ); diff --git a/src/plugins/presentation_util/public/components/controls/__stories__/decorators.tsx b/src/plugins/controls/public/__stories__/decorators.tsx similarity index 100% rename from src/plugins/presentation_util/public/components/controls/__stories__/decorators.tsx rename to src/plugins/controls/public/__stories__/decorators.tsx diff --git a/src/plugins/presentation_util/public/components/controls/__stories__/storybook_control_factories.ts b/src/plugins/controls/public/__stories__/storybook_control_factories.ts similarity index 83% rename from src/plugins/presentation_util/public/components/controls/__stories__/storybook_control_factories.ts rename to src/plugins/controls/public/__stories__/storybook_control_factories.ts index e4429c1d69b13..9809e90bd12fc 100644 --- a/src/plugins/presentation_util/public/components/controls/__stories__/storybook_control_factories.ts +++ b/src/plugins/controls/public/__stories__/storybook_control_factories.ts @@ -7,12 +7,10 @@ */ import { OptionsListEmbeddableFactory } from '../control_types/options_list'; -import { PresentationControlsService } from '../../../services/controls'; +import { ControlsService } from '../services/controls'; import { ControlFactory } from '..'; -export const populateStorybookControlFactories = ( - controlsServiceStub: PresentationControlsService -) => { +export const populateStorybookControlFactories = (controlsServiceStub: ControlsService) => { const optionsListFactoryStub = new OptionsListEmbeddableFactory(); // cast to unknown because the stub cannot use the embeddable start contract to transform the EmbeddableFactoryDefinition into an EmbeddableFactory diff --git a/src/plugins/presentation_util/public/components/controls/control_group/component/control_frame_component.tsx b/src/plugins/controls/public/control_group/component/control_frame_component.tsx similarity index 96% rename from src/plugins/presentation_util/public/components/controls/control_group/component/control_frame_component.tsx rename to src/plugins/controls/public/control_group/component/control_frame_component.tsx index bdc3b2978f888..e921cbd90d298 100644 --- a/src/plugins/presentation_util/public/components/controls/control_group/component/control_frame_component.tsx +++ b/src/plugins/controls/public/control_group/component/control_frame_component.tsx @@ -18,11 +18,11 @@ import { } from '@elastic/eui'; import { ControlGroupInput } from '../types'; +import { pluginServices } from '../../services'; import { EditControlButton } from '../editor/edit_control'; -import { useChildEmbeddable } from '../../hooks/use_child_embeddable'; -import { useReduxContainerContext } from '../../../redux_embeddables/redux_embeddable_context'; import { ControlGroupStrings } from '../control_group_strings'; -import { pluginServices } from '../../../../services'; +import { useChildEmbeddable } from '../../hooks/use_child_embeddable'; +import { useReduxContainerContext } from '../../../../presentation_util/public'; export interface ControlFrameProps { customPrepend?: JSX.Element; @@ -38,7 +38,7 @@ export const ControlFrame = ({ customPrepend, enableActions, embeddableId }: Con } = useReduxContainerContext(); const { controlStyle } = useEmbeddableSelector((state) => state); - // Presentation Services Context + // Controls Services Context const { overlays } = pluginServices.getHooks(); const { openConfirm } = overlays.useService(); diff --git a/src/plugins/presentation_util/public/components/controls/control_group/component/control_group_component.tsx b/src/plugins/controls/public/control_group/component/control_group_component.tsx similarity index 96% rename from src/plugins/presentation_util/public/components/controls/control_group/component/control_group_component.tsx rename to src/plugins/controls/public/control_group/component/control_group_component.tsx index 026f3154fe50e..a28807a36eb49 100644 --- a/src/plugins/presentation_util/public/components/controls/control_group/component/control_group_component.tsx +++ b/src/plugins/controls/public/control_group/component/control_group_component.tsx @@ -37,18 +37,18 @@ import { } from '@dnd-kit/core'; import { ControlGroupInput } from '../types'; -import { pluginServices } from '../../../../services'; +import { pluginServices } from '../../services'; +import { ViewMode } from '../../../../embeddable/public'; import { ControlGroupStrings } from '../control_group_strings'; import { CreateControlButton } from '../editor/create_control'; -import { ViewMode } from '../../../../../../embeddable/public'; import { EditControlGroup } from '../editor/edit_control_group'; import { forwardAllContext } from '../editor/forward_all_context'; import { controlGroupReducers } from '../state/control_group_reducers'; import { ControlClone, SortableControl } from './control_group_sortable_item'; -import { useReduxContainerContext } from '../../../redux_embeddables/redux_embeddable_context'; +import { useReduxContainerContext } from '../../../../presentation_util/public'; export const ControlGroup = () => { - // Presentation Services Context + // Controls Services Context const { overlays } = pluginServices.getHooks(); const { openFlyout } = overlays.useService(); diff --git a/src/plugins/presentation_util/public/components/controls/control_group/component/control_group_sortable_item.tsx b/src/plugins/controls/public/control_group/component/control_group_sortable_item.tsx similarity index 98% rename from src/plugins/presentation_util/public/components/controls/control_group/component/control_group_sortable_item.tsx rename to src/plugins/controls/public/control_group/component/control_group_sortable_item.tsx index f4c28e840556a..ecba29bb5f1db 100644 --- a/src/plugins/presentation_util/public/components/controls/control_group/component/control_group_sortable_item.tsx +++ b/src/plugins/controls/public/control_group/component/control_group_sortable_item.tsx @@ -14,7 +14,7 @@ import classNames from 'classnames'; import { ControlGroupInput } from '../types'; import { ControlFrame, ControlFrameProps } from './control_frame_component'; -import { useReduxContainerContext } from '../../../redux_embeddables/redux_embeddable_context'; +import { useReduxContainerContext } from '../../../../presentation_util/public'; interface DragInfo { isOver?: boolean; diff --git a/src/plugins/presentation_util/public/components/controls/control_group/control_group.scss b/src/plugins/controls/public/control_group/control_group.scss similarity index 100% rename from src/plugins/presentation_util/public/components/controls/control_group/control_group.scss rename to src/plugins/controls/public/control_group/control_group.scss diff --git a/src/plugins/controls/public/control_group/control_group_strings.ts b/src/plugins/controls/public/control_group/control_group_strings.ts new file mode 100644 index 0000000000000..91e857d083f7f --- /dev/null +++ b/src/plugins/controls/public/control_group/control_group_strings.ts @@ -0,0 +1,194 @@ +/* + * 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 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 or the Server + * Side Public License, v 1. + */ + +import { i18n } from '@kbn/i18n'; + +export const ControlGroupStrings = { + getEmbeddableTitle: () => + i18n.translate('controls.controlGroup.title', { + defaultMessage: 'Control group', + }), + emptyState: { + getCallToAction: () => + i18n.translate('controls.controlGroup.emptyState.callToAction', { + defaultMessage: 'Controls let you filter and interact with your dashboard data', + }), + getAddControlButtonTitle: () => + i18n.translate('controls.controlGroup.emptyState.addControlButtonTitle', { + defaultMessage: 'Add control', + }), + getTwoLineLoadingTitle: () => + i18n.translate('controls.controlGroup.emptyState.twoLineLoadingTitle', { + defaultMessage: '...', + }), + }, + manageControl: { + getFlyoutCreateTitle: () => + i18n.translate('controls.controlGroup.manageControl.createFlyoutTitle', { + defaultMessage: 'Create control', + }), + getFlyoutEditTitle: () => + i18n.translate('controls.controlGroup.manageControl.editFlyoutTitle', { + defaultMessage: 'Edit control', + }), + getTitleInputTitle: () => + i18n.translate('controls.controlGroup.manageControl.titleInputTitle', { + defaultMessage: 'Title', + }), + getWidthInputTitle: () => + i18n.translate('controls.controlGroup.manageControl.widthInputTitle', { + defaultMessage: 'Control size', + }), + getSaveChangesTitle: () => + i18n.translate('controls.controlGroup.manageControl.saveChangesTitle', { + defaultMessage: 'Save and close', + }), + getCancelTitle: () => + i18n.translate('controls.controlGroup.manageControl.cancelTitle', { + defaultMessage: 'Cancel', + }), + }, + management: { + getAddControlTitle: () => + i18n.translate('controls.controlGroup.management.addControl', { + defaultMessage: 'Add control', + }), + getManageButtonTitle: () => + i18n.translate('controls.controlGroup.management.buttonTitle', { + defaultMessage: 'Configure controls', + }), + getFlyoutTitle: () => + i18n.translate('controls.controlGroup.management.flyoutTitle', { + defaultMessage: 'Configure controls', + }), + getDefaultWidthTitle: () => + i18n.translate('controls.controlGroup.management.defaultWidthTitle', { + defaultMessage: 'Default size', + }), + getLayoutTitle: () => + i18n.translate('controls.controlGroup.management.layoutTitle', { + defaultMessage: 'Layout', + }), + getDeleteButtonTitle: () => + i18n.translate('controls.controlGroup.management.delete', { + defaultMessage: 'Delete control', + }), + getSetAllWidthsToDefaultTitle: () => + i18n.translate('controls.controlGroup.management.setAllWidths', { + defaultMessage: 'Set all sizes to default', + }), + getDeleteAllButtonTitle: () => + i18n.translate('controls.controlGroup.management.deleteAll', { + defaultMessage: 'Delete all', + }), + controlWidth: { + getWidthSwitchLegend: () => + i18n.translate('controls.controlGroup.management.layout.controlWidthLegend', { + defaultMessage: 'Change control size', + }), + getAutoWidthTitle: () => + i18n.translate('controls.controlGroup.management.layout.auto', { + defaultMessage: 'Auto', + }), + getSmallWidthTitle: () => + i18n.translate('controls.controlGroup.management.layout.small', { + defaultMessage: 'Small', + }), + getMediumWidthTitle: () => + i18n.translate('controls.controlGroup.management.layout.medium', { + defaultMessage: 'Medium', + }), + getLargeWidthTitle: () => + i18n.translate('controls.controlGroup.management.layout.large', { + defaultMessage: 'Large', + }), + }, + controlStyle: { + getDesignSwitchLegend: () => + i18n.translate('controls.controlGroup.management.layout.designSwitchLegend', { + defaultMessage: 'Switch control designs', + }), + getSingleLineTitle: () => + i18n.translate('controls.controlGroup.management.layout.singleLine', { + defaultMessage: 'Single line', + }), + getTwoLineTitle: () => + i18n.translate('controls.controlGroup.management.layout.twoLine', { + defaultMessage: 'Double line', + }), + }, + deleteControls: { + getDeleteAllTitle: () => + i18n.translate('controls.controlGroup.management.delete.deleteAllTitle', { + defaultMessage: 'Delete all controls?', + }), + getDeleteTitle: () => + i18n.translate('controls.controlGroup.management.delete.deleteTitle', { + defaultMessage: 'Delete control?', + }), + getSubtitle: () => + i18n.translate('controls.controlGroup.management.delete.sub', { + defaultMessage: 'Controls are not recoverable once removed.', + }), + getConfirm: () => + i18n.translate('controls.controlGroup.management.delete.confirm', { + defaultMessage: 'Delete', + }), + getCancel: () => + i18n.translate('controls.controlGroup.management.delete.cancel', { + defaultMessage: 'Cancel', + }), + }, + discardChanges: { + getTitle: () => + i18n.translate('controls.controlGroup.management.discard.title', { + defaultMessage: 'Discard changes?', + }), + getSubtitle: () => + i18n.translate('controls.controlGroup.management.discard.sub', { + defaultMessage: `Changes that you've made to this control will be discarded, are you sure you want to continue?`, + }), + getConfirm: () => + i18n.translate('controls.controlGroup.management.discard.confirm', { + defaultMessage: 'Discard changes', + }), + getCancel: () => + i18n.translate('controls.controlGroup.management.discard.cancel', { + defaultMessage: 'Cancel', + }), + }, + discardNewControl: { + getTitle: () => + i18n.translate('controls.controlGroup.management.deleteNew.title', { + defaultMessage: 'Discard new control', + }), + getSubtitle: () => + i18n.translate('controls.controlGroup.management.deleteNew.sub', { + defaultMessage: `Changes that you've made to this control will be discarded, are you sure you want to continue?`, + }), + getConfirm: () => + i18n.translate('controls.controlGroup.management.deleteNew.confirm', { + defaultMessage: 'Discard control', + }), + getCancel: () => + i18n.translate('controls.controlGroup.management.deleteNew.cancel', { + defaultMessage: 'Cancel', + }), + }, + }, + floatingActions: { + getEditButtonTitle: () => + i18n.translate('controls.controlGroup.floatingActions.editTitle', { + defaultMessage: 'Edit control', + }), + getRemoveButtonTitle: () => + i18n.translate('controls.controlGroup.floatingActions.removeTitle', { + defaultMessage: 'Remove control', + }), + }, +}; diff --git a/src/plugins/presentation_util/public/components/controls/control_group/editor/control_editor.tsx b/src/plugins/controls/public/control_group/editor/control_editor.tsx similarity index 100% rename from src/plugins/presentation_util/public/components/controls/control_group/editor/control_editor.tsx rename to src/plugins/controls/public/control_group/editor/control_editor.tsx diff --git a/src/plugins/presentation_util/public/components/controls/control_group/editor/create_control.tsx b/src/plugins/controls/public/control_group/editor/create_control.tsx similarity index 94% rename from src/plugins/presentation_util/public/components/controls/control_group/editor/create_control.tsx rename to src/plugins/controls/public/control_group/editor/create_control.tsx index de7e38400f6bc..b97ebb9aa519b 100644 --- a/src/plugins/presentation_util/public/components/controls/control_group/editor/create_control.tsx +++ b/src/plugins/controls/public/control_group/editor/create_control.tsx @@ -17,19 +17,19 @@ import { import React, { useState, ReactElement } from 'react'; import { ControlGroupInput } from '../types'; +import { pluginServices } from '../../services'; import { ControlEditor } from './control_editor'; -import { pluginServices } from '../../../../services'; +import { OverlayRef } from '../../../../../core/public'; import { forwardAllContext } from './forward_all_context'; import { DEFAULT_CONTROL_WIDTH } from './editor_constants'; -import { OverlayRef } from '../../../../../../../core/public'; import { ControlGroupStrings } from '../control_group_strings'; import { controlGroupReducers } from '../state/control_group_reducers'; +import { EmbeddableFactoryNotFoundError } from '../../../../embeddable/public'; +import { useReduxContainerContext } from '../../../../presentation_util/public'; import { ControlWidth, IEditableControlFactory, ControlInput } from '../../types'; -import { EmbeddableFactoryNotFoundError } from '../../../../../../embeddable/public'; -import { useReduxContainerContext } from '../../../redux_embeddables/redux_embeddable_context'; export const CreateControlButton = ({ isIconButton }: { isIconButton: boolean }) => { - // Presentation Services Context + // Controls Services Context const { overlays, controls } = pluginServices.getHooks(); const { getControlTypes, getControlFactory } = controls.useService(); const { openFlyout, openConfirm } = overlays.useService(); diff --git a/src/plugins/presentation_util/public/components/controls/control_group/editor/edit_control.tsx b/src/plugins/controls/public/control_group/editor/edit_control.tsx similarity index 93% rename from src/plugins/presentation_util/public/components/controls/control_group/editor/edit_control.tsx rename to src/plugins/controls/public/control_group/editor/edit_control.tsx index eb628049f7c93..210000e4f617c 100644 --- a/src/plugins/presentation_util/public/components/controls/control_group/editor/edit_control.tsx +++ b/src/plugins/controls/public/control_group/editor/edit_control.tsx @@ -12,17 +12,17 @@ import React, { useEffect, useRef } from 'react'; import { ControlGroupInput } from '../types'; import { ControlEditor } from './control_editor'; -import { pluginServices } from '../../../../services'; +import { pluginServices } from '../../services'; import { forwardAllContext } from './forward_all_context'; -import { OverlayRef } from '../../../../../../../core/public'; +import { OverlayRef } from '../../../../../core/public'; import { ControlGroupStrings } from '../control_group_strings'; import { IEditableControlFactory, ControlInput } from '../../types'; import { controlGroupReducers } from '../state/control_group_reducers'; -import { EmbeddableFactoryNotFoundError } from '../../../../../../embeddable/public'; -import { useReduxContainerContext } from '../../../redux_embeddables/redux_embeddable_context'; +import { EmbeddableFactoryNotFoundError } from '../../../../embeddable/public'; +import { useReduxContainerContext } from '../../../../presentation_util/public'; export const EditControlButton = ({ embeddableId }: { embeddableId: string }) => { - // Presentation Services Context + // Controls Services Context const { overlays, controls } = pluginServices.getHooks(); const { getControlFactory } = controls.useService(); const { openFlyout, openConfirm } = overlays.useService(); diff --git a/src/plugins/presentation_util/public/components/controls/control_group/editor/edit_control_group.tsx b/src/plugins/controls/public/control_group/editor/edit_control_group.tsx similarity index 97% rename from src/plugins/presentation_util/public/components/controls/control_group/editor/edit_control_group.tsx rename to src/plugins/controls/public/control_group/editor/edit_control_group.tsx index 9828f6317ad53..87a2a1407a761 100644 --- a/src/plugins/presentation_util/public/components/controls/control_group/editor/edit_control_group.tsx +++ b/src/plugins/controls/public/control_group/editor/edit_control_group.tsx @@ -28,11 +28,11 @@ import { DEFAULT_CONTROL_WIDTH, } from './editor_constants'; import { ControlGroupInput } from '../types'; -import { pluginServices } from '../../../../services'; +import { pluginServices } from '../../services'; import { ControlStyle, ControlWidth } from '../../types'; import { ControlGroupStrings } from '../control_group_strings'; import { controlGroupReducers } from '../state/control_group_reducers'; -import { useReduxContainerContext } from '../../../redux_embeddables/redux_embeddable_context'; +import { useReduxContainerContext } from '../../../../presentation_util/public'; interface EditControlGroupState { newControlStyle: ControlGroupInput['controlStyle']; diff --git a/src/plugins/presentation_util/public/components/controls/control_group/editor/editor_constants.ts b/src/plugins/controls/public/control_group/editor/editor_constants.ts similarity index 100% rename from src/plugins/presentation_util/public/components/controls/control_group/editor/editor_constants.ts rename to src/plugins/controls/public/control_group/editor/editor_constants.ts diff --git a/src/plugins/presentation_util/public/components/controls/control_group/editor/forward_all_context.tsx b/src/plugins/controls/public/control_group/editor/forward_all_context.tsx similarity index 67% rename from src/plugins/presentation_util/public/components/controls/control_group/editor/forward_all_context.tsx rename to src/plugins/controls/public/control_group/editor/forward_all_context.tsx index bb7356c240648..23d7e60573323 100644 --- a/src/plugins/presentation_util/public/components/controls/control_group/editor/forward_all_context.tsx +++ b/src/plugins/controls/public/control_group/editor/forward_all_context.tsx @@ -6,16 +6,16 @@ * Side Public License, v 1. */ -import { Provider } from 'react-redux'; import { ReactElement } from 'react'; import React from 'react'; import { ControlGroupInput } from '../types'; -import { pluginServices } from '../../../../services'; -import { toMountPoint } from '../../../../../../kibana_react/public'; -import { ReduxContainerContextServices } from '../../../redux_embeddables/types'; -import { ReduxEmbeddableContext } from '../../../redux_embeddables/redux_embeddable_context'; -import { getManagedEmbeddablesStore } from '../../../redux_embeddables/generic_embeddable_store'; +import { pluginServices } from '../../services'; +import { toMountPoint } from '../../../../kibana_react/public'; +import { + ReduxContainerContextServices, + ReduxEmbeddableContext, +} from '../../../../presentation_util/public'; /** * The overlays service creates its divs outside the flow of the component. This necessitates @@ -26,11 +26,12 @@ export const forwardAllContext = ( reduxContainerContext: ReduxContainerContextServices ) => { const PresentationUtilProvider = pluginServices.getContextProvider(); + const StoreProvider = reduxContainerContext.ReduxEmbeddableStoreProvider; return toMountPoint( - + {component} - + ); }; diff --git a/src/plugins/presentation_util/public/components/controls/control_group/embeddable/control_group_container.tsx b/src/plugins/controls/public/control_group/embeddable/control_group_container.tsx similarity index 89% rename from src/plugins/presentation_util/public/components/controls/control_group/embeddable/control_group_container.tsx rename to src/plugins/controls/public/control_group/embeddable/control_group_container.tsx index ff25286a75211..65c93e42a472f 100644 --- a/src/plugins/presentation_util/public/components/controls/control_group/embeddable/control_group_container.tsx +++ b/src/plugins/controls/public/control_group/embeddable/control_group_container.tsx @@ -27,14 +27,22 @@ import { ControlPanelState, CONTROL_GROUP_TYPE, } from '../types'; -import { pluginServices } from '../../../../services'; -import { DataView } from '../../../../../../data_views/public'; +import { + withSuspense, + LazyReduxEmbeddableWrapper, + ReduxEmbeddableWrapperPropsWithChildren, +} from '../../../../presentation_util/public'; +import { pluginServices } from '../../services'; +import { DataView } from '../../../../data_views/public'; +import { DEFAULT_CONTROL_WIDTH } from '../editor/editor_constants'; import { ControlGroup } from '../component/control_group_component'; import { controlGroupReducers } from '../state/control_group_reducers'; import { ControlEmbeddable, ControlInput, ControlOutput } from '../../types'; -import { Container, EmbeddableFactory } from '../../../../../../embeddable/public'; -import { ReduxEmbeddableWrapper } from '../../../redux_embeddables/redux_embeddable_wrapper'; -import { DEFAULT_CONTROL_WIDTH } from '../editor/editor_constants'; +import { Container, EmbeddableFactory } from '../../../../embeddable/public'; + +const ControlGroupReduxWrapper = withSuspense< + ReduxEmbeddableWrapperPropsWithChildren +>(LazyReduxEmbeddableWrapper); export class ControlGroupContainer extends Container< ControlInput, @@ -145,12 +153,9 @@ export class ControlGroupContainer extends Container< const PresentationUtilProvider = pluginServices.getContextProvider(); ReactDOM.render( - - embeddable={this} - reducers={controlGroupReducers} - > + - + , dom ); diff --git a/src/plugins/presentation_util/public/components/controls/control_group/embeddable/control_group_container_factory.ts b/src/plugins/controls/public/control_group/embeddable/control_group_container_factory.ts similarity index 91% rename from src/plugins/presentation_util/public/components/controls/control_group/embeddable/control_group_container_factory.ts rename to src/plugins/controls/public/control_group/embeddable/control_group_container_factory.ts index 5a71355da8bbe..d2e057a613070 100644 --- a/src/plugins/presentation_util/public/components/controls/control_group/embeddable/control_group_container_factory.ts +++ b/src/plugins/controls/public/control_group/embeddable/control_group_container_factory.ts @@ -14,14 +14,15 @@ * Side Public License, v 1. */ -import { Container, EmbeddableFactoryDefinition } from '../../../../../../embeddable/public'; -import { EmbeddablePersistableStateService } from '../../../../../../embeddable/common'; +import { Container, EmbeddableFactoryDefinition } from '../../../../embeddable/public'; +import { EmbeddablePersistableStateService } from '../../../../embeddable/common'; + import { ControlGroupInput, CONTROL_GROUP_TYPE } from '../types'; import { ControlGroupStrings } from '../control_group_strings'; import { createControlGroupExtract, createControlGroupInject, -} from '../../../../../common/controls/control_group/control_group_persistable_state'; +} from '../../../common/control_group/control_group_persistable_state'; export class ControlGroupContainerFactory implements EmbeddableFactoryDefinition { public readonly isContainerType = true; diff --git a/src/plugins/presentation_util/public/components/controls/control_group/index.ts b/src/plugins/controls/public/control_group/index.ts similarity index 92% rename from src/plugins/presentation_util/public/components/controls/control_group/index.ts rename to src/plugins/controls/public/control_group/index.ts index 95988d2e8143c..60050786d7c11 100644 --- a/src/plugins/presentation_util/public/components/controls/control_group/index.ts +++ b/src/plugins/controls/public/control_group/index.ts @@ -6,6 +6,8 @@ * Side Public License, v 1. */ -export type { ControlGroupInput, ControlGroupOutput } from './types'; export type { ControlGroupContainer } from './embeddable/control_group_container'; +export type { ControlGroupInput, ControlGroupOutput } from './types'; + +export { CONTROL_GROUP_TYPE } from './types'; export { ControlGroupContainerFactory } from './embeddable/control_group_container_factory'; diff --git a/src/plugins/presentation_util/public/components/controls/control_group/opt_a.svg b/src/plugins/controls/public/control_group/opt_a.svg similarity index 100% rename from src/plugins/presentation_util/public/components/controls/control_group/opt_a.svg rename to src/plugins/controls/public/control_group/opt_a.svg diff --git a/src/plugins/presentation_util/public/components/controls/control_group/state/control_group_reducers.ts b/src/plugins/controls/public/control_group/state/control_group_reducers.ts similarity index 100% rename from src/plugins/presentation_util/public/components/controls/control_group/state/control_group_reducers.ts rename to src/plugins/controls/public/control_group/state/control_group_reducers.ts diff --git a/src/plugins/presentation_util/public/components/controls/control_group/types.ts b/src/plugins/controls/public/control_group/types.ts similarity index 69% rename from src/plugins/presentation_util/public/components/controls/control_group/types.ts rename to src/plugins/controls/public/control_group/types.ts index 3d0123eb4192f..5f6ec00efffab 100644 --- a/src/plugins/presentation_util/public/components/controls/control_group/types.ts +++ b/src/plugins/controls/public/control_group/types.ts @@ -7,8 +7,13 @@ */ import { CommonControlOutput } from '../types'; -import { ContainerOutput } from '../../../../../embeddable/public'; +import { ContainerOutput } from '../../../embeddable/public'; export type ControlGroupOutput = ContainerOutput & CommonControlOutput; -export * from '../../../../common/controls/control_group/types'; +export { + type ControlsPanels, + type ControlGroupInput, + type ControlPanelState, + CONTROL_GROUP_TYPE, +} from '../../common/control_group/types'; diff --git a/src/plugins/presentation_util/public/components/controls/control_types/index.ts b/src/plugins/controls/public/control_types/index.ts similarity index 100% rename from src/plugins/presentation_util/public/components/controls/control_types/index.ts rename to src/plugins/controls/public/control_types/index.ts diff --git a/src/plugins/presentation_util/public/components/controls/control_types/options_list/index.ts b/src/plugins/controls/public/control_types/options_list/index.ts similarity index 72% rename from src/plugins/presentation_util/public/components/controls/control_types/options_list/index.ts rename to src/plugins/controls/public/control_types/options_list/index.ts index f2d9c29701a5f..7a254bf423ce4 100644 --- a/src/plugins/presentation_util/public/components/controls/control_types/options_list/index.ts +++ b/src/plugins/controls/public/control_types/options_list/index.ts @@ -6,5 +6,8 @@ * Side Public License, v 1. */ +export { OPTIONS_LIST_CONTROL } from '../../../common/control_types/options_list/types'; export { OptionsListEmbeddableFactory } from './options_list_embeddable_factory'; + export type { OptionsListEmbeddable } from './options_list_embeddable'; +export type { OptionsListEmbeddableInput } from '../../../common/control_types/options_list/types'; diff --git a/src/plugins/presentation_util/public/components/controls/control_types/options_list/options_list.scss b/src/plugins/controls/public/control_types/options_list/options_list.scss similarity index 100% rename from src/plugins/presentation_util/public/components/controls/control_types/options_list/options_list.scss rename to src/plugins/controls/public/control_types/options_list/options_list.scss diff --git a/src/plugins/presentation_util/public/components/controls/control_types/options_list/options_list_component.tsx b/src/plugins/controls/public/control_types/options_list/options_list_component.tsx similarity index 97% rename from src/plugins/presentation_util/public/components/controls/control_types/options_list/options_list_component.tsx rename to src/plugins/controls/public/control_types/options_list/options_list_component.tsx index 1c79d1ce3e9b0..74f2130528e97 100644 --- a/src/plugins/presentation_util/public/components/controls/control_types/options_list/options_list_component.tsx +++ b/src/plugins/controls/public/control_types/options_list/options_list_component.tsx @@ -15,7 +15,7 @@ import { debounce } from 'lodash'; import { OptionsListStrings } from './options_list_strings'; import { optionsListReducers } from './options_list_reducers'; import { OptionsListPopover } from './options_list_popover_component'; -import { useReduxEmbeddableContext } from '../../../redux_embeddables/redux_embeddable_context'; +import { useReduxEmbeddableContext } from '../../../../presentation_util/public'; import './options_list.scss'; import { useStateObservable } from '../../hooks/use_state_observable'; diff --git a/src/plugins/presentation_util/public/components/controls/control_types/options_list/options_list_editor.tsx b/src/plugins/controls/public/control_types/options_list/options_list_editor.tsx similarity index 90% rename from src/plugins/presentation_util/public/components/controls/control_types/options_list/options_list_editor.tsx rename to src/plugins/controls/public/control_types/options_list/options_list_editor.tsx index 86f4f85b3b0b2..d9231a5d8b2e5 100644 --- a/src/plugins/presentation_util/public/components/controls/control_types/options_list/options_list_editor.tsx +++ b/src/plugins/controls/public/control_types/options_list/options_list_editor.tsx @@ -10,13 +10,16 @@ import useMount from 'react-use/lib/useMount'; import React, { useEffect, useState } from 'react'; import { EuiFormRow, EuiSwitch } from '@elastic/eui'; +import { pluginServices } from '../../services'; import { ControlEditorProps } from '../../types'; -import { DataViewListItem, DataView } from '../../../../../../data_views/common'; -import { DataViewPicker } from '../../../data_view_picker/data_view_picker'; -import { OptionsListStrings } from './options_list_strings'; -import { pluginServices } from '../../../../services'; import { OptionsListEmbeddableInput } from './types'; -import { FieldPicker } from '../../../field_picker/field_picker'; +import { OptionsListStrings } from './options_list_strings'; +import { DataViewListItem, DataView } from '../../../../data_views/common'; +import { + LazyDataViewPicker, + LazyFieldPicker, + withSuspense, +} from '../../../../presentation_util/public'; interface OptionsListEditorState { singleSelect?: boolean; @@ -27,13 +30,16 @@ interface OptionsListEditorState { fieldName?: string; } +const FieldPicker = withSuspense(LazyFieldPicker, null); +const DataViewPicker = withSuspense(LazyDataViewPicker, null); + export const OptionsListEditor = ({ onChange, initialInput, setValidState, setDefaultTitle, }: ControlEditorProps) => { - // Presentation Services Context + // Controls Services Context const { dataViews } = pluginServices.getHooks(); const { getIdsWithTitle, getDefaultId, get } = dataViews.useService(); diff --git a/src/plugins/presentation_util/public/components/controls/control_types/options_list/options_list_embeddable.tsx b/src/plugins/controls/public/control_types/options_list/options_list_embeddable.tsx similarity index 89% rename from src/plugins/presentation_util/public/components/controls/control_types/options_list/options_list_embeddable.tsx rename to src/plugins/controls/public/control_types/options_list/options_list_embeddable.tsx index b980ee10293e5..ce570fcbf769e 100644 --- a/src/plugins/presentation_util/public/components/controls/control_types/options_list/options_list_embeddable.tsx +++ b/src/plugins/controls/public/control_types/options_list/options_list_embeddable.tsx @@ -6,31 +6,39 @@ * Side Public License, v 1. */ -import React from 'react'; -import ReactDOM from 'react-dom'; -import { isEqual } from 'lodash'; -import deepEqual from 'fast-deep-equal'; import { + Filter, buildEsQuery, + compareFilters, buildPhraseFilter, buildPhrasesFilter, - compareFilters, - Filter, } from '@kbn/es-query'; +import React from 'react'; +import ReactDOM from 'react-dom'; +import { isEqual } from 'lodash'; +import deepEqual from 'fast-deep-equal'; import { merge, Subject, Subscription, BehaviorSubject } from 'rxjs'; import { tap, debounceTime, map, distinctUntilChanged, skip } from 'rxjs/operators'; -import { ReduxEmbeddableWrapper } from '../../../redux_embeddables/redux_embeddable_wrapper'; import { OptionsListComponent, OptionsListComponentState } from './options_list_component'; -import { PresentationDataViewsService } from '../../../../services/data_views'; -import { Embeddable, IContainer } from '../../../../../../embeddable/public'; +import { + withSuspense, + LazyReduxEmbeddableWrapper, + ReduxEmbeddableWrapperPropsWithChildren, +} from '../../../../presentation_util/public'; import { OptionsListEmbeddableInput, OPTIONS_LIST_CONTROL } from './types'; -import { PresentationDataService } from '../../../../services/data'; -import { DataView } from '../../../../../../data_views/public'; +import { ControlsDataViewsService } from '../../services/data_views'; +import { Embeddable, IContainer } from '../../../../embeddable/public'; +import { ControlsDataService } from '../../services/data'; import { optionsListReducers } from './options_list_reducers'; import { OptionsListStrings } from './options_list_strings'; -import { pluginServices } from '../../../../services'; +import { DataView } from '../../../../data_views/public'; import { ControlInput, ControlOutput } from '../..'; +import { pluginServices } from '../../services'; + +const OptionsListReduxWrapper = withSuspense< + ReduxEmbeddableWrapperPropsWithChildren +>(LazyReduxEmbeddableWrapper); const diffDataFetchProps = ( current?: OptionsListDataFetchProps, @@ -62,9 +70,9 @@ export class OptionsListEmbeddable extends Embeddable = new Subject(); @@ -80,7 +88,7 @@ export class OptionsListEmbeddable extends Embeddable - embeddable={this} - reducers={optionsListReducers} - > + - , + , node ); }; diff --git a/src/plugins/presentation_util/public/components/controls/control_types/options_list/options_list_embeddable_factory.tsx b/src/plugins/controls/public/control_types/options_list/options_list_embeddable_factory.tsx similarity index 93% rename from src/plugins/presentation_util/public/components/controls/control_types/options_list/options_list_embeddable_factory.tsx rename to src/plugins/controls/public/control_types/options_list/options_list_embeddable_factory.tsx index cb53ac463be3f..98d616faadc58 100644 --- a/src/plugins/presentation_util/public/components/controls/control_types/options_list/options_list_embeddable_factory.tsx +++ b/src/plugins/controls/public/control_types/options_list/options_list_embeddable_factory.tsx @@ -11,11 +11,11 @@ import deepEqual from 'fast-deep-equal'; import { OptionsListEditor } from './options_list_editor'; import { ControlEmbeddable, IEditableControlFactory } from '../../types'; import { OptionsListEmbeddableInput, OPTIONS_LIST_CONTROL } from './types'; -import { EmbeddableFactoryDefinition, IContainer } from '../../../../../../embeddable/public'; +import { EmbeddableFactoryDefinition, IContainer } from '../../../../embeddable/public'; import { createOptionsListExtract, createOptionsListInject, -} from '../../../../../common/controls/control_types/options_list/options_list_persistable_state'; +} from '../../../common/control_types/options_list/options_list_persistable_state'; export class OptionsListEmbeddableFactory implements EmbeddableFactoryDefinition, IEditableControlFactory diff --git a/src/plugins/presentation_util/public/components/controls/control_types/options_list/options_list_popover_component.tsx b/src/plugins/controls/public/control_types/options_list/options_list_popover_component.tsx similarity index 98% rename from src/plugins/presentation_util/public/components/controls/control_types/options_list/options_list_popover_component.tsx rename to src/plugins/controls/public/control_types/options_list/options_list_popover_component.tsx index 4aae049a5d446..0634d676f57a2 100644 --- a/src/plugins/presentation_util/public/components/controls/control_types/options_list/options_list_popover_component.tsx +++ b/src/plugins/controls/public/control_types/options_list/options_list_popover_component.tsx @@ -24,7 +24,7 @@ import { OptionsListEmbeddableInput } from './types'; import { OptionsListStrings } from './options_list_strings'; import { optionsListReducers } from './options_list_reducers'; import { OptionsListComponentState } from './options_list_component'; -import { useReduxEmbeddableContext } from '../../../redux_embeddables/redux_embeddable_context'; +import { useReduxEmbeddableContext } from '../../../../presentation_util/public'; export const OptionsListPopover = ({ loading, diff --git a/src/plugins/presentation_util/public/components/controls/control_types/options_list/options_list_reducers.ts b/src/plugins/controls/public/control_types/options_list/options_list_reducers.ts similarity index 100% rename from src/plugins/presentation_util/public/components/controls/control_types/options_list/options_list_reducers.ts rename to src/plugins/controls/public/control_types/options_list/options_list_reducers.ts diff --git a/src/plugins/presentation_util/public/components/controls/control_types/options_list/options_list_strings.ts b/src/plugins/controls/public/control_types/options_list/options_list_strings.ts similarity index 59% rename from src/plugins/presentation_util/public/components/controls/control_types/options_list/options_list_strings.ts rename to src/plugins/controls/public/control_types/options_list/options_list_strings.ts index dee0d4e7e1807..0a6e46c514d11 100644 --- a/src/plugins/presentation_util/public/components/controls/control_types/options_list/options_list_strings.ts +++ b/src/plugins/controls/public/control_types/options_list/options_list_strings.ts @@ -11,65 +11,65 @@ import { i18n } from '@kbn/i18n'; export const OptionsListStrings = { summary: { getSeparator: () => - i18n.translate('presentationUtil.controls.optionsList.summary.separator', { + i18n.translate('controls.optionsList.summary.separator', { defaultMessage: ', ', }), getPlaceholder: () => - i18n.translate('presentationUtil.controls.optionsList.summary.placeholder', { + i18n.translate('controls.optionsList.summary.placeholder', { defaultMessage: 'Select...', }), }, editor: { getIndexPatternTitle: () => - i18n.translate('presentationUtil.controls.optionsList.editor.indexPatternTitle', { + i18n.translate('controls.optionsList.editor.indexPatternTitle', { defaultMessage: 'Index pattern', }), getDataViewTitle: () => - i18n.translate('presentationUtil.controls.optionsList.editor.dataViewTitle', { + i18n.translate('controls.optionsList.editor.dataViewTitle', { defaultMessage: 'Data view', }), getNoDataViewTitle: () => - i18n.translate('presentationUtil.controls.optionsList.editor.noDataViewTitle', { + i18n.translate('controls.optionsList.editor.noDataViewTitle', { defaultMessage: 'Select data view', }), getFieldTitle: () => - i18n.translate('presentationUtil.controls.optionsList.editor.fieldTitle', { + i18n.translate('controls.optionsList.editor.fieldTitle', { defaultMessage: 'Field', }), getAllowMultiselectTitle: () => - i18n.translate('presentationUtil.inputControls.optionsList.editor.allowMultiselectTitle', { + i18n.translate('controls.optionsList.editor.allowMultiselectTitle', { defaultMessage: 'Allow multiple selections in dropdown', }), }, popover: { getLoadingMessage: () => - i18n.translate('presentationUtil.controls.optionsList.popover.loading', { + i18n.translate('controls.optionsList.popover.loading', { defaultMessage: 'Loading filters', }), getEmptyMessage: () => - i18n.translate('presentationUtil.controls.optionsList.popover.empty', { + i18n.translate('controls.optionsList.popover.empty', { defaultMessage: 'No filters found', }), getSelectionsEmptyMessage: () => - i18n.translate('presentationUtil.controls.optionsList.popover.selectionsEmpty', { + i18n.translate('controls.optionsList.popover.selectionsEmpty', { defaultMessage: 'You have no selections', }), getAllOptionsButtonTitle: () => - i18n.translate('presentationUtil.controls.optionsList.popover.allOptionsTitle', { + i18n.translate('controls.optionsList.popover.allOptionsTitle', { defaultMessage: 'Show all options', }), getSelectedOptionsButtonTitle: () => - i18n.translate('presentationUtil.inputControls.optionsList.popover.selectedOptionsTitle', { + i18n.translate('controls.optionsList.popover.selectedOptionsTitle', { defaultMessage: 'Show only selected options', }), getClearAllSelectionsButtonTitle: () => - i18n.translate('presentationUtil.controls.optionsList.popover.clearAllSelectionsTitle', { + i18n.translate('controls.optionsList.popover.clearAllSelectionsTitle', { defaultMessage: 'Clear selections', }), }, errors: { getDataViewNotFoundError: (dataViewId: string) => - i18n.translate('presentationUtil.controls.optionsList.errors.dataViewNotFound', { + i18n.translate('controls.optionsList.errors.dataViewNotFound', { defaultMessage: 'Could not locate data view: {dataViewId}', values: { dataViewId }, }), diff --git a/src/plugins/presentation_util/public/components/controls/control_types/options_list/types.ts b/src/plugins/controls/public/control_types/options_list/types.ts similarity index 81% rename from src/plugins/presentation_util/public/components/controls/control_types/options_list/types.ts rename to src/plugins/controls/public/control_types/options_list/types.ts index 06b6526f38db4..f537cccf3d690 100644 --- a/src/plugins/presentation_util/public/components/controls/control_types/options_list/types.ts +++ b/src/plugins/controls/public/control_types/options_list/types.ts @@ -6,4 +6,4 @@ * Side Public License, v 1. */ -export * from '../../../../../common/controls/control_types/options_list/types'; +export * from '../../../common/control_types/options_list/types'; diff --git a/src/plugins/presentation_util/public/components/controls/controls_service.ts b/src/plugins/controls/public/controls_service.ts similarity index 88% rename from src/plugins/presentation_util/public/components/controls/controls_service.ts rename to src/plugins/controls/public/controls_service.ts index 436d36fcc9db0..69efacbef2915 100644 --- a/src/plugins/presentation_util/public/components/controls/controls_service.ts +++ b/src/plugins/controls/public/controls_service.ts @@ -7,8 +7,8 @@ */ import { ControlEmbeddable, ControlFactory, ControlInput, ControlOutput } from '.'; -import { EmbeddableFactory } from '../../../../embeddable/public'; -import { ControlTypeRegistry } from '../../services/controls'; +import { EmbeddableFactory } from '../../embeddable/public'; +import { ControlTypeRegistry } from './services/controls'; export class ControlsService { private controlsFactoriesMap: ControlTypeRegistry = {}; diff --git a/src/plugins/presentation_util/public/components/controls/hooks/use_child_embeddable.ts b/src/plugins/controls/public/hooks/use_child_embeddable.ts similarity index 100% rename from src/plugins/presentation_util/public/components/controls/hooks/use_child_embeddable.ts rename to src/plugins/controls/public/hooks/use_child_embeddable.ts diff --git a/src/plugins/presentation_util/public/components/controls/hooks/use_state_observable.ts b/src/plugins/controls/public/hooks/use_state_observable.ts similarity index 100% rename from src/plugins/presentation_util/public/components/controls/hooks/use_state_observable.ts rename to src/plugins/controls/public/hooks/use_state_observable.ts diff --git a/src/plugins/controls/public/index.ts b/src/plugins/controls/public/index.ts new file mode 100644 index 0000000000000..c8118fcdb1d77 --- /dev/null +++ b/src/plugins/controls/public/index.ts @@ -0,0 +1,44 @@ +/* + * 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 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 or the Server + * Side Public License, v 1. + */ + +import { ControlsPlugin } from './plugin'; + +export type { + ControlOutput, + ControlFactory, + ControlEmbeddable, + ControlEditorProps, + CommonControlOutput, + IEditableControlFactory, +} from './types'; + +export type { + ControlWidth, + ControlStyle, + ParentIgnoreSettings, + ControlInput, +} from '../common/types'; + +export { OPTIONS_LIST_CONTROL, CONTROL_GROUP_TYPE } from '../common'; + +export { + ControlGroupContainer, + ControlGroupContainerFactory, + type ControlGroupInput, + type ControlGroupOutput, +} from './control_group'; + +export { + OptionsListEmbeddableFactory, + OptionsListEmbeddable, + type OptionsListEmbeddableInput, +} from './control_types'; + +export function plugin() { + return new ControlsPlugin(); +} diff --git a/src/plugins/controls/public/plugin.ts b/src/plugins/controls/public/plugin.ts new file mode 100644 index 0000000000000..c4ff865a05e47 --- /dev/null +++ b/src/plugins/controls/public/plugin.ts @@ -0,0 +1,95 @@ +/* + * 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 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 or the Server + * Side Public License, v 1. + */ + +import { CoreSetup, CoreStart, Plugin } from '../../../core/public'; +import { pluginServices } from './services'; +import { registry } from './services/kibana'; +import { + ControlsPluginSetup, + ControlsPluginStart, + ControlsPluginSetupDeps, + ControlsPluginStartDeps, + IEditableControlFactory, + ControlEditorProps, + ControlEmbeddable, + ControlInput, +} from './types'; +import { OptionsListEmbeddableFactory } from './control_types/options_list'; +import { ControlGroupContainerFactory, CONTROL_GROUP_TYPE, OPTIONS_LIST_CONTROL } from '.'; + +export class ControlsPlugin + implements + Plugin< + ControlsPluginSetup, + ControlsPluginStart, + ControlsPluginSetupDeps, + ControlsPluginStartDeps + > +{ + private inlineEditors: { + [key: string]: { + controlEditorComponent?: (props: ControlEditorProps) => JSX.Element; + presaveTransformFunction?: ( + newInput: Partial, + embeddable?: ControlEmbeddable + ) => Partial; + }; + } = {}; + + public setup( + _coreSetup: CoreSetup, + _setupPlugins: ControlsPluginSetupDeps + ): ControlsPluginSetup { + _coreSetup.getStartServices().then(([coreStart, deps]) => { + // register control group embeddable factory + embeddable.registerEmbeddableFactory( + CONTROL_GROUP_TYPE, + new ControlGroupContainerFactory(deps.embeddable) + ); + }); + + const { embeddable } = _setupPlugins; + + // create control type embeddable factories. + const optionsListFactory = new OptionsListEmbeddableFactory(); + const editableOptionsListFactory = optionsListFactory as IEditableControlFactory; + this.inlineEditors[OPTIONS_LIST_CONTROL] = { + controlEditorComponent: editableOptionsListFactory.controlEditorComponent, + presaveTransformFunction: editableOptionsListFactory.presaveTransformFunction, + }; + embeddable.registerEmbeddableFactory(OPTIONS_LIST_CONTROL, optionsListFactory); + + return {}; + } + + public start(coreStart: CoreStart, startPlugins: ControlsPluginStartDeps): ControlsPluginStart { + pluginServices.setRegistry(registry.start({ coreStart, startPlugins })); + const { controls: controlsService } = pluginServices.getServices(); + const { embeddable } = startPlugins; + + // register control types with controls service. + const optionsListFactory = embeddable.getEmbeddableFactory(OPTIONS_LIST_CONTROL); + // Temporarily pass along inline editors - inline editing should be made a first-class feature of embeddables + const editableOptionsListFactory = optionsListFactory as IEditableControlFactory; + const { + controlEditorComponent: optionsListControlEditor, + presaveTransformFunction: optionsListPresaveTransform, + } = this.inlineEditors[OPTIONS_LIST_CONTROL]; + editableOptionsListFactory.controlEditorComponent = optionsListControlEditor; + editableOptionsListFactory.presaveTransformFunction = optionsListPresaveTransform; + + if (optionsListFactory) controlsService.registerControlType(optionsListFactory); + + return { + ContextProvider: pluginServices.getContextProvider(), + controlsService, + }; + } + + public stop() {} +} diff --git a/src/plugins/presentation_util/public/services/controls.ts b/src/plugins/controls/public/services/controls.ts similarity index 89% rename from src/plugins/presentation_util/public/services/controls.ts rename to src/plugins/controls/public/services/controls.ts index 76af24960bfe3..83a3c8eec98d3 100644 --- a/src/plugins/presentation_util/public/services/controls.ts +++ b/src/plugins/controls/public/services/controls.ts @@ -7,18 +7,13 @@ */ import { EmbeddableFactory } from '../../../embeddable/public'; -import { - ControlEmbeddable, - ControlFactory, - ControlOutput, - ControlInput, -} from '../components/controls/types'; +import { ControlEmbeddable, ControlFactory, ControlOutput, ControlInput } from '../types'; export interface ControlTypeRegistry { [key: string]: ControlFactory; } -export interface PresentationControlsService { +export interface ControlsService { registerControlType: (factory: ControlFactory) => void; getControlFactory: < diff --git a/src/plugins/presentation_util/public/services/data.ts b/src/plugins/controls/public/services/data.ts similarity index 91% rename from src/plugins/presentation_util/public/services/data.ts rename to src/plugins/controls/public/services/data.ts index 44f29dcd2d3ad..0a99317e29a26 100644 --- a/src/plugins/presentation_util/public/services/data.ts +++ b/src/plugins/controls/public/services/data.ts @@ -8,6 +8,6 @@ import { DataPublicPluginStart } from '../../../data/public'; -export interface PresentationDataService { +export interface ControlsDataService { autocomplete: DataPublicPluginStart['autocomplete']; } diff --git a/src/plugins/controls/public/services/data_views.ts b/src/plugins/controls/public/services/data_views.ts new file mode 100644 index 0000000000000..2308366e27660 --- /dev/null +++ b/src/plugins/controls/public/services/data_views.ts @@ -0,0 +1,15 @@ +/* + * 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 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 or the Server + * Side Public License, v 1. + */ + +import { DataViewsPublicPluginStart } from '../../../data_views/public'; + +export interface ControlsDataViewsService { + get: DataViewsPublicPluginStart['get']; + getDefaultId: DataViewsPublicPluginStart['getDefaultId']; + getIdsWithTitle: DataViewsPublicPluginStart['getIdsWithTitle']; +} diff --git a/src/plugins/controls/public/services/index.ts b/src/plugins/controls/public/services/index.ts new file mode 100644 index 0000000000000..5730dbf68cefb --- /dev/null +++ b/src/plugins/controls/public/services/index.ts @@ -0,0 +1,32 @@ +/* + * 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 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 or the Server + * Side Public License, v 1. + */ + +import { PluginServices } from '../../../presentation_util/public'; +import { ControlsDataViewsService } from './data_views'; +import { ControlsOverlaysService } from './overlays'; +import { registry as stubRegistry } from './stub'; +import { ControlsPluginStart } from '../types'; +import { ControlsDataService } from './data'; +import { ControlsService } from './controls'; + +export interface ControlsServices { + dataViews: ControlsDataViewsService; + overlays: ControlsOverlaysService; + data: ControlsDataService; + controls: ControlsService; +} + +export const pluginServices = new PluginServices(); + +export const getStubPluginServices = (): ControlsPluginStart => { + pluginServices.setRegistry(stubRegistry.start({})); + return { + ContextProvider: pluginServices.getContextProvider(), + controlsService: pluginServices.getServices().controls, + }; +}; diff --git a/src/plugins/presentation_util/public/services/stub/controls.ts b/src/plugins/controls/public/services/kibana/controls.ts similarity index 65% rename from src/plugins/presentation_util/public/services/stub/controls.ts rename to src/plugins/controls/public/services/kibana/controls.ts index e5dc84a3dd645..7c33ee8c33527 100644 --- a/src/plugins/presentation_util/public/services/stub/controls.ts +++ b/src/plugins/controls/public/services/kibana/controls.ts @@ -6,8 +6,8 @@ * Side Public License, v 1. */ -import { PluginServiceFactory } from '../create'; -import { getCommonControlsService, PresentationControlsService } from '../controls'; +import { PluginServiceFactory } from '../../../../presentation_util/public'; +import { getCommonControlsService, ControlsService } from '../controls'; -export type ControlsServiceFactory = PluginServiceFactory; +export type ControlsServiceFactory = PluginServiceFactory; export const controlsServiceFactory = () => getCommonControlsService(); diff --git a/src/plugins/presentation_util/public/services/kibana/data.ts b/src/plugins/controls/public/services/kibana/data.ts similarity index 71% rename from src/plugins/presentation_util/public/services/kibana/data.ts rename to src/plugins/controls/public/services/kibana/data.ts index 408e59fd4906c..4b4b9ad8afd81 100644 --- a/src/plugins/presentation_util/public/services/kibana/data.ts +++ b/src/plugins/controls/public/services/kibana/data.ts @@ -6,13 +6,13 @@ * Side Public License, v 1. */ -import { PresentationUtilPluginStartDeps } from '../../types'; -import { KibanaPluginServiceFactory } from '../create'; -import { PresentationDataService } from '../data'; +import { ControlsDataService } from '../data'; +import { ControlsPluginStartDeps } from '../../types'; +import { KibanaPluginServiceFactory } from '../../../../presentation_util/public'; export type DataServiceFactory = KibanaPluginServiceFactory< - PresentationDataService, - PresentationUtilPluginStartDeps + ControlsDataService, + ControlsPluginStartDeps >; export const dataServiceFactory: DataServiceFactory = ({ startPlugins }) => { diff --git a/src/plugins/controls/public/services/kibana/data_views.ts b/src/plugins/controls/public/services/kibana/data_views.ts new file mode 100644 index 0000000000000..c878423390a64 --- /dev/null +++ b/src/plugins/controls/public/services/kibana/data_views.ts @@ -0,0 +1,28 @@ +/* + * 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 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 or the Server + * Side Public License, v 1. + */ + +import { ControlsPluginStartDeps } from '../../types'; +import { ControlsDataViewsService } from '../data_views'; +import { KibanaPluginServiceFactory } from '../../../../presentation_util/public'; + +export type DataViewsServiceFactory = KibanaPluginServiceFactory< + ControlsDataViewsService, + ControlsPluginStartDeps +>; + +export const dataViewsServiceFactory: DataViewsServiceFactory = ({ startPlugins }) => { + const { + dataViews: { get, getIdsWithTitle, getDefaultId }, + } = startPlugins; + + return { + get, + getDefaultId, + getIdsWithTitle, + }; +}; diff --git a/src/plugins/controls/public/services/kibana/index.ts b/src/plugins/controls/public/services/kibana/index.ts new file mode 100644 index 0000000000000..5f7f05705203e --- /dev/null +++ b/src/plugins/controls/public/services/kibana/index.ts @@ -0,0 +1,36 @@ +/* + * 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 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 or the Server + * Side Public License, v 1. + */ + +import { + PluginServiceProviders, + KibanaPluginServiceParams, + PluginServiceProvider, + PluginServiceRegistry, +} from '../../../../presentation_util/public'; +import { ControlsPluginStartDeps } from '../../types'; +import { ControlsServices } from '..'; + +import { dataViewsServiceFactory } from './data_views'; +import { controlsServiceFactory } from './controls'; +import { overlaysServiceFactory } from './overlays'; +import { dataServiceFactory } from './data'; + +export const providers: PluginServiceProviders< + ControlsServices, + KibanaPluginServiceParams +> = { + data: new PluginServiceProvider(dataServiceFactory), + overlays: new PluginServiceProvider(overlaysServiceFactory), + controls: new PluginServiceProvider(controlsServiceFactory), + dataViews: new PluginServiceProvider(dataViewsServiceFactory), +}; + +export const registry = new PluginServiceRegistry< + ControlsServices, + KibanaPluginServiceParams +>(providers); diff --git a/src/plugins/presentation_util/public/services/kibana/overlays.ts b/src/plugins/controls/public/services/kibana/overlays.ts similarity index 71% rename from src/plugins/presentation_util/public/services/kibana/overlays.ts rename to src/plugins/controls/public/services/kibana/overlays.ts index b3a8d3a6e040a..43b8bd61ccb4a 100644 --- a/src/plugins/presentation_util/public/services/kibana/overlays.ts +++ b/src/plugins/controls/public/services/kibana/overlays.ts @@ -6,13 +6,13 @@ * Side Public License, v 1. */ -import { PresentationUtilPluginStartDeps } from '../../types'; -import { KibanaPluginServiceFactory } from '../create'; -import { PresentationOverlaysService } from '../overlays'; +import { ControlsPluginStartDeps } from '../../types'; +import { ControlsOverlaysService } from '../overlays'; +import { KibanaPluginServiceFactory } from '../../../../presentation_util/public'; export type OverlaysServiceFactory = KibanaPluginServiceFactory< - PresentationOverlaysService, - PresentationUtilPluginStartDeps + ControlsOverlaysService, + ControlsPluginStartDeps >; export const overlaysServiceFactory: OverlaysServiceFactory = ({ coreStart }) => { const { diff --git a/src/plugins/presentation_util/public/services/overlays.ts b/src/plugins/controls/public/services/overlays.ts similarity index 93% rename from src/plugins/presentation_util/public/services/overlays.ts rename to src/plugins/controls/public/services/overlays.ts index ee90de5231896..9a30fca209c2f 100644 --- a/src/plugins/presentation_util/public/services/overlays.ts +++ b/src/plugins/controls/public/services/overlays.ts @@ -13,7 +13,7 @@ import { OverlayRef, } from '../../../../core/public'; -export interface PresentationOverlaysService { +export interface ControlsOverlaysService { openFlyout(mount: MountPoint, options?: OverlayFlyoutOpenOptions): OverlayRef; openConfirm(message: MountPoint | string, options?: OverlayModalConfirmOptions): Promise; } diff --git a/src/plugins/presentation_util/public/services/kibana/controls.ts b/src/plugins/controls/public/services/storybook/controls.ts similarity index 65% rename from src/plugins/presentation_util/public/services/kibana/controls.ts rename to src/plugins/controls/public/services/storybook/controls.ts index e5dc84a3dd645..7c33ee8c33527 100644 --- a/src/plugins/presentation_util/public/services/kibana/controls.ts +++ b/src/plugins/controls/public/services/storybook/controls.ts @@ -6,8 +6,8 @@ * Side Public License, v 1. */ -import { PluginServiceFactory } from '../create'; -import { getCommonControlsService, PresentationControlsService } from '../controls'; +import { PluginServiceFactory } from '../../../../presentation_util/public'; +import { getCommonControlsService, ControlsService } from '../controls'; -export type ControlsServiceFactory = PluginServiceFactory; +export type ControlsServiceFactory = PluginServiceFactory; export const controlsServiceFactory = () => getCommonControlsService(); diff --git a/src/plugins/presentation_util/public/services/storybook/data.ts b/src/plugins/controls/public/services/storybook/data.ts similarity index 83% rename from src/plugins/presentation_util/public/services/storybook/data.ts rename to src/plugins/controls/public/services/storybook/data.ts index 841ee1bd9be71..6d4942b358ac3 100644 --- a/src/plugins/presentation_util/public/services/storybook/data.ts +++ b/src/plugins/controls/public/services/storybook/data.ts @@ -6,10 +6,10 @@ * Side Public License, v 1. */ +import { PluginServiceFactory } from '../../../../presentation_util/public'; import { DataPublicPluginStart } from '../../../../data/public'; import { DataViewField } from '../../../../data_views/common'; -import { PresentationDataService } from '../data'; -import { PluginServiceFactory } from '../create'; +import { ControlsDataService } from '../data'; let valueSuggestionMethod = ({ field, query }: { field: DataViewField; query: string }) => Promise.resolve(['storybook', 'default', 'values']); @@ -17,7 +17,7 @@ export const replaceValueSuggestionMethod = ( newMethod: ({ field, query }: { field: DataViewField; query: string }) => Promise ) => (valueSuggestionMethod = newMethod); -export type DataServiceFactory = PluginServiceFactory; +export type DataServiceFactory = PluginServiceFactory; export const dataServiceFactory: DataServiceFactory = () => ({ autocomplete: { getValueSuggestions: valueSuggestionMethod, diff --git a/src/plugins/controls/public/services/storybook/data_views.ts b/src/plugins/controls/public/services/storybook/data_views.ts new file mode 100644 index 0000000000000..5248dfbd70507 --- /dev/null +++ b/src/plugins/controls/public/services/storybook/data_views.ts @@ -0,0 +1,29 @@ +/* + * 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 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 or the Server + * Side Public License, v 1. + */ + +import { PluginServiceFactory } from '../../../../presentation_util/public'; +import { DataViewsPublicPluginStart } from '../../../../data_views/public'; +import { ControlsDataViewsService } from '../data_views'; +import { DataView } from '../../../../data_views/common'; + +export type DataViewsServiceFactory = PluginServiceFactory; + +let currentDataView: DataView; +export const injectStorybookDataView = (dataView: DataView) => (currentDataView = dataView); + +export const dataViewsServiceFactory: DataViewsServiceFactory = () => ({ + get: (() => + new Promise((r) => + setTimeout(() => r(currentDataView), 100) + ) as unknown) as DataViewsPublicPluginStart['get'], + getIdsWithTitle: (() => + new Promise((r) => + setTimeout(() => r([{ id: currentDataView.id, title: currentDataView.title }]), 100) + ) as unknown) as DataViewsPublicPluginStart['getIdsWithTitle'], + getDefaultId: () => Promise.resolve(currentDataView?.id ?? null), +}); diff --git a/src/plugins/controls/public/services/storybook/index.ts b/src/plugins/controls/public/services/storybook/index.ts new file mode 100644 index 0000000000000..36d8e7e78869d --- /dev/null +++ b/src/plugins/controls/public/services/storybook/index.ts @@ -0,0 +1,32 @@ +/* + * 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 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 or the Server + * Side Public License, v 1. + */ + +import { + PluginServices, + PluginServiceProviders, + PluginServiceProvider, + PluginServiceRegistry, +} from '../../../../presentation_util/public'; +import { ControlsServices } from '..'; +import { dataServiceFactory } from './data'; +import { overlaysServiceFactory } from './overlays'; +import { controlsServiceFactory } from './controls'; +import { dataViewsServiceFactory } from './data_views'; + +export type { ControlsServices } from '..'; + +export const providers: PluginServiceProviders = { + dataViews: new PluginServiceProvider(dataViewsServiceFactory), + data: new PluginServiceProvider(dataServiceFactory), + overlays: new PluginServiceProvider(overlaysServiceFactory), + controls: new PluginServiceProvider(controlsServiceFactory), +}; + +export const pluginServices = new PluginServices(); + +export const registry = new PluginServiceRegistry(providers); diff --git a/src/plugins/presentation_util/public/services/storybook/overlays.tsx b/src/plugins/controls/public/services/storybook/overlays.tsx similarity index 95% rename from src/plugins/presentation_util/public/services/storybook/overlays.tsx rename to src/plugins/controls/public/services/storybook/overlays.tsx index 50194fb636fa4..9ab4ea0b2c450 100644 --- a/src/plugins/presentation_util/public/services/storybook/overlays.tsx +++ b/src/plugins/controls/public/services/storybook/overlays.tsx @@ -7,20 +7,20 @@ */ import { EuiConfirmModal, EuiFlyout } from '@elastic/eui'; -import React from 'react'; import { render, unmountComponentAtNode } from 'react-dom'; import { Subject } from 'rxjs'; +import React from 'react'; import { MountPoint, OverlayFlyoutOpenOptions, OverlayModalConfirmOptions, OverlayRef, } from '../../../../../core/public'; +import { ControlsOverlaysService } from '../overlays'; import { MountWrapper } from '../../../../../core/public/utils'; -import { PluginServiceFactory } from '../create'; -import { PresentationOverlaysService } from '../overlays'; +import { PluginServiceFactory } from '../../../../presentation_util/public'; -type OverlaysServiceFactory = PluginServiceFactory; +type OverlaysServiceFactory = PluginServiceFactory; /** * This code is a storybook stub version of src/core/public/overlays/overlay_service.ts diff --git a/src/plugins/presentation_util/public/services/storybook/controls.ts b/src/plugins/controls/public/services/stub/controls.ts similarity index 65% rename from src/plugins/presentation_util/public/services/storybook/controls.ts rename to src/plugins/controls/public/services/stub/controls.ts index e5dc84a3dd645..7c33ee8c33527 100644 --- a/src/plugins/presentation_util/public/services/storybook/controls.ts +++ b/src/plugins/controls/public/services/stub/controls.ts @@ -6,8 +6,8 @@ * Side Public License, v 1. */ -import { PluginServiceFactory } from '../create'; -import { getCommonControlsService, PresentationControlsService } from '../controls'; +import { PluginServiceFactory } from '../../../../presentation_util/public'; +import { getCommonControlsService, ControlsService } from '../controls'; -export type ControlsServiceFactory = PluginServiceFactory; +export type ControlsServiceFactory = PluginServiceFactory; export const controlsServiceFactory = () => getCommonControlsService(); diff --git a/src/plugins/controls/public/services/stub/index.ts b/src/plugins/controls/public/services/stub/index.ts new file mode 100644 index 0000000000000..6927aa65c12b8 --- /dev/null +++ b/src/plugins/controls/public/services/stub/index.ts @@ -0,0 +1,28 @@ +/* + * 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 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 or the Server + * Side Public License, v 1. + */ + +import { + PluginServiceProviders, + PluginServiceProvider, + PluginServiceRegistry, +} from '../../../../presentation_util/public'; +import { ControlsServices } from '..'; +import { overlaysServiceFactory } from './overlays'; +import { controlsServiceFactory } from './controls'; + +import { dataServiceFactory } from '../storybook/data'; +import { dataViewsServiceFactory } from '../storybook/data_views'; + +export const providers: PluginServiceProviders = { + data: new PluginServiceProvider(dataServiceFactory), + overlays: new PluginServiceProvider(overlaysServiceFactory), + controls: new PluginServiceProvider(controlsServiceFactory), + dataViews: new PluginServiceProvider(dataViewsServiceFactory), +}; + +export const registry = new PluginServiceRegistry(providers); diff --git a/src/plugins/presentation_util/public/services/stub/overlays.ts b/src/plugins/controls/public/services/stub/overlays.ts similarity index 81% rename from src/plugins/presentation_util/public/services/stub/overlays.ts rename to src/plugins/controls/public/services/stub/overlays.ts index ecdec96d600d8..3c363111a6646 100644 --- a/src/plugins/presentation_util/public/services/stub/overlays.ts +++ b/src/plugins/controls/public/services/stub/overlays.ts @@ -12,10 +12,10 @@ import { OverlayModalConfirmOptions, OverlayRef, } from '../../../../../core/public'; -import { PluginServiceFactory } from '../create'; -import { PresentationOverlaysService } from '../overlays'; +import { PluginServiceFactory } from '../../../../presentation_util/public'; +import { ControlsOverlaysService } from '../overlays'; -type OverlaysServiceFactory = PluginServiceFactory; +type OverlaysServiceFactory = PluginServiceFactory; class StubRef implements OverlayRef { public readonly onClose: Promise = Promise.resolve(); diff --git a/src/plugins/presentation_util/public/components/controls/types.ts b/src/plugins/controls/public/types.ts similarity index 60% rename from src/plugins/presentation_util/public/components/controls/types.ts rename to src/plugins/controls/public/types.ts index 9d530fefe7373..70438baec756c 100644 --- a/src/plugins/presentation_util/public/components/controls/types.ts +++ b/src/plugins/controls/public/types.ts @@ -7,9 +7,18 @@ */ import { Filter } from '@kbn/es-query'; -import { DataView } from '../../../../data_views/public'; -import { ControlInput } from '../../../common/controls/types'; -import { EmbeddableFactory, EmbeddableOutput, IEmbeddable } from '../../../../embeddable/public'; + +import { + EmbeddableFactory, + EmbeddableOutput, + EmbeddableSetup, + EmbeddableStart, + IEmbeddable, +} from '../../embeddable/public'; +import { ControlInput } from '../common/types'; +import { DataPublicPluginStart } from '../../data/public'; +import { ControlsService } from './services/controls'; +import { DataView, DataViewsPublicPluginStart } from '../../data_views/public'; export interface CommonControlOutput { filters?: Filter[]; @@ -43,6 +52,25 @@ export interface ControlEditorProps { } /** - * Re-export control types from common + * Plugin types */ -export * from '../../../common/controls/types'; + +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface ControlsPluginSetup {} + +export interface ControlsPluginStart { + controlsService: ControlsService; + ContextProvider: React.FC; +} + +export interface ControlsPluginSetupDeps { + embeddable: EmbeddableSetup; +} +export interface ControlsPluginStartDeps { + data: DataPublicPluginStart; + embeddable: EmbeddableStart; + dataViews: DataViewsPublicPluginStart; +} + +// re-export from common +export type { ControlWidth, ControlInput, ControlStyle } from '../common/types'; diff --git a/src/plugins/presentation_util/server/controls/control_group/control_group_container_factory.ts b/src/plugins/controls/server/control_group/control_group_container_factory.ts similarity index 71% rename from src/plugins/presentation_util/server/controls/control_group/control_group_container_factory.ts rename to src/plugins/controls/server/control_group/control_group_container_factory.ts index 17dcbbd249435..39e1a9fbb12c9 100644 --- a/src/plugins/presentation_util/server/controls/control_group/control_group_container_factory.ts +++ b/src/plugins/controls/server/control_group/control_group_container_factory.ts @@ -6,13 +6,13 @@ * Side Public License, v 1. */ -import { EmbeddablePersistableStateService } from 'src/plugins/embeddable/common'; -import { EmbeddableRegistryDefinition } from '../../../../embeddable/server'; -import { CONTROL_GROUP_TYPE } from '../../../common/controls'; +import { EmbeddablePersistableStateService } from '../../../embeddable/common'; +import { EmbeddableRegistryDefinition } from '../../../embeddable/server'; +import { CONTROL_GROUP_TYPE } from '../../common'; import { createControlGroupExtract, createControlGroupInject, -} from '../../../common/controls/control_group/control_group_persistable_state'; +} from '../../common/control_group/control_group_persistable_state'; export const controlGroupContainerPersistableStateServiceFactory = ( persistableStateService: EmbeddablePersistableStateService diff --git a/src/plugins/presentation_util/server/controls/control_types/options_list/options_list_embeddable_factory.ts b/src/plugins/controls/server/control_types/options_list/options_list_embeddable_factory.ts similarity index 72% rename from src/plugins/presentation_util/server/controls/control_types/options_list/options_list_embeddable_factory.ts rename to src/plugins/controls/server/control_types/options_list/options_list_embeddable_factory.ts index b9d69ea489274..846e3cfe9342c 100644 --- a/src/plugins/presentation_util/server/controls/control_types/options_list/options_list_embeddable_factory.ts +++ b/src/plugins/controls/server/control_types/options_list/options_list_embeddable_factory.ts @@ -6,12 +6,12 @@ * Side Public License, v 1. */ -import { EmbeddableRegistryDefinition } from '../../../../../embeddable/server'; -import { OPTIONS_LIST_CONTROL } from '../../../../common/controls'; +import { EmbeddableRegistryDefinition } from '../../../../embeddable/server'; +import { OPTIONS_LIST_CONTROL } from '../../../common'; import { createOptionsListExtract, createOptionsListInject, -} from '../../../../common/controls/control_types/options_list/options_list_persistable_state'; +} from '../../../common/control_types/options_list/options_list_persistable_state'; export const optionsListPersistableStateServiceFactory = (): EmbeddableRegistryDefinition => { return { diff --git a/src/plugins/presentation_util/public/components/controls/index.ts b/src/plugins/controls/server/index.ts similarity index 79% rename from src/plugins/presentation_util/public/components/controls/index.ts rename to src/plugins/controls/server/index.ts index c110bc348498d..5928186715210 100644 --- a/src/plugins/presentation_util/public/components/controls/index.ts +++ b/src/plugins/controls/server/index.ts @@ -6,6 +6,6 @@ * Side Public License, v 1. */ -export * from './control_group'; -export * from './control_types'; -export * from './types'; +import { ControlsPlugin } from './plugin'; + +export const plugin = () => new ControlsPlugin(); diff --git a/src/plugins/controls/server/plugin.ts b/src/plugins/controls/server/plugin.ts new file mode 100644 index 0000000000000..fa7b7970c7e64 --- /dev/null +++ b/src/plugins/controls/server/plugin.ts @@ -0,0 +1,33 @@ +/* + * 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 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 or the Server + * Side Public License, v 1. + */ + +import { CoreSetup, Plugin } from 'kibana/server'; +import { EmbeddableSetup } from '../../embeddable/server'; +import { controlGroupContainerPersistableStateServiceFactory } from './control_group/control_group_container_factory'; +import { optionsListPersistableStateServiceFactory } from './control_types/options_list/options_list_embeddable_factory'; + +interface SetupDeps { + embeddable: EmbeddableSetup; +} + +export class ControlsPlugin implements Plugin { + public setup(core: CoreSetup, plugins: SetupDeps) { + plugins.embeddable.registerEmbeddableFactory(optionsListPersistableStateServiceFactory()); + + plugins.embeddable.registerEmbeddableFactory( + controlGroupContainerPersistableStateServiceFactory(plugins.embeddable) + ); + return {}; + } + + public start() { + return {}; + } + + public stop() {} +} diff --git a/src/plugins/controls/storybook/decorator.tsx b/src/plugins/controls/storybook/decorator.tsx new file mode 100644 index 0000000000000..603bddf320627 --- /dev/null +++ b/src/plugins/controls/storybook/decorator.tsx @@ -0,0 +1,46 @@ +/* + * 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 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 or the Server + * Side Public License, v 1. + */ + +import React from 'react'; + +import { DecoratorFn } from '@storybook/react'; +import { I18nProvider } from '@kbn/i18n-react'; +import { pluginServices } from '../public/services'; +import { ControlsServices } from '../public/services'; +import { providers } from '../public/services/storybook'; +import { PluginServiceRegistry } from '../../presentation_util/public'; +import { KibanaContextProvider as KibanaReactProvider } from '../../kibana_react/public'; + +const settings = new Map(); +settings.set('darkMode', true); + +const services = { + http: { + basePath: { + get: () => '', + prepend: () => '', + remove: () => '', + serverBasePath: '', + }, + }, + uiSettings: settings, +}; + +export const servicesContextDecorator: DecoratorFn = (story: Function, storybook) => { + const registry = new PluginServiceRegistry(providers); + pluginServices.setRegistry(registry.start(storybook.args)); + const ContextProvider = pluginServices.getContextProvider(); + + return ( + + + {story()} + + + ); +}; diff --git a/src/plugins/controls/storybook/main.ts b/src/plugins/controls/storybook/main.ts new file mode 100644 index 0000000000000..13f55f8be2df8 --- /dev/null +++ b/src/plugins/controls/storybook/main.ts @@ -0,0 +1,16 @@ +/* + * 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 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 or the Server + * Side Public License, v 1. + */ + +import { defaultConfigWebFinal } from '@kbn/storybook'; + +// We have to do this because the kbn/storybook preset overrides the manager entries, +// so we can't customize the theme. +module.exports = { + ...defaultConfigWebFinal, + addons: ['@storybook/addon-a11y', '@storybook/addon-essentials'], +}; diff --git a/src/plugins/controls/storybook/manager.ts b/src/plugins/controls/storybook/manager.ts new file mode 100644 index 0000000000000..1b8c9aa89e687 --- /dev/null +++ b/src/plugins/controls/storybook/manager.ts @@ -0,0 +1,26 @@ +/* + * 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 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 or the Server + * Side Public License, v 1. + */ + +import { addons } from '@storybook/addons'; +import { create } from '@storybook/theming'; +import { PANEL_ID } from '@storybook/addon-actions'; + +// @ts-expect-error There's probably a better way to do this. +import { registerThemeSwitcherAddon } from '@kbn/storybook/target_node/lib/register_theme_switcher_addon'; + +addons.setConfig({ + theme: create({ + base: 'light', + brandTitle: 'Kibana Controls Storybook', + brandUrl: 'https://github.com/elastic/kibana/tree/main/src/plugins/controls', + }), + showPanel: true.valueOf, + selectedPanel: PANEL_ID, +}); + +registerThemeSwitcherAddon(); diff --git a/src/plugins/controls/storybook/preview.tsx b/src/plugins/controls/storybook/preview.tsx new file mode 100644 index 0000000000000..e71f4e08b2027 --- /dev/null +++ b/src/plugins/controls/storybook/preview.tsx @@ -0,0 +1,29 @@ +/* + * 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 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 or the Server + * Side Public License, v 1. + */ + +import React from 'react'; +import { addDecorator } from '@storybook/react'; +import { Title, Subtitle, Description, Primary, Stories } from '@storybook/addon-docs/blocks'; + +import { servicesContextDecorator } from './decorator'; + +addDecorator(servicesContextDecorator); + +export const parameters = { + docs: { + page: () => ( + <> + + <Subtitle /> + <Description /> + <Primary /> + <Stories /> + </> + ), + }, +}; diff --git a/src/plugins/controls/tsconfig.json b/src/plugins/controls/tsconfig.json new file mode 100644 index 0000000000000..ed0c2e63011d0 --- /dev/null +++ b/src/plugins/controls/tsconfig.json @@ -0,0 +1,27 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "outDir": "./target/types", + "emitDeclarationOnly": true, + "declaration": true, + "declarationMap": true + }, + "extraPublicDirs": ["common"], + "include": [ + "common/**/*", + "public/**/*", + "public/**/*.json", + "server/**/*", + "storybook/**/*", + "../../../typings/**/*" + ], + "references": [ + { "path": "../../core/tsconfig.json" }, + { "path": "../saved_objects/tsconfig.json" }, + { "path": "../kibana_react/tsconfig.json" }, + { "path": "../embeddable/tsconfig.json" }, + { "path": "../presentation_util/tsconfig.json" }, + { "path": "../kibana_react/tsconfig.json" }, + { "path": "../data/tsconfig.json" } + ] +} diff --git a/src/plugins/dashboard/common/embeddable/dashboard_container_persistable_state.ts b/src/plugins/dashboard/common/embeddable/dashboard_container_persistable_state.ts index bc8f56fc8c4dc..c0768331d20c5 100644 --- a/src/plugins/dashboard/common/embeddable/dashboard_container_persistable_state.ts +++ b/src/plugins/dashboard/common/embeddable/dashboard_container_persistable_state.ts @@ -17,7 +17,7 @@ import { DashboardContainerStateWithType, DashboardPanelState, } from '../types'; -import { CONTROL_GROUP_TYPE } from '../../../presentation_util/common/lib'; +import { CONTROL_GROUP_TYPE } from '../../../controls/common'; const getPanelStatePrefix = (state: DashboardPanelState) => `${state.explicitInput.id}:`; diff --git a/src/plugins/dashboard/common/saved_dashboard_references.ts b/src/plugins/dashboard/common/saved_dashboard_references.ts index bc7358b49ceb4..346190e4fef91 100644 --- a/src/plugins/dashboard/common/saved_dashboard_references.ts +++ b/src/plugins/dashboard/common/saved_dashboard_references.ts @@ -19,7 +19,7 @@ import { convertSavedDashboardPanelToPanelState, } from './embeddable/embeddable_saved_object_converters'; import { SavedDashboardPanel } from './types'; -import { CONTROL_GROUP_TYPE } from '../../presentation_util/common/lib'; +import { CONTROL_GROUP_TYPE } from '../../controls/common'; export interface ExtractDeps { embeddablePersistableStateService: EmbeddablePersistableStateService; diff --git a/src/plugins/dashboard/common/types.ts b/src/plugins/dashboard/common/types.ts index bfe53514969d7..29e3d48d7f0d5 100644 --- a/src/plugins/dashboard/common/types.ts +++ b/src/plugins/dashboard/common/types.ts @@ -22,7 +22,7 @@ import { } from './bwc/types'; import { GridData } from './embeddable/types'; -import { ControlGroupInput } from '../../presentation_util/common/controls/control_group/types'; +import { ControlGroupInput } from '../../controls/common'; export type PanelId = string; export type SavedObjectId = string; diff --git a/src/plugins/dashboard/kibana.json b/src/plugins/dashboard/kibana.json index 2be6e9b269e71..683a1a551f81d 100644 --- a/src/plugins/dashboard/kibana.json +++ b/src/plugins/dashboard/kibana.json @@ -9,6 +9,7 @@ "requiredPlugins": [ "data", "embeddable", + "controls", "inspector", "navigation", "savedObjects", diff --git a/src/plugins/dashboard/public/application/embeddable/dashboard_container.tsx b/src/plugins/dashboard/public/application/embeddable/dashboard_container.tsx index 36261fbe130a3..d9733d1a35586 100644 --- a/src/plugins/dashboard/public/application/embeddable/dashboard_container.tsx +++ b/src/plugins/dashboard/public/application/embeddable/dashboard_container.tsx @@ -47,7 +47,7 @@ import { combineDashboardFiltersWithControlGroupFilters, syncDashboardControlGroup, } from '../lib/dashboard_control_group'; -import { ControlGroupContainer } from '../../../../presentation_util/public'; +import { ControlGroupContainer } from '../../../../controls/public'; export interface DashboardContainerServices { ExitFullScreenButton: React.ComponentType<any>; diff --git a/src/plugins/dashboard/public/application/embeddable/dashboard_container_factory.tsx b/src/plugins/dashboard/public/application/embeddable/dashboard_container_factory.tsx index f7cf329d0ae35..7be36a954d2f1 100644 --- a/src/plugins/dashboard/public/application/embeddable/dashboard_container_factory.tsx +++ b/src/plugins/dashboard/public/application/embeddable/dashboard_container_factory.tsx @@ -28,7 +28,7 @@ import { ControlGroupInput, ControlGroupOutput, CONTROL_GROUP_TYPE, -} from '../../../../presentation_util/public'; +} from '../../../../controls/public'; import { getDefaultDashboardControlGroupInput } from '../../dashboard_constants'; export type DashboardContainerFactory = EmbeddableFactory< diff --git a/src/plugins/dashboard/public/application/embeddable/viewport/dashboard_viewport.tsx b/src/plugins/dashboard/public/application/embeddable/viewport/dashboard_viewport.tsx index 1e19e495585fe..a862c084de400 100644 --- a/src/plugins/dashboard/public/application/embeddable/viewport/dashboard_viewport.tsx +++ b/src/plugins/dashboard/public/application/embeddable/viewport/dashboard_viewport.tsx @@ -13,7 +13,7 @@ import { DashboardContainer, DashboardReactContextValue } from '../dashboard_con import { DashboardGrid } from '../grid'; import { context } from '../../../services/kibana_react'; import { DashboardEmptyScreen } from '../empty_screen/dashboard_empty_screen'; -import { ControlGroupContainer } from '../../../../../presentation_util/public'; +import { ControlGroupContainer } from '../../../../../controls/public'; export interface DashboardViewportProps { container: DashboardContainer; diff --git a/src/plugins/dashboard/public/application/lib/convert_dashboard_state.ts b/src/plugins/dashboard/public/application/lib/convert_dashboard_state.ts index 8d55af5808da6..89ad65f58278f 100644 --- a/src/plugins/dashboard/public/application/lib/convert_dashboard_state.ts +++ b/src/plugins/dashboard/public/application/lib/convert_dashboard_state.ts @@ -21,7 +21,7 @@ import { } from '../../types'; import { convertSavedPanelsToPanelMap } from './convert_dashboard_panels'; import { deserializeControlGroupFromDashboardSavedObject } from './dashboard_control_group'; -import { ControlGroupInput } from '../../../../presentation_util/public'; +import { ControlGroupInput } from '../../../../controls/public'; interface SavedObjectToDashboardStateProps { version: string; diff --git a/src/plugins/dashboard/public/application/lib/dashboard_control_group.ts b/src/plugins/dashboard/public/application/lib/dashboard_control_group.ts index aaf6c5f0af4fc..90d5a67c3da47 100644 --- a/src/plugins/dashboard/public/application/lib/dashboard_control_group.ts +++ b/src/plugins/dashboard/public/application/lib/dashboard_control_group.ts @@ -15,7 +15,7 @@ import { DashboardContainer } from '..'; import { DashboardState } from '../../types'; import { getDefaultDashboardControlGroupInput } from '../../dashboard_constants'; import { DashboardContainerInput, DashboardSavedObject } from '../..'; -import { ControlGroupContainer, ControlGroupInput } from '../../../../presentation_util/public'; +import { ControlGroupContainer, ControlGroupInput } from '../../../../controls/public'; // only part of the control group input should be stored in dashboard state. The rest is passed down from the dashboard. export interface DashboardControlGroupInput { diff --git a/src/plugins/dashboard/public/dashboard_constants.ts b/src/plugins/dashboard/public/dashboard_constants.ts index 6f9a30e3a7041..9063b279c25f2 100644 --- a/src/plugins/dashboard/public/dashboard_constants.ts +++ b/src/plugins/dashboard/public/dashboard_constants.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import type { ControlStyle } from '../../presentation_util/public'; +import type { ControlStyle } from '../../controls/public'; export const DASHBOARD_STATE_STORAGE_KEY = '_a'; diff --git a/src/plugins/dashboard/public/saved_dashboards/saved_dashboard.ts b/src/plugins/dashboard/public/saved_dashboards/saved_dashboard.ts index d8e8b70fc1340..52ecb9549d54d 100644 --- a/src/plugins/dashboard/public/saved_dashboards/saved_dashboard.ts +++ b/src/plugins/dashboard/public/saved_dashboards/saved_dashboard.ts @@ -18,7 +18,7 @@ import { extractReferences, injectReferences } from '../../common/saved_dashboar import { SavedObjectAttributes, SavedObjectReference } from '../../../../core/types'; import { DashboardOptions } from '../types'; -import { ControlStyle } from '../../../presentation_util/public'; +import { ControlStyle } from '../../../controls/public'; export interface DashboardSavedObject extends SavedObject { id?: string; diff --git a/src/plugins/dashboard/tsconfig.json b/src/plugins/dashboard/tsconfig.json index 78a1958a43156..680d06780543a 100644 --- a/src/plugins/dashboard/tsconfig.json +++ b/src/plugins/dashboard/tsconfig.json @@ -6,23 +6,18 @@ "declaration": true, "declarationMap": true }, - "include": [ - "*.ts", - ".storybook/**/*", - "common/**/*", - "public/**/*", - "server/**/*", - ], + "include": ["*.ts", ".storybook/**/*", "common/**/*", "public/**/*", "server/**/*"], "references": [ { "path": "../../core/tsconfig.json" }, { "path": "../inspector/tsconfig.json" }, { "path": "../kibana_react/tsconfig.json" }, { "path": "../kibana_utils/tsconfig.json" }, { "path": "../share/tsconfig.json" }, + { "path": "../controls/tsconfig.json" }, { "path": "../presentation_util/tsconfig.json" }, { "path": "../url_forwarding/tsconfig.json" }, { "path": "../usage_collection/tsconfig.json" }, - { "path": "../data/tsconfig.json"}, + { "path": "../data/tsconfig.json" }, { "path": "../embeddable/tsconfig.json" }, { "path": "../home/tsconfig.json" }, { "path": "../navigation/tsconfig.json" }, @@ -32,6 +27,6 @@ { "path": "../charts/tsconfig.json" }, { "path": "../discover/tsconfig.json" }, { "path": "../visualizations/tsconfig.json" }, - { "path": "../../../x-pack/plugins/spaces/tsconfig.json" }, + { "path": "../../../x-pack/plugins/spaces/tsconfig.json" } ] } diff --git a/src/plugins/expression_image/public/expression_renderers/image_renderer.tsx b/src/plugins/expression_image/public/expression_renderers/image_renderer.tsx index a38649f13fb32..161fcef8be0fc 100644 --- a/src/plugins/expression_image/public/expression_renderers/image_renderer.tsx +++ b/src/plugins/expression_image/public/expression_renderers/image_renderer.tsx @@ -13,7 +13,7 @@ import { Observable } from 'rxjs'; import { CoreTheme } from 'kibana/public'; import { CoreSetup } from '../../../../core/public'; import { KibanaThemeProvider } from '../../../kibana_react/public'; -import { getElasticLogo, isValidUrl, defaultTheme$ } from '../../../presentation_util/public'; +import { getElasticLogo, defaultTheme$, isValidUrl } from '../../../presentation_util/public'; import { ImageRendererConfig } from '../../common/types'; const strings = { diff --git a/src/plugins/presentation_util/README.mdx b/src/plugins/presentation_util/README.mdx index 575e8002e6eb8..2cbb03232b9dd 100755 --- a/src/plugins/presentation_util/README.mdx +++ b/src/plugins/presentation_util/README.mdx @@ -209,3 +209,98 @@ export function MyComponent() { } ``` </DocAccordion> + +## 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, embeddable input sync, and context creation. To use this system, a developer can wrap their components in the ReduxEmbeddableWrapper, supplying only an object 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). + +<DocAccordion buttonContent="Reducers Example" initialIsOpen> +```ts +// my_embeddable_reducers.ts +import { MyEmbeddableInput } from './my_embeddable'; + +export const myEmbeddableReducers = { + setSpecialBoolean: ( + state: WritableDraft<MyEmbeddableInput>, + action: PayloadAction<MyEmbeddableInput['specialBoolean']> + ) => { + state.specialBoolean = action.payload; + } +} + +``` +</DocAccordion> + +### Lazy Component and Types +Because the ReduxEmbeddableWrapper is a lazy component, it also must be unwrapped with the `withSuspense` component from Presentation Util. When you await this component, you must also pass in the type information so that the redux store and actions are properly typed. + + <DocAccordion buttonContent="Awaiting LazyReduxEmbeddableWrapper" initialIsOpen> + ```ts + // my_embeddable.tsx + + import { + withSuspense, + LazyReduxEmbeddableWrapper, + ReduxEmbeddableWrapperPropsWithChildren, + } from '../../../../presentation_util/public'; + + export interface MyEmbeddableInput { + specialBoolean: boolean + } + + const MyEmbeddableReduxWrapper = withSuspense< + ReduxEmbeddableWrapperPropsWithChildren<MyEmbeddableInput> + >(LazyReduxEmbeddableWrapper); + + ``` +</DocAccordion> + +The ReduxEmbeddableWrapper should be used inside of embeddable classes, and should wrap all components under the embeddable in the render function. + +<DocAccordion buttonContent="Wrapping Embeddable Render" initialIsOpen> + ```ts + // my_embeddable.tsx + + public render(dom: HTMLElement) { + if (this.domNode) ReactDOM.unmountComponentAtNode(this.domNode); + this.domNode = dom; + ReactDOM.render( + <MyEmbeddableReduxWrapper embeddable={this} reducers={myEmbeddableReducers}> + <MyEmbeddableComponent /> + </MyEmbeddableReduxWrapper>, + dom + ); + } + ``` +</DocAccordion> + +### Accessing Actions and State + +From components under the embeddable, actions, containerActions, and the current state of the redux store are accessed via the ReduxEmbeddableContext. This context requires the input type and the type of the reducers, and will return the appropriately typed actions, a hook for dispatching actions, a selector to get the current redux state, and a suite of `containerActions` if the embeddable is a Container. + +<DocAccordion buttonContent="Accessing Redux Embeddable Context" initialIsOpen> + ```ts + // my_embeddable_component.tsx + import { useReduxEmbeddableContext } from '../../../../presentation_util/public'; + + const { + useEmbeddableSelector, + useEmbeddableDispatch, + actions: { setSpecialBoolean }, + } = useReduxEmbeddableContext< + MyEmbeddableInput, + typeof myEmbeddableReducers + >(); + + const dispatch = useEmbeddableDispatch(); + + // current state + const { specialBoolean } = useEmbeddableSelector((state) => state); + + // change specialBoolean after 5 seconds + setTimeout(() => dispatch(setSpecialBoolean(false)), 5000); + ``` +</DocAccordion> + diff --git a/src/plugins/presentation_util/common/lib/index.ts b/src/plugins/presentation_util/common/lib/index.ts index 030780c130fa5..3fe90009ad8df 100644 --- a/src/plugins/presentation_util/common/lib/index.ts +++ b/src/plugins/presentation_util/common/lib/index.ts @@ -8,4 +8,3 @@ export * from './utils'; export * from './test_helpers'; -export * from '../controls'; diff --git a/src/plugins/presentation_util/kibana.json b/src/plugins/presentation_util/kibana.json index 32460a8455152..6c8d38a5f8a1e 100644 --- a/src/plugins/presentation_util/kibana.json +++ b/src/plugins/presentation_util/kibana.json @@ -9,16 +9,7 @@ "kibanaVersion": "kibana", "server": true, "ui": true, - "extraPublicDirs": [ - "common/lib" - ], - "requiredPlugins": [ - "savedObjects", - "data", - "dataViews", - "embeddable", - "kibanaReact", - "expressions" - ], + "extraPublicDirs": ["common/lib"], + "requiredPlugins": ["savedObjects", "kibanaReact", "embeddable", "expressions", "dataViews"], "optionalPlugins": [] } diff --git a/src/plugins/presentation_util/public/components/controls/__stories__/fixtures/flights.ts b/src/plugins/presentation_util/public/__stories__/fixtures/flights.ts similarity index 93% rename from src/plugins/presentation_util/public/components/controls/__stories__/fixtures/flights.ts rename to src/plugins/presentation_util/public/__stories__/fixtures/flights.ts index 921b7f3999faa..0ec82b1e1994b 100644 --- a/src/plugins/presentation_util/public/components/controls/__stories__/fixtures/flights.ts +++ b/src/plugins/presentation_util/public/__stories__/fixtures/flights.ts @@ -7,12 +7,8 @@ */ import { map, uniq } from 'lodash'; -import { flights } from '../fixtures/flights_data'; -import { - DataView, - DataViewField, - IIndexPatternFieldList, -} from '../../../../../../data_views/common'; +import { flights } from './flights_data'; +import { DataView, DataViewField, IIndexPatternFieldList } from '../../../../data_views/public'; export type Flight = typeof flights[number]; export type FlightField = keyof Flight; diff --git a/src/plugins/presentation_util/public/components/controls/__stories__/fixtures/flights_data.ts b/src/plugins/presentation_util/public/__stories__/fixtures/flights_data.ts similarity index 100% rename from src/plugins/presentation_util/public/components/controls/__stories__/fixtures/flights_data.ts rename to src/plugins/presentation_util/public/__stories__/fixtures/flights_data.ts diff --git a/src/plugins/presentation_util/public/__stories__/index.tsx b/src/plugins/presentation_util/public/__stories__/index.tsx index a5633c4a2dd1f..94904abf43b98 100644 --- a/src/plugins/presentation_util/public/__stories__/index.tsx +++ b/src/plugins/presentation_util/public/__stories__/index.tsx @@ -8,3 +8,5 @@ export * from './render'; export * from './wait_for'; +export * from './fixtures/flights'; +export * from './fixtures/flights_data'; diff --git a/src/plugins/presentation_util/public/components/controls/control_group/control_group_strings.ts b/src/plugins/presentation_util/public/components/controls/control_group/control_group_strings.ts deleted file mode 100644 index 111b247d7417e..0000000000000 --- a/src/plugins/presentation_util/public/components/controls/control_group/control_group_strings.ts +++ /dev/null @@ -1,206 +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 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 or the Server - * Side Public License, v 1. - */ - -import { i18n } from '@kbn/i18n'; - -export const ControlGroupStrings = { - getEmbeddableTitle: () => - i18n.translate('presentationUtil.controls.controlGroup.title', { - defaultMessage: 'Control group', - }), - emptyState: { - getCallToAction: () => - i18n.translate('presentationUtil.inputControls.controlGroup.emptyState.callToAction', { - defaultMessage: 'Controls let you filter and interact with your dashboard data', - }), - getAddControlButtonTitle: () => - i18n.translate( - 'presentationUtil.inputControls.controlGroup.emptyState.addControlButtonTitle', - { - defaultMessage: 'Add control', - } - ), - getTwoLineLoadingTitle: () => - i18n.translate('presentationUtil.inputControls.controlGroup.emptyState.twoLineLoadingTitle', { - defaultMessage: '...', - }), - }, - manageControl: { - getFlyoutCreateTitle: () => - i18n.translate( - 'presentationUtil.inputControls.controlGroup.manageControl.createFlyoutTitle', - { - defaultMessage: 'Create control', - } - ), - getFlyoutEditTitle: () => - i18n.translate('presentationUtil.inputControls.controlGroup.manageControl.editFlyoutTitle', { - defaultMessage: 'Edit control', - }), - getTitleInputTitle: () => - i18n.translate('presentationUtil.controls.controlGroup.manageControl.titleInputTitle', { - defaultMessage: 'Title', - }), - getWidthInputTitle: () => - i18n.translate('presentationUtil.inputControls.controlGroup.manageControl.widthInputTitle', { - defaultMessage: 'Control size', - }), - getSaveChangesTitle: () => - i18n.translate('presentationUtil.controls.controlGroup.manageControl.saveChangesTitle', { - defaultMessage: 'Save and close', - }), - getCancelTitle: () => - i18n.translate('presentationUtil.controls.controlGroup.manageControl.cancelTitle', { - defaultMessage: 'Cancel', - }), - }, - management: { - getAddControlTitle: () => - i18n.translate('presentationUtil.controls.controlGroup.management.addControl', { - defaultMessage: 'Add control', - }), - getManageButtonTitle: () => - i18n.translate('presentationUtil.inputControls.controlGroup.management.buttonTitle', { - defaultMessage: 'Configure controls', - }), - getFlyoutTitle: () => - i18n.translate('presentationUtil.inputControls.controlGroup.management.flyoutTitle', { - defaultMessage: 'Configure controls', - }), - getDefaultWidthTitle: () => - i18n.translate('presentationUtil.inputControls.controlGroup.management.defaultWidthTitle', { - defaultMessage: 'Default size', - }), - getLayoutTitle: () => - i18n.translate('presentationUtil.controls.controlGroup.management.layoutTitle', { - defaultMessage: 'Layout', - }), - getDeleteButtonTitle: () => - i18n.translate('presentationUtil.controls.controlGroup.management.delete', { - defaultMessage: 'Delete control', - }), - getSetAllWidthsToDefaultTitle: () => - i18n.translate('presentationUtil.inputControls.controlGroup.management.setAllWidths', { - defaultMessage: 'Set all sizes to default', - }), - getDeleteAllButtonTitle: () => - i18n.translate('presentationUtil.controls.controlGroup.management.deleteAll', { - defaultMessage: 'Delete all', - }), - controlWidth: { - getWidthSwitchLegend: () => - i18n.translate( - 'presentationUtil.controls.controlGroup.management.layout.controlWidthLegend', - { - defaultMessage: 'Change control size', - } - ), - getAutoWidthTitle: () => - i18n.translate('presentationUtil.controls.controlGroup.management.layout.auto', { - defaultMessage: 'Auto', - }), - getSmallWidthTitle: () => - i18n.translate('presentationUtil.controls.controlGroup.management.layout.small', { - defaultMessage: 'Small', - }), - getMediumWidthTitle: () => - i18n.translate('presentationUtil.controls.controlGroup.management.layout.medium', { - defaultMessage: 'Medium', - }), - getLargeWidthTitle: () => - i18n.translate('presentationUtil.controls.controlGroup.management.layout.large', { - defaultMessage: 'Large', - }), - }, - controlStyle: { - getDesignSwitchLegend: () => - i18n.translate( - 'presentationUtil.controls.controlGroup.management.layout.designSwitchLegend', - { - defaultMessage: 'Switch control designs', - } - ), - getSingleLineTitle: () => - i18n.translate('presentationUtil.inputControls.controlGroup.management.layout.singleLine', { - defaultMessage: 'Single line', - }), - getTwoLineTitle: () => - i18n.translate('presentationUtil.inputControls.controlGroup.management.layout.twoLine', { - defaultMessage: 'Double line', - }), - }, - deleteControls: { - getDeleteAllTitle: () => - i18n.translate('presentationUtil.controls.controlGroup.management.delete.deleteAllTitle', { - defaultMessage: 'Delete all controls?', - }), - getDeleteTitle: () => - i18n.translate('presentationUtil.controls.controlGroup.management.delete.deleteTitle', { - defaultMessage: 'Delete control?', - }), - getSubtitle: () => - i18n.translate('presentationUtil.controls.controlGroup.management.delete.sub', { - defaultMessage: 'Controls are not recoverable once removed.', - }), - getConfirm: () => - i18n.translate('presentationUtil.controls.controlGroup.management.delete.confirm', { - defaultMessage: 'Delete', - }), - getCancel: () => - i18n.translate('presentationUtil.controls.controlGroup.management.delete.cancel', { - defaultMessage: 'Cancel', - }), - }, - discardChanges: { - getTitle: () => - i18n.translate('presentationUtil.inputControls.controlGroup.management.discard.title', { - defaultMessage: 'Discard changes?', - }), - getSubtitle: () => - i18n.translate('presentationUtil.inputControls.controlGroup.management.discard.sub', { - defaultMessage: `Changes that you've made to this control will be discarded, are you sure you want to continue?`, - }), - getConfirm: () => - i18n.translate('presentationUtil.inputControls.controlGroup.management.discard.confirm', { - defaultMessage: 'Discard changes', - }), - getCancel: () => - i18n.translate('presentationUtil.controls.controlGroup.management.discard.cancel', { - defaultMessage: 'Cancel', - }), - }, - discardNewControl: { - getTitle: () => - i18n.translate('presentationUtil.inputControls.controlGroup.management.deleteNew.title', { - defaultMessage: 'Discard new control', - }), - getSubtitle: () => - i18n.translate('presentationUtil.inputControls.controlGroup.management.deleteNew.sub', { - defaultMessage: `Changes that you've made to this control will be discarded, are you sure you want to continue?`, - }), - getConfirm: () => - i18n.translate('presentationUtil.inputControls.controlGroup.management.deleteNew.confirm', { - defaultMessage: 'Discard control', - }), - getCancel: () => - i18n.translate('presentationUtil.controls.controlGroup.management.deleteNew.cancel', { - defaultMessage: 'Cancel', - }), - }, - }, - floatingActions: { - getEditButtonTitle: () => - i18n.translate('presentationUtil.inputControls.controlGroup.floatingActions.editTitle', { - defaultMessage: 'Edit control', - }), - getRemoveButtonTitle: () => - i18n.translate('presentationUtil.controls.controlGroup.floatingActions.removeTitle', { - defaultMessage: 'Remove control', - }), - }, -}; diff --git a/src/plugins/presentation_util/public/components/data_view_picker/data_view_picker.stories.tsx b/src/plugins/presentation_util/public/components/data_view_picker/data_view_picker.stories.tsx index b8b0c46e7823d..f8c1539ecda28 100644 --- a/src/plugins/presentation_util/public/components/data_view_picker/data_view_picker.stories.tsx +++ b/src/plugins/presentation_util/public/components/data_view_picker/data_view_picker.stories.tsx @@ -12,7 +12,7 @@ import useMount from 'react-use/lib/useMount'; import { DataViewPicker } from './data_view_picker'; import { DataView, DataViewListItem } from '../../../../data_views/common'; import { injectStorybookDataView } from '../../services/storybook/data_views'; -import { storybookFlightsDataView } from '../controls/__stories__/fixtures/flights'; +import { storybookFlightsDataView } from '../../mocks'; import { pluginServices, registry, StorybookParams } from '../../services/storybook'; export default { diff --git a/src/plugins/presentation_util/public/components/data_view_picker/data_view_picker.tsx b/src/plugins/presentation_util/public/components/data_view_picker/data_view_picker.tsx index 2911ae7a1e687..2391f945d478a 100644 --- a/src/plugins/presentation_util/public/components/data_view_picker/data_view_picker.tsx +++ b/src/plugins/presentation_util/public/components/data_view_picker/data_view_picker.tsx @@ -114,3 +114,7 @@ export function DataViewPicker({ </> ); } + +// required for dynamic import using React.lazy() +// eslint-disable-next-line import/no-default-export +export default DataViewPicker; diff --git a/src/plugins/presentation_util/public/components/field_picker/field_picker.stories.tsx b/src/plugins/presentation_util/public/components/field_picker/field_picker.stories.tsx index 023d2be949a73..f2462f3a25bb4 100644 --- a/src/plugins/presentation_util/public/components/field_picker/field_picker.stories.tsx +++ b/src/plugins/presentation_util/public/components/field_picker/field_picker.stories.tsx @@ -10,7 +10,7 @@ import React from 'react'; import { FieldPicker } from './field_picker'; import { DataViewField } from '../../../../data_views/common'; -import { storybookFlightsDataView } from '../controls/__stories__/fixtures/flights'; +import { storybookFlightsDataView } from '../../mocks'; export default { component: FieldPicker, diff --git a/src/plugins/presentation_util/public/components/field_picker/field_picker.tsx b/src/plugins/presentation_util/public/components/field_picker/field_picker.tsx index 54efe87a7f432..f9fb6f985b629 100644 --- a/src/plugins/presentation_util/public/components/field_picker/field_picker.tsx +++ b/src/plugins/presentation_util/public/components/field_picker/field_picker.tsx @@ -142,3 +142,7 @@ export const FieldPicker = ({ </EuiFlexGroup> ); }; + +// required for dynamic import using React.lazy() +// eslint-disable-next-line import/no-default-export +export default FieldPicker; diff --git a/src/plugins/presentation_util/public/components/index.tsx b/src/plugins/presentation_util/public/components/index.tsx index b64cf9e97be9d..5a254877399ed 100644 --- a/src/plugins/presentation_util/public/components/index.tsx +++ b/src/plugins/presentation_util/public/components/index.tsx @@ -8,6 +8,7 @@ import React, { Suspense, ComponentType, ReactElement, Ref } from 'react'; import { EuiLoadingSpinner, EuiErrorBoundary } from '@elastic/eui'; +import { ReduxEmbeddableWrapperType } from './redux_embeddables/redux_embeddable_wrapper'; /** * A HOC which supplies React.Suspense with a fallback component, and a `EuiErrorBoundary` to contain errors. @@ -38,6 +39,14 @@ export const LazySavedObjectSaveModalDashboard = React.lazy( () => import('./saved_object_save_modal_dashboard') ); +export const LazyReduxEmbeddableWrapper = React.lazy( + () => import('./redux_embeddables/redux_embeddable_wrapper') +) as ReduxEmbeddableWrapperType; // Lazy component needs to be casted due to generic type props + +export const LazyDataViewPicker = React.lazy(() => import('./data_view_picker/data_view_picker')); + +export const LazyFieldPicker = React.lazy(() => import('./field_picker/field_picker')); + /** * A lazily-loaded ExpressionInput component. */ diff --git a/src/plugins/presentation_util/common/controls/index.ts b/src/plugins/presentation_util/public/components/redux_embeddables/index.ts similarity index 59% rename from src/plugins/presentation_util/common/controls/index.ts rename to src/plugins/presentation_util/public/components/redux_embeddables/index.ts index b01a242bdfa5f..55fb913635e81 100644 --- a/src/plugins/presentation_util/common/controls/index.ts +++ b/src/plugins/presentation_util/public/components/redux_embeddables/index.ts @@ -6,5 +6,12 @@ * Side Public License, v 1. */ -export * from './control_group/types'; -export * from './control_types/options_list/types'; +export { + ReduxEmbeddableContext, + useReduxContainerContext, + useReduxEmbeddableContext, +} from './redux_embeddable_context'; +export type { + ReduxContainerContextServices, + ReduxEmbeddableWrapperPropsWithChildren, +} from './types'; diff --git a/src/plugins/presentation_util/public/components/redux_embeddables/redux_embeddable_context.ts b/src/plugins/presentation_util/public/components/redux_embeddables/redux_embeddable_context.ts index 159230e4de024..40fdab429ae55 100644 --- a/src/plugins/presentation_util/public/components/redux_embeddables/redux_embeddable_context.ts +++ b/src/plugins/presentation_util/public/components/redux_embeddables/redux_embeddable_context.ts @@ -7,12 +7,12 @@ */ import { createContext, useContext } from 'react'; -import { +import type { GenericEmbeddableReducers, ReduxContainerContextServices, ReduxEmbeddableContextServices, } from './types'; -import { ContainerInput, EmbeddableInput } from '../../../../embeddable/public'; +import type { ContainerInput, EmbeddableInput } from '../../../../embeddable/public'; /** * When creating the context, a generic EmbeddableInput as placeholder is used. This will later be cast to diff --git a/src/plugins/presentation_util/public/components/redux_embeddables/redux_embeddable_wrapper.tsx b/src/plugins/presentation_util/public/components/redux_embeddables/redux_embeddable_wrapper.tsx index 9e7b53fb21c3b..a23dcf944812b 100644 --- a/src/plugins/presentation_util/public/components/redux_embeddables/redux_embeddable_wrapper.tsx +++ b/src/plugins/presentation_util/public/components/redux_embeddables/redux_embeddable_wrapper.tsx @@ -15,9 +15,10 @@ import { Filter } from '@kbn/es-query'; import { isEqual } from 'lodash'; import { + ReduxEmbeddableWrapperProps, ReduxContainerContextServices, ReduxEmbeddableContextServices, - ReduxEmbeddableWrapperProps, + ReduxEmbeddableWrapperPropsWithChildren, } from './types'; import { IContainer, @@ -78,7 +79,7 @@ export const getExplicitInput = <InputType extends EmbeddableInput = EmbeddableI * or ReduxContainerContext to interface with the state of the embeddable. */ export const ReduxEmbeddableWrapper = <InputType extends EmbeddableInput = EmbeddableInput>( - props: PropsWithChildren<ReduxEmbeddableWrapperProps<InputType>> + props: ReduxEmbeddableWrapperPropsWithChildren<InputType> ) => { const { embeddable, reducers, diffInput } = useMemo( () => ({ ...getDefaultProps<InputType>(), ...props }), @@ -98,6 +99,13 @@ export const ReduxEmbeddableWrapper = <InputType extends EmbeddableInput = Embed return; }, [embeddable]); + const ReduxEmbeddableStoreProvider = useMemo( + () => + ({ children }: PropsWithChildren<{}>) => + <Provider store={getManagedEmbeddablesStore()}>{children}</Provider>, + [] + ); + const reduxEmbeddableContext: ReduxEmbeddableContextServices | ReduxContainerContextServices = useMemo(() => { const key = `${embeddable.type}_${embeddable.id}`; @@ -145,19 +153,20 @@ export const ReduxEmbeddableWrapper = <InputType extends EmbeddableInput = Embed return { useEmbeddableDispatch: () => useDispatch<typeof store.dispatch>(), useEmbeddableSelector, + ReduxEmbeddableStoreProvider, actions: slice.actions as ReduxEmbeddableContextServices['actions'], containerActions, }; - }, [reducers, embeddable, containerActions]); + }, [reducers, embeddable, containerActions, ReduxEmbeddableStoreProvider]); return ( - <Provider store={getManagedEmbeddablesStore()}> + <ReduxEmbeddableStoreProvider> <ReduxEmbeddableContext.Provider value={reduxEmbeddableContext}> <ReduxEmbeddableSync diffInput={diffInput} embeddable={embeddable}> {props.children} </ReduxEmbeddableSync> </ReduxEmbeddableContext.Provider> - </Provider> + </ReduxEmbeddableStoreProvider> ); }; @@ -225,3 +234,9 @@ const ReduxEmbeddableSync = <InputType extends EmbeddableInput = EmbeddableInput return <>{children}</>; }; + +// required for dynamic import using React.lazy() +// eslint-disable-next-line import/no-default-export +export default ReduxEmbeddableWrapper; + +export type ReduxEmbeddableWrapperType = typeof ReduxEmbeddableWrapper; diff --git a/src/plugins/presentation_util/public/components/redux_embeddables/types.ts b/src/plugins/presentation_util/public/components/redux_embeddables/types.ts index 118b5d340528e..4fcc01ed51c48 100644 --- a/src/plugins/presentation_util/public/components/redux_embeddables/types.ts +++ b/src/plugins/presentation_util/public/components/redux_embeddables/types.ts @@ -13,6 +13,7 @@ import { Dispatch, PayloadAction, } from '@reduxjs/toolkit'; +import { PropsWithChildren } from 'react'; import { TypedUseSelectorHook } from 'react-redux'; import { EmbeddableInput, @@ -35,6 +36,10 @@ export interface ReduxEmbeddableWrapperProps<InputType extends EmbeddableInput = diffInput?: (a: InputType, b: InputType) => Partial<InputType>; } +export type ReduxEmbeddableWrapperPropsWithChildren< + InputType extends EmbeddableInput = EmbeddableInput +> = PropsWithChildren<ReduxEmbeddableWrapperProps<InputType>>; + /** * This context allows components underneath the redux embeddable wrapper to get access to the actions, selector, dispatch, and containerActions. */ @@ -47,6 +52,7 @@ export interface ReduxEmbeddableContextServices< Parameters<ReducerType[Property]>[1]['payload'] >; } & { updateEmbeddableReduxState: ActionCreatorWithPayload<Partial<InputType>> }; + ReduxEmbeddableStoreProvider: React.FC<PropsWithChildren<{}>>; useEmbeddableSelector: TypedUseSelectorHook<InputType>; useEmbeddableDispatch: () => Dispatch<AnyAction>; } diff --git a/src/plugins/presentation_util/public/index.ts b/src/plugins/presentation_util/public/index.ts index 6d83770499e78..7148b9fb6c7dd 100644 --- a/src/plugins/presentation_util/public/index.ts +++ b/src/plugins/presentation_util/public/index.ts @@ -41,6 +41,9 @@ export { LazyDashboardPicker, LazySavedObjectSaveModalDashboard, withSuspense, + LazyDataViewPicker, + LazyFieldPicker, + LazyReduxEmbeddableWrapper, } from './components'; export * from './components/types'; @@ -57,7 +60,13 @@ export { SolutionToolbarPopover, } from './components/solution_toolbar'; -export * from './components/controls'; +export { + ReduxEmbeddableContext, + useReduxContainerContext, + useReduxEmbeddableContext, + type ReduxContainerContextServices, + type ReduxEmbeddableWrapperPropsWithChildren, +} from './components/redux_embeddables'; /** * Register a set of Expression Functions with the Presentation Utility ExpressionInput. This allows diff --git a/src/plugins/presentation_util/public/mocks.ts b/src/plugins/presentation_util/public/mocks.ts index ec1c44d02c497..b569cb7436668 100644 --- a/src/plugins/presentation_util/public/mocks.ts +++ b/src/plugins/presentation_util/public/mocks.ts @@ -13,14 +13,11 @@ import { registry } from './services/kibana'; import { registerExpressionsLanguage } from '.'; const createStartContract = (coreStart: CoreStart): PresentationUtilPluginStart => { - pluginServices.setRegistry( - registry.start({ coreStart, startPlugins: { dataViews: {}, data: {} } as any }) - ); + pluginServices.setRegistry(registry.start({ coreStart, startPlugins: { dataViews: {} } as any })); const startContract: PresentationUtilPluginStart = { ContextProvider: pluginServices.getContextProvider(), labsService: pluginServices.getServices().labs, - controlsService: pluginServices.getServices().controls, registerExpressionsLanguage, }; return startContract; @@ -29,3 +26,5 @@ const createStartContract = (coreStart: CoreStart): PresentationUtilPluginStart export const presentationUtilPluginMock = { createStartContract, }; + +export * from './__stories__/fixtures/flights'; diff --git a/src/plugins/presentation_util/public/plugin.ts b/src/plugins/presentation_util/public/plugin.ts index 92802d0bc9934..9cd9027a53e76 100644 --- a/src/plugins/presentation_util/public/plugin.ts +++ b/src/plugins/presentation_util/public/plugin.ts @@ -12,16 +12,9 @@ import { registry } from './services/kibana'; import { PresentationUtilPluginSetupDeps, PresentationUtilPluginStartDeps, - ControlGroupContainerFactory, PresentationUtilPluginSetup, PresentationUtilPluginStart, - IEditableControlFactory, - ControlEditorProps, - ControlInput, - ControlEmbeddable, } from './types'; -import { OptionsListEmbeddableFactory } from './components/controls/control_types/options_list'; -import { CONTROL_GROUP_TYPE, OPTIONS_LIST_CONTROL } from '.'; import { registerExpressionsLanguage } from '.'; @@ -34,39 +27,10 @@ export class PresentationUtilPlugin PresentationUtilPluginStartDeps > { - private inlineEditors: { - [key: string]: { - controlEditorComponent?: (props: ControlEditorProps) => JSX.Element; - presaveTransformFunction?: ( - newInput: Partial<ControlInput>, - embeddable?: ControlEmbeddable - ) => Partial<ControlInput>; - }; - } = {}; - public setup( _coreSetup: CoreSetup<PresentationUtilPluginStartDeps, PresentationUtilPluginStart>, _setupPlugins: PresentationUtilPluginSetupDeps ): PresentationUtilPluginSetup { - _coreSetup.getStartServices().then(([coreStart, deps]) => { - // register control group embeddable factory - embeddable.registerEmbeddableFactory( - CONTROL_GROUP_TYPE, - new ControlGroupContainerFactory(deps.embeddable) - ); - }); - - const { embeddable } = _setupPlugins; - - // create control type embeddable factories. - const optionsListFactory = new OptionsListEmbeddableFactory(); - const editableOptionsListFactory = optionsListFactory as IEditableControlFactory; - this.inlineEditors[OPTIONS_LIST_CONTROL] = { - controlEditorComponent: editableOptionsListFactory.controlEditorComponent, - presaveTransformFunction: editableOptionsListFactory.presaveTransformFunction, - }; - embeddable.registerEmbeddableFactory(OPTIONS_LIST_CONTROL, optionsListFactory); - return {}; } @@ -75,25 +39,9 @@ export class PresentationUtilPlugin startPlugins: PresentationUtilPluginStartDeps ): PresentationUtilPluginStart { pluginServices.setRegistry(registry.start({ coreStart, startPlugins })); - const { controls: controlsService } = pluginServices.getServices(); - const { embeddable } = startPlugins; - - // register control types with controls service. - const optionsListFactory = embeddable.getEmbeddableFactory(OPTIONS_LIST_CONTROL); - // Temporarily pass along inline editors - inline editing should be made a first-class feature of embeddables - const editableOptionsListFactory = optionsListFactory as IEditableControlFactory; - const { - controlEditorComponent: optionsListControlEditor, - presaveTransformFunction: optionsListPresaveTransform, - } = this.inlineEditors[OPTIONS_LIST_CONTROL]; - editableOptionsListFactory.controlEditorComponent = optionsListControlEditor; - editableOptionsListFactory.presaveTransformFunction = optionsListPresaveTransform; - - if (optionsListFactory) controlsService.registerControlType(optionsListFactory); return { ContextProvider: pluginServices.getContextProvider(), - controlsService, labsService: pluginServices.getServices().labs, registerExpressionsLanguage, }; diff --git a/src/plugins/presentation_util/public/services/index.ts b/src/plugins/presentation_util/public/services/index.ts index d6112b86066e1..da840d7d24b2a 100644 --- a/src/plugins/presentation_util/public/services/index.ts +++ b/src/plugins/presentation_util/public/services/index.ts @@ -12,10 +12,7 @@ import { PresentationCapabilitiesService } from './capabilities'; import { PresentationDashboardsService } from './dashboards'; import { PresentationLabsService } from './labs'; import { registry as stubRegistry } from './stub'; -import { PresentationOverlaysService } from './overlays'; -import { PresentationControlsService } from './controls'; import { PresentationDataViewsService } from './data_views'; -import { PresentationDataService } from './data'; import { registerExpressionsLanguage } from '..'; export type { PresentationCapabilitiesService } from './capabilities'; @@ -25,10 +22,7 @@ export type { PresentationLabsService } from './labs'; export interface PresentationUtilServices { dashboards: PresentationDashboardsService; dataViews: PresentationDataViewsService; - data: PresentationDataService; capabilities: PresentationCapabilitiesService; - overlays: PresentationOverlaysService; - controls: PresentationControlsService; labs: PresentationLabsService; } @@ -39,7 +33,6 @@ export const getStubPluginServices = (): PresentationUtilPluginStart => { return { ContextProvider: pluginServices.getContextProvider(), labsService: pluginServices.getServices().labs, - controlsService: pluginServices.getServices().controls, registerExpressionsLanguage, }; }; diff --git a/src/plugins/presentation_util/public/services/kibana/index.ts b/src/plugins/presentation_util/public/services/kibana/index.ts index 3820442555c26..e412ca5ab1b48 100644 --- a/src/plugins/presentation_util/public/services/kibana/index.ts +++ b/src/plugins/presentation_util/public/services/kibana/index.ts @@ -18,9 +18,6 @@ import { PresentationUtilServices } from '..'; import { capabilitiesServiceFactory } from './capabilities'; import { dataViewsServiceFactory } from './data_views'; import { dashboardsServiceFactory } from './dashboards'; -import { controlsServiceFactory } from './controls'; -import { overlaysServiceFactory } from './overlays'; -import { dataServiceFactory } from './data'; import { labsServiceFactory } from './labs'; export const providers: PluginServiceProviders< @@ -30,10 +27,7 @@ export const providers: PluginServiceProviders< capabilities: new PluginServiceProvider(capabilitiesServiceFactory), labs: new PluginServiceProvider(labsServiceFactory), dataViews: new PluginServiceProvider(dataViewsServiceFactory), - data: new PluginServiceProvider(dataServiceFactory), dashboards: new PluginServiceProvider(dashboardsServiceFactory), - overlays: new PluginServiceProvider(overlaysServiceFactory), - controls: new PluginServiceProvider(controlsServiceFactory), }; export const registry = new PluginServiceRegistry< diff --git a/src/plugins/presentation_util/public/services/storybook/index.ts b/src/plugins/presentation_util/public/services/storybook/index.ts index a2d729f6d730a..18333bd5522ca 100644 --- a/src/plugins/presentation_util/public/services/storybook/index.ts +++ b/src/plugins/presentation_util/public/services/storybook/index.ts @@ -16,10 +16,7 @@ import { dashboardsServiceFactory } from '../stub/dashboards'; import { labsServiceFactory } from './labs'; import { capabilitiesServiceFactory } from './capabilities'; import { PresentationUtilServices } from '..'; -import { overlaysServiceFactory } from './overlays'; -import { controlsServiceFactory } from './controls'; import { dataViewsServiceFactory } from './data_views'; -import { dataServiceFactory } from './data'; export type { PluginServiceProviders } from '../create'; export { PluginServiceProvider, PluginServiceRegistry } from '../create'; @@ -36,9 +33,6 @@ export const providers: PluginServiceProviders<PresentationUtilServices, Storybo capabilities: new PluginServiceProvider(capabilitiesServiceFactory), dashboards: new PluginServiceProvider(dashboardsServiceFactory), dataViews: new PluginServiceProvider(dataViewsServiceFactory), - data: new PluginServiceProvider(dataServiceFactory), - overlays: new PluginServiceProvider(overlaysServiceFactory), - controls: new PluginServiceProvider(controlsServiceFactory), labs: new PluginServiceProvider(labsServiceFactory), }; diff --git a/src/plugins/presentation_util/public/services/stub/index.ts b/src/plugins/presentation_util/public/services/stub/index.ts index 2e312ff682927..34a13d030e535 100644 --- a/src/plugins/presentation_util/public/services/stub/index.ts +++ b/src/plugins/presentation_util/public/services/stub/index.ts @@ -11,21 +11,15 @@ import { dashboardsServiceFactory } from './dashboards'; import { labsServiceFactory } from './labs'; import { PluginServiceProviders, PluginServiceProvider, PluginServiceRegistry } from '../create'; import { PresentationUtilServices } from '..'; -import { overlaysServiceFactory } from './overlays'; -import { controlsServiceFactory } from './controls'; export { dashboardsServiceFactory } from './dashboards'; export { capabilitiesServiceFactory } from './capabilities'; -import { dataServiceFactory } from '../storybook/data'; import { dataViewsServiceFactory } from '../storybook/data_views'; export const providers: PluginServiceProviders<PresentationUtilServices> = { dashboards: new PluginServiceProvider(dashboardsServiceFactory), capabilities: new PluginServiceProvider(capabilitiesServiceFactory), - overlays: new PluginServiceProvider(overlaysServiceFactory), - controls: new PluginServiceProvider(controlsServiceFactory), labs: new PluginServiceProvider(labsServiceFactory), - data: new PluginServiceProvider(dataServiceFactory), dataViews: new PluginServiceProvider(dataViewsServiceFactory), }; diff --git a/src/plugins/presentation_util/public/types.ts b/src/plugins/presentation_util/public/types.ts index 3717cf2505dd8..a918ee3adaf35 100644 --- a/src/plugins/presentation_util/public/types.ts +++ b/src/plugins/presentation_util/public/types.ts @@ -6,12 +6,9 @@ * Side Public License, v 1. */ -import { DataPublicPluginStart } from '../../data/public'; +import { registerExpressionsLanguage } from '.'; import { PresentationLabsService } from './services/labs'; -import { PresentationControlsService } from './services/controls'; import { DataViewsPublicPluginStart } from '../../data_views/public'; -import { EmbeddableSetup, EmbeddableStart } from '../../embeddable/public'; -import { registerExpressionsLanguage } from '.'; // eslint-disable-next-line @typescript-eslint/no-empty-interface export interface PresentationUtilPluginSetup {} @@ -19,17 +16,11 @@ export interface PresentationUtilPluginSetup {} export interface PresentationUtilPluginStart { ContextProvider: React.FC; labsService: PresentationLabsService; - controlsService: PresentationControlsService; registerExpressionsLanguage: typeof registerExpressionsLanguage; } +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface PresentationUtilPluginSetupDeps {} -export interface PresentationUtilPluginSetupDeps { - embeddable: EmbeddableSetup; -} export interface PresentationUtilPluginStartDeps { - data: DataPublicPluginStart; - embeddable: EmbeddableStart; dataViews: DataViewsPublicPluginStart; } - -export * from './components/controls'; diff --git a/src/plugins/presentation_util/server/plugin.ts b/src/plugins/presentation_util/server/plugin.ts index 2c52fa1f6c2d8..eb55373920625 100644 --- a/src/plugins/presentation_util/server/plugin.ts +++ b/src/plugins/presentation_util/server/plugin.ts @@ -7,24 +7,11 @@ */ import { CoreSetup, Plugin } from 'kibana/server'; -import { EmbeddableSetup } from '../../embeddable/server'; -import { controlGroupContainerPersistableStateServiceFactory } from './controls/control_group/control_group_container_factory'; -import { optionsListPersistableStateServiceFactory } from './controls/control_types/options_list/options_list_embeddable_factory'; import { getUISettings } from './ui_settings'; -interface SetupDeps { - embeddable: EmbeddableSetup; -} - -export class PresentationUtilPlugin implements Plugin<object, object, SetupDeps> { - public setup(core: CoreSetup, plugins: SetupDeps) { +export class PresentationUtilPlugin implements Plugin<object, object> { + public setup(core: CoreSetup) { core.uiSettings.register(getUISettings()); - - plugins.embeddable.registerEmbeddableFactory(optionsListPersistableStateServiceFactory()); - - plugins.embeddable.registerEmbeddableFactory( - controlGroupContainerPersistableStateServiceFactory(plugins.embeddable) - ); return {}; } diff --git a/src/plugins/presentation_util/tsconfig.json b/src/plugins/presentation_util/tsconfig.json index caabd0b18af71..38f2cf3c14a12 100644 --- a/src/plugins/presentation_util/tsconfig.json +++ b/src/plugins/presentation_util/tsconfig.json @@ -6,9 +6,7 @@ "declaration": true, "declarationMap": true }, - "extraPublicDirs": [ - "common" - ], + "extraPublicDirs": ["common"], "include": [ "common/**/*", "public/**/*", @@ -20,9 +18,8 @@ "references": [ { "path": "../../core/tsconfig.json" }, { "path": "../saved_objects/tsconfig.json" }, - { "path": "../kibana_react/tsconfig.json" }, { "path": "../embeddable/tsconfig.json" }, - { "path": "../kibana_react/tsconfig.json"}, + { "path": "../kibana_react/tsconfig.json" }, { "path": "../data/tsconfig.json" } ] } diff --git a/test/functional/page_objects/dashboard_page_controls.ts b/test/functional/page_objects/dashboard_page_controls.ts index 2603608eebee9..1adc60b3596b6 100644 --- a/test/functional/page_objects/dashboard_page_controls.ts +++ b/test/functional/page_objects/dashboard_page_controls.ts @@ -7,8 +7,7 @@ */ import { WebElementWrapper } from 'test/functional/services/lib/web_element_wrapper'; -import { OPTIONS_LIST_CONTROL } from '../../../src/plugins/presentation_util/common/controls/'; -import { ControlWidth } from '../../../src/plugins/presentation_util/public/components/controls'; +import { OPTIONS_LIST_CONTROL, ControlWidth } from '../../../src/plugins/controls/common'; import { FtrService } from '../ftr_provider_context'; diff --git a/x-pack/plugins/canvas/public/functions/pie.test.js b/x-pack/plugins/canvas/public/functions/pie.test.js index ef180181701c9..6f1b66018fb49 100644 --- a/x-pack/plugins/canvas/public/functions/pie.test.js +++ b/x-pack/plugins/canvas/public/functions/pie.test.js @@ -6,7 +6,7 @@ */ import { testPie } from '../../canvas_plugin_src/functions/common/__fixtures__/test_pointseries'; -import { functionWrapper, fontStyle } from '../../../../../src/plugins/presentation_util/public'; +import { fontStyle, functionWrapper } from '../../../../../src/plugins/presentation_util/public'; import { grayscalePalette, seriesStyle, diff --git a/x-pack/plugins/canvas/public/functions/plot.test.js b/x-pack/plugins/canvas/public/functions/plot.test.js index b354c4c02b2f6..1f74698ccaf4e 100644 --- a/x-pack/plugins/canvas/public/functions/plot.test.js +++ b/x-pack/plugins/canvas/public/functions/plot.test.js @@ -5,7 +5,7 @@ * 2.0. */ -import { functionWrapper, fontStyle } from '../../../../../src/plugins/presentation_util/public'; +import { fontStyle, functionWrapper } from '../../../../../src/plugins/presentation_util/public'; import { testPlot } from '../../canvas_plugin_src/functions/common/__fixtures__/test_pointseries'; import { grayscalePalette,