mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 09:19:04 -04:00
[Lens] Remove <NativeRenderer /> (#161521)
## Summary The NativeRenderer component is currently used to mount another component in a separate mounting point. As far as I recall, we introduced <NativeRenderer/> to allow users to create visualizations in non-React frameworks. The idea was that users could write their own Lens visualizations or datasources code and integrate it with our system. However, it seems that this concept hasn't gained traction and we don’t have it prioritized. Even if users express interest in writing their visualizations outside of React, it is still possible to do so with some additional boilerplate code (which we could provide as an example non-React visualization). Pros: 1. Simplifies and shortens the code: 1.1. Testing and debugging become easier as we no longer need to check separate React trees when integrating frame, data source, and visualization components. 1.2. Components communicate using standard React patterns, making maintenance and comprehension simpler. 1.3. Context providers no longer need to be passed to each separate component since they are already within the context. 1.4. Easier propagation of events or any other form of inter-component communication. 2. Greatly improves performance and facilitates maintenance: 2.1. Directly accessing context inside the DatasourcePanel eliminates the need for context passing, resulting in better performance. 2.2. Removing the requirement for a separate React root also contributes to improved performance. 3. The render method will be removed when we upgrade to React 18. While we could replace it with the new createRoot method, it makes sense to perform some cleanup ;) Cons: 1. Setting up non-React visualization or data source code might become slightly more complex. Performance improvement for drag and drop action with these changes: before: <img width="1110" alt="Screenshot 2023-07-10 at 07 14 39" src="45a1b09b
-5189-46f5-af2b-7781fcf4e774"> after: <img width="1117" alt="Screenshot 2023-07-10 at 07 16 24" src="0e704da1
-3220-4eb9-8fa0-cc3584a90090"> ## Single render when dragging: (the first image is 3 screenshots from 3 different react roots as they have separate mounting point. The complete render time is ~380ms) <img width="1117" alt="Screenshot 2023-07-10 at 07 16 24" src="6d7f2d9f
-a758-476e-8efb-38693ae90097"> After we have one common render tree. Because we don't have to pass context down as a prop, we greatly reduced the number of components rerendered. (I will be working on reducing the render time for workspace panel as this seems to still be a bottleneck point) <img width="732" alt="Screenshot 2023-07-10 at 14 52 41" src="03ec97b3
-8225-490e-8884-0fd4e69587e8"> --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
5d066944fc
commit
95e50875e1
33 changed files with 425 additions and 1197 deletions
|
@ -16,9 +16,6 @@
|
|||
"developerExamples",
|
||||
"expressions",
|
||||
"fieldFormats"
|
||||
],
|
||||
"requiredBundles": [
|
||||
"kibanaReact"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,10 +7,8 @@
|
|||
|
||||
import React from 'react';
|
||||
import { EuiFormRow, EuiColorPicker } from '@elastic/eui';
|
||||
import { render } from 'react-dom';
|
||||
import { Ast } from '@kbn/interpreter';
|
||||
import { ThemeServiceStart } from '@kbn/core/public';
|
||||
import { KibanaThemeProvider } from '@kbn/kibana-react-plugin/public';
|
||||
import { Visualization, OperationMetadata } from '@kbn/lens-plugin/public';
|
||||
import { layerTypes } from '@kbn/lens-plugin/public';
|
||||
import type { RotatingNumberState } from '../common/types';
|
||||
|
@ -166,19 +164,16 @@ export const getRotatingNumberVisualization = ({
|
|||
return { ...prevState, accessor: undefined };
|
||||
},
|
||||
|
||||
renderDimensionEditor(domElement, props) {
|
||||
render(
|
||||
<KibanaThemeProvider theme$={theme.theme$}>
|
||||
<EuiFormRow label="Pick a color">
|
||||
<EuiColorPicker
|
||||
onChange={(newColor) => {
|
||||
props.setState({ ...props.state, color: newColor });
|
||||
}}
|
||||
color={props.state.color}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
</KibanaThemeProvider>,
|
||||
domElement
|
||||
DimensionEditorComponent(props) {
|
||||
return (
|
||||
<EuiFormRow label="Pick a color">
|
||||
<EuiColorPicker
|
||||
onChange={(newColor) => {
|
||||
props.setState({ ...props.state, color: newColor });
|
||||
}}
|
||||
color={props.state.color}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
"kbn_references": [
|
||||
"@kbn/core",
|
||||
"@kbn/expressions-plugin",
|
||||
"@kbn/kibana-react-plugin",
|
||||
"@kbn/data-views-plugin",
|
||||
"@kbn/field-formats-plugin",
|
||||
"@kbn/lens-plugin",
|
||||
|
|
|
@ -196,6 +196,7 @@ describe('LensEditConfigurationFlyout', () => {
|
|||
const { instance } = await prepareAndMountComponent(props);
|
||||
expect(instance.find(VisualizationToolbar).prop('activeVisualization')).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"DimensionEditorComponent": [MockFunction],
|
||||
"appendLayer": [MockFunction],
|
||||
"clearLayer": [MockFunction],
|
||||
"getConfiguration": [MockFunction] {
|
||||
|
@ -414,7 +415,6 @@ describe('LensEditConfigurationFlyout', () => {
|
|||
"initialize": [MockFunction],
|
||||
"removeDimension": [MockFunction],
|
||||
"removeLayer": [MockFunction],
|
||||
"renderDimensionEditor": [MockFunction],
|
||||
"setDimension": [MockFunction],
|
||||
"switchVisualizationType": [MockFunction],
|
||||
"toExpression": [MockFunction],
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { createMockedDragDropContext } from '../../mocks';
|
||||
import { dataPluginMock } from '@kbn/data-plugin/public/mocks';
|
||||
import {
|
||||
dataViewPluginMocks,
|
||||
|
@ -408,9 +407,6 @@ describe('FormBased Data Panel', () => {
|
|||
dataViews,
|
||||
}),
|
||||
setState: jest.fn(),
|
||||
dragDropContext: createMockedDragDropContext({
|
||||
dragging: { id: '1', humanData: { label: 'Label' } },
|
||||
}),
|
||||
dateRange: { fromDate: '2019-01-01', toDate: '2020-01-01' },
|
||||
frame: getFrameAPIMock({
|
||||
indexPatterns: indexPatterns as unknown as DataViewsState['indexPatterns'],
|
||||
|
|
|
@ -6,8 +6,6 @@
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { render } from 'react-dom';
|
||||
import { I18nProvider } from '@kbn/i18n-react';
|
||||
import type { CoreStart, SavedObjectReference } from '@kbn/core/public';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { TimeRange } from '@kbn/es-query';
|
||||
|
@ -16,7 +14,6 @@ import type { FieldFormatsStart } from '@kbn/field-formats-plugin/public';
|
|||
import { flatten, isEqual } from 'lodash';
|
||||
import type { DataViewsPublicPluginStart, DataView } from '@kbn/data-views-plugin/public';
|
||||
import type { IndexPatternFieldEditorStart } from '@kbn/data-view-field-editor-plugin/public';
|
||||
import { KibanaContextProvider, KibanaThemeProvider } from '@kbn/kibana-react-plugin/public';
|
||||
import { DataPublicPluginStart, UI_SETTINGS } from '@kbn/data-plugin/public';
|
||||
import { VisualizeFieldContext } from '@kbn/ui-actions-plugin/public';
|
||||
import { ChartsPluginSetup } from '@kbn/charts-plugin/public';
|
||||
|
@ -25,7 +22,7 @@ import { FormattedMessage } from '@kbn/i18n-react';
|
|||
import type { UnifiedSearchPublicPluginStart } from '@kbn/unified-search-plugin/public';
|
||||
import { EuiButton } from '@elastic/eui';
|
||||
import type { SharePluginStart } from '@kbn/share-plugin/public';
|
||||
import { ChildDragDropProvider, type DraggingIdentifier } from '@kbn/dom-drag-drop';
|
||||
import { type DraggingIdentifier } from '@kbn/dom-drag-drop';
|
||||
import { DimensionTrigger } from '@kbn/visualization-ui-components/public';
|
||||
import memoizeOne from 'memoize-one';
|
||||
import type {
|
||||
|
@ -191,7 +188,7 @@ export function getFormBasedDatasource({
|
|||
dataViewFieldEditor: IndexPatternFieldEditorStart;
|
||||
uiActions: UiActionsStart;
|
||||
}) {
|
||||
const { uiSettings, settings } = core;
|
||||
const { uiSettings } = core;
|
||||
|
||||
const DATASOURCE_ID = 'formBased';
|
||||
const ALIAS_IDS = ['indexpattern'];
|
||||
|
@ -452,68 +449,27 @@ export function getFormBasedDatasource({
|
|||
searchSessionId
|
||||
),
|
||||
|
||||
renderLayerSettings(domElement, props) {
|
||||
render(
|
||||
<KibanaThemeProvider theme$={core.theme.theme$}>
|
||||
<I18nProvider>
|
||||
<KibanaContextProvider
|
||||
services={{
|
||||
...core,
|
||||
data,
|
||||
dataViews,
|
||||
fieldFormats,
|
||||
charts,
|
||||
unifiedSearch,
|
||||
share,
|
||||
}}
|
||||
>
|
||||
<LayerSettingsPanel {...props} />
|
||||
</KibanaContextProvider>
|
||||
</I18nProvider>
|
||||
</KibanaThemeProvider>,
|
||||
domElement
|
||||
);
|
||||
LayerSettingsComponent(props) {
|
||||
return <LayerSettingsPanel {...props} />;
|
||||
},
|
||||
|
||||
renderDataPanel(domElement: Element, props: DatasourceDataPanelProps<FormBasedPrivateState>) {
|
||||
const { onChangeIndexPattern, dragDropContext, ...otherProps } = props;
|
||||
DataPanelComponent(props: DatasourceDataPanelProps<FormBasedPrivateState>) {
|
||||
const { onChangeIndexPattern, ...otherProps } = props;
|
||||
const layerFields = formBasedDatasource?.getSelectedFields?.(props.state);
|
||||
|
||||
render(
|
||||
<KibanaThemeProvider theme$={core.theme.theme$}>
|
||||
<I18nProvider>
|
||||
<KibanaContextProvider
|
||||
services={{
|
||||
...core,
|
||||
data,
|
||||
dataViews,
|
||||
fieldFormats,
|
||||
charts,
|
||||
unifiedSearch,
|
||||
share,
|
||||
}}
|
||||
>
|
||||
<ChildDragDropProvider value={dragDropContext}>
|
||||
<FormBasedDataPanel
|
||||
data={data}
|
||||
dataViews={dataViews}
|
||||
fieldFormats={fieldFormats}
|
||||
charts={charts}
|
||||
indexPatternFieldEditor={dataViewFieldEditor}
|
||||
{...otherProps}
|
||||
core={core}
|
||||
uiActions={uiActions}
|
||||
onIndexPatternRefresh={onRefreshIndexPattern}
|
||||
layerFields={layerFields}
|
||||
/>
|
||||
</ChildDragDropProvider>
|
||||
</KibanaContextProvider>
|
||||
</I18nProvider>
|
||||
</KibanaThemeProvider>,
|
||||
domElement
|
||||
return (
|
||||
<FormBasedDataPanel
|
||||
data={data}
|
||||
dataViews={dataViews}
|
||||
fieldFormats={fieldFormats}
|
||||
charts={charts}
|
||||
indexPatternFieldEditor={dataViewFieldEditor}
|
||||
{...otherProps}
|
||||
core={core}
|
||||
uiActions={uiActions}
|
||||
onIndexPatternRefresh={onRefreshIndexPattern}
|
||||
layerFields={layerFields}
|
||||
/>
|
||||
);
|
||||
},
|
||||
|
||||
uniqueLabels(state: FormBasedPrivateState, indexPatternsMap: IndexPatternMap) {
|
||||
const layers = state.layers;
|
||||
const columnLabelMap = {} as Record<string, string>;
|
||||
|
@ -540,115 +496,48 @@ export function getFormBasedDatasource({
|
|||
return columnLabelMap;
|
||||
},
|
||||
|
||||
renderDimensionTrigger: (
|
||||
domElement: Element,
|
||||
props: DatasourceDimensionTriggerProps<FormBasedPrivateState>
|
||||
) => {
|
||||
DimensionTriggerComponent: (props: DatasourceDimensionTriggerProps<FormBasedPrivateState>) => {
|
||||
const columnLabelMap = formBasedDatasource.uniqueLabels(props.state, props.indexPatterns);
|
||||
const uniqueLabel = columnLabelMap[props.columnId];
|
||||
const formattedLabel = wrapOnDot(uniqueLabel);
|
||||
|
||||
render(
|
||||
<KibanaThemeProvider theme$={core.theme.theme$}>
|
||||
<I18nProvider>
|
||||
<KibanaContextProvider
|
||||
services={{
|
||||
appName: 'lens',
|
||||
storage,
|
||||
uiSettings,
|
||||
settings,
|
||||
data,
|
||||
fieldFormats,
|
||||
savedObjects: core.savedObjects,
|
||||
docLinks: core.docLinks,
|
||||
unifiedSearch,
|
||||
}}
|
||||
>
|
||||
<DimensionTrigger id={props.columnId} label={formattedLabel} />
|
||||
</KibanaContextProvider>
|
||||
</I18nProvider>
|
||||
</KibanaThemeProvider>,
|
||||
domElement
|
||||
);
|
||||
return <DimensionTrigger id={props.columnId} label={formattedLabel} />;
|
||||
},
|
||||
|
||||
renderDimensionEditor: (
|
||||
domElement: Element,
|
||||
props: DatasourceDimensionEditorProps<FormBasedPrivateState>
|
||||
) => {
|
||||
DimensionEditorComponent: (props: DatasourceDimensionEditorProps<FormBasedPrivateState>) => {
|
||||
const columnLabelMap = formBasedDatasource.uniqueLabels(props.state, props.indexPatterns);
|
||||
|
||||
render(
|
||||
<KibanaThemeProvider theme$={core.theme.theme$}>
|
||||
<I18nProvider>
|
||||
<KibanaContextProvider
|
||||
services={{
|
||||
appName: 'lens',
|
||||
storage,
|
||||
uiSettings,
|
||||
settings,
|
||||
data,
|
||||
fieldFormats,
|
||||
savedObjects: core.savedObjects,
|
||||
docLinks: core.docLinks,
|
||||
http: core.http,
|
||||
unifiedSearch,
|
||||
}}
|
||||
>
|
||||
<FormBasedDimensionEditor
|
||||
uiSettings={uiSettings}
|
||||
storage={storage}
|
||||
fieldFormats={fieldFormats}
|
||||
http={core.http}
|
||||
data={data}
|
||||
unifiedSearch={unifiedSearch}
|
||||
dataViews={dataViews}
|
||||
uniqueLabel={columnLabelMap[props.columnId]}
|
||||
notifications={core.notifications}
|
||||
{...props}
|
||||
/>
|
||||
</KibanaContextProvider>
|
||||
</I18nProvider>
|
||||
</KibanaThemeProvider>,
|
||||
domElement
|
||||
return (
|
||||
<FormBasedDimensionEditor
|
||||
uiSettings={uiSettings}
|
||||
storage={storage}
|
||||
fieldFormats={fieldFormats}
|
||||
http={core.http}
|
||||
data={data}
|
||||
unifiedSearch={unifiedSearch}
|
||||
dataViews={dataViews}
|
||||
uniqueLabel={columnLabelMap[props.columnId]}
|
||||
notifications={core.notifications}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
},
|
||||
|
||||
renderLayerPanel: (
|
||||
domElement: Element,
|
||||
props: DatasourceLayerPanelProps<FormBasedPrivateState>
|
||||
) => {
|
||||
LayerPanelComponent: (props: DatasourceLayerPanelProps<FormBasedPrivateState>) => {
|
||||
const { onChangeIndexPattern, ...otherProps } = props;
|
||||
render(
|
||||
<KibanaThemeProvider theme$={core.theme.theme$}>
|
||||
<I18nProvider>
|
||||
<KibanaContextProvider
|
||||
services={{
|
||||
...core,
|
||||
data,
|
||||
dataViews,
|
||||
fieldFormats,
|
||||
charts,
|
||||
unifiedSearch,
|
||||
share,
|
||||
}}
|
||||
>
|
||||
<LayerPanel
|
||||
onChangeIndexPattern={(indexPatternId) => {
|
||||
triggerActionOnIndexPatternChange({
|
||||
indexPatternId,
|
||||
state: props.state,
|
||||
layerId: props.layerId,
|
||||
uiActions,
|
||||
});
|
||||
onChangeIndexPattern(indexPatternId, DATASOURCE_ID, props.layerId);
|
||||
}}
|
||||
{...otherProps}
|
||||
/>
|
||||
</KibanaContextProvider>
|
||||
</I18nProvider>
|
||||
</KibanaThemeProvider>,
|
||||
domElement
|
||||
return (
|
||||
<LayerPanel
|
||||
onChangeIndexPattern={(indexPatternId) => {
|
||||
triggerActionOnIndexPatternChange({
|
||||
indexPatternId,
|
||||
state: props.state,
|
||||
layerId: props.layerId,
|
||||
uiActions,
|
||||
});
|
||||
onChangeIndexPattern(indexPatternId, DATASOURCE_ID, props.layerId);
|
||||
}}
|
||||
{...otherProps}
|
||||
/>
|
||||
);
|
||||
},
|
||||
|
||||
|
|
|
@ -28,7 +28,7 @@ import { mountWithIntl } from '@kbn/test-jest-helpers';
|
|||
|
||||
import { uiActionsPluginMock } from '@kbn/ui-actions-plugin/public/mocks';
|
||||
import { createIndexPatternServiceMock } from '../../mocks/data_views_service_mock';
|
||||
import { createMockFramePublicAPI, createMockedDragDropContext } from '../../mocks';
|
||||
import { createMockFramePublicAPI } from '../../mocks';
|
||||
import { DataViewsState } from '../../state_management';
|
||||
|
||||
const fieldsFromQuery = [
|
||||
|
@ -184,7 +184,6 @@ describe('TextBased Query Languages Data Panel', () => {
|
|||
])
|
||||
),
|
||||
},
|
||||
dragDropContext: createMockedDragDropContext(),
|
||||
core,
|
||||
dateRange: {
|
||||
fromDate: 'now-7d',
|
||||
|
|
|
@ -6,15 +6,12 @@
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { render } from 'react-dom';
|
||||
import { I18nProvider } from '@kbn/i18n-react';
|
||||
import { CoreStart } from '@kbn/core/public';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { IStorageWrapper } from '@kbn/kibana-utils-plugin/public';
|
||||
import type { AggregateQuery } from '@kbn/es-query';
|
||||
import type { SavedObjectReference } from '@kbn/core/public';
|
||||
import { EuiFormRow } from '@elastic/eui';
|
||||
import { KibanaThemeProvider } from '@kbn/kibana-react-plugin/public';
|
||||
import type { ExpressionsStart, DatatableColumnType } from '@kbn/expressions-plugin/public';
|
||||
import type { DataViewsPublicPluginStart, DataView } from '@kbn/data-views-plugin/public';
|
||||
import type { DataPublicPluginStart } from '@kbn/data-plugin/public';
|
||||
|
@ -22,7 +19,6 @@ import { euiThemeVars } from '@kbn/ui-theme';
|
|||
import { DimensionTrigger } from '@kbn/visualization-ui-components/public';
|
||||
import memoizeOne from 'memoize-one';
|
||||
import { isEqual } from 'lodash';
|
||||
import { ChildDragDropProvider } from '@kbn/dom-drag-drop';
|
||||
import {
|
||||
DatasourceDimensionEditorProps,
|
||||
DatasourceDataPanelProps,
|
||||
|
@ -364,31 +360,20 @@ export function getTextBasedDatasource({
|
|||
);
|
||||
},
|
||||
|
||||
renderDataPanel(domElement: Element, props: DatasourceDataPanelProps<TextBasedPrivateState>) {
|
||||
DataPanelComponent(props: DatasourceDataPanelProps<TextBasedPrivateState>) {
|
||||
const layerFields = TextBasedDatasource?.getSelectedFields?.(props.state);
|
||||
const { dragDropContext, ...otherProps } = props;
|
||||
render(
|
||||
<KibanaThemeProvider theme$={core.theme.theme$}>
|
||||
<I18nProvider>
|
||||
<ChildDragDropProvider value={dragDropContext}>
|
||||
<TextBasedDataPanel
|
||||
data={data}
|
||||
dataViews={dataViews}
|
||||
expressions={expressions}
|
||||
layerFields={layerFields}
|
||||
{...otherProps}
|
||||
/>
|
||||
</ChildDragDropProvider>
|
||||
</I18nProvider>
|
||||
</KibanaThemeProvider>,
|
||||
domElement
|
||||
return (
|
||||
<TextBasedDataPanel
|
||||
data={data}
|
||||
dataViews={dataViews}
|
||||
expressions={expressions}
|
||||
layerFields={layerFields}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
},
|
||||
|
||||
renderDimensionTrigger: (
|
||||
domElement: Element,
|
||||
props: DatasourceDimensionTriggerProps<TextBasedPrivateState>
|
||||
) => {
|
||||
DimensionTriggerComponent: (props: DatasourceDimensionTriggerProps<TextBasedPrivateState>) => {
|
||||
const columnLabelMap = TextBasedDatasource.uniqueLabels(props.state, props.indexPatterns);
|
||||
const layer = props.state.layers[props.layerId];
|
||||
const selectedField = layer?.allColumns?.find((column) => column.columnId === props.columnId);
|
||||
|
@ -397,23 +382,18 @@ export function getTextBasedDatasource({
|
|||
customLabel = selectedField?.fieldName;
|
||||
}
|
||||
|
||||
render(
|
||||
<KibanaThemeProvider theme$={core.theme.theme$}>
|
||||
<I18nProvider>
|
||||
<DimensionTrigger
|
||||
id={props.columnId}
|
||||
color={customLabel && selectedField ? 'primary' : 'danger'}
|
||||
dataTestSubj="lns-dimensionTrigger-textBased"
|
||||
label={
|
||||
customLabel ??
|
||||
i18n.translate('xpack.lens.textBasedLanguages.missingField', {
|
||||
defaultMessage: 'Missing field',
|
||||
})
|
||||
}
|
||||
/>{' '}
|
||||
</I18nProvider>
|
||||
</KibanaThemeProvider>,
|
||||
domElement
|
||||
return (
|
||||
<DimensionTrigger
|
||||
id={props.columnId}
|
||||
color={customLabel && selectedField ? 'primary' : 'danger'}
|
||||
dataTestSubj="lns-dimensionTrigger-textBased"
|
||||
label={
|
||||
customLabel ??
|
||||
i18n.translate('xpack.lens.textBasedLanguages.missingField', {
|
||||
defaultMessage: 'Missing field',
|
||||
})
|
||||
}
|
||||
/>
|
||||
);
|
||||
},
|
||||
|
||||
|
@ -421,10 +401,7 @@ export function getTextBasedDatasource({
|
|||
return [];
|
||||
},
|
||||
|
||||
renderDimensionEditor: (
|
||||
domElement: Element,
|
||||
props: DatasourceDimensionEditorProps<TextBasedPrivateState>
|
||||
) => {
|
||||
DimensionEditorComponent: (props: DatasourceDimensionEditorProps<TextBasedPrivateState>) => {
|
||||
const fields = props.state.fieldList;
|
||||
const selectedField = props.state.layers[props.layerId]?.allColumns?.find(
|
||||
(column) => column.columnId === props.columnId
|
||||
|
@ -442,94 +419,81 @@ export function getTextBasedDatasource({
|
|||
: true,
|
||||
};
|
||||
});
|
||||
render(
|
||||
<KibanaThemeProvider theme$={core.theme.theme$}>
|
||||
<I18nProvider>
|
||||
<EuiFormRow
|
||||
data-test-subj="text-based-languages-field-selection-row"
|
||||
label={i18n.translate('xpack.lens.textBasedLanguages.chooseField', {
|
||||
defaultMessage: 'Field',
|
||||
})}
|
||||
fullWidth
|
||||
className="lnsIndexPatternDimensionEditor--padded"
|
||||
return (
|
||||
<>
|
||||
<EuiFormRow
|
||||
data-test-subj="text-based-languages-field-selection-row"
|
||||
label={i18n.translate('xpack.lens.textBasedLanguages.chooseField', {
|
||||
defaultMessage: 'Field',
|
||||
})}
|
||||
fullWidth
|
||||
className="lnsIndexPatternDimensionEditor--padded"
|
||||
>
|
||||
<FieldSelect
|
||||
existingFields={updatedFields}
|
||||
selectedField={selectedField}
|
||||
onChoose={(choice) => {
|
||||
const meta = fields.find((f) => f.name === choice.field)?.meta;
|
||||
const newColumn = {
|
||||
columnId: props.columnId,
|
||||
fieldName: choice.field,
|
||||
meta,
|
||||
};
|
||||
return props.setState(
|
||||
!selectedField
|
||||
? {
|
||||
...props.state,
|
||||
layers: {
|
||||
...props.state.layers,
|
||||
[props.layerId]: {
|
||||
...props.state.layers[props.layerId],
|
||||
columns: [...props.state.layers[props.layerId].columns, newColumn],
|
||||
allColumns: [
|
||||
...props.state.layers[props.layerId].allColumns,
|
||||
newColumn,
|
||||
],
|
||||
},
|
||||
},
|
||||
}
|
||||
: {
|
||||
...props.state,
|
||||
layers: {
|
||||
...props.state.layers,
|
||||
[props.layerId]: {
|
||||
...props.state.layers[props.layerId],
|
||||
columns: props.state.layers[props.layerId].columns.map((col) =>
|
||||
col.columnId !== props.columnId
|
||||
? col
|
||||
: { ...col, fieldName: choice.field, meta }
|
||||
),
|
||||
allColumns: props.state.layers[props.layerId].allColumns.map((col) =>
|
||||
col.columnId !== props.columnId
|
||||
? col
|
||||
: { ...col, fieldName: choice.field, meta }
|
||||
),
|
||||
},
|
||||
},
|
||||
}
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
{props.dataSectionExtra && (
|
||||
<div
|
||||
style={{
|
||||
paddingLeft: euiThemeVars.euiSize,
|
||||
paddingRight: euiThemeVars.euiSize,
|
||||
}}
|
||||
>
|
||||
<FieldSelect
|
||||
existingFields={updatedFields}
|
||||
selectedField={selectedField}
|
||||
onChoose={(choice) => {
|
||||
const meta = fields.find((f) => f.name === choice.field)?.meta;
|
||||
const newColumn = {
|
||||
columnId: props.columnId,
|
||||
fieldName: choice.field,
|
||||
meta,
|
||||
};
|
||||
return props.setState(
|
||||
!selectedField
|
||||
? {
|
||||
...props.state,
|
||||
layers: {
|
||||
...props.state.layers,
|
||||
[props.layerId]: {
|
||||
...props.state.layers[props.layerId],
|
||||
columns: [...props.state.layers[props.layerId].columns, newColumn],
|
||||
allColumns: [
|
||||
...props.state.layers[props.layerId].allColumns,
|
||||
newColumn,
|
||||
],
|
||||
},
|
||||
},
|
||||
}
|
||||
: {
|
||||
...props.state,
|
||||
layers: {
|
||||
...props.state.layers,
|
||||
[props.layerId]: {
|
||||
...props.state.layers[props.layerId],
|
||||
columns: props.state.layers[props.layerId].columns.map((col) =>
|
||||
col.columnId !== props.columnId
|
||||
? col
|
||||
: { ...col, fieldName: choice.field, meta }
|
||||
),
|
||||
allColumns: props.state.layers[props.layerId].allColumns.map((col) =>
|
||||
col.columnId !== props.columnId
|
||||
? col
|
||||
: { ...col, fieldName: choice.field, meta }
|
||||
),
|
||||
},
|
||||
},
|
||||
}
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
{props.dataSectionExtra && (
|
||||
<div
|
||||
style={{
|
||||
paddingLeft: euiThemeVars.euiSize,
|
||||
paddingRight: euiThemeVars.euiSize,
|
||||
}}
|
||||
>
|
||||
{props.dataSectionExtra}
|
||||
</div>
|
||||
)}
|
||||
</I18nProvider>
|
||||
</KibanaThemeProvider>,
|
||||
domElement
|
||||
{props.dataSectionExtra}
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
},
|
||||
|
||||
renderLayerPanel: (
|
||||
domElement: Element,
|
||||
props: DatasourceLayerPanelProps<TextBasedPrivateState>
|
||||
) => {
|
||||
render(
|
||||
<KibanaThemeProvider theme$={core.theme.theme$}>
|
||||
<I18nProvider>
|
||||
<LayerPanel {...props} />
|
||||
</I18nProvider>
|
||||
</KibanaThemeProvider>,
|
||||
domElement
|
||||
);
|
||||
LayerPanelComponent: (props: DatasourceLayerPanelProps<TextBasedPrivateState>) => {
|
||||
return <LayerPanel {...props} />;
|
||||
},
|
||||
|
||||
uniqueLabels(state: TextBasedPrivateState) {
|
||||
|
|
|
@ -390,7 +390,7 @@ describe('LayerPanel', () => {
|
|||
},
|
||||
],
|
||||
});
|
||||
mockVisualization.renderDimensionEditor = jest.fn();
|
||||
mockVisualization.DimensionEditorComponent = jest.fn().mockImplementation(() => <div />);
|
||||
|
||||
const { instance } = await mountWithProvider(<LayerPanel {...getDefaultProps()} />);
|
||||
act(() => {
|
||||
|
@ -434,14 +434,13 @@ describe('LayerPanel', () => {
|
|||
});
|
||||
instance.update();
|
||||
|
||||
expect(mockDatasource.renderDimensionEditor).toHaveBeenCalledWith(
|
||||
expect.any(Element),
|
||||
expect(mockDatasource.DimensionEditorComponent).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ columnId: 'newid' })
|
||||
);
|
||||
const stateFn =
|
||||
mockDatasource.renderDimensionEditor.mock.calls[
|
||||
mockDatasource.renderDimensionEditor.mock.calls.length - 1
|
||||
][1].setState;
|
||||
mockDatasource.DimensionEditorComponent.mock.calls[
|
||||
mockDatasource.DimensionEditorComponent.mock.calls.length - 1
|
||||
][0].setState;
|
||||
|
||||
act(() => {
|
||||
stateFn(
|
||||
|
@ -512,14 +511,13 @@ describe('LayerPanel', () => {
|
|||
});
|
||||
instance.update();
|
||||
|
||||
expect(mockDatasource.renderDimensionEditor).toHaveBeenCalledWith(
|
||||
expect.any(Element),
|
||||
expect(mockDatasource.DimensionEditorComponent).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ columnId: 'y' })
|
||||
);
|
||||
const stateFn =
|
||||
mockDatasource.renderDimensionEditor.mock.calls[
|
||||
mockDatasource.renderDimensionEditor.mock.calls.length - 1
|
||||
][1].setState;
|
||||
mockDatasource.DimensionEditorComponent.mock.calls[
|
||||
mockDatasource.DimensionEditorComponent.mock.calls.length - 1
|
||||
][0].setState;
|
||||
|
||||
act(() => {
|
||||
stateFn(
|
||||
|
@ -586,9 +584,9 @@ describe('LayerPanel', () => {
|
|||
expect(instance.find('EuiFlyoutHeader').exists()).toBe(true);
|
||||
|
||||
const lastArgs =
|
||||
mockDatasource.renderDimensionEditor.mock.calls[
|
||||
mockDatasource.renderDimensionEditor.mock.calls.length - 1
|
||||
][1];
|
||||
mockDatasource.DimensionEditorComponent.mock.calls[
|
||||
mockDatasource.DimensionEditorComponent.mock.calls.length - 1
|
||||
][0];
|
||||
|
||||
// Simulate what is called by the dimension editor
|
||||
act(() => {
|
||||
|
@ -597,7 +595,7 @@ describe('LayerPanel', () => {
|
|||
});
|
||||
});
|
||||
|
||||
expect(mockVisualization.renderDimensionEditor).toHaveBeenCalled();
|
||||
expect(mockVisualization.DimensionEditorComponent).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should close the DimensionContainer when the active visualization changes', async () => {
|
||||
|
@ -1331,7 +1329,7 @@ describe('LayerPanel', () => {
|
|||
],
|
||||
});
|
||||
await mountWithProvider(<LayerPanel {...getDefaultProps()} />);
|
||||
expect(mockDatasource.renderDimensionTrigger).toHaveBeenCalled();
|
||||
expect(mockDatasource.DimensionTriggerComponent).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should render visualization dimension trigger if there is no layer datasource', async () => {
|
||||
|
@ -1354,14 +1352,14 @@ describe('LayerPanel', () => {
|
|||
framePublicAPI: { ...props.framePublicAPI, datasourceLayers: {} },
|
||||
};
|
||||
|
||||
mockVisualization.renderDimensionTrigger = jest.fn();
|
||||
mockVisualization.DimensionTriggerComponent = jest.fn().mockImplementation(() => <div />);
|
||||
mockVisualization.getUniqueLabels = jest.fn(() => ({
|
||||
x: 'A',
|
||||
}));
|
||||
|
||||
await mountWithProvider(<LayerPanel {...propsWithVisOnlyLayer} />);
|
||||
expect(mockDatasource.renderDimensionTrigger).not.toHaveBeenCalled();
|
||||
expect(mockVisualization.renderDimensionTrigger).toHaveBeenCalled();
|
||||
expect(mockDatasource.DimensionTriggerComponent).not.toHaveBeenCalled();
|
||||
expect(mockVisualization.DimensionTriggerComponent).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
// TODO - test user message display
|
||||
|
|
|
@ -24,7 +24,6 @@ import { DragDropIdentifier, ReorderProvider, DropType } from '@kbn/dom-drag-dro
|
|||
import { DimensionButton, DimensionTrigger } from '@kbn/visualization-ui-components/public';
|
||||
import { LayerActions } from './layer_actions';
|
||||
import { IndexPatternServiceAPI } from '../../../data_views_service/service';
|
||||
import { NativeRenderer } from '../../../native_renderer';
|
||||
import {
|
||||
StateSetter,
|
||||
Visualization,
|
||||
|
@ -374,8 +373,8 @@ export function LayerPanel(
|
|||
isTextBasedLanguage,
|
||||
hasLayerSettings: Boolean(
|
||||
(Object.values(visualizationLayerSettings).some(Boolean) &&
|
||||
activeVisualization.renderLayerSettings) ||
|
||||
layerDatasource?.renderLayerSettings
|
||||
activeVisualization.LayerSettingsComponent) ||
|
||||
layerDatasource?.LayerSettingsComponent
|
||||
),
|
||||
openLayerSettings: () => setPanelSettingsOpen(true),
|
||||
onCloneLayer,
|
||||
|
@ -398,7 +397,7 @@ export function LayerPanel(
|
|||
isOnlyLayer,
|
||||
isTextBasedLanguage,
|
||||
visualizationLayerSettings,
|
||||
layerDatasource?.renderLayerSettings,
|
||||
layerDatasource?.LayerSettingsComponent,
|
||||
onCloneLayer,
|
||||
onRemoveLayer,
|
||||
]
|
||||
|
@ -438,11 +437,12 @@ export function LayerPanel(
|
|||
)}
|
||||
</EuiFlexGroup>
|
||||
{props.indexPatternService &&
|
||||
(layerDatasource || activeVisualization.renderLayerPanel) && <EuiSpacer size="s" />}
|
||||
(layerDatasource || activeVisualization.LayerPanelComponent) && (
|
||||
<EuiSpacer size="s" />
|
||||
)}
|
||||
{layerDatasource && props.indexPatternService && (
|
||||
<NativeRenderer
|
||||
render={layerDatasource.renderLayerPanel}
|
||||
nativeProps={{
|
||||
<layerDatasource.LayerPanelComponent
|
||||
{...{
|
||||
layerId,
|
||||
state: layerDatasourceState,
|
||||
activeData: props.framePublicAPI.activeData,
|
||||
|
@ -452,10 +452,9 @@ export function LayerPanel(
|
|||
}}
|
||||
/>
|
||||
)}
|
||||
{activeVisualization.renderLayerPanel && (
|
||||
<NativeRenderer
|
||||
render={activeVisualization.renderLayerPanel}
|
||||
nativeProps={{
|
||||
{activeVisualization.LayerPanelComponent && (
|
||||
<activeVisualization.LayerPanelComponent
|
||||
{...{
|
||||
layerId,
|
||||
state: visualizationState,
|
||||
frame: framePublicAPI,
|
||||
|
@ -612,19 +611,18 @@ export function LayerPanel(
|
|||
}}
|
||||
>
|
||||
{layerDatasource ? (
|
||||
<NativeRenderer
|
||||
render={layerDatasource.renderDimensionTrigger}
|
||||
nativeProps={{
|
||||
<>
|
||||
{layerDatasource.DimensionTriggerComponent({
|
||||
...layerDatasourceConfigProps,
|
||||
columnId: accessorConfig.columnId,
|
||||
groupId: group.groupId,
|
||||
filterOperations: group.filterOperations,
|
||||
indexPatterns: dataViews.indexPatterns,
|
||||
}}
|
||||
/>
|
||||
})}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
{activeVisualization?.renderDimensionTrigger?.({
|
||||
{activeVisualization?.DimensionTriggerComponent?.({
|
||||
columnId,
|
||||
label: columnLabelMap?.[columnId] ?? '',
|
||||
hideTooltip,
|
||||
|
@ -705,7 +703,7 @@ export function LayerPanel(
|
|||
})}
|
||||
</EuiPanel>
|
||||
</section>
|
||||
{(layerDatasource?.renderLayerSettings || activeVisualization?.renderLayerSettings) && (
|
||||
{(layerDatasource?.LayerSettingsComponent || activeVisualization?.LayerSettingsComponent) && (
|
||||
<FlyoutContainer
|
||||
panelRef={(el) => (settingsPanelRef.current = el)}
|
||||
isOpen={isPanelSettingsOpen}
|
||||
|
@ -721,7 +719,7 @@ export function LayerPanel(
|
|||
>
|
||||
<div id={layerId}>
|
||||
<div className="lnsIndexPatternDimensionEditor--padded">
|
||||
{layerDatasource?.renderLayerSettings || visualizationLayerSettings.data ? (
|
||||
{layerDatasource?.LayerSettingsComponent || visualizationLayerSettings.data ? (
|
||||
<EuiText
|
||||
size="s"
|
||||
css={css`
|
||||
|
@ -735,21 +733,17 @@ export function LayerPanel(
|
|||
</h4>
|
||||
</EuiText>
|
||||
) : null}
|
||||
{layerDatasource?.renderLayerSettings && (
|
||||
{layerDatasource?.LayerSettingsComponent && (
|
||||
<>
|
||||
<NativeRenderer
|
||||
render={layerDatasource.renderLayerSettings}
|
||||
nativeProps={layerDatasourceConfigProps}
|
||||
/>
|
||||
<layerDatasource.LayerSettingsComponent {...layerDatasourceConfigProps} />
|
||||
</>
|
||||
)}
|
||||
{layerDatasource?.renderLayerSettings && visualizationLayerSettings.data ? (
|
||||
{layerDatasource?.LayerSettingsComponent && visualizationLayerSettings.data ? (
|
||||
<EuiSpacer size="m" />
|
||||
) : null}
|
||||
{activeVisualization?.renderLayerSettings && visualizationLayerSettings.data ? (
|
||||
<NativeRenderer
|
||||
render={activeVisualization?.renderLayerSettings}
|
||||
nativeProps={{
|
||||
{activeVisualization?.LayerSettingsComponent && visualizationLayerSettings.data ? (
|
||||
<activeVisualization.LayerSettingsComponent
|
||||
{...{
|
||||
...layerVisualizationConfigProps,
|
||||
setState: props.updateVisualization,
|
||||
panelRef: settingsPanelRef,
|
||||
|
@ -771,10 +765,9 @@ export function LayerPanel(
|
|||
</h4>
|
||||
</EuiText>
|
||||
) : null}
|
||||
{activeVisualization?.renderLayerSettings && (
|
||||
<NativeRenderer
|
||||
render={activeVisualization?.renderLayerSettings}
|
||||
nativeProps={{
|
||||
{activeVisualization?.LayerSettingsComponent && (
|
||||
<activeVisualization.LayerSettingsComponent
|
||||
{...{
|
||||
...layerVisualizationConfigProps,
|
||||
setState: props.updateVisualization,
|
||||
panelRef: settingsPanelRef,
|
||||
|
@ -819,58 +812,54 @@ export function LayerPanel(
|
|||
}}
|
||||
panel={
|
||||
<>
|
||||
{activeGroup && activeId && layerDatasource && (
|
||||
<NativeRenderer
|
||||
render={layerDatasource.renderDimensionEditor}
|
||||
nativeProps={{
|
||||
...layerDatasourceConfigProps,
|
||||
core: props.core,
|
||||
columnId: activeId,
|
||||
groupId: activeGroup.groupId,
|
||||
hideGrouping: activeGroup.hideGrouping,
|
||||
filterOperations: activeGroup.filterOperations,
|
||||
isMetricDimension: activeGroup?.isMetricDimension,
|
||||
dimensionGroups,
|
||||
toggleFullscreen,
|
||||
isFullscreen,
|
||||
setState: updateDataLayerState,
|
||||
supportStaticValue: Boolean(activeGroup.supportStaticValue),
|
||||
paramEditorCustomProps: activeGroup.paramEditorCustomProps,
|
||||
enableFormatSelector: activeGroup.enableFormatSelector !== false,
|
||||
layerType: activeVisualization.getLayerType(layerId, visualizationState),
|
||||
indexPatterns: dataViews.indexPatterns,
|
||||
activeData: layerVisualizationConfigProps.activeData,
|
||||
dataSectionExtra: !isFullscreen &&
|
||||
!activeDimension.isNew &&
|
||||
activeVisualization.renderDimensionEditorDataExtra && (
|
||||
<NativeRenderer
|
||||
render={activeVisualization.renderDimensionEditorDataExtra}
|
||||
nativeProps={{
|
||||
...layerVisualizationConfigProps,
|
||||
groupId: activeGroup.groupId,
|
||||
accessor: activeId,
|
||||
datasource,
|
||||
setState: props.updateVisualization,
|
||||
addLayer: props.addLayer,
|
||||
removeLayer: props.onRemoveLayer,
|
||||
panelRef,
|
||||
}}
|
||||
/>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{activeGroup &&
|
||||
activeId &&
|
||||
layerDatasource &&
|
||||
layerDatasource.DimensionEditorComponent({
|
||||
...layerDatasourceConfigProps,
|
||||
core: props.core,
|
||||
columnId: activeId,
|
||||
groupId: activeGroup.groupId,
|
||||
hideGrouping: activeGroup.hideGrouping,
|
||||
filterOperations: activeGroup.filterOperations,
|
||||
isMetricDimension: activeGroup?.isMetricDimension,
|
||||
dimensionGroups,
|
||||
toggleFullscreen,
|
||||
isFullscreen,
|
||||
setState: updateDataLayerState,
|
||||
supportStaticValue: Boolean(activeGroup.supportStaticValue),
|
||||
paramEditorCustomProps: activeGroup.paramEditorCustomProps,
|
||||
enableFormatSelector: activeGroup.enableFormatSelector !== false,
|
||||
layerType: activeVisualization.getLayerType(layerId, visualizationState),
|
||||
indexPatterns: dataViews.indexPatterns,
|
||||
activeData: layerVisualizationConfigProps.activeData,
|
||||
dataSectionExtra: !isFullscreen &&
|
||||
!activeDimension.isNew &&
|
||||
activeVisualization.DimensionEditorDataExtraComponent && (
|
||||
<activeVisualization.DimensionEditorDataExtraComponent
|
||||
{...{
|
||||
...layerVisualizationConfigProps,
|
||||
groupId: activeGroup.groupId,
|
||||
accessor: activeId,
|
||||
datasource,
|
||||
setState: props.updateVisualization,
|
||||
addLayer: props.addLayer,
|
||||
removeLayer: props.onRemoveLayer,
|
||||
panelRef,
|
||||
}}
|
||||
/>
|
||||
),
|
||||
})}
|
||||
{activeGroup &&
|
||||
activeId &&
|
||||
!isFullscreen &&
|
||||
!activeDimension.isNew &&
|
||||
activeVisualization.renderDimensionEditor &&
|
||||
activeVisualization.DimensionEditorComponent &&
|
||||
activeGroup?.enableDimensionEditor && (
|
||||
<>
|
||||
<div className="lnsLayerPanel__styleEditor">
|
||||
<NativeRenderer
|
||||
render={activeVisualization.renderDimensionEditor}
|
||||
nativeProps={{
|
||||
<activeVisualization.DimensionEditorComponent
|
||||
{...{
|
||||
...layerVisualizationConfigProps,
|
||||
groupId: activeGroup.groupId,
|
||||
accessor: activeId,
|
||||
|
@ -882,10 +871,9 @@ export function LayerPanel(
|
|||
}}
|
||||
/>
|
||||
</div>
|
||||
{activeVisualization.renderDimensionEditorAdditionalSection && (
|
||||
<NativeRenderer
|
||||
render={activeVisualization.renderDimensionEditorAdditionalSection}
|
||||
nativeProps={{
|
||||
{activeVisualization.DimensionEditorAdditionalSectionComponent && (
|
||||
<activeVisualization.DimensionEditorAdditionalSectionComponent
|
||||
{...{
|
||||
...layerVisualizationConfigProps,
|
||||
groupId: activeGroup.groupId,
|
||||
accessor: activeId,
|
||||
|
|
|
@ -65,8 +65,8 @@ describe('LayerSettings', () => {
|
|||
});
|
||||
|
||||
it('should call the custom renderer if available', async () => {
|
||||
mockVisualization.renderLayerHeader = jest.fn();
|
||||
mockVisualization.LayerHeaderComponent = jest.fn().mockImplementation(() => <div />);
|
||||
await mountWithProvider(<LayerSettings {...getDefaultProps()} />);
|
||||
expect(mockVisualization.renderLayerHeader).toHaveBeenCalled();
|
||||
expect(mockVisualization.LayerHeaderComponent).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { NativeRenderer } from '../../../native_renderer';
|
||||
import { Visualization, VisualizationLayerWidgetProps } from '../../../types';
|
||||
import { StaticHeader } from '../../../shared_components';
|
||||
|
||||
|
@ -17,7 +16,7 @@ export function LayerSettings({
|
|||
activeVisualization: Visualization;
|
||||
layerConfigProps: VisualizationLayerWidgetProps;
|
||||
}) {
|
||||
if (!activeVisualization.renderLayerHeader) {
|
||||
if (!activeVisualization.LayerHeaderComponent) {
|
||||
const description = activeVisualization.getDescription(layerConfigProps.state);
|
||||
if (!description) {
|
||||
return null;
|
||||
|
@ -25,7 +24,5 @@ export function LayerSettings({
|
|||
return <StaticHeader label={description.label} icon={description.icon} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<NativeRenderer render={activeVisualization.renderLayerHeader} nativeProps={layerConfigProps} />
|
||||
);
|
||||
return <activeVisualization.LayerHeaderComponent {...layerConfigProps} />;
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { DragDropIdentifier } from '@kbn/dom-drag-drop';
|
||||
import { DataPanelWrapper } from './data_panel_wrapper';
|
||||
import { Datasource, DatasourceDataPanelProps, VisualizationMap } from '../../types';
|
||||
import { UiActionsStart } from '@kbn/ui-actions-plugin/public';
|
||||
|
@ -22,11 +21,11 @@ describe('Data Panel Wrapper', () => {
|
|||
let datasourceDataPanelProps: DatasourceDataPanelProps;
|
||||
let lensStore: Awaited<ReturnType<typeof mountWithProvider>>['lensStore'];
|
||||
beforeEach(async () => {
|
||||
const renderDataPanel = jest.fn();
|
||||
const DataPanelComponent = jest.fn().mockImplementation(() => <div />);
|
||||
|
||||
const datasourceMap = {
|
||||
activeDatasource: {
|
||||
renderDataPanel,
|
||||
DataPanelComponent,
|
||||
getUsedDataViews: jest.fn(),
|
||||
getLayers: jest.fn(() => []),
|
||||
} as unknown as Datasource,
|
||||
|
@ -38,8 +37,8 @@ describe('Data Panel Wrapper', () => {
|
|||
visualizationMap={{} as VisualizationMap}
|
||||
showNoDataPopover={() => {}}
|
||||
core={{} as DatasourceDataPanelProps['core']}
|
||||
dropOntoWorkspace={(field: DragDropIdentifier) => {}}
|
||||
hasSuggestionForField={(field: DragDropIdentifier) => true}
|
||||
dropOntoWorkspace={() => {}}
|
||||
hasSuggestionForField={() => true}
|
||||
plugins={{
|
||||
uiActions: {} as UiActionsStart,
|
||||
dataViews: {} as DataViewsPublicPluginStart,
|
||||
|
@ -66,7 +65,7 @@ describe('Data Panel Wrapper', () => {
|
|||
|
||||
lensStore = mountResult.lensStore;
|
||||
|
||||
datasourceDataPanelProps = renderDataPanel.mock.calls[0][1] as DatasourceDataPanelProps;
|
||||
datasourceDataPanelProps = DataPanelComponent.mock.calls[0][0] as DatasourceDataPanelProps;
|
||||
});
|
||||
|
||||
describe('setState', () => {
|
||||
|
|
|
@ -12,11 +12,10 @@ import { Storage } from '@kbn/kibana-utils-plugin/public';
|
|||
import { UiActionsStart } from '@kbn/ui-actions-plugin/public';
|
||||
import { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public';
|
||||
import { EventAnnotationServiceType } from '@kbn/event-annotation-plugin/public';
|
||||
import { useDragDropContext, DragDropIdentifier } from '@kbn/dom-drag-drop';
|
||||
import { DragDropIdentifier } from '@kbn/dom-drag-drop';
|
||||
import memoizeOne from 'memoize-one';
|
||||
import { isEqual } from 'lodash';
|
||||
import { Easteregg } from './easteregg';
|
||||
import { NativeRenderer } from '../../native_renderer';
|
||||
import {
|
||||
StateSetter,
|
||||
DatasourceDataPanelProps,
|
||||
|
@ -162,7 +161,6 @@ export const DataPanelWrapper = memo((props: DataPanelWrapperProps) => {
|
|||
|
||||
const datasourceProps: DatasourceDataPanelProps = {
|
||||
...externalContext,
|
||||
dragDropContext: useDragDropContext(),
|
||||
state: activeDatasourceId ? datasourceStates[activeDatasourceId].state : null,
|
||||
setState: setDatasourceState,
|
||||
core: props.core,
|
||||
|
@ -187,16 +185,16 @@ export const DataPanelWrapper = memo((props: DataPanelWrapperProps) => {
|
|||
[]),
|
||||
]),
|
||||
};
|
||||
const DataPanelComponent =
|
||||
activeDatasourceId && !datasourceIsLoading
|
||||
? props.datasourceMap[activeDatasourceId].DataPanelComponent
|
||||
: null;
|
||||
|
||||
return (
|
||||
<>
|
||||
<Easteregg query={externalContext?.query} />
|
||||
{activeDatasourceId && !datasourceIsLoading && (
|
||||
<NativeRenderer
|
||||
className="lnsDataPanelWrapper"
|
||||
render={props.datasourceMap[activeDatasourceId].renderDataPanel}
|
||||
nativeProps={datasourceProps}
|
||||
/>
|
||||
{DataPanelComponent && (
|
||||
<div className="lnsDataPanelWrapper">{DataPanelComponent(datasourceProps)}</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import React, { useEffect } from 'react';
|
||||
import { ReactWrapper } from 'enzyme';
|
||||
|
||||
// Tests are executed in a jsdom environment who does not have sizing methods,
|
||||
|
@ -41,7 +41,7 @@ import {
|
|||
} from '../../mocks';
|
||||
import { inspectorPluginMock } from '@kbn/inspector-plugin/public/mocks';
|
||||
import { ReactExpressionRendererType } from '@kbn/expressions-plugin/public';
|
||||
import { DragDrop } from '@kbn/dom-drag-drop';
|
||||
import { DragDrop, useDragDropContext } from '@kbn/dom-drag-drop';
|
||||
import { uiActionsPluginMock } from '@kbn/ui-actions-plugin/public/mocks';
|
||||
import { chartPluginMock } from '@kbn/charts-plugin/public/mocks';
|
||||
import { expressionsPluginMock } from '@kbn/expressions-plugin/public/mocks';
|
||||
|
@ -177,7 +177,7 @@ describe('editor_frame', () => {
|
|||
},
|
||||
},
|
||||
});
|
||||
expect(mockDatasource.renderDataPanel).not.toHaveBeenCalled();
|
||||
expect(mockDatasource.DataPanelComponent).not.toHaveBeenCalled();
|
||||
lensStore.dispatch(
|
||||
setState({
|
||||
datasourceStates: {
|
||||
|
@ -190,7 +190,7 @@ describe('editor_frame', () => {
|
|||
},
|
||||
})
|
||||
);
|
||||
expect(mockDatasource.renderDataPanel).toHaveBeenCalled();
|
||||
expect(mockDatasource.DataPanelComponent).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should initialize visualization state and render config panel', async () => {
|
||||
|
@ -304,7 +304,7 @@ describe('editor_frame', () => {
|
|||
},
|
||||
});
|
||||
const updatedState = {};
|
||||
const setDatasourceState = (mockDatasource.renderDataPanel as jest.Mock).mock.calls[0][1]
|
||||
const setDatasourceState = (mockDatasource.DataPanelComponent as jest.Mock).mock.calls[0][0]
|
||||
.setState;
|
||||
act(() => {
|
||||
setDatasourceState(updatedState);
|
||||
|
@ -334,10 +334,10 @@ describe('editor_frame', () => {
|
|||
};
|
||||
await mountWithProvider(<EditorFrame {...props} />);
|
||||
|
||||
const setDatasourceState = (mockDatasource.renderDataPanel as jest.Mock).mock.calls[0][1]
|
||||
const setDatasourceState = (mockDatasource.DataPanelComponent as jest.Mock).mock.calls[0][0]
|
||||
.setState;
|
||||
|
||||
mockDatasource.renderDataPanel.mockClear();
|
||||
mockDatasource.DataPanelComponent.mockClear();
|
||||
|
||||
const updatedState = {
|
||||
title: 'shazm',
|
||||
|
@ -346,9 +346,8 @@ describe('editor_frame', () => {
|
|||
setDatasourceState(updatedState);
|
||||
});
|
||||
|
||||
expect(mockDatasource.renderDataPanel).toHaveBeenCalledTimes(1);
|
||||
expect(mockDatasource.renderDataPanel).toHaveBeenLastCalledWith(
|
||||
expect.any(Element),
|
||||
expect(mockDatasource.DataPanelComponent).toHaveBeenCalledTimes(1);
|
||||
expect(mockDatasource.DataPanelComponent).toHaveBeenLastCalledWith(
|
||||
expect.objectContaining({
|
||||
state: updatedState,
|
||||
})
|
||||
|
@ -385,7 +384,7 @@ describe('editor_frame', () => {
|
|||
};
|
||||
mockDatasource.getPublicAPI.mockReturnValue(updatedPublicAPI);
|
||||
|
||||
const setDatasourceState = (mockDatasource.renderDataPanel as jest.Mock).mock.calls[0][1]
|
||||
const setDatasourceState = (mockDatasource.DataPanelComponent as jest.Mock).mock.calls[0][0]
|
||||
.setState;
|
||||
act(() => {
|
||||
setDatasourceState({});
|
||||
|
@ -722,8 +721,7 @@ describe('editor_frame', () => {
|
|||
state: suggestionVisState,
|
||||
})
|
||||
);
|
||||
expect(mockDatasource.renderDataPanel).toHaveBeenLastCalledWith(
|
||||
expect.any(Element),
|
||||
expect(mockDatasource.DataPanelComponent).toHaveBeenLastCalledWith(
|
||||
expect.objectContaining({
|
||||
state: newDatasourceState,
|
||||
})
|
||||
|
@ -820,19 +818,7 @@ describe('editor_frame', () => {
|
|||
getDatasourceSuggestionsForField: () => [generateSuggestion()],
|
||||
getDatasourceSuggestionsFromCurrentState: () => [generateSuggestion()],
|
||||
getDatasourceSuggestionsForVisualizeField: () => [generateSuggestion()],
|
||||
renderDataPanel: (_element, { dragDropContext: [{ dragging }, dndDispatch] }) => {
|
||||
if (!dragging || dragging.id !== 'draggedField') {
|
||||
dndDispatch({
|
||||
type: 'startDragging',
|
||||
payload: {
|
||||
dragging: {
|
||||
id: 'draggedField',
|
||||
humanData: { label: 'draggedField' },
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
},
|
||||
DataPanelComponent: jest.fn().mockImplementation(() => <div />),
|
||||
},
|
||||
},
|
||||
|
||||
|
@ -863,7 +849,7 @@ describe('editor_frame', () => {
|
|||
id: '1',
|
||||
humanData: { label: 'draggedField' },
|
||||
},
|
||||
'field_replace'
|
||||
'field_add'
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -927,8 +913,9 @@ describe('editor_frame', () => {
|
|||
getDatasourceSuggestionsForField: () => [generateSuggestion()],
|
||||
getDatasourceSuggestionsFromCurrentState: () => [generateSuggestion()],
|
||||
getDatasourceSuggestionsForVisualizeField: () => [generateSuggestion()],
|
||||
renderDataPanel: (_element, { dragDropContext: [{ dragging }, dndDispatch] }) => {
|
||||
if (!dragging || dragging.id !== 'draggedField') {
|
||||
DataPanelComponent: jest.fn().mockImplementation(() => {
|
||||
const [, dndDispatch] = useDragDropContext();
|
||||
useEffect(() => {
|
||||
dndDispatch({
|
||||
type: 'startDragging',
|
||||
payload: {
|
||||
|
@ -938,15 +925,16 @@ describe('editor_frame', () => {
|
|||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
},
|
||||
}, [dndDispatch]);
|
||||
return <div />;
|
||||
}),
|
||||
},
|
||||
},
|
||||
|
||||
ExpressionRenderer: expressionRendererMock,
|
||||
} as EditorFrameProps;
|
||||
|
||||
instance = (await mountWithProvider(<EditorFrame {...props} />)).instance;
|
||||
|
||||
instance.update();
|
||||
|
||||
act(() => {
|
||||
|
@ -959,7 +947,7 @@ describe('editor_frame', () => {
|
|||
label: 'label',
|
||||
},
|
||||
},
|
||||
'field_replace'
|
||||
'field_add'
|
||||
);
|
||||
});
|
||||
|
||||
|
|
|
@ -51,7 +51,7 @@ describe('workspace_panel_wrapper', () => {
|
|||
});
|
||||
|
||||
it('should call the toolbar renderer if provided', async () => {
|
||||
const renderToolbarMock = jest.fn();
|
||||
const ToolbarComponentMock = jest.fn(() => null);
|
||||
const visState = { internalState: 123 };
|
||||
await mountWithProvider(
|
||||
<WorkspacePanelWrapper
|
||||
|
@ -59,7 +59,9 @@ describe('workspace_panel_wrapper', () => {
|
|||
visualizationState={visState}
|
||||
children={<span />}
|
||||
visualizationId="myVis"
|
||||
visualizationMap={{ myVis: { ...mockVisualization, renderToolbar: renderToolbarMock } }}
|
||||
visualizationMap={{
|
||||
myVis: { ...mockVisualization, ToolbarComponent: ToolbarComponentMock },
|
||||
}}
|
||||
datasourceMap={{}}
|
||||
datasourceStates={{}}
|
||||
isFullscreen={false}
|
||||
|
@ -74,7 +76,7 @@ describe('workspace_panel_wrapper', () => {
|
|||
}
|
||||
);
|
||||
|
||||
expect(renderToolbarMock).toHaveBeenCalledWith(expect.any(Element), {
|
||||
expect(ToolbarComponentMock).toHaveBeenCalledWith({
|
||||
state: visState,
|
||||
frame: mockFrameAPI,
|
||||
setState: expect.anything(),
|
||||
|
|
|
@ -19,7 +19,6 @@ import {
|
|||
Visualization,
|
||||
} from '../../../types';
|
||||
import { DONT_CLOSE_DIMENSION_CONTAINER_ON_CLICK_CLASS } from '../../../utils';
|
||||
import { NativeRenderer } from '../../../native_renderer';
|
||||
import { ChartSwitch } from './chart_switch';
|
||||
import { MessageList } from './message_list';
|
||||
import {
|
||||
|
@ -59,37 +58,37 @@ export function VisualizationToolbar(props: {
|
|||
const { activeDatasourceId, visualization, datasourceStates } = useLensSelector(
|
||||
(state) => state.lens
|
||||
);
|
||||
const { activeVisualization, onUpdateStateCb } = props;
|
||||
const setVisualizationState = useCallback(
|
||||
(newState: unknown) => {
|
||||
if (!props.activeVisualization) {
|
||||
if (!activeVisualization) {
|
||||
return;
|
||||
}
|
||||
dispatchLens(
|
||||
updateVisualizationState({
|
||||
visualizationId: props.activeVisualization.id,
|
||||
visualizationId: activeVisualization.id,
|
||||
newState,
|
||||
})
|
||||
);
|
||||
if (activeDatasourceId && props.onUpdateStateCb) {
|
||||
if (activeDatasourceId && onUpdateStateCb) {
|
||||
const dsState = datasourceStates[activeDatasourceId].state;
|
||||
props.onUpdateStateCb?.(dsState, newState);
|
||||
onUpdateStateCb?.(dsState, newState);
|
||||
}
|
||||
},
|
||||
[activeDatasourceId, datasourceStates, dispatchLens, props]
|
||||
[activeDatasourceId, datasourceStates, dispatchLens, activeVisualization, onUpdateStateCb]
|
||||
);
|
||||
|
||||
const ToolbarComponent = props.activeVisualization?.ToolbarComponent;
|
||||
|
||||
return (
|
||||
<>
|
||||
{props.activeVisualization && props.activeVisualization.renderToolbar && (
|
||||
{ToolbarComponent && (
|
||||
<EuiFlexItem grow={false}>
|
||||
<NativeRenderer
|
||||
render={props.activeVisualization.renderToolbar}
|
||||
nativeProps={{
|
||||
frame: props.framePublicAPI,
|
||||
state: visualization.state,
|
||||
setState: setVisualizationState,
|
||||
}}
|
||||
/>
|
||||
{ToolbarComponent({
|
||||
frame: props.framePublicAPI,
|
||||
state: visualization.state,
|
||||
setState: setVisualizationState,
|
||||
})}
|
||||
</EuiFlexItem>
|
||||
)}
|
||||
</>
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { DatasourcePublicAPI, Datasource } from '../types';
|
||||
|
||||
export type DatasourceMock = jest.Mocked<Datasource> & {
|
||||
|
@ -44,8 +44,6 @@ export function createMockDatasource(
|
|||
getRenderEventCounters: jest.fn((_state) => []),
|
||||
getPublicAPI: jest.fn().mockReturnValue(publicAPIMock),
|
||||
initialize: jest.fn((_state?) => {}),
|
||||
renderDataPanel: jest.fn(),
|
||||
renderLayerPanel: jest.fn(),
|
||||
toExpression: jest.fn((_frame, _state, _indexPatterns, dateRange, nowInstant) => null),
|
||||
insertLayer: jest.fn((_state, _newLayerId) => ({})),
|
||||
removeLayer: jest.fn((state, layerId) => ({ newState: state, removedLayerIds: [layerId] })),
|
||||
|
@ -53,8 +51,6 @@ export function createMockDatasource(
|
|||
removeColumn: jest.fn((props) => {}),
|
||||
getLayers: jest.fn((_state) => []),
|
||||
uniqueLabels: jest.fn((_state, dataViews) => ({})),
|
||||
renderDimensionTrigger: jest.fn(),
|
||||
renderDimensionEditor: jest.fn(),
|
||||
getDropProps: jest.fn(),
|
||||
onDrop: jest.fn(),
|
||||
createEmptyLayer: jest.fn(),
|
||||
|
@ -71,6 +67,11 @@ export function createMockDatasource(
|
|||
getUsedDataViews: jest.fn(),
|
||||
onRefreshIndexPattern: jest.fn(),
|
||||
getDatasourceInfo: jest.fn(),
|
||||
|
||||
DataPanelComponent: jest.fn().mockImplementation(() => <div />),
|
||||
LayerPanelComponent: jest.fn().mockImplementation(() => <div />),
|
||||
DimensionTriggerComponent: jest.fn().mockImplementation(() => <div />),
|
||||
DimensionEditorComponent: jest.fn().mockImplementation(() => <div />),
|
||||
};
|
||||
}
|
||||
|
|
@ -100,6 +100,7 @@ export const mountWithProvider = async (
|
|||
wrappingComponent?: React.FC<{
|
||||
children: React.ReactNode;
|
||||
}>;
|
||||
wrappingComponentProps?: Record<string, unknown>;
|
||||
attachTo?: HTMLElement;
|
||||
}
|
||||
) => {
|
||||
|
@ -120,6 +121,7 @@ export const getMountWithProviderParams = (
|
|||
wrappingComponent?: React.FC<{
|
||||
children: React.ReactNode;
|
||||
}>;
|
||||
wrappingComponentProps?: Record<string, unknown>;
|
||||
attachTo?: HTMLElement;
|
||||
}
|
||||
) => {
|
||||
|
@ -133,12 +135,13 @@ export const getMountWithProviderParams = (
|
|||
attachTo?: HTMLElement | undefined;
|
||||
} = {};
|
||||
if (options) {
|
||||
const { wrappingComponent: _wrappingComponent, ...rest } = options;
|
||||
const { wrappingComponent: _wrappingComponent, wrappingComponentProps, ...rest } = options;
|
||||
restOptions = rest;
|
||||
|
||||
if (_wrappingComponent) {
|
||||
wrappingComponent = ({ children }) => {
|
||||
return _wrappingComponent({
|
||||
...wrappingComponentProps,
|
||||
children: <Provider store={lensStore}>{children}</Provider>,
|
||||
});
|
||||
};
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { LayerTypes } from '@kbn/expression-xy-plugin/public';
|
||||
import { Visualization, VisualizationMap } from '../types';
|
||||
|
||||
|
@ -49,7 +49,7 @@ export function createMockVisualization(id = 'testVis'): jest.Mocked<Visualizati
|
|||
|
||||
setDimension: jest.fn(),
|
||||
removeDimension: jest.fn(),
|
||||
renderDimensionEditor: jest.fn(),
|
||||
DimensionEditorComponent: jest.fn(() => <div />),
|
||||
};
|
||||
}
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
export * from './native_renderer';
|
|
@ -1,252 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import React, { useEffect } from 'react';
|
||||
import { render } from 'react-dom';
|
||||
import { NativeRenderer } from './native_renderer';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
|
||||
function renderAndTriggerHooks(element: JSX.Element, mountpoint: Element) {
|
||||
// act takes care of triggering state hooks
|
||||
act(() => {
|
||||
render(element, mountpoint);
|
||||
});
|
||||
}
|
||||
|
||||
describe('native_renderer', () => {
|
||||
let mountpoint: Element;
|
||||
|
||||
beforeEach(() => {
|
||||
mountpoint = document.createElement('div');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
mountpoint.remove();
|
||||
});
|
||||
|
||||
it('should render element in container', () => {
|
||||
const renderSpy = jest.fn();
|
||||
const testProps = { a: 'abc' };
|
||||
|
||||
renderAndTriggerHooks(
|
||||
<NativeRenderer render={renderSpy} nativeProps={testProps} />,
|
||||
mountpoint
|
||||
);
|
||||
const containerElement = mountpoint.firstElementChild;
|
||||
expect(renderSpy).toHaveBeenCalledWith(containerElement, testProps);
|
||||
});
|
||||
|
||||
it('should render again if props change', () => {
|
||||
const renderSpy = jest.fn();
|
||||
const testProps = { a: 'abc' };
|
||||
|
||||
renderAndTriggerHooks(
|
||||
<NativeRenderer render={renderSpy} nativeProps={testProps} />,
|
||||
mountpoint
|
||||
);
|
||||
renderAndTriggerHooks(
|
||||
<NativeRenderer render={renderSpy} nativeProps={{ a: 'def' }} />,
|
||||
mountpoint
|
||||
);
|
||||
renderAndTriggerHooks(
|
||||
<NativeRenderer render={renderSpy} nativeProps={{ a: 'def' }} />,
|
||||
mountpoint
|
||||
);
|
||||
expect(renderSpy).toHaveBeenCalledTimes(3);
|
||||
const containerElement = mountpoint.firstElementChild;
|
||||
expect(renderSpy).lastCalledWith(containerElement, { a: 'def' });
|
||||
});
|
||||
|
||||
it('should render again if props is just a string', () => {
|
||||
const renderSpy = jest.fn();
|
||||
const testProps = 'abc';
|
||||
|
||||
renderAndTriggerHooks(
|
||||
<NativeRenderer render={renderSpy} nativeProps={testProps} />,
|
||||
mountpoint
|
||||
);
|
||||
renderAndTriggerHooks(<NativeRenderer render={renderSpy} nativeProps="def" />, mountpoint);
|
||||
renderAndTriggerHooks(<NativeRenderer render={renderSpy} nativeProps="def" />, mountpoint);
|
||||
expect(renderSpy).toHaveBeenCalledTimes(3);
|
||||
const containerElement = mountpoint.firstElementChild;
|
||||
expect(renderSpy).lastCalledWith(containerElement, 'def');
|
||||
});
|
||||
|
||||
it('should render again if props are extended', () => {
|
||||
const renderSpy = jest.fn();
|
||||
const testProps = { a: 'abc' };
|
||||
|
||||
renderAndTriggerHooks(
|
||||
<NativeRenderer render={renderSpy} nativeProps={testProps} />,
|
||||
mountpoint
|
||||
);
|
||||
renderAndTriggerHooks(
|
||||
<NativeRenderer render={renderSpy} nativeProps={{ a: 'abc', b: 'def' }} />,
|
||||
mountpoint
|
||||
);
|
||||
expect(renderSpy).toHaveBeenCalledTimes(2);
|
||||
const containerElement = mountpoint.firstElementChild;
|
||||
expect(renderSpy).lastCalledWith(containerElement, { a: 'abc', b: 'def' });
|
||||
});
|
||||
|
||||
it('should render again if props are limited', () => {
|
||||
const renderSpy = jest.fn();
|
||||
const testProps = { a: 'abc', b: 'def' };
|
||||
|
||||
renderAndTriggerHooks(
|
||||
<NativeRenderer render={renderSpy} nativeProps={testProps} />,
|
||||
mountpoint
|
||||
);
|
||||
renderAndTriggerHooks(
|
||||
<NativeRenderer render={renderSpy} nativeProps={{ a: 'abc' }} />,
|
||||
mountpoint
|
||||
);
|
||||
expect(renderSpy).toHaveBeenCalledTimes(2);
|
||||
const containerElement = mountpoint.firstElementChild;
|
||||
expect(renderSpy).lastCalledWith(containerElement, { a: 'abc' });
|
||||
});
|
||||
|
||||
it('should render a div as container', () => {
|
||||
const renderSpy = jest.fn();
|
||||
const testProps = { a: 'abc' };
|
||||
|
||||
renderAndTriggerHooks(
|
||||
<NativeRenderer render={renderSpy} nativeProps={testProps} />,
|
||||
mountpoint
|
||||
);
|
||||
const containerElement: Element = mountpoint.firstElementChild!;
|
||||
expect(containerElement.nodeName).toBe('DIV');
|
||||
});
|
||||
|
||||
it('should pass regular html attributes to the wrapping element', () => {
|
||||
const renderSpy = jest.fn();
|
||||
const testProps = { a: 'abc' };
|
||||
|
||||
renderAndTriggerHooks(
|
||||
<NativeRenderer
|
||||
render={renderSpy}
|
||||
nativeProps={testProps}
|
||||
className="testClass"
|
||||
data-test-subj="container"
|
||||
/>,
|
||||
mountpoint
|
||||
);
|
||||
const containerElement: HTMLElement = mountpoint.firstElementChild! as HTMLElement;
|
||||
expect(containerElement.className).toBe('testClass');
|
||||
expect(containerElement.dataset.testSubj).toBe('container');
|
||||
});
|
||||
|
||||
it('should render a specified element as container', () => {
|
||||
const renderSpy = jest.fn();
|
||||
const testProps = { a: 'abc' };
|
||||
|
||||
renderAndTriggerHooks(
|
||||
<NativeRenderer render={renderSpy} tag="span" nativeProps={testProps} />,
|
||||
mountpoint
|
||||
);
|
||||
const containerElement: Element = mountpoint.firstElementChild!;
|
||||
expect(containerElement.nodeName).toBe('SPAN');
|
||||
});
|
||||
|
||||
it('should properly unmount a react element that is mounted inside the renderer', () => {
|
||||
let isUnmounted = false;
|
||||
|
||||
function TestComponent() {
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
isUnmounted = true;
|
||||
};
|
||||
}, []);
|
||||
return <>Hello</>;
|
||||
}
|
||||
|
||||
renderAndTriggerHooks(
|
||||
<NativeRenderer
|
||||
render={(element) => {
|
||||
// This render function mimics the most common usage inside Lens
|
||||
render(<TestComponent />, element);
|
||||
}}
|
||||
nativeProps={{}}
|
||||
/>,
|
||||
mountpoint
|
||||
);
|
||||
|
||||
// Replaces the component at the mountpoint with nothing
|
||||
renderAndTriggerHooks(<>Empty</>, mountpoint);
|
||||
|
||||
expect(isUnmounted).toBe(true);
|
||||
});
|
||||
|
||||
it('should call the unmount function provided for non-react elements', () => {
|
||||
const unmountCallback = jest.fn();
|
||||
|
||||
renderAndTriggerHooks(
|
||||
<NativeRenderer
|
||||
render={(element, props) => {
|
||||
return unmountCallback;
|
||||
}}
|
||||
nativeProps={{}}
|
||||
/>,
|
||||
mountpoint
|
||||
);
|
||||
|
||||
// Replaces the component at the mountpoint with nothing
|
||||
renderAndTriggerHooks(<>Empty</>, mountpoint);
|
||||
|
||||
expect(unmountCallback).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should handle when the mount function is asynchronous without a cleanup fn', () => {
|
||||
let isUnmounted = false;
|
||||
|
||||
function TestComponent() {
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
isUnmounted = true;
|
||||
};
|
||||
}, []);
|
||||
return <>Hello</>;
|
||||
}
|
||||
|
||||
renderAndTriggerHooks(
|
||||
<NativeRenderer
|
||||
render={async (element, props) => {
|
||||
render(<TestComponent />, element);
|
||||
}}
|
||||
nativeProps={{}}
|
||||
/>,
|
||||
mountpoint
|
||||
);
|
||||
|
||||
// Replaces the component at the mountpoint with nothing
|
||||
renderAndTriggerHooks(<>Empty</>, mountpoint);
|
||||
|
||||
expect(isUnmounted).toBe(true);
|
||||
});
|
||||
|
||||
it('should handle when the mount function is asynchronous with a cleanup fn', async () => {
|
||||
const unmountCallback = jest.fn();
|
||||
|
||||
renderAndTriggerHooks(
|
||||
<NativeRenderer
|
||||
render={async (element, props) => {
|
||||
return unmountCallback;
|
||||
}}
|
||||
nativeProps={{}}
|
||||
/>,
|
||||
mountpoint
|
||||
);
|
||||
|
||||
// Schedule a promise cycle to update the DOM
|
||||
await new Promise((resolve) => setTimeout(resolve, 0));
|
||||
|
||||
// Replaces the component at the mountpoint with nothing
|
||||
renderAndTriggerHooks(<>Empty</>, mountpoint);
|
||||
|
||||
expect(unmountCallback).toHaveBeenCalled();
|
||||
});
|
||||
});
|
|
@ -1,66 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import React, { HTMLAttributes, useEffect, useRef } from 'react';
|
||||
import { unmountComponentAtNode } from 'react-dom';
|
||||
|
||||
type CleanupCallback = (el: Element) => void;
|
||||
|
||||
export interface NativeRendererProps<T> extends HTMLAttributes<HTMLDivElement> {
|
||||
render: (
|
||||
domElement: Element,
|
||||
props: T
|
||||
) => Promise<CleanupCallback | void> | CleanupCallback | void;
|
||||
nativeProps: T;
|
||||
tag?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* A component which takes care of providing a mountpoint for a generic render
|
||||
* function which takes an html element and an optional props object.
|
||||
* By default the mountpoint element will be a div, this can be changed with the
|
||||
* `tag` prop.
|
||||
*
|
||||
* If the rendered component tree was using React, we need to clean it up manually,
|
||||
* otherwise the unmount event never happens. A future addition is for non-React components
|
||||
* to get cleaned up, which could be added in the future.
|
||||
*
|
||||
* @param props
|
||||
*/
|
||||
export function NativeRenderer<T>({ render, nativeProps, tag, ...rest }: NativeRendererProps<T>) {
|
||||
const elementRef = useRef<Element>();
|
||||
const cleanupRef = useRef<((cleanupElement: Element) => void) | void>();
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
if (elementRef.current) {
|
||||
if (cleanupRef.current && typeof cleanupRef.current === 'function') {
|
||||
cleanupRef.current(elementRef.current);
|
||||
}
|
||||
unmountComponentAtNode(elementRef.current);
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
return React.createElement(tag || 'div', {
|
||||
...rest,
|
||||
ref: (el) => {
|
||||
if (el) {
|
||||
elementRef.current = el;
|
||||
// Handles the editor frame renderer, which is async
|
||||
const result = render(el, nativeProps);
|
||||
if (result instanceof Promise) {
|
||||
result.then((cleanup) => {
|
||||
if (typeof cleanup === 'function') {
|
||||
cleanupRef.current = cleanup;
|
||||
}
|
||||
});
|
||||
} else if (typeof result === 'function') {
|
||||
cleanupRef.current = result;
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
|
@ -9,7 +9,7 @@ import type { IconType } from '@elastic/eui/src/components/icon/icon';
|
|||
import type { CoreStart, SavedObjectReference, ResolvedSimpleSavedObject } from '@kbn/core/public';
|
||||
import type { PaletteOutput } from '@kbn/coloring';
|
||||
import type { TopNavMenuData } from '@kbn/navigation-plugin/public';
|
||||
import type { MutableRefObject } from 'react';
|
||||
import type { MutableRefObject, ReactElement } from 'react';
|
||||
import type { Filter, TimeRange } from '@kbn/es-query';
|
||||
import type {
|
||||
ExpressionAstExpression,
|
||||
|
@ -40,12 +40,7 @@ import { estypes } from '@elastic/elasticsearch';
|
|||
import React from 'react';
|
||||
import { CellValueContext } from '@kbn/embeddable-plugin/public';
|
||||
import { EventAnnotationGroupConfig } from '@kbn/event-annotation-plugin/common';
|
||||
import type {
|
||||
DraggingIdentifier,
|
||||
DragDropIdentifier,
|
||||
DragContextValue,
|
||||
DropType,
|
||||
} from '@kbn/dom-drag-drop';
|
||||
import type { DraggingIdentifier, DragDropIdentifier, DropType } from '@kbn/dom-drag-drop';
|
||||
import type { AccessorConfig } from '@kbn/visualization-ui-components/public';
|
||||
import type { DateRange, LayerType, SortingHint } from '../common/types';
|
||||
import type {
|
||||
|
@ -372,26 +367,15 @@ export interface Datasource<T = unknown, P = unknown> {
|
|||
}) => T;
|
||||
getSelectedFields?: (state: T) => string[];
|
||||
|
||||
renderLayerSettings?: (
|
||||
domElement: Element,
|
||||
LayerSettingsComponent?: (
|
||||
props: DatasourceLayerSettingsProps<T>
|
||||
) => ((cleanupElement: Element) => void) | void;
|
||||
renderDataPanel: (
|
||||
domElement: Element,
|
||||
props: DatasourceDataPanelProps<T>
|
||||
) => ((cleanupElement: Element) => void) | void;
|
||||
renderDimensionTrigger: (
|
||||
domElement: Element,
|
||||
props: DatasourceDimensionTriggerProps<T>
|
||||
) => ((cleanupElement: Element) => void) | void;
|
||||
renderDimensionEditor: (
|
||||
domElement: Element,
|
||||
) => React.ReactElement<DatasourceLayerSettingsProps<T>> | null;
|
||||
DataPanelComponent: (props: DatasourceDataPanelProps<T>) => JSX.Element | null;
|
||||
DimensionTriggerComponent: (props: DatasourceDimensionTriggerProps<T>) => JSX.Element | null;
|
||||
DimensionEditorComponent: (
|
||||
props: DatasourceDimensionEditorProps<T>
|
||||
) => ((cleanupElement: Element) => void) | void;
|
||||
renderLayerPanel: (
|
||||
domElement: Element,
|
||||
props: DatasourceLayerPanelProps<T>
|
||||
) => ((cleanupElement: Element) => void) | void;
|
||||
) => ReactElement<DatasourceDimensionEditorProps<T>> | null;
|
||||
LayerPanelComponent: (props: DatasourceLayerPanelProps<T>) => JSX.Element | null;
|
||||
getDropProps: (
|
||||
props: GetDropPropsArgs<T>
|
||||
) => { dropTypes: DropType[]; nextLabel?: string } | undefined;
|
||||
|
@ -588,7 +572,6 @@ export interface DatasourceLayerSettingsProps<T = unknown> {
|
|||
|
||||
export interface DatasourceDataPanelProps<T = unknown> {
|
||||
state: T;
|
||||
dragDropContext: DragContextValue;
|
||||
setState: StateSetter<T, { applyImmediately?: boolean }>;
|
||||
showNoDataPopover: () => void;
|
||||
core: Pick<
|
||||
|
@ -1032,6 +1015,13 @@ export type RegisterLibraryAnnotationGroupFunction = (groupInfo: {
|
|||
id: string;
|
||||
group: EventAnnotationGroupConfig;
|
||||
}) => void;
|
||||
interface AddLayerButtonProps {
|
||||
supportedLayers: VisualizationLayerDescription[];
|
||||
addLayer: AddLayerFunction;
|
||||
ensureIndexPattern: (specOrId: DataViewSpec | string) => Promise<void>;
|
||||
registerLibraryAnnotationGroup: RegisterLibraryAnnotationGroupFunction;
|
||||
}
|
||||
|
||||
export interface Visualization<T = unknown, P = T, ExtraAppendLayerArg = unknown> {
|
||||
/** Plugin ID, such as "lnsXY" */
|
||||
id: string;
|
||||
|
@ -1161,27 +1151,24 @@ export interface Visualization<T = unknown, P = T, ExtraAppendLayerArg = unknown
|
|||
* Header rendered as layer title. This can be used for both static and dynamic content like
|
||||
* for extra configurability, such as for switch chart type
|
||||
*/
|
||||
renderLayerHeader?: (
|
||||
domElement: Element,
|
||||
LayerHeaderComponent?: (
|
||||
props: VisualizationLayerWidgetProps<T>
|
||||
) => ((cleanupElement: Element) => void) | void;
|
||||
) => null | ReactElement<VisualizationLayerWidgetProps<T>>;
|
||||
|
||||
/**
|
||||
* Layer panel content rendered. This can be used to render a custom content below the title,
|
||||
* like a custom dataview switch
|
||||
*/
|
||||
renderLayerPanel?: (
|
||||
domElement: Element,
|
||||
LayerPanelComponent?: (
|
||||
props: VisualizationLayerWidgetProps<T>
|
||||
) => ((cleanupElement: Element) => void) | void;
|
||||
) => null | ReactElement<VisualizationLayerWidgetProps<T>>;
|
||||
/**
|
||||
* Toolbar rendered above the visualization. This is meant to be used to provide chart-level
|
||||
* settings for the visualization.
|
||||
*/
|
||||
renderToolbar?: (
|
||||
domElement: Element,
|
||||
ToolbarComponent?: (
|
||||
props: VisualizationToolbarProps<T>
|
||||
) => ((cleanupElement: Element) => void) | void;
|
||||
) => null | ReactElement<VisualizationToolbarProps<T>>;
|
||||
|
||||
/**
|
||||
* The frame is telling the visualization to update or set a dimension based on user interaction
|
||||
|
@ -1216,49 +1203,42 @@ export interface Visualization<T = unknown, P = T, ExtraAppendLayerArg = unknown
|
|||
*/
|
||||
hasLayerSettings?: (props: VisualizationConfigProps<T>) => Record<'data' | 'appearance', boolean>;
|
||||
|
||||
renderLayerSettings?: (
|
||||
domElement: Element,
|
||||
LayerSettingsComponent?: (
|
||||
props: VisualizationLayerSettingsProps<T> & { section: 'data' | 'appearance' }
|
||||
) => ((cleanupElement: Element) => void) | void;
|
||||
) => null | ReactElement<VisualizationLayerSettingsProps<T>>;
|
||||
|
||||
/**
|
||||
* Additional editor that gets rendered inside the dimension popover in the "appearance" section.
|
||||
* This can be used to configure dimension-specific options
|
||||
*/
|
||||
renderDimensionEditor?: (
|
||||
domElement: Element,
|
||||
DimensionEditorComponent?: (
|
||||
props: VisualizationDimensionEditorProps<T>
|
||||
) => ((cleanupElement: Element) => void) | void;
|
||||
) => null | ReactElement<VisualizationDimensionEditorProps<T>>;
|
||||
/**
|
||||
* Additional editor that gets rendered inside the dimension popover in an additional section below "appearance".
|
||||
* This can be used to configure dimension-specific options
|
||||
*/
|
||||
renderDimensionEditorAdditionalSection?: (
|
||||
domElement: Element,
|
||||
DimensionEditorAdditionalSectionComponent?: (
|
||||
props: VisualizationDimensionEditorProps<T>
|
||||
) => ((cleanupElement: Element) => void) | void;
|
||||
) => null | ReactElement<VisualizationDimensionEditorProps<T>>;
|
||||
/**
|
||||
* Additional editor that gets rendered inside the data section.
|
||||
* This can be used to configure dimension-specific options
|
||||
*/
|
||||
renderDimensionEditorDataExtra?: (
|
||||
domElement: Element,
|
||||
DimensionEditorDataExtraComponent?: (
|
||||
props: VisualizationDimensionEditorProps<T>
|
||||
) => ((cleanupElement: Element) => void) | void;
|
||||
) => null | ReactElement<VisualizationDimensionEditorProps<T>>;
|
||||
/**
|
||||
* Renders dimension trigger. Used only for noDatasource layers
|
||||
*/
|
||||
renderDimensionTrigger?: (props: {
|
||||
DimensionTriggerComponent?: (props: {
|
||||
columnId: string;
|
||||
label: string;
|
||||
hideTooltip?: boolean;
|
||||
}) => JSX.Element | null;
|
||||
getAddLayerButtonComponent?: (props: {
|
||||
supportedLayers: VisualizationLayerDescription[];
|
||||
addLayer: AddLayerFunction;
|
||||
ensureIndexPattern: (specOrId: DataViewSpec | string) => Promise<void>;
|
||||
registerLibraryAnnotationGroup: RegisterLibraryAnnotationGroupFunction;
|
||||
}) => JSX.Element | null;
|
||||
}) => null | ReactElement<{ columnId: string; label: string; hideTooltip?: boolean }>;
|
||||
getAddLayerButtonComponent?: (
|
||||
props: AddLayerButtonProps
|
||||
) => null | ReactElement<AddLayerButtonProps>;
|
||||
/**
|
||||
* Creates map of columns ids and unique lables. Used only for noDatasource layers
|
||||
*/
|
||||
|
|
|
@ -6,13 +6,10 @@
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { render } from 'react-dom';
|
||||
import { Ast } from '@kbn/interpreter';
|
||||
import { I18nProvider } from '@kbn/i18n-react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { PaletteRegistry, CUSTOM_PALETTE } from '@kbn/coloring';
|
||||
import { ThemeServiceStart } from '@kbn/core/public';
|
||||
import { KibanaThemeProvider } from '@kbn/kibana-react-plugin/public';
|
||||
import { VIS_EVENT_TO_TRIGGER } from '@kbn/visualizations-plugin/public';
|
||||
import { IconChartDatatable } from '@kbn/chart-icons';
|
||||
import { LayerTypes } from '@kbn/expression-xy-plugin/public';
|
||||
|
@ -337,37 +334,16 @@ export const getDatatableVisualization = ({
|
|||
sorting: prevState.sorting?.columnId === columnId ? undefined : prevState.sorting,
|
||||
};
|
||||
},
|
||||
renderDimensionEditor(domElement, props) {
|
||||
render(
|
||||
<KibanaThemeProvider theme$={theme.theme$}>
|
||||
<I18nProvider>
|
||||
<TableDimensionEditor {...props} paletteService={paletteService} />
|
||||
</I18nProvider>
|
||||
</KibanaThemeProvider>,
|
||||
domElement
|
||||
);
|
||||
DimensionEditorComponent(props) {
|
||||
return <TableDimensionEditor {...props} paletteService={paletteService} />;
|
||||
},
|
||||
|
||||
renderDimensionEditorAdditionalSection(domElement, props) {
|
||||
render(
|
||||
<KibanaThemeProvider theme$={theme.theme$}>
|
||||
<I18nProvider>
|
||||
<TableDimensionEditorAdditionalSection {...props} paletteService={paletteService} />
|
||||
</I18nProvider>
|
||||
</KibanaThemeProvider>,
|
||||
domElement
|
||||
);
|
||||
DimensionEditorAdditionalSectionComponent(props) {
|
||||
return <TableDimensionEditorAdditionalSection {...props} paletteService={paletteService} />;
|
||||
},
|
||||
|
||||
renderDimensionEditorDataExtra(domElement, props) {
|
||||
render(
|
||||
<KibanaThemeProvider theme$={theme.theme$}>
|
||||
<I18nProvider>
|
||||
<TableDimensionDataExtraEditor {...props} paletteService={paletteService} />
|
||||
</I18nProvider>
|
||||
</KibanaThemeProvider>,
|
||||
domElement
|
||||
);
|
||||
DimensionEditorDataExtraComponent(props) {
|
||||
return <TableDimensionDataExtraEditor {...props} paletteService={paletteService} />;
|
||||
},
|
||||
|
||||
getSupportedLayers() {
|
||||
|
@ -522,15 +498,8 @@ export const getDatatableVisualization = ({
|
|||
}, []);
|
||||
},
|
||||
|
||||
renderToolbar(domElement, props) {
|
||||
render(
|
||||
<KibanaThemeProvider theme$={theme.theme$}>
|
||||
<I18nProvider>
|
||||
<DataTableToolbar {...props} />
|
||||
</I18nProvider>
|
||||
</KibanaThemeProvider>,
|
||||
domElement
|
||||
);
|
||||
ToolbarComponent(props) {
|
||||
return <DataTableToolbar {...props} />;
|
||||
},
|
||||
|
||||
onEditAction(state, event) {
|
||||
|
|
|
@ -6,11 +6,9 @@
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { render } from 'react-dom';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { ThemeServiceStart } from '@kbn/core/public';
|
||||
import { KibanaThemeProvider } from '@kbn/kibana-react-plugin/public';
|
||||
import { FormattedMessage, I18nProvider } from '@kbn/i18n-react';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { Ast } from '@kbn/interpreter';
|
||||
import { buildExpressionFunction, DatatableRow } from '@kbn/expressions-plugin/common';
|
||||
import { PaletteRegistry, CustomPaletteParams, CUSTOM_PALETTE } from '@kbn/coloring';
|
||||
|
@ -400,26 +398,12 @@ export const getGaugeVisualization = ({
|
|||
return update;
|
||||
},
|
||||
|
||||
renderDimensionEditor(domElement, props) {
|
||||
render(
|
||||
<KibanaThemeProvider theme$={theme.theme$}>
|
||||
<I18nProvider>
|
||||
<GaugeDimensionEditor {...props} paletteService={paletteService} />
|
||||
</I18nProvider>
|
||||
</KibanaThemeProvider>,
|
||||
domElement
|
||||
);
|
||||
DimensionEditorComponent(props) {
|
||||
return <GaugeDimensionEditor {...props} paletteService={paletteService} />;
|
||||
},
|
||||
|
||||
renderToolbar(domElement, props) {
|
||||
render(
|
||||
<KibanaThemeProvider theme$={theme.theme$}>
|
||||
<I18nProvider>
|
||||
<GaugeToolbar {...props} />
|
||||
</I18nProvider>
|
||||
</KibanaThemeProvider>,
|
||||
domElement
|
||||
);
|
||||
ToolbarComponent(props) {
|
||||
return <GaugeToolbar {...props} />;
|
||||
},
|
||||
|
||||
getSupportedLayers(state, frame) {
|
||||
|
|
|
@ -6,15 +6,13 @@
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { render } from 'react-dom';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { FormattedMessage, I18nProvider } from '@kbn/i18n-react';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { Ast } from '@kbn/interpreter';
|
||||
import { Position } from '@elastic/charts';
|
||||
import { IconChartHeatmap } from '@kbn/chart-icons';
|
||||
import { CUSTOM_PALETTE, PaletteRegistry, CustomPaletteParams } from '@kbn/coloring';
|
||||
import { ThemeServiceStart } from '@kbn/core/public';
|
||||
import { KibanaThemeProvider } from '@kbn/kibana-react-plugin/public';
|
||||
import { VIS_EVENT_TO_TRIGGER } from '@kbn/visualizations-plugin/public';
|
||||
import { LayerTypes } from '@kbn/expression-xy-plugin/public';
|
||||
import { HeatmapConfiguration } from '@kbn/visualizations-plugin/common';
|
||||
|
@ -272,26 +270,12 @@ export const getHeatmapVisualization = ({
|
|||
return update;
|
||||
},
|
||||
|
||||
renderDimensionEditor(domElement, props) {
|
||||
render(
|
||||
<KibanaThemeProvider theme$={theme.theme$}>
|
||||
<I18nProvider>
|
||||
<HeatmapDimensionEditor {...props} paletteService={paletteService} />
|
||||
</I18nProvider>
|
||||
</KibanaThemeProvider>,
|
||||
domElement
|
||||
);
|
||||
DimensionEditorComponent(props) {
|
||||
return <HeatmapDimensionEditor {...props} paletteService={paletteService} />;
|
||||
},
|
||||
|
||||
renderToolbar(domElement, props) {
|
||||
render(
|
||||
<KibanaThemeProvider theme$={theme.theme$}>
|
||||
<I18nProvider>
|
||||
<HeatmapToolbar {...props} />
|
||||
</I18nProvider>
|
||||
</KibanaThemeProvider>,
|
||||
domElement
|
||||
);
|
||||
ToolbarComponent(props) {
|
||||
return <HeatmapToolbar {...props} />;
|
||||
},
|
||||
|
||||
getSupportedLayers() {
|
||||
|
|
|
@ -5,13 +5,10 @@
|
|||
* 2.0.
|
||||
*/ import React from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { I18nProvider } from '@kbn/i18n-react';
|
||||
import { euiThemeVars } from '@kbn/ui-theme';
|
||||
import { render } from 'react-dom';
|
||||
import { Ast } from '@kbn/interpreter';
|
||||
import { PaletteOutput, PaletteRegistry, CUSTOM_PALETTE, shiftPalette } from '@kbn/coloring';
|
||||
import { ThemeServiceStart } from '@kbn/core/public';
|
||||
import { KibanaThemeProvider } from '@kbn/kibana-react-plugin/public';
|
||||
import { ColorMode, CustomPaletteState } from '@kbn/charts-plugin/common';
|
||||
import { VIS_EVENT_TO_TRIGGER } from '@kbn/visualizations-plugin/public';
|
||||
import { IconChartMetric } from '@kbn/chart-icons';
|
||||
|
@ -290,26 +287,12 @@ export const getLegacyMetricVisualization = ({
|
|||
return { ...prevState, accessor: undefined, colorMode: ColorMode.None, palette: undefined };
|
||||
},
|
||||
|
||||
renderToolbar(domElement, props) {
|
||||
render(
|
||||
<KibanaThemeProvider theme$={theme.theme$}>
|
||||
<I18nProvider>
|
||||
<MetricToolbar state={props.state} setState={props.setState} frame={props.frame} />
|
||||
</I18nProvider>
|
||||
</KibanaThemeProvider>,
|
||||
domElement
|
||||
);
|
||||
ToolbarComponent(props) {
|
||||
return <MetricToolbar state={props.state} setState={props.setState} frame={props.frame} />;
|
||||
},
|
||||
|
||||
renderDimensionEditor(domElement, props) {
|
||||
render(
|
||||
<KibanaThemeProvider theme$={theme.theme$}>
|
||||
<I18nProvider>
|
||||
<MetricDimensionEditor {...props} paletteService={paletteService} />
|
||||
</I18nProvider>
|
||||
</KibanaThemeProvider>,
|
||||
domElement
|
||||
);
|
||||
DimensionEditorComponent(props) {
|
||||
return <MetricDimensionEditor {...props} paletteService={paletteService} />;
|
||||
},
|
||||
|
||||
getVisualizationInfo(state: LegacyMetricState) {
|
||||
|
|
|
@ -7,14 +7,11 @@
|
|||
|
||||
import React from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { I18nProvider } from '@kbn/i18n-react';
|
||||
import { render } from 'react-dom';
|
||||
import { PaletteOutput, PaletteRegistry, CustomPaletteParams } from '@kbn/coloring';
|
||||
import { ThemeServiceStart } from '@kbn/core/public';
|
||||
import { VIS_EVENT_TO_TRIGGER } from '@kbn/visualizations-plugin/public';
|
||||
import { LayoutDirection } from '@elastic/charts';
|
||||
import { euiLightVars, euiThemeVars } from '@kbn/ui-theme';
|
||||
import { KibanaThemeProvider } from '@kbn/kibana-react-plugin/public';
|
||||
import { IconChartMetric } from '@kbn/chart-icons';
|
||||
import { AccessorConfig } from '@kbn/visualization-ui-components/public';
|
||||
import { CollapseFunction } from '../../../common/expressions';
|
||||
|
@ -592,37 +589,16 @@ export const getMetricVisualization = ({
|
|||
return updated;
|
||||
},
|
||||
|
||||
renderToolbar(domElement, props) {
|
||||
render(
|
||||
<KibanaThemeProvider theme$={theme.theme$}>
|
||||
<I18nProvider>
|
||||
<Toolbar {...props} />
|
||||
</I18nProvider>
|
||||
</KibanaThemeProvider>,
|
||||
domElement
|
||||
);
|
||||
ToolbarComponent(props) {
|
||||
return <Toolbar {...props} />;
|
||||
},
|
||||
|
||||
renderDimensionEditor(domElement, props) {
|
||||
render(
|
||||
<KibanaThemeProvider theme$={theme.theme$}>
|
||||
<I18nProvider>
|
||||
<DimensionEditor {...props} paletteService={paletteService} />
|
||||
</I18nProvider>
|
||||
</KibanaThemeProvider>,
|
||||
domElement
|
||||
);
|
||||
DimensionEditorComponent(props) {
|
||||
return <DimensionEditor {...props} paletteService={paletteService} />;
|
||||
},
|
||||
|
||||
renderDimensionEditorAdditionalSection(domElement, props) {
|
||||
render(
|
||||
<KibanaThemeProvider theme$={theme.theme$}>
|
||||
<I18nProvider>
|
||||
<DimensionEditorAdditionalSection {...props} />
|
||||
</I18nProvider>
|
||||
</KibanaThemeProvider>,
|
||||
domElement
|
||||
);
|
||||
DimensionEditorAdditionalSectionComponent(props) {
|
||||
return <DimensionEditorAdditionalSection {...props} />;
|
||||
},
|
||||
|
||||
getDisplayOptions() {
|
||||
|
|
|
@ -6,12 +6,10 @@
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { render } from 'react-dom';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { FormattedMessage, I18nProvider } from '@kbn/i18n-react';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import type { PaletteRegistry } from '@kbn/coloring';
|
||||
import { ThemeServiceStart } from '@kbn/core/public';
|
||||
import { KibanaThemeProvider } from '@kbn/kibana-react-plugin/public';
|
||||
import { VIS_EVENT_TO_TRIGGER } from '@kbn/visualizations-plugin/public';
|
||||
import { EuiSpacer } from '@elastic/eui';
|
||||
import { PartitionVisConfiguration } from '@kbn/visualizations-plugin/common/convert_to_lens';
|
||||
|
@ -459,25 +457,11 @@ export const getPieVisualization = ({
|
|||
layers: newState.layers.map((l) => (l.layerId === layerId ? newLayer : l)),
|
||||
};
|
||||
},
|
||||
renderDimensionEditor(domElement, props) {
|
||||
render(
|
||||
<KibanaThemeProvider theme$={kibanaTheme.theme$}>
|
||||
<I18nProvider>
|
||||
<DimensionEditor {...props} paletteService={paletteService} />
|
||||
</I18nProvider>
|
||||
</KibanaThemeProvider>,
|
||||
domElement
|
||||
);
|
||||
DimensionEditorComponent(props) {
|
||||
return <DimensionEditor {...props} paletteService={paletteService} />;
|
||||
},
|
||||
renderDimensionEditorDataExtra(domElement, props) {
|
||||
render(
|
||||
<KibanaThemeProvider theme$={kibanaTheme.theme$}>
|
||||
<I18nProvider>
|
||||
<DimensionDataExtraEditor {...props} paletteService={paletteService} />
|
||||
</I18nProvider>
|
||||
</KibanaThemeProvider>,
|
||||
domElement
|
||||
);
|
||||
DimensionEditorDataExtraComponent(props) {
|
||||
return <DimensionDataExtraEditor {...props} paletteService={paletteService} />;
|
||||
},
|
||||
|
||||
getSupportedLayers() {
|
||||
|
@ -501,30 +485,16 @@ export const getPieVisualization = ({
|
|||
toPreviewExpression: (state, layers, datasourceExpressionsByLayers) =>
|
||||
toPreviewExpression(state, layers, paletteService, datasourceExpressionsByLayers),
|
||||
|
||||
renderToolbar(domElement, props) {
|
||||
render(
|
||||
<KibanaThemeProvider theme$={kibanaTheme.theme$}>
|
||||
<I18nProvider>
|
||||
<PieToolbar {...props} />
|
||||
</I18nProvider>
|
||||
</KibanaThemeProvider>,
|
||||
domElement
|
||||
);
|
||||
ToolbarComponent(props) {
|
||||
return <PieToolbar {...props} />;
|
||||
},
|
||||
|
||||
hasLayerSettings(props) {
|
||||
return { data: props.state.shape !== PieChartTypes.MOSAIC, appearance: false };
|
||||
},
|
||||
|
||||
renderLayerSettings(domElement, props) {
|
||||
render(
|
||||
<KibanaThemeProvider theme$={kibanaTheme.theme$}>
|
||||
<I18nProvider>
|
||||
<LayerSettings {...props} />
|
||||
</I18nProvider>
|
||||
</KibanaThemeProvider>,
|
||||
domElement
|
||||
);
|
||||
LayerSettingsComponent(props) {
|
||||
return <LayerSettings {...props} />;
|
||||
},
|
||||
|
||||
getSuggestionFromConvertToLensContext(props) {
|
||||
|
|
|
@ -7,10 +7,7 @@
|
|||
|
||||
import React from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { I18nProvider } from '@kbn/i18n-react';
|
||||
import { render } from 'react-dom';
|
||||
import { ThemeServiceStart } from '@kbn/core/public';
|
||||
import { KibanaThemeProvider } from '@kbn/kibana-react-plugin/public';
|
||||
import { VIS_EVENT_TO_TRIGGER } from '@kbn/visualizations-plugin/public';
|
||||
import type { ExpressionTagcloudFunctionDefinition } from '@kbn/expression-tagcloud-plugin/common';
|
||||
import { LayerTypes } from '@kbn/expression-xy-plugin/public';
|
||||
|
@ -268,31 +265,20 @@ export const getTagcloudVisualization = ({
|
|||
return update;
|
||||
},
|
||||
|
||||
renderDimensionEditor(domElement, props) {
|
||||
DimensionEditorComponent(props) {
|
||||
if (props.groupId === TAG_GROUP_ID) {
|
||||
render(
|
||||
<KibanaThemeProvider theme$={theme.theme$}>
|
||||
<I18nProvider>
|
||||
<TagsDimensionEditor
|
||||
paletteService={paletteService}
|
||||
state={props.state}
|
||||
setState={props.setState}
|
||||
/>
|
||||
</I18nProvider>
|
||||
</KibanaThemeProvider>,
|
||||
domElement
|
||||
return (
|
||||
<TagsDimensionEditor
|
||||
paletteService={paletteService}
|
||||
state={props.state}
|
||||
setState={props.setState}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
renderToolbar(domElement, props) {
|
||||
render(
|
||||
<KibanaThemeProvider theme$={theme.theme$}>
|
||||
<I18nProvider>
|
||||
<TagcloudToolbar {...props} />
|
||||
</I18nProvider>
|
||||
</KibanaThemeProvider>,
|
||||
domElement
|
||||
);
|
||||
ToolbarComponent(props) {
|
||||
return <TagcloudToolbar {...props} />;
|
||||
},
|
||||
});
|
||||
|
|
|
@ -6,9 +6,8 @@
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { render } from 'react-dom';
|
||||
import { Position } from '@elastic/charts';
|
||||
import { FormattedMessage, I18nProvider } from '@kbn/i18n-react';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import type { PaletteRegistry } from '@kbn/coloring';
|
||||
import { IconChartBarReferenceLine, IconChartBarAnnotations } from '@kbn/chart-icons';
|
||||
|
@ -18,7 +17,6 @@ import {
|
|||
EventAnnotationServiceType,
|
||||
getAnnotationAccessor,
|
||||
} from '@kbn/event-annotation-plugin/public';
|
||||
import { KibanaContextProvider, KibanaThemeProvider } from '@kbn/kibana-react-plugin/public';
|
||||
import { VIS_EVENT_TO_TRIGGER } from '@kbn/visualizations-plugin/public';
|
||||
import type { DataPublicPluginStart } from '@kbn/data-plugin/public';
|
||||
import type { IStorageWrapper } from '@kbn/kibana-utils-plugin/public';
|
||||
|
@ -340,15 +338,8 @@ export const getXyVisualization = ({
|
|||
return { data: Boolean(layer && isAnnotationsLayer(layer)), appearance: false };
|
||||
},
|
||||
|
||||
renderLayerSettings(domElement, props) {
|
||||
render(
|
||||
<KibanaThemeProvider theme$={kibanaTheme.theme$}>
|
||||
<I18nProvider>
|
||||
<LayerSettings {...props} />
|
||||
</I18nProvider>
|
||||
</KibanaThemeProvider>,
|
||||
domElement
|
||||
);
|
||||
LayerSettingsComponent(props) {
|
||||
return <LayerSettings {...props} />;
|
||||
},
|
||||
onIndexPatternChange(state, indexPatternId, layerId) {
|
||||
const layerIndex = state.layers.findIndex((l) => l.layerId === layerId);
|
||||
|
@ -622,61 +613,29 @@ export const getXyVisualization = ({
|
|||
};
|
||||
},
|
||||
|
||||
renderLayerPanel(domElement, props) {
|
||||
LayerPanelComponent(props) {
|
||||
const { onChangeIndexPattern, ...otherProps } = props;
|
||||
render(
|
||||
<KibanaThemeProvider theme$={kibanaTheme.theme$}>
|
||||
<I18nProvider>
|
||||
<KibanaContextProvider
|
||||
services={{
|
||||
appName: 'lens',
|
||||
storage,
|
||||
uiSettings: core.uiSettings,
|
||||
data,
|
||||
fieldFormats,
|
||||
savedObjects: core.savedObjects,
|
||||
docLinks: core.docLinks,
|
||||
http: core.http,
|
||||
unifiedSearch,
|
||||
}}
|
||||
>
|
||||
<LayerHeaderContent
|
||||
{...otherProps}
|
||||
onChangeIndexPattern={(indexPatternId) => {
|
||||
// TODO: should it trigger an action as in the datasource?
|
||||
onChangeIndexPattern(indexPatternId);
|
||||
}}
|
||||
/>
|
||||
</KibanaContextProvider>
|
||||
</I18nProvider>
|
||||
</KibanaThemeProvider>,
|
||||
domElement
|
||||
|
||||
return (
|
||||
<LayerHeaderContent
|
||||
{...otherProps}
|
||||
onChangeIndexPattern={(indexPatternId) => {
|
||||
// TODO: should it trigger an action as in the datasource?
|
||||
onChangeIndexPattern(indexPatternId);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
},
|
||||
|
||||
renderLayerHeader(domElement, props) {
|
||||
render(
|
||||
<KibanaThemeProvider theme$={kibanaTheme.theme$}>
|
||||
<I18nProvider>
|
||||
<LayerHeader {...props} />
|
||||
</I18nProvider>
|
||||
</KibanaThemeProvider>,
|
||||
domElement
|
||||
);
|
||||
LayerHeaderComponent(props) {
|
||||
return <LayerHeader {...props} />;
|
||||
},
|
||||
|
||||
renderToolbar(domElement, props) {
|
||||
render(
|
||||
<KibanaThemeProvider theme$={kibanaTheme.theme$}>
|
||||
<I18nProvider>
|
||||
<XyToolbar {...props} useLegacyTimeAxis={useLegacyTimeAxis} />
|
||||
</I18nProvider>
|
||||
</KibanaThemeProvider>,
|
||||
domElement
|
||||
);
|
||||
ToolbarComponent(props) {
|
||||
return <XyToolbar {...props} useLegacyTimeAxis={useLegacyTimeAxis} />;
|
||||
},
|
||||
|
||||
renderDimensionEditor(domElement, props) {
|
||||
DimensionEditorComponent(props) {
|
||||
const allProps = {
|
||||
...props,
|
||||
datatableUtilities: data.datatableUtilities,
|
||||
|
@ -692,31 +651,10 @@ export const getXyVisualization = ({
|
|||
<DataDimensionEditor {...allProps} />
|
||||
);
|
||||
|
||||
render(
|
||||
<KibanaThemeProvider theme$={kibanaTheme.theme$}>
|
||||
<I18nProvider>
|
||||
<KibanaContextProvider
|
||||
services={{
|
||||
appName: 'lens',
|
||||
storage,
|
||||
uiSettings: core.uiSettings,
|
||||
data,
|
||||
fieldFormats,
|
||||
savedObjects: core.savedObjects,
|
||||
docLinks: core.docLinks,
|
||||
http: core.http,
|
||||
unifiedSearch,
|
||||
}}
|
||||
>
|
||||
{dimensionEditor}
|
||||
</KibanaContextProvider>
|
||||
</I18nProvider>
|
||||
</KibanaThemeProvider>,
|
||||
domElement
|
||||
);
|
||||
return dimensionEditor;
|
||||
},
|
||||
|
||||
renderDimensionEditorDataExtra(domElement, props) {
|
||||
DimensionEditorDataExtraComponent(props) {
|
||||
const allProps = {
|
||||
...props,
|
||||
datatableUtilities: data.datatableUtilities,
|
||||
|
@ -725,34 +663,13 @@ export const getXyVisualization = ({
|
|||
};
|
||||
const layer = props.state.layers.find((l) => l.layerId === props.layerId)!;
|
||||
if (isReferenceLayer(layer)) {
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
if (isAnnotationsLayer(layer)) {
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
render(
|
||||
<KibanaThemeProvider theme$={kibanaTheme.theme$}>
|
||||
<I18nProvider>
|
||||
<KibanaContextProvider
|
||||
services={{
|
||||
appName: 'lens',
|
||||
storage,
|
||||
uiSettings: core.uiSettings,
|
||||
data,
|
||||
fieldFormats,
|
||||
savedObjects: core.savedObjects,
|
||||
docLinks: core.docLinks,
|
||||
http: core.http,
|
||||
unifiedSearch,
|
||||
}}
|
||||
>
|
||||
<DataDimensionEditorDataSectionExtra {...allProps} />
|
||||
</KibanaContextProvider>
|
||||
</I18nProvider>
|
||||
</KibanaThemeProvider>,
|
||||
domElement
|
||||
);
|
||||
return <DataDimensionEditorDataSectionExtra {...allProps} />;
|
||||
},
|
||||
getAddLayerButtonComponent: (props) => {
|
||||
return (
|
||||
|
@ -986,7 +903,7 @@ export const getXyVisualization = ({
|
|||
state?.layers.filter(isAnnotationsLayer).map(({ indexPatternId }) => indexPatternId) ?? []
|
||||
);
|
||||
},
|
||||
renderDimensionTrigger({ columnId, label }) {
|
||||
DimensionTriggerComponent({ columnId, label }) {
|
||||
if (label) {
|
||||
return <DimensionTrigger id={columnId} label={label || defaultAnnotationLabel} />;
|
||||
}
|
||||
|
|
|
@ -7,12 +7,9 @@
|
|||
|
||||
import React from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { I18nProvider } from '@kbn/i18n-react';
|
||||
import { render } from 'react-dom';
|
||||
import type { FileLayer } from '@elastic/ems-client';
|
||||
import type { PaletteRegistry } from '@kbn/coloring';
|
||||
import { ThemeServiceStart } from '@kbn/core/public';
|
||||
import { KibanaThemeProvider } from '@kbn/kibana-react-plugin/public';
|
||||
import { layerTypes } from '@kbn/lens-plugin/public';
|
||||
import type { OperationMetadata, SuggestionRequest, Visualization } from '@kbn/lens-plugin/public';
|
||||
import { IconRegionMap } from '@kbn/chart-icons';
|
||||
|
@ -195,20 +192,16 @@ export const getVisualization = ({
|
|||
return update;
|
||||
},
|
||||
|
||||
renderDimensionEditor(domElement, props) {
|
||||
DimensionEditorComponent(props) {
|
||||
if (props.groupId === REGION_KEY_GROUP_ID) {
|
||||
render(
|
||||
<KibanaThemeProvider theme$={theme.theme$}>
|
||||
<I18nProvider>
|
||||
<RegionKeyEditor
|
||||
emsFileLayers={emsFileLayers}
|
||||
state={props.state}
|
||||
setState={props.setState}
|
||||
/>
|
||||
</I18nProvider>
|
||||
</KibanaThemeProvider>,
|
||||
domElement
|
||||
return (
|
||||
<RegionKeyEditor
|
||||
emsFileLayers={emsFileLayers}
|
||||
state={props.state}
|
||||
setState={props.setState}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
},
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue