Chore: remove UseState + add support for controls in TimeOfDayPicker/TimeRangeInput story (#53040)

* remove UseState + add support for controls in TimeOfDayPicker story

* remove UseState + add controls support in TimeRangeInput story

* Add documentation + fix error when clearing

* properly type range
This commit is contained in:
Ashley Harrison 2022-08-02 13:33:59 +01:00 committed by GitHub
parent db08ece3db
commit 9c6aab3bc9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 82 additions and 119 deletions

View File

@ -1401,11 +1401,6 @@ exports[`better eslint`] = {
"packages/grafana-ui/src/components/DateTimePickers/TimeOfDayPicker.tsx:5381": [ "packages/grafana-ui/src/components/DateTimePickers/TimeOfDayPicker.tsx:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"] [0, 0, 0, "Unexpected any. Specify a different type.", "0"]
], ],
"packages/grafana-ui/src/components/DateTimePickers/TimeRangeInput.tsx:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
[0, 0, 0, "Do not use any type assertions.", "1"],
[0, 0, 0, "Do not use any type assertions.", "2"]
],
"packages/grafana-ui/src/components/DateTimePickers/TimeZonePicker/TimeZoneOption.tsx:5381": [ "packages/grafana-ui/src/components/DateTimePickers/TimeZonePicker/TimeZoneOption.tsx:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"] [0, 0, 0, "Unexpected any. Specify a different type.", "0"]
], ],

View File

@ -1,57 +1,38 @@
import { action } from '@storybook/addon-actions'; import { action } from '@storybook/addon-actions';
import { ComponentMeta } from '@storybook/react'; import { useArgs } from '@storybook/client-api';
import { ComponentMeta, ComponentStory } from '@storybook/react';
import React from 'react'; import React from 'react';
import { dateTime } from '@grafana/data'; import { dateTime } from '@grafana/data';
import { TimeOfDayPicker } from '@grafana/ui'; import { TimeOfDayPicker } from '@grafana/ui';
import { UseState } from '../../utils/storybook/UseState';
import { withCenteredStory } from '../../utils/storybook/withCenteredStory'; import { withCenteredStory } from '../../utils/storybook/withCenteredStory';
const meta: ComponentMeta<typeof TimeOfDayPicker> = { const meta: ComponentMeta<typeof TimeOfDayPicker> = {
title: 'Pickers and Editors/TimePickers/TimeOfDayPicker', title: 'Pickers and Editors/TimePickers/TimeOfDayPicker',
component: TimeOfDayPicker, component: TimeOfDayPicker,
decorators: [withCenteredStory], decorators: [withCenteredStory],
parameters: {
controls: {
exclude: ['onChange'],
},
},
args: {
value: dateTime(Date.now()),
},
argTypes: { value: { control: 'date' } },
}; };
export const basic = () => { export const Basic: ComponentStory<typeof TimeOfDayPicker> = (args) => {
const [, updateArgs] = useArgs();
return ( return (
<UseState <TimeOfDayPicker
initialState={{ {...args}
value: dateTime(Date.now()), onChange={(newValue) => {
action('on selected')(newValue);
updateArgs({ value: newValue });
}} }}
> />
{(value, updateValue) => {
return (
<TimeOfDayPicker
onChange={(newValue) => {
action('on selected')(newValue);
updateValue({ value: newValue });
}}
value={value.value}
/>
);
}}
</UseState>
);
};
export const onlyMinutes = () => {
return (
<UseState initialState={{ value: dateTime(Date.now()) }}>
{(value, updateValue) => {
return (
<TimeOfDayPicker
onChange={(newValue) => {
action('on selected')(newValue);
updateValue({ value: newValue });
}}
value={value.value}
showHour={false}
/>
);
}}
</UseState>
); );
}; };

View File

@ -1,97 +1,82 @@
import { action } from '@storybook/addon-actions'; import { action } from '@storybook/addon-actions';
import { useArgs } from '@storybook/client-api';
import { ComponentMeta, ComponentStory } from '@storybook/react'; import { ComponentMeta, ComponentStory } from '@storybook/react';
import React from 'react'; import React from 'react';
import { dateTime, DefaultTimeZone, TimeRange, TimeZone } from '@grafana/data'; import { dateTime, DefaultTimeZone, isDateTime, TimeRange } from '@grafana/data';
import { TimeRangeInput } from '@grafana/ui'; import { TimeRangeInput } from '@grafana/ui';
import { UseState } from '../../utils/storybook/UseState';
import { withCenteredStory } from '../../utils/storybook/withCenteredStory'; import { withCenteredStory } from '../../utils/storybook/withCenteredStory';
import { TimeRangeInputProps } from './TimeRangeInput';
import mdx from './TimeRangeInput.mdx'; import mdx from './TimeRangeInput.mdx';
const now = dateTime(Date.now());
const isOnRangeClear = (value: TimeRange) => {
return (
!value.from.isValid() &&
!value.to.isValid() &&
isDateTime(value.raw.from) &&
!value.raw.from.isValid() &&
isDateTime(value.raw.to) &&
!value.raw.to.isValid()
);
};
const nullRange = {
from: null,
to: null,
raw: {
from: null,
to: null,
},
};
const meta: ComponentMeta<typeof TimeRangeInput> = { const meta: ComponentMeta<typeof TimeRangeInput> = {
title: 'Pickers and Editors/TimePickers/TimeRangeInput', title: 'Pickers and Editors/TimePickers/TimeRangeInput',
component: TimeRangeInput, component: TimeRangeInput,
decorators: [withCenteredStory], decorators: [withCenteredStory],
parameters: { parameters: {
controls: {
exclude: ['onChange', 'onChangeTimeZone'],
},
docs: { docs: {
page: mdx, page: mdx,
}, },
}, },
args: {
value: {
from: now.subtract(6, 'h'),
to: now,
raw: {
from: 'now-6h',
to: 'now',
},
},
timeZone: DefaultTimeZone,
},
}; };
interface State { export const Basic: ComponentStory<typeof TimeRangeInput> = (args) => {
value: TimeRange; const [, updateArgs] = useArgs();
timeZone: TimeZone; return (
} <TimeRangeInput
{...args}
const getComponentWithState = (initialState: State, props: TimeRangeInputProps) => ( onChange={(value) => {
<UseState initialState={initialState}> action('onChange fired')(value);
{(state, updateValue) => { // Need some special logic to handle when the range is cleared since
return ( // storybook controls don't support null datetimes
<TimeRangeInput updateArgs({
{...props} value: isOnRangeClear(value) ? nullRange : value,
value={state.value} });
timeZone={state.timeZone} }}
onChange={(value) => { onChangeTimeZone={(timeZone) => {
action('onChange fired')(value); action('onChangeTimeZone fired')(timeZone);
updateValue({ updateArgs({
...state, timeZone,
value, });
}); }}
}} />
onChangeTimeZone={(timeZone) => {
action('onChangeTimeZone fired')(timeZone);
updateValue({
...state,
timeZone,
});
}}
/>
);
}}
</UseState>
);
export const Relative: ComponentStory<typeof TimeRangeInput> = (props) => {
const to = dateTime();
const from = to.subtract(6, 'h');
return getComponentWithState(
{
value: {
from,
to,
raw: {
from: 'now-6h',
to: 'now',
},
},
timeZone: DefaultTimeZone,
},
props
);
};
export const Absolute: ComponentStory<typeof TimeRangeInput> = (props) => {
const to = dateTime();
const from = to.subtract(6, 'h');
return getComponentWithState(
{
value: {
from,
to,
raw: {
from,
to,
},
},
timeZone: DefaultTimeZone,
},
props
); );
}; };

View File

@ -14,7 +14,7 @@ import { TimePickerButtonLabel } from './TimeRangePicker';
import { TimePickerContent } from './TimeRangePicker/TimePickerContent'; import { TimePickerContent } from './TimeRangePicker/TimePickerContent';
import { quickOptions } from './options'; import { quickOptions } from './options';
const isValidTimeRange = (range: any) => { const isValidTimeRange = (range: TimeRange) => {
return dateMath.isValid(range.from) && dateMath.isValid(range.to); return dateMath.isValid(range.from) && dateMath.isValid(range.to);
}; };
@ -26,7 +26,9 @@ export interface TimeRangeInputProps {
hideTimeZone?: boolean; hideTimeZone?: boolean;
placeholder?: string; placeholder?: string;
clearable?: boolean; clearable?: boolean;
/** Controls horizontal alignment of the picker menu */
isReversed?: boolean; isReversed?: boolean;
/** Controls visibility of the preset time ranges (e.g. **Last 5 minutes**) in the picker menu */
hideQuickRanges?: boolean; hideQuickRanges?: boolean;
disabled?: boolean; disabled?: boolean;
} }
@ -83,7 +85,7 @@ export const TimeRangeInput: FC<TimeRangeInputProps> = ({
onClick={onOpen} onClick={onOpen}
> >
{isValidTimeRange(value) ? ( {isValidTimeRange(value) ? (
<TimePickerButtonLabel value={value as TimeRange} timeZone={timeZone} /> <TimePickerButtonLabel value={value} timeZone={timeZone} />
) : ( ) : (
<span className={styles.placeholder}>{placeholder}</span> <span className={styles.placeholder}>{placeholder}</span>
)} )}
@ -101,7 +103,7 @@ export const TimeRangeInput: FC<TimeRangeInputProps> = ({
<ClickOutsideWrapper includeButtonPress={false} onClick={onClose}> <ClickOutsideWrapper includeButtonPress={false} onClick={onClose}>
<TimePickerContent <TimePickerContent
timeZone={timeZone} timeZone={timeZone}
value={isValidTimeRange(value) ? (value as TimeRange) : getDefaultTimeRange()} value={isValidTimeRange(value) ? value : getDefaultTimeRange()}
onChange={onRangeChange} onChange={onRangeChange}
quickOptions={quickOptions} quickOptions={quickOptions}
onChangeTimeZone={onChangeTimeZone} onChangeTimeZone={onChangeTimeZone}