From c7e07043fe504734d9f1be45a5fa73fcb7d5bc8c Mon Sep 17 00:00:00 2001 From: Wylie Conlon Date: Thu, 9 May 2019 11:46:21 -0400 Subject: [PATCH] [lens] Editor frame initializes datasources and visualizations (#36060) * [lens] Editor frame initializes datasources and visualizations * Respond to review comments * Fix build issues * Fix state management issue --- .../plugins/lens/public/app_plugin/plugin.tsx | 4 + .../editor_frame_plugin/editor_frame.tsx | 116 +++++++++++++++--- .../public/editor_frame_plugin/plugin.tsx | 22 +++- x-pack/plugins/lens/public/types.ts | 2 +- .../public/xy_visualization_plugin/index.ts | 7 ++ .../public/xy_visualization_plugin/plugin.tsx | 22 ++++ .../xy_visualization.test.ts | 43 +++++++ .../xy_visualization.tsx | 37 ++++++ 8 files changed, 233 insertions(+), 20 deletions(-) create mode 100644 x-pack/plugins/lens/public/xy_visualization_plugin/index.ts create mode 100644 x-pack/plugins/lens/public/xy_visualization_plugin/plugin.tsx create mode 100644 x-pack/plugins/lens/public/xy_visualization_plugin/xy_visualization.test.ts create mode 100644 x-pack/plugins/lens/public/xy_visualization_plugin/xy_visualization.tsx diff --git a/x-pack/plugins/lens/public/app_plugin/plugin.tsx b/x-pack/plugins/lens/public/app_plugin/plugin.tsx index c885d44dff765..1a096d7c1326c 100644 --- a/x-pack/plugins/lens/public/app_plugin/plugin.tsx +++ b/x-pack/plugins/lens/public/app_plugin/plugin.tsx @@ -7,6 +7,7 @@ import React from 'react'; import { editorFrameSetup, editorFrameStop } from '../editor_frame_plugin'; import { indexPatternDatasourceSetup, indexPatternDatasourceStop } from '../indexpattern_plugin'; +import { xyVisualizationSetup, xyVisualizationStop } from '../xy_visualization_plugin'; import { App } from './app'; export class AppPlugin { @@ -16,15 +17,18 @@ export class AppPlugin { // TODO: These plugins should not be called from the top level, but since this is the // entry point to the app we have no choice until the new platform is ready const indexPattern = indexPatternDatasourceSetup(); + const xyVisualization = xyVisualizationSetup(); const editorFrame = editorFrameSetup(); editorFrame.registerDatasource('indexpattern', indexPattern); + editorFrame.registerVisualization('xy', xyVisualization); return ; } stop() { indexPatternDatasourceStop(); + xyVisualizationStop(); editorFrameStop(); } } diff --git a/x-pack/plugins/lens/public/editor_frame_plugin/editor_frame.tsx b/x-pack/plugins/lens/public/editor_frame_plugin/editor_frame.tsx index 091862e635d99..40b9ba40cd430 100644 --- a/x-pack/plugins/lens/public/editor_frame_plugin/editor_frame.tsx +++ b/x-pack/plugins/lens/public/editor_frame_plugin/editor_frame.tsx @@ -4,34 +4,120 @@ * you may not use this file except in compliance with the Elastic License. */ -import React from 'react'; +import React, { useReducer, useEffect } from 'react'; import { Datasource, Visualization } from '../types'; interface EditorFrameProps { datasources: { [key: string]: Datasource }; visualizations: { [key: string]: Visualization }; + + initialDatasource?: string; +} + +interface DatasourceState { + datasourceName: string; + visualizationName: string; + + datasourceState: any; + visualizationState: any; +} + +interface UpdateDatasourceAction { + type: 'UPDATE_DATASOURCE'; + payload: any; +} + +interface UpdateVisualizationAction { + type: 'UPDATE_VISUALIZATION'; + payload: any; +} + +type Action = UpdateDatasourceAction | UpdateVisualizationAction; + +function stateReducer(state: DatasourceState, action: Action): DatasourceState { + switch (action.type) { + case 'UPDATE_DATASOURCE': + return { + ...state, + datasourceState: action.payload, + }; + case 'UPDATE_VISUALIZATION': + return { + ...state, + visualizationState: action.payload, + }; + } + return state; } export function EditorFrame(props: EditorFrameProps) { - const keys = Object.keys(props.datasources); + const dsKeys = Object.keys(props.datasources); + const vKeys = Object.keys(props.visualizations); + + const [state, dispatch] = useReducer(stateReducer, { + datasourceName: props.initialDatasource || dsKeys[0], + visualizationName: vKeys[0], + + datasourceState: null, + visualizationState: null, + }); + + useEffect(() => { + const vState = props.visualizations[state.visualizationName].initialize(); + props.datasources[state.datasourceName].initialize().then(dsState => { + dispatch({ + type: 'UPDATE_DATASOURCE', + payload: dsState, + }); + }); + + dispatch({ + type: 'UPDATE_VISUALIZATION', + payload: vState, + }); + }, []); return (

Editor Frame

- {keys.map(key => ( -
{ - if (domElement) { - props.datasources[key].renderDataPanel(domElement, { - state: {}, - setState: () => {}, - }); - } - }} - /> - ))} +
{ + if (domElement) { + props.datasources[state.datasourceName].renderDataPanel(domElement, { + state: state.datasourceState, + setState: newState => + dispatch({ + type: 'UPDATE_DATASOURCE', + payload: newState, + }), + }); + } + }} + /> + +
{ + if (domElement) { + props.visualizations[state.visualizationName].renderConfigPanel(domElement, { + datasource: props.datasources[state.datasourceName].getPublicAPI( + state.datasourceState, + newState => + dispatch({ + type: 'UPDATE_DATASOURCE', + payload: newState, + }) + ), + state: state.visualizationState, + setState: newState => + dispatch({ + type: 'UPDATE_VISUALIZATION', + payload: newState, + }), + }); + } + }} + />
); } diff --git a/x-pack/plugins/lens/public/editor_frame_plugin/plugin.tsx b/x-pack/plugins/lens/public/editor_frame_plugin/plugin.tsx index 21ef8977eb9c8..6a0a82877cefb 100644 --- a/x-pack/plugins/lens/public/editor_frame_plugin/plugin.tsx +++ b/x-pack/plugins/lens/public/editor_frame_plugin/plugin.tsx @@ -13,8 +13,14 @@ import { EditorFrame } from './editor_frame'; class EditorFramePlugin { constructor() {} - private datasources: { [key: string]: Datasource } = {}; - private visualizations: { [key: string]: Visualization } = {}; + private datasources: { + [key: string]: Datasource; + } = {}; + private visualizations: { + [key: string]: Visualization; + } = {}; + + private initialDatasource?: string; private element: Element | null = null; @@ -23,7 +29,11 @@ class EditorFramePlugin { render: domElement => { this.element = domElement; render( - , + , domElement ); }, @@ -33,7 +43,11 @@ class EditorFramePlugin { // on it's own because we are loosing type information here. // So it's basically explicitly saying "I'm dropping the information about type T here // because this information isn't useful to me." but without using any which can leak - this.datasources[name] = datasource as Datasource; + this.datasources[name] = datasource as Datasource; + + if (!this.initialDatasource) { + this.initialDatasource = name; + } }, registerVisualization: (name, visualization) => { this.visualizations[name] = visualization as Visualization; diff --git a/x-pack/plugins/lens/public/types.ts b/x-pack/plugins/lens/public/types.ts index 96586abb8b5aa..ab70ff10e1cb5 100644 --- a/x-pack/plugins/lens/public/types.ts +++ b/x-pack/plugins/lens/public/types.ts @@ -147,7 +147,7 @@ export interface Visualization { // Given the current state, which parts should be saved? getPersistableState: (state: T) => P; - renderConfigPanel: (props: VisualizationProps) => void; + renderConfigPanel: (domElement: Element, props: VisualizationProps) => void; toExpression: (state: T, datasource: DatasourcePublicAPI) => string; diff --git a/x-pack/plugins/lens/public/xy_visualization_plugin/index.ts b/x-pack/plugins/lens/public/xy_visualization_plugin/index.ts new file mode 100644 index 0000000000000..f75dce9b7507f --- /dev/null +++ b/x-pack/plugins/lens/public/xy_visualization_plugin/index.ts @@ -0,0 +1,7 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export * from './plugin'; diff --git a/x-pack/plugins/lens/public/xy_visualization_plugin/plugin.tsx b/x-pack/plugins/lens/public/xy_visualization_plugin/plugin.tsx new file mode 100644 index 0000000000000..14f2511572f28 --- /dev/null +++ b/x-pack/plugins/lens/public/xy_visualization_plugin/plugin.tsx @@ -0,0 +1,22 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { xyVisualization } from './xy_visualization'; + +class XyVisualizationPlugin { + constructor() {} + + setup() { + return xyVisualization; + } + + stop() {} +} + +const plugin = new XyVisualizationPlugin(); + +export const xyVisualizationSetup = () => plugin.setup(); +export const xyVisualizationStop = () => plugin.stop(); diff --git a/x-pack/plugins/lens/public/xy_visualization_plugin/xy_visualization.test.ts b/x-pack/plugins/lens/public/xy_visualization_plugin/xy_visualization.test.ts new file mode 100644 index 0000000000000..a750d810d116c --- /dev/null +++ b/x-pack/plugins/lens/public/xy_visualization_plugin/xy_visualization.test.ts @@ -0,0 +1,43 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { xyVisualization, XyVisualizationPersistedState } from './xy_visualization'; + +describe('IndexPattern Data Source', () => { + let persistedState: XyVisualizationPersistedState; + + beforeEach(() => { + persistedState = { + roles: [], + }; + }); + + describe('#initialize', () => { + it('loads default state', () => { + expect(xyVisualization.initialize()).toEqual({ + roles: [], + }); + }); + + it('loads from persisted state', () => { + expect(xyVisualization.initialize(persistedState)).toEqual({ + roles: [], + }); + }); + }); + + describe('#getPersistableState', () => { + it('persists the state as given', () => { + expect( + xyVisualization.getPersistableState({ + roles: [], + }) + ).toEqual({ + roles: [], + }); + }); + }); +}); diff --git a/x-pack/plugins/lens/public/xy_visualization_plugin/xy_visualization.tsx b/x-pack/plugins/lens/public/xy_visualization_plugin/xy_visualization.tsx new file mode 100644 index 0000000000000..7ec2994161216 --- /dev/null +++ b/x-pack/plugins/lens/public/xy_visualization_plugin/xy_visualization.tsx @@ -0,0 +1,37 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { render } from 'react-dom'; +import { Visualization, DimensionRole } from '../types'; + +export interface XyVisualizationState { + roles: DimensionRole[]; +} + +export type XyVisualizationPersistedState = XyVisualizationState; + +export const xyVisualization: Visualization = { + initialize() { + return { + roles: [], + }; + }, + + getPersistableState(state) { + return state; + }, + + renderConfigPanel: (domElement, props) => { + render(
XY Visualization
, domElement); + }, + + getSuggestions: options => [], + + getMappingOfTableToRoles: (state, datasource) => [], + + toExpression: state => '', +};