mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 09:19:04 -04:00
[Expressions] Align renderMode with the embeddable viewMode (#110199)
* Add preview view mode to the embeddable * Rename display render mode to view * Extract no interactivity render mode to a separate flag
This commit is contained in:
parent
cb27ba01c1
commit
df43d253c8
21 changed files with 62 additions and 33 deletions
|
@ -12,6 +12,7 @@ import { PersistableStateService } from '../../kibana_utils/common';
|
|||
|
||||
export enum ViewMode {
|
||||
EDIT = 'edit',
|
||||
PREVIEW = 'preview',
|
||||
VIEW = 'view',
|
||||
}
|
||||
|
||||
|
|
|
@ -53,12 +53,11 @@ export type AnyExpressionRenderDefinition = ExpressionRenderDefinition<any>;
|
|||
* This value can be set from a consumer embedding an expression renderer and is accessible
|
||||
* from within the active render function as part of the handlers.
|
||||
* The following modes are supported:
|
||||
* * display (default): The chart is rendered in a container with the main purpose of viewing the chart (e.g. in a container like dashboard or canvas)
|
||||
* * view (default): The chart is rendered in a container with the main purpose of viewing the chart (e.g. in a container like dashboard or canvas)
|
||||
* * preview: The chart is rendered in very restricted space (below 100px width and height) and should only show a rough outline
|
||||
* * edit: The chart is rendered within an editor and configuration elements within the chart should be displayed
|
||||
* * noInteractivity: The chart is rendered in a non-interactive environment and should not provide any affordances for interaction like brushing
|
||||
*/
|
||||
export type RenderMode = 'noInteractivity' | 'edit' | 'preview' | 'display';
|
||||
export type RenderMode = 'edit' | 'preview' | 'view';
|
||||
|
||||
export interface IInterpreterRenderHandlers {
|
||||
/**
|
||||
|
@ -71,6 +70,12 @@ export interface IInterpreterRenderHandlers {
|
|||
event: (event: any) => void;
|
||||
hasCompatibleActions?: (event: any) => Promise<boolean>;
|
||||
getRenderMode: () => RenderMode;
|
||||
|
||||
/**
|
||||
* The chart is rendered in a non-interactive environment and should not provide any affordances for interaction like brushing.
|
||||
*/
|
||||
isInteractive: () => boolean;
|
||||
|
||||
isSyncColorsEnabled: () => boolean;
|
||||
/**
|
||||
* This uiState interface is actually `PersistedState` from the visualizations plugin,
|
||||
|
|
|
@ -53,6 +53,7 @@ export class ExpressionLoader {
|
|||
);
|
||||
|
||||
this.renderHandler = new ExpressionRenderHandler(element, {
|
||||
interactive: params?.interactive,
|
||||
onRenderError: params && params.onRenderError,
|
||||
renderMode: params?.renderMode,
|
||||
syncColors: params?.syncColors,
|
||||
|
|
|
@ -171,6 +171,7 @@ export const ReactExpressionRenderer = ({
|
|||
}, [
|
||||
hasCustomRenderErrorHandler,
|
||||
onEvent,
|
||||
expressionLoaderOptions.interactive,
|
||||
expressionLoaderOptions.renderMode,
|
||||
expressionLoaderOptions.syncColors,
|
||||
]);
|
||||
|
|
|
@ -21,6 +21,7 @@ export interface ExpressionRenderHandlerParams {
|
|||
onRenderError?: RenderErrorHandlerFnType;
|
||||
renderMode?: RenderMode;
|
||||
syncColors?: boolean;
|
||||
interactive?: boolean;
|
||||
hasCompatibleActions?: (event: ExpressionRendererEvent) => Promise<boolean>;
|
||||
}
|
||||
|
||||
|
@ -54,6 +55,7 @@ export class ExpressionRenderHandler {
|
|||
onRenderError,
|
||||
renderMode,
|
||||
syncColors,
|
||||
interactive,
|
||||
hasCompatibleActions = async () => false,
|
||||
}: ExpressionRenderHandlerParams = {}
|
||||
) {
|
||||
|
@ -90,11 +92,14 @@ export class ExpressionRenderHandler {
|
|||
this.eventsSubject.next(data);
|
||||
},
|
||||
getRenderMode: () => {
|
||||
return renderMode || 'display';
|
||||
return renderMode || 'view';
|
||||
},
|
||||
isSyncColorsEnabled: () => {
|
||||
return syncColors || false;
|
||||
},
|
||||
isInteractive: () => {
|
||||
return interactive ?? true;
|
||||
},
|
||||
hasCompatibleActions,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -44,6 +44,7 @@ export interface IExpressionLoaderParams {
|
|||
customRenderers?: [];
|
||||
uiState?: unknown;
|
||||
inspectorAdapters?: Adapters;
|
||||
interactive?: boolean;
|
||||
onRenderError?: RenderErrorHandlerFnType;
|
||||
searchSessionId?: string;
|
||||
renderMode?: RenderMode;
|
||||
|
|
|
@ -11,8 +11,9 @@ import React, { useRef, useEffect } from 'react';
|
|||
import { ExpressionRenderDefinition, IInterpreterRenderHandlers } from 'src/plugins/expressions';
|
||||
|
||||
export const defaultHandlers: IInterpreterRenderHandlers = {
|
||||
getRenderMode: () => 'display',
|
||||
getRenderMode: () => 'view',
|
||||
isSyncColorsEnabled: () => false,
|
||||
isInteractive: () => true,
|
||||
done: action('done'),
|
||||
onDestroy: action('onDestroy'),
|
||||
reload: action('reload'),
|
||||
|
|
|
@ -402,6 +402,7 @@ export class VisualizeEmbeddable
|
|||
searchSessionId: this.input.searchSessionId,
|
||||
syncColors: this.input.syncColors,
|
||||
uiState: this.vis.uiState,
|
||||
interactive: !this.input.disableTriggers,
|
||||
inspectorAdapters: this.inspectorAdapters,
|
||||
executionContext: context,
|
||||
};
|
||||
|
|
|
@ -85,7 +85,6 @@ export function savedLens(): ExpressionFunctionDefinition<
|
|||
title: args.title === null ? undefined : args.title,
|
||||
disableTriggers: true,
|
||||
palette: args.palette,
|
||||
renderMode: 'noInteractivity',
|
||||
},
|
||||
embeddableType: EmbeddableTypes.lens,
|
||||
generatedAt: Date.now(),
|
||||
|
|
|
@ -13,8 +13,9 @@ export const defaultHandlers: RendererHandlers = {
|
|||
destroy: () => action('destroy'),
|
||||
getElementId: () => 'element-id',
|
||||
getFilter: () => 'filter',
|
||||
getRenderMode: () => 'display',
|
||||
getRenderMode: () => 'view',
|
||||
isSyncColorsEnabled: () => false,
|
||||
isInteractive: () => true,
|
||||
onComplete: (fn) => undefined,
|
||||
onEmbeddableDestroyed: action('onEmbeddableDestroyed'),
|
||||
onEmbeddableInputChange: action('onEmbeddableInputChange'),
|
||||
|
|
|
@ -26,8 +26,9 @@ export const createBaseHandlers = (): IInterpreterRenderHandlers => ({
|
|||
update() {},
|
||||
event() {},
|
||||
onDestroy() {},
|
||||
getRenderMode: () => 'display',
|
||||
getRenderMode: () => 'view',
|
||||
isSyncColorsEnabled: () => false,
|
||||
isInteractive: () => true,
|
||||
});
|
||||
|
||||
export const createHandlers = (baseHandlers = createBaseHandlers()): RendererHandlers => ({
|
||||
|
|
|
@ -48,7 +48,7 @@ const LensMarkDownRendererComponent: React.FC<LensMarkDownRendererProps> = ({
|
|||
style={{ height: LENS_VISUALIZATION_HEIGHT }}
|
||||
timeRange={timeRange}
|
||||
attributes={attributes}
|
||||
renderMode="display"
|
||||
renderMode="view"
|
||||
/>
|
||||
<LensChartTooltipFix />
|
||||
</Container>
|
||||
|
|
|
@ -151,7 +151,7 @@ describe('DatatableComponent', () => {
|
|||
dispatchEvent={onDispatchEvent}
|
||||
getType={jest.fn()}
|
||||
rowHasRowClickTriggerActions={[false, false, false]}
|
||||
renderMode="display"
|
||||
renderMode="view"
|
||||
paletteService={chartPluginMock.createPaletteRegistry()}
|
||||
uiSettings={({ get: jest.fn() } as unknown) as IUiSettingsClient}
|
||||
/>
|
||||
|
@ -427,7 +427,7 @@ describe('DatatableComponent', () => {
|
|||
formatFactory={() => ({ convert: (x) => x } as IFieldFormat)}
|
||||
dispatchEvent={onDispatchEvent}
|
||||
getType={jest.fn()}
|
||||
renderMode="display"
|
||||
renderMode="view"
|
||||
paletteService={chartPluginMock.createPaletteRegistry()}
|
||||
uiSettings={({ get: jest.fn() } as unknown) as IUiSettingsClient}
|
||||
/>
|
||||
|
@ -457,7 +457,7 @@ describe('DatatableComponent', () => {
|
|||
formatFactory={() => ({ convert: (x) => x } as IFieldFormat)}
|
||||
dispatchEvent={onDispatchEvent}
|
||||
getType={jest.fn()}
|
||||
renderMode="display"
|
||||
renderMode="view"
|
||||
paletteService={chartPluginMock.createPaletteRegistry()}
|
||||
uiSettings={({ get: jest.fn() } as unknown) as IUiSettingsClient}
|
||||
/>
|
||||
|
@ -485,7 +485,7 @@ describe('DatatableComponent', () => {
|
|||
formatFactory={() => ({ convert: (x) => x } as IFieldFormat)}
|
||||
dispatchEvent={onDispatchEvent}
|
||||
getType={jest.fn()}
|
||||
renderMode="display"
|
||||
renderMode="view"
|
||||
paletteService={chartPluginMock.createPaletteRegistry()}
|
||||
uiSettings={({ get: jest.fn() } as unknown) as IUiSettingsClient}
|
||||
/>
|
||||
|
@ -546,7 +546,7 @@ describe('DatatableComponent', () => {
|
|||
formatFactory={() => ({ convert: (x) => x } as IFieldFormat)}
|
||||
dispatchEvent={onDispatchEvent}
|
||||
getType={jest.fn()}
|
||||
renderMode="display"
|
||||
renderMode="view"
|
||||
paletteService={chartPluginMock.createPaletteRegistry()}
|
||||
uiSettings={({ get: jest.fn() } as unknown) as IUiSettingsClient}
|
||||
/>
|
||||
|
@ -581,7 +581,7 @@ describe('DatatableComponent', () => {
|
|||
formatFactory={() => ({ convert: (x) => x } as IFieldFormat)}
|
||||
dispatchEvent={onDispatchEvent}
|
||||
getType={jest.fn()}
|
||||
renderMode="display"
|
||||
renderMode="view"
|
||||
paletteService={chartPluginMock.createPaletteRegistry()}
|
||||
uiSettings={({ get: jest.fn() } as unknown) as IUiSettingsClient}
|
||||
/>
|
||||
|
@ -616,7 +616,7 @@ describe('DatatableComponent', () => {
|
|||
formatFactory={() => ({ convert: (x) => x } as IFieldFormat)}
|
||||
dispatchEvent={onDispatchEvent}
|
||||
getType={jest.fn()}
|
||||
renderMode="display"
|
||||
renderMode="view"
|
||||
paletteService={chartPluginMock.createPaletteRegistry()}
|
||||
uiSettings={({ get: jest.fn() } as unknown) as IUiSettingsClient}
|
||||
/>
|
||||
|
@ -650,7 +650,7 @@ describe('DatatableComponent', () => {
|
|||
formatFactory={() => ({ convert: (x) => x } as IFieldFormat)}
|
||||
dispatchEvent={onDispatchEvent}
|
||||
getType={jest.fn()}
|
||||
renderMode="display"
|
||||
renderMode="view"
|
||||
paletteService={chartPluginMock.createPaletteRegistry()}
|
||||
uiSettings={({ get: jest.fn() } as unknown) as IUiSettingsClient}
|
||||
/>
|
||||
|
|
|
@ -566,7 +566,8 @@ describe('embeddable', () => {
|
|||
timeRange,
|
||||
query,
|
||||
filters,
|
||||
renderMode: 'noInteractivity',
|
||||
renderMode: 'view',
|
||||
disableTriggers: true,
|
||||
} as LensEmbeddableInput;
|
||||
|
||||
const embeddable = new Embeddable(
|
||||
|
@ -599,7 +600,12 @@ describe('embeddable', () => {
|
|||
await embeddable.initializeSavedVis(input);
|
||||
embeddable.render(mountpoint);
|
||||
|
||||
expect(expressionRenderer.mock.calls[0][0].renderMode).toEqual('noInteractivity');
|
||||
expect(expressionRenderer.mock.calls[0][0]).toEqual(
|
||||
expect.objectContaining({
|
||||
interactive: false,
|
||||
renderMode: 'view',
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('should merge external context with query and filters of the saved object', async () => {
|
||||
|
|
|
@ -361,6 +361,7 @@ export class Embeddable
|
|||
searchSessionId={this.externalSearchContext.searchSessionId}
|
||||
handleEvent={this.handleEvent}
|
||||
onData$={this.updateActiveData}
|
||||
interactive={!input.disableTriggers}
|
||||
renderMode={input.renderMode}
|
||||
syncColors={input.syncColors}
|
||||
hasCompatibleActions={this.hasCompatibleActions}
|
||||
|
|
|
@ -38,6 +38,7 @@ export interface ExpressionWrapperProps {
|
|||
expression: string | null;
|
||||
errors: ErrorMessage[] | undefined;
|
||||
variables?: Record<string, unknown>;
|
||||
interactive?: boolean;
|
||||
searchContext: ExecutionContextSearch;
|
||||
searchSessionId?: string;
|
||||
handleEvent: (event: ExpressionRendererEvent) => void;
|
||||
|
@ -113,6 +114,7 @@ export function ExpressionWrapper({
|
|||
searchContext,
|
||||
variables,
|
||||
handleEvent,
|
||||
interactive,
|
||||
searchSessionId,
|
||||
onData$,
|
||||
renderMode,
|
||||
|
@ -137,6 +139,7 @@ export function ExpressionWrapper({
|
|||
padding="s"
|
||||
variables={variables}
|
||||
expression={expression}
|
||||
interactive={interactive}
|
||||
searchContext={searchContext}
|
||||
searchSessionId={searchSessionId}
|
||||
onData$={onData$}
|
||||
|
|
|
@ -44,6 +44,7 @@ export const getPieRenderer = (dependencies: {
|
|||
{...config}
|
||||
formatFactory={dependencies.formatFactory}
|
||||
chartsThemeService={dependencies.chartsThemeService}
|
||||
interactive={handlers.isInteractive()}
|
||||
paletteService={dependencies.paletteService}
|
||||
onClickValue={onClickValue}
|
||||
renderMode={handlers.getRenderMode()}
|
||||
|
|
|
@ -77,7 +77,7 @@ describe('PieVisualization component', () => {
|
|||
onClickValue: jest.fn(),
|
||||
chartsThemeService,
|
||||
paletteService: chartPluginMock.createPaletteRegistry(),
|
||||
renderMode: 'display' as const,
|
||||
renderMode: 'view' as const,
|
||||
syncColors: false,
|
||||
};
|
||||
}
|
||||
|
@ -302,10 +302,10 @@ describe('PieVisualization component', () => {
|
|||
`);
|
||||
});
|
||||
|
||||
test('does not set click listener on noInteractivity render mode', () => {
|
||||
test('does not set click listener on non-interactive mode', () => {
|
||||
const defaultArgs = getDefaultArgs();
|
||||
const component = shallow(
|
||||
<PieComponent args={{ ...args }} {...defaultArgs} renderMode="noInteractivity" />
|
||||
<PieComponent args={{ ...args }} {...defaultArgs} interactive={false} />
|
||||
);
|
||||
expect(component.find(Settings).first().prop('onElementClick')).toBeUndefined();
|
||||
});
|
||||
|
|
|
@ -55,6 +55,7 @@ export function PieComponent(
|
|||
props: PieExpressionProps & {
|
||||
formatFactory: FormatFactory;
|
||||
chartsThemeService: ChartsPluginSetup['theme'];
|
||||
interactive?: boolean;
|
||||
paletteService: PaletteRegistry;
|
||||
onClickValue: (data: LensFilterEvent['data']) => void;
|
||||
renderMode: RenderMode;
|
||||
|
@ -289,9 +290,7 @@ export function PieComponent(
|
|||
}
|
||||
legendPosition={legendPosition || Position.Right}
|
||||
legendMaxDepth={nestedLegend ? undefined : 1 /* Color is based only on first layer */}
|
||||
onElementClick={
|
||||
props.renderMode !== 'noInteractivity' ? onElementClickHandler : undefined
|
||||
}
|
||||
onElementClick={props.interactive ?? true ? onElementClickHandler : undefined}
|
||||
legendAction={getLegendAction(firstTable, onClickValue)}
|
||||
theme={{
|
||||
...chartTheme,
|
||||
|
|
|
@ -480,7 +480,7 @@ describe('xy_expression', () => {
|
|||
defaultProps = {
|
||||
formatFactory: getFormatSpy,
|
||||
timeZone: 'UTC',
|
||||
renderMode: 'display',
|
||||
renderMode: 'view',
|
||||
chartsThemeService,
|
||||
chartsActiveCursorService,
|
||||
paletteService,
|
||||
|
@ -1064,11 +1064,11 @@ describe('xy_expression', () => {
|
|||
});
|
||||
});
|
||||
|
||||
test('onBrushEnd is not set on noInteractivity mode', () => {
|
||||
test('onBrushEnd is not set on non-interactive mode', () => {
|
||||
const { args, data } = sampleArgs();
|
||||
|
||||
const wrapper = mountWithIntl(
|
||||
<XYChart {...defaultProps} data={data} args={args} renderMode="noInteractivity" />
|
||||
<XYChart {...defaultProps} data={data} args={args} interactive={false} />
|
||||
);
|
||||
|
||||
expect(wrapper.find(Settings).first().prop('onBrushEnd')).toBeUndefined();
|
||||
|
@ -1334,11 +1334,11 @@ describe('xy_expression', () => {
|
|||
});
|
||||
});
|
||||
|
||||
test('onElementClick is not triggering event on noInteractivity mode', () => {
|
||||
test('onElementClick is not triggering event on non-interactive mode', () => {
|
||||
const { args, data } = sampleArgs();
|
||||
|
||||
const wrapper = mountWithIntl(
|
||||
<XYChart {...defaultProps} data={data} args={args} renderMode="noInteractivity" />
|
||||
<XYChart {...defaultProps} data={data} args={args} interactive={false} />
|
||||
);
|
||||
|
||||
expect(wrapper.find(Settings).first().prop('onElementClick')).toBeUndefined();
|
||||
|
|
|
@ -93,6 +93,7 @@ export type XYChartRenderProps = XYChartProps & {
|
|||
formatFactory: FormatFactory;
|
||||
timeZone: string;
|
||||
minInterval: number | undefined;
|
||||
interactive?: boolean;
|
||||
onClickValue: (data: LensFilterEvent['data']) => void;
|
||||
onSelectRange: (data: LensBrushEvent['data']) => void;
|
||||
renderMode: RenderMode;
|
||||
|
@ -160,6 +161,7 @@ export const getXyChartRenderer = (dependencies: {
|
|||
paletteService={dependencies.paletteService}
|
||||
timeZone={dependencies.timeZone}
|
||||
minInterval={calculateMinInterval(config)}
|
||||
interactive={handlers.isInteractive()}
|
||||
onClickValue={onClickValue}
|
||||
onSelectRange={onSelectRange}
|
||||
renderMode={handlers.getRenderMode()}
|
||||
|
@ -233,7 +235,7 @@ export function XYChart({
|
|||
minInterval,
|
||||
onClickValue,
|
||||
onSelectRange,
|
||||
renderMode,
|
||||
interactive = true,
|
||||
syncColors,
|
||||
}: XYChartRenderProps) {
|
||||
const {
|
||||
|
@ -528,8 +530,8 @@ export function XYChart({
|
|||
}}
|
||||
rotation={shouldRotate ? 90 : 0}
|
||||
xDomain={xDomain}
|
||||
onBrushEnd={renderMode !== 'noInteractivity' ? brushHandler : undefined}
|
||||
onElementClick={renderMode !== 'noInteractivity' ? clickHandler : undefined}
|
||||
onBrushEnd={interactive ? brushHandler : undefined}
|
||||
onElementClick={interactive ? clickHandler : undefined}
|
||||
legendAction={getLegendAction(
|
||||
filteredLayers,
|
||||
data.tables,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue