mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
TimeSeries: Support multiple timezones in x axis (#52424)
Co-authored-by: Leon Sorokin <leeoniya@gmail.com>
This commit is contained in:
@@ -44,7 +44,7 @@ export interface GraphNGProps extends Themeable2 {
|
||||
width: number;
|
||||
height: number;
|
||||
timeRange: TimeRange;
|
||||
timeZone: TimeZone;
|
||||
timeZones: TimeZone[] | TimeZone;
|
||||
legend: VizLegendOptions;
|
||||
fields?: XYFieldMatchers; // default will assume timeseries data
|
||||
renderers?: Renderers;
|
||||
@@ -216,17 +216,17 @@ export class GraphNG extends React.Component<GraphNGProps, GraphNGState> {
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps: GraphNGProps) {
|
||||
const { frames, structureRev, timeZone, propsToDiff } = this.props;
|
||||
const { frames, structureRev, timeZones, propsToDiff } = this.props;
|
||||
|
||||
const propsChanged = !sameProps(prevProps, this.props, propsToDiff);
|
||||
|
||||
if (frames !== prevProps.frames || propsChanged || timeZone !== prevProps.timeZone) {
|
||||
if (frames !== prevProps.frames || propsChanged || timeZones !== prevProps.timeZones) {
|
||||
let newState = this.prepState(this.props, false);
|
||||
|
||||
if (newState) {
|
||||
const shouldReconfig =
|
||||
this.state.config === undefined ||
|
||||
timeZone !== prevProps.timeZone ||
|
||||
timeZones !== prevProps.timeZones ||
|
||||
structureRev !== prevProps.structureRev ||
|
||||
!structureRev ||
|
||||
propsChanged;
|
||||
|
||||
@@ -214,7 +214,7 @@ describe('GraphNG utils', () => {
|
||||
const result = preparePlotConfigBuilder({
|
||||
frame: frame!,
|
||||
theme: createTheme(),
|
||||
timeZone: DefaultTimeZone,
|
||||
timeZones: [DefaultTimeZone],
|
||||
getTimeRange: getDefaultTimeRange,
|
||||
eventBus: new EventBusSrv(),
|
||||
sync: () => DashboardCursorSync.Tooltip,
|
||||
|
||||
@@ -22,12 +22,12 @@ export class UnthemedTimeSeries extends React.Component<TimeSeriesProps> {
|
||||
|
||||
prepConfig = (alignedFrame: DataFrame, allFrames: DataFrame[], getTimeRange: () => TimeRange) => {
|
||||
const { eventBus, sync } = this.context as PanelContext;
|
||||
const { theme, timeZone, renderers, tweakAxis, tweakScale } = this.props;
|
||||
const { theme, timeZones, renderers, tweakAxis, tweakScale } = this.props;
|
||||
|
||||
return preparePlotConfigBuilder({
|
||||
frame: alignedFrame,
|
||||
theme,
|
||||
timeZone,
|
||||
timeZones: Array.isArray(timeZones) ? timeZones : [timeZones],
|
||||
getTimeRange,
|
||||
eventBus,
|
||||
sync,
|
||||
|
||||
@@ -48,7 +48,7 @@ export const preparePlotConfigBuilder: UPlotConfigPrepFn<{
|
||||
}> = ({
|
||||
frame,
|
||||
theme,
|
||||
timeZone,
|
||||
timeZones,
|
||||
getTimeRange,
|
||||
eventBus,
|
||||
sync,
|
||||
@@ -57,7 +57,7 @@ export const preparePlotConfigBuilder: UPlotConfigPrepFn<{
|
||||
tweakScale = (opts) => opts,
|
||||
tweakAxis = (opts) => opts,
|
||||
}) => {
|
||||
const builder = new UPlotConfigBuilder(timeZone);
|
||||
const builder = new UPlotConfigBuilder(timeZones[0]);
|
||||
|
||||
let alignedFrame: DataFrame;
|
||||
|
||||
@@ -97,16 +97,51 @@ export const preparePlotConfigBuilder: UPlotConfigPrepFn<{
|
||||
},
|
||||
});
|
||||
|
||||
builder.addAxis({
|
||||
scaleKey: xScaleKey,
|
||||
isTime: true,
|
||||
placement: xFieldAxisPlacement,
|
||||
show: xFieldAxisShow,
|
||||
label: xField.config.custom?.axisLabel,
|
||||
timeZone,
|
||||
theme,
|
||||
grid: { show: xField.config.custom?.axisGridShow },
|
||||
});
|
||||
// filters first 2 ticks to make space for timezone labels
|
||||
const filterTicks: uPlot.Axis.Filter | undefined =
|
||||
timeZones.length > 1
|
||||
? (u, splits) => {
|
||||
return splits.map((v, i) => (i < 2 ? null : v));
|
||||
}
|
||||
: undefined;
|
||||
|
||||
for (let i = 0; i < timeZones.length; i++) {
|
||||
const timeZone = timeZones[i];
|
||||
builder.addAxis({
|
||||
scaleKey: xScaleKey,
|
||||
isTime: true,
|
||||
placement: xFieldAxisPlacement,
|
||||
show: xFieldAxisShow,
|
||||
label: xField.config.custom?.axisLabel,
|
||||
timeZone,
|
||||
theme,
|
||||
grid: { show: i === 0 && xField.config.custom?.axisGridShow },
|
||||
filter: filterTicks,
|
||||
});
|
||||
}
|
||||
|
||||
// render timezone labels
|
||||
if (timeZones.length > 1) {
|
||||
builder.addHook('drawAxes', (u: uPlot) => {
|
||||
u.ctx.save();
|
||||
|
||||
u.ctx.fillStyle = theme.colors.text.primary;
|
||||
u.ctx.textAlign = 'left';
|
||||
u.ctx.textBaseline = 'bottom';
|
||||
|
||||
let i = 0;
|
||||
u.axes.forEach((a) => {
|
||||
if (a.side === 2) {
|
||||
//@ts-ignore
|
||||
let cssBaseline: number = a._pos + a._size;
|
||||
u.ctx.fillText(timeZones[i], u.bbox.left, cssBaseline * uPlot.pxRatio);
|
||||
i++;
|
||||
}
|
||||
});
|
||||
|
||||
u.ctx.restore();
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// Not time!
|
||||
if (xField.config.unit) {
|
||||
|
||||
@@ -151,7 +151,7 @@ export class UPlotAxisBuilder extends PlotConfigBuilder<AxisProps, Axis> {
|
||||
ticks
|
||||
),
|
||||
splits,
|
||||
values: values,
|
||||
values,
|
||||
space:
|
||||
space ??
|
||||
((self, axisIdx, scaleMin, scaleMax, plotDim) => {
|
||||
@@ -227,7 +227,7 @@ export function formatTime(
|
||||
format = systemDateFormats.interval.month;
|
||||
}
|
||||
|
||||
return splits.map((v) => dateTimeFormat(v, { format, timeZone }));
|
||||
return splits.map((v) => (v == null ? '' : dateTimeFormat(v, { format, timeZone })));
|
||||
}
|
||||
|
||||
export function getUPlotSideFromAxis(axis: AxisPlacement) {
|
||||
|
||||
@@ -87,8 +87,14 @@ export class UPlotConfigBuilder {
|
||||
addAxis(props: AxisProps) {
|
||||
props.placement = props.placement ?? AxisPlacement.Auto;
|
||||
props.grid = props.grid ?? {};
|
||||
if (this.axes[props.scaleKey]) {
|
||||
this.axes[props.scaleKey].merge(props);
|
||||
let scaleKey = props.scaleKey;
|
||||
|
||||
if (scaleKey === 'x') {
|
||||
scaleKey += props.timeZone ?? '';
|
||||
}
|
||||
|
||||
if (this.axes[scaleKey]) {
|
||||
this.axes[scaleKey].merge(props);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -106,7 +112,7 @@ export class UPlotConfigBuilder {
|
||||
props.size = 0;
|
||||
}
|
||||
|
||||
this.axes[props.scaleKey] = new UPlotAxisBuilder(props);
|
||||
this.axes[scaleKey] = new UPlotAxisBuilder(props);
|
||||
}
|
||||
|
||||
getAxisPlacement(scaleKey: string): AxisPlacement {
|
||||
@@ -285,7 +291,7 @@ export type Renderers = Array<{
|
||||
type UPlotConfigPrepOpts<T extends Record<string, any> = {}> = {
|
||||
frame: DataFrame;
|
||||
theme: GrafanaTheme2;
|
||||
timeZone: TimeZone;
|
||||
timeZones: TimeZone[];
|
||||
getTimeRange: () => TimeRange;
|
||||
eventBus: EventBus;
|
||||
allFrames: DataFrame[];
|
||||
|
||||
Reference in New Issue
Block a user