Skip to content

Commit

Permalink
[Lens] Color mapping UX refactoring (elastic#175144)
Browse files Browse the repository at this point in the history
This commit revisits the UX for the Lens color mapping applying a
slightly different UX behaviour to allow looping of colors from a chosen
palette.
  • Loading branch information
markov00 authored and fkanout committed Mar 4, 2024
1 parent 8b16c7a commit 8e1a2e9
Show file tree
Hide file tree
Showing 36 changed files with 1,434 additions and 1,134 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,122 +6,115 @@
* Side Public License, v 1.
*/

import React, { FC } from 'react';
import { EuiFlyout, EuiForm } from '@elastic/eui';
import React, { FC, useState } from 'react';
import { EuiFlyout, EuiForm, EuiPage, isColorDark } from '@elastic/eui';
import { ComponentStory } from '@storybook/react';
import { css } from '@emotion/react';
import { CategoricalColorMapping, ColorMappingProps } from '../categorical_color_mapping';
import { AVAILABLE_PALETTES } from '../palettes';
import { AVAILABLE_PALETTES, getPalette, NeutralPalette } from '../palettes';
import { DEFAULT_COLOR_MAPPING_CONFIG } from '../config/default_color_mapping';
import { ColorMapping } from '../config';
import { getColorFactory } from '../color/color_handling';
import { ruleMatch } from '../color/rule_matching';
import { getValidColor } from '../color/color_math';

export default {
title: 'Color Mapping',
component: CategoricalColorMapping,
decorators: [
(story: Function) => (
<EuiFlyout style={{ width: 350, padding: '8px' }} onClose={() => {}} hideCloseButton>
<EuiForm>{story()}</EuiForm>
</EuiFlyout>
),
],
decorators: [(story: Function) => story()],
};

const Template: ComponentStory<FC<ColorMappingProps>> = (args) => (
<CategoricalColorMapping {...args} />
);
const Template: ComponentStory<FC<ColorMappingProps>> = (args) => {
const [updatedModel, setUpdateModel] = useState<ColorMapping.Config>(
DEFAULT_COLOR_MAPPING_CONFIG
);

const getPaletteFn = getPalette(AVAILABLE_PALETTES, NeutralPalette);

const colorFactory = getColorFactory(updatedModel, getPaletteFn, false, args.data);

return (
<EuiPage>
<ol>
{args.data.type === 'categories' &&
args.data.categories.map((c, i) => {
const match = updatedModel.assignments.some(({ rule }) => {
return ruleMatch(rule, c);
});
const color = colorFactory(c);
const isDark = isColorDark(...getValidColor(color).rgb());

return (
<li
key={i}
css={css`
width: ${100 + 200 * Math.abs(Math.cos(i))}px;
height: 30px;
margin: 2px;
padding: 5px;
background: ${color};
color: ${isDark ? 'white' : 'black'};
border: ${match ? '2px solid black' : 'none'};
font-weight: ${match ? 'bold' : 'normal'};
`}
>
{c}
</li>
);
})}
</ol>
<EuiFlyout
style={{ width: 350, minInlineSize: 366, padding: '8px', overflow: 'auto' }}
onClose={() => {}}
hideCloseButton
ownFocus={false}
>
<EuiForm>
<CategoricalColorMapping {...args} onModelUpdate={setUpdateModel} />
</EuiForm>
</EuiFlyout>
</EuiPage>
);
};
export const Default = Template.bind({});

Default.args = {
model: {
...DEFAULT_COLOR_MAPPING_CONFIG,
assignmentMode: 'manual',

colorMode: {
type: 'gradient',
steps: [
{
type: 'categorical',
colorIndex: 0,
paletteId: DEFAULT_COLOR_MAPPING_CONFIG.paletteId,
touched: false,
},
{
type: 'categorical',
colorIndex: 1,
paletteId: DEFAULT_COLOR_MAPPING_CONFIG.paletteId,
touched: false,
},
{
type: 'categorical',
colorIndex: 2,
paletteId: DEFAULT_COLOR_MAPPING_CONFIG.paletteId,
touched: false,
},
],
sort: 'asc',
type: 'categorical',
},
assignments: [
{
rule: {
type: 'matchExactly',
values: ['this is', 'a multi-line combobox that is very long and that will be truncated'],
},
color: {
type: 'gradient',
},
touched: false,
},
{
rule: {
type: 'matchExactly',
values: ['b', ['double', 'value']],
},
color: {
type: 'gradient',
},
touched: false,
},
{
rule: {
type: 'matchExactly',
values: ['c'],
},
color: {
type: 'gradient',
},
touched: false,
},
specialAssignments: [
{
rule: {
type: 'matchExactly',
values: [
'this is',
'a multi-line wrap',
'combo box',
'test combo',
'3 lines',
['double', 'value'],
],
type: 'other',
},
color: {
type: 'gradient',
type: 'loop',
},
touched: false,
},
],
assignments: [],
},
isDarkMode: false,
data: {
type: 'categories',
categories: [
'a',
'b',
'c',
'd',
'this is',
'a multi-line wrap',
'combo box',
'test combo',
'3 lines',
'US',
'Mexico',
'Brasil',
'Canada',
'Italy',
'Germany',
'France',
'Spain',
'UK',
'Portugal',
'Greece',
'Sweden',
'Finland',
],
},

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@ import { AVAILABLE_PALETTES } from './palettes';
import { DEFAULT_COLOR_MAPPING_CONFIG } from './config/default_color_mapping';
import { MULTI_FIELD_KEY_SEPARATOR } from '@kbn/data-plugin/common';

const AUTO_ASSIGN_SWITCH = '[data-test-subj="lns-colorMapping-autoAssignSwitch"]';
const ASSIGNMENTS_LIST = '[data-test-subj="lns-colorMapping-assignmentsList"]';
const ASSIGNMENTS_PROMPT = '[data-test-subj="lns-colorMapping-assignmentsPrompt"]';
const ASSIGNMENTS_PROMPT_ADD_ALL = '[data-test-subj="lns-colorMapping-assignmentsPromptAddAll"]';
const ASSIGNMENT_ITEM = (i: number) => `[data-test-subj="lns-colorMapping-assignmentsItem${i}"]`;

describe('color mapping', () => {
Expand All @@ -35,19 +36,12 @@ describe('color mapping', () => {
/>
);

expect(component.find(AUTO_ASSIGN_SWITCH).hostNodes().prop('aria-checked')).toEqual(true);
expect(component.find(ASSIGNMENTS_LIST).hostNodes().children().length).toEqual(
dataInput.categories.length
);
dataInput.categories.forEach((category, index) => {
const assignment = component.find(ASSIGNMENT_ITEM(index)).hostNodes();
expect(assignment.text()).toEqual(category);
expect(assignment.hasClass('euiComboBox-isDisabled')).toEqual(true);
});
// empty list prompt visible
expect(component.find(ASSIGNMENTS_PROMPT)).toBeTruthy();
expect(onModelUpdateFn).not.toBeCalled();
});

it('switch to manual assignments', () => {
it('Add all terms to assignments', () => {
const dataInput: ColorMappingInputData = {
type: 'categories',
categories: ['categoryA', 'categoryB'],
Expand All @@ -63,9 +57,8 @@ describe('color mapping', () => {
specialTokens={new Map()}
/>
);
component.find(AUTO_ASSIGN_SWITCH).hostNodes().simulate('click');
component.find(ASSIGNMENTS_PROMPT_ADD_ALL).hostNodes().simulate('click');
expect(onModelUpdateFn).toBeCalledTimes(1);
expect(component.find(AUTO_ASSIGN_SWITCH).hostNodes().prop('aria-checked')).toEqual(false);
expect(component.find(ASSIGNMENTS_LIST).hostNodes().children().length).toEqual(
dataInput.categories.length
);
Expand Down Expand Up @@ -97,6 +90,7 @@ describe('color mapping', () => {
}
/>
);
component.find(ASSIGNMENTS_PROMPT_ADD_ALL).hostNodes().simulate('click');
expect(component.find(ASSIGNMENTS_LIST).hostNodes().children().length).toEqual(
dataInput.categories.length
);
Expand Down
Loading

0 comments on commit 8e1a2e9

Please sign in to comment.