more elegant parameters for styling app

This commit is contained in:
Nate Kelley 2025-04-11 15:40:40 -06:00
parent 8aa9ee8751
commit dbd24125f7
No known key found for this signature in database
GPG Key ID: FD90372AB8D98B4F
17 changed files with 424 additions and 126 deletions

View File

@ -6,7 +6,7 @@ import type {
Y2AxisConfig,
YAxisConfig
} from './tickInterfaces';
import type { ShowLegendHeadline, BarSortBy } from './etcInterfaces';
import type { ShowLegendHeadline, BarSortBy, PieSortBy } from './etcInterfaces';
import type { GoalLine, Trendline } from './annotationInterfaces';
import type { ColumnSettings } from './columnInterfaces';
import type { IColumnLabelFormat } from './columnLabelInterfaces';
@ -54,6 +54,7 @@ type ScatterChartProps = {
};
type PieChartProps = {
pieSortBy?: PieSortBy; //OPTIONAL: default: value
pieChartAxis: PieChartAxis; // Required for Pie
pieDisplayLabelAs?: 'percent' | 'number'; //OPTIONAL: default: number
pieShowInnerLabel?: boolean; //OPTIONAL: default true if donut width is set. If the data contains a percentage, set this as false.

View File

@ -1,5 +1,7 @@
export type BarSortBy = ('asc' | 'desc' | 'none')[]; //OPTIONAL: default is no sorting (none). The first item in the array will be the primary sort. The second item will be the secondary sort. This will only apply if the X axis type is not a date.
export type PieSortBy = 'value' | 'key' | null; //OPTIONAL: default: value
//current is used for line charts with
export type ShowLegendHeadline =
| false

View File

@ -72,6 +72,7 @@ export const DEFAULT_CHART_CONFIG: IBusterMetricChartConfig = {
pieDonutWidth: 40,
pieMinimumSlicePercentage: 0,
pieDisplayLabelAs: 'number',
pieSortBy: 'value',
//METRIC
metricColumnId: '',
metricValueAggregate: 'sum',

View File

@ -9,12 +9,19 @@ import { useDatasetOptions } from './chartHooks';
export const BusterChartComponent: React.FC<BusterChartRenderComponentProps> = ({
data: dataProp,
barSortBy,
pieSortBy,
pieMinimumSlicePercentage,
trendlines,
...props
}) => {
const { barGroupType, lineGroupType, columnLabelFormats, selectedChartType, selectedAxis } =
props;
const {
barGroupType,
columnMetadata,
lineGroupType,
columnLabelFormats,
selectedChartType,
selectedAxis
} = props;
const {
datasetOptions,
@ -32,7 +39,9 @@ export const BusterChartComponent: React.FC<BusterChartRenderComponentProps> = (
columnLabelFormats,
barGroupType,
lineGroupType,
trendlines
trendlines,
pieSortBy,
columnMetadata
});
const chartProps: BusterChartComponentProps = useMemo(

View File

@ -41,6 +41,7 @@ export const DATASET_IDS = {
raw: 'raw',
relativeStack: 'relative-stack',
sortedByBar: 'sorted-by-bar',
sortedByValue: 'sorted-by-value',
rawWithDateNotDelimited: 'raw-with-date-not-delimited',
pieMinimum: (yAxisKey: string) => `pie-minimum-${yAxisKey}`,
//TRENDLINE IDS

View File

@ -3,42 +3,69 @@
import {
type BusterChartProps,
type ChartType,
type BarSortBy
type BarSortBy,
PieSortBy
} from '@/api/asset_interfaces/metric/charts';
import { createDayjsDate } from '@/lib/date';
import { extractFieldsFromChain, appendToKeyValueChain } from './groupingHelpers';
import { DATASET_IDS, GROUPING_SEPARATOR } from './config';
import { DatasetOption } from './interfaces';
import { DEFAULT_COLUMN_LABEL_FORMAT } from '@/api/asset_interfaces/metric';
import {
ColumnMetaData,
DEFAULT_COLUMN_LABEL_FORMAT,
SimplifiedColumnType
} from '@/api/asset_interfaces/metric';
type DataItem = NonNullable<BusterChartProps['data']>[number];
export const sortLineBarData = (
data: NonNullable<BusterChartProps['data']>,
columnMetadata: NonNullable<BusterChartProps['columnMetadata']>,
xFieldSorts: string[],
xFields: string[]
) => {
if (xFieldSorts.length === 0) return data;
const columnMetadataRecord = columnMetadata.reduce<Record<string, ColumnMetaData>>(
(acc, curr) => {
acc[curr.name] = curr;
return acc;
},
{}
);
const sortedData = [...data];
if (xFieldSorts.length > 0) {
sortedData.sort((a, b) => {
for (let i = 0; i < xFieldSorts.length; i++) {
const dateField = xFields[i];
const field = xFields[i];
const fieldType: SimplifiedColumnType = columnMetadataRecord[field]?.simple_type || 'text';
//NUMBER CASE
if (typeof a[dateField] === 'number' && typeof b[dateField] === 'number') {
if (a[dateField] !== b[dateField]) {
return (a[dateField] as number) - (b[dateField] as number);
if (
fieldType === 'number' ||
(typeof a[field] === 'number' && typeof b[field] === 'number')
) {
if (a[field] !== b[field]) {
return (a[field] as number) - (b[field] as number);
}
}
//DATE CASE
else {
const aDate = createDayjsDate(a[dateField] as string);
const bDate = createDayjsDate(b[dateField] as string);
else if (fieldType === 'date') {
const aDate = createDayjsDate(a[field] as string);
const bDate = createDayjsDate(b[field] as string);
if (aDate.valueOf() !== bDate.valueOf()) {
return aDate.valueOf() - bDate.valueOf();
}
}
//TEXT CASE
else {
if (a[field] !== b[field]) {
return String(a[field]).localeCompare(String(b[field]));
}
}
}
return 0;
});
@ -197,6 +224,7 @@ export const getLineBarPieDatasetOptions = (
selectedChartType: ChartType,
pieMinimumSlicePercentage: number | undefined,
barSortBy: BarSortBy | undefined,
pieSortBy: PieSortBy | undefined,
yAxisKeys: string[], //only used this for pie charts
xFieldDateSorts: string[],
barGroupType: BusterChartProps['barGroupType'] | undefined,
@ -281,6 +309,21 @@ export const getLineBarPieDatasetOptions = (
});
}
if (selectedChartType === 'pie' && pieSortBy === 'value') {
const lastDataset = datasets[datasets.length - 1];
const lastSource = lastDataset.source as (string | number | Date | null)[][];
const sortedData = [...lastSource]
.sort((a, b) => {
return (Number(b[1]) || 0) - (Number(a[1]) || 0);
})
.reverse();
datasets.push({
id: DATASET_IDS.sortedByValue,
dimensions,
source: sortedData
});
}
if (selectedChartType === 'bar' && barSortBy && barSortBy?.some((y) => y !== 'none')) {
// Sort the processed data based on the y-axis values at their respective indices
// Pre-calculate indices and directions to avoid repeated lookups

View File

@ -42,7 +42,8 @@ describe('useDatasetOptions - bar chart - all values are present', () => {
pieMinimumSlicePercentage: undefined,
barGroupType: undefined,
lineGroupType: undefined,
trendlines: undefined
trendlines: undefined,
columnMetadata: []
};
it('should return the correct structure - has category', () => {
@ -127,7 +128,8 @@ describe('useDatasetOptions - bar chart ', () => {
pieMinimumSlicePercentage: undefined,
barGroupType: undefined,
lineGroupType: undefined,
trendlines: undefined
trendlines: undefined,
columnMetadata: []
};
it('should return the correct structure', () => {
@ -201,7 +203,8 @@ describe('useDatasetOptions - bar chart - some numerical values are null', () =>
pieMinimumSlicePercentage: undefined,
barGroupType: undefined,
lineGroupType: undefined,
trendlines: undefined
trendlines: undefined,
columnMetadata: []
};
it('should process bar chart data correctly', () => {
@ -379,7 +382,8 @@ describe('useDatasetOptions - bar chart - some string values are null', () => {
pieMinimumSlicePercentage: undefined,
barGroupType: undefined,
lineGroupType: undefined,
trendlines: undefined
trendlines: undefined,
columnMetadata: []
};
it('should process bar chart data correctly', () => {

View File

@ -10,7 +10,8 @@ import {
ScatterAxis,
Trendline,
ComboChartAxis,
type IColumnLabelFormat
type IColumnLabelFormat,
PieSortBy
} from '@/api/asset_interfaces/metric/charts';
import uniq from 'lodash/uniq';
import {
@ -46,6 +47,7 @@ type DatasetHookResult = {
type DatasetHookParams = {
data: NonNullable<BusterChartProps['data']>;
barSortBy?: BarSortBy;
pieSortBy?: PieSortBy;
groupByMethod?: BusterChartProps['groupByMethod'];
selectedAxis: ChartEncodes;
selectedChartType: ChartType;
@ -54,6 +56,7 @@ type DatasetHookParams = {
barGroupType: BusterChartProps['barGroupType'] | undefined;
lineGroupType: BusterChartProps['lineGroupType'];
trendlines: Trendline[] | undefined;
columnMetadata: NonNullable<BusterChartProps['columnMetadata']>;
};
const defaultYAxis2 = [] as string[];
@ -68,7 +71,9 @@ export const useDatasetOptions = (params: DatasetHookParams): DatasetHookResult
pieMinimumSlicePercentage,
barGroupType,
lineGroupType,
trendlines
trendlines,
pieSortBy,
columnMetadata
} = params;
const {
x: xFields,
@ -100,13 +105,19 @@ export const useDatasetOptions = (params: DatasetHookParams): DatasetHookResult
);
}, [xFieldsString, columnLabelFormats]);
//WILL ONLY BE USED FOR BAR CHART
//WILL ONLY BE USED FOR BAR AND PIE CHART
const xFieldSorts = useMemo(() => {
if (isScatter) return [];
if (isPieChart) return [];
if (isBarChart && barSortBy && barSortBy?.some((y) => y !== 'none')) return [];
if (isPieChart) {
if (pieSortBy === 'key') return xFieldColumnLabelFormatColumnTypes;
return [];
}
if (isBarChart) {
if (barSortBy && barSortBy?.some((y) => y !== 'none')) return [];
}
return xFieldColumnLabelFormatColumnTypes.filter((columnType) => columnType === 'date');
}, [xFieldColumnLabelFormatColumnTypes, isPieChart, isBarChart, isScatter, barSortBy]);
}, [xFieldColumnLabelFormatColumnTypes, pieSortBy, isPieChart, isBarChart, isScatter, barSortBy]);
const xFieldSortsString = useMemo(() => xFieldSorts.join(','), [xFieldSorts]);
@ -116,7 +127,7 @@ export const useDatasetOptions = (params: DatasetHookParams): DatasetHookResult
const sortedAndLimitedData = useMemo(() => {
if (isScatter) return downsampleScatterData(data);
return sortLineBarData(data, xFieldSorts, xFields);
return sortLineBarData(data, columnMetadata, xFieldSorts, xFields);
}, [data, xFieldSortsString, xFieldsString, isScatter]);
const { dataMap, xValuesSet, categoriesSet } = useMemo(() => {
@ -218,6 +229,7 @@ export const useDatasetOptions = (params: DatasetHookParams): DatasetHookResult
selectedChartType,
pieMinimumSlicePercentage,
barSortBy,
pieSortBy,
yAxisKeys,
xFieldSorts,
barGroupType,
@ -230,6 +242,7 @@ export const useDatasetOptions = (params: DatasetHookParams): DatasetHookResult
dimensions,
yAxisKeys,
barSortBy,
pieSortBy,
pieMinimumSlicePercentage,
measureFields,
isScatter,

View File

@ -19,7 +19,7 @@ export interface BusterChartTypeComponentProps
export interface BusterChartComponentProps
extends Omit<
Required<BusterChartRenderComponentProps>,
'selectedAxis' | 'barSortBy' | 'trendlines' | 'data'
'selectedAxis' | 'barSortBy' | 'pieSortBy' | 'trendlines' | 'data'
>,
ReturnType<typeof useDatasetOptions> {
selectedAxis: ChartEncodes;

View File

@ -422,3 +422,71 @@ export const LargeDatasetWithDualYAxis: Story = {
);
}
};
export const WithSorting: Story = {
args: {
...Default.args,
barAndLineAxis: {
x: ['category'],
y: ['sales'],
category: []
},
barSortBy: ['asc']
}
};
export const WithDatesInXAxis: Story = {
args: {
...Default.args,
data: Array.from({ length: 7 }, (_, index) => ({
date: faker.date.past({ years: 1 }).toISOString(),
sales: faker.number.int({ min: 1000, max: 10000 })
})),
barAndLineAxis: {
x: ['date'],
y: ['sales'],
category: []
},
// barSortBy: ['asc'],
columnLabelFormats: {
date: {
columnType: 'date',
style: 'date',
dateFormat: 'LL'
} satisfies IColumnLabelFormat,
sales: {
columnType: 'number',
style: 'currency',
currency: 'USD'
} satisfies IColumnLabelFormat
}
}
};
export const WithDatesInXAxisAndSorting: Story = {
args: {
...Default.args,
data: Array.from({ length: 7 }, (_, index) => ({
date: faker.date.past({ years: 1 }).toISOString(),
sales: faker.number.int({ min: 1000, max: 10000 })
})),
barAndLineAxis: {
x: ['date'],
y: ['sales'],
category: []
},
barSortBy: ['asc'],
columnLabelFormats: {
date: {
columnType: 'date',
style: 'date',
dateFormat: 'LL'
} satisfies IColumnLabelFormat,
sales: {
columnType: 'number',
style: 'currency',
currency: 'USD'
} satisfies IColumnLabelFormat
}
}
};

View File

@ -5,6 +5,7 @@ import { ChartType } from '../../../../api/asset_interfaces/metric/charts/enum';
import { IColumnLabelFormat } from '../../../../api/asset_interfaces/metric/charts/columnLabelInterfaces';
import { generatePieChartData } from '../../../../mocks/chart/chartMocks';
import { sharedMeta } from './BusterChartShared';
import { faker } from '@faker-js/faker';
type PieChartData = ReturnType<typeof generatePieChartData>;
@ -170,3 +171,88 @@ export const ResizableContainer: Story = {
}
}
};
export const WithSortingByKey: Story = {
args: {
selectedChartType: ChartType.Pie,
data: Array.from({ length: 8 }, (_, index) => ({
segment: faker.word.adjective(),
value:
index === 1 || index === 5
? faker.number.int({ min: 10, max: 25 })
: faker.number.int({ min: 50, max: 150 })
})),
pieChartAxis: {
x: ['segment'],
y: ['value']
},
pieSortBy: 'key',
columnMetadata: [
{
name: 'segment',
simple_type: 'text',
min_value: 0,
max_value: 100,
unique_values: 10,
type: 'text'
},
{
name: 'value',
simple_type: 'number',
min_value: 0,
max_value: 100,
unique_values: 10,
type: 'number'
}
]
}
};
export const WithSortingByKeyWithDates: Story = {
args: {
selectedChartType: ChartType.Pie,
data: Array.from({ length: 8 }, (_, index) => ({
date: faker.date.recent({ days: 180 }).toISOString(),
value:
index === 1 || index === 5
? faker.number.int({ min: 10, max: 25 })
: faker.number.int({ min: 50, max: 150 })
})),
pieChartAxis: {
x: ['date'],
y: ['value']
},
pieSortBy: 'key',
columnLabelFormats: {
date: {
columnType: 'date',
style: 'date'
} satisfies IColumnLabelFormat
},
columnMetadata: [
{
name: 'date',
simple_type: 'date',
min_value: 0,
max_value: 100,
unique_values: 10,
type: 'date'
},
{
name: 'value',
simple_type: 'number',
min_value: 0,
max_value: 100,
unique_values: 10,
type: 'number'
}
]
}
};
export const WithSortingByValue: Story = {
args: {
...WithSortingByKey.args!,
pieSortBy: 'value'
}
};

View File

@ -8,21 +8,24 @@ import {
ChartBarTrendDown,
ChartBarTrendUp
} from '@/components/ui/icons/NucleoIconFilled';
import { BarChartSortAscIcon } from '@/components/ui/icons/customIcons/BarChartSortAscIcon';
import { BarChartSortNoneIcon } from '@/components/ui/icons/customIcons/BarChart_NoSort';
import { BarChartSortDescIcon } from '@/components/ui/icons/customIcons/BarChartSortDescIcon';
import { useMemoizedFn } from '@/hooks';
const options: SegmentedItem<BarSortBy[0]>[] = [
{
value: 'none',
tooltip: 'No sorting',
icon: <ChartBarAxisX />
icon: <BarChartSortNoneIcon />
},
{
icon: <ChartBarTrendUp />,
icon: <BarChartSortAscIcon />,
value: 'asc',
tooltip: 'Sort ascending'
},
{
icon: <ChartBarTrendDown />,
icon: <BarChartSortDescIcon />,
value: 'desc',
tooltip: 'Sort descending'
}

View File

@ -0,0 +1,36 @@
import type { Meta, StoryObj } from '@storybook/react';
import { EditPieSorting } from './EditPieSorting';
import { fn } from '@storybook/test';
const meta: Meta<typeof EditPieSorting> = {
title: 'Controllers/MetricController/EditPieSorting',
component: EditPieSorting,
parameters: {
layout: 'centered'
},
tags: ['autodocs']
};
export default meta;
type Story = StoryObj<typeof EditPieSorting>;
export const SortByKey: Story = {
args: {
pieSortBy: 'key',
onUpdateChartConfig: fn()
}
};
export const SortByValue: Story = {
args: {
pieSortBy: 'value',
onUpdateChartConfig: fn()
}
};
export const NoSorting: Story = {
args: {
pieSortBy: null,
onUpdateChartConfig: fn()
}
};

View File

@ -0,0 +1,55 @@
import { IBusterMetricChartConfig } from '@/api/asset_interfaces';
import React, { useMemo } from 'react';
import { LabelAndInput } from '../Common';
import { PieSortBy } from '@/api/asset_interfaces/metric/charts';
import { AppSegmented, SegmentedItem } from '@/components/ui/segmented';
import { SortAlphaAscending, SortNumAscending, Empty } from '@/components/ui/icons';
import { useMemoizedFn } from '@/hooks';
const options: SegmentedItem<NonNullable<PieSortBy> | 'none'>[] = [
{
value: 'key',
tooltip: 'Sort by key',
icon: <SortAlphaAscending />
},
{
icon: <SortNumAscending />,
value: 'value',
tooltip: 'Sort by value'
},
{
icon: <Empty />,
value: 'none',
tooltip: 'No sorting'
}
];
export const EditPieSorting: React.FC<{
pieSortBy: IBusterMetricChartConfig['pieSortBy'];
onUpdateChartConfig: (v: Partial<IBusterMetricChartConfig>) => void;
}> = React.memo(({ pieSortBy, onUpdateChartConfig }) => {
const selectedOption = useMemo(() => {
return (
options.find((option) => {
return pieSortBy === option.value;
})?.value || 'none'
);
}, [pieSortBy]);
const onChange = useMemoizedFn((value: SegmentedItem<NonNullable<PieSortBy> | 'none'>) => {
if (value.value === 'none') {
onUpdateChartConfig({ pieSortBy: null });
} else {
onUpdateChartConfig({ pieSortBy: value.value });
}
});
return (
<LabelAndInput label="Sorting">
<div className="flex justify-end">
<AppSegmented options={options} value={selectedOption} onChange={onChange} type="button" />
</div>
</LabelAndInput>
);
});
EditPieSorting.displayName = 'EditPieSorting';

View File

@ -1,5 +1,5 @@
import { IBusterMetricChartConfig } from '@/api/asset_interfaces';
import { ScatterAxis } from '@/api/asset_interfaces/metric/charts';
import { ChartEncodes, ScatterAxis } from '@/api/asset_interfaces/metric/charts';
import { Slider } from '@/components/ui/slider';
import React from 'react';
import isEmpty from 'lodash/isEmpty';
@ -8,10 +8,10 @@ import { LabelAndInput } from '../Common';
export const EditScatterDotSize: React.FC<{
scatterDotSize: IBusterMetricChartConfig['scatterDotSize'];
scatterAxis: ScatterAxis;
selectedAxis: ChartEncodes;
onUpdateChartConfig: (config: Partial<IBusterMetricChartConfig>) => void;
}> = React.memo(({ scatterDotSize, scatterAxis, onUpdateChartConfig }) => {
const hasSize = !isEmpty(scatterAxis.size);
}> = React.memo(({ scatterDotSize, selectedAxis, onUpdateChartConfig }) => {
const hasSize = !isEmpty((selectedAxis as ScatterAxis).size);
const defaultValue = hasSize ? scatterDotSize : scatterDotSize[0];
const onChange = useMemoizedFn((v: number[]) => {

View File

@ -6,13 +6,13 @@ import { WarningIcon } from '../Common/WarningIcon';
export const EditShowDataLabels: React.FC<{
showDataLabels: boolean;
rowCount: number;
onUpdateColumnSettingConfig: (v: boolean) => void;
}> = React.memo(({ showDataLabels, rowCount, onUpdateColumnSettingConfig }) => {
onUpdateDataLabel: (v: boolean) => void;
}> = React.memo(({ showDataLabels, rowCount, onUpdateDataLabel }) => {
return (
<LabelAndInput label={'Data labels'}>
<div className="flex justify-end gap-x-2">
<WarningIcon rowCount={rowCount} />
<Switch defaultChecked={showDataLabels} onCheckedChange={onUpdateColumnSettingConfig} />
<Switch defaultChecked={showDataLabels} onCheckedChange={onUpdateDataLabel} />
</div>
</LabelAndInput>
);

View File

@ -31,41 +31,20 @@ import {
import { StylingAppStylingNotSupported } from './StylingAppStylingNotSupported';
import { EditScatterDotSize } from './EditScatterDotSize';
import { useUpdateMetricChart } from '@/context/Metrics';
import { EditPieSorting } from './EditPieSorting';
const sectionClass = 'flex w-full flex-col space-y-3 my-3';
const UNSUPPORTED_CHART_TYPES: ChartType[] = [ChartType.Table, ChartType.Metric];
export const StylingAppStyling: React.FC<{
className?: string;
columnSettings: IBusterMetricChartConfig['columnSettings'];
showLegend: IBusterMetricChartConfig['showLegend'];
gridLines: IBusterMetricChartConfig['gridLines'];
yAxisShowAxisLabel: IBusterMetricChartConfig['yAxisShowAxisLabel'];
yAxisShowAxisTitle: IBusterMetricChartConfig['yAxisShowAxisTitle'];
barSortBy: IBusterMetricChartConfig['barSortBy'];
selectedChartType: IBusterMetricChartConfig['selectedChartType'];
lineGroupType: IBusterMetricChartConfig['lineGroupType'];
barGroupType: IBusterMetricChartConfig['barGroupType'];
pieChartAxis: IBusterMetricChartConfig['pieChartAxis'];
yAxisScaleType: IBusterMetricChartConfig['yAxisScaleType'];
y2AxisScaleType: IBusterMetricChartConfig['y2AxisScaleType'];
showLegendHeadline: IBusterMetricChartConfig['showLegendHeadline'];
goalLines: IBusterMetricChartConfig['goalLines'];
trendlines: IBusterMetricChartConfig['trendlines'];
pieDisplayLabelAs: IBusterMetricChartConfig['pieDisplayLabelAs'];
pieLabelPosition: IBusterMetricChartConfig['pieLabelPosition'];
pieDonutWidth: IBusterMetricChartConfig['pieDonutWidth'];
pieInnerLabelAggregate: IBusterMetricChartConfig['pieInnerLabelAggregate'];
pieInnerLabelTitle: IBusterMetricChartConfig['pieInnerLabelTitle'];
pieShowInnerLabel: IBusterMetricChartConfig['pieShowInnerLabel'];
pieMinimumSlicePercentage: IBusterMetricChartConfig['pieMinimumSlicePercentage'];
scatterDotSize: IBusterMetricChartConfig['scatterDotSize'];
selectedAxis: ChartEncodes;
columnMetadata: ColumnMetaData[];
rowCount: number;
columnLabelFormats: IBusterMetricChartConfig['columnLabelFormats'];
barShowTotalAtTop: IBusterMetricChartConfig['barShowTotalAtTop'];
}> = ({
export const StylingAppStyling: React.FC<
{
className?: string;
} & Parameters<typeof StylingAppStylingNotSupported>[0] &
Parameters<typeof GlobalSettings>[0] &
Parameters<typeof ChartSpecificSettings>[0] &
Parameters<typeof EtcSettings>[0] &
Parameters<typeof PieSettings>[0]
> = ({
className = '',
columnSettings,
showLegend,
@ -94,7 +73,8 @@ export const StylingAppStyling: React.FC<{
columnLabelFormats,
barShowTotalAtTop,
yAxisShowAxisTitle,
rowCount
rowCount,
pieSortBy
}) => {
const { onUpdateMetricChartConfig } = useUpdateMetricChart();
@ -167,6 +147,7 @@ export const StylingAppStyling: React.FC<{
selectedAxis={selectedAxis}
columnLabelFormats={columnLabelFormats}
barShowTotalAtTop={barShowTotalAtTop}
pieSortBy={pieSortBy}
/>
{selectedChartType === 'pie' && (
@ -201,22 +182,19 @@ export const StylingAppStyling: React.FC<{
);
};
const GlobalSettings: React.FC<{
className: string;
showLegend: IBusterMetricChartConfig['showLegend'];
gridLines: IBusterMetricChartConfig['gridLines'];
columnSettings: IBusterMetricChartConfig['columnSettings'];
yAxisShowAxisTitle: IBusterMetricChartConfig['yAxisShowAxisTitle'];
yAxisShowAxisLabel: IBusterMetricChartConfig['yAxisShowAxisLabel'];
pieDisplayLabelAs: IBusterMetricChartConfig['pieDisplayLabelAs'];
selectedChartType: IBusterMetricChartConfig['selectedChartType'];
pieLabelPosition: IBusterMetricChartConfig['pieLabelPosition'];
selectedAxis: ChartEncodes;
rowCount: number;
onUpdateChartConfig: (chartConfig: Partial<IBusterMetricChartConfig>) => void;
onUpdateDataLabel: (v: boolean) => void;
onUpdateYAxis: (v: boolean) => void;
}> = ({
const GlobalSettings: React.FC<
{
className: string;
columnSettings: IBusterMetricChartConfig['columnSettings'];
yAxisShowAxisTitle: IBusterMetricChartConfig['yAxisShowAxisTitle'];
yAxisShowAxisLabel: IBusterMetricChartConfig['yAxisShowAxisLabel'];
} & Parameters<typeof EditShowLegend>[0] &
Parameters<typeof EditGridLines>[0] &
Omit<Parameters<typeof EditHideYAxis>[0], 'hideYAxis'> &
Parameters<typeof EditShowLabelPieAsPercentage>[0] &
Parameters<typeof EditPieLabelLocation>[0] &
Omit<Parameters<typeof EditShowDataLabels>[0], 'showDataLabels'>
> = ({
className = '',
showLegend,
selectedAxis,
@ -267,7 +245,7 @@ const GlobalSettings: React.FC<{
Component: (
<EditShowDataLabels
showDataLabels={mostPermissiveDataLabel}
onUpdateColumnSettingConfig={onUpdateDataLabel}
onUpdateDataLabel={onUpdateDataLabel}
rowCount={rowCount}
/>
)
@ -313,24 +291,23 @@ const GlobalSettings: React.FC<{
);
};
const ChartSpecificSettings: React.FC<{
className: string;
columnSettings: IBusterMetricChartConfig['columnSettings'];
selectedChartType: IBusterMetricChartConfig['selectedChartType'];
barSortBy: IBusterMetricChartConfig['barSortBy'];
onUpdateChartConfig: (chartConfig: Partial<IBusterMetricChartConfig>) => void;
lineGroupType: IBusterMetricChartConfig['lineGroupType'];
barGroupType: IBusterMetricChartConfig['barGroupType'];
yAxisScaleType: IBusterMetricChartConfig['yAxisScaleType'];
y2AxisScaleType: IBusterMetricChartConfig['y2AxisScaleType'];
pieDonutWidth: IBusterMetricChartConfig['pieDonutWidth'];
pieMinimumSlicePercentage: IBusterMetricChartConfig['pieMinimumSlicePercentage'];
pieChartAxis: IBusterMetricChartConfig['pieChartAxis'];
scatterDotSize: IBusterMetricChartConfig['scatterDotSize'];
selectedAxis: ChartEncodes;
columnLabelFormats: IBusterMetricChartConfig['columnLabelFormats'];
barShowTotalAtTop: IBusterMetricChartConfig['barShowTotalAtTop'];
}> = ({
const ChartSpecificSettings: React.FC<
{
className: string;
columnSettings: IBusterMetricChartConfig['columnSettings'];
selectedChartType: IBusterMetricChartConfig['selectedChartType'];
columnLabelFormats: IBusterMetricChartConfig['columnLabelFormats'];
} & Parameters<typeof EditBarRoundnessGlobal>[0] &
Parameters<typeof EditBarSorting>[0] &
Parameters<typeof EditPieSorting>[0] &
Parameters<typeof EditGrouping>[0] &
Parameters<typeof EditSmoothLinesGlobal>[0] &
Parameters<typeof EditDotsOnLineGlobal>[0] &
Parameters<typeof EditYAxisScaleGlobal>[0] &
Parameters<typeof EditPieMinimumSlicePercentage>[0] &
Parameters<typeof EditPieAppearance>[0] &
Parameters<typeof EditScatterDotSize>[0]
> = ({
className = '',
barSortBy,
onUpdateChartConfig,
@ -346,7 +323,8 @@ const ChartSpecificSettings: React.FC<{
scatterDotSize,
selectedAxis,
columnLabelFormats,
barShowTotalAtTop
barShowTotalAtTop,
pieSortBy
}) => {
const isBarChart = selectedChartType === 'bar';
const isComboChart = selectedChartType === 'combo';
@ -374,6 +352,11 @@ const ChartSpecificSettings: React.FC<{
key: 'barSorting',
Component: <EditBarSorting barSortBy={barSortBy} onUpdateChartConfig={onUpdateChartConfig} />
},
{
enabled: isPieChart,
key: 'pieSorting',
Component: <EditPieSorting pieSortBy={pieSortBy} onUpdateChartConfig={onUpdateChartConfig} />
},
{
enabled:
isBarChart &&
@ -457,7 +440,7 @@ const ChartSpecificSettings: React.FC<{
Component: (
<EditScatterDotSize
scatterDotSize={scatterDotSize}
scatterAxis={selectedAxis as ScatterAxis}
selectedAxis={selectedAxis}
onUpdateChartConfig={onUpdateChartConfig}
/>
)
@ -479,19 +462,13 @@ const ChartSpecificSettings: React.FC<{
);
};
const EtcSettings: React.FC<{
className: string;
selectedChartType: IBusterMetricChartConfig['selectedChartType'];
showLegendHeadline: IBusterMetricChartConfig['showLegendHeadline'];
goalLines: IBusterMetricChartConfig['goalLines'];
trendlines: IBusterMetricChartConfig['trendlines'];
selectedAxis: ChartEncodes;
columnMetadata: ColumnMetaData[];
columnLabelFormats: IBusterMetricChartConfig['columnLabelFormats'];
lineGroupType: IBusterMetricChartConfig['lineGroupType'];
barGroupType: IBusterMetricChartConfig['barGroupType'];
onUpdateChartConfig: (chartConfig: Partial<IBusterMetricChartConfig>) => void;
}> = ({
const EtcSettings: React.FC<
{
className: string;
} & Parameters<typeof EditShowHeadline>[0] &
Parameters<typeof EditGoalLine>[0] &
Parameters<typeof EditTrendline>[0]
> = ({
className = '',
onUpdateChartConfig,
selectedChartType,
@ -563,14 +540,13 @@ const EtcSettings: React.FC<{
);
};
const PieSettings: React.FC<{
className: string;
pieInnerLabelAggregate: IBusterMetricChartConfig['pieInnerLabelAggregate'];
pieShowInnerLabel: IBusterMetricChartConfig['pieShowInnerLabel'];
pieInnerLabelTitle: IBusterMetricChartConfig['pieInnerLabelTitle'];
pieDonutWidth: IBusterMetricChartConfig['pieDonutWidth'];
onUpdateChartConfig: (chartConfig: Partial<IBusterMetricChartConfig>) => void;
}> = React.memo(
const PieSettings: React.FC<
{
className: string;
pieDonutWidth: IBusterMetricChartConfig['pieDonutWidth'];
} & Parameters<typeof EditPieShowInnerLabel>[0] &
Parameters<typeof EditPieInnerLabel>[0]
> = React.memo(
({
className,
pieInnerLabelAggregate,