From 87f04f3b1bb025044cd538eaca89d271fd0a9392 Mon Sep 17 00:00:00 2001
From: Caroline Horn <549577+cchaos@users.noreply.github.com>
Date: Mon, 14 Sep 2020 18:23:13 -0400
Subject: [PATCH] [Lens] Move configuration popover to flyout (#76046)
* Moving to a Flyout implementation
* Fix up inner layout of flyout
* Fix up form rows
---
.../editor_frame/_frame_layout.scss | 15 +-
.../editor_frame/config_panel/_index.scss | 2 -
.../{_config_panel.scss => config_panel.scss} | 0
.../config_panel/config_panel.tsx | 1 +
.../config_panel/dimension_container.scss | 29 ++
.../config_panel/dimension_container.tsx | 134 +++++++++
.../config_panel/dimension_popover.scss | 18 --
.../config_panel/dimension_popover.tsx | 50 ----
.../{_layer_panel.scss => layer_panel.scss} | 3 +-
.../config_panel/layer_panel.test.tsx | 26 +-
.../editor_frame/config_panel/layer_panel.tsx | 138 ++++-----
.../editor_frame/config_panel/types.ts | 3 +-
.../editor_frame/index.scss | 1 -
.../indexpattern_datasource/_field_item.scss | 6 +-
.../dimension_panel/bucket_nesting_editor.tsx | 1 +
.../dimension_panel/dimension_editor.scss | 21 ++
...opover_editor.tsx => dimension_editor.tsx} | 281 +++++++++---------
.../dimension_panel/dimension_panel.test.tsx | 14 +-
.../dimension_panel/dimension_panel.tsx | 8 +-
.../dimension_panel/field_select.tsx | 14 +-
.../dimension_panel/format_selector.tsx | 138 ++++-----
.../dimension_panel/popover_editor.scss | 20 --
.../indexpattern_datasource/field_item.tsx | 4 +-
.../operations/definitions/date_histogram.tsx | 9 +-
.../operations/definitions/terms.test.tsx | 2 +-
.../operations/definitions/terms.tsx | 3 +
x-pack/plugins/lens/public/types.ts | 2 +-
x-pack/plugins/lens/readme.md | 2 +-
.../translations/translations/ja-JP.json | 3 -
.../translations/translations/zh-CN.json | 3 -
.../test/functional/page_objects/lens_page.ts | 3 +-
31 files changed, 525 insertions(+), 429 deletions(-)
delete mode 100644 x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/_index.scss
rename x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/{_config_panel.scss => config_panel.scss} (100%)
create mode 100644 x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/dimension_container.scss
create mode 100644 x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/dimension_container.tsx
delete mode 100644 x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/dimension_popover.scss
delete mode 100644 x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/dimension_popover.tsx
rename x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/{_layer_panel.scss => layer_panel.scss} (96%)
create mode 100644 x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_editor.scss
rename x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/{popover_editor.tsx => dimension_editor.tsx} (64%)
delete mode 100644 x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/popover_editor.scss
diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/_frame_layout.scss b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/_frame_layout.scss
index c2e8d4f6c0049..9367e59b11717 100644
--- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/_frame_layout.scss
+++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/_frame_layout.scss
@@ -11,7 +11,7 @@
.lnsFrameLayout__pageContent {
display: flex;
- overflow: auto;
+ overflow: hidden;
flex-grow: 1;
}
@@ -39,11 +39,16 @@
}
.lnsFrameLayout__sidebar--right {
- @include euiScrollBar;
+ flex-basis: 25%;
background-color: lightOrDarkTheme($euiColorLightestShade, $euiColorInk);
min-width: $lnsPanelMinWidth + $euiSizeXL;
- overflow-x: hidden;
- overflow-y: scroll;
- padding: $euiSize 0 $euiSize $euiSize;
+ max-width: $euiFormMaxWidth + $euiSizeXXL;
max-height: 100%;
+
+ .lnsConfigPanel {
+ @include euiScrollBar;
+ padding: $euiSize 0 $euiSize $euiSize;
+ overflow-x: hidden;
+ overflow-y: scroll;
+ }
}
diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/_index.scss b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/_index.scss
deleted file mode 100644
index 954fbfadf159b..0000000000000
--- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/_index.scss
+++ /dev/null
@@ -1,2 +0,0 @@
-@import 'config_panel';
-@import 'layer_panel';
diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/_config_panel.scss b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.scss
similarity index 100%
rename from x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/_config_panel.scss
rename to x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.scss
diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.tsx
index 446f5b44c2e50..ad16038f44911 100644
--- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.tsx
+++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.tsx
@@ -3,6 +3,7 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
+import './config_panel.scss';
import React, { useMemo, memo } from 'react';
import { EuiFlexItem, EuiToolTip, EuiButton, EuiForm } from '@elastic/eui';
diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/dimension_container.scss b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/dimension_container.scss
new file mode 100644
index 0000000000000..f200e25453a2a
--- /dev/null
+++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/dimension_container.scss
@@ -0,0 +1,29 @@
+@import '@elastic/eui/src/components/flyout/variables';
+@import '@elastic/eui/src/components/flyout/mixins';
+
+.lnsDimensionContainer {
+ // Use the EuiFlyout style
+ @include euiFlyout;
+ // But with custom positioning to keep it within the sidebar contents
+ position: absolute;
+ right: 0;
+ left: 0;
+ top: 0;
+ bottom: 0;
+ animation: euiFlyout $euiAnimSpeedNormal $euiAnimSlightResistance;
+}
+
+.lnsDimensionContainer--noAnimation {
+ animation: none;
+}
+
+.lnsDimensionContainer__footer,
+.lnsDimensionContainer__header {
+ padding: $euiSizeS;
+}
+
+.lnsDimensionContainer__trigger {
+ width: 100%;
+ display: block;
+ word-break: break-word;
+}
diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/dimension_container.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/dimension_container.tsx
new file mode 100644
index 0000000000000..d6b395ac74cce
--- /dev/null
+++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/dimension_container.tsx
@@ -0,0 +1,134 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+import './dimension_container.scss';
+
+import React, { useState, useEffect } from 'react';
+import {
+ EuiFlyoutHeader,
+ EuiFlyoutFooter,
+ EuiTitle,
+ EuiButtonEmpty,
+ EuiFlexItem,
+ EuiFocusTrap,
+} from '@elastic/eui';
+
+import classNames from 'classnames';
+import { i18n } from '@kbn/i18n';
+import { VisualizationDimensionGroupConfig } from '../../../types';
+import { DimensionContainerState } from './types';
+
+export function DimensionContainer({
+ dimensionContainerState,
+ setDimensionContainerState,
+ groups,
+ accessor,
+ groupId,
+ trigger,
+ panel,
+ panelTitle,
+}: {
+ dimensionContainerState: DimensionContainerState;
+ setDimensionContainerState: (newState: DimensionContainerState) => void;
+ groups: VisualizationDimensionGroupConfig[];
+ accessor: string;
+ groupId: string;
+ trigger: React.ReactElement;
+ panel: React.ReactElement;
+ panelTitle: React.ReactNode;
+}) {
+ const [openByCreation, setIsOpenByCreation] = useState(
+ dimensionContainerState.openId === accessor
+ );
+ const [focusTrapIsEnabled, setFocusTrapIsEnabled] = useState(false);
+ const [flyoutIsVisible, setFlyoutIsVisible] = useState(false);
+
+ const noMatch = dimensionContainerState.isOpen
+ ? !groups.some((d) => d.accessors.includes(accessor))
+ : false;
+
+ const closeFlyout = () => {
+ setDimensionContainerState({
+ isOpen: false,
+ openId: null,
+ addingToGroupId: null,
+ });
+ setIsOpenByCreation(false);
+ setFocusTrapIsEnabled(false);
+ setFlyoutIsVisible(false);
+ };
+
+ const openFlyout = () => {
+ setFlyoutIsVisible(true);
+ setTimeout(() => {
+ setFocusTrapIsEnabled(true);
+ }, 255);
+ };
+
+ const flyoutShouldBeOpen =
+ dimensionContainerState.isOpen &&
+ (dimensionContainerState.openId === accessor ||
+ (noMatch && dimensionContainerState.addingToGroupId === groupId));
+
+ useEffect(() => {
+ if (flyoutShouldBeOpen) {
+ openFlyout();
+ }
+ });
+
+ useEffect(() => {
+ if (!flyoutShouldBeOpen) {
+ if (flyoutIsVisible) {
+ setFlyoutIsVisible(false);
+ }
+ if (focusTrapIsEnabled) {
+ setFocusTrapIsEnabled(false);
+ }
+ }
+ }, [flyoutShouldBeOpen, flyoutIsVisible, focusTrapIsEnabled]);
+
+ const flyout = flyoutIsVisible && (
+
+
+
+
+
+ {panelTitle}
+
+
+
+
+ {panel}
+
+
+
+ {i18n.translate('xpack.lens.dimensionContainer.close', {
+ defaultMessage: 'Close',
+ })}
+
+
+
+
+ );
+
+ return (
+ <>
+
{trigger}
+ {flyout}
+ >
+ );
+}
diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/dimension_popover.scss b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/dimension_popover.scss
deleted file mode 100644
index 98036c7f31bd9..0000000000000
--- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/dimension_popover.scss
+++ /dev/null
@@ -1,18 +0,0 @@
-.lnsDimensionPopover {
- line-height: 0;
- flex-grow: 1;
- max-width: calc(100% - #{$euiSizeL});
-}
-
-.lnsDimensionPopover__trigger {
- max-width: 100%;
- display: block;
- word-break: break-word;
-}
-
-// todo: remove after closing https://github.com/elastic/eui/issues/3548
-.lnsDimensionPopover__fixTranslateDnd {
- // sass-lint:disable-block no-important
- transform: none !important;
-}
-
diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/dimension_popover.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/dimension_popover.tsx
deleted file mode 100644
index a90bd8122d18e..0000000000000
--- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/dimension_popover.tsx
+++ /dev/null
@@ -1,50 +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;
- * you may not use this file except in compliance with the Elastic License.
- */
-import './dimension_popover.scss';
-
-import React from 'react';
-import { EuiPopover } from '@elastic/eui';
-import { VisualizationDimensionGroupConfig } from '../../../types';
-import { DimensionPopoverState } from './types';
-
-export function DimensionPopover({
- popoverState,
- setPopoverState,
- groups,
- accessor,
- groupId,
- trigger,
- panel,
-}: {
- popoverState: DimensionPopoverState;
- setPopoverState: (newState: DimensionPopoverState) => void;
- groups: VisualizationDimensionGroupConfig[];
- accessor: string;
- groupId: string;
- trigger: React.ReactElement;
- panel: React.ReactElement;
-}) {
- const noMatch = popoverState.isOpen ? !groups.some((d) => d.accessors.includes(accessor)) : false;
- return (
- {
- setPopoverState({ isOpen: false, openId: null, addingToGroupId: null, tabId: null });
- }}
- button={trigger}
- anchorPosition="leftUp"
- panelPaddingSize="none"
- >
- {panel}
-
- );
-}
diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/_layer_panel.scss b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.scss
similarity index 96%
rename from x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/_layer_panel.scss
rename to x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.scss
index 0329c856f97f2..b85c3e843613d 100644
--- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/_layer_panel.scss
+++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.scss
@@ -60,6 +60,5 @@
}
.lnsLayerPanel__styleEditor {
- width: $euiSize * 30;
- padding: $euiSizeS;
+ padding: 0 $euiSizeS $euiSizeS;
}
diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.test.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.test.tsx
index 85dbee6de524f..a9e2d6dc696ab 100644
--- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.test.tsx
+++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.test.tsx
@@ -13,7 +13,7 @@ import {
DatasourceMock,
} from '../../mocks';
import { ChildDragDropProvider } from '../../../drag_drop';
-import { EuiFormRow, EuiPopover } from '@elastic/eui';
+import { EuiFormRow } from '@elastic/eui';
import { mount } from 'enzyme';
import { mountWithIntl } from 'test_utils/enzyme_helpers';
import { Visualization } from '../../../types';
@@ -203,7 +203,7 @@ describe('LayerPanel', () => {
expect(group).toHaveLength(1);
});
- it('should render the datasource and visualization panels inside the dimension popover', () => {
+ it('should render the datasource and visualization panels inside the dimension container', () => {
mockVisualization.getConfiguration.mockReturnValueOnce({
groups: [
{
@@ -221,16 +221,16 @@ describe('LayerPanel', () => {
const component = mountWithIntl();
- const group = component.find('DimensionPopover');
+ const group = component.find('DimensionContainer');
const panel = mount(group.prop('panel'));
- expect(panel.find('EuiTabbedContent').prop('tabs')).toHaveLength(2);
+ expect(panel.children()).toHaveLength(2);
});
- it('should keep the popover open when configuring a new dimension', () => {
+ it('should keep the DimensionContainer open when configuring a new dimension', () => {
/**
* The ID generation system for new dimensions has been messy before, so
- * this tests that the ID used in the first render is used to keep the popover
+ * this tests that the ID used in the first render is used to keep the container
* open in future renders
*/
(generateId as jest.Mock).mockReturnValueOnce(`newid`);
@@ -264,20 +264,20 @@ describe('LayerPanel', () => {
const component = mountWithIntl();
- const group = component.find('DimensionPopover');
+ const group = component.find('DimensionContainer');
const triggerButton = mountWithIntl(group.prop('trigger'));
act(() => {
triggerButton.find('[data-test-subj="lns-empty-dimension"]').first().simulate('click');
});
component.update();
- expect(component.find(EuiPopover).prop('isOpen')).toBe(true);
+ expect(component.find('EuiFlyoutHeader').exists()).toBe(true);
});
- it('should close the popover when the active visualization changes', () => {
+ it('should close the DimensionContainer when the active visualization changes', () => {
/**
* The ID generation system for new dimensions has been messy before, so
- * this tests that the ID used in the first render is used to keep the popover
+ * this tests that the ID used in the first render is used to keep the container
* open in future renders
*/
@@ -312,18 +312,18 @@ describe('LayerPanel', () => {
const component = mountWithIntl();
- const group = component.find('DimensionPopover');
+ const group = component.find('DimensionContainer');
const triggerButton = mountWithIntl(group.prop('trigger'));
act(() => {
triggerButton.find('[data-test-subj="lns-empty-dimension"]').first().simulate('click');
});
component.update();
- expect(component.find(EuiPopover).prop('isOpen')).toBe(true);
+ expect(component.find('EuiFlyoutHeader').exists()).toBe(true);
act(() => {
component.setProps({ activeVisualizationId: 'vis2' });
});
component.update();
- expect(component.find(EuiPopover).prop('isOpen')).toBe(false);
+ expect(component.find('EuiFlyoutHeader').exists()).toBe(false);
});
});
diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx
index e9de2f935f0f3..46cd0292f2459 100644
--- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx
+++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx
@@ -3,6 +3,7 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
+import './layer_panel.scss';
import React, { useContext, useState, useEffect } from 'react';
import {
@@ -13,7 +14,6 @@ import {
EuiFlexItem,
EuiButtonEmpty,
EuiFormRow,
- EuiTabbedContent,
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
@@ -24,14 +24,13 @@ import { DragContext, DragDrop, ChildDragDropProvider } from '../../../drag_drop
import { LayerSettings } from './layer_settings';
import { trackUiEvent } from '../../../lens_ui_telemetry';
import { generateId } from '../../../id_generator';
-import { ConfigPanelWrapperProps, DimensionPopoverState } from './types';
-import { DimensionPopover } from './dimension_popover';
+import { ConfigPanelWrapperProps, DimensionContainerState } from './types';
+import { DimensionContainer } from './dimension_container';
-const initialPopoverState = {
+const initialDimensionContainerState = {
isOpen: false,
openId: null,
addingToGroupId: null,
- tabId: null,
};
export function LayerPanel(
@@ -50,13 +49,15 @@ export function LayerPanel(
}
) {
const dragDropContext = useContext(DragContext);
- const [popoverState, setPopoverState] = useState(initialPopoverState);
+ const [dimensionContainerState, setDimensionContainerState] = useState(
+ initialDimensionContainerState
+ );
const { framePublicAPI, layerId, isOnlyLayer, onRemoveLayer, dataTestSubj } = props;
const datasourcePublicAPI = framePublicAPI.datasourceLayers[layerId];
useEffect(() => {
- setPopoverState(initialPopoverState);
+ setDimensionContainerState(initialDimensionContainerState);
}, [props.activeVisualizationId]);
if (
@@ -159,7 +160,9 @@ export function LayerPanel(
return (
<>
{group.accessors.map((accessor) => {
- const tabs = [
- {
- id: 'datasource',
- name: i18n.translate('xpack.lens.editorFrame.quickFunctionsLabel', {
- defaultMessage: 'Quick functions',
- }),
- content: (
+ const datasourceDimensionEditor = (
+
+ );
+ const visDimensionEditor =
+ activeVisualization.renderDimensionEditor && group.enableDimensionEditor ? (
+
- ),
- },
- ];
-
- if (activeVisualization.renderDimensionEditor && group.enableDimensionEditor) {
- tabs.push({
- id: 'visualization',
- name: i18n.translate('xpack.lens.editorFrame.formatStyleLabel', {
- defaultMessage: 'Format & style',
- }),
- content: (
-
-
-
- ),
- });
- }
-
+
+ ) : null;
return (
- {
- if (popoverState.isOpen) {
- setPopoverState(initialPopoverState);
+ onClick: () => {
+ if (dimensionContainerState.isOpen) {
+ setDimensionContainerState(initialDimensionContainerState);
} else {
- setPopoverState({
+ setDimensionContainerState({
isOpen: true,
openId: accessor,
addingToGroupId: null, // not set for existing dimension
- tabId: 'datasource',
});
}
},
@@ -298,22 +283,21 @@ export function LayerPanel(
/>
}
panel={
- t.id === popoverState.tabId) || tabs[0]}
- size="s"
- onTabClick={(tab) => {
- setPopoverState({
- ...popoverState,
- tabId: tab.id as typeof popoverState['tabId'],
- });
- }}
- />
+ <>
+ {datasourceDimensionEditor}
+ {visDimensionEditor}
+ >
}
+ panelTitle={i18n.translate('xpack.lens.configure.configurePanelTitle', {
+ defaultMessage: '{groupLabel} configuration',
+ values: {
+ groupLabel: group.groupLabel,
+ },
+ })}
/>
- {
- if (popoverState.isOpen) {
- setPopoverState(initialPopoverState);
+ if (dimensionContainerState.isOpen) {
+ setDimensionContainerState(initialDimensionContainerState);
} else {
- setPopoverState({
+ setDimensionContainerState({
isOpen: true,
openId: newId,
addingToGroupId: group.groupId,
- tabId: 'datasource',
});
}
}}
@@ -428,6 +411,12 @@ export function LayerPanel(
}
+ panelTitle={i18n.translate('xpack.lens.configure.configurePanelTitle', {
+ defaultMessage: '{groupLabel} configuration',
+ values: {
+ groupLabel: group.groupLabel,
+ },
+ })}
panel={
<>
button {
+ padding-top: 0;
+ padding-bottom: 0;
+}
diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/popover_editor.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_editor.tsx
similarity index 64%
rename from x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/popover_editor.tsx
rename to x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_editor.tsx
index d5f0110f071f1..98e9389a85819 100644
--- a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/popover_editor.tsx
+++ b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_editor.tsx
@@ -4,21 +4,18 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import './popover_editor.scss';
+import './dimension_editor.scss';
import _ from 'lodash';
import React, { useState, useMemo, useEffect } from 'react';
import { i18n } from '@kbn/i18n';
import {
- EuiFlexItem,
- EuiFlexGroup,
EuiListGroup,
- EuiCallOut,
EuiFormRow,
EuiFieldText,
EuiSpacer,
EuiListGroupItemProps,
} from '@elastic/eui';
-import classNames from 'classnames';
+import { EuiFormLabel } from '@elastic/eui';
import { IndexPatternColumn, OperationType } from '../indexpattern';
import { IndexPatternDimensionEditorProps, OperationFieldSupportMatrix } from './dimension_panel';
import {
@@ -37,7 +34,7 @@ import { FormatSelector } from './format_selector';
const operationPanels = getOperationDisplay();
-export interface PopoverEditorProps extends IndexPatternDimensionEditorProps {
+export interface DimensionEditorProps extends IndexPatternDimensionEditorProps {
selectedColumn?: IndexPatternColumn;
operationFieldSupportMatrix: OperationFieldSupportMatrix;
currentIndexPattern: IndexPattern;
@@ -72,16 +69,25 @@ const LabelInput = ({ value, onChange }: { value: string; onChange: (value: stri
};
return (
-
+
+
+
);
};
-export function PopoverEditor(props: PopoverEditorProps) {
+export function DimensionEditor(props: DimensionEditorProps) {
const {
selectedColumn,
operationFieldSupportMatrix,
@@ -128,8 +134,8 @@ export function PopoverEditor(props: PopoverEditorProps) {
);
}
- function getSideNavItems(): EuiListGroupItemProps[] {
- return getOperationTypes().map(({ operationType, compatibleWithCurrentField }) => {
+ const sideNavItems: EuiListGroupItemProps[] = getOperationTypes().map(
+ ({ operationType, compatibleWithCurrentField }) => {
const isActive = Boolean(
incompatibleSelectedOperationType === operationType ||
(!incompatibleSelectedOperationType &&
@@ -220,13 +226,42 @@ export function PopoverEditor(props: PopoverEditorProps) {
);
},
};
- });
- }
+ }
+ );
return (
-
-
-
+
+
+
+ {i18n.translate('xpack.lens.indexPattern.functionsLabel', {
+ defaultMessage: 'Choose a function',
+ })}
+
+
+ 3 ? 'lnsIndexPatternDimensionEditor__columns' : ''}
+ gutterSize="none"
+ listItems={sideNavItems}
+ maxWidth={false}
+ />
+
+
+
+
-
-
-
-
-
-
-
- {incompatibleSelectedOperationType && selectedColumn && (
- <>
-
-
- >
- )}
- {incompatibleSelectedOperationType && !selectedColumn && (
- <>
-
-
- >
- )}
- {!incompatibleSelectedOperationType && ParamEditor && (
- <>
-
-
- >
- )}
- {!incompatibleSelectedOperationType && selectedColumn && (
-
- {
- setState({
- ...state,
- layers: {
- ...state.layers,
- [layerId]: {
- ...state.layers[layerId],
- columns: {
- ...state.layers[layerId].columns,
- [columnId]: {
- ...selectedColumn,
- label: value,
- customLabel: true,
- },
- },
- },
- },
- });
- }}
- />
-
- )}
+
- {!hideGrouping && (
-
{
- setState({
- ...state,
- layers: {
- ...state.layers,
- [props.layerId]: {
- ...state.layers[props.layerId],
- columnOrder,
- },
+ {!incompatibleSelectedOperationType && ParamEditor && (
+ <>
+
+
+ >
+ )}
+
+
+
+
+
+ {!incompatibleSelectedOperationType && selectedColumn && (
+ {
+ setState({
+ ...state,
+ layers: {
+ ...state.layers,
+ [layerId]: {
+ ...state.layers[layerId],
+ columns: {
+ ...state.layers[layerId].columns,
+ [columnId]: {
+ ...selectedColumn,
+ label: value,
+ customLabel: true,
},
- });
- }}
- />
- )}
+ },
+ },
+ },
+ });
+ }}
+ />
+ )}
- {selectedColumn && selectedColumn.dataType === 'number' ? (
- {
- setState(
- updateColumnParam({
- state,
- layerId,
- currentColumn: selectedColumn,
- paramName: 'format',
- value: newFormat,
- })
- );
- }}
- />
- ) : null}
-
-
-
-
+ {!hideGrouping && (
+ {
+ setState({
+ ...state,
+ layers: {
+ ...state.layers,
+ [props.layerId]: {
+ ...state.layers[props.layerId],
+ columnOrder,
+ },
+ },
+ });
+ }}
+ />
+ )}
+
+ {selectedColumn && selectedColumn.dataType === 'number' ? (
+ {
+ setState(
+ updateColumnParam({
+ state,
+ layerId,
+ currentColumn: selectedColumn,
+ paramName: 'format',
+ value: newFormat,
+ })
+ );
+ }}
+ />
+ ) : null}
+
);
}
diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.test.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.test.tsx
index f184d5628ab1c..dd7611ca7f41f 100644
--- a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.test.tsx
+++ b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.test.tsx
@@ -7,7 +7,7 @@
import { ReactWrapper, ShallowWrapper } from 'enzyme';
import React from 'react';
import { act } from 'react-dom/test-utils';
-import { EuiComboBox, EuiListGroupItemProps, EuiListGroup, EuiFieldNumber } from '@elastic/eui';
+import { EuiComboBox, EuiListGroupItemProps, EuiListGroup, EuiRange } from '@elastic/eui';
import { DataPublicPluginStart } from '../../../../../../src/plugins/data/public';
import { changeColumn } from '../state_helpers';
import {
@@ -619,9 +619,9 @@ describe('IndexPatternDimensionEditorPanel', () => {
.find('button[data-test-subj="lns-indexPatternDimension-terms incompatible"]')
.simulate('click');
- expect(wrapper.find('[data-test-subj="indexPattern-invalid-operation"]')).not.toHaveLength(
- 0
- );
+ expect(
+ wrapper.find('[data-test-subj="indexPattern-field-selection-row"]').first().prop('error')
+ ).toBeDefined();
expect(setState).not.toHaveBeenCalled();
});
@@ -1188,7 +1188,7 @@ describe('IndexPatternDimensionEditorPanel', () => {
expect(
wrapper
- .find(EuiFieldNumber)
+ .find(EuiRange)
.filter('[data-test-subj="indexPattern-dimension-formatDecimals"]')
.prop('value')
).toEqual(0);
@@ -1224,9 +1224,9 @@ describe('IndexPatternDimensionEditorPanel', () => {
act(() => {
wrapper
- .find(EuiFieldNumber)
+ .find(EuiRange)
.filter('[data-test-subj="indexPattern-dimension-formatDecimals"]')
- .prop('onChange')!({ target: { value: '0' } });
+ .prop('onChange')!({ currentTarget: { value: '0' } });
});
expect(setState).toHaveBeenCalledWith({
diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.tsx
index 1fbbefd8f1117..923f7145d1c64 100644
--- a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.tsx
+++ b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.tsx
@@ -20,7 +20,7 @@ import {
import { DataPublicPluginStart } from '../../../../../../src/plugins/data/public';
import { IndexPatternColumn, OperationType } from '../indexpattern';
import { getAvailableOperationsByMetadata, buildColumn, changeField } from '../operations';
-import { PopoverEditor } from './popover_editor';
+import { DimensionEditor } from './dimension_editor';
import { changeColumn } from '../state_helpers';
import { isDraggedField, hasField } from '../utils';
import { IndexPatternPrivateState, IndexPatternField } from '../types';
@@ -239,9 +239,7 @@ export const IndexPatternDimensionTriggerComponent = function IndexPatternDimens
{
- props.togglePopover();
- }}
+ onClick={props.onClick}
data-test-subj="lns-dimensionTrigger"
aria-label={i18n.translate('xpack.lens.configure.editConfig', {
defaultMessage: 'Edit configuration',
@@ -267,7 +265,7 @@ export const IndexPatternDimensionEditorComponent = function IndexPatternDimensi
props.state.layers[layerId].columns[props.columnId] || null;
return (
- {
currentIndexPattern: IndexPattern;
fieldMap: Record;
incompatibleSelectedOperationType: OperationType | null;
@@ -47,6 +53,7 @@ export function FieldSelect({
onChoose,
onDeleteColumn,
existingFields,
+ ...rest
}: FieldSelectProps) {
const { operationByField } = operationFieldSupportMatrix;
const memoizedFieldOptions = useMemo(() => {
@@ -155,7 +162,7 @@ export function FieldSelect({
defaultMessage: 'Field',
})}
options={(memoizedFieldOptions as unknown) as EuiComboBoxOptionOption[]}
- isInvalid={Boolean(incompatibleSelectedOperationType && selectedColumnOperationType)}
+ isInvalid={Boolean(incompatibleSelectedOperationType)}
selectedOptions={
((selectedColumnOperationType
? selectedColumnSourceField
@@ -194,6 +201,7 @@ export function FieldSelect({
);
}}
+ {...rest}
/>
);
}
diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/format_selector.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/format_selector.tsx
index b3b0190b9c400..8d9a91397cf2f 100644
--- a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/format_selector.tsx
+++ b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/format_selector.tsx
@@ -6,7 +6,7 @@
import React, { useState } from 'react';
import { i18n } from '@kbn/i18n';
-import { EuiFormRow, EuiFieldNumber, EuiComboBox } from '@elastic/eui';
+import { EuiFormRow, EuiComboBox, EuiSpacer, EuiRange } from '@elastic/eui';
import { IndexPatternColumn } from '../indexpattern';
const supportedFormats: Record
= {
@@ -57,80 +57,84 @@ export function FormatSelector(props: FormatSelectorProps) {
}),
};
+ const label = i18n.translate('xpack.lens.indexPattern.columnFormatLabel', {
+ defaultMessage: 'Value format',
+ });
+
+ const decimalsLabel = i18n.translate('xpack.lens.indexPattern.decimalPlacesLabel', {
+ defaultMessage: 'Decimals',
+ });
+
return (
<>
-
- ({
- value: id,
- label: format.title ?? id,
- })),
- ]}
- selectedOptions={
- currentFormat
- ? [
- {
- value: currentFormat.id,
- label: selectedFormat?.title ?? currentFormat.id,
- },
- ]
- : [defaultOption]
- }
- onChange={(choices) => {
- if (choices.length === 0) {
- return;
- }
-
- if (!choices[0].value) {
- onChange();
- return;
+
+
+ ({
+ value: id,
+ label: format.title ?? id,
+ })),
+ ]}
+ selectedOptions={
+ currentFormat
+ ? [
+ {
+ value: currentFormat.id,
+ label: selectedFormat?.title ?? currentFormat.id,
+ },
+ ]
+ : [defaultOption]
}
- onChange({
- id: choices[0].value,
- params: { decimals: state.decimalPlaces },
- });
- }}
- />
-
+ onChange={(choices) => {
+ if (choices.length === 0) {
+ return;
+ }
- {currentFormat ? (
-
- {
- setState({ decimalPlaces: Number(e.target.value) });
+ if (!choices[0].value) {
+ onChange();
+ return;
+ }
onChange({
- id: (selectedColumn.params as { format: { id: string } }).format.id,
- params: {
- decimals: Number(e.target.value),
- },
+ id: choices[0].value,
+ params: { decimals: state.decimalPlaces },
});
}}
- compressed
- fullWidth
/>
-
- ) : null}
+ {currentFormat ? (
+ <>
+
+ {
+ setState({ decimalPlaces: Number(e.currentTarget.value) });
+ onChange({
+ id: (selectedColumn.params as { format: { id: string } }).format.id,
+ params: {
+ decimals: Number(e.currentTarget.value),
+ },
+ });
+ }}
+ data-test-subj="indexPattern-dimension-formatDecimals"
+ compressed
+ fullWidth
+ prepend={decimalsLabel}
+ aria-label={decimalsLabel}
+ />
+ >
+ ) : null}
+
+
>
);
}
diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/popover_editor.scss b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/popover_editor.scss
deleted file mode 100644
index 3f75b5ad86669..0000000000000
--- a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/popover_editor.scss
+++ /dev/null
@@ -1,20 +0,0 @@
-.lnsIndexPatternDimensionEditor {
- width: $euiSize * 30;
- padding: $euiSizeS;
- max-width: 100%;
-}
-
-.lnsIndexPatternDimensionEditor__left,
-.lnsIndexPatternDimensionEditor__right {
- padding: $euiSizeS;
-}
-
-.lnsIndexPatternDimensionEditor__left {
- background-color: $euiPageBackgroundColor;
- width: $euiSize * 8;
-}
-
-.lnsIndexPatternDimensionEditor__operation > button {
- padding-top: 0;
- padding-bottom: 0;
-}
diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/field_item.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/field_item.tsx
index cf15c29844053..1f6d7911b3a33 100644
--- a/x-pack/plugins/lens/public/indexpattern_datasource/field_item.tsx
+++ b/x-pack/plugins/lens/public/indexpattern_datasource/field_item.tsx
@@ -231,7 +231,7 @@ export const InnerFieldItem = function InnerFieldItem(props: FieldItemProps) {
isOpen={infoIsOpen}
closePopover={() => setOpen(false)}
anchorPosition="rightUp"
- panelClassName="lnsFieldItem__fieldPopoverPanel"
+ panelClassName="lnsFieldItem__fieldPanel"
>
@@ -316,7 +316,7 @@ function FieldItemPopoverContents(props: State & FieldItemProps) {
if (histogram && histogram.buckets.length && topValues && topValues.buckets.length) {
title = (
{!intervalIsRestricted && (
-
+
)}
@@ -197,6 +198,8 @@ export const dateHistogramOperation: OperationDefinition
{intervalIsRestricted ? (
) : (
<>
-
+
{
diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms.test.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms.test.tsx
index 2972ed2d0231b..6f67cb7531e1c 100644
--- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms.test.tsx
+++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms.test.tsx
@@ -345,7 +345,7 @@ describe('terms', () => {
});
});
- describe('popover param editor', () => {
+ describe('param editor', () => {
it('should render current order by value and options', () => {
const setStateSpy = jest.fn();
const instance = shallow(
diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms.tsx
index c1b19fd5549e7..20c421008a746 100644
--- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms.tsx
+++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms.tsx
@@ -177,6 +177,7 @@ export const termsOperation: OperationDefinition = {
defaultMessage: 'Number of values',
})}
display="columnCompressed"
+ fullWidth
>
= {
defaultMessage: 'Order by',
})}
display="columnCompressed"
+ fullWidth
>
= {
defaultMessage: 'Order direction',
})}
display="columnCompressed"
+ fullWidth
>
= DatasourceDimensionPro
export type DatasourceDimensionTriggerProps = DatasourceDimensionProps & {
dragDropContext: DragContextState;
- togglePopover: () => void;
+ onClick: () => void;
};
export interface DatasourceLayerPanelProps {
diff --git a/x-pack/plugins/lens/readme.md b/x-pack/plugins/lens/readme.md
index e69ba9ec9506d..98bb60827af42 100644
--- a/x-pack/plugins/lens/readme.md
+++ b/x-pack/plugins/lens/readme.md
@@ -30,7 +30,7 @@ Lens has a lot of UI elements – to make it easier to refer to them in issues o
* **Config panel** Panel to the right showing configuration of the current chart, separated by layers
* **Layer panel** One of multiple panels in the config panel, holding configuration for separate layers
* **Dimension trigger** Chart dimension like "X axis", "Break down by" or "Slice by" in the config panel
- * **Dimension popover** Popover shown when clicking a dimension trigger
+ * **Dimension container** Container shown when clicking a dimension trigger and contains the dimension settints
* **Layer settings popover** Popover shown when clicking the button in the top left of a layer panel
* **Workspace panel** Center panel containing the chart preview, title and toolbar
* **Chart preview** Full-sized rendered chart in the center of the screen
diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json
index 1f39703b98303..d77ec100ddb0b 100644
--- a/x-pack/plugins/translations/translations/ja-JP.json
+++ b/x-pack/plugins/translations/translations/ja-JP.json
@@ -9568,10 +9568,8 @@
"xpack.lens.editorFrame.emptyWorkspaceHeading": "レンズはビジュアライゼーションを作成するための新しいツールです",
"xpack.lens.editorFrame.expandRenderingErrorButton": "エラーの詳細を表示",
"xpack.lens.editorFrame.expressionFailure": "表現を正常に実行できませんでした",
- "xpack.lens.editorFrame.formatStyleLabel": "書式とスタイル",
"xpack.lens.editorFrame.goToForums": "リクエストとフィードバック",
"xpack.lens.editorFrame.previewErrorLabel": "レンダリングのプレビューに失敗しました",
- "xpack.lens.editorFrame.quickFunctionsLabel": "クイック機能",
"xpack.lens.editorFrame.requiredDimensionWarningLabel": "必要な次元",
"xpack.lens.editorFrame.suggestionPanelTitle": "提案",
"xpack.lens.embeddable.failure": "ビジュアライゼーションを表示できませんでした",
@@ -9625,7 +9623,6 @@
"xpack.lens.indexPattern.existenceErrorLabel": "フィールド情報を読み込めません",
"xpack.lens.indexPattern.fieldDistributionLabel": "分布",
"xpack.lens.indexPattern.fieldItemTooltip": "可視化するには、ドラッグアンドドロップします。",
- "xpack.lens.indexPattern.fieldlessOperationLabel": "この関数を使用するには、フィールドを選択してください。",
"xpack.lens.indexPattern.fieldPanelEmptyStringValue": "空の文字列",
"xpack.lens.indexPattern.fieldPlaceholder": "フィールド",
"xpack.lens.indexPattern.fieldStatsButtonLabel": "フィールドプレビューを表示するには、クリックします。可視化するには、ドラッグアンドドロップします。",
diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json
index e299bd1624460..2ffc81cd05293 100644
--- a/x-pack/plugins/translations/translations/zh-CN.json
+++ b/x-pack/plugins/translations/translations/zh-CN.json
@@ -9574,10 +9574,8 @@
"xpack.lens.editorFrame.emptyWorkspaceHeading": "Lens 是用于创建可视化的新工具",
"xpack.lens.editorFrame.expandRenderingErrorButton": "显示错误的详情",
"xpack.lens.editorFrame.expressionFailure": "无法成功执行表达式",
- "xpack.lens.editorFrame.formatStyleLabel": "格式和样式",
"xpack.lens.editorFrame.goToForums": "提出请求并提供反馈",
"xpack.lens.editorFrame.previewErrorLabel": "预览呈现失败",
- "xpack.lens.editorFrame.quickFunctionsLabel": "快选函数",
"xpack.lens.editorFrame.requiredDimensionWarningLabel": "所需尺寸",
"xpack.lens.editorFrame.suggestionPanelTitle": "建议",
"xpack.lens.embeddable.failure": "无法显示可视化",
@@ -9631,7 +9629,6 @@
"xpack.lens.indexPattern.existenceErrorLabel": "无法加载字段信息",
"xpack.lens.indexPattern.fieldDistributionLabel": "分布",
"xpack.lens.indexPattern.fieldItemTooltip": "拖放以可视化。",
- "xpack.lens.indexPattern.fieldlessOperationLabel": "要使用此函数,请选择字段。",
"xpack.lens.indexPattern.fieldPanelEmptyStringValue": "空字符串",
"xpack.lens.indexPattern.fieldPlaceholder": "字段",
"xpack.lens.indexPattern.fieldStatsButtonLabel": "单击以进行字段预览,或拖放以进行可视化。",
diff --git a/x-pack/test/functional/page_objects/lens_page.ts b/x-pack/test/functional/page_objects/lens_page.ts
index acba3fa472b1a..edd7c2a43b343 100644
--- a/x-pack/test/functional/page_objects/lens_page.ts
+++ b/x-pack/test/functional/page_objects/lens_page.ts
@@ -105,13 +105,14 @@ export function LensPageProvider({ getService, getPageObjects }: FtrProviderCont
const target = await testSubjects.find('indexPattern-dimension-field');
await comboBox.openOptionsList(target);
await comboBox.setElement(target, opts.field);
+ await testSubjects.click('lns-indexPattern-dimensionContainerTitle');
},
/**
* Removes the dimension matching a specific test subject
*/
async removeDimension(dimensionTestSubj: string) {
- await testSubjects.click(`${dimensionTestSubj} > indexPattern-dimensionPopover-remove`);
+ await testSubjects.click(`${dimensionTestSubj} > indexPattern-dimension-remove`);
},
/**