Skip to content

Commit

Permalink
RELATED: ONE-3353 Support color palette in SDK visualizations
Browse files Browse the repository at this point in the history
  • Loading branch information
Peter Lacko authored and ivan-nejezchleb committed Oct 12, 2018
1 parent f3dc133 commit 6205f6d
Show file tree
Hide file tree
Showing 31 changed files with 612 additions and 179 deletions.
5 changes: 4 additions & 1 deletion src/components/core/base/BaseChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {
import { ChartPropTypes, Requireable } from '../../../proptypes/Chart';
import { BaseVisualization } from './BaseVisualization';
import { OnLegendReady } from '../../../interfaces/Events';
import { getValidColorPalette } from '../../visualizations/utils/color';
export { Requireable };

export interface ICommonChartProps extends ICommonVisualizationProps {
Expand Down Expand Up @@ -61,7 +62,9 @@ export class StatelessBaseChart extends BaseVisualization<IBaseChartProps & ILoa
pushData
} = this.props;

const fullConfig = { ...config, type };
const colorPalette = getValidColorPalette(config);

const fullConfig = { ...config, type, colorPalette };

return (
<IntlWrapper locale={locale}>
Expand Down
15 changes: 15 additions & 0 deletions src/components/visualizations/chart/Chart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ export interface IChartLimits {

export interface IChartConfig {
colors?: string[];
colorPalette?: IColorPalette;
type?: VisType;
legend?: ILegendConfig;
legendLayout?: string;
Expand Down Expand Up @@ -73,6 +74,20 @@ export interface IChartProps {
callback(): void;
}

export interface IColorPaletteItem {
guid: string;
fill: {
r: number;
g: number;
b: number;
};
}

export interface IColorPalette {
[index: number]: IColorPaletteItem;
length: number;
}

export default class Chart extends React.Component<IChartProps> {
public static defaultProps: Partial<IChartProps> = {
callback: noop,
Expand Down
2 changes: 1 addition & 1 deletion src/components/visualizations/chart/chartOptionsBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1383,7 +1383,7 @@ export function getChartOptions(
invariant(measureGroup, 'missing measureGroup');

const colorStrategy = ColorFactory.getColorStrategy(
config.colors,
config.colorPalette,
measureGroup,
viewByAttribute,
stackByAttribute,
Expand Down
33 changes: 17 additions & 16 deletions src/components/visualizations/chart/colorFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ import {
DEFAULT_COLOR_PALETTE,
HEATMAP_BLUE_COLOR_PALETTE,
getLighterColor,
normalizeColorToRGB
normalizeColorToRGB,
getRgbString
} from '../utils/color';

import {
Expand All @@ -21,12 +22,12 @@ import {
isDerivedMeasure,
findParentMeasureIndex
} from './chartOptionsBuilder';
import { IColorPalette } from './Chart';

export interface IColorStrategy {
getColorByIndex(index: number): string;
}
export type HighChartColorPalette = string[];
export type ColorPalette = string[];
export type MeasureGroupType = Execution.IMeasureGroupHeader['measureGroupHeader'];
export const attributeChartSupportedTypes = [
VisualizationTypes.PIE,
Expand All @@ -39,7 +40,7 @@ export const attributeChartSupportedTypes = [
export abstract class ColorStrategy implements IColorStrategy {
protected palette: HighChartColorPalette;
constructor(
colorPalette: ColorPalette,
colorPalette: IColorPalette,
measureGroup: MeasureGroupType,
viewByAttribute: any,
stackByAttribute: any,
Expand All @@ -58,17 +59,18 @@ export abstract class ColorStrategy implements IColorStrategy {
}

protected abstract createPalette(
colorPalette: ColorPalette,
colorPalette: IColorPalette,
measureGroup: MeasureGroupType,
viewByAttribute: any,
stackByAttribute: any,
afm: AFM.IAfm
): HighChartColorPalette;
}
const emptyColorPaletteItem = { guid: 'none', fill: { r: 0, g: 0, b: 0 } };

export class MeasureColorStrategy extends ColorStrategy {
protected createPalette(
colorPalette: ColorPalette,
colorPalette: IColorPalette,
measureGroup: MeasureGroupType,
_viewByAttribute: any,
_stackByAttribute: any,
Expand All @@ -78,7 +80,7 @@ export class MeasureColorStrategy extends ColorStrategy {

const paletteMeasures = range(measureGroup.items.length).map((measureItemIndex) => {
if (isDerivedMeasure(measureGroup.items[measureItemIndex], afm)) {
return '';
return emptyColorPaletteItem;
}
const colorIndex = parentMeasuresCounter % colorPalette.length;
parentMeasuresCounter++;
Expand All @@ -87,29 +89,28 @@ export class MeasureColorStrategy extends ColorStrategy {

return paletteMeasures.map((color, measureItemIndex) => {
if (!isDerivedMeasure(measureGroup.items[measureItemIndex], afm)) {
return color;
return getRgbString(color);
}
const parentMeasureIndex = findParentMeasureIndex(afm, measureItemIndex);
if (parentMeasureIndex > -1) {
const sourceMeasureColor = paletteMeasures[parentMeasureIndex];
return getLighterColor(normalizeColorToRGB(sourceMeasureColor), 0.6);
return getLighterColor(normalizeColorToRGB(getRgbString(sourceMeasureColor)), 0.6);
}
return color;

return getRgbString(color);
});
}
}

export class AttributeColorStrategy extends ColorStrategy {
protected createPalette(
colorPalette: ColorPalette,
colorPalette: IColorPalette,
_measureGroup: MeasureGroupType,
viewByAttribute: any,
stackByAttribute: any,
_afm: AFM.IAfm
): HighChartColorPalette {
const itemsCount = stackByAttribute ? stackByAttribute.items.length : viewByAttribute.items.length;
return range(itemsCount).map(itemIndex => colorPalette[itemIndex % colorPalette.length]);
return range(itemsCount).map(itemIndex => getRgbString(colorPalette[itemIndex % colorPalette.length]));
}
}

Expand All @@ -119,7 +120,7 @@ export class HeatMapColorStrategy extends ColorStrategy {
}

protected createPalette(
_colorPalette: ColorPalette,
_colorPalette: IColorPalette,
_measureGroup: MeasureGroupType,
_viewByAttribute: any,
_stackByAttribute: any,
Expand All @@ -131,15 +132,15 @@ export class HeatMapColorStrategy extends ColorStrategy {

export class TreeMapColorStrategy extends MeasureColorStrategy {
protected createPalette(
colorPalette: ColorPalette,
colorPalette: IColorPalette,
measureGroup: MeasureGroupType,
viewByAttribute: any,
stackByAttribute: any,
afm: AFM.IAfm
): HighChartColorPalette {
if (viewByAttribute) {
const itemsCount = viewByAttribute.items.length;
return range(itemsCount).map(itemIndex => colorPalette[itemIndex % colorPalette.length]);
return range(itemsCount).map(itemIndex => getRgbString(colorPalette[itemIndex % colorPalette.length]));
}
return super.createPalette(
colorPalette,
Expand All @@ -158,7 +159,7 @@ export function isAttributeColorPalette(type: string, afm: AFM.IAfm, stackByAttr

export class ColorFactory {
public static getColorStrategy(
colorPalette: ColorPalette = DEFAULT_COLOR_PALETTE,
colorPalette: IColorPalette = DEFAULT_COLOR_PALETTE,
measureGroup: MeasureGroupType,
viewByAttribute: any,
stackByAttribute: any,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@ import {
} from '../dataLabelsColors';

import {
DEFAULT_COLOR_PALETTE
DEFAULT_COLOR_PALETTE,
getRgbString
} from '../../../../utils/color';
import { IColorPaletteItem } from '../../../Chart';

describe('dataLabelsColors', () => {
describe('isWhiteNotContrastEnough', () => {
Expand All @@ -19,7 +21,8 @@ describe('dataLabelsColors', () => {

it('should fullfill UX requirement for default color palette', () => {
const result: boolean[] = DEFAULT_COLOR_PALETTE
.map((defaultColor: string) => isWhiteNotContrastEnough(defaultColor));
.map((defaultColorPaletteItem: IColorPaletteItem) => getRgbString(defaultColorPaletteItem))
.map((defaultColor: string) => isWhiteNotContrastEnough(defaultColor));

// first 17 colors should return false -> have white label
const expectedValues = new Array(20).fill(false);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
// (C) 2007-2018 GoodData Corporation
import * as React from 'react';
import { shallow, mount } from 'enzyme';
import noop = require('lodash/noop');

import ChartTransformation from '../ChartTransformation';
import * as fixtures from '../../../../../stories/test_data/fixtures';
import { TOP } from '../legend/PositionTypes';
import HighChartsRenderer from '../HighChartsRenderer';
import noop = require('lodash/noop');
import { IChartConfig } from '../Chart';
import { IChartConfig, IColorPaletteItem } from '../Chart';
import { getRgbString } from '../../utils/color';

describe('ChartTransformation', () => {
const defaultProps = {
Expand Down Expand Up @@ -35,22 +37,39 @@ describe('ChartTransformation', () => {
});

it('should use custom color palette', () => {
let colorPalette;
const customColors = ['#000000', '#ff0000'];
let colors: string[] = [];
const customColorPalette = [{
guid: 'black',
fill: {
r: 0,
g: 0,
b: 0
}
}, {
guid: 'red',
fill: {
r: 255,
g: 0,
b: 0
}
}];
const renderer = (params: any) => {
colorPalette = params.chartOptions.data.series.map((serie: any) => serie.color);
colors = params.chartOptions.data.series.map((serie: any) => serie.color);
return <div />;
};
const componentProps = {
renderer,
...fixtures.barChartWithStackByAndViewByAttributes,
config: {
...defaultProps.config,
colors: customColors
colorPalette: customColorPalette
}
};
mount(createComponent(componentProps));
expect(colorPalette).toEqual(customColors);
expect(colors).toEqual(
customColorPalette
.map((colorPaletteItem: IColorPaletteItem) => getRgbString(colorPaletteItem))
);
});

describe('Stacking config', () => {
Expand Down
Loading

0 comments on commit 6205f6d

Please sign in to comment.