Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Lens] Categorical color palettes #75309

Merged
merged 100 commits into from
Nov 4, 2020

Conversation

flash1293
Copy link
Contributor

@flash1293 flash1293 commented Aug 18, 2020

This PR implements a general palette service in the charts plugin an exposes it in Lens to be able to pick palettes there.

This PR roughly contains three separate pieces which are linked to each other but can be reviewed separately:

Canvas changes

  • Move palette definition to the charts plugin
  • Change the shape of the palette expression data type to
{
  type: 'palette';
  name: string;
  params: unknown;
}
  • Move color expansion (getColorsFromPalette) in plot and pie functions into the charts plugin (part of the "custom" plugin)
  • Move plot and pie functions into public plugin so they can depend on the public contract of the charts plugin to pull in palette logic
  • Pass palettes into embeddable renderer to rewrite expression including a custom palette
  • Lens chart will use the palette configured in Lens if not overwritten

Screenshot 2020-10-20 at 11 59 50

Charts changes

  • Add expressions functions palette and system_palette - both produce a palette data type (palette is a shortcut function for the "custom" palette used in canvas):
{
  type: 'palette';
  name: string;
  params: unknown;
}
  • Add palette registry which can be lazily loaded using charts.palettes.getPalettes()
  • Each palette is defined by a PaletteDefinition, featuring functions for serializing it to the expression, a getter for an array of colors and a getter for an individual colors which gets a configuration passed in
  • Supported palettes:
    • default: EUI palette, differentiating between behind text and regular palette - assigned by position of the current series
    • kibana_palette: Using the vislib color assignment by series name only (will sync up with vislib charts)
    • warm/cool/positive/negative/grey/status/temperature/complimentary: Assigned by position of the current series, using full spectrum of the palette
    • custom: Using former canvas palette logic based on chrome-js or round-robin assignment

Lens changes

  • Pass down palette registry as part of frame API as availablePalettes
  • xy and pie serialize palettes in their state and put them onto the expression as well (with a fallback to a set theme variable)
  • Visualization API is extended by getMainPalette to return current main palette based on state, and an optional parameter on initialize and getSuggestions to pass it into another chart type - this is the way to hand over a palette from one chart to another one. A visualization can use multiple palettes internally
  • Xy chart allows to define a palette as part of the break down dimension per layer. The first layer palette is transferred to other chart types. If the break down dimension is deleted, it falls back to the default palette
  • Pie chart allows a single palette which is editable from within each slice dimension
  • No indicator in dimension trigger yet (this will be handled by a separate PR)

Screenshot 2020-10-20 at 12 13 53

Screenshot 2020-10-20 at 12 14 03

flash1293 and others added 30 commits June 16, 2020 15:22
…workspace_panel_wrapper.scss

Co-authored-by: Caroline Horn <[email protected]>
Copy link
Contributor

@wylieconlon wylieconlon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tested this in Chrome and reviewed the code. I have left a few comments about the code, and I found one bug that I couldn't identify the cause of. The bug is:

  1. Add any saved Lens vis to canvas
  2. Edit the expression by adding an invalid palette, something like palette={}. This will cause an error state Can not cast null to palette
  3. Delete the invalid palette and the visualization won't update, the console shows
Embeddable has been destroyed
    at Embeddable.updateInput (embeddable.tsx:134)
    ```

'#F1932D',
'#E8601C',
'#DC050C',
];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This choice of palette for the fallback doesn't make a lot of sense to me- why not default to EUI, or throw an error when missing a palette definition?

I worry that we could get stuck with a palette that can never be changed, similar to the 7-color default vislib palette. Also, the name of the palette here is Paul Tol 14

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is done for BWC with Canvas. The palette function is only used by canvas and is already in use there (with these defaults). We can think about changing this with 8.0

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see, can you add a code comment about this?

src/plugins/charts/common/palette.ts Show resolved Hide resolved
params: {
help: i18n.translate('charts.functions.systemPalette.args.paramsHelpText', {
defaultMessage: 'Palette specific params of the palette',
}),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand what this is supposed to contain- or even what the datatype is. Do we need this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not used right now, but this is the place system palettes would use that to serialize custom params. E.g.
{system_palette id="myFancyPalette" params={fancy_palette_params somethingCustom=true }}. We can also remove it for now if you think that would be better.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My preference would be to remove it until needed.

src/plugins/charts/common/palette.test.ts Show resolved Hide resolved
src/plugins/charts/common/palette.test.ts Outdated Show resolved Hide resolved
import { LegacyColorsService } from '../legacy_colors';

function buildRoundRobinCategoricalWithMappedColors(): Omit<PaletteDefinition, 'title'> {
const colors = euiPaletteColorBlind({ rotations: 2 });
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that increasing the number of EUI colors from 10 to 20 is helpful for medium-size charts, but have we showed this to others to confirm?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I discussed this approach with @AlonaNadler

@flash1293
Copy link
Contributor Author

@wylieconlon Thanks for the review - I either fixed your comments in code are answered to the comments. I checked the bug but it doesn't seem like this is caused by my changes. I did the same with the visualize embeddable and it causes the same problem there (embeddable destroyed). I think it makes sense to open a separate bug for this.

Copy link
Contributor

@wylieconlon wylieconlon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the updates, did not test again but LGTM

'#F1932D',
'#E8601C',
'#DC050C',
];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see, can you add a code comment about this?

params: {
help: i18n.translate('charts.functions.systemPalette.args.paramsHelpText', {
defaultMessage: 'Palette specific params of the palette',
}),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My preference would be to remove it until needed.

Copy link
Contributor

@MichaelMarcialis MichaelMarcialis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey, @flash1293. This looks great! I left one small comment below to use the compressed form style for the color palette picker.

Otherwise, I just had one question regarding adding Lens visualizations to Canvas workpads. I noticed that all visualizations added to Canvas workpads had transparent backgrounds by default, except for Lens visualizations (which had a white background). Is this intentional? Not sure if part of this PR, but thought I'd ask.

image

})}
>
<>
<EuiColorPalettePicker
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

EuiColorPalettePicker requires a prop of compressed in order to be displayed in the same compressed style as the rest of the form.

@flash1293
Copy link
Contributor Author

Otherwise, I just had one question regarding adding Lens visualizations to Canvas workpads. I noticed that all visualizations added to Canvas workpads had transparent backgrounds by default, except for Lens visualizations (which had a white background). Is this intentional? Not sure if part of this PR, but thought I'd ask.

@MichaelMarcialis Good point. It's not a side effect of this PR, but definitely something we should fix. Could you open a separate issue?

Copy link
Contributor

@clintandrewhall clintandrewhall left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have some feedback, but approving to unblock.

name: string;
params?: T;
}
export const defaultCustomColors = [
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
export const defaultCustomColors = [
// This set of defaults originated in Canvas, which, at present, is the primary
// consumer of this function. Changing this default requires a change in Canvas
// logic, which would likely be a breaking change in 7.x.
export const defaultCustomColors = [

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, forgot about that, thanks for the great suggestion!

src/plugins/charts/common/palette.ts Outdated Show resolved Hide resolved
import { FunctionHelp } from '../function_help';
import { FunctionFactory } from '../../../types';
import { Legend } from '../../../types';
import { CSS, FONT_FAMILY, FONT_WEIGHT, BOOLEAN_FALSE } from '../../constants';

export const help: FunctionHelp<FunctionFactory<typeof pie>> = {
export const help: FunctionHelp<FunctionFactory<ReturnType<typeof pieFunctionFactory>>> = {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: this feels very dense... might be nice to shorten this to a common type? Probably beyond the scope of this PR, though.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, I added FunctionFactoryHelp to function_help.ts

}

export function pieFunctionFactory(
initialize: InitializeArguments
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it makes more sense to pass the Palette Service to this function, rather than InitializeArguments. The contract is much more explicit.

@AlonaNadler
Copy link

Hey looks awesome!!! it works very nicely 👍
I created a really nice dashboard playing with colors
image

few questions:

  1. why can't I change the color in the treemap but if I changed it in the pie chart it persists in the treemap?
    image
  2. I saw that in 7.10 BC as well, something is wrong in the area stacked colors, the white areas it is unclear what to assume from them
    image
  3. It's probably a different PR but in the mocks, the color should be visible in the configurator without clicking on it. The reason it is important is since in some chart you configure from the breakdown by in others from the dimensions and it might be confusing, the way to solve it is to show visibly the colors palette in the dimension it can be configured
  4. Im not sure I understand how color applies on the external circle in a pie/donut chart. Should it be based on the internal ring color? should colors be by the value? so the same values have the same color? I'm ok with it as is for 7.11 but just wondering
    image

@flash1293
Copy link
Contributor Author

Thanks for the feedback!

@AlonaNadler answers to your questions:

why can't I change the color in the treemap but if I changed it in the pie chart it persists in the treemap?

Great catch, that's a bug. Should be fixed now.

I saw that in 7.10 BC as well, something is wrong in the area stacked colors, the white areas it is unclear what to assume from them

I think that's caused by series not being available on all buckets. It's not caused by this PR, could you open an elastic-charts issue for this?

It's probably a different PR but in the mocks, the color should be visible in the configurator without clicking on it. The reason it is important is since in some chart you configure from the breakdown by in others from the dimensions and it might be confusing, the way to solve it is to show visibly the colors palette in the dimension it can be configured

Yes, working on that separately. It will also be part of 7.11

Im not sure I understand how color applies on the external circle in a pie/donut chart. Should it be based on the internal ring color? should colors be by the value? so the same values have the same color? I'm ok with it as is for 7.11 but just wondering

The color is assigned based on the inner ring, then lightened up for the outer rings (like it's working right now, didn't change that behavior). Once we implement color sync for Lens (#81976), we can decide whether we want to color by value on the outer layers as well like visualize is doing it today.

@mbondyra
Copy link
Contributor

mbondyra commented Nov 3, 2020

LGTM. Tested in Safari, it's working really well!

@flash1293
Copy link
Contributor Author

@elasticmachine merge upstream

@kibanamachine
Copy link
Contributor

💚 Build Succeeded

Metrics [docs]

@kbn/optimizer bundle module count

id before after diff
canvas 1119 1116 -3
charts 39 49 +10
lens 565 558 -7
total -0

async chunk count

id before after diff
charts 0 1 +1

async chunks size

id before after diff
canvas 1.3MB 1.3MB +9.3KB
charts 0.0B 57.4KB +57.4KB
lens 1.0MB 1020.4KB -26.7KB
visTypeTimeseries 1.7MB 1.7MB +6.0B
visTypeVislib 702.0KB 702.0KB +12.0B
total +40.0KB

distributable file count

id before after diff
default 42713 42709 -4
oss 22410 22415 +5

page load bundle size

id before after diff
canvas 919.3KB 908.8KB -10.5KB
charts 134.9KB 147.9KB +13.0KB
lens 49.5KB 49.7KB +292.0B
visTypeTagcloud 19.5KB 19.6KB +6.0B
total +2.8KB

History

To update your PR or re-run it, just comment with:
@elasticmachine merge upstream

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Feature:Lens release_note:enhancement Team:Visualizations Visualization editors, elastic-charts and infrastructure v7.11.0 v8.0.0
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants