Skip to content

Commit e8d6df9

Browse files
committed
remove custom lens component, use embeddable renderer an clean up
1 parent 06ac9dd commit e8d6df9

10 files changed

Lines changed: 152 additions & 169 deletions

File tree

x-pack/examples/embedded_lens_example/public/app.tsx

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,15 @@ import {
1616
EuiTitle,
1717
} from '@elastic/eui';
1818
import { CoreStart } from 'kibana/public';
19-
import { LensProps } from '../../../plugins/lens/public';
19+
import { TypedLensByValueInput } from '../../../plugins/lens/public';
2020
import { StartDependencies } from './plugin';
2121

22-
function getLensAttributes(defaultIndex: string, color: string): LensProps['attributes'] {
22+
// Generate a Lens state based on some app-specific input parameters.
23+
// `TypedLensByValueInput` can be used for type-safety - it uses the same interfaces as Lens-internal code.
24+
function getLensAttributes(
25+
defaultIndex: string,
26+
color: string
27+
): TypedLensByValueInput['attributes'] {
2328
return {
2429
visualizationType: 'lnsXY',
2530
title: '',
@@ -131,7 +136,7 @@ export const App = (props: { core: CoreStart; plugins: StartDependencies }) => {
131136
</EuiButton>
132137
<LensComponent
133138
id=""
134-
height={500}
139+
style={{ height: 500 }}
135140
timeRange={{
136141
from: 'now-5d',
137142
to: 'now',

x-pack/plugins/lens/public/app_plugin/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ import {
3838
EmbeddableStateTransfer,
3939
} from '../../../../../src/plugins/embeddable/public';
4040
import { TableInspectorAdapter } from '../editor_frame_service/types';
41-
import { EditorFrameInstance } from '..';
41+
import { EditorFrameInstance } from '../types';
4242

4343
export interface LensAppState {
4444
isLoading: boolean;

x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable.tsx

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,17 +57,18 @@ interface LensBaseEmbeddableInput extends EmbeddableInput {
5757
filters?: Filter[];
5858
query?: Query;
5959
timeRange?: TimeRange;
60+
palette?: PaletteOutput;
61+
renderMode?: RenderMode;
62+
style?: React.CSSProperties;
63+
className?: string;
6064
}
6165

6266
export type LensByValueInput = {
6367
attributes: LensSavedObjectAttributes;
6468
} & LensBaseEmbeddableInput;
6569

6670
export type LensByReferenceInput = SavedObjectEmbeddableInput & LensBaseEmbeddableInput;
67-
export type LensEmbeddableInput = (LensByValueInput | LensByReferenceInput) & {
68-
palette?: PaletteOutput;
69-
renderMode?: RenderMode;
70-
};
71+
export type LensEmbeddableInput = LensByValueInput | LensByReferenceInput;
7172

7273
export interface LensEmbeddableOutput extends EmbeddableOutput {
7374
indexPatterns?: IIndexPattern[];
@@ -159,6 +160,37 @@ export class Embeddable
159160
.subscribe((input) => {
160161
this.reload();
161162
});
163+
164+
// Re-initialize the visualization if either the attributes or the saved object id changes
165+
input$
166+
.pipe(
167+
distinctUntilChanged((a, b) =>
168+
isEqual(
169+
['attributes' in a && a.attributes, 'savedObjectId' in a && a.savedObjectId],
170+
['attributes' in b && b.attributes, 'savedObjectId' in b && b.savedObjectId]
171+
)
172+
),
173+
skip(1)
174+
)
175+
.subscribe(async (input) => {
176+
await this.initializeSavedVis(input);
177+
this.reload();
178+
});
179+
180+
// Update search context and reload on changes related to search
181+
input$
182+
.pipe(
183+
distinctUntilChanged((a, b) =>
184+
isEqual(
185+
[a.filters, a.query, a.timeRange, a.searchSessionId],
186+
[b.filters, b.query, b.timeRange, b.searchSessionId]
187+
)
188+
),
189+
skip(1)
190+
)
191+
.subscribe(async (input) => {
192+
this.onContainerStateChanged(input);
193+
});
162194
}
163195

164196
public supportedTriggers() {
@@ -261,6 +293,8 @@ export class Embeddable
261293
onData$={this.updateActiveData}
262294
renderMode={input.renderMode}
263295
hasCompatibleActions={this.hasCompatibleActions}
296+
className={input.className}
297+
style={input.style}
264298
/>,
265299
domNode
266300
);

x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable_component.tsx

Lines changed: 17 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,12 @@
33
* or more contributor license agreements. Licensed under the Elastic License;
44
* you may not use this file except in compliance with the Elastic License.
55
*/
6-
import React, { useRef, MutableRefObject, useEffect } from 'react';
7-
import { RenderMode } from 'src/plugins/expressions';
8-
import { PaletteOutput } from 'src/plugins/charts/public';
9-
import { TimeRange } from 'src/plugins/data/public';
10-
import { LensEmbeddableDeps, LensEmbeddableInput } from './embeddable';
11-
import { Embeddable } from './embeddable';
6+
import React from 'react';
7+
import {
8+
EmbeddableRenderer,
9+
EmbeddableStart,
10+
} from '../../../../../../src/plugins/embeddable/public';
11+
import { LensByReferenceInput, LensByValueInput } from './embeddable';
1212
import { Document } from '../../persistence';
1313
import { IndexPatternPersistedState } from '../../indexpattern_datasource/types';
1414
import { XYState } from '../../xy_visualization/types';
@@ -29,61 +29,23 @@ type LensAttributes<TVisType, TVisState> = Omit<
2929
};
3030
};
3131

32-
export type LensProps = Omit<LensEmbeddableInput, 'attributes'> & {
33-
palette?: PaletteOutput;
34-
renderMode?: RenderMode;
35-
timeRange?: TimeRange;
36-
height: number;
32+
/**
33+
* Type-safe variant of by value embeddable input for Lens.
34+
* This can be used to hardcode certain Lens chart configurations within another app.
35+
*/
36+
export type TypedLensByValueInput = Omit<LensByValueInput, 'attributes'> & {
3737
attributes:
3838
| LensAttributes<'lnsXY', XYState>
3939
| LensAttributes<'lnsPie', PieVisualizationState>
4040
| LensAttributes<'lnsDatatable', DatatableVisualizationState>
4141
| LensAttributes<'lnsMetric', MetricState>;
4242
};
4343

44-
function LensComponent({
45-
props: { timeRange, height, ...props },
46-
deps,
47-
}: {
48-
props: LensProps;
49-
deps: LensEmbeddableDeps;
50-
}) {
51-
const elementRef: MutableRefObject<HTMLDivElement | null> = useRef(null);
52-
const embeddableRef: MutableRefObject<Embeddable | null> = useRef(null);
53-
54-
useEffect(() => {
55-
(async () => {
56-
if (elementRef.current && embeddableRef.current) {
57-
if (timeRange) {
58-
embeddableRef.current.onContainerStateChanged({ ...props, timeRange });
59-
}
60-
await embeddableRef.current.initializeSavedVis(props);
61-
embeddableRef.current.render(elementRef.current);
62-
}
63-
})();
64-
// TODO do not re-render too often
65-
}, [props, timeRange]);
44+
export type EmbeddableComponentProps = TypedLensByValueInput | LensByReferenceInput;
6645

67-
return (
68-
<div
69-
style={{ height }}
70-
ref={(newElement) => {
71-
if (newElement) {
72-
if (!embeddableRef.current) {
73-
embeddableRef.current = new Embeddable(deps, props);
74-
}
75-
if (timeRange) {
76-
embeddableRef.current.onContainerStateChanged({ ...props, timeRange });
77-
}
78-
if (elementRef.current !== newElement) {
79-
embeddableRef.current!.render(newElement);
80-
}
81-
elementRef.current = newElement;
82-
}
83-
}}
84-
/>
85-
);
46+
export function getEmbeddableComponent(embeddableStart: EmbeddableStart) {
47+
return (props: EmbeddableComponentProps) => {
48+
const factory = embeddableStart.getEmbeddableFactory('lens')!;
49+
return <EmbeddableRenderer factory={factory} input={props} />;
50+
};
8651
}
87-
88-
// eslint-disable-next-line import/no-default-export
89-
export { LensComponent as default };

x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable_component_loader.tsx

Lines changed: 0 additions & 39 deletions
This file was deleted.

x-pack/plugins/lens/public/editor_frame_service/embeddable/expression_wrapper.tsx

Lines changed: 50 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ export interface ExpressionWrapperProps {
3030
) => void;
3131
renderMode?: RenderMode;
3232
hasCompatibleActions?: ReactExpressionRendererProps['hasCompatibleActions'];
33+
style?: React.CSSProperties;
34+
className?: string;
3335
}
3436

3537
export function ExpressionWrapper({
@@ -42,53 +44,57 @@ export function ExpressionWrapper({
4244
onData$,
4345
renderMode,
4446
hasCompatibleActions,
47+
style,
48+
className,
4549
}: ExpressionWrapperProps) {
4650
return (
4751
<I18nProvider>
48-
{expression === null || expression === '' ? (
49-
<EuiFlexGroup direction="column" alignItems="center" justifyContent="center">
50-
<EuiFlexItem>
51-
<EuiIcon type="alert" color="danger" />
52-
</EuiFlexItem>
53-
<EuiFlexItem>
54-
<EuiText size="s">
55-
<FormattedMessage
56-
id="xpack.lens.embeddable.failure"
57-
defaultMessage="Visualization couldn't be displayed"
58-
/>
59-
</EuiText>
60-
</EuiFlexItem>
61-
</EuiFlexGroup>
62-
) : (
63-
<div className="lnsExpressionRenderer">
64-
<ExpressionRendererComponent
65-
className="lnsExpressionRenderer__component"
66-
padding="s"
67-
variables={variables}
68-
expression={expression}
69-
searchContext={searchContext}
70-
searchSessionId={searchSessionId}
71-
onData$={onData$}
72-
renderMode={renderMode}
73-
renderError={(errorMessage, error) => (
74-
<div data-test-subj="expression-renderer-error">
75-
<EuiFlexGroup direction="column" alignItems="center" justifyContent="center">
76-
<EuiFlexItem>
77-
<EuiIcon type="alert" color="danger" />
78-
</EuiFlexItem>
79-
<EuiFlexItem>
80-
<EuiText size="s">
81-
{getOriginalRequestErrorMessage(error) || errorMessage}
82-
</EuiText>
83-
</EuiFlexItem>
84-
</EuiFlexGroup>
85-
</div>
86-
)}
87-
onEvent={handleEvent}
88-
hasCompatibleActions={hasCompatibleActions}
89-
/>
90-
</div>
91-
)}
52+
<div className={className} style={style}>
53+
{expression === null || expression === '' ? (
54+
<EuiFlexGroup direction="column" alignItems="center" justifyContent="center">
55+
<EuiFlexItem>
56+
<EuiIcon type="alert" color="danger" />
57+
</EuiFlexItem>
58+
<EuiFlexItem>
59+
<EuiText size="s">
60+
<FormattedMessage
61+
id="xpack.lens.embeddable.failure"
62+
defaultMessage="Visualization couldn't be displayed"
63+
/>
64+
</EuiText>
65+
</EuiFlexItem>
66+
</EuiFlexGroup>
67+
) : (
68+
<div className="lnsExpressionRenderer">
69+
<ExpressionRendererComponent
70+
className="lnsExpressionRenderer__component"
71+
padding="s"
72+
variables={variables}
73+
expression={expression}
74+
searchContext={searchContext}
75+
searchSessionId={searchSessionId}
76+
onData$={onData$}
77+
renderMode={renderMode}
78+
renderError={(errorMessage, error) => (
79+
<div data-test-subj="expression-renderer-error">
80+
<EuiFlexGroup direction="column" alignItems="center" justifyContent="center">
81+
<EuiFlexItem>
82+
<EuiIcon type="alert" color="danger" />
83+
</EuiFlexItem>
84+
<EuiFlexItem>
85+
<EuiText size="s">
86+
{getOriginalRequestErrorMessage(error) || errorMessage}
87+
</EuiText>
88+
</EuiFlexItem>
89+
</EuiFlexGroup>
90+
</div>
91+
)}
92+
onEvent={handleEvent}
93+
hasCompatibleActions={hasCompatibleActions}
94+
/>
95+
</div>
96+
)}
97+
</div>
9298
</I18nProvider>
9399
);
94100
}

x-pack/plugins/lens/public/editor_frame_service/service.tsx

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ import { UiActionsStart } from '../../../../../src/plugins/ui_actions/public';
2828
import { ChartsPluginSetup } from '../../../../../src/plugins/charts/public';
2929
import { DashboardStart } from '../../../../../src/plugins/dashboard/public';
3030
import { LensAttributeService } from '../lens_attribute_service';
31-
import { getEmbeddableComponent } from './embeddable/embeddable_component_loader';
3231

3332
export interface EditorFrameSetupPlugins {
3433
data: DataPublicPluginSetup;
@@ -65,7 +64,6 @@ export class EditorFrameService {
6564

6665
private readonly datasources: Array<Datasource | (() => Promise<Datasource>)> = [];
6766
private readonly visualizations: Array<Visualization | (() => Promise<Visualization>)> = [];
68-
private getAttributeService: (() => Promise<LensAttributeService>) | null = null;
6967

7068
/**
7169
* This method takes a Lens saved object as returned from the persistence helper,
@@ -89,7 +87,6 @@ export class EditorFrameService {
8987
plugins: EditorFrameSetupPlugins,
9088
getAttributeService: () => Promise<LensAttributeService>
9189
): EditorFrameSetup {
92-
this.getAttributeService = getAttributeService;
9390
plugins.expressions.registerFunction(() => mergeTables);
9491

9592
const getStartServices = async (): Promise<LensEmbeddableStartServices> => {
@@ -190,16 +187,6 @@ export class EditorFrameService {
190187

191188
return {
192189
createInstance,
193-
EmbeddableComponent: getEmbeddableComponent({
194-
indexPatternService: plugins.data.indexPatterns,
195-
timefilter: plugins.data.query.timefilter.timefilter,
196-
expressionRenderer: plugins.expressions.ReactExpressionRenderer,
197-
editable: false,
198-
basePath: core.http.basePath,
199-
getTrigger: plugins.uiActions?.getTrigger,
200-
documentToExpression: this.documentToExpression.bind(this),
201-
attributeService: this.getAttributeService!,
202-
}),
203190
};
204191
}
205192
}

0 commit comments

Comments
 (0)