React-vis: color quirks

Created on 22 Jun 2017  路  5Comments  路  Source: uber/react-vis

color sometimes works in strange and mysterious ways.

Categorical or linear scales don't work at the series level, except for lineSeries.
ie

<XYPlot height={200} width={200} colorType='category' colorDomain={[0, 1, 2]} colorRange={['yellow', 'blue', 'red']}>
<VerticalBarSeries data={data} color={0} />
</XYPlot> 

or

<XYPlot height={200} width={200} colorType='category' colorDomain={[0, 1]} colorRange={[ 'blue', 'red']}>
<VerticalBarSeries data={data} color={0.5} />
</XYPlot> 

In both cases, the series will appear black. A scale will be created which will return an undefined value and override the defaults.

bug

Most helpful comment

Is there any progress on this issue? I'm using a MarkSerie and everytime I try to set a color string (such as rgb or hex) it appears black no matter what.

All 5 comments

BarSeries and MarkSeries appear black in documentation.

I think I'm experiencing the same issue here: https://codepen.io/anon/pen/PaxBaP?editors=0010

I'm having the same issue with a VerticalBarSeries
(NB: <FlexibleXYPlot> can be swapped out for XYPlot)

<FlexibleXYPlot height={350} xType="ordinal">
   <VerticalBarSeries animation cluster="things" data={data} />
</FlexibleXYPlot>
  • I'm setting a color property on each data list item to "green" (aka rgb(0, 128, 0))
  • <VerticalBarSeries> imports <BarSeries> which maps over every item in the data list
  • It applies these properties in a style object:
    js style: { opacity: opacityFunctor && opacityFunctor(d), stroke: strokeFunctor && strokeFunctor(d), fill: fillFunctor && fillFunctor(d), ...style },
  • The fill and stroke properties come from
    js this._getAttributeFunctor('fill') || this._getAttributeFunctor('color');
    and
    js this._getAttributeFunctor('stroke') || this._getAttributeFunctor('color');
    respectively.
  • this._getAttributeFunctor is defined in <AbstractSeries> which <BarSeries> extends.
  • It returns getAttributeFunctor(this.props, attr), where getAttributeFunctor is defined in utils/scales-utils
  • The props are plentiful, and the important bits look like this:
    js { ... colorDomain: (2) ["green", "green"], colorRange: (2) ["#EF5D28", "#FF9833"], data: [...], ... }
  • The getAttributeFunctor function from utils/scales-utils looks like this:
    js export function getAttributeFunctor(props, attr) { const scaleObject = getScaleObjectFromProps(props, attr); if (scaleObject) { const scaleFn = getScaleFnFromScaleObject(scaleObject); return d => scaleFn(_getAttrValue(d, scaleObject.accessor)); } return null; }
  • getScaleObjectFromProps(props, attr) calls _collectScaleObjectFromProps with the same args.
  • This creates a scaleObject for use with the underlying d3 libs from the props:

    const {
    [attr]: value,
    [`_${attr}Value`]: fallbackValue,
    [`${attr}Range`]: range,  // !!
    [`${attr}Distance`]: distance = 0,
    [`${attr}BaseValue`]: baseValue,
    [`${attr}Type`]: type = LINEAR_SCALE_TYPE,
    [`${attr}NoFallBack`]: noFallBack,
    [`get${toTitleCase(attr)}`]: accessor = d => d[attr],
    [`get${toTitleCase(attr)}0`]: accessor0 = d => d[`${attr}0`]
    } = props;
    
    let {[`${attr}Domain`]: domain} = props; // !!
    

    In my case the important ones are colorRange and colorDomain

  • I end up with an scaleObject that looks like the following:
    js { accessor: 茠 (d), accessor0: 茠 (d), attr: "color", baseValue: undefined, distance: 0, domain: (2) ["rgb(0, 128, 0)", "rgb(0, 128, 0)"], isValue: false, range: (2) ["rgb(239, 93, 40)", "rgb(255, 152, 51)"], type: "linear" }
  • Back in the getAttributeFunctor function, the scaleObject is returned
  • Now getAttributeFunctor calls getScaleFnFromScaleObject in
    js if (scaleObject) { const scaleFn = getScaleFnFromScaleObject(scaleObject); return d => scaleFn(_getAttrValue(d, attr)); }
    The returned function is going to be called with the data list (d) that is passed in as a prop to the VerticalBarSeries as <VerticalBarSeries ... data={data} /> (remember opacity: opacityFunctor && opacityFunctor(d) from <BarSeries>)
  • The _getAttrValue(d, scaleObject.accessor) returns the expected "green" value in RGB form: rgb(0, 128, 0)
    But when this is passed to the scaleFn (which comes from d3), it always returns rgb(0, 0, 0), and it is this value that gets applied to the stroke and fill properties in the data.map within BarSeries (explaining the black bars).
    js { opacity: 1, stroke: "rgb(0, 0, 0)", fill: "rgb(0, 0, 0)" }

What I can't figure out is _why_ the scaleFn always returns rgb(0, 0, 0) (black).

image

It might be a problem with this function in utils/scales-utils:

export function getScaleFnFromScaleObject(scaleObject) {
  ...

  const { type, domain, range } = scaleObject;

  /*
    My data is: 
    {
      attr: "color",
      baseValue: undefined,
      ...
      domain: (2) ["rgb(0, 128, 0)", "rgb(0, 128, 0)"],
      ...
      range: (2) ["rgb(239, 93, 40)", "rgb(255, 152, 51)"],
      type: "linear"
    }
  */

  const modDomain =
    ( domain[0] === domain[1] ) ? (
      ( domain[0] === 0 ) ? [-1, 0] : [-domain[0], domain[0]]
    ) : domain;

  /*
    My modDomain is: 
    [NaN, "rgb(0, 128, 0)"]

    My range (from above) is:
    ["rgb(239, 93, 40)", "rgb(255, 152, 51)"]
  */

  ...

  const scale = SCALE_FUNCTIONS[type]()
    .domain(modDomain)
    .range(range);

  ...

  return scale;
}

That NaN in [NaN, "rgb(0, 128, 0)"] looks pretty suspect, caused by trying to get a negative value of a string in [-domain[0], domain[0]] (aka [-"rgb(0, 128, 0)", "rgb(0, 128, 0)"])

Is there any progress on this issue? I'm using a MarkSerie and everytime I try to set a color string (such as rgb or hex) it appears black no matter what.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

cvalmonte picture cvalmonte  路  4Comments

martoncsikos picture martoncsikos  路  4Comments

yennycheung picture yennycheung  路  4Comments

mcnuttandrew picture mcnuttandrew  路  5Comments

JayFields picture JayFields  路  5Comments