diff --git a/src/chart_types/heatmap/layout/viewmodel/viewmodel.ts b/src/chart_types/heatmap/layout/viewmodel/viewmodel.ts index 20302ab943..a37fd9911b 100644 --- a/src/chart_types/heatmap/layout/viewmodel/viewmodel.ts +++ b/src/chart_types/heatmap/layout/viewmodel/viewmodel.ts @@ -108,12 +108,13 @@ export function shapeViewModel( let xValues = xDomain.domain as any[]; const timeScale = - xDomain.scaleType === ScaleType.Time + xDomain.scaleConfig.type === ScaleType.Time ? new ScaleContinuous( { type: ScaleType.Time, domain: xDomain.domain, range: [0, chartDimensions.width], + nice: false, }, { ticks: getTicks(chartDimensions.width, config.xAxisLabel), @@ -313,7 +314,7 @@ export function shapeViewModel( * @param y */ const pickHighlightedArea: PickHighlightedArea = (x: Array, y: Array) => { - if (xDomain.scaleType !== ScaleType.Time) { + if (xDomain.scaleConfig.type !== ScaleType.Time) { return null; } const [startValue, endValue] = x; diff --git a/src/chart_types/heatmap/state/selectors/get_x_axis_right_overflow.ts b/src/chart_types/heatmap/state/selectors/get_x_axis_right_overflow.ts index 8b6e900943..8879fdd3c1 100644 --- a/src/chart_types/heatmap/state/selectors/get_x_axis_right_overflow.ts +++ b/src/chart_types/heatmap/state/selectors/get_x_axis_right_overflow.ts @@ -33,7 +33,7 @@ import { getHeatmapTableSelector } from './get_heatmap_table'; export const getXAxisRightOverflow = createCachedSelector( [getHeatmapConfigSelector, getHeatmapTableSelector], ({ xAxisLabel: { fontSize, fontFamily, padding, formatter, width }, timeZone }, { xDomain }): number => { - if (xDomain.scaleType !== ScaleType.Time) { + if (xDomain.scaleConfig.type !== ScaleType.Time) { return 0; } if (typeof width === 'number') { diff --git a/src/chart_types/xy_chart/crosshair/crosshair_utils.linear_snap.test.ts b/src/chart_types/xy_chart/crosshair/crosshair_utils.linear_snap.test.ts index 4d9eacbb6c..e5aa73ee4f 100644 --- a/src/chart_types/xy_chart/crosshair/crosshair_utils.linear_snap.test.ts +++ b/src/chart_types/xy_chart/crosshair/crosshair_utils.linear_snap.test.ts @@ -21,6 +21,7 @@ import { ChartTypes } from '../..'; import { ScaleType } from '../../../scales/constants'; import { SpecTypes } from '../../../specs/constants'; import { Dimensions } from '../../../utils/dimensions'; +import { getXScaleConfig } from '../scales/get_scale_config'; import { computeSeriesDomains } from '../state/utils/utils'; import { computeXScale } from '../utils/scales'; import { BasicSeriesSpec, SeriesTypes } from '../utils/specs'; @@ -1460,7 +1461,7 @@ describe('Crosshair utils linear scale', () => { domain: [0.5, 3.5], isBandScale: true, minInterval: 1, - scaleType: ScaleType.Linear, + scaleConfig: getXScaleConfig(ScaleType.Linear), type: 'xDomain', }, totalBarsInCluster: 1, @@ -1491,7 +1492,7 @@ describe('Crosshair utils linear scale', () => { domain: [-0.5, 2.5], isBandScale: true, minInterval: 1, - scaleType: ScaleType.Linear, + scaleConfig: getXScaleConfig(ScaleType.Linear), type: 'xDomain', }, totalBarsInCluster: barSeries.length, @@ -1522,7 +1523,7 @@ describe('Crosshair utils linear scale', () => { domain: [0.5, 3.5], isBandScale: true, minInterval: 1, - scaleType: ScaleType.Linear, + scaleConfig: getXScaleConfig(ScaleType.Linear), type: 'xDomain', }, totalBarsInCluster: 1, @@ -1553,7 +1554,7 @@ describe('Crosshair utils linear scale', () => { domain: [-0.5, 2.5], isBandScale: true, minInterval: 1, - scaleType: ScaleType.Linear, + scaleConfig: getXScaleConfig(ScaleType.Linear), type: 'xDomain', }, totalBarsInCluster: barSeries.length, diff --git a/src/chart_types/xy_chart/domains/nice.ts b/src/chart_types/xy_chart/domains/nice.ts new file mode 100644 index 0000000000..665fb4d417 --- /dev/null +++ b/src/chart_types/xy_chart/domains/nice.ts @@ -0,0 +1,23 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you 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. + */ + +/** @internal */ +export function areAllNiceDomain(nice: Array) { + return nice.length > 0 && nice.every((d) => d); +} diff --git a/src/chart_types/xy_chart/domains/types.ts b/src/chart_types/xy_chart/domains/types.ts index 1b00986de6..fedbd9b56d 100644 --- a/src/chart_types/xy_chart/domains/types.ts +++ b/src/chart_types/xy_chart/domains/types.ts @@ -18,35 +18,30 @@ */ import { ScaleContinuousType } from '../../../scales'; -import { ScaleType } from '../../../scales/constants'; import { LogScaleOptions } from '../../../scales/scale_continuous'; import { OrdinalDomain, ContinuousDomain } from '../../../utils/domain'; import { GroupId } from '../../../utils/ids'; +import { ScaleConfig } from '../scales/get_scale_config'; +import { XScaleType } from '../utils/specs'; /** @internal */ -export interface BaseDomain { - scaleType: typeof ScaleType.Ordinal | ScaleContinuousType; +export type XDomain = Pick & { + type: 'xDomain'; + scaleConfig: ScaleConfig; /* if the scale needs to be a band scale: used when displaying bars */ isBandScale: boolean; -} + /* the minimum interval of the scale if not-ordinal band-scale */ + minInterval: number; + /** if x domain is time, we should also specify the timezone */ + timeZone?: string; + domain: OrdinalDomain | ContinuousDomain; +}; /** @internal */ -export type XDomain = BaseDomain & - Pick & { - type: 'xDomain'; - /* the minimum interval of the scale if not-ordinal band-scale */ - minInterval: number; - /** if x domain is time, we should also specify the timezone */ - timeZone?: string; - domain: OrdinalDomain | ContinuousDomain; - }; - -/** @internal */ -export type YDomain = BaseDomain & - LogScaleOptions & { - type: 'yDomain'; - isBandScale: false; - scaleType: ScaleContinuousType; - groupId: GroupId; - domain: ContinuousDomain; - }; +export type YDomain = LogScaleOptions & { + type: 'yDomain'; + scaleConfig: ScaleConfig; + isBandScale: false; + groupId: GroupId; + domain: ContinuousDomain; +}; diff --git a/src/chart_types/xy_chart/domains/x_domain.test.ts b/src/chart_types/xy_chart/domains/x_domain.test.ts index 01f7dc5603..f799e70020 100644 --- a/src/chart_types/xy_chart/domains/x_domain.test.ts +++ b/src/chart_types/xy_chart/domains/x_domain.test.ts @@ -22,6 +22,7 @@ import { MockSeriesSpecs } from '../../../mocks/specs'; import { ScaleType } from '../../../scales/constants'; import { SpecTypes, Direction, BinAgg } from '../../../specs/constants'; import { Logger } from '../../../utils/logger'; +import { getXScaleConfig } from '../scales/get_scale_config'; import { getDataSeriesFromSpecs } from '../utils/series'; import { BasicSeriesSpec, SeriesTypes } from '../utils/specs'; import { convertXScaleTypes, findMinInterval, mergeXDomain } from './x_domain'; @@ -33,16 +34,10 @@ jest.mock('../../../utils/logger', () => ({ })); describe('X Domain', () => { - test('Should return null when missing specs or specs types', () => { + test('Should return a default scale when missing specs or specs types', () => { const seriesSpecs: BasicSeriesSpec[] = []; const mainXScale = convertXScaleTypes(seriesSpecs); - expect(mainXScale).toBe(null); - }); - - test('should throw if we miss calling merge X domain without specs configured', () => { - expect(() => { - mergeXDomain([], new Set()); - }).toThrow(); + expect(mainXScale).not.toBeNull(); }); test('Should return correct scale type with single bar', () => { @@ -54,7 +49,7 @@ describe('X Domain', () => { ]; const mainXScale = convertXScaleTypes(seriesSpecs); expect(mainXScale).toEqual({ - scaleType: ScaleType.Linear, + scaleConfig: getXScaleConfig(ScaleType.Linear), isBandScale: true, }); }); @@ -68,7 +63,7 @@ describe('X Domain', () => { ]; const mainXScale = convertXScaleTypes(seriesSpecs); expect(mainXScale).toEqual({ - scaleType: ScaleType.Ordinal, + scaleConfig: getXScaleConfig(ScaleType.Ordinal), isBandScale: true, }); }); @@ -82,7 +77,7 @@ describe('X Domain', () => { ]; const mainXScale = convertXScaleTypes(seriesSpecs); expect(mainXScale).toEqual({ - scaleType: ScaleType.Linear, + scaleConfig: getXScaleConfig(ScaleType.Linear), isBandScale: false, }); }); @@ -96,7 +91,7 @@ describe('X Domain', () => { ]; const mainXScale = convertXScaleTypes(seriesSpecs); expect(mainXScale).toEqual({ - scaleType: ScaleType.Time, + scaleConfig: getXScaleConfig(ScaleType.Time), isBandScale: false, timeZone: 'utc-3', }); @@ -116,7 +111,7 @@ describe('X Domain', () => { ]; const mainXScale = convertXScaleTypes(seriesSpecs); expect(mainXScale).toEqual({ - scaleType: ScaleType.Time, + scaleConfig: getXScaleConfig(ScaleType.Time), isBandScale: false, timeZone: 'utc-3', }); @@ -136,7 +131,7 @@ describe('X Domain', () => { ]; const mainXScale = convertXScaleTypes(seriesSpecs); expect(mainXScale).toEqual({ - scaleType: ScaleType.Time, + scaleConfig: getXScaleConfig(ScaleType.Time), isBandScale: false, timeZone: 'utc', }); @@ -155,7 +150,7 @@ describe('X Domain', () => { ]; const mainXScale = convertXScaleTypes(seriesSpecs); expect(mainXScale).toEqual({ - scaleType: ScaleType.Ordinal, + scaleConfig: getXScaleConfig(ScaleType.Ordinal), isBandScale: false, }); }); @@ -172,7 +167,7 @@ describe('X Domain', () => { ]; const mainXScale = convertXScaleTypes(seriesSpecs); expect(mainXScale).toEqual({ - scaleType: ScaleType.Ordinal, + scaleConfig: getXScaleConfig(ScaleType.Ordinal), isBandScale: true, }); }); @@ -190,7 +185,7 @@ describe('X Domain', () => { ]; const mainXScale = convertXScaleTypes(seriesSpecs); expect(mainXScale).toEqual({ - scaleType: ScaleType.Linear, + scaleConfig: getXScaleConfig(ScaleType.Linear), isBandScale: true, }); }); @@ -497,14 +492,14 @@ describe('X Domain', () => { ], xValues, customDomain, - ScaleType.Ordinal, + { type: ScaleType.Ordinal, nice: false }, ); expect(getResult).not.toThrow(); const mergedDomain = getResult(); expect(mergedDomain.domain).toEqual([0, 'a', 2, 5, 7]); - expect(mergedDomain.scaleType).toEqual(ScaleType.Ordinal); + expect(mergedDomain.scaleConfig.type).toEqual(ScaleType.Ordinal); }); test('Should merge multi bar/line ordinal series correctly', () => { diff --git a/src/chart_types/xy_chart/domains/x_domain.ts b/src/chart_types/xy_chart/domains/x_domain.ts index dd260b7355..a9e377dd8c 100644 --- a/src/chart_types/xy_chart/domains/x_domain.ts +++ b/src/chart_types/xy_chart/domains/x_domain.ts @@ -23,8 +23,10 @@ import { ScaleType } from '../../../scales/constants'; import { compareByValueAsc, identity } from '../../../utils/common'; import { computeContinuousDataDomain, computeOrdinalDataDomain } from '../../../utils/domain'; import { Logger } from '../../../utils/logger'; +import { getXScaleConfig, ScaleConfig } from '../scales/get_scale_config'; import { isCompleteBound, isLowerBound, isUpperBound } from '../utils/axis_type_utils'; import { BasicSeriesSpec, CustomXDomain, SeriesTypes, XScaleType } from '../utils/specs'; +import { areAllNiceDomain } from './nice'; import { XDomain } from './types'; /** @@ -40,21 +42,18 @@ export function mergeXDomain( specs: Optional, 'seriesType'>[], xValues: Set, customXDomain?: CustomXDomain, - fallbackScale?: XScaleType, + fallbackScale?: ScaleConfig, ): XDomain { - const mainXScaleType = convertXScaleTypes(specs); - if (!mainXScaleType) { - throw new Error(`Cannot merge the domain. Missing X scale types ${JSON.stringify(specs)}`); - } + const { scaleConfig, isBandScale, timeZone } = convertXScaleTypes(specs); const values = [...xValues.values()]; let seriesXComputedDomains; let minInterval = 0; - if (mainXScaleType.scaleType === ScaleType.Ordinal || fallbackScale === ScaleType.Ordinal) { - if (mainXScaleType.scaleType !== ScaleType.Ordinal) { + if (scaleConfig.type === ScaleType.Ordinal || fallbackScale?.type === ScaleType.Ordinal) { + if (scaleConfig.type !== ScaleType.Ordinal) { Logger.warn( - `Each X value in a ${mainXScaleType.scaleType} x scale needs be be a number. Using ordinal x scale as fallback.`, + `Each X value in a ${scaleConfig.type} x scale needs be be a number. Using ordinal x scale as fallback.`, ); } @@ -63,10 +62,10 @@ export function mergeXDomain( if (Array.isArray(customXDomain)) { seriesXComputedDomains = customXDomain; } else { - if (fallbackScale === ScaleType.Ordinal) { + if (fallbackScale?.type === ScaleType.Ordinal) { Logger.warn(`xDomain ignored for fallback ordinal scale. Options to resolve: -1) Correct data to match ${mainXScaleType.scaleType} scale type (see previous warning) +1) Correct data to match ${scaleConfig.type} scale type (see previous warning) 2) Change xScaleType to ordinal and set xDomain to Domain array`); } else { Logger.warn( @@ -119,11 +118,11 @@ export function mergeXDomain( return { type: 'xDomain', - scaleType: fallbackScale ?? mainXScaleType.scaleType, - isBandScale: mainXScaleType.isBandScale, + scaleConfig: fallbackScale ?? scaleConfig, + isBandScale, domain: seriesXComputedDomains, minInterval, - timeZone: mainXScaleType.timeZone, + timeZone, logBase: customXDomain && 'logBase' in customXDomain ? customXDomain.logBase : undefined, }; } @@ -184,35 +183,54 @@ export function findMinInterval(xValues: number[]): number { export function convertXScaleTypes( specs: Optional, 'seriesType'>[], ): { - scaleType: XScaleType; + scaleConfig: ScaleConfig; isBandScale: boolean; timeZone?: string; -} | null { +} { const seriesTypes = new Set(); const scaleTypes = new Set(); const timeZones = new Set(); + const niceDomainConfigs: Array = []; specs.forEach((spec) => { + const scaleConfig = getXScaleConfig(spec.xScaleType); + niceDomainConfigs.push(scaleConfig.nice); seriesTypes.add(spec.seriesType); - scaleTypes.add(spec.xScaleType); + scaleTypes.add(scaleConfig.type); if (spec.timeZone) { timeZones.add(spec.timeZone.toLowerCase()); } }); if (specs.length === 0 || seriesTypes.size === 0 || scaleTypes.size === 0) { - return null; + return { + scaleConfig: { + type: ScaleType.Linear, + nice: true, + }, + isBandScale: false, + }; } + const nice = areAllNiceDomain(niceDomainConfigs); const isBandScale = seriesTypes.has(SeriesTypes.Bar); if (scaleTypes.size === 1) { const scaleType = scaleTypes.values().next().value; - let timeZone: string | undefined; - if (scaleType === ScaleType.Time) { - timeZone = timeZones.size > 1 ? 'utc' : timeZones.values().next().value; - } - return { scaleType, isBandScale, timeZone }; + const timeZone = timeZones.size > 1 ? 'utc' : timeZones.values().next().value; + return { scaleConfig: { type: scaleType, nice }, isBandScale, timeZone }; } if (scaleTypes.size > 1 && scaleTypes.has(ScaleType.Ordinal)) { - return { scaleType: ScaleType.Ordinal, isBandScale }; + return { + scaleConfig: { + type: ScaleType.Ordinal, + nice, + }, + isBandScale, + }; } - return { scaleType: ScaleType.Linear, isBandScale }; + return { + scaleConfig: { + type: ScaleType.Linear, + nice, + }, + isBandScale, + }; } diff --git a/src/chart_types/xy_chart/domains/y_domain.test.ts b/src/chart_types/xy_chart/domains/y_domain.test.ts index f37f168a8b..e52a764268 100644 --- a/src/chart_types/xy_chart/domains/y_domain.test.ts +++ b/src/chart_types/xy_chart/domains/y_domain.test.ts @@ -25,6 +25,7 @@ import { SpecTypes } from '../../../specs/constants'; import { Position } from '../../../utils/common'; import { BARCHART_1Y0G } from '../../../utils/data_samples/test_dataset'; import { Logger } from '../../../utils/logger'; +import { getYScaleConfig } from '../scales/get_scale_config'; import { computeSeriesDomainsSelector } from '../state/selectors/compute_series_domains'; import { BasicSeriesSpec, SeriesTypes, DEFAULT_GLOBAL_ID, StackMode } from '../utils/specs'; import { coerceYScaleTypes, groupSeriesByYGroup } from './y_domain'; @@ -88,7 +89,7 @@ describe('Y Domain', () => { type: 'yDomain', groupId: DEFAULT_GLOBAL_ID, domain: [2, 12], - scaleType: ScaleType.Linear, + scaleConfig: getYScaleConfig(ScaleType.Linear), isBandScale: false, }, ]); @@ -116,7 +117,7 @@ describe('Y Domain', () => { type: 'yDomain', groupId: DEFAULT_GLOBAL_ID, domain: [0, 12], - scaleType: ScaleType.Linear, + scaleConfig: getYScaleConfig(ScaleType.Linear), isBandScale: false, }, ]); @@ -151,14 +152,14 @@ describe('Y Domain', () => { { groupId: 'a', domain: [2, 12], - scaleType: ScaleType.Linear, + scaleConfig: getYScaleConfig(ScaleType.Linear), isBandScale: false, type: 'yDomain', }, { groupId: 'b', domain: [2, 10], - scaleType: ScaleType.Log, + scaleConfig: getYScaleConfig(ScaleType.Log), isBandScale: false, type: 'yDomain', }, @@ -189,7 +190,7 @@ describe('Y Domain', () => { { groupId: 'a', domain: [0, 17], - scaleType: ScaleType.Linear, + scaleConfig: getYScaleConfig(ScaleType.Linear), isBandScale: false, type: 'yDomain', }, @@ -218,7 +219,7 @@ describe('Y Domain', () => { { groupId: 'a', domain: [0, 12], - scaleType: ScaleType.Linear, + scaleConfig: getYScaleConfig(ScaleType.Linear), isBandScale: false, type: 'yDomain', }, @@ -384,8 +385,9 @@ describe('Y Domain', () => { }); test('Should return a default Scale Linear for YScaleType when there are no specs', () => { - const specs: Pick[] = []; - expect(coerceYScaleTypes(specs)).toBe(ScaleType.Linear); + const specs: BasicSeriesSpec['yScaleType'][] = []; + const specScaleConfigs = specs.map(getYScaleConfig); + expect(coerceYScaleTypes(specScaleConfigs)).toEqual(getYScaleConfig(ScaleType.Linear)); }); test('Should merge Y domain accounting for custom domain limits: complete bounded domain', () => { @@ -409,7 +411,7 @@ describe('Y Domain', () => { type: 'yDomain', groupId: 'a', domain: [0, 20], - scaleType: ScaleType.Linear, + scaleConfig: getYScaleConfig(ScaleType.Linear), isBandScale: false, }, ]); @@ -435,7 +437,7 @@ describe('Y Domain', () => { type: 'yDomain', groupId: 'a', domain: [0, 12], - scaleType: ScaleType.Linear, + scaleConfig: getYScaleConfig(ScaleType.Linear), isBandScale: false, }, ]); @@ -484,7 +486,7 @@ describe('Y Domain', () => { type: 'yDomain', groupId: 'a', domain: [2, 20], - scaleType: ScaleType.Linear, + scaleConfig: getYScaleConfig(ScaleType.Linear), isBandScale: false, }, ]); @@ -533,7 +535,7 @@ describe('Y Domain', () => { { groupId: 'a', domain: [0, 1], - scaleType: ScaleType.Linear, + scaleConfig: getYScaleConfig(ScaleType.Linear), isBandScale: false, type: 'yDomain', }, @@ -562,7 +564,7 @@ describe('Y Domain', () => { type: 'yDomain', groupId: 'a', domain: [0, 1], - scaleType: ScaleType.Linear, + scaleConfig: getYScaleConfig(ScaleType.Linear), isBandScale: false, }, ]); diff --git a/src/chart_types/xy_chart/domains/y_domain.ts b/src/chart_types/xy_chart/domains/y_domain.ts index 4bb6199577..275308e742 100644 --- a/src/chart_types/xy_chart/domains/y_domain.ts +++ b/src/chart_types/xy_chart/domains/y_domain.ts @@ -23,11 +23,13 @@ import { identity } from '../../../utils/common'; import { computeContinuousDataDomain, ContinuousDomain } from '../../../utils/domain'; import { GroupId } from '../../../utils/ids'; import { Logger } from '../../../utils/logger'; +import { getYScaleConfig, ScaleConfig } from '../scales/get_scale_config'; import { getSpecDomainGroupId } from '../state/utils/spec'; import { isCompleteBound, isLowerBound, isUpperBound } from '../utils/axis_type_utils'; import { groupBy } from '../utils/group_data_series'; import { DataSeries } from '../utils/series'; import { BasicSeriesSpec, YDomainRange, SeriesTypes, StackMode } from '../utils/specs'; +import { areAllNiceDomain } from './nice'; import { YDomain } from './types'; export type YBasicSeriesSpec = Pick< @@ -68,9 +70,7 @@ function mergeYDomainForGroup( if (dataSeries.length === 0) { return null; } - const yScaleTypes = dataSeries.map(({ spec: { yScaleType } }) => ({ - yScaleType, - })); + const yScaleTypes = dataSeries.map(({ spec: { yScaleType } }) => getYScaleConfig(yScaleType)); const groupYScaleType = coerceYScaleTypes(yScaleTypes); const [{ stackMode, spec }] = dataSeries; const groupId = getSpecDomainGroupId(spec); @@ -119,7 +119,7 @@ function mergeYDomainForGroup( return { type: 'yDomain', isBandScale: false, - scaleType: groupYScaleType, + scaleConfig: groupYScaleType, groupId, domain, logBase: customDomain?.logBase, @@ -213,19 +213,23 @@ export function isStackedSpec(spec: YBasicSeriesSpec, histogramEnabled: boolean) * @returns {ScaleContinuousType} * @internal */ -export function coerceYScaleTypes(scales: { yScaleType: ScaleContinuousType }[]): ScaleContinuousType { - const scaleTypes = new Set(); - scales.forEach(({ yScaleType }) => { - scaleTypes.add(yScaleType); - }); - return coerceYScale(scaleTypes); -} - -function coerceYScale(scaleTypes: Set): ScaleContinuousType { - if (scaleTypes.size === 1) { - const scales = scaleTypes.values(); - const { value } = scales.next(); - return value; - } - return ScaleType.Linear; +export function coerceYScaleTypes(scales: ScaleConfig[]): ScaleConfig { + const scaleCollection = scales.reduce( + (acc, scale) => { + acc.types.add(scale.type); + acc.nice.push(scale.nice); + return acc; + }, + { + types: new Set(), + nice: [] as Array, + }, + ); + const nice = areAllNiceDomain(scaleCollection.nice); + return scaleCollection.types.size === 1 + ? { type: scaleCollection.types.values().next().value, nice } + : { + type: ScaleType.Linear, + nice, + }; } diff --git a/src/chart_types/xy_chart/legend/legend.ts b/src/chart_types/xy_chart/legend/legend.ts index 78180cd85e..71ed469860 100644 --- a/src/chart_types/xy_chart/legend/legend.ts +++ b/src/chart_types/xy_chart/legend/legend.ts @@ -24,6 +24,7 @@ import { SortSeriesByConfig, TickFormatterOptions } from '../../../specs'; import { Color } from '../../../utils/common'; import { BandedAccessorType } from '../../../utils/geometry'; import { getLegendCompareFn, SeriesCompareFn } from '../../../utils/series_sort'; +import { getXScaleConfig } from '../scales/get_scale_config'; import { getAxesSpecForSpecId, getSpecsById } from '../state/utils/spec'; import { LastValues } from '../state/utils/types'; import { Y0_ACCESSOR_POSTFIX, Y1_ACCESSOR_POSTFIX } from '../tooltip/tooltip'; @@ -137,6 +138,7 @@ export function computeLegend( const lastValue = lastValues.get(key); const seriesIdentifier = getSeriesIdentifierFromDataSeries(series); + const scaleConf = getXScaleConfig(spec.xScaleType); legendItems.push({ color, label: labelY1, @@ -145,7 +147,7 @@ export function computeLegend( isSeriesHidden, isItemHidden: hideInLegend, isToggleable: true, - defaultExtra: getLegendExtra(showLegendExtra, spec.xScaleType, formatter, 'y1', lastValue), + defaultExtra: getLegendExtra(showLegendExtra, scaleConf.type, formatter, 'y1', lastValue), path: [{ index: 0, value: seriesIdentifier.key }], keys: [specId, spec.groupId, yAccessor, ...series.splitAccessors.values()], }); @@ -159,7 +161,7 @@ export function computeLegend( isSeriesHidden, isItemHidden: hideInLegend, isToggleable: true, - defaultExtra: getLegendExtra(showLegendExtra, spec.xScaleType, formatter, 'y0', lastValue), + defaultExtra: getLegendExtra(showLegendExtra, scaleConf.type, formatter, 'y0', lastValue), path: [{ index: 0, value: seriesIdentifier.key }], keys: [specId, spec.groupId, yAccessor, ...series.splitAccessors.values()], }); diff --git a/src/chart_types/xy_chart/scales/get_scale_config.ts b/src/chart_types/xy_chart/scales/get_scale_config.ts new file mode 100644 index 0000000000..2fedab9d4b --- /dev/null +++ b/src/chart_types/xy_chart/scales/get_scale_config.ts @@ -0,0 +1,49 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you 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 { ScaleContinuousType } from '../../../scales'; +import { ScaleType } from '../../../scales/constants'; +import { BasicSeriesSpec, XScaleType } from '../utils/specs'; +import { X_SCALE_DEFAULT, Y_SCALE_DEFAULT } from './scale_defaults'; + +/** @internal */ +export interface ScaleConfig { + type: T; + nice: boolean; +} + +/** @internal */ +export function getXScaleConfig(scaleType: BasicSeriesSpec['xScaleType']): ScaleConfig { + return getScaleConfig(scaleType, X_SCALE_DEFAULT); +} + +/** @internal */ +export function getYScaleConfig(scaleType: BasicSeriesSpec['yScaleType']): ScaleConfig { + return getScaleConfig(scaleType, Y_SCALE_DEFAULT); +} + +/** @internal */ +function getScaleConfig(scaleType: T | ScaleConfig, defaults: ScaleConfig): ScaleConfig { + if (typeof scaleType === 'object') { + return scaleType; + } + return { + ...defaults, + type: scaleType, + }; +} diff --git a/src/chart_types/xy_chart/scales/scale_defaults.ts b/src/chart_types/xy_chart/scales/scale_defaults.ts new file mode 100644 index 0000000000..9c065998d9 --- /dev/null +++ b/src/chart_types/xy_chart/scales/scale_defaults.ts @@ -0,0 +1,31 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you 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 { ScaleType } from '../../../scales/constants'; + +/** @internal */ +export const X_SCALE_DEFAULT = { + type: ScaleType.Ordinal, + nice: false, +}; + +/** @internal */ +export const Y_SCALE_DEFAULT = { + type: ScaleType.Linear, + nice: false, +}; diff --git a/src/chart_types/xy_chart/state/utils/utils.test.ts b/src/chart_types/xy_chart/state/utils/utils.test.ts index 30e2685caf..cd319220c6 100644 --- a/src/chart_types/xy_chart/state/utils/utils.test.ts +++ b/src/chart_types/xy_chart/state/utils/utils.test.ts @@ -30,6 +30,7 @@ import { BARCHART_1Y0G, BARCHART_1Y1G } from '../../../../utils/data_samples/tes import { ContinuousDomain, Range } from '../../../../utils/domain'; import { SpecId } from '../../../../utils/ids'; import { PointShape } from '../../../../utils/themes/theme'; +import { getXScaleConfig, getYScaleConfig } from '../../scales/get_scale_config'; import { getSeriesIndex, XYChartSeriesIdentifier } from '../../utils/series'; import { BasicSeriesSpec, HistogramModeAlignments, SeriesColorAccessorFn } from '../../utils/specs'; import { computeSeriesDomainsSelector } from '../selectors/compute_series_domains'; @@ -80,14 +81,14 @@ describe('Chart State utils', () => { expect(domains.xDomain).toEqual({ domain: [0, 3], isBandScale: false, - scaleType: ScaleType.Linear, + scaleConfig: getXScaleConfig(ScaleType.Linear), minInterval: 1, type: 'xDomain', }); expect(domains.yDomains).toEqual([ { domain: [0, 10], - scaleType: ScaleType.Log, + scaleConfig: getYScaleConfig(ScaleType.Log), groupId: 'group1', isBandScale: false, type: 'yDomain', @@ -96,7 +97,7 @@ describe('Chart State utils', () => { }, { domain: [0, 10], - scaleType: ScaleType.Log, + scaleConfig: getYScaleConfig(ScaleType.Log), groupId: 'group2', isBandScale: false, type: 'yDomain', @@ -132,14 +133,14 @@ describe('Chart State utils', () => { expect(domains.xDomain).toEqual({ domain: [0, 3], isBandScale: false, - scaleType: ScaleType.Linear, + scaleConfig: getXScaleConfig(ScaleType.Linear), minInterval: 1, type: 'xDomain', }); expect(domains.yDomains).toEqual([ { domain: [0, 5], - scaleType: ScaleType.Log, + scaleConfig: getYScaleConfig(ScaleType.Log), groupId: 'group1', isBandScale: false, type: 'yDomain', @@ -148,7 +149,7 @@ describe('Chart State utils', () => { }, { domain: [0, 9], - scaleType: ScaleType.Log, + scaleConfig: getYScaleConfig(ScaleType.Log), groupId: 'group2', isBandScale: false, type: 'yDomain', diff --git a/src/chart_types/xy_chart/state/utils/utils.ts b/src/chart_types/xy_chart/state/utils/utils.ts index 39de5946be..0636c1cb66 100644 --- a/src/chart_types/xy_chart/state/utils/utils.ts +++ b/src/chart_types/xy_chart/state/utils/utils.ts @@ -146,15 +146,18 @@ export function computeSeriesDomains( const xDomain = mergeXDomain(seriesSpecs, xValues, customXDomain, fallbackScale); // fill series with missing x values - const filledDataSeries = fillSeries(dataSeries, xValues, xDomain.scaleType); + const filledDataSeries = fillSeries(dataSeries, xValues, xDomain.scaleConfig.type); const seriesSortFn = getRenderingCompareFn(sortSeriesBy, (a: SeriesIdentifier, b: SeriesIdentifier) => { return defaultXYSeriesSort(a as DataSeries, b as DataSeries); }); - const formattedDataSeries = getFormattedDataSeries(seriesSpecs, filledDataSeries, xValues, xDomain.scaleType).sort( - seriesSortFn, - ); + const formattedDataSeries = getFormattedDataSeries( + seriesSpecs, + filledDataSeries, + xValues, + xDomain.scaleConfig.type, + ).sort(seriesSortFn); // let's compute the yDomains after computing all stacked values const yDomains = mergeYDomain(formattedDataSeries, customYDomainsByGroupId); diff --git a/src/chart_types/xy_chart/utils/axis_utils.test.ts b/src/chart_types/xy_chart/utils/axis_utils.test.ts index abb06a23af..f085123784 100644 --- a/src/chart_types/xy_chart/utils/axis_utils.test.ts +++ b/src/chart_types/xy_chart/utils/axis_utils.test.ts @@ -35,6 +35,7 @@ import { AxisId, GroupId } from '../../../utils/ids'; import { LIGHT_THEME } from '../../../utils/themes/light_theme'; import { AxisStyle, TextOffset } from '../../../utils/themes/theme'; import { XDomain, YDomain } from '../domains/types'; +import { getXScaleConfig, getYScaleConfig } from '../scales/get_scale_config'; import { computeAxesGeometriesSelector } from '../state/selectors/compute_axes_geometries'; import { computeAxisTicksDimensionsSelector } from '../state/selectors/compute_axis_ticks_dimensions'; import { getScale, SmallMultipleScales } from '../state/selectors/compute_small_multiple_scales'; @@ -191,14 +192,14 @@ describe('Axis computational utils', () => { }); const xDomain: XDomain = { type: 'xDomain', - scaleType: ScaleType.Linear, + scaleConfig: getXScaleConfig(ScaleType.Linear), domain: [0, 1], isBandScale: false, minInterval: 0, }; const yDomain: YDomain = { - scaleType: ScaleType.Linear, + scaleConfig: getYScaleConfig(ScaleType.Linear), groupId: 'group_1', type: 'yDomain', domain: [0, 1], @@ -281,7 +282,7 @@ describe('Axis computational utils', () => { const bboxCalculator = new SvgTextBBoxCalculator(); const xDomain: XDomain = { type: 'xDomain', - scaleType: ScaleType.Time, + scaleConfig: getXScaleConfig(ScaleType.Time), domain: [1551438000000, 1551441300000], isBandScale: false, minInterval: 0, @@ -419,7 +420,7 @@ describe('Axis computational utils', () => { { label: '0.7', position: 30 + rotationalOffset, value: 0.7 }, { label: '0.8', position: 20 + rotationalOffset, value: 0.8 }, { label: '0.9', position: 10 + rotationalOffset, value: 0.9 }, - { label: '1', position: 0 + rotationalOffset, value: 1 }, + { label: '1', position: rotationalOffset, value: 1 }, ]; expect(axisPositions).toEqual(expectedAxisPositions); }); @@ -428,7 +429,7 @@ describe('Axis computational utils', () => { const enableHistogramMode = true; const xBandDomain: XDomain = { type: 'xDomain', - scaleType: ScaleType.Linear, + scaleConfig: getXScaleConfig(ScaleType.Linear), domain: [0, 100], isBandScale: true, minInterval: 10, @@ -450,7 +451,7 @@ describe('Axis computational utils', () => { const enableHistogramMode = true; const xBandDomain: XDomain = { type: 'xDomain', - scaleType: ScaleType.Time, + scaleConfig: getXScaleConfig(ScaleType.Time), domain: [1560438420000, 1560438510000], isBandScale: true, minInterval: 90000, @@ -489,7 +490,7 @@ describe('Axis computational utils', () => { const enableHistogramMode = true; const xBandDomain: XDomain = { type: 'xDomain', - scaleType: ScaleType.Time, + scaleConfig: getXScaleConfig(ScaleType.Time), domain: [1560438420000, 1560438420000], // a single datum scale will have the same value for domain start & end isBandScale: true, minInterval: 90000, @@ -1528,7 +1529,7 @@ describe('Axis computational utils', () => { isBandScale: false, domain: [1547190000000, 1547622000000], minInterval: 86400000, - scaleType: ScaleType.Time, + scaleConfig: getXScaleConfig(ScaleType.Time), }; const scale: Scale = computeXScale({ xDomain: xDomainTime, totalBarsInCluster: 0, range: [0, 603.5] }); const offset = 0; @@ -1563,7 +1564,7 @@ describe('Axis computational utils', () => { timeZone: 'utc+1', domain: [1547190000000, 1547622000000], minInterval: 86400000, - scaleType: ScaleType.Time, + scaleConfig: getXScaleConfig(ScaleType.Time), }; const scale: Scale = computeXScale({ xDomain: xDomainTime, totalBarsInCluster: 0, range: [0, 603.5] }); const offset = 0; @@ -1605,7 +1606,7 @@ describe('Axis computational utils', () => { isBandScale: false, domain: [1547190000000, 1547622000000], minInterval: 86400000, - scaleType: ScaleType.Time, + scaleConfig: getXScaleConfig(ScaleType.Time), }; const scale: Scale = computeXScale({ xDomain: xDomainTime, totalBarsInCluster: 0, range: [0, 603.5] }); const offset = 0; @@ -1645,7 +1646,7 @@ describe('Axis computational utils', () => { isBandScale: false, domain: [1547190000000, 1547622000000], minInterval: 86400000, - scaleType: ScaleType.Time, + scaleConfig: getXScaleConfig(ScaleType.Time), }; const scale: Scale = computeXScale({ xDomain: xDomainTime, totalBarsInCluster: 0, range: [0, 603.5] }); const offset = 0; diff --git a/src/chart_types/xy_chart/utils/scales.test.ts b/src/chart_types/xy_chart/utils/scales.test.ts index b2b9c3a4d7..6241d02008 100644 --- a/src/chart_types/xy_chart/utils/scales.test.ts +++ b/src/chart_types/xy_chart/utils/scales.test.ts @@ -27,7 +27,10 @@ describe('Series scales', () => { isBandScale: true, domain: [0, 3], minInterval: 1, - scaleType: ScaleType.Linear, + scaleConfig: { + type: ScaleType.Linear, + nice: false, + }, }; const xDomainOrdinal: XDomain = { @@ -35,7 +38,10 @@ describe('Series scales', () => { isBandScale: true, domain: ['a', 'b'], minInterval: 1, - scaleType: ScaleType.Ordinal, + scaleConfig: { + type: ScaleType.Ordinal, + nice: false, + }, }; test('should compute X Scale linear min, max with bands', () => { @@ -69,7 +75,10 @@ describe('Series scales', () => { isBandScale: true, domain: [singleDomainValue, singleDomainValue], minInterval, - scaleType: ScaleType.Linear, + scaleConfig: { + type: ScaleType.Linear, + nice: false, + }, }; const enableHistogramMode = true; @@ -92,7 +101,10 @@ describe('Series scales', () => { isBandScale: true, domain: [singleDomainValue, singleDomainValue], minInterval, - scaleType: ScaleType.Linear, + scaleConfig: { + type: ScaleType.Linear, + nice: false, + }, }; const enableHistogramMode = false; @@ -126,7 +138,10 @@ describe('Series scales', () => { isBandScale: true, domain: [0, 3], minInterval: 1, - scaleType: ScaleType.Linear, + scaleConfig: { + type: ScaleType.Linear, + nice: false, + }, }; const maxRange = 120; const scaleOver0 = computeXScale({ diff --git a/src/chart_types/xy_chart/utils/scales.ts b/src/chart_types/xy_chart/utils/scales.ts index 985298067b..5f7d44e181 100644 --- a/src/chart_types/xy_chart/utils/scales.ts +++ b/src/chart_types/xy_chart/utils/scales.ts @@ -54,17 +54,14 @@ interface XScaleOptions { /** * Compute the x scale used to align geometries to the x axis. - * @param xDomain the x domain - * @param totalBarsInCluster the total number of grouped series - * @param axisLength the length of the x axis * @internal */ export function computeXScale(options: XScaleOptions): Scale { const { xDomain, totalBarsInCluster, range, barsPadding, enableHistogramMode, ticks, integersOnly } = options; - const { scaleType, minInterval, domain, isBandScale, timeZone, logBase } = xDomain; + const { scaleConfig, minInterval, domain, isBandScale, timeZone, logBase } = xDomain; const rangeDiff = Math.abs(range[1] - range[0]); const isInverse = range[1] < range[0]; - if (scaleType === ScaleType.Ordinal) { + if (scaleConfig.type === ScaleType.Ordinal) { const dividend = totalBarsInCluster > 0 ? totalBarsInCluster : 1; const bandwidth = rangeDiff / (domain.length * dividend); return new ScaleBand(domain, range, bandwidth, barsPadding); @@ -77,15 +74,16 @@ export function computeXScale(options: XScaleOptions): Scale { const adjustedDomain = [domainMin, adjustedDomainMax]; const intervalCount = (adjustedDomain[1] - adjustedDomain[0]) / minInterval; - const intervalCountOffest = isSingleValueHistogram ? 0 : 1; - const bandwidth = rangeDiff / (intervalCount + intervalCountOffest); + const intervalCountOffset = isSingleValueHistogram ? 0 : 1; + const bandwidth = rangeDiff / (intervalCount + intervalCountOffset); const { start, end } = getBandScaleRange(isInverse, isSingleValueHistogram, range[0], range[1], bandwidth); - const scale = new ScaleContinuous( + return new ScaleContinuous( { - type: scaleType, + type: scaleConfig.type, domain: adjustedDomain, range: [start, end], + nice: scaleConfig.nice, }, { bandwidth: totalBarsInCluster > 0 ? bandwidth / totalBarsInCluster : bandwidth, @@ -98,11 +96,9 @@ export function computeXScale(options: XScaleOptions): Scale { logBase, }, ); - - return scale; } return new ScaleContinuous( - { type: scaleType, domain, range }, + { type: scaleConfig.type, domain, range, nice: scaleConfig.nice }, { bandwidth: 0, minInterval, @@ -125,19 +121,17 @@ interface YScaleOptions { /** * Compute the y scales, one per groupId for the y axis. - * @param yDomains the y domains - * @param axisLength the axisLength of the y axis * @internal */ export function computeYScales(options: YScaleOptions): Map { - const yScales: Map = new Map(); const { yDomains, range, ticks, integersOnly } = options; - yDomains.forEach(({ scaleType: type, domain, groupId, logBase, logMinLimit }) => { + return yDomains.reduce((yScales, { scaleConfig: { type, nice }, domain, groupId, logBase, logMinLimit }) => { const yScale = new ScaleContinuous( { type, domain, range, + nice, }, { ticks, @@ -147,7 +141,6 @@ export function computeYScales(options: YScaleOptions): Map { }, ); yScales.set(groupId, yScale); - }); - - return yScales; + return yScales; + }, new Map()); } diff --git a/src/chart_types/xy_chart/utils/series.ts b/src/chart_types/xy_chart/utils/series.ts index 5bb674e656..5afe82ab1e 100644 --- a/src/chart_types/xy_chart/utils/series.ts +++ b/src/chart_types/xy_chart/utils/series.ts @@ -28,6 +28,7 @@ import { GroupId } from '../../../utils/ids'; import { Logger } from '../../../utils/logger'; import { ColorConfig } from '../../../utils/themes/theme'; import { groupSeriesByYGroup, isHistogramEnabled, isStackedSpec } from '../domains/y_domain'; +import { ScaleConfig } from '../scales/get_scale_config'; import { SmallMultiplesGroupBy } from '../state/selectors/get_specs'; import { applyFitFunctionToDataSeries } from './fit_function_utils'; import { groupBy } from './group_data_series'; @@ -359,7 +360,7 @@ export function getDataSeriesFromSpecs( xValues: Set; smVValues: Set; smHValues: Set; - fallbackScale?: XScaleType; + fallbackScale?: ScaleConfig; } { let globalDataSeries: DataSeries[] = []; const mutatedXValueSums = new Map(); @@ -454,7 +455,7 @@ export function getDataSeriesFromSpecs( // keep the user order for ordinal scales xValues, ...smallMultipleUniqueValues, - fallbackScale: !isOrdinalScale && !isNumberArray ? ScaleType.Ordinal : undefined, + fallbackScale: !isOrdinalScale && !isNumberArray ? { type: ScaleType.Ordinal, nice: false } : undefined, }; } diff --git a/src/chart_types/xy_chart/utils/specs.ts b/src/chart_types/xy_chart/utils/specs.ts index 018cdc304f..051c139c73 100644 --- a/src/chart_types/xy_chart/utils/specs.ts +++ b/src/chart_types/xy_chart/utils/specs.ts @@ -44,6 +44,7 @@ import { } from '../../../utils/themes/theme'; import { PrimitiveValue } from '../../partition_chart/layout/utils/group_by_rollup'; import { AnnotationTooltipFormatter, CustomAnnotationTooltip } from '../annotations/types'; +import { ScaleConfig } from '../scales/get_scale_config'; import { XYChartSeriesIdentifier, DataSeriesDatum } from './series'; /** @public */ @@ -452,7 +453,7 @@ export interface SeriesScales { * The x axis scale type * @defaultValue `ordinal` {@link (ScaleType:type) | ScaleType.Ordinal} */ - xScaleType: XScaleType; + xScaleType: XScaleType | ScaleConfig; /** * If using a ScaleType.Time this timezone identifier is required to * compute a nice set of xScale ticks. Can be any IANA zone supported by @@ -464,7 +465,7 @@ export interface SeriesScales { * The y axis scale type * @defaultValue `linear` {@link (ScaleType:type) | ScaleType.Linear} */ - yScaleType: ScaleContinuousType; + yScaleType: ScaleContinuousType | ScaleConfig; /** * if true, the min y value is set to the minimum domain value, 0 otherwise * @deprecated use `domain.fit` instead diff --git a/src/scales/scale_continuous.test.ts b/src/scales/scale_continuous.test.ts index 84345fe663..6305260c56 100644 --- a/src/scales/scale_continuous.test.ts +++ b/src/scales/scale_continuous.test.ts @@ -21,6 +21,7 @@ import { DateTime, Settings } from 'luxon'; import { ScaleContinuous, ScaleBand } from '.'; import { XDomain } from '../chart_types/xy_chart/domains/types'; +import { getXScaleConfig } from '../chart_types/xy_chart/scales/get_scale_config'; import { computeXScale } from '../chart_types/xy_chart/utils/scales'; import { ContinuousDomain, Range } from '../utils/domain'; import { LOG_MIN_ABS_DOMAIN, ScaleType } from './constants'; @@ -105,7 +106,7 @@ describe('Scale Continuous', () => { domain: [0, 2], isBandScale: true, minInterval: 1, - scaleType: ScaleType.Linear, + scaleConfig: getXScaleConfig(ScaleType.Linear), type: 'xDomain', }; @@ -146,7 +147,7 @@ describe('Scale Continuous', () => { domain: [0, 100], isBandScale: true, minInterval: 10, - scaleType: ScaleType.Linear, + scaleConfig: getXScaleConfig(ScaleType.Linear), type: 'xDomain', }; // we tweak the maxRange removing the bandwidth to correctly compute diff --git a/src/scales/scale_continuous.ts b/src/scales/scale_continuous.ts index 3c7d258bc6..67d457a26f 100644 --- a/src/scales/scale_continuous.ts +++ b/src/scales/scale_continuous.ts @@ -151,6 +151,7 @@ interface ScaleData { domain: any[]; /** The data output range */ range: Range; + nice?: boolean; } /** @@ -261,8 +262,10 @@ export class ScaleContinuous implements Scale { private readonly d3Scale: D3Scale; - constructor(scaleData: ScaleData, options?: Partial) { - const { type, domain, range } = scaleData; + constructor( + { type = ScaleType.Linear, domain = [0, 1], range = [0, 1], nice = false }: ScaleData, + options?: Partial, + ) { const { bandwidth, minInterval, @@ -286,8 +289,8 @@ export class ScaleContinuous implements Scale { } this.d3Scale.domain(this.domain); - if (type !== ScaleType.Time) { - (this.d3Scale as ScaleContinuousNumeric).domain(this.domain).nice(); + if (nice && type !== ScaleType.Time) { + (this.d3Scale as ScaleContinuousNumeric).domain(this.domain).nice(ticks); this.domain = this.d3Scale.domain(); } diff --git a/src/scales/scales.test.ts b/src/scales/scales.test.ts index bcb50ab5a5..9a8664ab0b 100644 --- a/src/scales/scales.test.ts +++ b/src/scales/scales.test.ts @@ -46,7 +46,12 @@ describe('Scale Test', () => { const data = [0, 10]; const minRange = 0; const maxRange = 100; - const linearScale = new ScaleContinuous({ type: ScaleType.Linear, domain: data, range: [minRange, maxRange] }); + const linearScale = new ScaleContinuous({ + type: ScaleType.Linear, + domain: data, + range: [minRange, maxRange], + nice: false, + }); const { domain, range } = linearScale; expect(domain).toEqual([0, 10]); expect(range).toEqual([minRange, maxRange]); @@ -66,7 +71,12 @@ describe('Scale Test', () => { const data = [date1, date3]; const minRange = 0; const maxRange = 100; - const timeScale = new ScaleContinuous({ type: ScaleType.Time, domain: data, range: [minRange, maxRange] }); + const timeScale = new ScaleContinuous({ + type: ScaleType.Time, + domain: data, + range: [minRange, maxRange], + nice: false, + }); const { domain, range } = timeScale; expect(domain).toEqual([date1, date3]); expect(range).toEqual([minRange, maxRange]); @@ -81,7 +91,12 @@ describe('Scale Test', () => { const data = [1, 10]; const minRange = 0; const maxRange = 100; - const logScale = new ScaleContinuous({ type: ScaleType.Log, domain: data, range: [minRange, maxRange] }); + const logScale = new ScaleContinuous({ + type: ScaleType.Log, + domain: data, + range: [minRange, maxRange], + nice: false, + }); const { domain, range } = logScale; expect(domain).toEqual([1, 10]); expect(range).toEqual([minRange, maxRange]); @@ -94,7 +109,12 @@ describe('Scale Test', () => { const data = [0, 10]; const minRange = 0; const maxRange = 100; - const logScale = new ScaleContinuous({ type: ScaleType.Log, domain: data, range: [minRange, maxRange] }); + const logScale = new ScaleContinuous({ + type: ScaleType.Log, + domain: data, + range: [minRange, maxRange], + nice: false, + }); const { domain, range } = logScale; expect(domain).toEqual([1, 10]); expect(range).toEqual([minRange, maxRange]); @@ -107,7 +127,12 @@ describe('Scale Test', () => { const data = [0, 10]; const minRange = 0; const maxRange = 100; - const sqrtScale = new ScaleContinuous({ type: ScaleType.Sqrt, domain: data, range: [minRange, maxRange] }); + const sqrtScale = new ScaleContinuous({ + type: ScaleType.Sqrt, + domain: data, + range: [minRange, maxRange], + nice: false, + }); const { domain, range } = sqrtScale; expect(domain).toEqual([0, 10]); expect(range).toEqual([minRange, maxRange]); @@ -165,7 +190,7 @@ describe('Scale Test', () => { const maxRange = 120; const bandwidth = maxRange / 3; const linearScale = new ScaleContinuous( - { type: ScaleType.Linear, domain: domainLinear, range: [minRange, maxRange - bandwidth] }, // we currently limit the range like that a band linear scale + { type: ScaleType.Linear, domain: domainLinear, range: [minRange, maxRange - bandwidth], nice: false }, // we currently limit the range like that a band linear scale { bandwidth, minInterval: 1 }, ); const ordinalScale = new ScaleBand(domainOrdinal, [minRange, maxRange]); @@ -188,6 +213,7 @@ describe('Scale Test', () => { type: ScaleType.Linear, domain: dataLinear, range: [minRange, maxRange - bandwidth], + nice: false, }, // we currently limit the range like that a band linear scale { bandwidth, minInterval: 1 }, ); diff --git a/stories/legend/11_legend_actions.tsx b/stories/legend/11_legend_actions.tsx index 143b913060..9cbc5b71d0 100644 --- a/stories/legend/11_legend_actions.tsx +++ b/stories/legend/11_legend_actions.tsx @@ -124,7 +124,6 @@ const getAction = (hideActions: boolean, anchorPosition: PopoverAnchorPosition): isOpen={popoverOpen} closePopover={() => setPopoverOpen(false)} panelPaddingSize="none" - withTitle anchorPosition={anchorPosition} ownFocus >