From 9c6aab3bc950ea6bd1c65ecf0f50f8cfe43ede94 Mon Sep 17 00:00:00 2001 From: Ashley Harrison Date: Tue, 2 Aug 2022 13:33:59 +0100 Subject: [PATCH] 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 --- .betterer.results | 5 - .../DateTimePickers/TimeOfDayPicker.story.tsx | 57 +++----- .../DateTimePickers/TimeRangeInput.story.tsx | 131 ++++++++---------- .../DateTimePickers/TimeRangeInput.tsx | 8 +- 4 files changed, 82 insertions(+), 119 deletions(-) diff --git a/.betterer.results b/.betterer.results index d049b0568bb..567cc847348 100644 --- a/.betterer.results +++ b/.betterer.results @@ -1401,11 +1401,6 @@ exports[`better eslint`] = { "packages/grafana-ui/src/components/DateTimePickers/TimeOfDayPicker.tsx:5381": [ [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": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"] ], diff --git a/packages/grafana-ui/src/components/DateTimePickers/TimeOfDayPicker.story.tsx b/packages/grafana-ui/src/components/DateTimePickers/TimeOfDayPicker.story.tsx index 532515643f3..28dba764250 100644 --- a/packages/grafana-ui/src/components/DateTimePickers/TimeOfDayPicker.story.tsx +++ b/packages/grafana-ui/src/components/DateTimePickers/TimeOfDayPicker.story.tsx @@ -1,57 +1,38 @@ 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 { dateTime } from '@grafana/data'; import { TimeOfDayPicker } from '@grafana/ui'; -import { UseState } from '../../utils/storybook/UseState'; import { withCenteredStory } from '../../utils/storybook/withCenteredStory'; const meta: ComponentMeta = { title: 'Pickers and Editors/TimePickers/TimeOfDayPicker', component: TimeOfDayPicker, decorators: [withCenteredStory], + parameters: { + controls: { + exclude: ['onChange'], + }, + }, + args: { + value: dateTime(Date.now()), + }, + argTypes: { value: { control: 'date' } }, }; -export const basic = () => { +export const Basic: ComponentStory = (args) => { + const [, updateArgs] = useArgs(); return ( - { + action('on selected')(newValue); + updateArgs({ value: newValue }); }} - > - {(value, updateValue) => { - return ( - { - action('on selected')(newValue); - updateValue({ value: newValue }); - }} - value={value.value} - /> - ); - }} - - ); -}; - -export const onlyMinutes = () => { - return ( - - {(value, updateValue) => { - return ( - { - action('on selected')(newValue); - updateValue({ value: newValue }); - }} - value={value.value} - showHour={false} - /> - ); - }} - + /> ); }; diff --git a/packages/grafana-ui/src/components/DateTimePickers/TimeRangeInput.story.tsx b/packages/grafana-ui/src/components/DateTimePickers/TimeRangeInput.story.tsx index 2297ceb4083..cd84f8e4e48 100644 --- a/packages/grafana-ui/src/components/DateTimePickers/TimeRangeInput.story.tsx +++ b/packages/grafana-ui/src/components/DateTimePickers/TimeRangeInput.story.tsx @@ -1,97 +1,82 @@ import { action } from '@storybook/addon-actions'; +import { useArgs } from '@storybook/client-api'; import { ComponentMeta, ComponentStory } from '@storybook/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 { UseState } from '../../utils/storybook/UseState'; import { withCenteredStory } from '../../utils/storybook/withCenteredStory'; -import { TimeRangeInputProps } from './TimeRangeInput'; 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 = { title: 'Pickers and Editors/TimePickers/TimeRangeInput', component: TimeRangeInput, decorators: [withCenteredStory], parameters: { + controls: { + exclude: ['onChange', 'onChangeTimeZone'], + }, docs: { page: mdx, }, }, + args: { + value: { + from: now.subtract(6, 'h'), + to: now, + raw: { + from: 'now-6h', + to: 'now', + }, + }, + timeZone: DefaultTimeZone, + }, }; -interface State { - value: TimeRange; - timeZone: TimeZone; -} - -const getComponentWithState = (initialState: State, props: TimeRangeInputProps) => ( - - {(state, updateValue) => { - return ( - { - action('onChange fired')(value); - updateValue({ - ...state, - value, - }); - }} - onChangeTimeZone={(timeZone) => { - action('onChangeTimeZone fired')(timeZone); - updateValue({ - ...state, - timeZone, - }); - }} - /> - ); - }} - -); - -export const Relative: ComponentStory = (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 = (props) => { - const to = dateTime(); - const from = to.subtract(6, 'h'); - - return getComponentWithState( - { - value: { - from, - to, - raw: { - from, - to, - }, - }, - timeZone: DefaultTimeZone, - }, - props +export const Basic: ComponentStory = (args) => { + const [, updateArgs] = useArgs(); + return ( + { + action('onChange fired')(value); + // Need some special logic to handle when the range is cleared since + // storybook controls don't support null datetimes + updateArgs({ + value: isOnRangeClear(value) ? nullRange : value, + }); + }} + onChangeTimeZone={(timeZone) => { + action('onChangeTimeZone fired')(timeZone); + updateArgs({ + timeZone, + }); + }} + /> ); }; diff --git a/packages/grafana-ui/src/components/DateTimePickers/TimeRangeInput.tsx b/packages/grafana-ui/src/components/DateTimePickers/TimeRangeInput.tsx index f934ed4a7eb..f3407f2efcf 100644 --- a/packages/grafana-ui/src/components/DateTimePickers/TimeRangeInput.tsx +++ b/packages/grafana-ui/src/components/DateTimePickers/TimeRangeInput.tsx @@ -14,7 +14,7 @@ import { TimePickerButtonLabel } from './TimeRangePicker'; import { TimePickerContent } from './TimeRangePicker/TimePickerContent'; import { quickOptions } from './options'; -const isValidTimeRange = (range: any) => { +const isValidTimeRange = (range: TimeRange) => { return dateMath.isValid(range.from) && dateMath.isValid(range.to); }; @@ -26,7 +26,9 @@ export interface TimeRangeInputProps { hideTimeZone?: boolean; placeholder?: string; clearable?: boolean; + /** Controls horizontal alignment of the picker menu */ isReversed?: boolean; + /** Controls visibility of the preset time ranges (e.g. **Last 5 minutes**) in the picker menu */ hideQuickRanges?: boolean; disabled?: boolean; } @@ -83,7 +85,7 @@ export const TimeRangeInput: FC = ({ onClick={onOpen} > {isValidTimeRange(value) ? ( - + ) : ( {placeholder} )} @@ -101,7 +103,7 @@ export const TimeRangeInput: FC = ({