From 4fdcf6bdbfaa8909af3669fc845d7ca53d119031 Mon Sep 17 00:00:00 2001 From: Peter Pisljar Date: Fri, 21 Aug 2020 16:14:37 +0200 Subject: [PATCH] adding markdown vis renderer (#75532) (#75628) --- src/plugins/vis_type_markdown/kibana.json | 2 +- .../__snapshots__/markdown_fn.test.ts.snap | 2 +- .../public/__snapshots__/to_ast.test.ts.snap | 67 +++++++++++++++++ .../vis_type_markdown/public/markdown_fn.ts | 8 +- .../public/markdown_renderer.tsx | 57 ++++++++++++++ .../vis_type_markdown/public/markdown_vis.ts | 4 +- .../public/markdown_vis_controller.test.tsx | 74 ++++++++++++------- .../public/markdown_vis_controller.tsx | 11 ++- .../vis_type_markdown/public/plugin.ts | 6 +- .../vis_type_markdown/public/to_ast.test.ts | 54 ++++++++++++++ .../vis_type_markdown/public/to_ast.ts | 39 ++++++++++ .../__snapshots__/build_pipeline.test.ts.snap | 4 - .../public/legacy/build_pipeline.test.ts | 17 ----- .../public/legacy/build_pipeline.ts | 11 --- 14 files changed, 288 insertions(+), 68 deletions(-) create mode 100644 src/plugins/vis_type_markdown/public/__snapshots__/to_ast.test.ts.snap create mode 100644 src/plugins/vis_type_markdown/public/markdown_renderer.tsx create mode 100644 src/plugins/vis_type_markdown/public/to_ast.test.ts create mode 100644 src/plugins/vis_type_markdown/public/to_ast.ts diff --git a/src/plugins/vis_type_markdown/kibana.json b/src/plugins/vis_type_markdown/kibana.json index 9241f5eeee837..4196bd7e85707 100644 --- a/src/plugins/vis_type_markdown/kibana.json +++ b/src/plugins/vis_type_markdown/kibana.json @@ -4,5 +4,5 @@ "ui": true, "server": true, "requiredPlugins": ["expressions", "visualizations"], - "requiredBundles": ["kibanaUtils", "kibanaReact", "data", "charts"] + "requiredBundles": ["kibanaUtils", "kibanaReact", "data", "charts", "visualizations", "expressions"] } diff --git a/src/plugins/vis_type_markdown/public/__snapshots__/markdown_fn.test.ts.snap b/src/plugins/vis_type_markdown/public/__snapshots__/markdown_fn.test.ts.snap index 5a107bdfed9e5..473e2cba742b7 100644 --- a/src/plugins/vis_type_markdown/public/__snapshots__/markdown_fn.test.ts.snap +++ b/src/plugins/vis_type_markdown/public/__snapshots__/markdown_fn.test.ts.snap @@ -2,7 +2,7 @@ exports[`interpreter/functions#markdown returns an object with the correct structure 1`] = ` Object { - "as": "visualization", + "as": "markdown_vis", "type": "render", "value": Object { "visConfig": Object { diff --git a/src/plugins/vis_type_markdown/public/__snapshots__/to_ast.test.ts.snap b/src/plugins/vis_type_markdown/public/__snapshots__/to_ast.test.ts.snap new file mode 100644 index 0000000000000..2b8ff47be3f23 --- /dev/null +++ b/src/plugins/vis_type_markdown/public/__snapshots__/to_ast.test.ts.snap @@ -0,0 +1,67 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`markdown vis toExpressionAst function with params 1`] = ` +Object { + "chain": Array [ + Object { + "arguments": Object { + "font": Array [ + Object { + "chain": Array [ + Object { + "arguments": Object { + "size": Array [ + 15, + ], + }, + "function": "font", + "type": "function", + }, + ], + "type": "expression", + }, + ], + "markdown": Array [ + "### my markdown", + ], + "openLinksInNewTab": Array [ + true, + ], + }, + "function": "markdownVis", + "type": "function", + }, + ], + "type": "expression", +} +`; + +exports[`markdown vis toExpressionAst function without params 1`] = ` +Object { + "chain": Array [ + Object { + "arguments": Object { + "font": Array [ + Object { + "chain": Array [ + Object { + "arguments": Object { + "size": Array [ + "undefined", + ], + }, + "function": "font", + "type": "function", + }, + ], + "type": "expression", + }, + ], + }, + "function": "markdownVis", + "type": "function", + }, + ], + "type": "expression", +} +`; diff --git a/src/plugins/vis_type_markdown/public/markdown_fn.ts b/src/plugins/vis_type_markdown/public/markdown_fn.ts index 9f0809109e465..4b3c9989431f9 100644 --- a/src/plugins/vis_type_markdown/public/markdown_fn.ts +++ b/src/plugins/vis_type_markdown/public/markdown_fn.ts @@ -26,12 +26,14 @@ interface RenderValue { visConfig: MarkdownVisParams; } -export const createMarkdownVisFn = (): ExpressionFunctionDefinition< +export type MarkdownVisExpressionFunctionDefinition = ExpressionFunctionDefinition< 'markdownVis', unknown, Arguments, Render -> => ({ +>; + +export const createMarkdownVisFn = (): MarkdownVisExpressionFunctionDefinition => ({ name: 'markdownVis', type: 'render', inputTypes: [], @@ -65,7 +67,7 @@ export const createMarkdownVisFn = (): ExpressionFunctionDefinition< fn(input, args) { return { type: 'render', - as: 'visualization', + as: 'markdown_vis', value: { visType: 'markdown', visConfig: { diff --git a/src/plugins/vis_type_markdown/public/markdown_renderer.tsx b/src/plugins/vis_type_markdown/public/markdown_renderer.tsx new file mode 100644 index 0000000000000..5950a762635b2 --- /dev/null +++ b/src/plugins/vis_type_markdown/public/markdown_renderer.tsx @@ -0,0 +1,57 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import React from 'react'; +import { render, unmountComponentAtNode } from 'react-dom'; +import { VisualizationContainer } from '../../visualizations/public'; +import { ExpressionRenderDefinition } from '../../expressions/common/expression_renderers'; +import { MarkdownVisWrapper } from './markdown_vis_controller'; +import { StartServicesGetter } from '../../kibana_utils/public'; + +export const getMarkdownRenderer = (start: StartServicesGetter) => { + const markdownVisRenderer: () => ExpressionRenderDefinition = () => ({ + name: 'markdown_vis', + displayName: 'markdown visualization', + reuseDomNode: true, + render: async (domNode: HTMLElement, config: any, handlers: any) => { + const { visConfig } = config; + + const I18nContext = await start().core.i18n.Context; + + handlers.onDestroy(() => { + unmountComponentAtNode(domNode); + }); + + render( + + + + + , + domNode + ); + }, + }); + + return markdownVisRenderer; +}; diff --git a/src/plugins/vis_type_markdown/public/markdown_vis.ts b/src/plugins/vis_type_markdown/public/markdown_vis.ts index 089e00bb44937..27ac038aee6ff 100644 --- a/src/plugins/vis_type_markdown/public/markdown_vis.ts +++ b/src/plugins/vis_type_markdown/public/markdown_vis.ts @@ -19,10 +19,10 @@ import { i18n } from '@kbn/i18n'; -import { MarkdownVisWrapper } from './markdown_vis_controller'; import { MarkdownOptions } from './markdown_options'; import { SettingsOptions } from './settings_options_lazy'; import { DefaultEditorSize } from '../../vis_default_editor/public'; +import { toExpressionAst } from './to_ast'; export const markdownVisDefinition = { name: 'markdown', @@ -32,8 +32,8 @@ export const markdownVisDefinition = { description: i18n.translate('visTypeMarkdown.markdownDescription', { defaultMessage: 'Create a document using markdown syntax', }), + toExpressionAst, visConfig: { - component: MarkdownVisWrapper, defaults: { fontSize: 12, openLinksInNewTab: false, diff --git a/src/plugins/vis_type_markdown/public/markdown_vis_controller.test.tsx b/src/plugins/vis_type_markdown/public/markdown_vis_controller.test.tsx index 103879cb6e6df..ff0cc89a5d9c9 100644 --- a/src/plugins/vis_type_markdown/public/markdown_vis_controller.test.tsx +++ b/src/plugins/vis_type_markdown/public/markdown_vis_controller.test.tsx @@ -25,13 +25,15 @@ describe('markdown vis controller', () => { it('should set html from markdown params', () => { const vis = { params: { + openLinksInNewTab: false, + fontSize: 16, markdown: 'This is a test of the [markdown](http://daringfireball.net/projects/markdown) vis.', }, }; const wrapper = render( - + ); expect(wrapper.find('a').text()).toBe('markdown'); }); @@ -39,12 +41,14 @@ describe('markdown vis controller', () => { it('should not render the html', () => { const vis = { params: { + openLinksInNewTab: false, + fontSize: 16, markdown: 'Testing html', }, }; const wrapper = render( - + ); expect(wrapper.text()).toBe('Testing html\n'); }); @@ -52,12 +56,14 @@ describe('markdown vis controller', () => { it('should update the HTML when render again with changed params', () => { const vis = { params: { + openLinksInNewTab: false, + fontSize: 16, markdown: 'Initial', }, }; const wrapper = mount( - + ); expect(wrapper.text().trim()).toBe('Initial'); vis.params.markdown = 'Updated'; @@ -66,52 +72,68 @@ describe('markdown vis controller', () => { }); describe('renderComplete', () => { + const vis = { + params: { + openLinksInNewTab: false, + fontSize: 16, + markdown: 'test', + }, + }; + + const renderComplete = jest.fn(); + + beforeEach(() => { + renderComplete.mockClear(); + }); + it('should be called on initial rendering', () => { - const vis = { - params: { - markdown: 'test', - }, - }; - const renderComplete = jest.fn(); mount( - + ); expect(renderComplete.mock.calls.length).toBe(1); }); it('should be called on successive render when params change', () => { - const vis = { - params: { - markdown: 'test', - }, - }; - const renderComplete = jest.fn(); mount( - + ); expect(renderComplete.mock.calls.length).toBe(1); renderComplete.mockClear(); vis.params.markdown = 'changed'; mount( - + ); expect(renderComplete.mock.calls.length).toBe(1); }); it('should be called on successive render even without data change', () => { - const vis = { - params: { - markdown: 'test', - }, - }; - const renderComplete = jest.fn(); mount( - + ); expect(renderComplete.mock.calls.length).toBe(1); renderComplete.mockClear(); mount( - + ); expect(renderComplete.mock.calls.length).toBe(1); }); diff --git a/src/plugins/vis_type_markdown/public/markdown_vis_controller.tsx b/src/plugins/vis_type_markdown/public/markdown_vis_controller.tsx index 4e77bb196b713..e1155ca42df72 100644 --- a/src/plugins/vis_type_markdown/public/markdown_vis_controller.tsx +++ b/src/plugins/vis_type_markdown/public/markdown_vis_controller.tsx @@ -22,7 +22,7 @@ import { Markdown } from '../../kibana_react/public'; import { MarkdownVisParams } from './types'; interface MarkdownVisComponentProps extends MarkdownVisParams { - renderComplete: () => {}; + renderComplete: () => void; } /** @@ -80,7 +80,14 @@ class MarkdownVisComponent extends React.Component { * The way React works, this wrapper nearly brings no overhead, but allows us * to use proper lifecycle methods in the actual component. */ -export function MarkdownVisWrapper(props: any) { + +export interface MarkdownVisWrapperProps { + visParams: MarkdownVisParams; + fireEvent: (event: any) => void; + renderComplete: () => void; +} + +export function MarkdownVisWrapper(props: MarkdownVisWrapperProps) { return ( { } public setup(core: CoreSetup, { expressions, visualizations }: MarkdownPluginSetupDependencies) { - visualizations.createReactVisualization(markdownVisDefinition); + const start = createStartServicesGetter(core.getStartServices); + visualizations.createBaseVisualization(markdownVisDefinition); + expressions.registerRenderer(getMarkdownRenderer(start)); expressions.registerFunction(createMarkdownVisFn); } diff --git a/src/plugins/vis_type_markdown/public/to_ast.test.ts b/src/plugins/vis_type_markdown/public/to_ast.test.ts new file mode 100644 index 0000000000000..1ad1fa0ee2517 --- /dev/null +++ b/src/plugins/vis_type_markdown/public/to_ast.test.ts @@ -0,0 +1,54 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { toExpressionAst } from './to_ast'; +import { Vis } from '../../visualizations/public'; + +describe('markdown vis toExpressionAst function', () => { + let vis: Vis; + + beforeEach(() => { + vis = { + isHierarchical: () => false, + type: {}, + params: { + percentageMode: false, + }, + data: { + indexPattern: { id: '123' } as any, + aggs: { + getResponseAggs: () => [], + aggs: [], + } as any, + }, + } as any; + }); + + it('without params', () => { + vis.params = {}; + const actual = toExpressionAst(vis); + expect(actual).toMatchSnapshot(); + }); + + it('with params', () => { + vis.params = { markdown: '### my markdown', fontSize: 15, openLinksInNewTab: true }; + const actual = toExpressionAst(vis); + expect(actual).toMatchSnapshot(); + }); +}); diff --git a/src/plugins/vis_type_markdown/public/to_ast.ts b/src/plugins/vis_type_markdown/public/to_ast.ts new file mode 100644 index 0000000000000..9b481218b42ea --- /dev/null +++ b/src/plugins/vis_type_markdown/public/to_ast.ts @@ -0,0 +1,39 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { Vis } from '../../visualizations/public'; +import { buildExpression, buildExpressionFunction } from '../../expressions/public'; +import { MarkdownVisExpressionFunctionDefinition } from './markdown_fn'; + +export const toExpressionAst = (vis: Vis) => { + const { markdown, fontSize, openLinksInNewTab } = vis.params; + + const markdownVis = buildExpressionFunction( + 'markdownVis', + { + markdown, + font: buildExpression(`font size=${fontSize}`), + openLinksInNewTab, + } + ); + + const ast = buildExpression([markdownVis]); + + return ast.toAst(); +}; diff --git a/src/plugins/visualizations/public/legacy/__snapshots__/build_pipeline.test.ts.snap b/src/plugins/visualizations/public/legacy/__snapshots__/build_pipeline.test.ts.snap index df29c078d23e4..fae777b98ef63 100644 --- a/src/plugins/visualizations/public/legacy/__snapshots__/build_pipeline.test.ts.snap +++ b/src/plugins/visualizations/public/legacy/__snapshots__/build_pipeline.test.ts.snap @@ -4,8 +4,6 @@ exports[`visualize loader pipeline helpers: build pipeline buildPipeline calls t exports[`visualize loader pipeline helpers: build pipeline buildPipelineVisFunction handles input_control_vis function 1`] = `"input_control_vis visConfig='{\\"some\\":\\"nested\\",\\"data\\":{\\"here\\":true}}' "`; -exports[`visualize loader pipeline helpers: build pipeline buildPipelineVisFunction handles markdown function 1`] = `"markdownvis '## hello _markdown_' font={font size=12} openLinksInNewTab=true "`; - exports[`visualize loader pipeline helpers: build pipeline buildPipelineVisFunction handles metrics/tsvb function 1`] = `"tsvb params='{\\"foo\\":\\"bar\\"}' uiState='{}' "`; exports[`visualize loader pipeline helpers: build pipeline buildPipelineVisFunction handles pie function 1`] = `"kibana_pie visConfig='{\\"dimensions\\":{\\"metric\\":{\\"accessor\\":0,\\"label\\":\\"\\",\\"format\\":{},\\"params\\":{},\\"aggType\\":\\"\\"},\\"buckets\\":[1,2]}}' "`; @@ -34,6 +32,4 @@ exports[`visualize loader pipeline helpers: build pipeline buildPipelineVisFunct exports[`visualize loader pipeline helpers: build pipeline buildPipelineVisFunction handles timelion function 1`] = `"timelion_vis expression='foo' interval='bar' "`; -exports[`visualize loader pipeline helpers: build pipeline buildPipelineVisFunction handles undefined markdown function 1`] = `"markdownvis '' font={font size=12} openLinksInNewTab=true "`; - exports[`visualize loader pipeline helpers: build pipeline buildPipelineVisFunction handles vega function 1`] = `"vega spec='this is a test' "`; diff --git a/src/plugins/visualizations/public/legacy/build_pipeline.test.ts b/src/plugins/visualizations/public/legacy/build_pipeline.test.ts index 50fa5ac64e2a1..2d92b386253b0 100644 --- a/src/plugins/visualizations/public/legacy/build_pipeline.test.ts +++ b/src/plugins/visualizations/public/legacy/build_pipeline.test.ts @@ -123,23 +123,6 @@ describe('visualize loader pipeline helpers: build pipeline', () => { expect(actual).toMatchSnapshot(); }); - it('handles markdown function', () => { - const params = { - markdown: '## hello _markdown_', - fontSize: 12, - openLinksInNewTab: true, - foo: 'bar', - }; - const actual = buildPipelineVisFunction.markdown(params, schemasDef, uiState); - expect(actual).toMatchSnapshot(); - }); - - it('handles undefined markdown function', () => { - const params = { fontSize: 12, openLinksInNewTab: true, foo: 'bar' }; - const actual = buildPipelineVisFunction.markdown(params, schemasDef, uiState); - expect(actual).toMatchSnapshot(); - }); - describe('handles table function', () => { it('without splits or buckets', () => { const params = { foo: 'bar' }; diff --git a/src/plugins/visualizations/public/legacy/build_pipeline.ts b/src/plugins/visualizations/public/legacy/build_pipeline.ts index ebd240c79287a..438a6d2337724 100644 --- a/src/plugins/visualizations/public/legacy/build_pipeline.ts +++ b/src/plugins/visualizations/public/legacy/build_pipeline.ts @@ -269,17 +269,6 @@ export const buildPipelineVisFunction: BuildPipelineVisFunction = { const interval = prepareString('interval', params.interval); return `timelion_vis ${expression}${interval}`; }, - markdown: (params) => { - const { markdown, fontSize, openLinksInNewTab } = params; - let escapedMarkdown = ''; - if (typeof markdown === 'string' || markdown instanceof String) { - escapedMarkdown = escapeString(markdown.toString()); - } - let expr = `markdownvis '${escapedMarkdown}' `; - expr += prepareValue('font', `{font size=${fontSize}}`, true); - expr += prepareValue('openLinksInNewTab', openLinksInNewTab); - return expr; - }, table: (params, schemas) => { const visConfig = { ...params,