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

Improve categorical color management #5815

Merged
merged 33 commits into from
Sep 12, 2018
Merged

Conversation

kristw
Copy link
Contributor

@kristw kristw commented Sep 5, 2018

Rewrite how categorical color scales and color schemes are managed.

  • Create centralized singleton ColorSchemeManager for managing color schemes, with mechanism for registering schemes.
  • Separate color schemes into their own files.
  • Create color scale class CategoricalColorScale that can be passed around as an object and support color overriding (forcedColors).
  • Create notion of CategoricalColorNamespace which can contain one or more scales. Any color overriding (forcedColors) are scoped within the namespace. This is useful for dashboard. @graceguo-supercat - you can also add new function for serializing the namespace and use it for saving dashboard color config.
  • Color overriding now can be set at scale-level (e.g. only for scale that uses bnbColors) or namespace-level (i.e. all scales within the namespace).
  • Separate logic for forcing colors and getting colors.
  • Add shim for legacy function getColorFromScheme, which can be removed in follow-up PR.

More details please read the unit test output below which also serves as documentation.

@graceguo-supercat @williaster

Testing

  • Pass existing unit tests for color.
  • Add new unit tests for the new classes.
  CategoricalColorScale
    ✓ exists
    new CategoricalColorScale(colors, parentForcedColors)
      ✓ can create new scale when parentForcedColors is not given
      ✓ can create new scale when parentForcedColors is given
    .getColor(value)
      ✓ returns same color for same value
      ✓ returns different color for consecutive items
      ✓ recycles colors when number of items exceed available colors
    .setColor(value, forcedColor)
      ✓ overrides default color
      ✓ does not override parentForcedColors
      ✓ returns the scale
    .toFunction()
      ✓ returns a function that wraps getColor
  CategoricalColorNamespace
    ✓ The class constructor cannot be accessed directly
    static getNamespace()
      ✓ returns default namespace if name is not specified
      ✓ returns namespace with specified name
      ✓ returns existing instance if the name already exists
    .getScale()
      ✓ returns a CategoricalColorScale from given scheme name
      ✓ returns same scale if the scale with that name already exists in this namespace
    .setColor()
      ✓ overwrites color for all CategoricalColorScales in this namespace
      ✓ can override forcedColors in each scale
      ✓ does not affect scales in other namespaces
      ✓ returns the namespace instance
    static getScale()
      ✓ getScale() returns a CategoricalColorScale with default scheme in default namespace
      ✓ getScale(scheme) returns a CategoricalColorScale with specified scheme in default namespace
      ✓ getScale(scheme, namespace) returns a CategoricalColorScale with specified scheme in specified namespace
    static getColor()
      ✓ getColor(value) returns a color from default scheme in default namespace
      ✓ getColor(value, scheme) returns a color from specified scheme in default namespace
      ✓ getColor(value, scheme, namespace) returns a color from specified scheme in specified namespace
  ColorSchemeManager
    ✓ The class constructor cannot be accessed directly
    static getInstance()
      ✓ returns a singleton instance
    .getScheme()
      ✓ .getScheme() returns default color scheme
      ✓ .getScheme(name) returns color scheme with specified name
    .getAllSchemes()
      ✓ returns all registered schemes
    .getDefaultSchemeName()
      ✓ returns default scheme name
    .setDefaultSchemeName()
      ✓ set default scheme name
      ✓ returns the ColorSchemeManager instance
    .registerScheme(name, colors)
      ✓ sets schemename and color
      ✓ returns the ColorSchemeManager instance
    .registerMultipleSchemes(object)
      ✓ sets multiple schemes at once
      ✓ returns the ColorSchemeManager instance
    static getScheme()
      ✓ is equivalent to getInstance().getScheme()
    static getAllSchemes()
      ✓ is equivalent to getInstance().getAllSchemes()
    static getDefaultSchemeName()
      ✓ is equivalent to getInstance().getDefaultSchemeName()
    static setDefaultSchemeName()
      ✓ is equivalent to getInstance().setDefaultSchemeName()
    static registerScheme()
      ✓ is equivalent to getInstance().registerScheme()
    static registerMultipleSchemes()
      ✓ is equivalent to getInstance().registerMultipleSchemes()

@codecov-io
Copy link

codecov-io commented Sep 5, 2018

Codecov Report

Merging #5815 into master will increase coverage by 0.09%.
The diff coverage is 73.03%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #5815      +/-   ##
==========================================
+ Coverage   63.73%   63.83%   +0.09%     
==========================================
  Files         374      381       +7     
  Lines       23325    23446     +121     
  Branches     2607     2614       +7     
==========================================
+ Hits        14867    14966      +99     
- Misses       8445     8467      +22     
  Partials       13       13
Impacted Files Coverage Δ
superset/assets/src/visualizations/chord.jsx 0% <0%> (ø) ⬆️
superset/assets/src/common.js 0% <0%> (ø) ⬆️
superset/assets/src/visualizations/partition.js 0% <0%> (ø) ⬆️
...t/assets/src/dashboard/reducers/getInitialState.js 0% <0%> (ø) ⬆️
superset/assets/src/visualizations/sunburst.js 0% <0%> (ø) ⬆️
...t/assets/src/visualizations/wordcloud/WordCloud.js 0% <0%> (ø) ⬆️
...sualizations/deckgl/CategoricalDeckGLContainer.jsx 0% <0%> (ø) ⬆️
superset/assets/src/visualizations/rose.js 0% <0%> (ø) ⬆️
superset/assets/src/visualizations/treemap.js 0% <0%> (ø) ⬆️
superset/assets/src/visualizations/sankey.js 0% <0%> (ø) ⬆️
... and 18 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update c82cea3...1cf4695. Read the comment docs.

@kristw kristw changed the title Improve categorical color management [WIP] Improve categorical color management Sep 8, 2018
@kristw kristw changed the title [WIP] Improve categorical color management Improve categorical color management Sep 11, 2018
Copy link
Contributor

@williaster williaster left a comment

Choose a reason for hiding this comment

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

This looks really great overall! thanks for improving the color model significantly 🥇

Left a couple of pretty minor comments.

export default class CategoricalColorScale {
constructor(colors, sharedForcedColors) {
this.colors = colors;
this.sharedForcedColors = sharedForcedColors;
Copy link
Contributor

Choose a reason for hiding this comment

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

could you add a comment that clarifies the distinction between sharedForcedColors and forcedColors?

from reading it seems like

  1. sharedForcedColors takes precedence over forcedColos if present and
  2. sharedForcedColors is never modified vs forcedColors which is

Copy link
Contributor Author

Choose a reason for hiding this comment

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

sharedForcedColors is an optional parameter that comes from parent (usually CategoricalColorNamespace) and supersede this.forcedColors. This let us do the namespace-level color override across all scales.

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 added more comments in the code.

@@ -0,0 +1,58 @@
import { TIME_SHIFT_PATTERN } from '../utils/common';

export function cleanValue(value) {
Copy link
Contributor

Choose a reason for hiding this comment

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

could be in it's own file but don't feel strongly

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 agree it should not be here at all but have to track down which component really need this cleaning. can be tricky

Copy link
Contributor Author

@kristw kristw Sep 12, 2018

Choose a reason for hiding this comment

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

This might be mostly nvd3 so let's keep it here for now and I will get rid of it in a follow-up PR.

}

const namespaces = {};
const DEFAULT_NAMESPACE = 'GLOBAL';
Copy link
Contributor

@williaster williaster Sep 11, 2018

Choose a reason for hiding this comment

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

any use in exporting this so you could import it for tests? and/or adding color somewhere in the value for easier debugging.

expect(c1).to.equal(c3);
expect(c2).to.equal(c5);
});
it('returns different color for consecutive items', () => {
Copy link
Contributor

Choose a reason for hiding this comment

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

do you think it's worthwhile to test color value recycling logic if # items > # colors?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Great idea. Just add another test for this.


// These registration code eventually should go into per-app configuration
// when we migrate to the plug-in system.
getInstance()
Copy link
Contributor

Choose a reason for hiding this comment

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

would it be worth moving it to the appSetup call in common.js?

@kristw
Copy link
Contributor Author

kristw commented Sep 12, 2018

Addressed comments and rename sharedForceColors to parentForcedColors

Copy link
Contributor

@williaster williaster left a comment

Choose a reason for hiding this comment

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

nice! this LGTM! 🌈

@kristw
Copy link
Contributor Author

kristw commented Sep 12, 2018 via email

@williaster williaster merged commit f482a6c into apache:master Sep 12, 2018
@kristw kristw deleted the kristw-colors branch September 12, 2018 21:17
betodealmeida pushed a commit to lyft/incubator-superset that referenced this pull request Oct 12, 2018
* Create new classes for handling categorical colors

* verify to pass existing unit tests

* separate logic for forcing color and getting color

* replace getColorFromScheme with CategoricalColorManager

* organize static functions

* migrate to new function

* Remove ALL_COLOR_SCHEMES

* move sequential colors to another file

* extract categorical colors to separate file

* move airbnb and lyft colors to separate files

* fix missing toFunction()

* Rewrite to support local and global force items, plus namespacing.

* fix references

* revert nvd3

* update namespace api

* Update the visualizations

* update usage with static functions

* update unit test

* add unit test

* rename default namespace

* add unit test for color namespace

* add unit test for namespace

* start unit test for colorschememanager

* add unit tests for color scheme manager

* check returns for chaining

* complete unit test for the new classes

* fix color tests

* update unit tests

* update unit tests

* move color scheme registration to common

* update unit test

* rename sharedForcedColors to parentForcedColors

* remove import
wenchma pushed a commit to wenchma/incubator-superset that referenced this pull request Nov 16, 2018
* Create new classes for handling categorical colors

* verify to pass existing unit tests

* separate logic for forcing color and getting color

* replace getColorFromScheme with CategoricalColorManager

* organize static functions

* migrate to new function

* Remove ALL_COLOR_SCHEMES

* move sequential colors to another file

* extract categorical colors to separate file

* move airbnb and lyft colors to separate files

* fix missing toFunction()

* Rewrite to support local and global force items, plus namespacing.

* fix references

* revert nvd3

* update namespace api

* Update the visualizations

* update usage with static functions

* update unit test

* add unit test

* rename default namespace

* add unit test for color namespace

* add unit test for namespace

* start unit test for colorschememanager

* add unit tests for color scheme manager

* check returns for chaining

* complete unit test for the new classes

* fix color tests

* update unit tests

* update unit tests

* move color scheme registration to common

* update unit test

* rename sharedForcedColors to parentForcedColors

* remove import
@mistercrunch mistercrunch added 🏷️ bot A label used by `supersetbot` to keep track of which PR where auto-tagged with release labels 🚢 0.28.0 labels Feb 27, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
🏷️ bot A label used by `supersetbot` to keep track of which PR where auto-tagged with release labels 🚢 0.28.0
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants