[Lens] close dimension editor when editing color palette and clicking outside (#175215)

e85b7f97-114a-4b16-8b72-02eee5a121b9

Fixes https://github.com/elastic/kibana/issues/176247 (I removed the
flaky tests because they were bad. Replaced them with functional ones,
flaky test runner here:
https://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/5071)

Fixes an issue with the dimension editor visible on the video. 
When the second flyout (palette) is open and we change the chart (for
example from Bar Vertical Stacked to Bar Vertical when only having a
breakdown dimension defined), the breakdown dimension moves to
horizontal axis, but it’s not reflected on the flyout (we still get the
settings for breakdown, palette etc). I initially fixed it by closing
two flyouts and not fixing updating the state of the flyout, but then I
found a solution to also update the state instead.

I did a refactor here for the palette flyout and generalized it to
setting with sibling flyout component. I also moved some repeating logic
inside of it.
Styles are moved, but nothing changes from the user perspective.  

The only visual change is to unify palette settings by removing `Edit`
copy from the button (some palette settings had it, other didn't). We
can also do it the other way around if it makes more sense:

before:
<img width="422" alt="Screenshot 2024-01-29 at 11 17 47"
src="60499df6-60ea-4e81-b087-bd20d5bd0940">

after:
<img width="422" alt="Screenshot 2024-01-29 at 11 18 32"
src="2bc29ec2-4aad-4662-bef0-ea3fe06c50e1">

---------

Co-authored-by: Michael Marcialis <michael.l.marcialis@gmail.com>
This commit is contained in:
Marta Bondyra 2024-02-13 13:54:26 +01:00 committed by GitHub
parent 98536eba48
commit 1b51e72e14
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
44 changed files with 726 additions and 1074 deletions

View file

@ -0,0 +1,30 @@
/*
* 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 from 'react';
import { euiThemeVars } from '@kbn/ui-theme';
import { css } from '@emotion/react';
import { DimensionTrigger } from '@kbn/visualization-ui-components';
export const FakeDimensionButton = ({ label }: { label: string }) => (
<div
css={css`
display: flex;
align-items: center;
border-radius: ${euiThemeVars.euiBorderRadius};
min-height: ${euiThemeVars.euiSizeXL};
cursor: default !important;
background-color: ${euiThemeVars.euiColorLightShade} !important;
border-color: transparent !important;
box-shadow: none !important;
padding: 0 ${euiThemeVars.euiSizeS};
`}
>
<DimensionTrigger label={label} id="lns-fakeDimension" dataTestSubj="lns-fakeDimension" />
</div>
);

View file

@ -15,9 +15,9 @@ export function DimensionContainer({
...props
}: {
isOpen: boolean;
handleClose: () => boolean;
handleClose: () => void;
panel: React.ReactElement | null;
groupLabel: string;
label: string;
isFullscreen: boolean;
panelRef: (el: HTMLDivElement) => void;
isInlineEditing?: boolean;

View file

@ -74,7 +74,6 @@ const onDropToDimension = jest.fn();
describe('LayerPanel', () => {
let mockVisualization: jest.Mocked<Visualization>;
let mockVisualization2: jest.Mocked<Visualization>;
let mockDatasource = createMockDatasource('testDatasource');
@ -144,7 +143,6 @@ describe('LayerPanel', () => {
beforeEach(() => {
mockVisualization = createMockVisualization(faker.random.alphaNumeric());
mockVisualization.getLayerIds.mockReturnValue(['first']);
mockVisualization2 = createMockVisualization(faker.random.alphaNumeric());
mockDatasource = createMockDatasource();
});
@ -170,8 +168,7 @@ describe('LayerPanel', () => {
});
});
// FLAKY: https://github.com/elastic/kibana/issues/176247
describe.skip('single group', () => {
describe('single group', () => {
it('should render the group with a way to add a new column', async () => {
mockVisualization.getConfiguration.mockReturnValue({
groups: [defaultGroup],
@ -435,6 +432,7 @@ describe('LayerPanel', () => {
act(() => {
stateFn('newDatasourceState');
});
expect(updateAll).toHaveBeenCalled();
});
@ -513,79 +511,6 @@ describe('LayerPanel', () => {
expect(screen.getByRole('heading', { name: defaultGroup.groupLabel })).toBeInTheDocument();
});
it('should close the DimensionContainer when the activeVisualization has changed', async () => {
/**
* 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 container
* open in future renders
*/
(generateId as jest.Mock).mockReturnValue(`columnId`);
mockVisualization.getConfiguration.mockReturnValue({
groups: [
{
...defaultGroup,
accessors: [{ columnId: 'columnId' }],
},
],
});
// Normally the configuration would change in response to a state update,
// but this test is updating it directly
mockVisualization2.getConfiguration.mockReturnValue({
groups: [
{
...defaultGroup,
accessors: [{ columnId: 'columnId' }, { columnId: 'secondColumnId' }],
},
],
});
const { rerender } = renderLayerPanel();
userEvent.click(screen.getAllByTestId('lns-empty-dimension')[0]);
expect(screen.getByRole('heading', { name: defaultGroup.groupLabel })).toBeInTheDocument();
rerender(<LayerPanel {...props} activeVisualization={mockVisualization2} />);
expect(
screen.queryByRole('heading', { name: defaultGroup.groupLabel })
).not.toBeInTheDocument();
});
it('should close the DimensionContainer when the column cannot be found in the config', async () => {
/**
* 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 container
* open in future renders
*/
(generateId as jest.Mock).mockReturnValue(`columnId`);
mockVisualization.getConfiguration.mockReturnValue({
groups: [
{
...defaultGroup,
accessors: [{ columnId: 'columnId' }],
},
],
});
// Normally the configuration would change in response to a state update,
// but this test is updating it directly
mockVisualization2.getConfiguration.mockReturnValue({
groups: [
{
...defaultGroup,
accessors: [{ columnId: 'secondColumnId' }],
},
],
});
const { rerender } = renderLayerPanel();
// opens dimension editor and creates another column with secondColumnId
userEvent.click(screen.getAllByTestId('lns-empty-dimension')[0]);
expect(screen.getByRole('heading', { name: defaultGroup.groupLabel })).toBeInTheDocument();
rerender(<LayerPanel {...getDefaultProps()} activeVisualization={mockVisualization2} />);
expect(
screen.queryByRole('heading', { name: defaultGroup.groupLabel })
).not.toBeInTheDocument();
});
it('should only update the state on close when needed', async () => {
const updateDatasource = jest.fn();
mockVisualization.getConfiguration.mockReturnValue({
@ -1077,6 +1002,5 @@ describe('LayerPanel', () => {
});
});
});
// TODO - test user message display
});

View file

@ -21,11 +21,11 @@ import { i18n } from '@kbn/i18n';
import { css } from '@emotion/react';
import { euiThemeVars } from '@kbn/ui-theme';
import { DragDropIdentifier, ReorderProvider, DropType } from '@kbn/dom-drag-drop';
import { DimensionButton, DimensionTrigger } from '@kbn/visualization-ui-components';
import { DimensionButton } from '@kbn/visualization-ui-components';
import { LayerActions } from './layer_actions';
import { isOperation, LayerAction } from '../../../types';
import { isOperation, LayerAction, VisualizationDimensionGroupConfig } from '../../../types';
import { LayerSettings } from './layer_settings';
import { LayerPanelProps, ActiveDimensionState } from './types';
import { LayerPanelProps } from './types';
import { DimensionContainer } from './dimension_container';
import { EmptyDimensionButton } from './buttons/empty_dimension_button';
import { DraggableDimensionButton } from './buttons/draggable_dimension_button';
@ -38,15 +38,15 @@ import {
} from '../../../state_management';
import { getSharedActions } from './layer_actions/layer_actions';
import { FlyoutContainer } from '../../../shared_components/flyout_container';
const initialActiveDimensionState = {
isNew: false,
};
import { FakeDimensionButton } from './buttons/fake_dimension_button';
export function LayerPanel(props: LayerPanelProps) {
const [activeDimension, setActiveDimension] = useState<ActiveDimensionState>(
initialActiveDimensionState
);
const [openDimension, setOpenDimension] = useState<{
isComplete?: boolean;
openColumnId?: string;
openColumnGroup?: VisualizationDimensionGroupConfig;
}>({});
const [isPanelSettingsOpen, setPanelSettingsOpen] = useState(false);
const [hideTooltip, setHideTooltip] = useState<boolean>(false);
@ -70,6 +70,7 @@ export function LayerPanel(props: LayerPanelProps) {
onChangeIndexPattern,
core,
onDropToDimension,
setIsInlineFlyoutVisible,
} = props;
const isSaveable = useLensSelector((state) => state.lens.isSaveable);
@ -78,15 +79,10 @@ export function LayerPanel(props: LayerPanelProps) {
const isFullscreen = useLensSelector(selectIsFullscreenDatasource);
const dateRange = useLensSelector(selectResolvedDateRange);
useEffect(() => {
setActiveDimension(initialActiveDimensionState);
}, [activeVisualization.id]);
useEffect(() => {
// is undefined when the dimension panel is closed
const activeDimensionId = activeDimension.activeId;
props?.setIsInlineFlyoutVisible?.(!Boolean(activeDimensionId));
}, [activeDimension.activeId, activeVisualization.id, props]);
setIsInlineFlyoutVisible?.(!openDimension.openColumnId);
}, [openDimension.openColumnId, setIsInlineFlyoutVisible]);
const panelRef = useRef<HTMLDivElement | null>(null);
const settingsPanelRef = useRef<HTMLDivElement | null>(null);
@ -96,6 +92,26 @@ export function LayerPanel(props: LayerPanelProps) {
[layerId, registerNewLayerRef]
);
const closeDimensionEditor = () => {
if (layerDatasource) {
if (layerDatasource.updateStateOnCloseDimension) {
const newState = layerDatasource.updateStateOnCloseDimension({
state: layerDatasourceState,
layerId,
columnId: openColumnId!,
});
if (newState) {
props.updateDatasource(datasourceId, newState);
}
}
}
setOpenDimension({});
if (isFullscreen) {
toggleFullscreen();
}
};
const layerVisualizationConfigProps = {
layerId,
state: props.visualizationState,
@ -137,7 +153,36 @@ export function LayerPanel(props: LayerPanelProps) {
);
const isEmptyLayer = !dimensionGroups.some((d) => d.accessors.length > 0);
const { activeId, activeGroup } = activeDimension;
const { openColumnId, openColumnGroup, isComplete } = openDimension;
useEffect(() => {
if (!openColumnId) {
return;
}
const derivedOpenColumnGroup = dimensionGroups.find((group) =>
group.accessors.some((a) => a.columnId === openColumnId)
);
// dont update if nothing has changed
if (
isComplete === !!derivedOpenColumnGroup &&
derivedOpenColumnGroup?.groupId === openColumnGroup?.groupId
) {
return;
}
if (derivedOpenColumnGroup) {
// if column is found, mark it as complete. If it's moved to another group, update the group
setOpenDimension({
openColumnId,
openColumnGroup: derivedOpenColumnGroup,
isComplete: !!derivedOpenColumnGroup,
});
}
// if column is not found but is not new (is complete), close the dimension panel
if (isComplete && !derivedOpenColumnGroup) {
setOpenDimension({});
}
}, [openColumnId, dimensionGroups, isComplete, openColumnGroup?.groupId]);
const allAccessors = dimensionGroups.flatMap((group) =>
group.accessors.map((accessor) => accessor.columnId)
@ -169,7 +214,7 @@ export function LayerPanel(props: LayerPanelProps) {
[setNextFocusedButtonId, onDropToDimension]
);
const isDimensionPanelOpen = Boolean(activeId);
const isDimensionPanelOpen = Boolean(openColumnId);
const updateDataLayerState = useCallback(
(
@ -181,10 +226,10 @@ export function LayerPanel(props: LayerPanelProps) {
forceRender = false,
}: { isDimensionComplete?: boolean; forceRender?: boolean } = {}
) => {
if (!activeGroup || !activeId) {
if (!openColumnGroup || !openColumnId) {
return;
}
if (allAccessors.includes(activeId)) {
if (allAccessors.includes(openColumnId)) {
if (isDimensionComplete) {
if (forceRender) {
updateDatasource(datasourceId, newState);
@ -196,7 +241,7 @@ export function LayerPanel(props: LayerPanelProps) {
// complete, which clears the visualization. This keeps the flyout open and reuses
// the previous columnId
props.updateDatasource(datasourceId, newState);
props.onRemoveDimension({ layerId, columnId: activeId });
props.onRemoveDimension({ layerId, columnId: openColumnId });
}
} else if (isDimensionComplete) {
updateAll(
@ -204,13 +249,12 @@ export function LayerPanel(props: LayerPanelProps) {
newState,
activeVisualization.setDimension({
layerId,
groupId: activeGroup.groupId,
columnId: activeId,
groupId: openColumnGroup.groupId,
columnId: openColumnId,
prevState: visualizationState,
frame: framePublicAPI,
})
);
setActiveDimension({ ...activeDimension, isNew: false });
} else {
if (forceRender) {
updateDatasource(datasourceId, newState);
@ -221,9 +265,9 @@ export function LayerPanel(props: LayerPanelProps) {
},
// eslint-disable-next-line react-hooks/exhaustive-deps
[
activeDimension,
activeGroup,
activeId,
openDimension,
openColumnGroup,
openColumnId,
activeVisualization,
datasourceId,
layerId,
@ -510,10 +554,9 @@ export function LayerPanel(props: LayerPanelProps) {
label={columnLabelMap?.[accessorConfig.columnId] ?? ''}
groupLabel={group.groupLabel}
onClick={(id: string) => {
setActiveDimension({
isNew: false,
activeGroup: group,
activeId: id,
setOpenDimension({
openColumnGroup: group,
openColumnId: id,
});
}}
onRemoveClick={(id: string) => {
@ -552,26 +595,7 @@ export function LayerPanel(props: LayerPanelProps) {
) : null}
{group.fakeFinalAccessor && (
<div
css={css`
display: flex;
align-items: center;
border-radius: ${euiThemeVars.euiBorderRadius};
min-height: ${euiThemeVars.euiSizeXL};
cursor: default !important;
background-color: ${euiThemeVars.euiColorLightShade} !important;
border-color: transparent !important;
box-shadow: none !important;
padding: 0 ${euiThemeVars.euiSizeS};
`}
>
<DimensionTrigger
label={group.fakeFinalAccessor.label}
id="lns-fakeDimension"
dataTestSubj="lns-fakeDimension"
/>
</div>
<FakeDimensionButton label={group.fakeFinalAccessor.label} />
)}
{group.supportsMoreColumns ? (
@ -603,10 +627,9 @@ export function LayerPanel(props: LayerPanelProps) {
datasourceLayers={framePublicAPI.datasourceLayers}
onClick={(id) => {
props.onEmptyDimensionAdd(id, group);
setActiveDimension({
activeGroup: group,
activeId: id,
isNew: !group.supportStaticValue && Boolean(layerDatasource),
setOpenDimension({
openColumnGroup: group,
openColumnId: id,
});
}}
onDrop={onDrop}
@ -622,15 +645,13 @@ export function LayerPanel(props: LayerPanelProps) {
{(layerDatasource?.LayerSettingsComponent || activeVisualization?.LayerSettingsComponent) && (
<FlyoutContainer
panelRef={(el) => (settingsPanelRef.current = el)}
isOpen={isPanelSettingsOpen}
isFullscreen={false}
groupLabel={i18n.translate('xpack.lens.editorFrame.layerSettingsTitle', {
label={i18n.translate('xpack.lens.editorFrame.layerSettingsTitle', {
defaultMessage: 'Layer settings',
})}
isOpen={isPanelSettingsOpen}
handleClose={() => {
// update the current layer settings
setPanelSettingsOpen(false);
return true;
}}
isInlineEditing={Boolean(props?.setIsInlineFlyoutVisible)}
>
@ -651,9 +672,7 @@ export function LayerPanel(props: LayerPanelProps) {
</EuiText>
) : null}
{layerDatasource?.LayerSettingsComponent && (
<>
<layerDatasource.LayerSettingsComponent {...layerDatasourceConfigProps} />
</>
<layerDatasource.LayerSettingsComponent {...layerDatasourceConfigProps} />
)}
{layerDatasource?.LayerSettingsComponent && visualizationLayerSettings.data ? (
<EuiSpacer size="m" />
@ -700,59 +719,40 @@ export function LayerPanel(props: LayerPanelProps) {
panelRef={(el) => (panelRef.current = el)}
isOpen={isDimensionPanelOpen}
isFullscreen={isFullscreen}
groupLabel={activeGroup?.dimensionEditorGroupLabel ?? (activeGroup?.groupLabel || '')}
label={openColumnGroup?.dimensionEditorGroupLabel ?? (openColumnGroup?.groupLabel || '')}
isInlineEditing={Boolean(props?.setIsInlineFlyoutVisible)}
handleClose={() => {
if (layerDatasource) {
if (layerDatasource.updateStateOnCloseDimension) {
const newState = layerDatasource.updateStateOnCloseDimension({
state: layerDatasourceState,
layerId,
columnId: activeId!,
});
if (newState) {
props.updateDatasource(datasourceId, newState);
}
}
}
setActiveDimension(initialActiveDimensionState);
if (isFullscreen) {
toggleFullscreen();
}
return true;
}}
handleClose={closeDimensionEditor}
panel={
<>
{activeGroup &&
activeId &&
{openColumnGroup &&
openColumnId &&
layerDatasource &&
layerDatasource.DimensionEditorComponent({
...layerDatasourceConfigProps,
core: props.core,
columnId: activeId,
groupId: activeGroup.groupId,
hideGrouping: activeGroup.hideGrouping,
filterOperations: activeGroup.filterOperations,
isMetricDimension: activeGroup?.isMetricDimension,
columnId: openColumnId,
groupId: openColumnGroup.groupId,
hideGrouping: openColumnGroup.hideGrouping,
filterOperations: openColumnGroup.filterOperations,
isMetricDimension: openColumnGroup?.isMetricDimension,
dimensionGroups,
toggleFullscreen,
isFullscreen,
setState: updateDataLayerState,
supportStaticValue: Boolean(activeGroup.supportStaticValue),
paramEditorCustomProps: activeGroup.paramEditorCustomProps,
enableFormatSelector: activeGroup.enableFormatSelector !== false,
supportStaticValue: Boolean(openColumnGroup.supportStaticValue),
paramEditorCustomProps: openColumnGroup.paramEditorCustomProps,
enableFormatSelector: openColumnGroup.enableFormatSelector !== false,
layerType: activeVisualization.getLayerType(layerId, visualizationState),
indexPatterns: dataViews.indexPatterns,
activeData: layerVisualizationConfigProps.activeData,
dataSectionExtra: !isFullscreen &&
!activeDimension.isNew &&
openDimension.isComplete &&
activeVisualization.DimensionEditorDataExtraComponent && (
<activeVisualization.DimensionEditorDataExtraComponent
{...{
...layerVisualizationConfigProps,
groupId: activeGroup.groupId,
accessor: activeId,
groupId: openColumnGroup.groupId,
accessor: openColumnId,
datasource,
setState: props.updateVisualization,
addLayer: props.addLayer,
@ -762,19 +762,19 @@ export function LayerPanel(props: LayerPanelProps) {
/>
),
})}
{activeGroup &&
activeId &&
{openColumnGroup &&
openColumnId &&
!isFullscreen &&
!activeDimension.isNew &&
openDimension.isComplete &&
activeVisualization.DimensionEditorComponent &&
activeGroup?.enableDimensionEditor && (
openColumnGroup?.enableDimensionEditor && (
<>
<div className="lnsLayerPanel__styleEditor">
<activeVisualization.DimensionEditorComponent
{...{
...layerVisualizationConfigProps,
groupId: activeGroup.groupId,
accessor: activeId,
groupId: openColumnGroup.groupId,
accessor: openColumnId,
datasource,
setState: props.updateVisualization,
addLayer: props.addLayer,
@ -788,8 +788,8 @@ export function LayerPanel(props: LayerPanelProps) {
<activeVisualization.DimensionEditorAdditionalSectionComponent
{...{
...layerVisualizationConfigProps,
groupId: activeGroup.groupId,
accessor: activeId,
groupId: openColumnGroup.groupId,
accessor: openColumnId,
datasource,
setState: props.updateVisualization,
addLayer: props.addLayer,

View file

@ -14,7 +14,6 @@ import {
Visualization,
FramePublicAPI,
DatasourceDimensionEditorProps,
VisualizationDimensionGroupConfig,
DatasourceMap,
VisualizationMap,
UserMessagesGetter,
@ -22,6 +21,7 @@ import {
RegisterLibraryAnnotationGroupFunction,
StateSetter,
DragDropOperation,
VisualizationDimensionGroupConfig,
} from '../../../types';
export interface ConfigPanelWrapperProps {
@ -88,9 +88,3 @@ export interface LayerDatasourceDropProps {
state: unknown;
setState: (newState: unknown) => void;
}
export interface ActiveDimensionState {
isNew: boolean;
activeId?: string;
activeGroup?: VisualizationDimensionGroupConfig;
}

View file

@ -5,105 +5,51 @@
* 2.0.
*/
import './palette_panel_container.scss';
import { i18n } from '@kbn/i18n';
import React, { useState, useEffect, MutableRefObject } from 'react';
import {
EuiFlyoutHeader,
EuiFlyoutFooter,
EuiTitle,
EuiButtonIcon,
EuiButtonEmpty,
EuiFlexGroup,
EuiFlexItem,
EuiFocusTrap,
EuiOutsideClickDetector,
EuiPortal,
} from '@elastic/eui';
import React, { MutableRefObject } from 'react';
import { EuiButtonIcon, EuiFlexItem, EuiColorPaletteDisplay, EuiToolTip } from '@elastic/eui';
import { FIXED_PROGRESSION } from '@kbn/coloring';
import { SettingWithSiblingFlyout } from '../setting_with_sibling_flyout';
export function PalettePanelContainer({
isOpen,
handleClose,
siblingRef,
children,
title,
isInlineEditing,
}: {
isOpen: boolean;
title: string;
handleClose: () => void;
export function PalettePanelContainer(props: {
palette: string[];
siblingRef: MutableRefObject<HTMLDivElement | null>;
children?: React.ReactElement | React.ReactElement[];
isInlineEditing?: boolean;
title?: string;
}) {
const [focusTrapIsEnabled, setFocusTrapIsEnabled] = useState(false);
const closeFlyout = () => {
handleClose();
setFocusTrapIsEnabled(false);
};
useEffect(() => {
// The EuiFocusTrap is disabled when inline editing as it causes bugs with comboboxes
if (isOpen && !isInlineEditing) {
// without setTimeout here the flyout pushes content when animating
setTimeout(() => {
setFocusTrapIsEnabled(true);
}, 255);
}
}, [isInlineEditing, isOpen]);
return isOpen && siblingRef.current ? (
<EuiPortal insert={{ sibling: siblingRef.current, position: 'after' }}>
<EuiFocusTrap disabled={!focusTrapIsEnabled} clickOutsideDisables={true}>
<EuiOutsideClickDetector onOutsideClick={closeFlyout} isDisabled={!isOpen}>
<div
role="dialog"
aria-labelledby="lnsPalettePanelContainerTitle"
data-test-subj="lns-indexPattern-PalettePanelContainer"
className="lnsPalettePanelContainer"
>
<EuiFlyoutHeader hasBorder className="lnsPalettePanelContainer__header">
<EuiFlexGroup gutterSize="s" alignItems="center">
<EuiFlexItem grow={false}>
<EuiButtonIcon
color="text"
data-test-subj="lns-indexPattern-PalettePanelContainerBack"
className="lnsPalettePanelContainer__backIcon"
onClick={closeFlyout}
iconType="sortLeft"
aria-label={i18n.translate('xpack.lens.table.palettePanelContainer.back', {
defaultMessage: 'Back',
})}
/>
</EuiFlexItem>
<EuiFlexItem>
<EuiTitle size="xs">
<h3
id="lnsPalettePanelContainerTitle"
className="lnsPalettePanelContainer__headerTitle"
>
{title}
</h3>
</EuiTitle>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlyoutHeader>
{children && <div className="lnsPalettePanelContainer__content">{children}</div>}
<EuiFlyoutFooter className="lnsPalettePanelContainer__footer">
<EuiButtonEmpty flush="left" size="s" iconType="sortLeft" onClick={closeFlyout}>
{i18n.translate('xpack.lens.table.palettePanelContainer.back', {
defaultMessage: 'Back',
return (
<SettingWithSiblingFlyout
{...props}
SettingTrigger={({ onClick }: { onClick: () => void }) => (
<>
<EuiFlexItem>
<EuiColorPaletteDisplay
data-test-subj="lns_dynamicColoring_edit"
palette={props.palette}
type={FIXED_PROGRESSION}
onClick={onClick}
/>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiToolTip
content={i18n.translate('xpack.lens.colorMapping.editColors', {
defaultMessage: 'Edit colors',
})}
>
<EuiButtonIcon
data-test-subj="lns_colorEditing_trigger"
aria-label={i18n.translate('xpack.lens.colorMapping.editColors', {
defaultMessage: 'Edit colors',
})}
</EuiButtonEmpty>
</EuiFlyoutFooter>
</div>
</EuiOutsideClickDetector>
</EuiFocusTrap>
</EuiPortal>
) : null;
iconType="controlsHorizontal"
onClick={onClick}
size="xs"
/>
</EuiToolTip>
</EuiFlexItem>
</>
)}
/>
);
}

View file

@ -41,7 +41,7 @@ function fromExcludedClickTarget(event: Event) {
export function FlyoutContainer({
isOpen,
groupLabel,
label,
handleClose,
isFullscreen,
panelRef,
@ -51,9 +51,9 @@ export function FlyoutContainer({
isInlineEditing,
}: {
isOpen: boolean;
handleClose: () => boolean;
handleClose: () => void;
children: React.ReactElement | null;
groupLabel: string;
label: string;
isFullscreen?: boolean;
panelRef?: (el: HTMLDivElement) => void;
panelContainerRef?: (el: HTMLDivElement) => void;
@ -63,11 +63,8 @@ export function FlyoutContainer({
const [focusTrapIsEnabled, setFocusTrapIsEnabled] = useState(false);
const closeFlyout = useCallback(() => {
const canClose = handleClose();
if (canClose) {
setFocusTrapIsEnabled(false);
}
return canClose;
setFocusTrapIsEnabled(false);
handleClose();
}, [handleClose]);
useEffect(() => {
@ -138,12 +135,7 @@ export function FlyoutContainer({
id="lnsDimensionContainerTitle"
className="lnsDimensionContainer__headerTitle"
>
{i18n.translate('xpack.lens.configure.configurePanelTitle', {
defaultMessage: '{groupLabel}',
values: {
groupLabel,
},
})}
{label}
</h2>
</EuiTitle>
</EuiFlexItem>

View file

@ -1,6 +1,6 @@
@import '../../mixins';
@import '../mixins';
.lnsPalettePanelContainer {
.lnsSettingWithSiblingFlyout {
// Use the EuiFlyout style
@include euiFlyout;
// But with custom positioning to keep it within the sidebar contents
@ -14,15 +14,15 @@
z-index: $euiZLevel3 + 1
}
.lnsPalettePanelContainer__header {
.lnsSettingWithSiblingFlyout__header {
padding: $euiSize;
}
.lnsPalettePanelContainer__content {
.lnsSettingWithSiblingFlyout__content {
@include euiYScroll;
flex: 1;
}
.lnsPalettePanelContainer__footer {
.lnsSettingWithSiblingFlyout__footer {
padding: $euiSize;
}

View file

@ -0,0 +1,120 @@
/*
* 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 './setting_with_sibling_flyout.scss';
import { i18n } from '@kbn/i18n';
import React, { useState, useEffect, MutableRefObject } from 'react';
import {
EuiFlyoutHeader,
EuiFlyoutFooter,
EuiTitle,
EuiButtonIcon,
EuiButtonEmpty,
EuiFlexGroup,
EuiFlexItem,
EuiFocusTrap,
EuiOutsideClickDetector,
EuiPortal,
} from '@elastic/eui';
const DEFAULT_TITLE = i18n.translate('xpack.lens.colorSiblingFlyoutTitle', {
defaultMessage: 'Color',
});
export function SettingWithSiblingFlyout({
siblingRef,
children,
title = DEFAULT_TITLE,
isInlineEditing,
SettingTrigger,
}: {
title?: string;
siblingRef: MutableRefObject<HTMLDivElement | null>;
SettingTrigger: ({ onClick }: { onClick: () => void }) => JSX.Element;
children?: React.ReactElement | React.ReactElement[];
isInlineEditing?: boolean;
}) {
const [focusTrapIsEnabled, setFocusTrapIsEnabled] = useState(false);
const [isFlyoutOpen, setIsFlyoutOpen] = useState(false);
const toggleFlyout = () => {
setIsFlyoutOpen(!isFlyoutOpen);
};
const closeFlyout = () => {
setIsFlyoutOpen(false);
setFocusTrapIsEnabled(false);
};
useEffect(() => {
// The EuiFocusTrap is disabled when inline editing as it causes bugs with comboboxes
if (isFlyoutOpen && !isInlineEditing) {
// without setTimeout here the flyout pushes content when animating
setTimeout(() => {
setFocusTrapIsEnabled(true);
}, 255);
}
}, [isInlineEditing, isFlyoutOpen]);
return (
<EuiFlexGroup alignItems="center" gutterSize="s" responsive={false} css={{ cursor: 'pointer' }}>
<SettingTrigger onClick={toggleFlyout} />
{isFlyoutOpen && siblingRef.current && (
<EuiPortal insert={{ sibling: siblingRef.current, position: 'after' }}>
<EuiFocusTrap disabled={!focusTrapIsEnabled} clickOutsideDisables={true}>
<EuiOutsideClickDetector onOutsideClick={closeFlyout} isDisabled={!isFlyoutOpen}>
<div
role="dialog"
aria-labelledby="lnsSettingWithSiblingFlyoutTitle"
data-test-subj="lns-indexPattern-SettingWithSiblingFlyout"
className="lnsSettingWithSiblingFlyout"
>
<EuiFlyoutHeader hasBorder className="lnsSettingWithSiblingFlyout__header">
<EuiFlexGroup gutterSize="s" alignItems="center" responsive={false}>
<EuiFlexItem grow={false}>
<EuiButtonIcon
color="text"
data-test-subj="lns-indexPattern-SettingWithSiblingFlyoutBack"
className="lnsSettingWithSiblingFlyout__backIcon"
onClick={closeFlyout}
iconType="sortLeft"
aria-label={i18n.translate('xpack.lens.settingWithSiblingFlyout.back', {
defaultMessage: 'Back',
})}
/>
</EuiFlexItem>
<EuiFlexItem>
<EuiTitle size="xs">
<h3
id="lnsSettingWithSiblingFlyoutTitle"
className="lnsSettingWithSiblingFlyout__headerTitle"
>
{title}
</h3>
</EuiTitle>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlyoutHeader>
{children && <div className="lnsSettingWithSiblingFlyout__content">{children}</div>}
<EuiFlyoutFooter className="lnsSettingWithSiblingFlyout__footer">
<EuiButtonEmpty flush="left" size="s" iconType="sortLeft" onClick={closeFlyout}>
{i18n.translate('xpack.lens.settingWithSiblingFlyout.back', {
defaultMessage: 'Back',
})}
</EuiButtonEmpty>
</EuiFlyoutFooter>
</div>
</EuiOutsideClickDetector>
</EuiFocusTrap>
</EuiPortal>
)}
</EuiFlexGroup>
);
}

View file

@ -1,7 +1,3 @@
.lnsDynamicColoringRow {
align-items: center;
}
.lnsDynamicColoringClickable {
cursor: pointer;
}

View file

@ -142,9 +142,7 @@ describe('data table dimension editor', () => {
expect(instance.find('[data-test-subj="lnsDatatable_dynamicColoring_groups"]').exists()).toBe(
false
);
expect(instance.find('[data-test-subj="lnsDatatable_dynamicColoring_palette"]').exists()).toBe(
false
);
expect(instance.find('[data-test-subj="lns_dynamicColoring_edit"]').exists()).toBe(false);
});
it('should set the dynamic coloring default to "none"', () => {
@ -157,9 +155,7 @@ describe('data table dimension editor', () => {
.prop('idSelected')
).toEqual(expect.stringContaining('none'));
expect(instance.find('[data-test-subj="lnsDatatable_dynamicColoring_palette"]').exists()).toBe(
false
);
expect(instance.find('[data-test-subj="lns_dynamicColoring_edit"]').exists()).toBe(false);
});
it('should show the dynamic palette display ony when colorMode is different from "none"', () => {
@ -173,9 +169,7 @@ describe('data table dimension editor', () => {
.prop('idSelected')
).toEqual(expect.stringContaining('text'));
expect(instance.find('[data-test-subj="lnsDatatable_dynamicColoring_palette"]').exists()).toBe(
true
);
expect(instance.find('[data-test-subj="lns_dynamicColoring_edit"]').exists()).toBe(true);
});
it('should set the coloring mode to the right column', () => {
@ -214,10 +208,7 @@ describe('data table dimension editor', () => {
const instance = mountWithIntl(<TableDimensionEditor {...props} />);
act(() => {
instance
.find('[data-test-subj="lnsDatatable_dynamicColoring_trigger"]')
.first()
.simulate('click');
instance.find('[data-test-subj="lns_colorEditing_trigger"]').first().simulate('click');
});
expect(instance.find(PalettePanelContainer).exists()).toBe(true);
@ -235,9 +226,7 @@ describe('data table dimension editor', () => {
expect(instance.find('[data-test-subj="lnsDatatable_dynamicColoring_groups"]').exists()).toBe(
false
);
expect(instance.find('[data-test-subj="lnsDatatable_dynamicColoring_palette"]').exists()).toBe(
false
);
expect(instance.find('[data-test-subj="lns_dynamicColoring_edit"]').exists()).toBe(false);
});
it('should show the summary field for non numeric columns', () => {

View file

@ -5,19 +5,10 @@
* 2.0.
*/
import React, { useState } from 'react';
import React from 'react';
import { i18n } from '@kbn/i18n';
import {
EuiFormRow,
EuiSwitch,
EuiButtonGroup,
htmlIdGenerator,
EuiColorPaletteDisplay,
EuiFlexItem,
EuiFlexGroup,
EuiButtonEmpty,
} from '@elastic/eui';
import { CustomizablePalette, PaletteRegistry, FIXED_PROGRESSION } from '@kbn/coloring';
import { EuiFormRow, EuiSwitch, EuiButtonGroup, htmlIdGenerator } from '@elastic/eui';
import { CustomizablePalette, PaletteRegistry } from '@kbn/coloring';
import type { VisualizationDimensionEditorProps } from '../../../types';
import type { DatatableVisualizationState } from '../visualization';
@ -58,7 +49,6 @@ export function TableDimensionEditor(
) {
const { state, setState, frame, accessor, isInlineEditing } = props;
const column = state.columns.find(({ columnId }) => accessor === columnId);
const [isPaletteOpen, setIsPaletteOpen] = useState(false);
if (!column) return null;
if (column.isTransposed) return null;
@ -219,59 +209,23 @@ export function TableDimensionEditor(
defaultMessage: 'Color',
})}
>
<EuiFlexGroup
alignItems="center"
gutterSize="s"
responsive={false}
className="lnsDynamicColoringClickable"
<PalettePanelContainer
palette={displayStops.map(({ color }) => color)}
siblingRef={props.panelRef}
isInlineEditing={isInlineEditing}
>
<EuiFlexItem>
<EuiColorPaletteDisplay
data-test-subj="lnsDatatable_dynamicColoring_palette"
palette={displayStops.map(({ color }) => color)}
type={FIXED_PROGRESSION}
onClick={() => {
setIsPaletteOpen(!isPaletteOpen);
}}
/>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButtonEmpty
data-test-subj="lnsDatatable_dynamicColoring_trigger"
iconType="controlsHorizontal"
onClick={() => {
setIsPaletteOpen(!isPaletteOpen);
}}
size="xs"
flush="both"
>
{i18n.translate('xpack.lens.paletteTableGradient.customize', {
defaultMessage: 'Edit',
})}
</EuiButtonEmpty>
<PalettePanelContainer
siblingRef={props.panelRef}
isOpen={isPaletteOpen}
handleClose={() => setIsPaletteOpen(!isPaletteOpen)}
title={i18n.translate('xpack.lens.table.colorByRangePanelTitle', {
defaultMessage: 'Color',
})}
isInlineEditing={isInlineEditing}
>
<CustomizablePalette
palettes={props.paletteService}
activePalette={activePalette}
dataBounds={currentMinMax}
setPalette={(newPalette) => {
setState({
...state,
columns: updateColumnWith(state, accessor, { palette: newPalette }),
});
}}
/>
</PalettePanelContainer>
</EuiFlexItem>
</EuiFlexGroup>
<CustomizablePalette
palettes={props.paletteService}
activePalette={activePalette}
dataBounds={currentMinMax}
setPalette={(newPalette) => {
setState({
...state,
columns: updateColumnWith(state, accessor, { palette: newPalette }),
});
}}
/>
</PalettePanelContainer>
</EuiFormRow>
)}
</>

View file

@ -5,24 +5,10 @@
* 2.0.
*/
import {
EuiButtonEmpty,
EuiFlexGroup,
EuiColorPaletteDisplay,
EuiFormRow,
EuiFlexItem,
EuiSwitchEvent,
EuiSwitch,
EuiIcon,
} from '@elastic/eui';
import React, { useState } from 'react';
import { EuiFormRow, EuiSwitchEvent, EuiSwitch, EuiIcon } from '@elastic/eui';
import React from 'react';
import { i18n } from '@kbn/i18n';
import {
PaletteRegistry,
CustomizablePalette,
CUSTOM_PALETTE,
FIXED_PROGRESSION,
} from '@kbn/coloring';
import { PaletteRegistry, CustomizablePalette, CUSTOM_PALETTE } from '@kbn/coloring';
import { GaugeTicksPositions, GaugeColorModes } from '@kbn/expression-gauge-plugin/common';
import { getMaxValue, getMinValue } from '@kbn/expression-gauge-plugin/public';
import { TooltipWrapper } from '@kbn/visualization-utils';
@ -41,7 +27,6 @@ export function GaugeDimensionEditor(
}
) {
const { state, setState, frame, accessor, isInlineEditing } = props;
const [isPaletteOpen, setIsPaletteOpen] = useState(false);
if (state?.metricAccessor !== accessor) return null;
@ -76,7 +61,6 @@ export function GaugeDimensionEditor(
const displayStops = applyPaletteParams(props.paletteService, activePalette, currentMinMax);
const togglePalette = () => setIsPaletteOpen(!isPaletteOpen);
return (
<>
<EuiFormRow
@ -129,63 +113,31 @@ export function GaugeDimensionEditor(
defaultMessage: 'Color',
})}
>
<EuiFlexGroup
alignItems="center"
gutterSize="s"
responsive={false}
className="lnsDynamicColoringClickable"
<PalettePanelContainer
palette={displayStops.map(({ color }) => color)}
siblingRef={props.panelRef}
isInlineEditing={isInlineEditing}
>
<EuiFlexItem>
<EuiColorPaletteDisplay
data-test-subj="lnsGauge_dynamicColoring_palette"
palette={displayStops.map(({ color }) => color)}
type={FIXED_PROGRESSION}
onClick={togglePalette}
/>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButtonEmpty
data-test-subj="lnsGauge_dynamicColoring_trigger"
iconType="controlsHorizontal"
onClick={togglePalette}
size="xs"
flush="both"
>
{i18n.translate('xpack.lens.paletteTableGradient.customize', {
defaultMessage: 'Edit',
})}
</EuiButtonEmpty>
<PalettePanelContainer
siblingRef={props.panelRef}
isOpen={isPaletteOpen}
handleClose={togglePalette}
title={i18n.translate('xpack.lens.table.colorByRangePanelTitle', {
defaultMessage: 'Color',
})}
isInlineEditing={isInlineEditing}
>
<CustomizablePalette
palettes={props.paletteService}
activePalette={activePalette}
dataBounds={currentMinMax}
setPalette={(newPalette) => {
// if the new palette is not custom, replace the rangeMin with the artificial one
if (
newPalette.name !== CUSTOM_PALETTE &&
newPalette.params &&
newPalette.params.rangeMin !== currentMinMax.min
) {
newPalette.params.rangeMin = currentMinMax.min;
}
setState({
...state,
palette: newPalette,
});
}}
/>
</PalettePanelContainer>
</EuiFlexItem>
</EuiFlexGroup>
<CustomizablePalette
palettes={props.paletteService}
activePalette={activePalette}
dataBounds={currentMinMax}
setPalette={(newPalette) => {
// if the new palette is not custom, replace the rangeMin with the artificial one
if (
newPalette.name !== CUSTOM_PALETTE &&
newPalette.params &&
newPalette.params.rangeMin !== currentMinMax.min
) {
newPalette.params.rangeMin = currentMinMax.min;
}
setState({
...state,
palette: newPalette,
});
}}
/>
</PalettePanelContainer>
</EuiFormRow>
<EuiFormRow
display="columnCompressedSwitch"

View file

@ -5,16 +5,10 @@
* 2.0.
*/
import React, { useState } from 'react';
import React from 'react';
import { i18n } from '@kbn/i18n';
import {
EuiFormRow,
EuiColorPaletteDisplay,
EuiFlexItem,
EuiFlexGroup,
EuiButtonEmpty,
} from '@elastic/eui';
import { CustomizablePalette, FIXED_PROGRESSION, PaletteRegistry } from '@kbn/coloring';
import { EuiFormRow } from '@elastic/eui';
import { CustomizablePalette, PaletteRegistry } from '@kbn/coloring';
import type { VisualizationDimensionEditorProps } from '../../types';
import { PalettePanelContainer } from '../../shared_components';
import './dimension_editor.scss';
@ -27,7 +21,6 @@ export function HeatmapDimensionEditor(
}
) {
const { state, setState, frame, accessor, isInlineEditing } = props;
const [isPaletteOpen, setIsPaletteOpen] = useState(false);
if (state?.valueAccessor !== accessor) return null;
@ -51,69 +44,30 @@ export function HeatmapDimensionEditor(
defaultMessage: 'Color',
})}
>
<EuiFlexGroup
alignItems="center"
gutterSize="s"
responsive={false}
className="lnsDynamicColoringClickable"
<PalettePanelContainer
palette={displayStops.map(({ color }) => color)}
siblingRef={props.panelRef}
isInlineEditing={isInlineEditing}
>
<EuiFlexItem>
<EuiColorPaletteDisplay
data-test-subj="lnsHeatmap_dynamicColoring_palette"
palette={displayStops.map(({ color }) => color)}
type={FIXED_PROGRESSION}
onClick={() => {
setIsPaletteOpen(!isPaletteOpen);
{activePalette && (
<CustomizablePalette
palettes={props.paletteService}
activePalette={activePalette}
dataBounds={currentMinMax}
setPalette={(newPalette) => {
// make sure to always have a list of stops
if (newPalette.params && !newPalette.params.stops) {
newPalette.params.stops = displayStops;
}
(newPalette as HeatmapVisualizationState['palette'])!.accessor = accessor;
setState({
...state,
palette: newPalette as HeatmapVisualizationState['palette'],
});
}}
/>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButtonEmpty
data-test-subj="lnsHeatmap_dynamicColoring_trigger"
aria-label={i18n.translate('xpack.lens.paletteHeatmapGradient.customizeLong', {
defaultMessage: 'Edit palette',
})}
iconType="controlsHorizontal"
onClick={() => {
setIsPaletteOpen(!isPaletteOpen);
}}
size="xs"
flush="both"
>
{i18n.translate('xpack.lens.paletteHeatmapGradient.customize', {
defaultMessage: 'Edit',
})}
</EuiButtonEmpty>
<PalettePanelContainer
siblingRef={props.panelRef}
isOpen={isPaletteOpen}
handleClose={() => setIsPaletteOpen(!isPaletteOpen)}
title={i18n.translate('xpack.lens.table.colorByRangePanelTitle', {
defaultMessage: 'Color',
})}
isInlineEditing={isInlineEditing}
>
{activePalette && (
<CustomizablePalette
palettes={props.paletteService}
activePalette={activePalette}
dataBounds={currentMinMax}
setPalette={(newPalette) => {
// make sure to always have a list of stops
if (newPalette.params && !newPalette.params.stops) {
newPalette.params.stops = displayStops;
}
(newPalette as HeatmapVisualizationState['palette'])!.accessor = accessor;
setState({
...state,
palette: newPalette as HeatmapVisualizationState['palette'],
});
}}
/>
)}
</PalettePanelContainer>
</EuiFlexItem>
</EuiFlexGroup>
)}
</PalettePanelContainer>
</EuiFormRow>
</>
);

View file

@ -94,9 +94,7 @@ describe('metric dimension editor', () => {
expect(
instance.find('[data-test-subj="lnsLegacyMetric_dynamicColoring_groups"]').exists()
).toBe(false);
expect(
instance.find('[data-test-subj="lnsLegacyMetric_dynamicColoring_palette"]').exists()
).toBe(false);
expect(instance.find('[data-test-subj="lns_dynamicColoring_edit"]').exists()).toBe(false);
});
it('should set the dynamic coloring default to "none"', () => {
@ -109,9 +107,7 @@ describe('metric dimension editor', () => {
.prop('idSelected')
).toEqual(expect.stringContaining(ColorMode.None));
expect(
instance.find('[data-test-subj="lnsLegacyMetric_dynamicColoring_palette"]').exists()
).toBe(false);
expect(instance.find('[data-test-subj="lns_dynamicColoring_edit"]').exists()).toBe(false);
});
it('should show the dynamic palette display ony when colorMode is different from "none"', () => {
@ -125,9 +121,7 @@ describe('metric dimension editor', () => {
.prop('idSelected')
).toEqual(expect.stringContaining(ColorMode.Labels));
expect(
instance.find('[data-test-subj="lnsLegacyMetric_dynamicColoring_palette"]').exists()
).toBe(true);
expect(instance.find('[data-test-subj="lns_dynamicColoring_edit"]').exists()).toBe(true);
});
it('should prefill the palette stops with some colors when enabling coloring', () => {
@ -155,10 +149,7 @@ describe('metric dimension editor', () => {
const instance = mountWithIntl(<MetricDimensionEditor {...props} />);
act(() => {
instance
.find('[data-test-subj="lnsLegacyMetric_dynamicColoring_trigger"]')
.first()
.simulate('click');
instance.find('[data-test-subj="lns_colorEditing_trigger"]').first().simulate('click');
});
instance.update();
@ -172,10 +163,7 @@ describe('metric dimension editor', () => {
const instance = mountWithIntl(<MetricDimensionEditor {...props} />);
act(() => {
instance
.find('[data-test-subj="lnsLegacyMetric_dynamicColoring_trigger"]')
.first()
.simulate('click');
instance.find('[data-test-subj="lns_colorEditing_trigger"]').first().simulate('click');
});
instance.update();
@ -189,10 +177,7 @@ describe('metric dimension editor', () => {
const instance = mountWithIntl(<MetricDimensionEditor {...props} />);
act(() => {
instance
.find('[data-test-subj="lnsLegacyMetric_dynamicColoring_trigger"]')
.first()
.simulate('click');
instance.find('[data-test-subj="lns_colorEditing_trigger"]').first().simulate('click');
});
instance.update();

View file

@ -4,23 +4,10 @@
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import {
EuiButtonEmpty,
EuiButtonGroup,
EuiColorPaletteDisplay,
EuiFlexGroup,
EuiFlexItem,
EuiFormRow,
htmlIdGenerator,
} from '@elastic/eui';
import {
PaletteRegistry,
CustomizablePalette,
CUSTOM_PALETTE,
FIXED_PROGRESSION,
} from '@kbn/coloring';
import { EuiButtonGroup, EuiFormRow, htmlIdGenerator } from '@elastic/eui';
import { PaletteRegistry, CustomizablePalette, CUSTOM_PALETTE } from '@kbn/coloring';
import { i18n } from '@kbn/i18n';
import React, { useCallback, useState } from 'react';
import React from 'react';
import { ColorMode } from '@kbn/charts-plugin/common';
import type { LegacyMetricState } from '../../../common/types';
import { isNumericFieldForDatatable } from '../../../common/expressions/datatable/utils';
@ -38,11 +25,6 @@ export function MetricDimensionEditor(
}
) {
const { state, setState, frame, accessor, isInlineEditing } = props;
const [isPaletteOpen, setIsPaletteOpen] = useState(false);
const togglePalette = useCallback(() => {
setIsPaletteOpen(!isPaletteOpen);
}, [isPaletteOpen]);
const currentData = frame.activeData?.[state.layerId];
const [firstRow] = currentData?.rows || [];
@ -155,64 +137,32 @@ export function MetricDimensionEditor(
defaultMessage: 'Color',
})}
>
<EuiFlexGroup
alignItems="center"
gutterSize="s"
responsive={false}
className="lnsDynamicColoringClickable"
<PalettePanelContainer
palette={displayStops.map(({ color }) => color)}
siblingRef={props.panelRef}
isInlineEditing={isInlineEditing}
>
<EuiFlexItem>
<EuiColorPaletteDisplay
data-test-subj="lnsLegacyMetric_dynamicColoring_palette"
palette={displayStops.map(({ color }) => color)}
type={FIXED_PROGRESSION}
onClick={togglePalette}
/>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButtonEmpty
data-test-subj="lnsLegacyMetric_dynamicColoring_trigger"
iconType="controlsHorizontal"
onClick={togglePalette}
size="xs"
flush="both"
>
{i18n.translate('xpack.lens.paletteTableGradient.customize', {
defaultMessage: 'Edit',
})}
</EuiButtonEmpty>
<PalettePanelContainer
siblingRef={props.panelRef}
isOpen={isPaletteOpen}
handleClose={togglePalette}
title={i18n.translate('xpack.lens.table.colorByRangePanelTitle', {
defaultMessage: 'Color',
})}
isInlineEditing={isInlineEditing}
>
<CustomizablePalette
palettes={props.paletteService}
activePalette={activePalette}
dataBounds={currentMinMax}
setPalette={(newPalette) => {
// if the new palette is not custom, replace the rangeMin with the artificial one
if (
newPalette.name !== CUSTOM_PALETTE &&
newPalette.params &&
newPalette.params.rangeMin !== currentMinMax.min
) {
newPalette.params.rangeMin = currentMinMax.min;
}
setState({
...state,
palette: newPalette,
});
}}
showRangeTypeSelector={false}
/>
</PalettePanelContainer>
</EuiFlexItem>
</EuiFlexGroup>
<CustomizablePalette
palettes={props.paletteService}
activePalette={activePalette}
dataBounds={currentMinMax}
setPalette={(newPalette) => {
// if the new palette is not custom, replace the rangeMin with the artificial one
if (
newPalette.name !== CUSTOM_PALETTE &&
newPalette.params &&
newPalette.params.rangeMin !== currentMinMax.min
) {
newPalette.params.rangeMin = currentMinMax.min;
}
setState({
...state,
palette: newPalette,
});
}}
showRangeTypeSelector={false}
/>
</PalettePanelContainer>
</EuiFormRow>
)}
</>

View file

@ -6,11 +6,7 @@
*/
import {
EuiButtonEmpty,
EuiFlexGroup,
EuiColorPaletteDisplay,
EuiFormRow,
EuiFlexItem,
EuiButtonGroup,
EuiFieldNumber,
htmlIdGenerator,
@ -21,12 +17,11 @@ import {
useEuiTheme,
} from '@elastic/eui';
import { LayoutDirection } from '@elastic/charts';
import React, { useCallback, useState } from 'react';
import React, { useCallback } from 'react';
import { i18n } from '@kbn/i18n';
import {
PaletteRegistry,
CustomizablePalette,
FIXED_PROGRESSION,
DEFAULT_MAX_STOP,
DEFAULT_MIN_STOP,
} from '@kbn/coloring';
@ -220,8 +215,6 @@ function SecondaryMetricEditor({ accessor, idPrefix, frame, layerId, setState, s
function PrimaryMetricEditor(props: SubProps) {
const { state, setState, frame, accessor, idPrefix, isInlineEditing } = props;
const [isPaletteOpen, setIsPaletteOpen] = useState(false);
const currentData = frame.activeData?.[state.layerId];
const isMetricNumeric = isNumericFieldForDatatable(currentData, accessor);
@ -262,8 +255,6 @@ function PrimaryMetricEditor(props: SubProps) {
max: currentMinMax.max ?? DEFAULT_MAX_STOP,
});
const togglePalette = () => setIsPaletteOpen(!isPaletteOpen);
return (
<>
{isMetricNumeric && (
@ -334,56 +325,24 @@ function PrimaryMetricEditor(props: SubProps) {
defaultMessage: 'Color',
})}
>
<EuiFlexGroup
alignItems="center"
gutterSize="s"
responsive={false}
className="lnsDynamicColoringClickable"
<PalettePanelContainer
palette={displayStops.map(({ color }) => color)}
siblingRef={props.panelRef}
isInlineEditing={isInlineEditing}
>
<EuiFlexItem>
<EuiColorPaletteDisplay
data-test-subj="lnsMetric_dynamicColoring_palette"
palette={displayStops.map(({ color }) => color)}
type={FIXED_PROGRESSION}
onClick={togglePalette}
/>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButtonEmpty
data-test-subj="lnsMetric_dynamicColoring_trigger"
iconType="controlsHorizontal"
onClick={togglePalette}
size="xs"
flush="both"
>
{i18n.translate('xpack.lens.paletteTableGradient.customize', {
defaultMessage: 'Edit',
})}
</EuiButtonEmpty>
<PalettePanelContainer
siblingRef={props.panelRef}
isOpen={isPaletteOpen}
handleClose={togglePalette}
title={i18n.translate('xpack.lens.table.colorByRangePanelTitle', {
defaultMessage: 'Color',
})}
isInlineEditing={isInlineEditing}
>
<CustomizablePalette
palettes={props.paletteService}
activePalette={activePalette}
dataBounds={currentMinMax}
showRangeTypeSelector={supportsPercentPalette}
setPalette={(newPalette) => {
setState({
...state,
palette: newPalette,
});
}}
/>
</PalettePanelContainer>
</EuiFlexItem>
</EuiFlexGroup>
<CustomizablePalette
palettes={props.paletteService}
activePalette={activePalette}
dataBounds={currentMinMax}
showRangeTypeSelector={supportsPercentPalette}
setPalette={(newPalette) => {
setState({
...state,
palette: newPalette,
});
}}
/>
</PalettePanelContainer>
</EuiFormRow>
)}
<EuiFormRow

View file

@ -18,16 +18,7 @@ import {
getColorsFromMapping,
} from '@kbn/coloring';
import { ColorPicker, useDebouncedValue } from '@kbn/visualization-ui-components';
import {
EuiFormRow,
EuiButtonIcon,
EuiColorPaletteDisplay,
EuiFlexGroup,
EuiFlexItem,
EuiSwitch,
EuiText,
EuiBadge,
} from '@elastic/eui';
import { EuiFormRow, EuiFlexGroup, EuiFlexItem, EuiSwitch, EuiText, EuiBadge } from '@elastic/eui';
import { useState, useCallback } from 'react';
import { getColorCategories } from '@kbn/chart-expressions-common';
import { PieVisualizationState } from '../../../common/types';
@ -52,7 +43,6 @@ export function DimensionEditor(props: DimensionEditorProps) {
value: props.state,
onChange: props.setState,
});
const [isPaletteOpen, setIsPaletteOpen] = useState(false);
const currentLayer = localState.layers.find((layer) => layer.layerId === props.layerId);
@ -142,109 +132,76 @@ export function DimensionEditor(props: DimensionEditorProps) {
style={{ alignItems: 'center' }}
fullWidth
>
<EuiFlexGroup
alignItems="center"
gutterSize="s"
responsive={false}
className="lnsDynamicColoringClickable"
<PalettePanelContainer
palette={colors}
siblingRef={props.panelRef}
title={
useNewColorMapping
? i18n.translate('xpack.lens.colorMapping.editColorMappingTitle', {
defaultMessage: 'Edit colors by term mapping',
})
: i18n.translate('xpack.lens.colorMapping.editColorsTitle', {
defaultMessage: 'Edit colors',
})
}
isInlineEditing={props.isInlineEditing}
>
<EuiFlexItem>
<EuiColorPaletteDisplay
data-test-subj="lns_dynamicColoring_edit"
palette={colors}
type={'fixed'}
onClick={() => {
setIsPaletteOpen(!isPaletteOpen);
}}
/>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButtonIcon
data-test-subj="lns_colorEditing_trigger"
aria-label={i18n.translate('xpack.lens.colorMapping.editColorMappingButton', {
defaultMessage: 'Edit palette',
})}
iconType="controlsHorizontal"
onClick={() => {
setIsPaletteOpen(!isPaletteOpen);
}}
size="xs"
/>
<PalettePanelContainer
siblingRef={props.panelRef}
isOpen={isPaletteOpen}
handleClose={() => setIsPaletteOpen(!isPaletteOpen)}
title={
useNewColorMapping
? i18n.translate('xpack.lens.colorMapping.editColorMappingTitle', {
defaultMessage: 'Edit colors by term mapping',
})
: i18n.translate('xpack.lens.colorMapping.editColorsTitle', {
defaultMessage: 'Edit colors',
})
}
isInlineEditing={props.isInlineEditing}
>
<div className="lnsPalettePanel__section lnsPalettePanel__section--shaded lnsIndexPatternDimensionEditor--padded">
<EuiFlexGroup direction="column" gutterSize="s" justifyContent="flexStart">
<EuiFlexItem>
<EuiSwitch
label={
<EuiText size="xs">
<span>
{i18n.translate('xpack.lens.colorMapping.tryLabel', {
defaultMessage: 'Use the new Color Mapping feature',
})}{' '}
<EuiBadge color="hollow">
{i18n.translate('xpack.lens.colorMapping.techPreviewLabel', {
defaultMessage: 'Tech preview',
})}
</EuiBadge>
</span>
</EuiText>
}
data-test-subj="lns_colorMappingOrLegacyPalette_switch"
compressed
checked={useNewColorMapping}
onChange={({ target: { checked } }) => {
trackUiCounterEvents(
`color_mapping_switch_${checked ? 'enabled' : 'disabled'}`
);
setColorMapping(
checked ? { ...DEFAULT_COLOR_MAPPING_CONFIG } : undefined
);
setUseNewColorMapping(checked);
}}
/>
</EuiFlexItem>
<EuiFlexItem>
{canUseColorMapping || useNewColorMapping ? (
<CategoricalColorMapping
isDarkMode={props.isDarkMode}
model={currentLayer.colorMapping ?? { ...DEFAULT_COLOR_MAPPING_CONFIG }}
onModelUpdate={(model: ColorMapping.Config) => setColorMapping(model)}
palettes={AVAILABLE_PALETTES}
data={{
type: 'categories',
categories: splitCategories,
}}
specialTokens={SPECIAL_TOKENS_STRING_CONVERTION}
/>
) : (
<PalettePicker
palettes={props.paletteService}
activePalette={props.state.palette}
setPalette={(newPalette) => {
setLocalState({ ...props.state, palette: newPalette });
}}
/>
)}
</EuiFlexItem>
</EuiFlexGroup>
</div>
</PalettePanelContainer>
</EuiFlexItem>
</EuiFlexGroup>
<div className="lnsPalettePanel__section lnsPalettePanel__section--shaded lnsIndexPatternDimensionEditor--padded">
<EuiFlexGroup direction="column" gutterSize="s" justifyContent="flexStart">
<EuiFlexItem>
<EuiSwitch
label={
<EuiText size="xs">
<span>
{i18n.translate('xpack.lens.colorMapping.tryLabel', {
defaultMessage: 'Use the new Color Mapping feature',
})}{' '}
<EuiBadge color="hollow">
{i18n.translate('xpack.lens.colorMapping.techPreviewLabel', {
defaultMessage: 'Tech preview',
})}
</EuiBadge>
</span>
</EuiText>
}
data-test-subj="lns_colorMappingOrLegacyPalette_switch"
compressed
checked={useNewColorMapping}
onChange={({ target: { checked } }) => {
trackUiCounterEvents(
`color_mapping_switch_${checked ? 'enabled' : 'disabled'}`
);
setColorMapping(checked ? { ...DEFAULT_COLOR_MAPPING_CONFIG } : undefined);
setUseNewColorMapping(checked);
}}
/>
</EuiFlexItem>
<EuiFlexItem>
{canUseColorMapping || useNewColorMapping ? (
<CategoricalColorMapping
isDarkMode={props.isDarkMode}
model={currentLayer.colorMapping ?? { ...DEFAULT_COLOR_MAPPING_CONFIG }}
onModelUpdate={(model: ColorMapping.Config) => setColorMapping(model)}
palettes={AVAILABLE_PALETTES}
data={{
type: 'categories',
categories: splitCategories,
}}
specialTokens={SPECIAL_TOKENS_STRING_CONVERTION}
/>
) : (
<PalettePicker
palettes={props.paletteService}
activePalette={props.state.palette}
setPalette={(newPalette) => {
setLocalState({ ...props.state, palette: newPalette });
}}
/>
)}
</EuiFlexItem>
</EuiFlexGroup>
</div>
</PalettePanelContainer>
</EuiFormRow>
)}
{/* TODO: understand how this works */}

View file

@ -17,16 +17,7 @@ import {
getColorsFromMapping,
} from '@kbn/coloring';
import { i18n } from '@kbn/i18n';
import {
EuiButtonIcon,
EuiColorPaletteDisplay,
EuiFlexGroup,
EuiFlexItem,
EuiSwitch,
EuiFormRow,
EuiText,
EuiBadge,
} from '@elastic/eui';
import { EuiFlexGroup, EuiFlexItem, EuiSwitch, EuiFormRow, EuiText, EuiBadge } from '@elastic/eui';
import { useState, MutableRefObject, useCallback } from 'react';
import { useDebouncedValue } from '@kbn/visualization-ui-components';
import { getColorCategories } from '@kbn/chart-expressions-common';
@ -59,7 +50,6 @@ export function TagsDimensionEditor({
value: state,
onChange: setState,
});
const [isPaletteOpen, setIsPaletteOpen] = useState(false);
const [useNewColorMapping, setUseNewColorMapping] = useState(state.colorMapping ? true : false);
const colors = getColorsFromMapping(isDarkMode, state.colorMapping);
@ -98,107 +88,74 @@ export function TagsDimensionEditor({
style={{ alignItems: 'center' }}
fullWidth
>
<EuiFlexGroup
alignItems="center"
gutterSize="s"
responsive={false}
className="lnsDynamicColoringClickable"
<PalettePanelContainer
palette={colors}
siblingRef={panelRef}
title={
useNewColorMapping
? i18n.translate('xpack.lens.colorMapping.editColorMappingTitle', {
defaultMessage: 'Edit colors by term mapping',
})
: i18n.translate('xpack.lens.colorMapping.editColorsTitle', {
defaultMessage: 'Edit colors',
})
}
isInlineEditing={isInlineEditing}
>
<EuiFlexItem>
<EuiColorPaletteDisplay
data-test-subj="lns_dynamicColoring_edit"
palette={colors}
type={'fixed'}
onClick={() => {
setIsPaletteOpen(!isPaletteOpen);
}}
/>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButtonIcon
data-test-subj="lns_colorEditing_trigger"
aria-label={i18n.translate('xpack.lens.colorMapping.editColorMappingButton', {
defaultMessage: 'Edit palette',
})}
iconType="controlsHorizontal"
onClick={() => {
setIsPaletteOpen(!isPaletteOpen);
}}
size="xs"
/>
<PalettePanelContainer
siblingRef={panelRef}
isOpen={isPaletteOpen}
handleClose={() => setIsPaletteOpen(!isPaletteOpen)}
title={
useNewColorMapping
? i18n.translate('xpack.lens.colorMapping.editColorMappingTitle', {
defaultMessage: 'Edit colors by term mapping',
})
: i18n.translate('xpack.lens.colorMapping.editColorsTitle', {
defaultMessage: 'Edit colors',
})
}
isInlineEditing={isInlineEditing}
>
<div className="lnsPalettePanel__section lnsPalettePanel__section--shaded lnsIndexPatternDimensionEditor--padded">
<EuiFlexGroup direction="column" gutterSize="s" justifyContent="flexStart">
<EuiFlexItem>
<EuiSwitch
label={
<EuiText size="xs">
<span>
{i18n.translate('xpack.lens.colorMapping.tryLabel', {
defaultMessage: 'Use the new Color Mapping feature',
})}{' '}
<EuiBadge color="hollow">
{i18n.translate('xpack.lens.colorMapping.techPreviewLabel', {
defaultMessage: 'Tech preview',
})}
</EuiBadge>
</span>
</EuiText>
}
data-test-subj="lns_colorMappingOrLegacyPalette_switch"
compressed
checked={useNewColorMapping}
onChange={({ target: { checked } }) => {
trackUiCounterEvents(
`color_mapping_switch_${checked ? 'enabled' : 'disabled'}`
);
setColorMapping(checked ? { ...DEFAULT_COLOR_MAPPING_CONFIG } : undefined);
setUseNewColorMapping(checked);
}}
/>
</EuiFlexItem>
<EuiFlexItem>
{canUseColorMapping || useNewColorMapping ? (
<CategoricalColorMapping
isDarkMode={isDarkMode}
model={state.colorMapping ?? { ...DEFAULT_COLOR_MAPPING_CONFIG }}
onModelUpdate={(model: ColorMapping.Config) => setColorMapping(model)}
palettes={AVAILABLE_PALETTES}
data={{
type: 'categories',
categories: splitCategories,
}}
specialTokens={SPECIAL_TOKENS_STRING_CONVERTION}
/>
) : (
<PalettePicker
palettes={paletteService}
activePalette={state.palette}
setPalette={(newPalette) => {
setPalette(newPalette);
}}
/>
)}
</EuiFlexItem>
</EuiFlexGroup>
</div>
</PalettePanelContainer>
</EuiFlexItem>
</EuiFlexGroup>
<div className="lnsPalettePanel__section lnsPalettePanel__section--shaded lnsIndexPatternDimensionEditor--padded">
<EuiFlexGroup direction="column" gutterSize="s" justifyContent="flexStart">
<EuiFlexItem>
<EuiSwitch
label={
<EuiText size="xs">
<span>
{i18n.translate('xpack.lens.colorMapping.tryLabel', {
defaultMessage: 'Use the new Color Mapping feature',
})}{' '}
<EuiBadge color="hollow">
{i18n.translate('xpack.lens.colorMapping.techPreviewLabel', {
defaultMessage: 'Tech preview',
})}
</EuiBadge>
</span>
</EuiText>
}
data-test-subj="lns_colorMappingOrLegacyPalette_switch"
compressed
checked={useNewColorMapping}
onChange={({ target: { checked } }) => {
trackUiCounterEvents(`color_mapping_switch_${checked ? 'enabled' : 'disabled'}`);
setColorMapping(checked ? { ...DEFAULT_COLOR_MAPPING_CONFIG } : undefined);
setUseNewColorMapping(checked);
}}
/>
</EuiFlexItem>
<EuiFlexItem>
{canUseColorMapping || useNewColorMapping ? (
<CategoricalColorMapping
isDarkMode={isDarkMode}
model={state.colorMapping ?? { ...DEFAULT_COLOR_MAPPING_CONFIG }}
onModelUpdate={(model: ColorMapping.Config) => setColorMapping(model)}
palettes={AVAILABLE_PALETTES}
data={{
type: 'categories',
categories: splitCategories,
}}
specialTokens={SPECIAL_TOKENS_STRING_CONVERTION}
/>
) : (
<PalettePicker
palettes={paletteService}
activePalette={state.palette}
setPalette={(newPalette) => {
setPalette(newPalette);
}}
/>
)}
</EuiFlexItem>
</EuiFlexGroup>
</div>
</PalettePanelContainer>
</EuiFormRow>
);
}

View file

@ -61,12 +61,11 @@ export function LoadAnnotationLibraryFlyout({
</EuiFlyoutFooter>
}
isOpen={Boolean(isLoadLibraryVisible)}
groupLabel={i18n.translate('xpack.lens.editorFrame.loadFromLibrary', {
label={i18n.translate('xpack.lens.editorFrame.loadFromLibrary', {
defaultMessage: 'Select annotations from library',
})}
handleClose={() => {
setLoadLibraryFlyoutVisible(false);
return true;
}}
isInlineEditing={isInlineEditing}
>

View file

@ -13,8 +13,6 @@ import { ColorPicker } from '@kbn/visualization-ui-components';
import {
EuiBadge,
EuiButtonGroup,
EuiButtonIcon,
EuiColorPaletteDisplay,
EuiFlexGroup,
EuiFlexItem,
EuiFormRow,
@ -75,7 +73,6 @@ export function DataDimensionEditor(
const layer = state.layers[index] as XYDataLayerConfig;
const canUseColorMapping = layer.colorMapping ? true : false;
const [isPaletteOpen, setIsPaletteOpen] = useState(false);
const [useNewColorMapping, setUseNewColorMapping] = useState(canUseColorMapping);
const { inputValue: localState, handleInputChange: setLocalState } = useDebouncedValue<XYState>({
@ -168,108 +165,77 @@ export function DataDimensionEditor(
style={{ alignItems: 'center' }}
fullWidth
>
<EuiFlexGroup
alignItems="center"
gutterSize="s"
responsive={false}
className="lnsDynamicColoringClickable"
<PalettePanelContainer
palette={colors}
siblingRef={props.panelRef}
title={
useNewColorMapping
? i18n.translate('xpack.lens.colorMapping.editColorMappingTitle', {
defaultMessage: 'Edit colors by term mapping',
})
: i18n.translate('xpack.lens.colorMapping.editColorsTitle', {
defaultMessage: 'Edit colors',
})
}
isInlineEditing={isInlineEditing}
>
<EuiFlexItem>
<EuiColorPaletteDisplay
data-test-subj="lns_dynamicColoring_edit"
palette={colors}
type={'fixed'}
onClick={() => {
setIsPaletteOpen(!isPaletteOpen);
}}
/>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButtonIcon
data-test-subj="lns_colorEditing_trigger"
aria-label={i18n.translate('xpack.lens.colorMapping.editColorMappingButton', {
defaultMessage: 'Edit palette',
})}
iconType="controlsHorizontal"
onClick={() => {
setIsPaletteOpen(!isPaletteOpen);
}}
size="xs"
/>
<PalettePanelContainer
siblingRef={props.panelRef}
isOpen={isPaletteOpen}
handleClose={() => setIsPaletteOpen(!isPaletteOpen)}
title={
useNewColorMapping
? i18n.translate('xpack.lens.colorMapping.editColorMappingTitle', {
defaultMessage: 'Edit colors by term mapping',
})
: i18n.translate('xpack.lens.colorMapping.editColorsTitle', {
defaultMessage: 'Edit colors',
})
}
isInlineEditing={isInlineEditing}
>
<div className="lnsPalettePanel__section lnsPalettePanel__section--shaded lnsIndexPatternDimensionEditor--padded">
<EuiFlexGroup direction="column" gutterSize="s" justifyContent="flexStart">
<EuiFlexItem>
<EuiSwitch
label={
<EuiText size="xs">
<span>
{i18n.translate('xpack.lens.colorMapping.tryLabel', {
defaultMessage: 'Use the new Color Mapping feature',
})}{' '}
<EuiBadge color="hollow">
{i18n.translate('xpack.lens.colorMapping.techPreviewLabel', {
defaultMessage: 'Tech preview',
})}
</EuiBadge>
</span>
</EuiText>
}
data-test-subj="lns_colorMappingOrLegacyPalette_switch"
compressed
checked={useNewColorMapping}
onChange={({ target: { checked } }) => {
trackUiCounterEvents(
`color_mapping_switch_${checked ? 'enabled' : 'disabled'}`
);
setColorMapping(checked ? { ...DEFAULT_COLOR_MAPPING_CONFIG } : undefined);
setUseNewColorMapping(checked);
}}
/>
<EuiSpacer size="s" />
</EuiFlexItem>
<EuiFlexItem>
{canUseColorMapping || useNewColorMapping ? (
<CategoricalColorMapping
isDarkMode={darkMode}
model={layer.colorMapping ?? { ...DEFAULT_COLOR_MAPPING_CONFIG }}
onModelUpdate={(model: ColorMapping.Config) => setColorMapping(model)}
palettes={AVAILABLE_PALETTES}
data={{
type: 'categories',
categories: splitCategories,
}}
specialTokens={SPECIAL_TOKENS_STRING_CONVERTION}
/>
) : (
<PalettePicker
palettes={props.paletteService}
activePalette={localLayer?.palette}
setPalette={(newPalette) => {
setPalette(newPalette);
}}
/>
)}
</EuiFlexItem>
</EuiFlexGroup>
</div>
</PalettePanelContainer>
</EuiFlexItem>
</EuiFlexGroup>
<div className="lnsPalettePanel__section lnsPalettePanel__section--shaded lnsIndexPatternDimensionEditor--padded">
<EuiFlexGroup direction="column" gutterSize="s" justifyContent="flexStart">
<EuiFlexItem>
<EuiSwitch
label={
<EuiText size="xs">
<span>
{i18n.translate('xpack.lens.colorMapping.tryLabel', {
defaultMessage: 'Use the new Color Mapping feature',
})}{' '}
<EuiBadge color="hollow">
{i18n.translate('xpack.lens.colorMapping.techPreviewLabel', {
defaultMessage: 'Tech preview',
})}
</EuiBadge>
</span>
</EuiText>
}
data-test-subj="lns_colorMappingOrLegacyPalette_switch"
compressed
checked={useNewColorMapping}
onChange={({ target: { checked } }) => {
trackUiCounterEvents(
`color_mapping_switch_${checked ? 'enabled' : 'disabled'}`
);
setColorMapping(checked ? { ...DEFAULT_COLOR_MAPPING_CONFIG } : undefined);
setUseNewColorMapping(checked);
}}
/>
<EuiSpacer size="s" />
</EuiFlexItem>
<EuiFlexItem>
{canUseColorMapping || useNewColorMapping ? (
<CategoricalColorMapping
isDarkMode={darkMode}
model={layer.colorMapping ?? { ...DEFAULT_COLOR_MAPPING_CONFIG }}
onModelUpdate={(model: ColorMapping.Config) => setColorMapping(model)}
palettes={AVAILABLE_PALETTES}
data={{
type: 'categories',
categories: splitCategories,
}}
specialTokens={SPECIAL_TOKENS_STRING_CONVERTION}
/>
) : (
<PalettePicker
palettes={props.paletteService}
activePalette={localLayer?.palette}
setPalette={(newPalette) => {
setPalette(newPalette);
}}
/>
)}
</EuiFlexItem>
</EuiFlexGroup>
</div>
</PalettePanelContainer>
</EuiFormRow>
);
}

View file

@ -21473,7 +21473,6 @@
"xpack.lens.app.updatePanel": "Mettre à jour le panneau sur {originatingAppName}",
"xpack.lens.breadcrumbsEditInLensFromDashboard": "Conversion de la visualisation {title}",
"xpack.lens.chartSwitch.noResults": "Résultats introuvables pour {term}.",
"xpack.lens.configure.configurePanelTitle": "{groupLabel}",
"xpack.lens.configure.suggestedValuee": "Valeur suggérée : {value}",
"xpack.lens.confirmModal.saveDuplicateButtonLabel": "Enregistrer {name}",
"xpack.lens.datatable.visualizationOf": "Tableau {operations}",
@ -21678,7 +21677,6 @@
"xpack.lens.collapse.min": "Min.",
"xpack.lens.collapse.none": "Aucun",
"xpack.lens.collapse.sum": "Somme",
"xpack.lens.colorMapping.editColorMappingButton": "Modifier la palette",
"xpack.lens.colorMapping.editColorMappingSectionlabel": "Mapping des couleurs",
"xpack.lens.colorMapping.editColorMappingTitle": "Modifier les couleurs par mapping de terme",
"xpack.lens.colorMapping.editColorsTitle": "Modifier les couleurs",
@ -22233,12 +22231,9 @@
"xpack.lens.modalTitle.title.deleteReferenceLines": "Supprimer le calque de lignes de référence ?",
"xpack.lens.modalTitle.title.deleteVis": "Supprimer le calque de visualisation ?",
"xpack.lens.pageTitle": "Lens",
"xpack.lens.paletteHeatmapGradient.customize": "Modifier",
"xpack.lens.paletteHeatmapGradient.customizeLong": "Modifier la palette",
"xpack.lens.paletteHeatmapGradient.label": "Couleur",
"xpack.lens.paletteMetricGradient.label": "Couleur",
"xpack.lens.palettePicker.label": "Palette",
"xpack.lens.paletteTableGradient.customize": "Modifier",
"xpack.lens.paletteTableGradient.label": "Couleur",
"xpack.lens.pie.addLayer": "Visualisation",
"xpack.lens.pie.collapsedDimensionsDontCount": "(Les dimensions réduites ne sont pas concernées par cette limite.)",
@ -22348,7 +22343,7 @@
"xpack.lens.table.alignment.label": "Alignement du texte",
"xpack.lens.table.alignment.left": "Gauche",
"xpack.lens.table.alignment.right": "Droite",
"xpack.lens.table.colorByRangePanelTitle": "Couleur",
"xpack.lens.colorSiblingFlyoutTitle": "Couleur",
"xpack.lens.table.columnFilter.filterForValueText": "Filtrer sur",
"xpack.lens.table.columnFilter.filterOutValueText": "Exclure",
"xpack.lens.table.columnFilterClickLabel": "Filtrer directement avec un clic",
@ -22359,7 +22354,7 @@
"xpack.lens.table.dynamicColoring.none": "Aucun",
"xpack.lens.table.dynamicColoring.text": "Texte",
"xpack.lens.table.hide.hideLabel": "Masquer",
"xpack.lens.table.palettePanelContainer.back": "Retour",
"xpack.lens.settingWithSiblingFlyout.back": "Retour",
"xpack.lens.table.resize.reset": "Réinitialiser la largeur",
"xpack.lens.table.sort.ascLabel": "Trier dans l'ordre croissant",
"xpack.lens.table.sort.descLabel": "Trier dans l'ordre décroissant",

View file

@ -21487,7 +21487,6 @@
"xpack.lens.app.updatePanel": "{originatingAppName}でパネルを更新",
"xpack.lens.breadcrumbsEditInLensFromDashboard": "{title}ビジュアライゼーションを変換中",
"xpack.lens.chartSwitch.noResults": "{term}の結果が見つかりませんでした。",
"xpack.lens.configure.configurePanelTitle": "{groupLabel}",
"xpack.lens.configure.suggestedValuee": "候補の値:{value}",
"xpack.lens.confirmModal.saveDuplicateButtonLabel": "{name}を保存",
"xpack.lens.datatable.visualizationOf": "表{operations}",
@ -21692,7 +21691,6 @@
"xpack.lens.collapse.min": "最低",
"xpack.lens.collapse.none": "なし",
"xpack.lens.collapse.sum": "合計",
"xpack.lens.colorMapping.editColorMappingButton": "パレットを編集",
"xpack.lens.colorMapping.editColorMappingSectionlabel": "カラーマッピング",
"xpack.lens.colorMapping.editColorMappingTitle": "用語マッピングで色を編集",
"xpack.lens.colorMapping.editColorsTitle": "色を編集",
@ -22247,12 +22245,9 @@
"xpack.lens.modalTitle.title.deleteReferenceLines": "基準線レイヤーを削除しますか?",
"xpack.lens.modalTitle.title.deleteVis": "ビジュアライゼーションレイヤーを削除しますか?",
"xpack.lens.pageTitle": "レンズ",
"xpack.lens.paletteHeatmapGradient.customize": "編集",
"xpack.lens.paletteHeatmapGradient.customizeLong": "パレットを編集",
"xpack.lens.paletteHeatmapGradient.label": "色",
"xpack.lens.paletteMetricGradient.label": "色",
"xpack.lens.palettePicker.label": "パレット",
"xpack.lens.paletteTableGradient.customize": "編集",
"xpack.lens.paletteTableGradient.label": "色",
"xpack.lens.pie.addLayer": "ビジュアライゼーション",
"xpack.lens.pie.collapsedDimensionsDontCount": "(折りたたまれたディメンションはこの制限に対してカウントされません。)",
@ -22362,7 +22357,7 @@
"xpack.lens.table.alignment.label": "テキスト配置",
"xpack.lens.table.alignment.left": "左",
"xpack.lens.table.alignment.right": "右",
"xpack.lens.table.colorByRangePanelTitle": "色",
"xpack.lens.colorSiblingFlyoutTitle": "色",
"xpack.lens.table.columnFilter.filterForValueText": "フィルター",
"xpack.lens.table.columnFilter.filterOutValueText": "除外",
"xpack.lens.table.columnFilterClickLabel": "クリック時に直接フィルター",
@ -22373,7 +22368,7 @@
"xpack.lens.table.dynamicColoring.none": "なし",
"xpack.lens.table.dynamicColoring.text": "テキスト",
"xpack.lens.table.hide.hideLabel": "非表示",
"xpack.lens.table.palettePanelContainer.back": "戻る",
"xpack.lens.settingWithSiblingFlyout.back": "戻る",
"xpack.lens.table.resize.reset": "幅のリセット",
"xpack.lens.table.sort.ascLabel": "昇順に並べ替える",
"xpack.lens.table.sort.descLabel": "降順に並べ替える",

View file

@ -21580,7 +21580,6 @@
"xpack.lens.app.updatePanel": "更新 {originatingAppName} 中的面板",
"xpack.lens.breadcrumbsEditInLensFromDashboard": "正在转换 {title} 可视化",
"xpack.lens.chartSwitch.noResults": "找不到 {term} 的结果。",
"xpack.lens.configure.configurePanelTitle": "{groupLabel}",
"xpack.lens.configure.suggestedValuee": "建议值:{value}",
"xpack.lens.confirmModal.saveDuplicateButtonLabel": "保存 {name}",
"xpack.lens.datatable.visualizationOf": "表 {operations}",
@ -21785,7 +21784,6 @@
"xpack.lens.collapse.min": "最小值",
"xpack.lens.collapse.none": "无",
"xpack.lens.collapse.sum": "求和",
"xpack.lens.colorMapping.editColorMappingButton": "编辑调色板",
"xpack.lens.colorMapping.editColorMappingSectionlabel": "颜色映射",
"xpack.lens.colorMapping.editColorMappingTitle": "按词映射编辑颜色",
"xpack.lens.colorMapping.editColorsTitle": "编辑颜色",
@ -22340,12 +22338,9 @@
"xpack.lens.modalTitle.title.deleteReferenceLines": "删除参考线图层?",
"xpack.lens.modalTitle.title.deleteVis": "删除可视化图层?",
"xpack.lens.pageTitle": "Lens",
"xpack.lens.paletteHeatmapGradient.customize": "编辑",
"xpack.lens.paletteHeatmapGradient.customizeLong": "编辑调色板",
"xpack.lens.paletteHeatmapGradient.label": "颜色",
"xpack.lens.paletteMetricGradient.label": "颜色",
"xpack.lens.palettePicker.label": "调色板",
"xpack.lens.paletteTableGradient.customize": "编辑",
"xpack.lens.paletteTableGradient.label": "颜色",
"xpack.lens.pie.addLayer": "可视化",
"xpack.lens.pie.collapsedDimensionsDontCount": "(折叠的维度不计入此限制。)",
@ -22455,7 +22450,7 @@
"xpack.lens.table.alignment.label": "文本对齐",
"xpack.lens.table.alignment.left": "左",
"xpack.lens.table.alignment.right": "右",
"xpack.lens.table.colorByRangePanelTitle": "颜色",
"xpack.lens.colorSiblingFlyoutTitle": "颜色",
"xpack.lens.table.columnFilter.filterForValueText": "筛留",
"xpack.lens.table.columnFilter.filterOutValueText": "筛除",
"xpack.lens.table.columnFilterClickLabel": "单击时直接筛选",
@ -22466,7 +22461,7 @@
"xpack.lens.table.dynamicColoring.none": "无",
"xpack.lens.table.dynamicColoring.text": "文本",
"xpack.lens.table.hide.hideLabel": "隐藏",
"xpack.lens.table.palettePanelContainer.back": "返回",
"xpack.lens.settingWithSiblingFlyout.back": "返回",
"xpack.lens.table.resize.reset": "重置宽度",
"xpack.lens.table.sort.ascLabel": "升序",
"xpack.lens.table.sort.descLabel": "降序",

View file

@ -89,7 +89,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
});
it('lens datatable with palette panel open', async () => {
await PageObjects.lens.openPalettePanel('lnsDatatable');
await PageObjects.lens.openPalettePanel();
await a11y.testAppSnapshot();
});

View file

@ -71,6 +71,47 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
expect(await find.allByCssSelector('.echLegendItem')).to.have.length(4);
});
describe('dimension flyout keeping open/closing when palette is open ', () => {
it('should keep the dimension editor open when switching to a chart that moves the column to the new group', async () => {
await PageObjects.visualize.navigateToNewVisualization();
await PageObjects.visualize.clickVisType('lens');
await PageObjects.lens.goToTimeRange();
await PageObjects.lens.configureDimension({
dimension: 'lnsXY_yDimensionPanel > lns-empty-dimension',
operation: 'average',
field: 'bytes',
});
await PageObjects.lens.configureDimension({
dimension: 'lnsXY_splitDimensionPanel > lns-empty-dimension',
operation: 'terms',
field: '@message.raw',
keepOpen: true,
});
await PageObjects.lens.openPalettePanel();
await PageObjects.lens.switchToVisualization('bar');
expect(await PageObjects.lens.isDimensionEditorOpen()).to.eql(true);
});
it('should close the dimension editor when switching to a chart that removes the column', async () => {
await PageObjects.lens.closeDimensionEditor();
await PageObjects.lens.configureDimension({
dimension: 'lnsXY_splitDimensionPanel > lns-empty-dimension',
operation: 'terms',
field: '@message.raw',
keepOpen: true,
});
await PageObjects.lens.openPalettePanel();
await PageObjects.lens.switchToVisualization('lnsLegacyMetric');
expect(await PageObjects.lens.isDimensionEditorOpen()).to.eql(false);
});
});
it('should create an xy visualization with filters aggregation', async () => {
await PageObjects.visualize.gotoVisualizationLandingPage();
await listingTable.searchForItemWithName('lnsXYvis');

View file

@ -162,7 +162,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
});
it('should open the palette panel to customize the palette look', async () => {
await PageObjects.lens.openPalettePanel('lnsDatatable');
await PageObjects.lens.openPalettePanel();
await PageObjects.lens.waitForVisualization();
await PageObjects.lens.changePaletteTo('temperature');
await PageObjects.lens.waitForVisualization();

View file

@ -258,7 +258,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await PageObjects.lens.waitForVisualization('heatmapChart');
await PageObjects.lens.openDimensionEditor('lnsHeatmap_cellPanel > lns-dimensionTrigger');
await PageObjects.lens.openPalettePanel('lnsHeatmap');
await PageObjects.lens.openPalettePanel();
await testSubjects.click('lnsPalettePanel_dynamicColoring_rangeType_groups_number');
await PageObjects.header.waitUntilLoadingHasFinished();

View file

@ -66,7 +66,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
it('should reflect stop colors change on the chart', async () => {
await PageObjects.lens.openDimensionEditor('lnsHeatmap_cellPanel > lns-dimensionTrigger');
await PageObjects.lens.openPalettePanel('lnsHeatmap');
await PageObjects.lens.openPalettePanel();
await PageObjects.common.sleep(1000);
await retry.try(async () => {
await testSubjects.setValue('lnsPalettePanel_dynamicColoring_range_value_0', '10', {

View file

@ -45,7 +45,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
});
it('should change the color of the metric when tweaking the values in the panel', async () => {
await PageObjects.lens.openPalettePanel('lnsLegacyMetric');
await PageObjects.lens.openPalettePanel();
await PageObjects.header.waitUntilLoadingHasFinished();
await testSubjects.setValue('lnsPalettePanel_dynamicColoring_range_value_1', '21000', {
clearWithKeyboard: true,

View file

@ -298,7 +298,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
});
it('converts color stops to number', async () => {
await PageObjects.lens.openPalettePanel('lnsMetric');
await PageObjects.lens.openPalettePanel();
await PageObjects.common.sleep(1000);
await testSubjects.click('lnsPalettePanel_dynamicColoring_rangeType_groups_number');
expect([

View file

@ -122,7 +122,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
await dimensions[0].click();
await lens.openPalettePanel('lnsGauge');
await lens.openPalettePanel();
const colorStops = await lens.getPaletteColorStops();
expect(colorStops).to.eql([

View file

@ -215,7 +215,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
await dimensions[0].click();
await lens.openPalettePanel('lnsMetric');
await lens.openPalettePanel();
const colorStops = await lens.getPaletteColorStops();
expect(colorStops).to.eql([

View file

@ -225,7 +225,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
await dimensions[0].click();
await lens.openPalettePanel('lnsMetric');
await lens.openPalettePanel();
const colorStops = await lens.getPaletteColorStops();
expect(colorStops).to.eql([

View file

@ -103,7 +103,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
await lens.waitForVisualization('mtrVis');
await retry.try(async () => {
const closePalettePanels = await testSubjects.findAll(
'lns-indexPattern-PalettePanelContainerBack'
'lns-indexPattern-SettingWithSiblingFlyoutBack'
);
if (closePalettePanels.length) {
await lens.closePalettePanel();
@ -115,7 +115,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
await dimensions[0].click();
await lens.openPalettePanel('lnsMetric');
await lens.openPalettePanel();
const colorStops = await lens.getPaletteColorStops();
expect(colorStops).to.eql([

View file

@ -106,7 +106,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
await lens.waitForVisualization('mtrVis');
await retry.try(async () => {
const closePalettePanels = await testSubjects.findAll(
'lns-indexPattern-PalettePanelContainerBack'
'lns-indexPattern-SettingWithSiblingFlyoutBack'
);
if (closePalettePanels.length) {
await lens.closePalettePanel();
@ -118,7 +118,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
await dimensions[0].click();
await lens.openPalettePanel('lnsMetric');
await lens.openPalettePanel();
const colorStops = await lens.getPaletteColorStops();
expect(colorStops).to.eql([

View file

@ -200,7 +200,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
await lens.waitForVisualization('lnsDataTable');
await retry.try(async () => {
const closePalettePanels = await testSubjects.findAll(
'lns-indexPattern-PalettePanelContainerBack'
'lns-indexPattern-SettingWithSiblingFlyoutBack'
);
if (closePalettePanels.length) {
await lens.closePalettePanel();
@ -209,7 +209,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
await lens.openDimensionEditor('lnsDatatable_metrics > lns-dimensionTrigger');
await lens.openPalettePanel('lnsDatatable');
await lens.openPalettePanel();
const colorStops = await lens.getPaletteColorStops();
expect(colorStops).to.eql([

View file

@ -1220,17 +1220,19 @@ export function LensPageProvider({ getService, getPageObjects }: FtrProviderCont
await testSubjects.click('lnsDatatable_dynamicColoring_groups_' + coloringType);
},
async openPalettePanel(chartType: string) {
async openPalettePanel() {
await retry.try(async () => {
await testSubjects.click(`${chartType}_dynamicColoring_trigger`);
await testSubjects.click(`lns_colorEditing_trigger`);
// wait for the UI to settle
await PageObjects.common.sleep(100);
await testSubjects.existOrFail('lns-indexPattern-PalettePanelContainer', { timeout: 2500 });
await testSubjects.existOrFail('lns-indexPattern-SettingWithSiblingFlyout', {
timeout: 2500,
});
});
},
async closePalettePanel() {
await testSubjects.click('lns-indexPattern-PalettePanelContainerBack');
await testSubjects.click('lns-indexPattern-SettingWithSiblingFlyoutBack');
},
// different picker from the next one
@ -1257,8 +1259,8 @@ export function LensPageProvider({ getService, getPageObjects }: FtrProviderCont
async closePaletteEditor() {
await retry.try(async () => {
await testSubjects.click('lns-indexPattern-PalettePanelContainerBack');
await testSubjects.missingOrFail('lns-indexPattern-PalettePanelContainerBack');
await testSubjects.click('lns-indexPattern-SettingWithSiblingFlyoutBack');
await testSubjects.missingOrFail('lns-indexPattern-SettingWithSiblingFlyoutBack');
});
},

View file

@ -95,7 +95,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
await dimensions[0].click();
await lens.openPalettePanel('lnsGauge');
await lens.openPalettePanel();
const colorStops = await lens.getPaletteColorStops();
expect(colorStops).to.eql([

View file

@ -201,7 +201,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
await dimensions[0].click();
await lens.openPalettePanel('lnsMetric');
await lens.openPalettePanel();
const colorStops = await lens.getPaletteColorStops();
expect(colorStops).to.eql([

View file

@ -198,7 +198,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
await dimensions[0].click();
await lens.openPalettePanel('lnsMetric');
await lens.openPalettePanel();
const colorStops = await lens.getPaletteColorStops();
expect(colorStops).to.eql([

View file

@ -81,7 +81,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
await retry.try(async () => {
const closePalettePanels = await testSubjects.findAll(
'lns-indexPattern-PalettePanelContainerBack'
'lns-indexPattern-SettingWithSiblingFlyoutBack'
);
if (closePalettePanels.length) {
await lens.closePalettePanel();
@ -93,7 +93,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
await dimensions[0].click();
await lens.openPalettePanel('lnsMetric');
await lens.openPalettePanel();
const colorStops = await lens.getPaletteColorStops();
expect(colorStops).to.eql([

View file

@ -93,7 +93,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
await retry.try(async () => {
const closePalettePanels = await testSubjects.findAll(
'lns-indexPattern-PalettePanelContainerBack'
'lns-indexPattern-SettingWithSiblingFlyoutBack'
);
if (closePalettePanels.length) {
await lens.closePalettePanel();
@ -105,7 +105,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
await dimensions[0].click();
await lens.openPalettePanel('lnsMetric');
await lens.openPalettePanel();
const colorStops = await lens.getPaletteColorStops();
expect(colorStops).to.eql([

View file

@ -150,7 +150,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
await retry.try(async () => {
const closePalettePanels = await testSubjects.findAll(
'lns-indexPattern-PalettePanelContainerBack'
'lns-indexPattern-SettingWithSiblingFlyoutBack'
);
if (closePalettePanels.length) {
await lens.closePalettePanel();
@ -159,7 +159,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
await lens.openDimensionEditor('lnsDatatable_metrics > lns-dimensionTrigger');
await lens.openPalettePanel('lnsDatatable');
await lens.openPalettePanel();
const colorStops = await lens.getPaletteColorStops();
expect(colorStops).to.eql([