hyperdx/packages/app/src/components/ChartDisplaySettingsDrawer.tsx
Drew Davis ddc54e43f0
feat: Allow customizing empty-period fill value (#1617)
Closes HDX-3220
Closes HDX-1718
Closes HDX-3205

# Summary

This PR adds an option that allows users to customize the 0-fill behavior on time charts. The default behavior remains to fill all empty intervals with 0. The user can now disable the filling behavior. When fill is disabled, series will appear to be interpolated.

This PR also consolidates various display settings into a drawer, replacing the existing Number Format drawer. In the process, various form-related bugs were fixed in the drawer, and micro/nano second input factors were added.

## New Chart Display Settings Drawer

<img width="1697" height="979" alt="Screenshot 2026-01-20 at 9 10 59 AM" src="https://github.com/user-attachments/assets/1683666a-7c56-4018-8e5b-2c6c814f0cd2" />

## Zero-fill behavior

Enabled (default):

<img width="1458" height="494" alt="Screenshot 2026-01-20 at 9 12 45 AM" src="https://github.com/user-attachments/assets/0306644e-d2ff-46d6-998b-eb458d5c9ccc" />

Disabled:

<img width="1456" height="505" alt="Screenshot 2026-01-20 at 9 12 37 AM" src="https://github.com/user-attachments/assets/f084887e-4099-4365-af4f-73eceaf5dc3d" />
2026-01-21 22:13:16 +00:00

144 lines
3.9 KiB
TypeScript

import { useCallback } from 'react';
import { useForm, useWatch } from 'react-hook-form';
import {
ChartConfigWithDateRange,
DisplayType,
} from '@hyperdx/common-utils/dist/types';
import {
Box,
Button,
Checkbox,
Divider,
Drawer,
Group,
Stack,
} from '@mantine/core';
import { shouldFillNullsWithZero } from '@/ChartUtils';
import { FormatTime } from '@/useFormatTime';
import { DEFAULT_NUMBER_FORMAT, NumberFormatForm } from './NumberFormat';
export type ChartConfigDisplaySettings = Pick<
ChartConfigWithDateRange,
| 'numberFormat'
| 'alignDateRangeToGranularity'
| 'fillNulls'
| 'compareToPreviousPeriod'
>;
interface ChartDisplaySettingsDrawerProps {
opened: boolean;
settings: ChartConfigDisplaySettings;
displayType: DisplayType;
previousDateRange?: [Date, Date];
onChange: (settings: ChartConfigDisplaySettings) => void;
onClose: () => void;
}
function applyDefaultSettings({
numberFormat,
alignDateRangeToGranularity,
compareToPreviousPeriod,
fillNulls,
}: ChartConfigDisplaySettings): ChartConfigDisplaySettings {
return {
numberFormat: numberFormat ?? DEFAULT_NUMBER_FORMAT,
alignDateRangeToGranularity:
alignDateRangeToGranularity == null ? true : alignDateRangeToGranularity,
fillNulls: fillNulls ?? 0,
compareToPreviousPeriod: compareToPreviousPeriod ?? false,
};
}
export default function ChartDisplaySettingsDrawer({
settings,
opened,
displayType,
onChange,
onClose,
previousDateRange,
}: ChartDisplaySettingsDrawerProps) {
const { control, handleSubmit, register, reset, setValue } =
useForm<ChartConfigDisplaySettings>({
defaultValues: applyDefaultSettings(settings),
});
const fillNulls = useWatch({ control, name: 'fillNulls' });
const isFillNullsEnabled = shouldFillNullsWithZero(fillNulls);
const handleClose = useCallback(() => {
reset(applyDefaultSettings(settings)); // Reset to default values, without saving
onClose();
}, [onClose, reset, settings]);
const applyChanges = useCallback(() => {
handleSubmit(onChange)();
onClose();
}, [onChange, handleSubmit, onClose]);
const resetToDefaults = useCallback(() => {
reset(applyDefaultSettings({}));
}, [reset]);
const isTimeChart =
displayType === DisplayType.Line || displayType === DisplayType.StackedBar;
return (
<Drawer
title="Display Settings"
opened={opened}
onClose={handleClose}
position="right"
>
<Stack>
{isTimeChart && (
<>
<Checkbox
size="xs"
label="Show Complete Intervals"
{...register('alignDateRangeToGranularity')}
/>
<Box>
<Checkbox
size="xs"
label="Fill Missing Intervals with Zero"
checked={isFillNullsEnabled}
onChange={e => {
setValue('fillNulls', e.currentTarget.checked ? 0 : false);
}}
/>
</Box>
<Checkbox
size="xs"
label="Compare to Previous Period"
description={
previousDateRange && (
<>
(
<FormatTime value={previousDateRange[0]} format="short" />
{' - '}
<FormatTime value={previousDateRange[1]} format="short" />)
</>
)
}
{...register('compareToPreviousPeriod')}
/>
<Divider />
</>
)}
<NumberFormatForm control={control} />
<Divider />
<Group gap="xs" mt="xs" justify="space-between">
<Button type="submit" variant="secondary" onClick={resetToDefaults}>
Reset to Defaults
</Button>
<Button type="submit" variant="primary" onClick={applyChanges}>
Apply
</Button>
</Group>
</Stack>
</Drawer>
);
}