Fix chart sorting (#16996)
I introduced a bug in the chart sorting in this PR: https://github.com/twentyhq/twenty/pull/16794 This PR fixes this. The sorting on the first axis is already done by the group by for FIELD_ASC and FIELD_DESC. It is sorted also for the second axis but in each group. For instance, if I create a graph that displays the amount of sales on each day of the week grouped by vendor I can have for: Monday: Vendor B, Vendor C Tuesday: Vendor A, Vendor B Wednesday: Vendor A, Vendor C ... Inside each day the order of the vendor is correct, but by looking at only one day, I can't know the order of all the vendors. That is why we always need to sort the second axis.
This commit is contained in:
+18
-9
@@ -4,9 +4,11 @@ import { sortBarChartDataBySecondaryDimensionSum } from '@/page-layout/widgets/g
|
||||
import { type GraphColor } from '@/page-layout/widgets/graph/types/GraphColor';
|
||||
import { type RawDimensionValue } from '@/page-layout/widgets/graph/types/RawDimensionValue';
|
||||
import { sortSecondaryAxisData } from '@/page-layout/widgets/graph/utils/sortSecondaryAxisData';
|
||||
import { sortTwoDimensionalChartPrimaryAxisDataByFieldOrManually } from '@/page-layout/widgets/graph/utils/sortTwoDimensionalChartPrimaryAxisDataByFieldOrManually';
|
||||
import { sortTwoDimensionalChartPrimaryAxisDataByFieldOrManuallyIfNeeded } from '@/page-layout/widgets/graph/utils/sortTwoDimensionalChartPrimaryAxisDataByFieldOrManuallyIfNeeded';
|
||||
import { type BarDatum } from '@nivo/bar';
|
||||
import { type CompositeFieldSubFieldName } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { type FieldMetadataType } from '~/generated-metadata/graphql';
|
||||
import { type BarChartConfiguration, GraphOrderBy } from '~/generated/graphql';
|
||||
|
||||
type SortTwoDimensionalBarChartDataConfiguration = {
|
||||
@@ -18,6 +20,8 @@ type SortTwoDimensionalBarChartDataConfiguration = {
|
||||
primaryAxisSelectFieldOptions?: FieldMetadataItemOption[] | null;
|
||||
secondaryAxisFormattedToRawLookup?: Map<string, RawDimensionValue>;
|
||||
secondaryAxisSelectFieldOptions?: FieldMetadataItemOption[] | null;
|
||||
secondaryAxisFieldType?: FieldMetadataType;
|
||||
secondaryAxisSubFieldName?: CompositeFieldSubFieldName;
|
||||
};
|
||||
|
||||
type SortTwoDimensionalBarChartDataResult = {
|
||||
@@ -41,6 +45,8 @@ export const sortTwoDimensionalBarChartData = ({
|
||||
primaryAxisSelectFieldOptions,
|
||||
secondaryAxisFormattedToRawLookup,
|
||||
secondaryAxisSelectFieldOptions,
|
||||
secondaryAxisFieldType,
|
||||
secondaryAxisSubFieldName,
|
||||
}: SortTwoDimensionalBarChartDataConfiguration): SortTwoDimensionalBarChartDataResult => {
|
||||
const sortedKeys = sortSecondaryAxisData({
|
||||
items: keys,
|
||||
@@ -49,6 +55,8 @@ export const sortTwoDimensionalBarChartData = ({
|
||||
formattedToRawLookup: secondaryAxisFormattedToRawLookup,
|
||||
selectFieldOptions: secondaryAxisSelectFieldOptions,
|
||||
getFormattedValue: (item) => item,
|
||||
fieldType: secondaryAxisFieldType,
|
||||
subFieldName: secondaryAxisSubFieldName,
|
||||
});
|
||||
|
||||
const sortedSeries: BarChartSeries[] = sortedKeys.map((key) => ({
|
||||
@@ -70,14 +78,15 @@ export const sortTwoDimensionalBarChartData = ({
|
||||
orderBy: primaryAxisOrderBy,
|
||||
});
|
||||
} else {
|
||||
sortedData = sortTwoDimensionalChartPrimaryAxisDataByFieldOrManually({
|
||||
data,
|
||||
orderBy: primaryAxisOrderBy,
|
||||
manualSortOrder: primaryAxisManualSortOrder,
|
||||
formattedToRawLookup: primaryAxisFormattedToRawLookup,
|
||||
getFormattedValue: (datum) => datum[indexByKey] as string,
|
||||
selectFieldOptions: primaryAxisSelectFieldOptions,
|
||||
});
|
||||
sortedData =
|
||||
sortTwoDimensionalChartPrimaryAxisDataByFieldOrManuallyIfNeeded({
|
||||
data,
|
||||
orderBy: primaryAxisOrderBy,
|
||||
manualSortOrder: primaryAxisManualSortOrder,
|
||||
formattedToRawLookup: primaryAxisFormattedToRawLookup,
|
||||
getFormattedValue: (datum) => datum[indexByKey] as string,
|
||||
selectFieldOptions: primaryAxisSelectFieldOptions,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -9,7 +9,7 @@ import { type GroupByRawResult } from '@/page-layout/widgets/graph/types/GroupBy
|
||||
import { type RawDimensionValue } from '@/page-layout/widgets/graph/types/RawDimensionValue';
|
||||
import { getFieldKey } from '@/page-layout/widgets/graph/utils/getFieldKey';
|
||||
import { processOneDimensionalGroupByResults } from '@/page-layout/widgets/graph/utils/processOneDimensionalGroupByResults';
|
||||
import { sortChartData } from '@/page-layout/widgets/graph/utils/sortChartData';
|
||||
import { sortChartDataIfNeeded } from '@/page-layout/widgets/graph/utils/sortChartDataIfNeeded';
|
||||
import { type BarDatum } from '@nivo/bar';
|
||||
import {
|
||||
type FirstDayOfTheWeek,
|
||||
@@ -79,7 +79,7 @@ export const transformOneDimensionalGroupByToBarChartData = ({
|
||||
}),
|
||||
);
|
||||
|
||||
const sortedData = sortChartData({
|
||||
const sortedData = sortChartDataIfNeeded({
|
||||
data: unsortedData,
|
||||
orderBy: configuration.primaryAxisOrderBy,
|
||||
manualSortOrder: configuration.primaryAxisManualSortOrder,
|
||||
|
||||
+6
@@ -10,6 +10,7 @@ import { type RawDimensionValue } from '@/page-layout/widgets/graph/types/RawDim
|
||||
import { getFieldKey } from '@/page-layout/widgets/graph/utils/getFieldKey';
|
||||
import { processTwoDimensionalGroupByResults } from '@/page-layout/widgets/graph/utils/processTwoDimensionalGroupByResults';
|
||||
import { type BarDatum } from '@nivo/bar';
|
||||
import { type CompositeFieldSubFieldName } from 'twenty-shared/types';
|
||||
import { type FirstDayOfTheWeek } from 'twenty-shared/utils';
|
||||
import { type BarChartConfiguration } from '~/generated/graphql';
|
||||
|
||||
@@ -81,6 +82,11 @@ export const transformTwoDimensionalGroupByToBarChartData = ({
|
||||
primaryAxisSelectFieldOptions: groupByFieldX.options,
|
||||
secondaryAxisFormattedToRawLookup: yFormattedToRawLookup,
|
||||
secondaryAxisSelectFieldOptions: groupByFieldY.options,
|
||||
secondaryAxisFieldType: groupByFieldY.type,
|
||||
secondaryAxisSubFieldName:
|
||||
(configuration.secondaryAxisGroupBySubFieldName ?? undefined) as
|
||||
| CompositeFieldSubFieldName
|
||||
| undefined,
|
||||
});
|
||||
|
||||
const { limitedData, limitedKeys, limitedSeries, hasTooManyGroups } =
|
||||
|
||||
+10
-2
@@ -4,8 +4,10 @@ import { type LineChartSeries } from '@/page-layout/widgets/graph/graphWidgetLin
|
||||
import { sortLineChartDataBySecondaryDimensionSum } from '@/page-layout/widgets/graph/graphWidgetLineChart/utils/sortLineChartDataBySecondaryDimensionSum';
|
||||
import { type RawDimensionValue } from '@/page-layout/widgets/graph/types/RawDimensionValue';
|
||||
import { sortSecondaryAxisData } from '@/page-layout/widgets/graph/utils/sortSecondaryAxisData';
|
||||
import { sortTwoDimensionalChartPrimaryAxisDataByFieldOrManually } from '@/page-layout/widgets/graph/utils/sortTwoDimensionalChartPrimaryAxisDataByFieldOrManually';
|
||||
import { sortTwoDimensionalChartPrimaryAxisDataByFieldOrManuallyIfNeeded } from '@/page-layout/widgets/graph/utils/sortTwoDimensionalChartPrimaryAxisDataByFieldOrManuallyIfNeeded';
|
||||
import { type CompositeFieldSubFieldName } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { type FieldMetadataType } from '~/generated-metadata/graphql';
|
||||
import { type LineChartConfiguration, GraphOrderBy } from '~/generated/graphql';
|
||||
|
||||
type SortTwoDimensionalLineChartDataConfiguration = {
|
||||
@@ -15,6 +17,8 @@ type SortTwoDimensionalLineChartDataConfiguration = {
|
||||
primaryAxisSelectFieldOptions?: FieldMetadataItemOption[] | null;
|
||||
secondaryAxisFormattedToRawLookup?: Map<string, RawDimensionValue>;
|
||||
secondaryAxisSelectFieldOptions?: FieldMetadataItemOption[] | null;
|
||||
secondaryAxisFieldType?: FieldMetadataType;
|
||||
secondaryAxisSubFieldName?: CompositeFieldSubFieldName;
|
||||
};
|
||||
|
||||
type SortTwoDimensionalLineChartDataResult = {
|
||||
@@ -33,6 +37,8 @@ export const sortTwoDimensionalLineChartData = ({
|
||||
primaryAxisSelectFieldOptions,
|
||||
secondaryAxisFormattedToRawLookup,
|
||||
secondaryAxisSelectFieldOptions,
|
||||
secondaryAxisFieldType,
|
||||
secondaryAxisSubFieldName,
|
||||
}: SortTwoDimensionalLineChartDataConfiguration): SortTwoDimensionalLineChartDataResult => {
|
||||
let sortedSeries = series;
|
||||
|
||||
@@ -48,7 +54,7 @@ export const sortTwoDimensionalLineChartData = ({
|
||||
} else {
|
||||
sortedSeries = series.map((seriesItem) => {
|
||||
const sortedDataPoints =
|
||||
sortTwoDimensionalChartPrimaryAxisDataByFieldOrManually({
|
||||
sortTwoDimensionalChartPrimaryAxisDataByFieldOrManuallyIfNeeded({
|
||||
data: seriesItem.data,
|
||||
orderBy: primaryAxisOrderBy,
|
||||
manualSortOrder: primaryAxisManualSortOrder,
|
||||
@@ -73,6 +79,8 @@ export const sortTwoDimensionalLineChartData = ({
|
||||
formattedToRawLookup: secondaryAxisFormattedToRawLookup,
|
||||
selectFieldOptions: secondaryAxisSelectFieldOptions,
|
||||
getFormattedValue: (item) => item.id,
|
||||
fieldType: secondaryAxisFieldType,
|
||||
subFieldName: secondaryAxisSubFieldName,
|
||||
});
|
||||
|
||||
return {
|
||||
|
||||
+2
-2
@@ -9,7 +9,7 @@ import { type GraphColor } from '@/page-layout/widgets/graph/types/GraphColor';
|
||||
import { type GroupByRawResult } from '@/page-layout/widgets/graph/types/GroupByRawResult';
|
||||
import { type RawDimensionValue } from '@/page-layout/widgets/graph/types/RawDimensionValue';
|
||||
import { processOneDimensionalGroupByResults } from '@/page-layout/widgets/graph/utils/processOneDimensionalGroupByResults';
|
||||
import { sortChartData } from '@/page-layout/widgets/graph/utils/sortChartData';
|
||||
import { sortChartDataIfNeeded } from '@/page-layout/widgets/graph/utils/sortChartDataIfNeeded';
|
||||
import {
|
||||
isFieldMetadataSelectKind,
|
||||
type FirstDayOfTheWeek,
|
||||
@@ -65,7 +65,7 @@ export const transformOneDimensionalGroupByToLineChartData = ({
|
||||
}),
|
||||
);
|
||||
|
||||
const sortedData = sortChartData({
|
||||
const sortedData = sortChartDataIfNeeded({
|
||||
data: unsortedData,
|
||||
orderBy: configuration.primaryAxisOrderBy,
|
||||
manualSortOrder: configuration.primaryAxisManualSortOrder,
|
||||
|
||||
+6
@@ -10,6 +10,7 @@ import { type GraphColor } from '@/page-layout/widgets/graph/types/GraphColor';
|
||||
import { type GroupByRawResult } from '@/page-layout/widgets/graph/types/GroupByRawResult';
|
||||
import { type RawDimensionValue } from '@/page-layout/widgets/graph/types/RawDimensionValue';
|
||||
import { processTwoDimensionalGroupByResults } from '@/page-layout/widgets/graph/utils/processTwoDimensionalGroupByResults';
|
||||
import { type CompositeFieldSubFieldName } from 'twenty-shared/types';
|
||||
import { type FirstDayOfTheWeek } from 'twenty-shared/utils';
|
||||
import { type LineChartConfiguration } from '~/generated/graphql';
|
||||
|
||||
@@ -70,6 +71,11 @@ export const transformTwoDimensionalGroupByToLineChartData = ({
|
||||
primaryAxisSelectFieldOptions: groupByFieldX.options,
|
||||
secondaryAxisFormattedToRawLookup: yFormattedToRawLookup,
|
||||
secondaryAxisSelectFieldOptions: groupByFieldY.options,
|
||||
secondaryAxisFieldType: groupByFieldY.type,
|
||||
secondaryAxisSubFieldName:
|
||||
(configuration.secondaryAxisGroupBySubFieldName ?? undefined) as
|
||||
| CompositeFieldSubFieldName
|
||||
| undefined,
|
||||
});
|
||||
|
||||
const { limitedSeries, hasTooManyGroups } = limitTwoDimensionalLineChartData({
|
||||
|
||||
+2
-2
@@ -8,7 +8,7 @@ import { type GraphColor } from '@/page-layout/widgets/graph/types/GraphColor';
|
||||
import { type GroupByRawResult } from '@/page-layout/widgets/graph/types/GroupByRawResult';
|
||||
import { type RawDimensionValue } from '@/page-layout/widgets/graph/types/RawDimensionValue';
|
||||
import { processOneDimensionalGroupByResults } from '@/page-layout/widgets/graph/utils/processOneDimensionalGroupByResults';
|
||||
import { sortChartData } from '@/page-layout/widgets/graph/utils/sortChartData';
|
||||
import { sortChartDataIfNeeded } from '@/page-layout/widgets/graph/utils/sortChartDataIfNeeded';
|
||||
import { type FirstDayOfTheWeek } from 'twenty-shared/types';
|
||||
import { isDefined, isFieldMetadataSelectKind } from 'twenty-shared/utils';
|
||||
import { type PieChartConfiguration } from '~/generated/graphql';
|
||||
@@ -106,7 +106,7 @@ export const transformGroupByDataToPieChartData = ({
|
||||
rawValue: isDefined(rawXValue) ? String(rawXValue) : null,
|
||||
}));
|
||||
|
||||
const sortedDataWithRawValues = sortChartData({
|
||||
const sortedDataWithRawValues = sortChartDataIfNeeded({
|
||||
data: unsortedDataWithRawValues,
|
||||
orderBy: configuration.orderBy ?? undefined,
|
||||
manualSortOrder: configuration.manualSortOrder ?? undefined,
|
||||
|
||||
+32
-20
@@ -1,9 +1,9 @@
|
||||
import { type FieldMetadataItemOption } from '@/object-metadata/types/FieldMetadataItem';
|
||||
import { type RawDimensionValue } from '@/page-layout/widgets/graph/types/RawDimensionValue';
|
||||
import { sortChartData } from '@/page-layout/widgets/graph/utils/sortChartData';
|
||||
import { sortChartDataIfNeeded } from '@/page-layout/widgets/graph/utils/sortChartDataIfNeeded';
|
||||
import { GraphOrderBy } from '~/generated/graphql';
|
||||
|
||||
describe('sortChartData', () => {
|
||||
describe('sortChartDataIfNeeded', () => {
|
||||
type TestDataPoint = { label: string; value: number };
|
||||
|
||||
const testData: TestDataPoint[] = [
|
||||
@@ -12,6 +12,18 @@ describe('sortChartData', () => {
|
||||
{ label: 'Gamma', value: 20 },
|
||||
];
|
||||
|
||||
const fieldAscTestData: TestDataPoint[] = [
|
||||
{ label: 'Alpha', value: 10 },
|
||||
{ label: 'Beta', value: 30 },
|
||||
{ label: 'Gamma', value: 20 },
|
||||
];
|
||||
|
||||
const fieldDescTestData: TestDataPoint[] = [
|
||||
{ label: 'Gamma', value: 20 },
|
||||
{ label: 'Beta', value: 30 },
|
||||
{ label: 'Alpha', value: 10 },
|
||||
];
|
||||
|
||||
const formattedToRawLookup = new Map<string, RawDimensionValue>([
|
||||
['Alpha', 'ALPHA'],
|
||||
['Beta', 'BETA'],
|
||||
@@ -23,7 +35,7 @@ describe('sortChartData', () => {
|
||||
|
||||
describe('null or undefined orderBy', () => {
|
||||
it('should return data unchanged when orderBy is null', () => {
|
||||
const result = sortChartData({
|
||||
const result = sortChartDataIfNeeded({
|
||||
data: testData,
|
||||
orderBy: null,
|
||||
formattedToRawLookup,
|
||||
@@ -35,7 +47,7 @@ describe('sortChartData', () => {
|
||||
});
|
||||
|
||||
it('should return data unchanged when orderBy is undefined', () => {
|
||||
const result = sortChartData({
|
||||
const result = sortChartDataIfNeeded({
|
||||
data: testData,
|
||||
orderBy: undefined,
|
||||
formattedToRawLookup,
|
||||
@@ -48,9 +60,9 @@ describe('sortChartData', () => {
|
||||
});
|
||||
|
||||
describe('FIELD_ASC sorting', () => {
|
||||
it('should sort by field value ascending', () => {
|
||||
const result = sortChartData({
|
||||
data: testData,
|
||||
it('should return data unchanged by field value ascending, since it is already sorted by the backend', () => {
|
||||
const result = sortChartDataIfNeeded({
|
||||
data: fieldAscTestData,
|
||||
orderBy: GraphOrderBy.FIELD_ASC,
|
||||
formattedToRawLookup,
|
||||
getFieldValue,
|
||||
@@ -66,9 +78,9 @@ describe('sortChartData', () => {
|
||||
});
|
||||
|
||||
describe('FIELD_DESC sorting', () => {
|
||||
it('should sort by field value descending', () => {
|
||||
const result = sortChartData({
|
||||
data: testData,
|
||||
it('should return data unchanged by field value descending, since it is already sorted by the backend', () => {
|
||||
const result = sortChartDataIfNeeded({
|
||||
data: fieldDescTestData,
|
||||
orderBy: GraphOrderBy.FIELD_DESC,
|
||||
formattedToRawLookup,
|
||||
getFieldValue,
|
||||
@@ -85,7 +97,7 @@ describe('sortChartData', () => {
|
||||
|
||||
describe('VALUE_ASC sorting', () => {
|
||||
it('should sort by numeric value ascending', () => {
|
||||
const result = sortChartData({
|
||||
const result = sortChartDataIfNeeded({
|
||||
data: testData,
|
||||
orderBy: GraphOrderBy.VALUE_ASC,
|
||||
formattedToRawLookup,
|
||||
@@ -99,7 +111,7 @@ describe('sortChartData', () => {
|
||||
|
||||
describe('VALUE_DESC sorting', () => {
|
||||
it('should sort by numeric value descending', () => {
|
||||
const result = sortChartData({
|
||||
const result = sortChartDataIfNeeded({
|
||||
data: testData,
|
||||
orderBy: GraphOrderBy.VALUE_DESC,
|
||||
formattedToRawLookup,
|
||||
@@ -115,7 +127,7 @@ describe('sortChartData', () => {
|
||||
it('should sort by manual order', () => {
|
||||
const manualSortOrder = ['GAMMA', 'ALPHA', 'BETA'];
|
||||
|
||||
const result = sortChartData({
|
||||
const result = sortChartDataIfNeeded({
|
||||
data: testData,
|
||||
orderBy: GraphOrderBy.MANUAL,
|
||||
manualSortOrder,
|
||||
@@ -132,7 +144,7 @@ describe('sortChartData', () => {
|
||||
});
|
||||
|
||||
it('should return data unchanged when manual order is undefined', () => {
|
||||
const result = sortChartData({
|
||||
const result = sortChartDataIfNeeded({
|
||||
data: testData,
|
||||
orderBy: GraphOrderBy.MANUAL,
|
||||
manualSortOrder: undefined,
|
||||
@@ -145,7 +157,7 @@ describe('sortChartData', () => {
|
||||
});
|
||||
|
||||
it('should return data unchanged when manual order is null', () => {
|
||||
const result = sortChartData({
|
||||
const result = sortChartDataIfNeeded({
|
||||
data: testData,
|
||||
orderBy: GraphOrderBy.MANUAL,
|
||||
manualSortOrder: null,
|
||||
@@ -166,7 +178,7 @@ describe('sortChartData', () => {
|
||||
];
|
||||
|
||||
it('should sort by select option position ascending', () => {
|
||||
const result = sortChartData({
|
||||
const result = sortChartDataIfNeeded({
|
||||
data: testData,
|
||||
orderBy: GraphOrderBy.FIELD_POSITION_ASC,
|
||||
formattedToRawLookup,
|
||||
@@ -184,7 +196,7 @@ describe('sortChartData', () => {
|
||||
|
||||
it('should throw error when select options are not provided', () => {
|
||||
expect(() =>
|
||||
sortChartData({
|
||||
sortChartDataIfNeeded({
|
||||
data: testData,
|
||||
orderBy: GraphOrderBy.FIELD_POSITION_ASC,
|
||||
formattedToRawLookup,
|
||||
@@ -196,7 +208,7 @@ describe('sortChartData', () => {
|
||||
|
||||
it('should throw error when select options are empty', () => {
|
||||
expect(() =>
|
||||
sortChartData({
|
||||
sortChartDataIfNeeded({
|
||||
data: testData,
|
||||
orderBy: GraphOrderBy.FIELD_POSITION_ASC,
|
||||
formattedToRawLookup,
|
||||
@@ -216,7 +228,7 @@ describe('sortChartData', () => {
|
||||
];
|
||||
|
||||
it('should sort by select option position descending', () => {
|
||||
const result = sortChartData({
|
||||
const result = sortChartDataIfNeeded({
|
||||
data: testData,
|
||||
orderBy: GraphOrderBy.FIELD_POSITION_DESC,
|
||||
formattedToRawLookup,
|
||||
@@ -237,7 +249,7 @@ describe('sortChartData', () => {
|
||||
it('should not mutate the original data array', () => {
|
||||
const originalData = [...testData];
|
||||
|
||||
sortChartData({
|
||||
sortChartDataIfNeeded({
|
||||
data: testData,
|
||||
orderBy: GraphOrderBy.FIELD_ASC,
|
||||
formattedToRawLookup,
|
||||
+33
-22
@@ -1,4 +1,4 @@
|
||||
import { sortTwoDimensionalChartPrimaryAxisDataByFieldOrManually } from '@/page-layout/widgets/graph/utils/sortTwoDimensionalChartPrimaryAxisDataByFieldOrManually';
|
||||
import { sortTwoDimensionalChartPrimaryAxisDataByFieldOrManuallyIfNeeded } from '@/page-layout/widgets/graph/utils/sortTwoDimensionalChartPrimaryAxisDataByFieldOrManuallyIfNeeded';
|
||||
import { GraphOrderBy } from '~/generated/graphql';
|
||||
|
||||
type TestItem = { label: string };
|
||||
@@ -8,44 +8,55 @@ const testData: TestItem[] = [
|
||||
{ label: 'Alpha' },
|
||||
{ label: 'Gamma' },
|
||||
];
|
||||
|
||||
const fieldAscTestData: TestItem[] = [
|
||||
{ label: 'Alpha' },
|
||||
{ label: 'Beta' },
|
||||
{ label: 'Gamma' },
|
||||
];
|
||||
|
||||
const formattedToRawLookup = new Map([
|
||||
['Alpha', 'ALPHA'],
|
||||
['Beta', 'BETA'],
|
||||
['Gamma', 'GAMMA'],
|
||||
]);
|
||||
|
||||
const getFormattedValue = (item: TestItem) => item.label;
|
||||
|
||||
describe('sortTwoDimensionalChartPrimaryAxisDataByFieldOrManually', () => {
|
||||
it('should sort by FIELD_ASC', () => {
|
||||
const result = sortTwoDimensionalChartPrimaryAxisDataByFieldOrManually({
|
||||
data: testData,
|
||||
orderBy: GraphOrderBy.FIELD_ASC,
|
||||
formattedToRawLookup,
|
||||
getFormattedValue,
|
||||
});
|
||||
describe('sortTwoDimensionalChartPrimaryAxisDataByFieldOrManuallyIfNeeded', () => {
|
||||
it('should return data unchanged by FIELD_ASC, since it is already sorted by the backend', () => {
|
||||
const result =
|
||||
sortTwoDimensionalChartPrimaryAxisDataByFieldOrManuallyIfNeeded({
|
||||
data: fieldAscTestData,
|
||||
orderBy: GraphOrderBy.FIELD_ASC,
|
||||
formattedToRawLookup,
|
||||
getFormattedValue,
|
||||
});
|
||||
|
||||
expect(result.map((i) => i.label)).toEqual(['Alpha', 'Beta', 'Gamma']);
|
||||
});
|
||||
|
||||
it('should sort by MANUAL order', () => {
|
||||
const result = sortTwoDimensionalChartPrimaryAxisDataByFieldOrManually({
|
||||
data: testData,
|
||||
orderBy: GraphOrderBy.MANUAL,
|
||||
manualSortOrder: ['GAMMA', 'ALPHA', 'BETA'],
|
||||
formattedToRawLookup,
|
||||
getFormattedValue,
|
||||
});
|
||||
const result =
|
||||
sortTwoDimensionalChartPrimaryAxisDataByFieldOrManuallyIfNeeded({
|
||||
data: testData,
|
||||
orderBy: GraphOrderBy.MANUAL,
|
||||
manualSortOrder: ['GAMMA', 'ALPHA', 'BETA'],
|
||||
formattedToRawLookup,
|
||||
getFormattedValue,
|
||||
});
|
||||
|
||||
expect(result.map((i) => i.label)).toEqual(['Gamma', 'Alpha', 'Beta']);
|
||||
});
|
||||
|
||||
it('should return data unchanged when orderBy is undefined', () => {
|
||||
const result = sortTwoDimensionalChartPrimaryAxisDataByFieldOrManually({
|
||||
data: testData,
|
||||
orderBy: undefined,
|
||||
formattedToRawLookup,
|
||||
getFormattedValue,
|
||||
});
|
||||
const result =
|
||||
sortTwoDimensionalChartPrimaryAxisDataByFieldOrManuallyIfNeeded({
|
||||
data: testData,
|
||||
orderBy: undefined,
|
||||
formattedToRawLookup,
|
||||
getFormattedValue,
|
||||
});
|
||||
|
||||
expect(result).toEqual(testData);
|
||||
});
|
||||
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
import { type RawDimensionValue } from '@/page-layout/widgets/graph/types/RawDimensionValue';
|
||||
import { Temporal } from 'temporal-polyfill';
|
||||
import { type CompositeFieldSubFieldName } from 'twenty-shared/types';
|
||||
import {
|
||||
isDefined,
|
||||
isFieldMetadataDateKind,
|
||||
isFieldMetadataNumericKind,
|
||||
} from 'twenty-shared/utils';
|
||||
import { FieldMetadataType } from '~/generated-metadata/graphql';
|
||||
|
||||
const parseDate = (
|
||||
rawValue: RawDimensionValue | undefined,
|
||||
fieldType?: FieldMetadataType,
|
||||
): Temporal.PlainDate | null => {
|
||||
if (!isDefined(rawValue)) return null;
|
||||
const stringValue = String(rawValue);
|
||||
return fieldType === FieldMetadataType.DATE
|
||||
? Temporal.PlainDate.from(stringValue)
|
||||
: Temporal.Instant.from(stringValue)
|
||||
.toZonedDateTimeISO('UTC')
|
||||
.toPlainDate();
|
||||
};
|
||||
|
||||
type CompareDimensionValuesParams = {
|
||||
rawValueA: RawDimensionValue | undefined;
|
||||
rawValueB: RawDimensionValue | undefined;
|
||||
formattedValueA: string;
|
||||
formattedValueB: string;
|
||||
direction: 'ASC' | 'DESC';
|
||||
fieldType?: FieldMetadataType;
|
||||
subFieldName?: CompositeFieldSubFieldName;
|
||||
};
|
||||
|
||||
export const compareDimensionValues = ({
|
||||
rawValueA,
|
||||
rawValueB,
|
||||
formattedValueA,
|
||||
formattedValueB,
|
||||
direction,
|
||||
fieldType,
|
||||
subFieldName,
|
||||
}: CompareDimensionValuesParams): number => {
|
||||
const applyDirection = (comparison: number) =>
|
||||
direction === 'ASC' ? comparison : -comparison;
|
||||
|
||||
if (isDefined(fieldType)) {
|
||||
if (isFieldMetadataDateKind(fieldType)) {
|
||||
const dateA = parseDate(rawValueA, fieldType);
|
||||
const dateB = parseDate(rawValueB, fieldType);
|
||||
if (isDefined(dateA) && isDefined(dateB)) {
|
||||
return applyDirection(Temporal.PlainDate.compare(dateA, dateB));
|
||||
}
|
||||
}
|
||||
|
||||
if (fieldType === FieldMetadataType.CURRENCY) {
|
||||
if (subFieldName === 'amountMicros') {
|
||||
if (isDefined(rawValueA) && isDefined(rawValueB)) {
|
||||
return applyDirection(Number(rawValueA) - Number(rawValueB));
|
||||
}
|
||||
}
|
||||
return applyDirection(formattedValueA.localeCompare(formattedValueB));
|
||||
}
|
||||
|
||||
if (isFieldMetadataNumericKind(fieldType)) {
|
||||
if (isDefined(rawValueA) && isDefined(rawValueB)) {
|
||||
return applyDirection(Number(rawValueA) - Number(rawValueB));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return applyDirection(formattedValueA.localeCompare(formattedValueB));
|
||||
};
|
||||
+2
-7
@@ -15,7 +15,7 @@ type SortChartDataParams<T> = {
|
||||
selectFieldOptions?: FieldMetadataItemOption[] | null;
|
||||
};
|
||||
|
||||
export const sortChartData = <T>({
|
||||
export const sortChartDataIfNeeded = <T>({
|
||||
data,
|
||||
orderBy,
|
||||
manualSortOrder,
|
||||
@@ -50,13 +50,8 @@ export const sortChartData = <T>({
|
||||
case GraphOrderBy.VALUE_DESC:
|
||||
return data.toSorted((a, b) => getNumericValue(b) - getNumericValue(a));
|
||||
case GraphOrderBy.FIELD_ASC:
|
||||
return data.toSorted((a, b) =>
|
||||
getFieldValue(a).localeCompare(getFieldValue(b)),
|
||||
);
|
||||
case GraphOrderBy.FIELD_DESC:
|
||||
return data.toSorted((a, b) =>
|
||||
getFieldValue(b).localeCompare(getFieldValue(a)),
|
||||
);
|
||||
return data;
|
||||
case GraphOrderBy.FIELD_POSITION_ASC:
|
||||
if (!isDefined(selectFieldOptions) || selectFieldOptions.length === 0) {
|
||||
throw new Error('Select field options are required');
|
||||
+21
-7
@@ -1,8 +1,11 @@
|
||||
import { type FieldMetadataItemOption } from '@/object-metadata/types/FieldMetadataItem';
|
||||
import { type RawDimensionValue } from '@/page-layout/widgets/graph/types/RawDimensionValue';
|
||||
import { compareDimensionValues } from '@/page-layout/widgets/graph/utils/compareDimensionValues';
|
||||
import { sortByManualOrder } from '@/page-layout/widgets/graph/utils/sortByManualOrder';
|
||||
import { sortBySelectOptionPosition } from '@/page-layout/widgets/graph/utils/sortBySelectOptionPosition';
|
||||
import { type CompositeFieldSubFieldName } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { type FieldMetadataType } from '~/generated-metadata/graphql';
|
||||
import { GraphOrderBy } from '~/generated/graphql';
|
||||
|
||||
type SortSecondaryAxisDataParams<T> = {
|
||||
@@ -12,6 +15,8 @@ type SortSecondaryAxisDataParams<T> = {
|
||||
formattedToRawLookup?: Map<string, RawDimensionValue>;
|
||||
selectFieldOptions?: FieldMetadataItemOption[] | null;
|
||||
getFormattedValue: (item: T) => string;
|
||||
fieldType?: FieldMetadataType;
|
||||
subFieldName?: CompositeFieldSubFieldName;
|
||||
};
|
||||
|
||||
export const sortSecondaryAxisData = <T>({
|
||||
@@ -21,17 +26,26 @@ export const sortSecondaryAxisData = <T>({
|
||||
formattedToRawLookup,
|
||||
selectFieldOptions,
|
||||
getFormattedValue,
|
||||
fieldType,
|
||||
subFieldName,
|
||||
}: SortSecondaryAxisDataParams<T>): T[] => {
|
||||
switch (orderBy) {
|
||||
case GraphOrderBy.FIELD_ASC:
|
||||
return items.toSorted((a, b) =>
|
||||
getFormattedValue(a).localeCompare(getFormattedValue(b)),
|
||||
);
|
||||
|
||||
case GraphOrderBy.FIELD_DESC:
|
||||
return items.toSorted((a, b) =>
|
||||
getFormattedValue(b).localeCompare(getFormattedValue(a)),
|
||||
);
|
||||
return items.toSorted((a, b) => {
|
||||
const formattedValueA = getFormattedValue(a);
|
||||
const formattedValueB = getFormattedValue(b);
|
||||
|
||||
return compareDimensionValues({
|
||||
rawValueA: formattedToRawLookup?.get(formattedValueA),
|
||||
rawValueB: formattedToRawLookup?.get(formattedValueB),
|
||||
formattedValueA,
|
||||
formattedValueB,
|
||||
direction: orderBy === GraphOrderBy.FIELD_ASC ? 'ASC' : 'DESC',
|
||||
fieldType,
|
||||
subFieldName,
|
||||
});
|
||||
});
|
||||
|
||||
case GraphOrderBy.FIELD_POSITION_ASC:
|
||||
case GraphOrderBy.FIELD_POSITION_DESC: {
|
||||
|
||||
+4
-8
@@ -19,7 +19,9 @@ type SortTwoDimensionalChartPrimaryAxisDataParams<T> = {
|
||||
selectFieldOptions?: FieldMetadataItemOption[] | null;
|
||||
};
|
||||
|
||||
export const sortTwoDimensionalChartPrimaryAxisDataByFieldOrManually = <T>({
|
||||
export const sortTwoDimensionalChartPrimaryAxisDataByFieldOrManuallyIfNeeded = <
|
||||
T,
|
||||
>({
|
||||
data,
|
||||
orderBy,
|
||||
manualSortOrder,
|
||||
@@ -50,14 +52,8 @@ export const sortTwoDimensionalChartPrimaryAxisDataByFieldOrManually = <T>({
|
||||
}
|
||||
|
||||
case GraphOrderBy.FIELD_ASC:
|
||||
return data.toSorted((a, b) =>
|
||||
getFormattedValue(a).localeCompare(getFormattedValue(b)),
|
||||
);
|
||||
|
||||
case GraphOrderBy.FIELD_DESC:
|
||||
return data.toSorted((a, b) =>
|
||||
getFormattedValue(b).localeCompare(getFormattedValue(a)),
|
||||
);
|
||||
return data;
|
||||
|
||||
case GraphOrderBy.FIELD_POSITION_ASC:
|
||||
case GraphOrderBy.FIELD_POSITION_DESC: {
|
||||
Reference in New Issue
Block a user