diff --git a/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-chart-controls/src/utils/D3Formatting.ts b/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-chart-controls/src/utils/D3Formatting.ts index 026ac63540a6..f0b83aa72633 100644 --- a/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-chart-controls/src/utils/D3Formatting.ts +++ b/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-chart-controls/src/utils/D3Formatting.ts @@ -46,7 +46,7 @@ export const D3_TIME_FORMAT_OPTIONS: [string, string][] = [ ['%m/%d/%Y', '%m/%d/%Y | 01/14/2019'], ['%Y-%m-%d', '%Y-%m-%d | 2019-01-14'], ['%Y-%m-%d %H:%M:%S', '%Y-%m-%d %H:%M:%S | 2019-01-14 01:32:10'], - ['%d-%m-%Y %H:%M:%S', '%Y-%m-%d %H:%M:%S | 14-01-2019 01:32:10'], + ['%d-%m-%Y %H:%M:%S', '%d-%m-%Y %H:%M:%S | 14-01-2019 01:32:10'], ['%H:%M:%S', '%H:%M:%S | 01:32:10'], ]; diff --git a/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-core/src/time-format/formatters/smartDateDetailed.ts b/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-core/src/time-format/formatters/smartDateDetailed.ts new file mode 100644 index 000000000000..f582641bb958 --- /dev/null +++ b/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-core/src/time-format/formatters/smartDateDetailed.ts @@ -0,0 +1,18 @@ +import createMultiFormatter from '../factories/createMultiFormatter'; + +const smartDateDetailedFormatter = createMultiFormatter({ + id: 'smart_date_detailed', + label: 'Detailed adaptive formatter', + formats: { + millisecond: '%Y-%m-%d %H:%M:%S.%L', + second: '%Y-%m-%d %H:%M:%S', + minute: '%Y-%m-%d %H:%M', + hour: '%Y-%m-%d %H:%M', + day: '%Y-%m-%d', + week: '%Y-%m-%d', + month: '%Y-%m-%d', + year: '%Y', + }, +}); + +export default smartDateDetailedFormatter; diff --git a/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-core/src/time-format/index.ts b/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-core/src/time-format/index.ts index fcb39df25b34..78c19970841f 100644 --- a/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-core/src/time-format/index.ts +++ b/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-core/src/time-format/index.ts @@ -14,6 +14,7 @@ export { default as createD3TimeFormatter } from './factories/createD3TimeFormat export { default as createMultiFormatter } from './factories/createMultiFormatter'; export { default as smartDateFormatter } from './formatters/smartDate'; +export { default as smartDateDetailedFormatter } from './formatters/smartDateDetailed'; export { default as smartDateVerboseFormatter } from './formatters/smartDateVerbose'; export * from './types'; diff --git a/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-core/test/time-format/formatters/smartDateDetailed.test.ts b/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-core/test/time-format/formatters/smartDateDetailed.test.ts new file mode 100644 index 000000000000..fb1c99511b90 --- /dev/null +++ b/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-core/test/time-format/formatters/smartDateDetailed.test.ts @@ -0,0 +1,30 @@ +import TimeFormatter from '@superset-ui/core/src/time-format/TimeFormatter'; +import smartDateDetailedFormatter from '@superset-ui/core/src/time-format/formatters/smartDateDetailed'; + +describe('smartDateDetailedFormatter', () => { + const formatter = smartDateDetailedFormatter; + + it('is a function', () => { + expect(formatter).toBeInstanceOf(TimeFormatter); + }); + + it('shows only year when 1st day of the year', () => { + expect(formatter(new Date('2020-01-01 0:00:00'))).toBe('2020'); + }); + + it('shows full date when a regular date', () => { + expect(formatter(new Date('2020-03-01 00:00:00'))).toBe('2020-03-01'); + }); + + it('shows full date including time of day without seconds when hour precision', () => { + expect(formatter(new Date('2020-03-01 13:00:00'))).toBe('2020-03-01 13:00'); + }); + + it('shows full date including time of day when minute precision', () => { + expect(formatter(new Date('2020-03-10 13:10:00'))).toBe('2020-03-10 13:10'); + }); + + it('shows full date including time of day when subsecond precision', () => { + expect(formatter(new Date('2020-03-10 13:10:00.1'))).toBe('2020-03-10 13:10:00.100'); + }); +}); diff --git a/superset-frontend/temporary_superset_ui/superset-ui/plugins/plugin-chart-echarts/src/Timeseries/controlPanel.tsx b/superset-frontend/temporary_superset_ui/superset-ui/plugins/plugin-chart-echarts/src/Timeseries/controlPanel.tsx index 5986ef44e79e..8e8407fa7195 100644 --- a/superset-frontend/temporary_superset_ui/superset-ui/plugins/plugin-chart-echarts/src/Timeseries/controlPanel.tsx +++ b/superset-frontend/temporary_superset_ui/superset-ui/plugins/plugin-chart-echarts/src/Timeseries/controlPanel.tsx @@ -18,7 +18,12 @@ */ import React from 'react'; import { legacyValidateInteger, legacyValidateNumber, t } from '@superset-ui/core'; -import { ControlPanelConfig, sections } from '@superset-ui/chart-controls'; +import { + ControlPanelConfig, + D3_TIME_FORMAT_DOCS, + sections, + sharedControls, +} from '@superset-ui/chart-controls'; import { DEFAULT_FORM_DATA, @@ -51,12 +56,14 @@ const { rowLimit, seriesType, stack, + tooltipTimeFormat, truncateYAxis, yAxisBounds, zoomable, xAxisLabelRotation, + xAxisShowMinLabel, + xAxisShowMaxLabel, } = DEFAULT_FORM_DATA; - const config: ControlPanelConfig = { controlPanelSections: [ sections.legacyTimeseriesTime, @@ -314,14 +321,25 @@ const config: ControlPanelConfig = { [legendTypeControl, legendOrientationControl], [legendMarginControl, noopControl], [

{t('X Axis')}

], - ['x_axis_time_format'], + [ + { + name: 'x_axis_time_format', + config: { + ...sharedControls.x_axis_time_format, + default: 'smart_date', + description: `${D3_TIME_FORMAT_DOCS}. ${t( + 'When using other than adaptive formatting, labels may overlap.', + )}`, + }, + }, + ], [ { name: 'xAxisShowMinLabel', config: { type: 'CheckboxControl', label: t('Show Min Label'), - default: true, + default: xAxisShowMinLabel, renderTrigger: true, description: t('Show Min Label'), }, @@ -333,7 +351,7 @@ const config: ControlPanelConfig = { config: { type: 'CheckboxControl', label: t('Show Max Label'), - default: true, + default: xAxisShowMaxLabel, renderTrigger: true, description: t('Show Max Label'), }, @@ -371,6 +389,17 @@ const config: ControlPanelConfig = { }, }, ], + [ + { + name: 'tooltipTimeFormat', + config: { + ...sharedControls.x_axis_time_format, + label: t('Tooltip time format'), + default: tooltipTimeFormat, + clearable: false, + }, + }, + ], // eslint-disable-next-line react/jsx-key [

{t('Y Axis')}

], ['y_axis_format'], diff --git a/superset-frontend/temporary_superset_ui/superset-ui/plugins/plugin-chart-echarts/src/Timeseries/transformProps.ts b/superset-frontend/temporary_superset_ui/superset-ui/plugins/plugin-chart-echarts/src/Timeseries/transformProps.ts index 21b4d1dec988..baee870ac776 100644 --- a/superset-frontend/temporary_superset_ui/superset-ui/plugins/plugin-chart-echarts/src/Timeseries/transformProps.ts +++ b/superset-frontend/temporary_superset_ui/superset-ui/plugins/plugin-chart-echarts/src/Timeseries/transformProps.ts @@ -27,8 +27,8 @@ import { isIntervalAnnotationLayer, isTimeseriesAnnotationLayer, getTimeFormatter, - getTimeFormatterForGranularity, smartDateFormatter, + smartDateDetailedFormatter, TimeseriesChartDataResponseResult, TimeFormatter, } from '@superset-ui/core'; @@ -85,7 +85,7 @@ export default function transformProps(chartProps: ChartProps): EchartsProps { xAxisShowMaxLabel, xAxisTimeFormat, yAxisBounds, - timeGrainSqla, + tooltipTimeFormat, zoomable, richTooltip, xAxisLabelRotation, @@ -138,9 +138,18 @@ export default function transformProps(chartProps: ChartProps): EchartsProps { if (max === undefined) max = 1; } - let xAxisFormatter: TimeFormatter | StringConstructor; - if (xAxisTimeFormat === smartDateFormatter.id) { - xAxisFormatter = getTimeFormatterForGranularity(timeGrainSqla); + let tooltipFormatter: TimeFormatter | StringConstructor; + if (tooltipTimeFormat === smartDateFormatter.id) { + tooltipFormatter = smartDateDetailedFormatter; + } else if (tooltipTimeFormat) { + tooltipFormatter = getTimeFormatter(xAxisTimeFormat); + } else { + tooltipFormatter = String; + } + + let xAxisFormatter: TimeFormatter | StringConstructor | undefined; + if (xAxisTimeFormat === smartDateFormatter.id || !xAxisTimeFormat) { + xAxisFormatter = undefined; } else if (xAxisTimeFormat) { xAxisFormatter = getTimeFormatter(xAxisTimeFormat); } else { @@ -189,7 +198,7 @@ export default function transformProps(chartProps: ChartProps): EchartsProps { const value: number = !richTooltip ? params.value : params[0].value[0]; const prophetValue = !richTooltip ? [params] : params; - const rows: Array = [`${xAxisFormatter(value)}`]; + const rows: Array = [`${tooltipFormatter(value)}`]; const prophetValues: Record = extractProphetValuesFromTooltipParams( prophetValue, ); diff --git a/superset-frontend/temporary_superset_ui/superset-ui/plugins/plugin-chart-echarts/src/Timeseries/types.ts b/superset-frontend/temporary_superset_ui/superset-ui/plugins/plugin-chart-echarts/src/Timeseries/types.ts index c3a7ef53f496..bf312d7f99a7 100644 --- a/superset-frontend/temporary_superset_ui/superset-ui/plugins/plugin-chart-echarts/src/Timeseries/types.ts +++ b/superset-frontend/temporary_superset_ui/superset-ui/plugins/plugin-chart-echarts/src/Timeseries/types.ts @@ -54,6 +54,7 @@ export type EchartsTimeseriesFormData = { rowLimit: number; seriesType: EchartsTimeseriesSeriesType; stack: boolean; + tooltipTimeFormat?: string; truncateYAxis: boolean; yAxisFormat?: string; xAxisShowMinLabel?: boolean; @@ -85,11 +86,12 @@ export const DEFAULT_FORM_DATA: EchartsTimeseriesFormData = { rowLimit: 10000, seriesType: EchartsTimeseriesSeriesType.Line, stack: false, + tooltipTimeFormat: 'smart_date', truncateYAxis: true, yAxisBounds: [null, null], - xAxisShowMinLabel: true, - xAxisShowMaxLabel: true, + xAxisShowMinLabel: false, + xAxisShowMaxLabel: false, zoomable: false, richTooltip: true, - xAxisLabelRotation: 45, + xAxisLabelRotation: 0, };