Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,16 @@
// @ts-ignore
import { MAP_SAVED_OBJECT_TYPE } from '../../../maps/common/constants';
import { VISUALIZE_EMBEDDABLE_TYPE } from '../../../../../../src/legacy/core_plugins/visualizations/public';
import { LENS_EMBEDDABLE_TYPE } from '../../../../../plugins/lens/common/constants';
import { SEARCH_EMBEDDABLE_TYPE } from '../../../../../../src/legacy/core_plugins/kibana/public/discover/np_ready/embeddable/constants';

export const EmbeddableTypes: { map: string; search: string; visualization: string } = {
export const EmbeddableTypes: {
lens: string;
map: string;
search: string;
visualization: string;
} = {
lens: LENS_EMBEDDABLE_TYPE,
map: MAP_SAVED_OBJECT_TYPE,
search: SEARCH_EMBEDDABLE_TYPE,
visualization: VISUALIZE_EMBEDDABLE_TYPE,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ import { rounddate } from './rounddate';
import { rowCount } from './rowCount';
import { repeatImage } from './repeatImage';
import { revealImage } from './revealImage';
import { savedLens } from './saved_lens';
import { savedMap } from './saved_map';
import { savedSearch } from './saved_search';
import { savedVisualization } from './saved_visualization';
Expand Down Expand Up @@ -109,6 +110,7 @@ export const functions = [
revealImage,
rounddate,
rowCount,
savedLens,
savedMap,
savedSearch,
savedVisualization,
Expand Down
Original file line number Diff line number Diff line change
@@ -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.
*/
jest.mock('ui/new_platform');
import { savedLens } from './saved_lens';
import { getQueryFilters } from '../../../public/lib/build_embeddable_filters';

const filterContext = {
and: [
{ and: [], value: 'filter-value', column: 'filter-column', type: 'exactly' },
{
and: [],
column: 'time-column',
type: 'time',
from: '2019-06-04T04:00:00.000Z',
to: '2019-06-05T04:00:00.000Z',
},
],
};

describe('savedLens', () => {
const fn = savedLens().fn;
const args = {
id: 'some-id',
title: null,
timerange: null,
};

it('accepts null context', () => {
const expression = fn(null, args, {} as any);

expect(expression.input.filters).toEqual([]);
});

it('accepts filter context', () => {
const expression = fn(filterContext, args, {} as any);
const embeddableFilters = getQueryFilters(filterContext.and);

expect(expression.input.filters).toEqual(embeddableFilters);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/*
* 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 { ExpressionFunctionDefinition } from 'src/plugins/expressions/common';
import { TimeRange } from 'src/plugins/data/public';
import { EmbeddableInput } from 'src/legacy/core_plugins/embeddable_api/public/np_ready/public';
import { getQueryFilters } from '../../../public/lib/build_embeddable_filters';
import { Filter, TimeRange as TimeRangeArg } from '../../../types';
import {
EmbeddableTypes,
EmbeddableExpressionType,
EmbeddableExpression,
} from '../../expression_types';
import { getFunctionHelp } from '../../../i18n';
import { Filter as DataFilter } from '../../../../../../../src/plugins/data/public';

interface Arguments {
id: string;
title: string | null;
timerange: TimeRangeArg | null;
}

export type SavedLensInput = EmbeddableInput & {
id: string;
timeRange?: TimeRange;
filters: DataFilter[];
};

const defaultTimeRange = {
from: 'now-15m',
to: 'now',
};

type Return = EmbeddableExpression<SavedLensInput>;

export function savedLens(): ExpressionFunctionDefinition<
'savedLens',
Filter | null,
Arguments,
Return
> {
const { help, args: argHelp } = getFunctionHelp().savedLens;
return {
name: 'savedLens',
help,
args: {
id: {
types: ['string'],
required: false,
help: argHelp.id,
},
timerange: {
types: ['timerange'],
help: argHelp.timerange,
required: false,
},
title: {
types: ['string'],
help: argHelp.title,
required: false,
},
},
type: EmbeddableExpressionType,
fn: (context, args) => {
const filters = context ? context.and : [];

return {
type: EmbeddableExpressionType,
input: {
id: args.id,
filters: getQueryFilters(filters),
timeRange: args.timerange || defaultTimeRange,
title: args.title ? args.title : undefined,
disableTriggers: true,
},
embeddableType: EmbeddableTypes.lens,
};
},
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
.canvasEmbeddable {
.embPanel {
border: none;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe more of a @ryankeairns concern, but I think we should keep the border at least in edit mode. It feels weird having the gear hanging out in the top disconnected from everything, but the border at least boxes it in and gives you some clue that all of that is tied in together.

background: none;

.embPanel__title {
margin-bottom: $euiSizeXS;
}

.embPanel__optionsMenuButton {
border-radius: $euiBorderRadius;
}

.canvas-isFullscreen & {
.embPanel__optionsMenuButton {
opacity: 0;
}

&:focus .embPanel__optionsMenuButton,
&:hover .embPanel__optionsMenuButton {
opacity: 1;
}
}
}

.euiTable {
background: none;
}

.lnsExpressionRenderer {
@include euiScrollBar;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,12 @@ import { start } from '../../../../../../../src/legacy/core_plugins/embeddable_a
import { EmbeddableExpression } from '../../expression_types/embeddable';
import { RendererStrings } from '../../../i18n';
import { getSavedObjectFinder } from '../../../../../../../src/plugins/saved_objects/public';

const { embeddable: strings } = RendererStrings;
import { embeddableInputToExpression } from './embeddable_input_to_expression';
import { EmbeddableInput } from '../../expression_types';
import { RendererHandlers } from '../../../types';
import { CANVAS_EMBEDDABLE_CLASSNAME } from '../../../common/lib';

const { embeddable: strings } = RendererStrings;

const embeddablesRegistry: {
[key: string]: IEmbeddable;
Expand All @@ -31,7 +32,7 @@ const embeddablesRegistry: {
const renderEmbeddable = (embeddableObject: IEmbeddable, domNode: HTMLElement) => {
return (
<div
className="embeddable"
className={CANVAS_EMBEDDABLE_CLASSNAME}
style={{ width: domNode.offsetWidth, height: domNode.offsetHeight, cursor: 'auto' }}
>
<I18nContext>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,17 @@
jest.mock('ui/new_platform');
import { embeddableInputToExpression } from './embeddable_input_to_expression';
import { SavedMapInput } from '../../functions/common/saved_map';
import { SavedLensInput } from '../../functions/common/saved_lens';
import { EmbeddableTypes } from '../../expression_types';
import { fromExpression, Ast } from '@kbn/interpreter/common';

const baseSavedMapInput = {
const baseEmbeddableInput = {
id: 'embeddableId',
filters: [],
};

const baseSavedMapInput = {
...baseEmbeddableInput,
isLayerTOCOpen: false,
refreshConfig: {
isPaused: true,
Expand Down Expand Up @@ -73,4 +78,45 @@ describe('input to expression', () => {
expect(timerangeExpression.chain[0].arguments.to[0]).toEqual(input.timeRange?.to);
});
});

describe('Lens Embeddable', () => {
it('converts to a savedLens expression', () => {
const input: SavedLensInput = {
...baseEmbeddableInput,
};

const expression = embeddableInputToExpression(input, EmbeddableTypes.lens);
const ast = fromExpression(expression);

expect(ast.type).toBe('expression');
expect(ast.chain[0].function).toBe('savedLens');

expect(ast.chain[0].arguments.id).toStrictEqual([input.id]);

expect(ast.chain[0].arguments).not.toHaveProperty('title');
expect(ast.chain[0].arguments).not.toHaveProperty('timerange');
});

it('includes optional input values', () => {
const input: SavedLensInput = {
...baseEmbeddableInput,
title: 'title',
timeRange: {
from: 'now-1h',
to: 'now',
},
};

const expression = embeddableInputToExpression(input, EmbeddableTypes.map);
const ast = fromExpression(expression);

expect(ast.chain[0].arguments).toHaveProperty('title', [input.title]);
expect(ast.chain[0].arguments).toHaveProperty('timerange');

const timerangeExpression = ast.chain[0].arguments.timerange[0] as Ast;
expect(timerangeExpression.chain[0].function).toBe('timerange');
expect(timerangeExpression.chain[0].arguments.from[0]).toEqual(input.timeRange?.from);
expect(timerangeExpression.chain[0].arguments.to[0]).toEqual(input.timeRange?.to);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

import { EmbeddableTypes, EmbeddableInput } from '../../expression_types';
import { SavedMapInput } from '../../functions/common/saved_map';
import { SavedLensInput } from '../../functions/common/saved_lens';

/*
Take the input from an embeddable and the type of embeddable and convert it into an expression
Expand Down Expand Up @@ -46,5 +47,23 @@ export function embeddableInputToExpression(
}
}

if (embeddableType === EmbeddableTypes.lens) {
const lensInput = input as SavedLensInput;

expressionParts.push('savedLens');

expressionParts.push(`id="${input.id}"`);

if (input.title) {
expressionParts.push(`title="${input.title}"`);
}

if (lensInput.timeRange) {
expressionParts.push(
`timerange={timerange from="${lensInput.timeRange.from}" to="${lensInput.timeRange.to}"}`
);
}
}

return expressionParts.join(' ');
}
1 change: 1 addition & 0 deletions x-pack/legacy/plugins/canvas/common/lib/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,4 @@ export const API_ROUTE_SHAREABLE_BASE = '/public/canvas';
export const API_ROUTE_SHAREABLE_ZIP = '/public/canvas/zip';
export const API_ROUTE_SHAREABLE_RUNTIME = '/public/canvas/runtime';
export const API_ROUTE_SHAREABLE_RUNTIME_DOWNLOAD = `/public/canvas/${SHAREABLE_RUNTIME_NAME}.js`;
export const CANVAS_EMBEDDABLE_CLASSNAME = `canvasEmbeddable`;
27 changes: 27 additions & 0 deletions x-pack/legacy/plugins/canvas/i18n/functions/dict/saved_lens.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
* 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 { i18n } from '@kbn/i18n';
import { savedLens } from '../../../canvas_plugin_src/functions/common/saved_lens';
import { FunctionHelp } from '../function_help';
import { FunctionFactory } from '../../../types';

export const help: FunctionHelp<FunctionFactory<typeof savedLens>> = {
help: i18n.translate('xpack.canvas.functions.savedLensHelpText', {
defaultMessage: `Returns an embeddable for a saved lens object`,
}),
args: {
id: i18n.translate('xpack.canvas.functions.savedLens.args.idHelpText', {
defaultMessage: `The ID of the Saved Lens Object`,
}),
timerange: i18n.translate('xpack.canvas.functions.savedLens.args.timerangeHelpText', {
defaultMessage: `The timerange of data that should be included`,
}),
title: i18n.translate('xpack.canvas.functions.savedLens.args.titleHelpText', {
defaultMessage: `The title for the lens emebeddable`,
}),
},
};
2 changes: 2 additions & 0 deletions x-pack/legacy/plugins/canvas/i18n/functions/function_help.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ import { help as replace } from './dict/replace';
import { help as revealImage } from './dict/reveal_image';
import { help as rounddate } from './dict/rounddate';
import { help as rowCount } from './dict/row_count';
import { help as savedLens } from './dict/saved_lens';
import { help as savedMap } from './dict/saved_map';
import { help as savedSearch } from './dict/saved_search';
import { help as savedVisualization } from './dict/saved_visualization';
Expand Down Expand Up @@ -216,6 +217,7 @@ export const getFunctionHelp = (): FunctionHelpDict => ({
revealImage,
rounddate,
rowCount,
savedLens,
savedMap,
savedSearch,
savedVisualization,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ const allowedEmbeddables = {
[EmbeddableTypes.map]: (id: string) => {
return `savedMap id="${id}" | render`;
},
[EmbeddableTypes.lens]: (id: string) => {
return `savedLens id="${id}" | render`;
},
// FIX: Only currently allow Map embeddables
/* [EmbeddableTypes.visualization]: (id: string) => {
return `filters | savedVisualization id="${id}" | render`;
Expand Down
Loading