Skip to content

Commit

Permalink
remove custom lens component, use embeddable renderer an clean up
Browse files Browse the repository at this point in the history
  • Loading branch information
flash1293 committed Dec 22, 2020
1 parent 06ac9dd commit e8d6df9
Show file tree
Hide file tree
Showing 10 changed files with 152 additions and 169 deletions.
11 changes: 8 additions & 3 deletions x-pack/examples/embedded_lens_example/public/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,15 @@ import {
EuiTitle,
} from '@elastic/eui';
import { CoreStart } from 'kibana/public';
import { LensProps } from '../../../plugins/lens/public';
import { TypedLensByValueInput } from '../../../plugins/lens/public';
import { StartDependencies } from './plugin';

function getLensAttributes(defaultIndex: string, color: string): LensProps['attributes'] {
// Generate a Lens state based on some app-specific input parameters.
// `TypedLensByValueInput` can be used for type-safety - it uses the same interfaces as Lens-internal code.
function getLensAttributes(
defaultIndex: string,
color: string
): TypedLensByValueInput['attributes'] {
return {
visualizationType: 'lnsXY',
title: '',
Expand Down Expand Up @@ -131,7 +136,7 @@ export const App = (props: { core: CoreStart; plugins: StartDependencies }) => {
</EuiButton>
<LensComponent
id=""
height={500}
style={{ height: 500 }}
timeRange={{
from: 'now-5d',
to: 'now',
Expand Down
2 changes: 1 addition & 1 deletion x-pack/plugins/lens/public/app_plugin/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ import {
EmbeddableStateTransfer,
} from '../../../../../src/plugins/embeddable/public';
import { TableInspectorAdapter } from '../editor_frame_service/types';
import { EditorFrameInstance } from '..';
import { EditorFrameInstance } from '../types';

export interface LensAppState {
isLoading: boolean;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,17 +57,18 @@ interface LensBaseEmbeddableInput extends EmbeddableInput {
filters?: Filter[];
query?: Query;
timeRange?: TimeRange;
palette?: PaletteOutput;
renderMode?: RenderMode;
style?: React.CSSProperties;
className?: string;
}

export type LensByValueInput = {
attributes: LensSavedObjectAttributes;
} & LensBaseEmbeddableInput;

export type LensByReferenceInput = SavedObjectEmbeddableInput & LensBaseEmbeddableInput;
export type LensEmbeddableInput = (LensByValueInput | LensByReferenceInput) & {
palette?: PaletteOutput;
renderMode?: RenderMode;
};
export type LensEmbeddableInput = LensByValueInput | LensByReferenceInput;

export interface LensEmbeddableOutput extends EmbeddableOutput {
indexPatterns?: IIndexPattern[];
Expand Down Expand Up @@ -159,6 +160,37 @@ export class Embeddable
.subscribe((input) => {
this.reload();
});

// Re-initialize the visualization if either the attributes or the saved object id changes
input$
.pipe(
distinctUntilChanged((a, b) =>
isEqual(
['attributes' in a && a.attributes, 'savedObjectId' in a && a.savedObjectId],
['attributes' in b && b.attributes, 'savedObjectId' in b && b.savedObjectId]
)
),
skip(1)
)
.subscribe(async (input) => {
await this.initializeSavedVis(input);
this.reload();
});

// Update search context and reload on changes related to search
input$
.pipe(
distinctUntilChanged((a, b) =>
isEqual(
[a.filters, a.query, a.timeRange, a.searchSessionId],
[b.filters, b.query, b.timeRange, b.searchSessionId]
)
),
skip(1)
)
.subscribe(async (input) => {
this.onContainerStateChanged(input);
});
}

public supportedTriggers() {
Expand Down Expand Up @@ -261,6 +293,8 @@ export class Embeddable
onData$={this.updateActiveData}
renderMode={input.renderMode}
hasCompatibleActions={this.hasCompatibleActions}
className={input.className}
style={input.style}
/>,
domNode
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@
* 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, { useRef, MutableRefObject, useEffect } from 'react';
import { RenderMode } from 'src/plugins/expressions';
import { PaletteOutput } from 'src/plugins/charts/public';
import { TimeRange } from 'src/plugins/data/public';
import { LensEmbeddableDeps, LensEmbeddableInput } from './embeddable';
import { Embeddable } from './embeddable';
import React from 'react';
import {
EmbeddableRenderer,
EmbeddableStart,
} from '../../../../../../src/plugins/embeddable/public';
import { LensByReferenceInput, LensByValueInput } from './embeddable';
import { Document } from '../../persistence';
import { IndexPatternPersistedState } from '../../indexpattern_datasource/types';
import { XYState } from '../../xy_visualization/types';
Expand All @@ -29,61 +29,23 @@ type LensAttributes<TVisType, TVisState> = Omit<
};
};

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

function LensComponent({
props: { timeRange, height, ...props },
deps,
}: {
props: LensProps;
deps: LensEmbeddableDeps;
}) {
const elementRef: MutableRefObject<HTMLDivElement | null> = useRef(null);
const embeddableRef: MutableRefObject<Embeddable | null> = useRef(null);

useEffect(() => {
(async () => {
if (elementRef.current && embeddableRef.current) {
if (timeRange) {
embeddableRef.current.onContainerStateChanged({ ...props, timeRange });
}
await embeddableRef.current.initializeSavedVis(props);
embeddableRef.current.render(elementRef.current);
}
})();
// TODO do not re-render too often
}, [props, timeRange]);
export type EmbeddableComponentProps = TypedLensByValueInput | LensByReferenceInput;

return (
<div
style={{ height }}
ref={(newElement) => {
if (newElement) {
if (!embeddableRef.current) {
embeddableRef.current = new Embeddable(deps, props);
}
if (timeRange) {
embeddableRef.current.onContainerStateChanged({ ...props, timeRange });
}
if (elementRef.current !== newElement) {
embeddableRef.current!.render(newElement);
}
elementRef.current = newElement;
}
}}
/>
);
export function getEmbeddableComponent(embeddableStart: EmbeddableStart) {
return (props: EmbeddableComponentProps) => {
const factory = embeddableStart.getEmbeddableFactory('lens')!;
return <EmbeddableRenderer factory={factory} input={props} />;
};
}

// eslint-disable-next-line import/no-default-export
export { LensComponent as default };

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ export interface ExpressionWrapperProps {
) => void;
renderMode?: RenderMode;
hasCompatibleActions?: ReactExpressionRendererProps['hasCompatibleActions'];
style?: React.CSSProperties;
className?: string;
}

export function ExpressionWrapper({
Expand All @@ -42,53 +44,57 @@ export function ExpressionWrapper({
onData$,
renderMode,
hasCompatibleActions,
style,
className,
}: ExpressionWrapperProps) {
return (
<I18nProvider>
{expression === null || expression === '' ? (
<EuiFlexGroup direction="column" alignItems="center" justifyContent="center">
<EuiFlexItem>
<EuiIcon type="alert" color="danger" />
</EuiFlexItem>
<EuiFlexItem>
<EuiText size="s">
<FormattedMessage
id="xpack.lens.embeddable.failure"
defaultMessage="Visualization couldn't be displayed"
/>
</EuiText>
</EuiFlexItem>
</EuiFlexGroup>
) : (
<div className="lnsExpressionRenderer">
<ExpressionRendererComponent
className="lnsExpressionRenderer__component"
padding="s"
variables={variables}
expression={expression}
searchContext={searchContext}
searchSessionId={searchSessionId}
onData$={onData$}
renderMode={renderMode}
renderError={(errorMessage, error) => (
<div data-test-subj="expression-renderer-error">
<EuiFlexGroup direction="column" alignItems="center" justifyContent="center">
<EuiFlexItem>
<EuiIcon type="alert" color="danger" />
</EuiFlexItem>
<EuiFlexItem>
<EuiText size="s">
{getOriginalRequestErrorMessage(error) || errorMessage}
</EuiText>
</EuiFlexItem>
</EuiFlexGroup>
</div>
)}
onEvent={handleEvent}
hasCompatibleActions={hasCompatibleActions}
/>
</div>
)}
<div className={className} style={style}>
{expression === null || expression === '' ? (
<EuiFlexGroup direction="column" alignItems="center" justifyContent="center">
<EuiFlexItem>
<EuiIcon type="alert" color="danger" />
</EuiFlexItem>
<EuiFlexItem>
<EuiText size="s">
<FormattedMessage
id="xpack.lens.embeddable.failure"
defaultMessage="Visualization couldn't be displayed"
/>
</EuiText>
</EuiFlexItem>
</EuiFlexGroup>
) : (
<div className="lnsExpressionRenderer">
<ExpressionRendererComponent
className="lnsExpressionRenderer__component"
padding="s"
variables={variables}
expression={expression}
searchContext={searchContext}
searchSessionId={searchSessionId}
onData$={onData$}
renderMode={renderMode}
renderError={(errorMessage, error) => (
<div data-test-subj="expression-renderer-error">
<EuiFlexGroup direction="column" alignItems="center" justifyContent="center">
<EuiFlexItem>
<EuiIcon type="alert" color="danger" />
</EuiFlexItem>
<EuiFlexItem>
<EuiText size="s">
{getOriginalRequestErrorMessage(error) || errorMessage}
</EuiText>
</EuiFlexItem>
</EuiFlexGroup>
</div>
)}
onEvent={handleEvent}
hasCompatibleActions={hasCompatibleActions}
/>
</div>
)}
</div>
</I18nProvider>
);
}
13 changes: 0 additions & 13 deletions x-pack/plugins/lens/public/editor_frame_service/service.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ import { UiActionsStart } from '../../../../../src/plugins/ui_actions/public';
import { ChartsPluginSetup } from '../../../../../src/plugins/charts/public';
import { DashboardStart } from '../../../../../src/plugins/dashboard/public';
import { LensAttributeService } from '../lens_attribute_service';
import { getEmbeddableComponent } from './embeddable/embeddable_component_loader';

export interface EditorFrameSetupPlugins {
data: DataPublicPluginSetup;
Expand Down Expand Up @@ -65,7 +64,6 @@ export class EditorFrameService {

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

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

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

return {
createInstance,
EmbeddableComponent: getEmbeddableComponent({
indexPatternService: plugins.data.indexPatterns,
timefilter: plugins.data.query.timefilter.timefilter,
expressionRenderer: plugins.expressions.ReactExpressionRenderer,
editable: false,
basePath: core.http.basePath,
getTrigger: plugins.uiActions?.getTrigger,
documentToExpression: this.documentToExpression.bind(this),
attributeService: this.getAttributeService!,
}),
};
}
}
Loading

0 comments on commit e8d6df9

Please sign in to comment.