feat(plugin-chart-echarts): add x-axis sort to multi series (#23644)

This commit is contained in:
Ville Brofeldt 2023-04-12 16:13:41 +03:00 committed by GitHub
parent 587e7759b1
commit f49702feff
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 317 additions and 63 deletions

View File

@ -24,7 +24,7 @@ import {
QueryColumn, QueryColumn,
DatasourceType, DatasourceType,
} from '@superset-ui/core'; } from '@superset-ui/core';
import { ColumnMeta } from './types'; import { ColumnMeta, SortSeriesData, SortSeriesType } from './types';
// eslint-disable-next-line import/prefer-default-export // eslint-disable-next-line import/prefer-default-export
export const TIME_FILTER_LABELS = { export const TIME_FILTER_LABELS = {
@ -57,3 +57,21 @@ export const QueryModeLabel = {
[QueryMode.aggregate]: t('Aggregate'), [QueryMode.aggregate]: t('Aggregate'),
[QueryMode.raw]: t('Raw records'), [QueryMode.raw]: t('Raw records'),
}; };
export const DEFAULT_SORT_SERIES_DATA: SortSeriesData = {
sort_series_type: SortSeriesType.Sum,
sort_series_ascending: false,
};
export const SORT_SERIES_CHOICES = [
[SortSeriesType.Name, t('Category name')],
[SortSeriesType.Sum, t('Total value')],
[SortSeriesType.Min, t('Minimum value')],
[SortSeriesType.Max, t('Maximum value')],
[SortSeriesType.Avg, t('Average value')],
];
export const DEFAULT_XAXIS_SORT_SERIES_DATA: SortSeriesData = {
sort_series_type: SortSeriesType.Name,
sort_series_ascending: true,
};

View File

@ -20,8 +20,10 @@ import { hasGenericChartAxes, t } from '@superset-ui/core';
import { ControlPanelSectionConfig, ControlSetRow } from '../types'; import { ControlPanelSectionConfig, ControlSetRow } from '../types';
import { import {
contributionModeControl, contributionModeControl,
xAxisSortControl,
xAxisSortAscControl, xAxisSortAscControl,
xAxisSortControl,
xAxisSortSeriesAscendingControl,
xAxisSortSeriesControl,
} from '../shared-controls'; } from '../shared-controls';
const controlsWithoutXAxis: ControlSetRow[] = [ const controlsWithoutXAxis: ControlSetRow[] = [
@ -55,6 +57,8 @@ export const echartsTimeSeriesQueryWithXAxisSort: ControlPanelSectionConfig = {
[hasGenericChartAxes ? 'time_grain_sqla' : null], [hasGenericChartAxes ? 'time_grain_sqla' : null],
[hasGenericChartAxes ? xAxisSortControl : null], [hasGenericChartAxes ? xAxisSortControl : null],
[hasGenericChartAxes ? xAxisSortAscControl : null], [hasGenericChartAxes ? xAxisSortAscControl : null],
[hasGenericChartAxes ? xAxisSortSeriesControl : null],
[hasGenericChartAxes ? xAxisSortSeriesAscendingControl : null],
...controlsWithoutXAxis, ...controlsWithoutXAxis,
], ],
}; };

View File

@ -34,6 +34,10 @@ import {
isDataset, isDataset,
} from '../types'; } from '../types';
import { isTemporalColumn } from '../utils'; import { isTemporalColumn } from '../utils';
import {
DEFAULT_XAXIS_SORT_SERIES_DATA,
SORT_SERIES_CHOICES,
} from '../constants';
export const contributionModeControl = { export const contributionModeControl = {
name: 'contributionMode', name: 'contributionMode',
@ -59,6 +63,19 @@ const xAxisSortVisibility = ({ controls }: { controls: ControlStateMapping }) =>
Array.isArray(controls?.groupby?.value) && Array.isArray(controls?.groupby?.value) &&
controls.groupby.value.length === 0; controls.groupby.value.length === 0;
const xAxisMultiSortVisibility = ({
controls,
}: {
controls: ControlStateMapping;
}) =>
isDefined(controls?.x_axis?.value) &&
!isTemporalColumn(
getColumnLabel(controls?.x_axis?.value as QueryFormColumn),
controls?.datasource?.datasource,
) &&
Array.isArray(controls?.groupby?.value) &&
!!controls.groupby.value.length;
export const xAxisSortControl = { export const xAxisSortControl = {
name: 'x_axis_sort', name: 'x_axis_sort',
config: { config: {
@ -125,3 +142,35 @@ export const xAxisSortAscControl = {
visibility: xAxisSortVisibility, visibility: xAxisSortVisibility,
}, },
}; };
export const xAxisSortSeriesControl = {
name: 'x_axis_sort_series',
config: {
type: 'SelectControl',
freeForm: false,
label: (state: ControlPanelState) =>
state.form_data?.orientation === 'horizontal'
? t('Y-Axis Sort By')
: t('X-Axis Sort By'),
choices: SORT_SERIES_CHOICES,
default: DEFAULT_XAXIS_SORT_SERIES_DATA.sort_series_type,
renderTrigger: true,
description: t('Decides which measure to sort the base axis by.'),
visibility: xAxisMultiSortVisibility,
},
};
export const xAxisSortSeriesAscendingControl = {
name: 'x_axis_sort_series_ascending',
config: {
type: 'CheckboxControl',
label: (state: ControlPanelState) =>
state.form_data?.orientation === 'horizontal'
? t('Y-Axis Sort Ascending')
: t('X-Axis Sort Ascending'),
default: DEFAULT_XAXIS_SORT_SERIES_DATA.sort_series_ascending,
description: t('Whether to sort ascending or descending on the base Axis.'),
renderTrigger: true,
visibility: xAxisMultiSortVisibility,
},
};

View File

@ -481,3 +481,16 @@ export function isQueryResponse(
): datasource is QueryResponse { ): datasource is QueryResponse {
return !!datasource && 'results' in datasource && 'sql' in datasource; return !!datasource && 'results' in datasource && 'sql' in datasource;
} }
export enum SortSeriesType {
Name = 'name',
Max = 'max',
Min = 'min',
Sum = 'sum',
Avg = 'avg',
}
export type SortSeriesData = {
sort_series_type: SortSeriesType;
sort_series_ascending: boolean;
};

View File

@ -16,7 +16,10 @@
* specific language governing permissions and limitations * specific language governing permissions and limitations
* under the License. * under the License.
*/ */
import { sections } from '@superset-ui/chart-controls'; import {
DEFAULT_SORT_SERIES_DATA,
sections,
} from '@superset-ui/chart-controls';
import { t } from '@superset-ui/core'; import { t } from '@superset-ui/core';
import { import {
OrientationType, OrientationType,
@ -25,7 +28,6 @@ import {
} from './types'; } from './types';
import { import {
DEFAULT_LEGEND_FORM_DATA, DEFAULT_LEGEND_FORM_DATA,
DEFAULT_SORT_SERIES_DATA,
DEFAULT_TITLE_FORM_DATA, DEFAULT_TITLE_FORM_DATA,
} from '../constants'; } from '../constants';

View File

@ -146,6 +146,8 @@ export default function transformProps(
truncateYAxis, truncateYAxis,
xAxis: xAxisOrig, xAxis: xAxisOrig,
xAxisLabelRotation, xAxisLabelRotation,
xAxisSortSeries,
xAxisSortSeriesAscending,
xAxisTimeFormat, xAxisTimeFormat,
xAxisTitle, xAxisTitle,
xAxisTitleMargin, xAxisTitleMargin,
@ -200,6 +202,10 @@ export default function transformProps(
isHorizontal, isHorizontal,
sortSeriesType, sortSeriesType,
sortSeriesAscending, sortSeriesAscending,
xAxisSortSeries: groupby.length ? xAxisSortSeries : undefined,
xAxisSortSeriesAscending: groupby.length
? xAxisSortSeriesAscending
: undefined,
}); });
const showValueIndexes = extractShowValueIndexes(rawSeries, { const showValueIndexes = extractShowValueIndexes(rawSeries, {
stack, stack,

View File

@ -24,8 +24,6 @@ import {
LegendFormData, LegendFormData,
LegendOrientation, LegendOrientation,
LegendType, LegendType,
SortSeriesData,
SortSeriesType,
TitleFormData, TitleFormData,
} from './types'; } from './types';
@ -122,8 +120,3 @@ export const TOOLTIP_POINTER_MARGIN = 10;
// If no satisfactory position can be found, how far away // If no satisfactory position can be found, how far away
// from the edge of the window should the tooltip be kept // from the edge of the window should the tooltip be kept
export const TOOLTIP_OVERFLOW_MARGIN = 5; export const TOOLTIP_OVERFLOW_MARGIN = 5;
export const DEFAULT_SORT_SERIES_DATA: SortSeriesData = {
sort_series_type: SortSeriesType.Sum,
sort_series_ascending: false,
};

View File

@ -22,15 +22,12 @@ import {
ControlPanelsContainerProps, ControlPanelsContainerProps,
ControlSetItem, ControlSetItem,
ControlSetRow, ControlSetRow,
DEFAULT_SORT_SERIES_DATA,
SORT_SERIES_CHOICES,
sharedControls, sharedControls,
} from '@superset-ui/chart-controls'; } from '@superset-ui/chart-controls';
import { import { DEFAULT_LEGEND_FORM_DATA, StackControlOptions } from './constants';
DEFAULT_LEGEND_FORM_DATA,
DEFAULT_SORT_SERIES_DATA,
StackControlOptions,
} from './constants';
import { DEFAULT_FORM_DATA } from './Timeseries/constants'; import { DEFAULT_FORM_DATA } from './Timeseries/constants';
import { SortSeriesType } from './types';
const { legendMargin, legendOrientation, legendType, showLegend } = const { legendMargin, legendOrientation, legendType, showLegend } =
DEFAULT_LEGEND_FORM_DATA; DEFAULT_LEGEND_FORM_DATA;
@ -225,13 +222,7 @@ const sortSeriesType: ControlSetItem = {
type: 'SelectControl', type: 'SelectControl',
freeForm: false, freeForm: false,
label: t('Sort Series By'), label: t('Sort Series By'),
choices: [ choices: SORT_SERIES_CHOICES,
[SortSeriesType.Name, t('Category name')],
[SortSeriesType.Sum, t('Total value')],
[SortSeriesType.Min, t('Minimum value')],
[SortSeriesType.Max, t('Maximum value')],
[SortSeriesType.Avg, t('Average value')],
],
default: DEFAULT_SORT_SERIES_DATA.sort_series_type, default: DEFAULT_SORT_SERIES_DATA.sort_series_type,
renderTrigger: true, renderTrigger: true,
description: t( description: t(

View File

@ -167,17 +167,4 @@ export interface TreePathInfo {
value: number | number[]; value: number | number[];
} }
export enum SortSeriesType {
Name = 'name',
Max = 'max',
Min = 'min',
Sum = 'sum',
Avg = 'avg',
}
export type SortSeriesData = {
sort_series_type: SortSeriesType;
sort_series_ascending: boolean;
};
export * from './Timeseries/types'; export * from './Timeseries/types';

View File

@ -18,6 +18,7 @@
* under the License. * under the License.
*/ */
import { import {
AxisType,
ChartDataResponseResult, ChartDataResponseResult,
DataRecord, DataRecord,
DataRecordValue, DataRecordValue,
@ -27,22 +28,17 @@ import {
NumberFormats, NumberFormats,
NumberFormatter, NumberFormatter,
TimeFormatter, TimeFormatter,
AxisType,
SupersetTheme, SupersetTheme,
} from '@superset-ui/core'; } from '@superset-ui/core';
import { SortSeriesType } from '@superset-ui/chart-controls';
import { format, LegendComponentOption, SeriesOption } from 'echarts'; import { format, LegendComponentOption, SeriesOption } from 'echarts';
import { sumBy, meanBy, minBy, maxBy, orderBy } from 'lodash'; import { maxBy, meanBy, minBy, orderBy, sumBy } from 'lodash';
import { import {
StackControlsValue,
NULL_STRING, NULL_STRING,
StackControlsValue,
TIMESERIES_CONSTANTS, TIMESERIES_CONSTANTS,
} from '../constants'; } from '../constants';
import { import { LegendOrientation, LegendType, StackType } from '../types';
LegendOrientation,
LegendType,
SortSeriesType,
StackType,
} from '../types';
import { defaultLegendPadding } from '../defaults'; import { defaultLegendPadding } from '../defaults';
function isDefined<T>(value: T | undefined | null): boolean { function isDefined<T>(value: T | undefined | null): boolean {
@ -155,6 +151,84 @@ export function sortAndFilterSeries(
).map(({ name }) => name); ).map(({ name }) => name);
} }
export function sortRows(
rows: DataRecord[],
xAxis: string,
xAxisSortSeries: SortSeriesType,
xAxisSortSeriesAscending: boolean,
) {
const sortedRows = rows.map(row => {
let sortKey: DataRecordValue = '';
let aggregate: number | undefined;
let entries = 0;
Object.entries(row).forEach(([key, value]) => {
const isValueDefined = isDefined(value);
if (key === xAxis) {
sortKey = value;
}
if (
xAxisSortSeries === SortSeriesType.Name ||
typeof value !== 'number'
) {
return;
}
if (!(xAxisSortSeries === SortSeriesType.Avg && !isValueDefined)) {
entries += 1;
}
switch (xAxisSortSeries) {
case SortSeriesType.Avg:
case SortSeriesType.Sum:
if (aggregate === undefined) {
aggregate = value;
} else {
aggregate += value;
}
break;
case SortSeriesType.Min:
aggregate =
aggregate === undefined || (isValueDefined && value < aggregate)
? value
: aggregate;
break;
case SortSeriesType.Max:
aggregate =
aggregate === undefined || (isValueDefined && value > aggregate)
? value
: aggregate;
break;
default:
break;
}
});
if (
xAxisSortSeries === SortSeriesType.Avg &&
entries > 0 &&
aggregate !== undefined
) {
aggregate /= entries;
}
const value =
xAxisSortSeries === SortSeriesType.Name && typeof sortKey === 'string'
? sortKey.toLowerCase()
: aggregate;
return {
key: sortKey,
value,
row,
};
});
return orderBy(
sortedRows,
['value'],
[xAxisSortSeriesAscending ? 'asc' : 'desc'],
).map(({ row }) => row);
}
export function extractSeries( export function extractSeries(
data: DataRecord[], data: DataRecord[],
opts: { opts: {
@ -167,6 +241,8 @@ export function extractSeries(
isHorizontal?: boolean; isHorizontal?: boolean;
sortSeriesType?: SortSeriesType; sortSeriesType?: SortSeriesType;
sortSeriesAscending?: boolean; sortSeriesAscending?: boolean;
xAxisSortSeries?: SortSeriesType;
xAxisSortSeriesAscending?: boolean;
} = {}, } = {},
): SeriesOption[] { ): SeriesOption[] {
const { const {
@ -179,24 +255,30 @@ export function extractSeries(
isHorizontal = false, isHorizontal = false,
sortSeriesType, sortSeriesType,
sortSeriesAscending, sortSeriesAscending,
xAxisSortSeries,
xAxisSortSeriesAscending,
} = opts; } = opts;
if (data.length === 0) return []; if (data.length === 0) return [];
const rows: DataRecord[] = data.map(datum => ({ const rows: DataRecord[] = data.map(datum => ({
...datum, ...datum,
[xAxis]: datum[xAxis], [xAxis]: datum[xAxis],
})); }));
const series = sortAndFilterSeries( const sortedSeries = sortAndFilterSeries(
rows, rows,
xAxis, xAxis,
extraMetricLabels, extraMetricLabels,
sortSeriesType, sortSeriesType,
sortSeriesAscending, sortSeriesAscending,
); );
const sortedRows =
isDefined(xAxisSortSeries) && isDefined(xAxisSortSeriesAscending)
? sortRows(rows, xAxis, xAxisSortSeries!, xAxisSortSeriesAscending!)
: rows;
return series.map(name => ({ return sortedSeries.map(name => ({
id: name, id: name,
name, name,
data: rows data: sortedRows
.map((row, idx) => { .map((row, idx) => {
const isNextToDefinedValue = const isNextToDefinedValue =
isDefined(rows[idx - 1]?.[name]) || isDefined(rows[idx + 1]?.[name]); isDefined(rows[idx - 1]?.[name]) || isDefined(rows[idx + 1]?.[name]);

View File

@ -16,6 +16,7 @@
* specific language governing permissions and limitations * specific language governing permissions and limitations
* under the License. * under the License.
*/ */
import { SortSeriesType } from '@superset-ui/chart-controls';
import { import {
DataRecord, DataRecord,
getNumberFormatter, getNumberFormatter,
@ -33,8 +34,9 @@ import {
getOverMaxHiddenFormatter, getOverMaxHiddenFormatter,
sanitizeHtml, sanitizeHtml,
sortAndFilterSeries, sortAndFilterSeries,
sortRows,
} from '../../src/utils/series'; } from '../../src/utils/series';
import { LegendOrientation, LegendType, SortSeriesType } from '../../src/types'; import { LegendOrientation, LegendType } from '../../src/types';
import { defaultLegendPadding } from '../../src/defaults'; import { defaultLegendPadding } from '../../src/defaults';
import { NULL_STRING } from '../../src/constants'; import { NULL_STRING } from '../../src/constants';
@ -48,42 +50,149 @@ const expectedThemeProps = {
}, },
}; };
test('sortAndFilterSeries', () => { const sortData: DataRecord[] = [
const data: DataRecord[] = [ { my_x_axis: 'abc', x: 1, y: 0, z: 2 },
{ my_x_axis: 'foo', x: null, y: 10, z: 5 },
{ my_x_axis: null, x: 4, y: 3, z: 7 },
];
test('sortRows by name ascending', () => {
expect(sortRows(sortData, 'my_x_axis', SortSeriesType.Name, true)).toEqual([
{ my_x_axis: 'abc', x: 1, y: 0, z: 2 }, { my_x_axis: 'abc', x: 1, y: 0, z: 2 },
{ my_x_axis: 'foo', x: null, y: 10, z: 5 }, { my_x_axis: 'foo', x: null, y: 10, z: 5 },
{ my_x_axis: null, x: 4, y: 3, z: 7 }, { my_x_axis: null, x: 4, y: 3, z: 7 },
]; ]);
});
test('sortRows by name descending', () => {
expect(sortRows(sortData, 'my_x_axis', SortSeriesType.Name, false)).toEqual([
{ my_x_axis: null, x: 4, y: 3, z: 7 },
{ my_x_axis: 'foo', x: null, y: 10, z: 5 },
{ my_x_axis: 'abc', x: 1, y: 0, z: 2 },
]);
});
test('sortRows by sum ascending', () => {
expect(sortRows(sortData, 'my_x_axis', SortSeriesType.Sum, true)).toEqual([
{ my_x_axis: 'abc', x: 1, y: 0, z: 2 },
{ my_x_axis: null, x: 4, y: 3, z: 7 },
{ my_x_axis: 'foo', x: null, y: 10, z: 5 },
]);
});
test('sortRows by sum descending', () => {
expect(sortRows(sortData, 'my_x_axis', SortSeriesType.Sum, false)).toEqual([
{ my_x_axis: 'foo', x: null, y: 10, z: 5 },
{ my_x_axis: null, x: 4, y: 3, z: 7 },
{ my_x_axis: 'abc', x: 1, y: 0, z: 2 },
]);
});
test('sortRows by avg ascending', () => {
expect(sortRows(sortData, 'my_x_axis', SortSeriesType.Avg, true)).toEqual([
{ my_x_axis: 'abc', x: 1, y: 0, z: 2 },
{ my_x_axis: null, x: 4, y: 3, z: 7 },
{ my_x_axis: 'foo', x: null, y: 10, z: 5 },
]);
});
test('sortRows by avg descending', () => {
expect(sortRows(sortData, 'my_x_axis', SortSeriesType.Avg, false)).toEqual([
{ my_x_axis: 'foo', x: null, y: 10, z: 5 },
{ my_x_axis: null, x: 4, y: 3, z: 7 },
{ my_x_axis: 'abc', x: 1, y: 0, z: 2 },
]);
});
test('sortRows by min ascending', () => {
expect(sortRows(sortData, 'my_x_axis', SortSeriesType.Min, true)).toEqual([
{ my_x_axis: 'abc', x: 1, y: 0, z: 2 },
{ my_x_axis: null, x: 4, y: 3, z: 7 },
{ my_x_axis: 'foo', x: null, y: 10, z: 5 },
]);
});
test('sortRows by min descending', () => {
expect(sortRows(sortData, 'my_x_axis', SortSeriesType.Min, false)).toEqual([
{ my_x_axis: 'foo', x: null, y: 10, z: 5 },
{ my_x_axis: null, x: 4, y: 3, z: 7 },
{ my_x_axis: 'abc', x: 1, y: 0, z: 2 },
]);
});
test('sortRows by max ascending', () => {
expect(sortRows(sortData, 'my_x_axis', SortSeriesType.Min, true)).toEqual([
{ my_x_axis: 'abc', x: 1, y: 0, z: 2 },
{ my_x_axis: null, x: 4, y: 3, z: 7 },
{ my_x_axis: 'foo', x: null, y: 10, z: 5 },
]);
});
test('sortRows by max descending', () => {
expect(sortRows(sortData, 'my_x_axis', SortSeriesType.Min, false)).toEqual([
{ my_x_axis: 'foo', x: null, y: 10, z: 5 },
{ my_x_axis: null, x: 4, y: 3, z: 7 },
{ my_x_axis: 'abc', x: 1, y: 0, z: 2 },
]);
});
test('sortAndFilterSeries by min ascending', () => {
expect( expect(
sortAndFilterSeries(data, 'my_x_axis', [], SortSeriesType.Min, true), sortAndFilterSeries(sortData, 'my_x_axis', [], SortSeriesType.Min, true),
).toEqual(['y', 'x', 'z']); ).toEqual(['y', 'x', 'z']);
});
test('sortAndFilterSeries by min descending', () => {
expect( expect(
sortAndFilterSeries(data, 'my_x_axis', [], SortSeriesType.Min, false), sortAndFilterSeries(sortData, 'my_x_axis', [], SortSeriesType.Min, false),
).toEqual(['z', 'x', 'y']); ).toEqual(['z', 'x', 'y']);
});
test('sortAndFilterSeries by max ascending', () => {
expect( expect(
sortAndFilterSeries(data, 'my_x_axis', [], SortSeriesType.Max, true), sortAndFilterSeries(sortData, 'my_x_axis', [], SortSeriesType.Max, true),
).toEqual(['x', 'z', 'y']); ).toEqual(['x', 'z', 'y']);
});
test('sortAndFilterSeries by max descending', () => {
expect( expect(
sortAndFilterSeries(data, 'my_x_axis', [], SortSeriesType.Max, false), sortAndFilterSeries(sortData, 'my_x_axis', [], SortSeriesType.Max, false),
).toEqual(['y', 'z', 'x']); ).toEqual(['y', 'z', 'x']);
});
test('sortAndFilterSeries by avg ascending', () => {
expect( expect(
sortAndFilterSeries(data, 'my_x_axis', [], SortSeriesType.Avg, true), sortAndFilterSeries(sortData, 'my_x_axis', [], SortSeriesType.Avg, true),
).toEqual(['x', 'y', 'z']); ).toEqual(['x', 'y', 'z']);
});
test('sortAndFilterSeries by avg descending', () => {
expect( expect(
sortAndFilterSeries(data, 'my_x_axis', [], SortSeriesType.Avg, false), sortAndFilterSeries(sortData, 'my_x_axis', [], SortSeriesType.Avg, false),
).toEqual(['z', 'y', 'x']); ).toEqual(['z', 'y', 'x']);
});
test('sortAndFilterSeries by sum ascending', () => {
expect( expect(
sortAndFilterSeries(data, 'my_x_axis', [], SortSeriesType.Sum, true), sortAndFilterSeries(sortData, 'my_x_axis', [], SortSeriesType.Sum, true),
).toEqual(['x', 'y', 'z']); ).toEqual(['x', 'y', 'z']);
});
test('sortAndFilterSeries by sum descending', () => {
expect( expect(
sortAndFilterSeries(data, 'my_x_axis', [], SortSeriesType.Sum, false), sortAndFilterSeries(sortData, 'my_x_axis', [], SortSeriesType.Sum, false),
).toEqual(['z', 'y', 'x']); ).toEqual(['z', 'y', 'x']);
});
test('sortAndFilterSeries by name ascending', () => {
expect( expect(
sortAndFilterSeries(data, 'my_x_axis', [], SortSeriesType.Name, true), sortAndFilterSeries(sortData, 'my_x_axis', [], SortSeriesType.Name, true),
).toEqual(['x', 'y', 'z']); ).toEqual(['x', 'y', 'z']);
});
test('sortAndFilterSeries by name descending', () => {
expect( expect(
sortAndFilterSeries(data, 'my_x_axis', [], SortSeriesType.Name, false), sortAndFilterSeries(sortData, 'my_x_axis', [], SortSeriesType.Name, false),
).toEqual(['z', 'y', 'x']); ).toEqual(['z', 'y', 'x']);
}); });