mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -04:00
parent
d57460d521
commit
4fdcf6bdbf
14 changed files with 288 additions and 68 deletions
|
@ -4,5 +4,5 @@
|
|||
"ui": true,
|
||||
"server": true,
|
||||
"requiredPlugins": ["expressions", "visualizations"],
|
||||
"requiredBundles": ["kibanaUtils", "kibanaReact", "data", "charts"]
|
||||
"requiredBundles": ["kibanaUtils", "kibanaReact", "data", "charts", "visualizations", "expressions"]
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
67
src/plugins/vis_type_markdown/public/__snapshots__/to_ast.test.ts.snap
generated
Normal file
67
src/plugins/vis_type_markdown/public/__snapshots__/to_ast.test.ts.snap
generated
Normal file
|
@ -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",
|
||||
}
|
||||
`;
|
|
@ -26,12 +26,14 @@ interface RenderValue {
|
|||
visConfig: MarkdownVisParams;
|
||||
}
|
||||
|
||||
export const createMarkdownVisFn = (): ExpressionFunctionDefinition<
|
||||
export type MarkdownVisExpressionFunctionDefinition = ExpressionFunctionDefinition<
|
||||
'markdownVis',
|
||||
unknown,
|
||||
Arguments,
|
||||
Render<RenderValue>
|
||||
> => ({
|
||||
>;
|
||||
|
||||
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: {
|
||||
|
|
57
src/plugins/vis_type_markdown/public/markdown_renderer.tsx
Normal file
57
src/plugins/vis_type_markdown/public/markdown_renderer.tsx
Normal file
|
@ -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(
|
||||
<I18nContext>
|
||||
<VisualizationContainer className="markdownVis">
|
||||
<MarkdownVisWrapper
|
||||
visParams={visConfig}
|
||||
renderComplete={handlers.done}
|
||||
fireEvent={handlers.event}
|
||||
/>
|
||||
</VisualizationContainer>
|
||||
</I18nContext>,
|
||||
domNode
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
return markdownVisRenderer;
|
||||
};
|
|
@ -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,
|
||||
|
|
|
@ -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(
|
||||
<MarkdownVisWrapper vis={vis} visParams={vis.params} renderComplete={jest.fn()} />
|
||||
<MarkdownVisWrapper visParams={vis.params} renderComplete={jest.fn()} fireEvent={jest.fn()} />
|
||||
);
|
||||
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 <a>html</a>',
|
||||
},
|
||||
};
|
||||
|
||||
const wrapper = render(
|
||||
<MarkdownVisWrapper vis={vis} visParams={vis.params} renderComplete={jest.fn()} />
|
||||
<MarkdownVisWrapper visParams={vis.params} renderComplete={jest.fn()} fireEvent={jest.fn()} />
|
||||
);
|
||||
expect(wrapper.text()).toBe('Testing <a>html</a>\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(
|
||||
<MarkdownVisWrapper vis={vis} visParams={vis.params} renderComplete={jest.fn()} />
|
||||
<MarkdownVisWrapper visParams={vis.params} renderComplete={jest.fn()} fireEvent={jest.fn()} />
|
||||
);
|
||||
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(
|
||||
<MarkdownVisWrapper vis={vis} visParams={vis.params} renderComplete={renderComplete} />
|
||||
<MarkdownVisWrapper
|
||||
visParams={vis.params}
|
||||
renderComplete={renderComplete}
|
||||
fireEvent={jest.fn()}
|
||||
/>
|
||||
);
|
||||
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(
|
||||
<MarkdownVisWrapper vis={vis} visParams={vis.params} renderComplete={renderComplete} />
|
||||
<MarkdownVisWrapper
|
||||
visParams={vis.params}
|
||||
renderComplete={renderComplete}
|
||||
fireEvent={jest.fn()}
|
||||
/>
|
||||
);
|
||||
expect(renderComplete.mock.calls.length).toBe(1);
|
||||
renderComplete.mockClear();
|
||||
vis.params.markdown = 'changed';
|
||||
mount(
|
||||
<MarkdownVisWrapper vis={vis} visParams={vis.params} renderComplete={renderComplete} />
|
||||
<MarkdownVisWrapper
|
||||
visParams={vis.params}
|
||||
renderComplete={renderComplete}
|
||||
fireEvent={jest.fn()}
|
||||
/>
|
||||
);
|
||||
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(
|
||||
<MarkdownVisWrapper vis={vis} visParams={vis.params} renderComplete={renderComplete} />
|
||||
<MarkdownVisWrapper
|
||||
visParams={vis.params}
|
||||
renderComplete={renderComplete}
|
||||
fireEvent={jest.fn()}
|
||||
/>
|
||||
);
|
||||
expect(renderComplete.mock.calls.length).toBe(1);
|
||||
renderComplete.mockClear();
|
||||
mount(
|
||||
<MarkdownVisWrapper vis={vis} visParams={vis.params} renderComplete={renderComplete} />
|
||||
<MarkdownVisWrapper
|
||||
visParams={vis.params}
|
||||
renderComplete={renderComplete}
|
||||
fireEvent={jest.fn()}
|
||||
/>
|
||||
);
|
||||
expect(renderComplete.mock.calls.length).toBe(1);
|
||||
});
|
||||
|
|
|
@ -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<MarkdownVisComponentProps> {
|
|||
* 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 (
|
||||
<MarkdownVisComponent
|
||||
fontSize={props.visParams.fontSize}
|
||||
|
|
|
@ -26,6 +26,8 @@ import { createMarkdownVisFn } from './markdown_fn';
|
|||
import { ConfigSchema } from '../config';
|
||||
|
||||
import './index.scss';
|
||||
import { getMarkdownRenderer } from './markdown_renderer';
|
||||
import { createStartServicesGetter } from '../../kibana_utils/public';
|
||||
|
||||
/** @internal */
|
||||
export interface MarkdownPluginSetupDependencies {
|
||||
|
@ -42,7 +44,9 @@ export class MarkdownPlugin implements Plugin<void, void> {
|
|||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
54
src/plugins/vis_type_markdown/public/to_ast.test.ts
Normal file
54
src/plugins/vis_type_markdown/public/to_ast.test.ts
Normal file
|
@ -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();
|
||||
});
|
||||
});
|
39
src/plugins/vis_type_markdown/public/to_ast.ts
Normal file
39
src/plugins/vis_type_markdown/public/to_ast.ts
Normal file
|
@ -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<MarkdownVisExpressionFunctionDefinition>(
|
||||
'markdownVis',
|
||||
{
|
||||
markdown,
|
||||
font: buildExpression(`font size=${fontSize}`),
|
||||
openLinksInNewTab,
|
||||
}
|
||||
);
|
||||
|
||||
const ast = buildExpression([markdownVis]);
|
||||
|
||||
return ast.toAst();
|
||||
};
|
|
@ -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' "`;
|
||||
|
|
|
@ -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' };
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue