From 657d593658c26a57e92c11349a0a4316af487a9d Mon Sep 17 00:00:00 2001 From: Ashwin P Chandran Date: Tue, 14 Mar 2023 07:24:13 +0000 Subject: [PATCH 01/10] adds uiActions to visBuilder Signed-off-by: Ashwin P Chandran --- .../vis_builder/opensearch_dashboards.json | 5 +- .../vis_builder/public/application/app.tsx | 3 +- .../application/components/right_nav.tsx | 4 +- .../application/components/workspace.tsx | 11 +- .../utils/get_saved_vis_builder_vis.ts | 19 ---- .../application/utils/get_top_nav_config.tsx | 4 +- .../utils/handle_vis_event.test.ts | 102 +++++++++++++++++ .../application/utils/handle_vis_event.ts | 34 ++++++ .../utils/use/use_saved_vis_builder_vis.ts | 60 ++++------ .../embeddable/vis_builder_embeddable.tsx | 106 +++++++++--------- .../vis_builder_embeddable_factory.tsx | 5 +- src/plugins/vis_builder/public/plugin.ts | 5 +- .../vis_builder/public/plugin_services.ts | 2 + .../saved_visualization_references.ts | 4 +- .../saved_visualizations/transforms.test.ts | 57 +++++++++- .../public/saved_visualizations/transforms.ts | 59 +++++++++- src/plugins/vis_builder/public/types.ts | 5 +- 17 files changed, 351 insertions(+), 134 deletions(-) delete mode 100644 src/plugins/vis_builder/public/application/utils/get_saved_vis_builder_vis.ts create mode 100644 src/plugins/vis_builder/public/application/utils/handle_vis_event.test.ts create mode 100644 src/plugins/vis_builder/public/application/utils/handle_vis_event.ts diff --git a/src/plugins/vis_builder/opensearch_dashboards.json b/src/plugins/vis_builder/opensearch_dashboards.json index 98ef5153a9b0..477deb4db841 100644 --- a/src/plugins/vis_builder/opensearch_dashboards.json +++ b/src/plugins/vis_builder/opensearch_dashboards.json @@ -11,7 +11,8 @@ "expressions", "navigation", "savedObjects", - "visualizations" + "visualizations", + "uiActions" ], "requiredBundles": [ "charts", @@ -20,4 +21,4 @@ "visDefaultEditor", "visTypeVislib" ] -} +} \ No newline at end of file diff --git a/src/plugins/vis_builder/public/application/app.tsx b/src/plugins/vis_builder/public/application/app.tsx index 2bdc2b1c631b..9a3367651fc2 100644 --- a/src/plugins/vis_builder/public/application/app.tsx +++ b/src/plugins/vis_builder/public/application/app.tsx @@ -11,12 +11,13 @@ import { DragDropProvider } from './utils/drag_drop/drag_drop_context'; import { LeftNav } from './components/left_nav'; import { TopNav } from './components/top_nav'; import { Workspace } from './components/workspace'; -import './app.scss'; import { RightNav } from './components/right_nav'; import { useOpenSearchDashboards } from '../../../opensearch_dashboards_react/public'; import { VisBuilderServices } from '../types'; import { syncQueryStateWithUrl } from '../../../data/public'; +import './app.scss'; + export const VisBuilderApp = () => { const { services: { diff --git a/src/plugins/vis_builder/public/application/components/right_nav.tsx b/src/plugins/vis_builder/public/application/components/right_nav.tsx index 70b22d600ae2..3053609ff111 100644 --- a/src/plugins/vis_builder/public/application/components/right_nav.tsx +++ b/src/plugins/vis_builder/public/application/components/right_nav.tsx @@ -25,7 +25,7 @@ import { } from '../utils/state_management'; import { getPersistedAggParams } from '../utils/get_persisted_agg_params'; -export const RightNav = () => { +export const RightNavUI = () => { const { ui, name: activeVisName } = useVisualizationType(); const [confirmAggs, setConfirmAggs] = useState(); const { @@ -121,3 +121,5 @@ const OptionItem = ({ icon, title }: { icon: IconType; title: string }) => ( {title} ); + +export const RightNav = React.memo(RightNavUI); diff --git a/src/plugins/vis_builder/public/application/components/workspace.tsx b/src/plugins/vis_builder/public/application/components/workspace.tsx index 6e3371404355..bcfa866cafeb 100644 --- a/src/plugins/vis_builder/public/application/components/workspace.tsx +++ b/src/plugins/vis_builder/public/application/components/workspace.tsx @@ -5,7 +5,7 @@ import { i18n } from '@osd/i18n'; import { EuiEmptyPrompt, EuiFlexGroup, EuiFlexItem, EuiIcon, EuiPanel } from '@elastic/eui'; -import React, { FC, useState, useMemo, useEffect, useLayoutEffect } from 'react'; +import React, { useState, useMemo, useEffect, useLayoutEffect } from 'react'; import { useOpenSearchDashboards } from '../../../../opensearch_dashboards_react/public'; import { IExpressionLoaderParams } from '../../../../expressions/public'; import { VisBuilderServices } from '../../types'; @@ -19,17 +19,19 @@ import fields_bg from '../../assets/fields_bg.svg'; import './workspace.scss'; import { ExperimentalInfo } from './experimental_info'; +import { handleVisEvent } from '../utils/handle_vis_event'; -export const Workspace: FC = ({ children }) => { +export const WorkspaceUI = () => { const { services: { expressions: { ReactExpressionRenderer }, notifications: { toasts }, data, + uiActions, }, } = useOpenSearchDashboards(); const { toExpression, ui } = useVisualizationType(); - const { aggConfigs } = useAggs(); + const { aggConfigs, indexPattern } = useAggs(); const [expression, setExpression] = useState(); const [searchContext, setSearchContext] = useState({ query: data.query.queryString.getQuery(), @@ -91,6 +93,7 @@ export const Workspace: FC = ({ children }) => { expression={expression} searchContext={searchContext} uiState={uiState} + onEvent={(event) => handleVisEvent(event, uiActions, indexPattern?.timeFieldName)} /> ) : ( @@ -127,3 +130,5 @@ export const Workspace: FC = ({ children }) => { ); }; + +export const Workspace = React.memo(WorkspaceUI); diff --git a/src/plugins/vis_builder/public/application/utils/get_saved_vis_builder_vis.ts b/src/plugins/vis_builder/public/application/utils/get_saved_vis_builder_vis.ts deleted file mode 100644 index 6e4551e67e5d..000000000000 --- a/src/plugins/vis_builder/public/application/utils/get_saved_vis_builder_vis.ts +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -import { VisBuilderServices } from '../..'; - -export const getSavedVisBuilderVis = async ( - services: VisBuilderServices, - visBuilderVisId?: string -) => { - const { savedVisBuilderLoader } = services; - if (!savedVisBuilderLoader) { - return {}; - } - const savedVisBuilderVis = await savedVisBuilderLoader.get(visBuilderVisId); - - return savedVisBuilderVis; -}; diff --git a/src/plugins/vis_builder/public/application/utils/get_top_nav_config.tsx b/src/plugins/vis_builder/public/application/utils/get_top_nav_config.tsx index 9df321822852..2a30e1700b43 100644 --- a/src/plugins/vis_builder/public/application/utils/get_top_nav_config.tsx +++ b/src/plugins/vis_builder/public/application/utils/get_top_nav_config.tsx @@ -37,13 +37,13 @@ import { showSaveModal, } from '../../../../saved_objects/public'; import { VisBuilderServices } from '../..'; -import { VisBuilderVisSavedObject } from '../../types'; +import { VisBuilderSavedObject } from '../../types'; import { AppDispatch } from './state_management'; import { EDIT_PATH, VISBUILDER_SAVED_OBJECT } from '../../../common'; import { setEditorState } from './state_management/metadata_slice'; export interface TopNavConfigParams { visualizationIdFromUrl: string; - savedVisBuilderVis: VisBuilderVisSavedObject; + savedVisBuilderVis: VisBuilderSavedObject; saveDisabledReason?: string; dispatch: AppDispatch; originatingApp?: string; diff --git a/src/plugins/vis_builder/public/application/utils/handle_vis_event.test.ts b/src/plugins/vis_builder/public/application/utils/handle_vis_event.test.ts new file mode 100644 index 000000000000..d92f77a7f51a --- /dev/null +++ b/src/plugins/vis_builder/public/application/utils/handle_vis_event.test.ts @@ -0,0 +1,102 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { ExpressionRendererEvent } from '../../../../expressions/public'; +import { VIS_EVENT_TO_TRIGGER } from '../../../../visualizations/public'; +import { handleVisEvent } from './handle_vis_event'; +import { uiActionsPluginMock } from '../../../../ui_actions/public/mocks'; +import { Action, ActionType, createAction } from '../../../../ui_actions/public'; + +const executeFn = jest.fn(); + +function createTestAction( + type: string, + checkCompatibility: (context: C) => boolean, + autoExecutable = true +): Action { + return createAction({ + type: type as ActionType, + id: type, + isCompatible: (context: C) => Promise.resolve(checkCompatibility(context)), + execute: (context) => { + return executeFn(context); + }, + shouldAutoExecute: () => Promise.resolve(autoExecutable), + }); +} + +let uiActions: ReturnType; + +describe('handleVisEvent', () => { + beforeEach(() => { + uiActions = uiActionsPluginMock.createPlugin(); + + executeFn.mockClear(); + jest.useFakeTimers(); + }); + + test('should trigger the correct event', async () => { + const event: ExpressionRendererEvent = { + name: 'filter', + data: {}, + }; + const action = createTestAction('test1', () => true); + const timeFieldName = 'test-timefeild-name'; + uiActions.setup.addTriggerAction(VIS_EVENT_TO_TRIGGER.filter, action); + + await handleVisEvent(event, uiActions.doStart(), timeFieldName); + + jest.runAllTimers(); + + expect(executeFn).toBeCalledTimes(1); + expect(executeFn).toBeCalledWith( + expect.objectContaining({ + data: { timeFieldName }, + }) + ); + }); + + test('should trigger the default trigger when not found', async () => { + const event: ExpressionRendererEvent = { + name: 'test', + data: {}, + }; + const action = createTestAction('test2', () => true); + const timeFieldName = 'test-timefeild-name'; + uiActions.setup.addTriggerAction(VIS_EVENT_TO_TRIGGER.filter, action); + + await handleVisEvent(event, uiActions.doStart(), timeFieldName); + + jest.runAllTimers(); + + expect(executeFn).toBeCalledTimes(1); + expect(executeFn).toBeCalledWith( + expect.objectContaining({ + data: { timeFieldName }, + }) + ); + }); + + test('should have the correct context for `applyfilter`', async () => { + const event: ExpressionRendererEvent = { + name: 'applyFilter', + data: {}, + }; + const action = createTestAction('test3', () => true); + const timeFieldName = 'test-timefeild-name'; + uiActions.setup.addTriggerAction(VIS_EVENT_TO_TRIGGER.applyFilter, action); + + await handleVisEvent(event, uiActions.doStart(), timeFieldName); + + jest.runAllTimers(); + + expect(executeFn).toBeCalledTimes(1); + expect(executeFn).toBeCalledWith( + expect.objectContaining({ + timeFieldName, + }) + ); + }); +}); diff --git a/src/plugins/vis_builder/public/application/utils/handle_vis_event.ts b/src/plugins/vis_builder/public/application/utils/handle_vis_event.ts new file mode 100644 index 000000000000..06e3299ce348 --- /dev/null +++ b/src/plugins/vis_builder/public/application/utils/handle_vis_event.ts @@ -0,0 +1,34 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { get } from 'lodash'; +import { ExpressionRendererEvent } from '../../../../expressions/public'; +import { VIS_EVENT_TO_TRIGGER } from '../../../../visualizations/public'; +import { UiActionsStart } from '../../../../ui_actions/public'; + +export const handleVisEvent = async ( + event: ExpressionRendererEvent, + uiActions: UiActionsStart, + timeFieldName?: string +) => { + const triggerId = get(VIS_EVENT_TO_TRIGGER, event.name, VIS_EVENT_TO_TRIGGER.filter); + let context; + + if (triggerId === VIS_EVENT_TO_TRIGGER.applyFilter) { + context = { + timeFieldName, + ...event.data, + }; + } else { + context = { + data: { + timeFieldName, + ...event.data, + }, + }; + } + + await uiActions.getTrigger(triggerId).exec(context); +}; diff --git a/src/plugins/vis_builder/public/application/utils/use/use_saved_vis_builder_vis.ts b/src/plugins/vis_builder/public/application/utils/use/use_saved_vis_builder_vis.ts index 29c14dc07b08..cd01471b4f25 100644 --- a/src/plugins/vis_builder/public/application/utils/use/use_saved_vis_builder_vis.ts +++ b/src/plugins/vis_builder/public/application/utils/use/use_saved_vis_builder_vis.ts @@ -14,16 +14,10 @@ import { import { EDIT_PATH, PLUGIN_ID } from '../../../../common'; import { VisBuilderServices } from '../../../types'; import { getCreateBreadcrumbs, getEditBreadcrumbs } from '../breadcrumbs'; -import { getSavedVisBuilderVis } from '../get_saved_vis_builder_vis'; -import { - useTypedDispatch, - setStyleState, - setVisualizationState, - VisualizationState, -} from '../state_management'; +import { useTypedDispatch, setStyleState, setVisualizationState } from '../state_management'; import { useOpenSearchDashboards } from '../../../../../opensearch_dashboards_react/public'; import { setEditorState } from '../state_management/metadata_slice'; -import { validateVisBuilderState } from '../validations/vis_builder_state_validation'; +import { getStateFromSavedObject } from '../../../saved_visualizations/transforms'; // This function can be used when instantiating a saved vis or creating a new one // using url parameters, embedding and destroying it in DOM @@ -39,6 +33,7 @@ export const useSavedVisBuilderVis = (visualizationIdFromUrl: string | undefined history, http: { basePath }, toastNotifications, + savedVisBuilderLoader, } = services; const toastNotification = (message: string) => { toastNotifications.addDanger({ @@ -51,41 +46,21 @@ export const useSavedVisBuilderVis = (visualizationIdFromUrl: string | undefined const loadSavedVisBuilderVis = async () => { try { - const savedVisBuilderVis = await getSavedVisBuilderVis(services, visualizationIdFromUrl); + const savedVisBuilderVis = await getSavedVisBuilderVis( + savedVisBuilderLoader, + visualizationIdFromUrl + ); + const { id, title, state } = getStateFromSavedObject(savedVisBuilderVis); - if (savedVisBuilderVis.id) { - chrome.setBreadcrumbs(getEditBreadcrumbs(savedVisBuilderVis.title, navigateToApp)); - chrome.docTitle.change(savedVisBuilderVis.title); + if (id && title) { + chrome.setBreadcrumbs(getEditBreadcrumbs(title, navigateToApp)); + chrome.docTitle.change(title); } else { chrome.setBreadcrumbs(getCreateBreadcrumbs(navigateToApp)); } - if ( - savedVisBuilderVis.styleState !== '{}' && - savedVisBuilderVis.visualizationState !== '{}' - ) { - const styleState = JSON.parse(savedVisBuilderVis.styleState); - const vizStateWithoutIndex = JSON.parse(savedVisBuilderVis.visualizationState); - const visualizationState: VisualizationState = { - searchField: vizStateWithoutIndex.searchField, - activeVisualization: vizStateWithoutIndex.activeVisualization, - indexPattern: savedVisBuilderVis.searchSourceFields.index, - }; - - const validateResult = validateVisBuilderState({ styleState, visualizationState }); - if (!validateResult.valid) { - throw new InvalidJSONProperty( - validateResult.errorMsg || - i18n.translate('visBuilder.useSavedVisBuilderVis.genericJSONError', { - defaultMessage: - 'Something went wrong while loading your saved object. The object may be corrupted or does not match the latest schema', - }) - ); - } - - dispatch(setStyleState(styleState)); - dispatch(setVisualizationState(visualizationState)); - } + dispatch(setStyleState(state.style)); + dispatch(setVisualizationState(state.visualization)); setSavedVisState(savedVisBuilderVis); dispatch(setEditorState({ state: 'clean' })); @@ -123,3 +98,12 @@ export const useSavedVisBuilderVis = (visualizationIdFromUrl: string | undefined return savedVisState; }; + +async function getSavedVisBuilderVis( + savedVisBuilderLoader: VisBuilderServices['savedVisBuilderLoader'], + visBuilderVisId?: string +) { + const savedVisBuilderVis = await savedVisBuilderLoader.get(visBuilderVisId); + + return savedVisBuilderVis; +} diff --git a/src/plugins/vis_builder/public/embeddable/vis_builder_embeddable.tsx b/src/plugins/vis_builder/public/embeddable/vis_builder_embeddable.tsx index 6282845372ac..8697ff393478 100644 --- a/src/plugins/vis_builder/public/embeddable/vis_builder_embeddable.tsx +++ b/src/plugins/vis_builder/public/embeddable/vis_builder_embeddable.tsx @@ -7,7 +7,7 @@ import { cloneDeep, isEqual } from 'lodash'; import ReactDOM from 'react-dom'; import { merge, Subscription } from 'rxjs'; -import { PLUGIN_ID, VisBuilderSavedObjectAttributes, VISBUILDER_SAVED_OBJECT } from '../../common'; +import { PLUGIN_ID, VISBUILDER_SAVED_OBJECT } from '../../common'; import { Embeddable, EmbeddableOutput, @@ -28,15 +28,21 @@ import { TimeRange, } from '../../../data/public'; import { validateSchemaState } from '../application/utils/validations/validate_schema_state'; -import { getExpressionLoader, getTypeService } from '../plugin_services'; +import { + getExpressionLoader, + getIndexPatterns, + getTypeService, + getUIActions, +} from '../plugin_services'; import { PersistedState } from '../../../visualizations/public'; -import { RenderState, VisualizationState } from '../application/utils/state_management'; +import { VisBuilderSavedVis } from '../saved_visualizations/transforms'; +import { handleVisEvent } from '../application/utils/handle_vis_event'; // Apparently this needs to match the saved object type for the clone and replace panel actions to work export const VISBUILDER_EMBEDDABLE = VISBUILDER_SAVED_OBJECT; export interface VisBuilderEmbeddableConfiguration { - savedVisBuilder: VisBuilderSavedObjectAttributes; + savedVis: VisBuilderSavedVis; // TODO: add indexPatterns as part of configuration // indexPatterns?: IIndexPattern[]; editPath: string; @@ -49,7 +55,7 @@ export interface VisBuilderOutput extends EmbeddableOutput { * Will contain the saved object attributes of the VisBuilder Saved Object that matches * `input.savedObjectId`. If the id is invalid, this may be undefined. */ - savedVisBuilder?: VisBuilderSavedObjectAttributes; + savedVis?: VisBuilderSavedVis; } type ExpressionLoader = InstanceType; @@ -65,13 +71,13 @@ export class VisBuilderEmbeddable extends Embeddable { - const { visualizationState: visualization = '{}', styleState: style = '{}' } = - this.savedVisBuilder || {}; - return { - visualization, - style, - }; - }; + private getSerializedState = () => JSON.stringify(this.savedVis?.state); private getExpression = async () => { - if (!this.serializedState) { - return; - } - const { visualization, style } = this.serializedState; + try { + // Check if saved visualization exists + const renderState = this.savedVis?.state; + if (!renderState) throw new Error('No saved visualization'); + + const visTypeString = renderState.visualization?.activeVisualization?.name || ''; + const visualizationType = getTypeService().get(visTypeString); + + if (!visualizationType) throw new Error(`Invalid visualization type ${visTypeString}`); + + const { toExpression, ui } = visualizationType; + const schemas = ui.containerConfig.data.schemas; + const { valid, errorMsg } = validateSchemaState(schemas, renderState.visualization); + + if (!valid && errorMsg) throw new Error(errorMsg); - const vizStateWithoutIndex = JSON.parse(visualization); - const visualizationState: VisualizationState = { - searchField: vizStateWithoutIndex.searchField, - activeVisualization: vizStateWithoutIndex.activeVisualization, - indexPattern: this.savedVisBuilder?.searchSourceFields?.index, - }; - const renderState: RenderState = { - visualization: visualizationState, - style: JSON.parse(style), - }; - const visualizationName = renderState.visualization?.activeVisualization?.name ?? ''; - const visualizationType = getTypeService().get(visualizationName); - if (!visualizationType) { - this.onContainerError(new Error(`Invalid visualization type ${visualizationName}`)); - return; - } - const { toExpression, ui } = visualizationType; - const schemas = ui.containerConfig.data.schemas; - const { valid, errorMsg } = validateSchemaState(schemas, visualizationState); - - if (!valid) { - if (errorMsg) { - this.onContainerError(new Error(errorMsg)); - return; - } - } else { - // TODO: handle error in Expression creation const exp = await toExpression(renderState, { filters: this.filters, query: this.query, timeRange: this.timeRange, }); return exp; + } catch (error) { + this.onContainerError(error as Error); + return; } }; @@ -167,7 +153,7 @@ export class VisBuilderEmbeddable extends Embeddable { + if (!this.input.disableTriggers) { + const indexPattern = await getIndexPatterns().get( + this.savedVis?.state.visualization.indexPattern ?? '' + ); + + handleVisEvent(event, getUIActions(), indexPattern.timeFieldName); + } + }) + ); + + if (this.savedVis?.description) { + div.setAttribute('data-description', this.savedVis.description); } div.setAttribute('data-test-subj', 'visBuilderLoader'); @@ -271,7 +269,7 @@ export class VisBuilderEmbeddable extends Embeddable { try { - const savedVisBuilder = await getSavedVisBuilderLoader().get(savedObjectId); + const savedObject = await getSavedVisBuilderLoader().get(savedObjectId); const editPath = `${EDIT_PATH}/${savedObjectId}`; const editUrl = getHttp().basePath.prepend(`/app/${PLUGIN_ID}${editPath}`); const isLabsEnabled = getUISettings().get(VISUALIZE_ENABLE_LABS_SETTING); @@ -91,7 +92,7 @@ export class VisBuilderEmbeddableFactoryDefinition return new VisBuilderEmbeddable( getTimeFilter(), { - savedVisBuilder, + savedVis: getStateFromSavedObject(savedObject), editUrl, editPath, editable: true, diff --git a/src/plugins/vis_builder/public/plugin.ts b/src/plugins/vis_builder/public/plugin.ts index 3995c1246de5..0c1d569f6bed 100644 --- a/src/plugins/vis_builder/public/plugin.ts +++ b/src/plugins/vis_builder/public/plugin.ts @@ -46,6 +46,7 @@ import { setTypeService, setReactExpressionRenderer, setQueryService, + setUIActions, } from './plugin_services'; import { createSavedVisBuilderLoader } from './saved_visualizations'; import { registerDefaultTypes } from './visualizations'; @@ -158,6 +159,7 @@ export class VisBuilderPlugin savedVisBuilderLoader: selfStart.savedVisBuilderLoader, embeddable: pluginsStart.embeddable, dashboard: pluginsStart.dashboard, + uiActions: pluginsStart.uiActions, }; // Instantiate the store @@ -217,7 +219,7 @@ export class VisBuilderPlugin public start( core: CoreStart, - { expressions, data }: VisBuilderPluginStartDependencies + { expressions, data, uiActions }: VisBuilderPluginStartDependencies ): VisBuilderStart { const typeService = this.typeService.start(); @@ -239,6 +241,7 @@ export class VisBuilderPlugin setTimeFilter(data.query.timefilter.timefilter); setTypeService(typeService); setUISettings(core.uiSettings); + setUIActions(uiActions); setQueryService(data.query); return { diff --git a/src/plugins/vis_builder/public/plugin_services.ts b/src/plugins/vis_builder/public/plugin_services.ts index c5583e3c5e43..844a56566d0e 100644 --- a/src/plugins/vis_builder/public/plugin_services.ts +++ b/src/plugins/vis_builder/public/plugin_services.ts @@ -9,6 +9,7 @@ import { SavedVisBuilderLoader } from './saved_visualizations'; import { HttpStart, IUiSettingsClient } from '../../../core/public'; import { ExpressionsStart } from '../../expressions/public'; import { TypeServiceStart } from './services/type_service'; +import { UiActionsStart } from '../../ui_actions/public'; export const [getSearchService, setSearchService] = createGetterSetter< DataPublicPluginStart['search'] @@ -37,6 +38,7 @@ export const [getTimeFilter, setTimeFilter] = createGetterSetter('TypeService'); export const [getUISettings, setUISettings] = createGetterSetter('UISettings'); +export const [getUIActions, setUIActions] = createGetterSetter('UIActions'); export const [getQueryService, setQueryService] = createGetterSetter< DataPublicPluginStart['query'] diff --git a/src/plugins/vis_builder/public/saved_visualizations/saved_visualization_references.ts b/src/plugins/vis_builder/public/saved_visualizations/saved_visualization_references.ts index 8a897b35ccda..06710c4d0780 100644 --- a/src/plugins/vis_builder/public/saved_visualizations/saved_visualization_references.ts +++ b/src/plugins/vis_builder/public/saved_visualizations/saved_visualization_references.ts @@ -4,11 +4,11 @@ */ import { SavedObjectReference } from '../../../../core/public'; -import { VisBuilderVisSavedObject } from '../types'; +import { VisBuilderSavedObject } from '../types'; import { injectSearchSourceReferences } from '../../../data/public'; export function injectReferences( - savedObject: VisBuilderVisSavedObject, + savedObject: VisBuilderSavedObject, references: SavedObjectReference[] ) { if (savedObject.searchSourceFields) { diff --git a/src/plugins/vis_builder/public/saved_visualizations/transforms.test.ts b/src/plugins/vis_builder/public/saved_visualizations/transforms.test.ts index 3fb5b7ff7bda..b308f7d2248f 100644 --- a/src/plugins/vis_builder/public/saved_visualizations/transforms.test.ts +++ b/src/plugins/vis_builder/public/saved_visualizations/transforms.test.ts @@ -7,8 +7,9 @@ import { coreMock } from '../../../../core/public/mocks'; import { getStubIndexPattern } from '../../../data/public/test_utils'; import { IndexPattern } from '../../../data/public'; import { RootState } from '../application/utils/state_management'; -import { VisBuilderVisSavedObject } from '../types'; -import { saveStateToSavedObject } from './transforms'; +import { VisBuilderSavedObject } from '../types'; +import { getStateFromSavedObject, saveStateToSavedObject } from './transforms'; +import { VisBuilderSavedObjectAttributes } from '../../common/vis_builder_saved_object_attributes'; const getConfig = (cfg: any) => cfg; @@ -21,9 +22,9 @@ describe('transforms', () => { beforeEach(() => { TEST_INDEX_PATTERN_ID = 'test-pattern'; - savedObject = {} as VisBuilderVisSavedObject; + savedObject = {} as VisBuilderSavedObject; rootState = { - metadata: { editor: { state: 'loading', validity: {} } }, + metadata: { editor: { state: 'loading', errors: {} } }, style: '', visualization: { searchField: '', @@ -61,4 +62,52 @@ describe('transforms', () => { ); }); }); + + describe('getStateFromSavedObject', () => { + const defaultVBSaveObj = { + styleState: '{}', + visualizationState: JSON.stringify({ + searchField: '', + }), + searchSourceFields: { + index: 'test-index', + }, + } as VisBuilderSavedObjectAttributes; + + test('should return saved object with state', () => { + const { state } = getStateFromSavedObject(defaultVBSaveObj); + + expect(state).toMatchInlineSnapshot(` + Object { + "style": Object {}, + "visualization": Object { + "indexPattern": "test-index", + "searchField": "", + }, + } + `); + }); + + test('should throw error if state is invalid', () => { + const mockVBSaveObj = { + ...defaultVBSaveObj, + }; + mockVBSaveObj.visualizationState = '{}'; + + expect(() => getStateFromSavedObject(mockVBSaveObj)).toThrowErrorMatchingInlineSnapshot( + `"/visualizationState must have required property 'searchField'"` + ); + }); + + test('should throw error if index pattern is missing', () => { + const mockVBSaveObj = { + ...defaultVBSaveObj, + }; + delete mockVBSaveObj.searchSourceFields; + + expect(() => getStateFromSavedObject(mockVBSaveObj)).toThrowErrorMatchingInlineSnapshot( + `"The saved object is missing an index pattern"` + ); + }); + }); }); diff --git a/src/plugins/vis_builder/public/saved_visualizations/transforms.ts b/src/plugins/vis_builder/public/saved_visualizations/transforms.ts index 672f80111076..8395b44177c4 100644 --- a/src/plugins/vis_builder/public/saved_visualizations/transforms.ts +++ b/src/plugins/vis_builder/public/saved_visualizations/transforms.ts @@ -3,16 +3,20 @@ * SPDX-License-Identifier: Apache-2.0 */ +import { i18n } from '@osd/i18n'; import produce from 'immer'; import { IndexPattern } from '../../../data/public'; -import { RootState, VisualizationState } from '../application/utils/state_management'; -import { VisBuilderVisSavedObject } from '../types'; +import { InvalidJSONProperty } from '../../../opensearch_dashboards_utils/public'; +import { RenderState, RootState, VisualizationState } from '../application/utils/state_management'; +import { validateVisBuilderState } from '../application/utils/validations'; +import { VisBuilderSavedObject } from '../types'; +import { VisBuilderSavedObjectAttributes } from '../../common'; export const saveStateToSavedObject = ( - obj: VisBuilderVisSavedObject, + obj: VisBuilderSavedObject, state: RootState, indexPattern: IndexPattern -): VisBuilderVisSavedObject => { +): VisBuilderSavedObject => { if (state.visualization.indexPattern !== indexPattern.id) throw new Error('indexPattern id should match the value in redux state'); @@ -26,3 +30,50 @@ export const saveStateToSavedObject = ( return obj; }; + +export interface VisBuilderSavedVis + extends Pick { + state: RenderState; +} + +export const getStateFromSavedObject = ( + obj: VisBuilderSavedObjectAttributes +): VisBuilderSavedVis => { + const { id, title, description } = obj; + const styleState = JSON.parse(obj.styleState || ''); + const vizStateWithoutIndex = JSON.parse(obj.visualizationState || ''); + const visualizationState: VisualizationState = { + ...vizStateWithoutIndex, + indexPattern: obj.searchSourceFields?.index, + }; + + const validateResult = validateVisBuilderState({ styleState, visualizationState }); + + if (!validateResult.valid) { + throw new InvalidJSONProperty( + validateResult.errorMsg || + i18n.translate('visBuilder.getStateFromSavedObject.genericJSONError', { + defaultMessage: + 'Something went wrong while loading your saved object. The object may be corrupted or does not match the latest schema', + }) + ); + } + + if (!visualizationState.indexPattern) { + throw new Error( + i18n.translate('visBuilder.getStateFromSavedObject.missingIndexPattern', { + defaultMessage: 'The saved object is missing an index pattern', + }) + ); + } + + return { + id, + title, + description, + state: { + visualization: visualizationState, + style: styleState, + }, + }; +}; diff --git a/src/plugins/vis_builder/public/types.ts b/src/plugins/vis_builder/public/types.ts index e79762bedc1f..5221a1c513ec 100644 --- a/src/plugins/vis_builder/public/types.ts +++ b/src/plugins/vis_builder/public/types.ts @@ -16,6 +16,7 @@ import { SavedObjectLoader } from '../../saved_objects/public'; import { AppMountParameters, CoreStart, ToastsStart, ScopedHistory } from '../../../core/public'; import { IOsdUrlStateStorage } from '../../opensearch_dashboards_utils/public'; import { DataPublicPluginSetup } from '../../data/public'; +import { UiActionsStart } from '../../ui_actions/public'; export type VisBuilderSetup = TypeServiceSetup; export interface VisBuilderStart extends TypeServiceStart { @@ -34,6 +35,7 @@ export interface VisBuilderPluginStartDependencies { savedObjects: SavedObjectsStart; dashboard: DashboardStart; expressions: ExpressionsStart; + uiActions: UiActionsStart; } export interface VisBuilderServices extends CoreStart { @@ -51,6 +53,7 @@ export interface VisBuilderServices extends CoreStart { scopedHistory: ScopedHistory; osdUrlStateStorage: IOsdUrlStateStorage; dashboard: DashboardStart; + uiActions: UiActionsStart; } export interface ISavedVis { @@ -62,4 +65,4 @@ export interface ISavedVis { version?: number; } -export interface VisBuilderVisSavedObject extends SavedObject, ISavedVis {} +export interface VisBuilderSavedObject extends SavedObject, ISavedVis {} From c71fc226838fad0b5429717e631dc4b79f0949c3 Mon Sep 17 00:00:00 2001 From: Ashwin P Chandran Date: Tue, 14 Mar 2023 08:07:10 +0000 Subject: [PATCH 02/10] prevents multiple errors on load Signed-off-by: Ashwin P Chandran --- .../public/application/components/workspace.tsx | 8 +++++--- .../public/saved_visualizations/transforms.test.ts | 4 ++-- .../vis_builder/public/saved_visualizations/transforms.ts | 1 + 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/plugins/vis_builder/public/application/components/workspace.tsx b/src/plugins/vis_builder/public/application/components/workspace.tsx index bcfa866cafeb..107bf4479034 100644 --- a/src/plugins/vis_builder/public/application/components/workspace.tsx +++ b/src/plugins/vis_builder/public/application/components/workspace.tsx @@ -46,15 +46,17 @@ export const WorkspaceUI = () => { async function loadExpression() { const schemas = ui.containerConfig.data.schemas; - const noAggs = aggConfigs?.aggs?.length === 0; + const noAggs = (aggConfigs?.aggs?.length ?? 0) === 0; const schemaValidation = validateSchemaState(schemas, rootState.visualization); const aggValidation = validateAggregations(aggConfigs?.aggs || []); - if (noAggs || !aggValidation.valid || !schemaValidation.valid) { + if (!aggValidation.valid || !schemaValidation.valid) { + setExpression(undefined); + if (noAggs) return; // don't show error when there are no active aggregations + const err = schemaValidation.errorMsg || aggValidation.errorMsg; if (err) toasts.addWarning(err); - setExpression(undefined); return; } diff --git a/src/plugins/vis_builder/public/saved_visualizations/transforms.test.ts b/src/plugins/vis_builder/public/saved_visualizations/transforms.test.ts index b308f7d2248f..de7963313de1 100644 --- a/src/plugins/vis_builder/public/saved_visualizations/transforms.test.ts +++ b/src/plugins/vis_builder/public/saved_visualizations/transforms.test.ts @@ -92,10 +92,10 @@ describe('transforms', () => { const mockVBSaveObj = { ...defaultVBSaveObj, }; - mockVBSaveObj.visualizationState = '{}'; + delete mockVBSaveObj.visualizationState; expect(() => getStateFromSavedObject(mockVBSaveObj)).toThrowErrorMatchingInlineSnapshot( - `"/visualizationState must have required property 'searchField'"` + `"Unexpected end of JSON input"` ); }); diff --git a/src/plugins/vis_builder/public/saved_visualizations/transforms.ts b/src/plugins/vis_builder/public/saved_visualizations/transforms.ts index 8395b44177c4..0a7a6e529a6b 100644 --- a/src/plugins/vis_builder/public/saved_visualizations/transforms.ts +++ b/src/plugins/vis_builder/public/saved_visualizations/transforms.ts @@ -43,6 +43,7 @@ export const getStateFromSavedObject = ( const styleState = JSON.parse(obj.styleState || ''); const vizStateWithoutIndex = JSON.parse(obj.visualizationState || ''); const visualizationState: VisualizationState = { + searchField: '', ...vizStateWithoutIndex, indexPattern: obj.searchSourceFields?.index, }; From ed20b436172df81cda27e88d1cbc85dc43b8bd58 Mon Sep 17 00:00:00 2001 From: Ashwin P Chandran Date: Tue, 14 Mar 2023 08:11:53 +0000 Subject: [PATCH 03/10] fixes visbuilder type errors Signed-off-by: Ashwin P Chandran --- .../vis_builder/public/embeddable/vis_builder_embeddable.tsx | 2 ++ .../public/embeddable/vis_builder_embeddable_factory.tsx | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/plugins/vis_builder/public/embeddable/vis_builder_embeddable.tsx b/src/plugins/vis_builder/public/embeddable/vis_builder_embeddable.tsx index 8697ff393478..7142c9c3c0cf 100644 --- a/src/plugins/vis_builder/public/embeddable/vis_builder_embeddable.tsx +++ b/src/plugins/vis_builder/public/embeddable/vis_builder_embeddable.tsx @@ -50,6 +50,8 @@ export interface VisBuilderEmbeddableConfiguration { editable: boolean; } +export type VisBuilderInput = SavedObjectEmbeddableInput; + export interface VisBuilderOutput extends EmbeddableOutput { /** * Will contain the saved object attributes of the VisBuilder Saved Object that matches diff --git a/src/plugins/vis_builder/public/embeddable/vis_builder_embeddable_factory.tsx b/src/plugins/vis_builder/public/embeddable/vis_builder_embeddable_factory.tsx index 610e71dbba78..f80ad18ee363 100644 --- a/src/plugins/vis_builder/public/embeddable/vis_builder_embeddable_factory.tsx +++ b/src/plugins/vis_builder/public/embeddable/vis_builder_embeddable_factory.tsx @@ -23,6 +23,7 @@ import { import { DisabledEmbeddable } from './disabled_embeddable'; import { VisBuilderEmbeddable, + VisBuilderInput, VisBuilderOutput, VISBUILDER_EMBEDDABLE, } from './vis_builder_embeddable'; @@ -76,7 +77,7 @@ export class VisBuilderEmbeddableFactoryDefinition public async createFromSavedObject( savedObjectId: string, - input: Partial & { id: string }, + input: VisBuilderInput, parent?: IContainer ): Promise { try { From 61aeccb3070246f26fe2b3bd9eca5a37bd36f0a2 Mon Sep 17 00:00:00 2001 From: Ashwin P Chandran Date: Thu, 23 Mar 2023 22:40:47 +0000 Subject: [PATCH 04/10] fixes save Signed-off-by: Ashwin P Chandran --- .../application/utils/use/use_saved_vis_builder_vis.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/plugins/vis_builder/public/application/utils/use/use_saved_vis_builder_vis.ts b/src/plugins/vis_builder/public/application/utils/use/use_saved_vis_builder_vis.ts index cd01471b4f25..604c90a25ccd 100644 --- a/src/plugins/vis_builder/public/application/utils/use/use_saved_vis_builder_vis.ts +++ b/src/plugins/vis_builder/public/application/utils/use/use_saved_vis_builder_vis.ts @@ -50,18 +50,18 @@ export const useSavedVisBuilderVis = (visualizationIdFromUrl: string | undefined savedVisBuilderLoader, visualizationIdFromUrl ); - const { id, title, state } = getStateFromSavedObject(savedVisBuilderVis); - if (id && title) { + if (savedVisBuilderVis.id) { + const { title, state } = getStateFromSavedObject(savedVisBuilderVis); chrome.setBreadcrumbs(getEditBreadcrumbs(title, navigateToApp)); chrome.docTitle.change(title); + + dispatch(setStyleState(state.style)); + dispatch(setVisualizationState(state.visualization)); } else { chrome.setBreadcrumbs(getCreateBreadcrumbs(navigateToApp)); } - dispatch(setStyleState(state.style)); - dispatch(setVisualizationState(state.visualization)); - setSavedVisState(savedVisBuilderVis); dispatch(setEditorState({ state: 'clean' })); } catch (error) { From f4f2cf07b0e90653aef2db075761156355c212f3 Mon Sep 17 00:00:00 2001 From: Ashwin P Chandran Date: Thu, 30 Mar 2023 03:15:01 +0000 Subject: [PATCH 05/10] updates changelog Signed-off-by: Ashwin P Chandran --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d3628963e9b5..b96d98f7e59f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -71,6 +71,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - Use mirrors to download Node.js binaries to escape sporadic 404 errors ([#3619](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3619)) - [Multiple DataSource] Refactor dev tool console to use opensearch-js client to send requests ([#3544](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3544)) - [Data] Add geo shape filter field ([#3605](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3605)) +- [VisBuilder] Adds UI actions handler ([#3732](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3732)) ### 🐛 Bug Fixes @@ -112,6 +113,9 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - Clean up and rebuild `@osd/pm` ([#3570](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3570)) - [Vega] Add Filter custom label for opensearchDashboardsAddFilter ([#3640](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3640)) - [Timeline] Fix y-axis label color in dark mode ([#3698](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3698)) +- [VisBuilder] Fixes multiple warnings thrown on page load ([#3732](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3732)) +- [VisBuilder] Fixes Firefox legend selection issue ([#3732](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3732)) +- [VisBuilder] Fixes type errors ([#3732](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3732)) ### 🚞 Infrastructure From a4dbecfbf4ae53859561cac6e0e0e6f36f15f4fe Mon Sep 17 00:00:00 2001 From: Ashwin P Chandran Date: Fri, 14 Apr 2023 14:40:47 -0700 Subject: [PATCH 06/10] Update CHANGELOG.md Signed-off-by: Ashwin P Chandran Co-authored-by: Josh Romero --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f53a454bff4d..a1cf3f8d6323 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -71,7 +71,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - Use mirrors to download Node.js binaries to escape sporadic 404 errors ([#3619](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3619)) - [Multiple DataSource] Refactor dev tool console to use opensearch-js client to send requests ([#3544](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3544)) - [Data] Add geo shape filter field ([#3605](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3605)) -- [VisBuilder] Adds UI actions handler ([#3732](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3732)) +- [VisBuilder] Add UI actions handler ([#3732](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3732)) - [Multiple DataSource] Allow create and distinguish index pattern with same name but from different datasources ([#3571](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3571)) - [Multiple DataSource] Integrate multiple datasource with dev tool console ([#3754](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3754)) From cb25986b6da01366b8f79775339784f435281688 Mon Sep 17 00:00:00 2001 From: Ashwin P Chandran Date: Fri, 14 Apr 2023 14:41:10 -0700 Subject: [PATCH 07/10] Update CHANGELOG.md Signed-off-by: Ashwin P Chandran Co-authored-by: Josh Romero --- CHANGELOG.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a1cf3f8d6323..2b791007c3be 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -115,9 +115,9 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - Clean up and rebuild `@osd/pm` ([#3570](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3570)) - [Vega] Add Filter custom label for opensearchDashboardsAddFilter ([#3640](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3640)) - [Timeline] Fix y-axis label color in dark mode ([#3698](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3698)) -- [VisBuilder] Fixes multiple warnings thrown on page load ([#3732](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3732)) -- [VisBuilder] Fixes Firefox legend selection issue ([#3732](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3732)) -- [VisBuilder] Fixes type errors ([#3732](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3732)) +- [VisBuilder] Fix multiple warnings thrown on page load ([#3732](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3732)) +- [VisBuilder] Fix Firefox legend selection issue ([#3732](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3732)) +- [VisBuilder] Fix type errors ([#3732](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3732)) ### 🚞 Infrastructure From c709fbf6ea54d7590e5e86c5502c08e1553f4c3f Mon Sep 17 00:00:00 2001 From: Ashwin P Chandran Date: Fri, 14 Apr 2023 15:03:55 -0700 Subject: [PATCH 08/10] Update src/plugins/vis_builder/public/application/utils/handle_vis_event.ts Signed-off-by: Ashwin P Chandran Co-authored-by: Josh Romero --- .../application/utils/handle_vis_event.ts | 21 ++++++------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/src/plugins/vis_builder/public/application/utils/handle_vis_event.ts b/src/plugins/vis_builder/public/application/utils/handle_vis_event.ts index 06e3299ce348..51824eb6f234 100644 --- a/src/plugins/vis_builder/public/application/utils/handle_vis_event.ts +++ b/src/plugins/vis_builder/public/application/utils/handle_vis_event.ts @@ -14,21 +14,12 @@ export const handleVisEvent = async ( timeFieldName?: string ) => { const triggerId = get(VIS_EVENT_TO_TRIGGER, event.name, VIS_EVENT_TO_TRIGGER.filter); - let context; - - if (triggerId === VIS_EVENT_TO_TRIGGER.applyFilter) { - context = { - timeFieldName, - ...event.data, - }; - } else { - context = { - data: { - timeFieldName, - ...event.data, - }, - }; - } + const isApplyFilter = triggerId === VIS_EVENT_TO_TRIGGER.applyFilter; + const dataContext { + timeFieldName, + ...event.data, + }; + const context = isApplyFilter ? dataContext : { data: dataContext } await uiActions.getTrigger(triggerId).exec(context); }; From ec7f64623d7fc8a92d968aabe5ad46b3c780fb02 Mon Sep 17 00:00:00 2001 From: Ashwin P Chandran Date: Fri, 14 Apr 2023 22:44:30 +0000 Subject: [PATCH 09/10] Addresses PR comments Signed-off-by: Ashwin P Chandran --- .../public/application/components/right_nav.tsx | 2 ++ .../public/application/components/workspace.tsx | 2 ++ .../public/application/utils/handle_vis_event.ts | 7 +++---- .../public/saved_visualizations/transforms.test.ts | 2 +- 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/plugins/vis_builder/public/application/components/right_nav.tsx b/src/plugins/vis_builder/public/application/components/right_nav.tsx index 3053609ff111..5d3f298c6bf3 100644 --- a/src/plugins/vis_builder/public/application/components/right_nav.tsx +++ b/src/plugins/vis_builder/public/application/components/right_nav.tsx @@ -122,4 +122,6 @@ const OptionItem = ({ icon, title }: { icon: IconType; title: string }) => ( ); +// The app uses EuiResizableContainer that triggers a rerender for ever mouseover action. +// To prevent this child component from unnecessarily rerendering in that instance, it needs to be memoized export const RightNav = React.memo(RightNavUI); diff --git a/src/plugins/vis_builder/public/application/components/workspace.tsx b/src/plugins/vis_builder/public/application/components/workspace.tsx index 107bf4479034..214a735f6ba8 100644 --- a/src/plugins/vis_builder/public/application/components/workspace.tsx +++ b/src/plugins/vis_builder/public/application/components/workspace.tsx @@ -133,4 +133,6 @@ export const WorkspaceUI = () => { ); }; +// The app uses EuiResizableContainer that triggers a rerender for ever mouseover action. +// To prevent this child component from unnecessarily rerendering in that instance, it needs to be memoized export const Workspace = React.memo(WorkspaceUI); diff --git a/src/plugins/vis_builder/public/application/utils/handle_vis_event.ts b/src/plugins/vis_builder/public/application/utils/handle_vis_event.ts index 51824eb6f234..55404a00a052 100644 --- a/src/plugins/vis_builder/public/application/utils/handle_vis_event.ts +++ b/src/plugins/vis_builder/public/application/utils/handle_vis_event.ts @@ -3,7 +3,6 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { get } from 'lodash'; import { ExpressionRendererEvent } from '../../../../expressions/public'; import { VIS_EVENT_TO_TRIGGER } from '../../../../visualizations/public'; import { UiActionsStart } from '../../../../ui_actions/public'; @@ -13,13 +12,13 @@ export const handleVisEvent = async ( uiActions: UiActionsStart, timeFieldName?: string ) => { - const triggerId = get(VIS_EVENT_TO_TRIGGER, event.name, VIS_EVENT_TO_TRIGGER.filter); + const triggerId = VIS_EVENT_TO_TRIGGER[event.name] ?? VIS_EVENT_TO_TRIGGER.filter; const isApplyFilter = triggerId === VIS_EVENT_TO_TRIGGER.applyFilter; - const dataContext { + const dataContext = { timeFieldName, ...event.data, }; - const context = isApplyFilter ? dataContext : { data: dataContext } + const context = isApplyFilter ? dataContext : { data: dataContext }; await uiActions.getTrigger(triggerId).exec(context); }; diff --git a/src/plugins/vis_builder/public/saved_visualizations/transforms.test.ts b/src/plugins/vis_builder/public/saved_visualizations/transforms.test.ts index de7963313de1..efbcfd23f799 100644 --- a/src/plugins/vis_builder/public/saved_visualizations/transforms.test.ts +++ b/src/plugins/vis_builder/public/saved_visualizations/transforms.test.ts @@ -9,7 +9,7 @@ import { IndexPattern } from '../../../data/public'; import { RootState } from '../application/utils/state_management'; import { VisBuilderSavedObject } from '../types'; import { getStateFromSavedObject, saveStateToSavedObject } from './transforms'; -import { VisBuilderSavedObjectAttributes } from '../../common/vis_builder_saved_object_attributes'; +import { VisBuilderSavedObjectAttributes } from '../../common'; const getConfig = (cfg: any) => cfg; From 259de2f11fbf34c295c8590728f139674914f5f9 Mon Sep 17 00:00:00 2001 From: Ashwin P Chandran Date: Fri, 14 Apr 2023 22:49:21 +0000 Subject: [PATCH 10/10] Fix changelog Signed-off-by: Ashwin P Chandran --- CHANGELOG.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2939146e5ff8..f6cadcac29ad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -118,14 +118,12 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - Clean up and rebuild `@osd/pm` ([#3570](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3570)) - [Vega] Add Filter custom label for opensearchDashboardsAddFilter ([#3640](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3640)) - [Timeline] Fix y-axis label color in dark mode ([#3698](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3698)) - <<<<<<< HEAD - [VisBuilder] Fix multiple warnings thrown on page load ([#3732](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3732)) - [VisBuilder] Fix Firefox legend selection issue ([#3732](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3732)) -- # [VisBuilder] Fix type errors ([#3732](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3732)) +- [VisBuilder] Fix type errors ([#3732](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3732)) - [Table Visualization] Fix table rendering empty unused space ([#3797](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3797)) - [Table Visualization] Fix data table not adjusting height on the initial load ([#3816](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3816)) - Cleanup unused url ([#3847](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3847)) - > > > > > > > main ### 🚞 Infrastructure