From e54194d018863ea1173e60b3532a12de084350a5 Mon Sep 17 00:00:00 2001 From: Nate Kelley Date: Fri, 18 Apr 2025 14:42:15 -0600 Subject: [PATCH] update chart theme and ticker --- .../ui/charts/BusterChartJS/ChartJSTheme.ts | 3 +- .../plugins/chartjs-scale-tick-duplicate.ts | 93 ++----------------- .../hooks/useOptions/useXAxis/useXAxis.ts | 8 +- .../stories/BusterChart.LineChart.stories.tsx | 38 ++++++++ 4 files changed, 52 insertions(+), 90 deletions(-) diff --git a/web/src/components/ui/charts/BusterChartJS/ChartJSTheme.ts b/web/src/components/ui/charts/BusterChartJS/ChartJSTheme.ts index 6c0b95e14..c26fe8c55 100644 --- a/web/src/components/ui/charts/BusterChartJS/ChartJSTheme.ts +++ b/web/src/components/ui/charts/BusterChartJS/ChartJSTheme.ts @@ -32,10 +32,9 @@ import { isServer } from '@tanstack/react-query'; import './core/plugins/chartjs-plugin-dayjs'; import { truncateText } from '@/lib/text'; -import { DeduplicatedTimeScale } from './core/plugins/chartjs-scale-tick-duplicate'; +import './core/plugins/chartjs-scale-tick-duplicate'; // Register the scale properly -registry.addScales(DeduplicatedTimeScale); const fontFamily = isServer ? 'Roobert_Pro' diff --git a/web/src/components/ui/charts/BusterChartJS/core/plugins/chartjs-scale-tick-duplicate.ts b/web/src/components/ui/charts/BusterChartJS/core/plugins/chartjs-scale-tick-duplicate.ts index 609c96629..476d240a3 100644 --- a/web/src/components/ui/charts/BusterChartJS/core/plugins/chartjs-scale-tick-duplicate.ts +++ b/web/src/components/ui/charts/BusterChartJS/core/plugins/chartjs-scale-tick-duplicate.ts @@ -23,13 +23,12 @@ declare module 'chart.js' { const originalBuildTicks = TimeScale.prototype.buildTicks; -// Override buildTicks +// // Override buildTicks TimeScale.prototype.buildTicks = function () { - // Step 1: Get default ticks using the original method + // Generate default ticks const defaultTicks = originalBuildTicks.call(this); - console.log('defaultTicks', defaultTicks); - // Step 2: Access tick callback and display format + // Access tick callback and display format const tickCallback = this.options.ticks?.callback; const displayFormat = this.options.time?.displayFormats?.[this._unit] || @@ -37,30 +36,17 @@ TimeScale.prototype.buildTicks = function () { 'MMM'; const format = this._adapter.format.bind(this._adapter); - // Step 3: Track seen labels and collect unique ticks + // Deduplicate ticks const seen = new Set(); const uniqueTicks = []; for (let i = 0; i < defaultTicks.length; i++) { const tick = defaultTicks[i]; - - // Step 4: Generate tick label - let label; - try { - if (typeof tickCallback === 'function') { - label = tickCallback.call(this, tick.value, i, defaultTicks); - } else { - label = format(tick.value, displayFormat); - } - } catch (e) { - console.error('Tick callback error at index', i, e); - label = '???'; - } - - // Ensure label is a string for consistent comparison + let label = tickCallback + ? tickCallback.call(this, tick.value, i, defaultTicks) + : format(tick.value, displayFormat); const stringLabel = String(label ?? ''); - // Step 5: Only include tick if label is unique if (!seen.has(stringLabel)) { seen.add(stringLabel); uniqueTicks.push({ @@ -70,67 +56,8 @@ TimeScale.prototype.buildTicks = function () { } } - // Step 6: Return the filtered ticks + // Set the filtered ticks on the axis instance + this.ticks = uniqueTicks; + return uniqueTicks; }; - -export class DeduplicatedTimeScale extends TimeScale { - static id = 'deduplicated-time'; - static defaults = TimeScale.defaults; - - /** - * Override buildTicks to eliminate duplicate ticks based on formatted values. - * @returns {Array} Array of unique tick objects - */ - buildTicks() { - console.log('buildTicks'); - // Step 1: Get default ticks from parent TimeScale - const defaultTicks = super.buildTicks(); - - // Step 2: Access tick callback and display format - const tickCallback = this.options.ticks?.callback; - const displayFormat = - this.options.time?.displayFormats?.[this._unit] || - this.options.time?.displayFormats?.month || - 'MMM'; - const format = this._adapter.format.bind(this._adapter); - - // Step 3: Track seen labels and collect unique ticks - const seen = new Set(); - const uniqueTicks = []; - - for (let i = 0; i < defaultTicks.length; i++) { - const tick = defaultTicks[i]; - - // Step 4: Generate tick label - let label; - try { - if (typeof tickCallback === 'function') { - // Pass tick value, index, and ticks array to callback - label = tickCallback.call(this, tick.value, i, defaultTicks); - } else { - // Format using the adapter with the appropriate display format - label = format(tick.value, displayFormat); - } - } catch (e) { - console.error('Tick callback error at index', i, e); - label = '???'; - } - - // Ensure label is a string for consistent comparison - const stringLabel = String(label ?? ''); - - // Step 5: Only include tick if label is unique - if (!seen.has(stringLabel)) { - seen.add(stringLabel); - uniqueTicks.push({ - ...tick, - label: stringLabel // Ensure the tick object has the correct label - }); - } - } - - // Step 6: Return the filtered ticks - return uniqueTicks; - } -} diff --git a/web/src/components/ui/charts/BusterChartJS/hooks/useOptions/useXAxis/useXAxis.ts b/web/src/components/ui/charts/BusterChartJS/hooks/useOptions/useXAxis/useXAxis.ts index 5c5fa3ee0..3b13d2787 100644 --- a/web/src/components/ui/charts/BusterChartJS/hooks/useOptions/useXAxis/useXAxis.ts +++ b/web/src/components/ui/charts/BusterChartJS/hooks/useOptions/useXAxis/useXAxis.ts @@ -155,8 +155,8 @@ export const useXAxis = ({ | 'month' | 'quarter' | 'year'; - console.log('selected unit', unit); const format = DATE_FORMATS[unit]; + console.log('auto unit?', { unit, format }); return formatLabel(rawValue, { ...xColumnLabelFormat, dateFormat: format }); } const res = formatLabel(rawValue, xColumnLabelFormat); @@ -201,8 +201,6 @@ export const useXAxis = ({ return derivedTimeUnit; }, [type, derivedTimeUnit, xAxisTimeInterval]); - console.log('timeUnit', { timeUnit, derivedTimeUnit }); - const offset = useMemo(() => { if (isScatterChart) return false; if (isLineChart) return lineGroupType !== 'percentage-stack'; @@ -212,7 +210,7 @@ export const useXAxis = ({ const memoizedXAxisOptions: DeepPartial['scales']['x']> | undefined = useMemo(() => { if (isPieChart) return undefined; - console.log('type', { type, timeUnit }); + console.log('type', { type, timeUnit, derivedTimeUnit }); return { type, offset, @@ -231,7 +229,7 @@ export const useXAxis = ({ enabled: false //test }, autoSkip: false, - maxTicksLimit: type === 'time' ? (timeUnit === 'month' ? 11 : 18) : undefined, + maxTicksLimit: type === 'time' ? (timeUnit === 'month' ? 12 : 18) : undefined, sampleSize: type === 'time' ? 24 : undefined, display: xAxisShowAxisLabel, callback: tickCallback as any, //I need to use null for auto date diff --git a/web/src/components/ui/charts/stories/BusterChart.LineChart.stories.tsx b/web/src/components/ui/charts/stories/BusterChart.LineChart.stories.tsx index abb770af0..f65ead559 100644 --- a/web/src/components/ui/charts/stories/BusterChart.LineChart.stories.tsx +++ b/web/src/components/ui/charts/stories/BusterChart.LineChart.stories.tsx @@ -377,6 +377,44 @@ export const FixedDateFormat_TimeIntervalTest_Days_WithAutoUnit_UnevenDays: Stor } }; +export const XAxisTimeIntervalWithMismatchingData_Days: Story = { + args: { + ...AutoDateFormat_TimeIntervalTest_MonthWithForcedUnit_ManyMonths.args, + xAxisTimeInterval: 'day', + data: Array.from({ length: 31 }, (_, i) => ({ + date: dayjs('2024-01-01').add(i, 'day').toISOString(), + sales: addNoise(i * 15 + 55, 10) + })), + columnLabelFormats: { + ...AutoDateFormat_TimeIntervalTest_MonthWithForcedUnit_ManyMonths.args!.columnLabelFormats, + date: { + columnType: 'date', + style: 'date', + dateFormat: 'MMM DD' + } satisfies IColumnLabelFormat + } + } +}; + +export const XAxisTimeIntervalWithMismatchingData_Months: Story = { + args: { + ...AutoDateFormat_TimeIntervalTest_MonthWithForcedUnit_ManyMonths.args, + xAxisTimeInterval: 'month', + data: Array.from({ length: 14 }, (_, i) => ({ + date: dayjs('2024-01-01').add(i, 'month').toISOString(), + sales: addNoise(i * 15 + 55, 10) + })), + columnLabelFormats: { + ...AutoDateFormat_TimeIntervalTest_MonthWithForcedUnit_ManyMonths.args!.columnLabelFormats, + date: { + columnType: 'date', + style: 'date', + dateFormat: 'MMM DD' + } satisfies IColumnLabelFormat + } + } +}; + // Simple X and Y axis with numeric values export const NumericXY: Story = { args: {