diff --git a/docs/configuration-customizations.md b/docs/configuration-customizations.md index 2e564be8a..21c92c6ae 100644 --- a/docs/configuration-customizations.md +++ b/docs/configuration-customizations.md @@ -8,9 +8,62 @@ layout: page You can define a `customization:` section in the config to configure some aspects of the look and feel of Turnilo. -## Visual +## Theming Turnilo -Can customize the header background color and logo icon by supplying a color string and SVG string respectively. +Turnilo allows you to customize colors of user interface. You should keep in sync values in all subsections here. +For example brand CSS variable should match main visualization color. + +### CSS Variables + +Turnilo allows you to override CSS variables to apply your own theming + +For example: + +```yaml +customization: + cssVariables: + brand: '#829aa3;' + item-dimension: '#f2cee0;' + item-dimension-text: white; + item-measure: '#cef2e0;' + item-measure-text: white; + background-brand: white; + background-brand-text: '#999;' + background-base: '#fbfbfb;' +``` + +### Visualisation colors + +Turnilo allows you to override colors for charts to apply your own theming. Default values for each field are defined in [colors.ts](https://github.com/allegro/turnilo/blob/master/src/common/models/colors/colors.ts). + +* `main` property is used for drawing marks (lines, bars, points etc.) whenever Turnilo draws single series. + +* `series` is an array of colors used for drawing different series marks. For example line chart with two splits will use `series` colors to distinguish different values from second split. + +For example, we can override main color and use [Tableu10](https://www.tableau.com/blog/colors-upgrade-tableau-10-56782) color scheme for series: + +```yaml +customizaiton: + visualizationColors: + main: #829aa3 + series: + - #4e79a7 + - #f28e2c + - #e15759 + - #76b7b2 + - #59a14f + - #edc949 + - #af7aa1 + - #ff9da7 + - #9c755f + - #bab0ab +``` + +By default, Turnilo uses 10 different colors for series. But it is possible to define more and Turnilo will adjust necessary split limits. + +### Logo + +Turnilo allows you to set custom customize logo icon by supplying an SVG string respectively. ```yaml customization: @@ -20,8 +73,15 @@ customization: xmlns:xlink="http://www.w3.org/1999/xlink"> +``` + +### Header color - headerBackground: '#2D95CA' +Turnilo allows you to set custom header background color supplying a string with CSS color. + +```yaml +customization: + headerBackground: '#2D95CA' ``` ## Url Shortener @@ -30,7 +90,6 @@ Turnilo supports url shorteners for generating short links for current view defi Function will receive three arguments, `request` - [node request module](https://github.com/request/request-promise-native), `url` with current hash, and `context` which includes: `clientIp` (the ip of the original client, considering a possible XFF header). Function should return Promise with shortened url as string inside. - For example: ```yaml @@ -39,25 +98,6 @@ customization: return request.get('http://tinyurl.com/api-create.php?url=' + encodeURIComponent(url)) ``` -## CSS Variables - -Turnilo allows you to override CSS variables to apply your own theming - -For example: - -```yaml -customization: - cssVariables: - brand: '#829aa3;' - item-dimension: '#f2cee0;' - item-dimension-text: white; - item-measure: '#cef2e0;' - item-measure-text: white; - background-brand: white; - background-brand-text: '#999;' - background-base: '#fbfbfb;' -``` - ## External links Turnilo supports defining external view links with access to `dataCube`, `filter`, `splits`, and `timezone` objects at link generation time. diff --git a/src/client/applications/turnilo-application/turnilo-application.tsx b/src/client/applications/turnilo-application/turnilo-application.tsx index 693f57641..c0ec60440 100644 --- a/src/client/applications/turnilo-application/turnilo-application.tsx +++ b/src/client/applications/turnilo-application/turnilo-application.tsx @@ -16,8 +16,10 @@ */ import { NamedArray } from "immutable-class"; +import memoizeOne from "memoize-one"; import React from "react"; import { ClientAppSettings } from "../../../common/models/app-settings/app-settings"; +import { ClientCustomization } from "../../../common/models/customization/customization"; import { ClientDataCube } from "../../../common/models/data-cube/data-cube"; import { Essence } from "../../../common/models/essence/essence"; import { isEnabled as isOAuthEnabled } from "../../../common/models/oauth/oauth"; @@ -34,6 +36,7 @@ import { Ajax } from "../../utils/ajax/ajax"; import { reportError } from "../../utils/error-reporter/error-reporter"; import { replaceHash } from "../../utils/url/url"; import { CubeView } from "../../views/cube-view/cube-view"; +import { SettingsContext, SettingsContextValue } from "../../views/cube-view/settings-context"; import { GeneralError } from "../../views/error-view/general-error"; import { HomeView } from "../../views/home-view/home-view"; import "./turnilo-application.scss"; @@ -226,13 +229,23 @@ export class TurniloApplication extends React.Component ({ customization })); + render() { return
- {this.renderView()} - {this.renderAboutModal()} - - + + {this.renderView()} + {this.renderAboutModal()} + + +
; } diff --git a/src/client/components/header-bar/header-bar.tsx b/src/client/components/header-bar/header-bar.tsx index c8776248b..f36584cde 100644 --- a/src/client/components/header-bar/header-bar.tsx +++ b/src/client/components/header-bar/header-bar.tsx @@ -16,16 +16,16 @@ */ import React from "react"; -import { ClientCustomization } from "../../../common/models/customization/customization"; +import { useSettingsContext } from "../../views/cube-view/settings-context"; import "./header-bar.scss"; export interface HeaderBarProps { - customization?: ClientCustomization; title?: string; } export const HeaderBar: React.FunctionComponent = props => { - const { customization, title } = props; + const { title } = props; + const { customization } = useSettingsContext(); const headerStyle: React.CSSProperties = customization && customization.headerBackground && { background: customization.headerBackground }; diff --git a/src/client/deserializers/app-settings.ts b/src/client/deserializers/app-settings.ts index b4b29e350..574a1312b 100644 --- a/src/client/deserializers/app-settings.ts +++ b/src/client/deserializers/app-settings.ts @@ -26,3 +26,29 @@ export function deserialize({ oauth, clientTimeout, customization, version }: Se oauth: deserializeOauth(oauth) }; } + +/* + NOTE: Function is used only for serialize-deserialize cycle in Essence class. + Now it's hard to remove that functionality but in the end, Essence does not need to serialize itself. +*/ +export function serialize(appSettings: ClientAppSettings): SerializedAppSettings { + const { clientTimeout, version, customization, oauth } = appSettings; + const { visualizationColors, messages, customLogoSvg, hasUrlShortener, locale, headerBackground, sentryDSN, timezones, externalViews } = customization; + + return { + clientTimeout, + version, + oauth, + customization: { + visualizationColors, + messages, + customLogoSvg, + locale, + hasUrlShortener, + headerBackground, + sentryDSN, + timezones: timezones.map(t => t.toJS()), + externalViews + } + }; +} diff --git a/src/client/deserializers/customization.ts b/src/client/deserializers/customization.ts index bf6064601..3d09dd1ff 100644 --- a/src/client/deserializers/customization.ts +++ b/src/client/deserializers/customization.ts @@ -19,7 +19,7 @@ import { ClientCustomization, SerializedCustomization } from "../../common/model import { deserialize as deserializeLocale } from "../../common/models/locale/locale"; export function deserialize(customization: SerializedCustomization): ClientCustomization { - const { headerBackground, messages, locale, customLogoSvg, timezones, externalViews, hasUrlShortener, sentryDSN } = customization; + const { headerBackground, messages, locale, customLogoSvg, timezones, externalViews, hasUrlShortener, sentryDSN, visualizationColors } = customization; return { headerBackground, customLogoSvg, @@ -28,6 +28,7 @@ export function deserialize(customization: SerializedCustomization): ClientCusto sentryDSN, messages, locale: deserializeLocale(locale), - timezones: timezones.map(Timezone.fromJS) + timezones: timezones.map(Timezone.fromJS), + visualizationColors }; } diff --git a/src/client/utils/styles/_variables.scss b/src/client/utils/styles/_variables.scss index 60cb356e4..bd56dc3ae 100644 --- a/src/client/utils/styles/_variables.scss +++ b/src/client/utils/styles/_variables.scss @@ -75,8 +75,6 @@ $theme-colors: ( item-measure-hover: darken($item-measure, 6%), item-measure-text: $text-standard, item-measure: $item-measure, - main-time-area: rgba($brand, 0.14), - main-time-line: $brand, negative: #e14040, pinboard-icon: darken($background-base, 5%), positive: #08b157, diff --git a/src/client/views/cube-view/cube-view.tsx b/src/client/views/cube-view/cube-view.tsx index 3cc8d34f3..e603067ef 100644 --- a/src/client/views/cube-view/cube-view.tsx +++ b/src/client/views/cube-view/cube-view.tsx @@ -92,7 +92,7 @@ export interface CubeViewProps { hash: string; changeCubeAndEssence: Ternary; urlForCubeAndEssence: Binary; - getEssenceFromHash: Binary; + getEssenceFromHash: Ternary; dataCube: ClientDataCube; dataCubes: ClientDataCube[]; openAboutModal: Fn; @@ -292,7 +292,8 @@ export class CubeView extends React.Component { } getEssenceFromDataCube(dataCube: ClientDataCube): Essence { - return Essence.fromDataCube(dataCube); + const { appSettings } = this.props; + return Essence.fromDataCube(dataCube, appSettings); } getEssenceFromHash(hash: string, dataCube: ClientDataCube): Essence { @@ -304,8 +305,8 @@ export class CubeView extends React.Component { throw new Error("Hash is required."); } - const { getEssenceFromHash } = this.props; - return getEssenceFromHash(hash, dataCube); + const { getEssenceFromHash, appSettings } = this.props; + return getEssenceFromHash(hash, appSettings, dataCube); } globalResizeListener = () => { diff --git a/src/client/views/cube-view/settings-context.ts b/src/client/views/cube-view/settings-context.ts new file mode 100644 index 000000000..751e455ff --- /dev/null +++ b/src/client/views/cube-view/settings-context.ts @@ -0,0 +1,32 @@ +/* + * Copyright 2017-2022 Allegro.pl + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React, { useContext } from "react"; +import { ClientCustomization } from "../../../common/models/customization/customization"; + +export interface SettingsContextValue { + customization: ClientCustomization; +} + +export const SettingsContext = React.createContext({ + get customization(): ClientCustomization { + throw new Error("Attempted to consume SettingsContext when there was no Provider in place."); + } +}); + +export function useSettingsContext(): SettingsContextValue { + return useContext(SettingsContext); +} diff --git a/src/client/views/home-view/home-view.tsx b/src/client/views/home-view/home-view.tsx index be24bfca8..5cf1d297d 100644 --- a/src/client/views/home-view/home-view.tsx +++ b/src/client/views/home-view/home-view.tsx @@ -73,15 +73,12 @@ export class HomeView extends React.Component { } render() { - const { onOpenAbout, dataCubes, customization } = this.props; + const { onOpenAbout, dataCubes } = this.props; const { query } = this.state; const hasDataCubes = dataCubes.length > 0; return
- + diff --git a/src/client/visualizations/bar-chart/bar-chart.scss b/src/client/visualizations/bar-chart/bar-chart.scss index 684645fdc..109c2813e 100644 --- a/src/client/visualizations/bar-chart/bar-chart.scss +++ b/src/client/visualizations/bar-chart/bar-chart.scss @@ -37,6 +37,7 @@ position: absolute; .selection-highlight { + // TODO: replace with visualisationColors.main @include css-variable(border-color, brand); pointer-events: none; border-width: 1px; @@ -113,7 +114,6 @@ } > rect.background { - @include css-variable(fill, brand); pointer-events: none; } diff --git a/src/client/visualizations/bar-chart/bar-chart.tsx b/src/client/visualizations/bar-chart/bar-chart.tsx index 57c795771..d51c18085 100644 --- a/src/client/visualizations/bar-chart/bar-chart.tsx +++ b/src/client/visualizations/bar-chart/bar-chart.tsx @@ -54,6 +54,7 @@ import { VisMeasureLabel } from "../../components/vis-measure-label/vis-measure- import { SPLIT, VIS_H_PADDING } from "../../config/constants"; import { classNames, roundToPx } from "../../utils/dom/dom"; import { ChartPanel, DefaultVisualizationControls, VisualizationProps } from "../../views/cube-view/center-panel/center-panel"; +import { SettingsContext, SettingsContextValue } from "../../views/cube-view/settings-context"; import { hasHighlightOn } from "../highlight-controller/highlight-controller"; import "./bar-chart.scss"; import { BarCoordinates } from "./bar-coordinates"; @@ -168,6 +169,7 @@ export default function BarChartVisualization(props: VisualizationProps) { } class BarChart extends React.Component { + static contextType = SettingsContext; protected className = BAR_CHART_MANIFEST.name; private coordinatesCache: BarCoordinates[][] = []; @@ -175,6 +177,8 @@ class BarChart extends React.Component { state: BarChartState = this.initState(); + context: SettingsContextValue; + componentDidUpdate() { const { scrollerYPosition, scrollerXPosition } = this.state; @@ -491,6 +495,7 @@ class BarChart extends React.Component { ): { bars: JSX.Element[], highlight: JSX.Element } { const { essence } = this.props; const { timezone } = essence; + const { customization: { visualizationColors } } = this.context; const bars: JSX.Element[] = []; let highlight: JSX.Element; @@ -547,6 +552,7 @@ class BarChart extends React.Component { className="background" width={roundToPx(barWidth)} height={roundToPx(Math.abs(height))} + fill={visualizationColors.main} x={barOffset} y={roundToPx(y)} /> diff --git a/src/client/visualizations/bar-chart/improved-bar-chart/bar-chart.tsx b/src/client/visualizations/bar-chart/improved-bar-chart/bar-chart.tsx index 63efad50f..4255437ee 100644 --- a/src/client/visualizations/bar-chart/improved-bar-chart/bar-chart.tsx +++ b/src/client/visualizations/bar-chart/improved-bar-chart/bar-chart.tsx @@ -25,6 +25,7 @@ import { MessageCard } from "../../../components/message-card/message-card"; import { Scroller } from "../../../components/scroller/scroller"; import { SPLIT } from "../../../config/constants"; import { selectMainDatum } from "../../../utils/dataset/selectors/selectors"; +import { useSettingsContext } from "../../../views/cube-view/settings-context"; import { Highlight } from "../../highlight-controller/highlight"; import { BarCharts } from "./bar-charts/bar-charts"; import { InteractionController } from "./interactions/interaction-controller"; @@ -49,8 +50,9 @@ interface BarChartProps { } export const BarChart: React.FunctionComponent = props => { + const { customization } = useSettingsContext(); const { dataset, essence, stage, highlight, acceptHighlight, dropHighlight, saveHighlight } = props; - const model = create(essence, dataset); + const model = create(essence, dataset, customization); const transposedDataset = transposeDataset(dataset, model); if (transposedDataset.length === 0) { diff --git a/src/client/visualizations/bar-chart/improved-bar-chart/bar/single-bar.tsx b/src/client/visualizations/bar-chart/improved-bar-chart/bar/single-bar.tsx index 657d521da..5c048d95a 100644 --- a/src/client/visualizations/bar-chart/improved-bar-chart/bar/single-bar.tsx +++ b/src/client/visualizations/bar-chart/improved-bar-chart/bar/single-bar.tsx @@ -18,6 +18,7 @@ import { Datum } from "plywood"; import React from "react"; import { ConcreteSeries } from "../../../../../common/models/series/concrete-series"; import { LinearScale } from "../../../../utils/linear-scale/linear-scale"; +import { useSettingsContext } from "../../../../views/cube-view/settings-context"; import { BaseBarChartModel } from "../utils/bar-chart-model"; import { DomainValue } from "../utils/x-domain"; import { XScale } from "../utils/x-scale"; @@ -32,6 +33,7 @@ interface SingleBarProps { } export const SingleBar: React.FunctionComponent = props => { + const { customization: { visualizationColors } } = useSettingsContext(); const { datum, xScale, yScale, model: { continuousSplit }, series } = props; const [maxHeight] = yScale.range(); const x = continuousSplit.selectValue(datum); @@ -40,11 +42,13 @@ export const SingleBar: React.FunctionComponent = props => { const y = series.selectValue(datum); const yPos = yScale(y); const height = maxHeight - yPos; + const fill = visualizationColors.main; return ; }; diff --git a/src/client/visualizations/bar-chart/improved-bar-chart/bar/single-time-shift-bar.tsx b/src/client/visualizations/bar-chart/improved-bar-chart/bar/single-time-shift-bar.tsx index eb08eb7a9..f007bcdf4 100644 --- a/src/client/visualizations/bar-chart/improved-bar-chart/bar/single-time-shift-bar.tsx +++ b/src/client/visualizations/bar-chart/improved-bar-chart/bar/single-time-shift-bar.tsx @@ -16,8 +16,10 @@ import { Datum } from "plywood"; import React from "react"; +import { alphaMain } from "../../../../../common/models/colors/colors"; import { ConcreteSeries, SeriesDerivation } from "../../../../../common/models/series/concrete-series"; import { LinearScale } from "../../../../utils/linear-scale/linear-scale"; +import { useSettingsContext } from "../../../../views/cube-view/settings-context"; import { BaseBarChartModel } from "../utils/bar-chart-model"; import { DomainValue } from "../utils/x-domain"; import { XScale } from "../utils/x-scale"; @@ -32,6 +34,7 @@ interface SingleTimeShiftBar { } export const SingleTimeShiftBar: React.FunctionComponent = props => { + const { customization: { visualizationColors } } = useSettingsContext(); const { datum, xScale, yScale, model: { continuousSplit }, series } = props; const [maxHeight] = yScale.range(); const x = continuousSplit.selectValue(datum); @@ -45,17 +48,22 @@ export const SingleTimeShiftBar: React.FunctionComponent = p const yCurrentStart = yScale(yCurrent); const yPreviousStart = yScale(yPrevious); + const currentFill = visualizationColors.main; + const previousFill = alphaMain(visualizationColors); + return ; diff --git a/src/client/visualizations/bar-chart/improved-bar-chart/bars/bars.scss b/src/client/visualizations/bar-chart/improved-bar-chart/bars/bars.scss index 4132b374d..f23e115bb 100644 --- a/src/client/visualizations/bar-chart/improved-bar-chart/bars/bars.scss +++ b/src/client/visualizations/bar-chart/improved-bar-chart/bars/bars.scss @@ -17,15 +17,6 @@ @import '../../../../imports'; .bar-chart-bars { - .bar-chart-bar { - @include css-variable(fill, brand); - } - - .bar-chart-bar-previous { - @include css-variable(fill, main-time-area); - } - - .bar-chart-total { position: absolute; top: 15px; diff --git a/src/client/visualizations/bar-chart/improved-bar-chart/utils/bar-chart-model.ts b/src/client/visualizations/bar-chart/improved-bar-chart/utils/bar-chart-model.ts index ba1c141cd..d17ccff09 100644 --- a/src/client/visualizations/bar-chart/improved-bar-chart/utils/bar-chart-model.ts +++ b/src/client/visualizations/bar-chart/improved-bar-chart/utils/bar-chart-model.ts @@ -17,7 +17,8 @@ import { Timezone } from "chronoshift"; import { List, OrderedMap } from "immutable"; import { Dataset, Datum } from "plywood"; -import { NORMAL_COLORS } from "../../../../../common/models/colors/colors"; +import { VisualizationColors } from "../../../../../common/models/colors/colors"; +import { ClientCustomization } from "../../../../../common/models/customization/customization"; import { Dimension } from "../../../../../common/models/dimension/dimension"; import { Essence } from "../../../../../common/models/essence/essence"; import { ConcreteSeries } from "../../../../../common/models/series/concrete-series"; @@ -70,17 +71,18 @@ function readCommons(essence: Essence): Omit { }; } -function createColorMap(nominalSplit: Split, dataset: Dataset): ColorMap { +function createColorMap(nominalSplit: Split, dataset: Dataset, colors: VisualizationColors): ColorMap { const datums = selectFirstSplitDatums(dataset); return datums.reduce((map: ColorMap, datum: Datum, i: number) => { const key = String(nominalSplit.selectValue(datum)); - const colorIndex = i % NORMAL_COLORS.length; - const color = NORMAL_COLORS[colorIndex]; + const colorIndex = i % colors.series.length; + const color = colors.series[colorIndex]; return map.set(key, color); }, OrderedMap()); } -export function create(essence: Essence, dataset: Dataset): BarChartModel { +export function create(essence: Essence, dataset: Dataset, customization: ClientCustomization): BarChartModel { + const { visualizationColors } = customization; const commons = readCommons(essence); if (!hasNominalSplit(essence)) { return { @@ -90,7 +92,7 @@ export function create(essence: Essence, dataset: Dataset): BarChartModel { } const nominalSplit = getNominalSplit(essence); const nominalDimension = getNominalDimension(essence); - const colors = createColorMap(nominalSplit, dataset); + const colors = createColorMap(nominalSplit, dataset, visualizationColors); return { ...commons, variant: ModelVariantId.STACKED, diff --git a/src/client/visualizations/heat-map/utils/scales.ts b/src/client/visualizations/heat-map/utils/scales.ts index 957518ce2..f0e435b30 100644 --- a/src/client/visualizations/heat-map/utils/scales.ts +++ b/src/client/visualizations/heat-map/utils/scales.ts @@ -25,6 +25,7 @@ import { nestedDataset } from "./nested-dataset"; export type ColorScale = d3.ScaleLinear; const white = "#fff"; +// TODO: replace with visualisationColors.main const orange = "#ff5a00"; interface Scales { diff --git a/src/client/visualizations/line-chart/chart-line/chart-line.scss b/src/client/visualizations/line-chart/chart-line/chart-line.scss index e0b5280ef..ee1a27be0 100644 --- a/src/client/visualizations/line-chart/chart-line/chart-line.scss +++ b/src/client/visualizations/line-chart/chart-line/chart-line.scss @@ -21,25 +21,16 @@ pointer-events: none; .area { - @include css-variable(fill, main-time-area); stroke: none; fill-opacity: .9; } .line { - @include css-variable(stroke, main-time-line); fill: none; stroke-width: 1.6px; } .singleton { - @include css-variable(fill, main-time-line); stroke: none; } - - .hover { - @include css-variable(stroke, main-time-line); - fill: $white; - stroke-width: 2; - } } diff --git a/src/client/visualizations/line-chart/chart-line/chart-line.tsx b/src/client/visualizations/line-chart/chart-line/chart-line.tsx index 8b7defec1..8cbd99ffc 100644 --- a/src/client/visualizations/line-chart/chart-line/chart-line.tsx +++ b/src/client/visualizations/line-chart/chart-line/chart-line.tsx @@ -17,8 +17,10 @@ import * as d3 from "d3"; import { Datum } from "plywood"; import React from "react"; +import { alphaMain } from "../../../../common/models/colors/colors"; import { Stage } from "../../../../common/models/stage/stage"; import { Unary } from "../../../../common/utils/functional/functional"; +import { useSettingsContext } from "../../../views/cube-view/settings-context"; import { ContinuousRange, ContinuousScale } from "../utils/continuous-types"; import "./chart-line.scss"; import { prepareDataPoints } from "./prepare-data-points"; @@ -37,13 +39,10 @@ export interface ChartLineProps { stage: Stage; } -const stroke = (color: string, dashed: boolean): Pick => ({ - stroke: color, - strokeDasharray: dashed ? "4 2" : undefined -}); - export const ChartLine: React.FunctionComponent = props => { - const { color, dashed, getX, getY, dataset, showArea, stage, xScale, yScale } = props; + const { customization: { visualizationColors } } = useSettingsContext(); + const mainColor = visualizationColors.main; + const { color = mainColor, dashed, getX, getY, dataset, showArea, stage, xScale, yScale } = props; const area = d3.area().y0(yScale(0)); const line = d3.line(); @@ -54,8 +53,15 @@ export const ChartLine: React.FunctionComponent = props => { const hasSinglePoint = points.length === 1; return - {hasMultiplePoints && } - {hasMultiplePoints && showArea && } + {hasMultiplePoints && } + {hasMultiplePoints && showArea && } {hasSinglePoint && = props => { + const { customization: { visualizationColors } } = useSettingsContext(); const { chartId, interactions, visualisationStage, chartStage, essence, series, xScale, xTicks, dataset } = props; const hasComparison = essence.hasComparison(); const continuousSplitDataset = selectFirstSplitDataset(dataset); @@ -84,7 +85,7 @@ export const SeriesChart: React.FunctionComponent = props => { {({ yScale, lineStage }) => {continuousSplitDataset.data.map((datum, index) => { const splitKey = nominalSplit.selectValue(datum); - const color = NORMAL_COLORS[index]; + const color = visualizationColors.series[index]; return ; } -function colorEntries(dataset: Dataset, range: PlywoodRange, series: ConcreteSeries, essence: Essence): ColorEntry[] { +function colorEntries(dataset: Dataset, range: PlywoodRange, series: ConcreteSeries, essence: Essence, visualizationColors: VisualizationColors): ColorEntry[] { const { data } = dataset; const nominalSplit = getNominalSplit(essence); const continuousRef = getContinuousReference(essence); const hasComparison = essence.hasComparison(); return data.map((datum, i) => { const name = String(nominalSplit.selectValue(datum)); - const color = NORMAL_COLORS[i]; + const color = visualizationColors.series[i]; const hoverDatum = findSplitDatumByAttribute(datum, continuousRef, range); if (!hoverDatum) { @@ -75,9 +76,10 @@ interface SeriesHoverContentProps { } export const SeriesHoverContent: React.FunctionComponent = props => { + const { customization: { visualizationColors } } = useSettingsContext(); const { essence, range, series, dataset } = props; if (hasNominalSplit(essence)) { - const entries = colorEntries(dataset, range, series, essence); + const entries = colorEntries(dataset, range, series, essence, visualizationColors); return ; } return diff --git a/src/client/visualizations/line-chart/charts/charts-per-split/split-chart.tsx b/src/client/visualizations/line-chart/charts/charts-per-split/split-chart.tsx index 8e9751b01..dc7c4c561 100644 --- a/src/client/visualizations/line-chart/charts/charts-per-split/split-chart.tsx +++ b/src/client/visualizations/line-chart/charts/charts-per-split/split-chart.tsx @@ -16,12 +16,12 @@ import { Dataset, Datum, NumberRange, TimeRange } from "plywood"; import React from "react"; -import { NORMAL_COLORS } from "../../../../../common/models/colors/colors"; import { Essence } from "../../../../../common/models/essence/essence"; import { defaultFormatter } from "../../../../../common/models/series/series-format"; import { Stage } from "../../../../../common/models/stage/stage"; import { Unary } from "../../../../../common/utils/functional/functional"; import { selectSplitDataset } from "../../../../utils/dataset/selectors/selectors"; +import { useSettingsContext } from "../../../../views/cube-view/settings-context"; import { BaseChart } from "../../base-chart/base-chart"; import { ColoredSeriesChartLine } from "../../chart-line/colored-series-chart-line"; import { SingletonSeriesChartLine } from "../../chart-line/singleton-series-chart-line"; @@ -47,18 +47,29 @@ interface SplitChartProps { } export const SplitChart: React.FunctionComponent = props => { - const { chartId, interactions, visualisationStage, chartStage, essence, xScale, xTicks, selectDatum, dataset } = props; + const { customization: { visualizationColors } } = useSettingsContext(); + const { + chartId, + interactions, + visualisationStage, + chartStage, + essence, + xScale, + xTicks, + selectDatum, + dataset + } = props; const { interaction } = interactions; const splitDatum = selectDatum(dataset); const splitDataset = selectSplitDataset(splitDatum); const series = essence.getConcreteSeries(); - const label =