mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Grafana-UI: Enhances for TimeRangePicker and TimeRangeInput (#30102)
This commit is contained in:
parent
9734b7069c
commit
46167785e6
@ -39,6 +39,7 @@
|
||||
"@types/react-color": "3.0.1",
|
||||
"@types/react-select": "3.0.8",
|
||||
"@types/react-table": "7.0.12",
|
||||
"@testing-library/jest-dom": "5.11.9",
|
||||
"@sentry/browser": "5.25.0",
|
||||
"@types/slate": "0.47.1",
|
||||
"@types/slate-react": "0.22.5",
|
||||
|
@ -1,9 +1,11 @@
|
||||
import { Story } from '@storybook/react';
|
||||
import React from 'react';
|
||||
import { action } from '@storybook/addon-actions';
|
||||
import { dateTime, TimeFragment } from '@grafana/data';
|
||||
import { dateTime, DefaultTimeZone, TimeRange, TimeZone } from '@grafana/data';
|
||||
import { withCenteredStory } from '../../utils/storybook/withCenteredStory';
|
||||
import { UseState } from '../../utils/storybook/UseState';
|
||||
import { TimeRangeInput } from '@grafana/ui';
|
||||
import { TimeRangeInputProps } from './TimeRangeInput';
|
||||
import mdx from './TimeRangeInput.mdx';
|
||||
|
||||
export default {
|
||||
@ -17,51 +19,75 @@ export default {
|
||||
},
|
||||
};
|
||||
|
||||
export const basic = () => {
|
||||
return (
|
||||
<UseState
|
||||
initialState={{
|
||||
from: dateTime(),
|
||||
to: dateTime(),
|
||||
raw: { from: 'now-6h' as TimeFragment, to: 'now' as TimeFragment },
|
||||
}}
|
||||
>
|
||||
{(value, updateValue) => {
|
||||
return (
|
||||
<TimeRangeInput
|
||||
value={value}
|
||||
onChange={timeRange => {
|
||||
action('onChange fired')(timeRange);
|
||||
updateValue(timeRange);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
</UseState>
|
||||
interface State {
|
||||
value: TimeRange;
|
||||
timeZone: TimeZone;
|
||||
}
|
||||
|
||||
const getComponentWithState = (initialState: State, props: TimeRangeInputProps) => (
|
||||
<UseState initialState={initialState}>
|
||||
{(state, updateValue) => {
|
||||
return (
|
||||
<TimeRangeInput
|
||||
{...props}
|
||||
value={state.value}
|
||||
timeZone={state.timeZone}
|
||||
onChange={value => {
|
||||
action('onChange fired')(value);
|
||||
updateValue({
|
||||
...state,
|
||||
value,
|
||||
});
|
||||
}}
|
||||
onChangeTimeZone={timeZone => {
|
||||
action('onChangeTimeZone fired')(timeZone);
|
||||
updateValue({
|
||||
...state,
|
||||
timeZone,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
</UseState>
|
||||
);
|
||||
|
||||
export const Relative: Story<TimeRangeInputProps> = 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 clearable = () => {
|
||||
return (
|
||||
<UseState
|
||||
initialState={{
|
||||
from: dateTime(),
|
||||
to: dateTime(),
|
||||
raw: { from: 'now-6h' as TimeFragment, to: 'now' as TimeFragment },
|
||||
}}
|
||||
>
|
||||
{(value, updateValue) => {
|
||||
return (
|
||||
<TimeRangeInput
|
||||
clearable
|
||||
value={value}
|
||||
onChange={timeRange => {
|
||||
action('onChange fired')(timeRange);
|
||||
updateValue(timeRange);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
</UseState>
|
||||
export const Absolute: Story<TimeRangeInputProps> = props => {
|
||||
const to = dateTime();
|
||||
const from = to.subtract(6, 'h');
|
||||
|
||||
return getComponentWithState(
|
||||
{
|
||||
value: {
|
||||
from,
|
||||
to,
|
||||
raw: {
|
||||
from,
|
||||
to,
|
||||
},
|
||||
},
|
||||
timeZone: DefaultTimeZone,
|
||||
},
|
||||
props
|
||||
);
|
||||
};
|
||||
|
@ -14,7 +14,7 @@ const isValidTimeRange = (range: any) => {
|
||||
return dateMath.isValid(range.from) && dateMath.isValid(range.to);
|
||||
};
|
||||
|
||||
export interface Props {
|
||||
export interface TimeRangeInputProps {
|
||||
value: TimeRange;
|
||||
timeZone?: TimeZone;
|
||||
onChange: (timeRange: TimeRange) => void;
|
||||
@ -22,18 +22,22 @@ export interface Props {
|
||||
hideTimeZone?: boolean;
|
||||
placeholder?: string;
|
||||
clearable?: boolean;
|
||||
isReversed?: boolean;
|
||||
hideQuickRanges?: boolean;
|
||||
}
|
||||
|
||||
const noop = () => {};
|
||||
|
||||
export const TimeRangeInput: FC<Props> = ({
|
||||
export const TimeRangeInput: FC<TimeRangeInputProps> = ({
|
||||
value,
|
||||
onChange,
|
||||
onChangeTimeZone,
|
||||
onChangeTimeZone = noop,
|
||||
clearable,
|
||||
hideTimeZone = true,
|
||||
timeZone = 'browser',
|
||||
placeholder = 'Select time range',
|
||||
isReversed = true,
|
||||
hideQuickRanges = false,
|
||||
}) => {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const styles = useStyles(getStyles);
|
||||
@ -84,10 +88,11 @@ export const TimeRangeInput: FC<Props> = ({
|
||||
onChange={onRangeChange}
|
||||
otherOptions={otherOptions}
|
||||
quickOptions={quickOptions}
|
||||
onChangeTimeZone={onChangeTimeZone || noop}
|
||||
onChangeTimeZone={onChangeTimeZone}
|
||||
className={styles.content}
|
||||
hideTimeZone={hideTimeZone}
|
||||
isReversed
|
||||
isReversed={isReversed}
|
||||
hideQuickRanges={hideQuickRanges}
|
||||
/>
|
||||
</ClickOutsideWrapper>
|
||||
)}
|
||||
|
@ -1,10 +1,12 @@
|
||||
import { Story } from '@storybook/react';
|
||||
import React from 'react';
|
||||
import { action } from '@storybook/addon-actions';
|
||||
|
||||
import { TimeRangePicker } from '@grafana/ui';
|
||||
import { Button, TimeRangePicker } from '@grafana/ui';
|
||||
import { UseState } from '../../utils/storybook/UseState';
|
||||
import { withCenteredStory } from '../../utils/storybook/withCenteredStory';
|
||||
import { TimeFragment, dateTime } from '@grafana/data';
|
||||
import { dateTime, TimeRange, DefaultTimeZone, TimeZone, isDateTime } from '@grafana/data';
|
||||
import { TimeRangePickerProps } from './TimeRangePicker';
|
||||
|
||||
export default {
|
||||
title: 'Pickers and Editors/TimePickers/TimeRangePicker',
|
||||
@ -12,24 +14,37 @@ export default {
|
||||
decorators: [withCenteredStory],
|
||||
};
|
||||
|
||||
export const basic = () => {
|
||||
return (
|
||||
<UseState
|
||||
initialState={{
|
||||
from: dateTime(),
|
||||
to: dateTime(),
|
||||
raw: { from: 'now-6h' as TimeFragment, to: 'now' as TimeFragment },
|
||||
}}
|
||||
>
|
||||
{(value, updateValue) => {
|
||||
return (
|
||||
interface State {
|
||||
value: TimeRange;
|
||||
timeZone: TimeZone;
|
||||
history: TimeRange[];
|
||||
}
|
||||
|
||||
const getComponentWithState = (initialState: State, props: TimeRangePickerProps) => (
|
||||
<UseState initialState={initialState}>
|
||||
{(state, updateValue) => {
|
||||
return (
|
||||
<>
|
||||
<TimeRangePicker
|
||||
onChangeTimeZone={() => {}}
|
||||
timeZone="browser"
|
||||
value={value}
|
||||
onChange={timeRange => {
|
||||
action('onChange fired')(timeRange);
|
||||
updateValue(timeRange);
|
||||
{...props}
|
||||
timeZone={state.timeZone}
|
||||
value={state.value}
|
||||
history={state.history}
|
||||
onChange={value => {
|
||||
action('onChange fired')(value);
|
||||
updateValue({
|
||||
...state,
|
||||
value,
|
||||
history:
|
||||
isDateTime(value.raw.from) && isDateTime(value.raw.to) ? [...state.history, value] : state.history,
|
||||
});
|
||||
}}
|
||||
onChangeTimeZone={timeZone => {
|
||||
action('onChangeTimeZone fired')(timeZone);
|
||||
updateValue({
|
||||
...state,
|
||||
timeZone,
|
||||
});
|
||||
}}
|
||||
onMoveBackward={() => {
|
||||
action('onMoveBackward fired')();
|
||||
@ -41,8 +56,60 @@ export const basic = () => {
|
||||
action('onZoom fired')();
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
</UseState>
|
||||
<Button
|
||||
onClick={() => {
|
||||
updateValue({
|
||||
...state,
|
||||
history: [],
|
||||
});
|
||||
}}
|
||||
>
|
||||
Clear history
|
||||
</Button>
|
||||
</>
|
||||
);
|
||||
}}
|
||||
</UseState>
|
||||
);
|
||||
|
||||
export const Relative: Story<TimeRangePickerProps> = props => {
|
||||
const to = dateTime();
|
||||
const from = to.subtract(6, 'h');
|
||||
|
||||
return getComponentWithState(
|
||||
{
|
||||
value: {
|
||||
from,
|
||||
to,
|
||||
raw: {
|
||||
from: 'now-6h',
|
||||
to: 'now',
|
||||
},
|
||||
},
|
||||
timeZone: DefaultTimeZone,
|
||||
history: [],
|
||||
},
|
||||
props
|
||||
);
|
||||
};
|
||||
|
||||
export const Absolute: Story<TimeRangePickerProps> = props => {
|
||||
const to = dateTime();
|
||||
const from = to.subtract(6, 'h');
|
||||
|
||||
return getComponentWithState(
|
||||
{
|
||||
value: {
|
||||
from,
|
||||
to,
|
||||
raw: {
|
||||
from,
|
||||
to,
|
||||
},
|
||||
},
|
||||
timeZone: DefaultTimeZone,
|
||||
history: [],
|
||||
},
|
||||
props
|
||||
);
|
||||
};
|
||||
|
@ -1,21 +1,21 @@
|
||||
import React from 'react';
|
||||
import { mount } from 'enzyme';
|
||||
import { UnthemedTimeRangePicker } from './TimeRangePicker';
|
||||
import { dateTime, TimeRange } from '@grafana/data';
|
||||
import { render } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
import dark from '../../themes/dark';
|
||||
import { UnthemedTimeRangePicker } from './TimeRangePicker';
|
||||
|
||||
const from = '2019-12-17T07:48:27.433Z';
|
||||
const to = '2019-12-18T07:48:27.433Z';
|
||||
const from = dateTime('2019-12-17T07:48:27.433Z');
|
||||
const to = dateTime('2019-12-18T07:48:27.433Z');
|
||||
|
||||
const value: TimeRange = {
|
||||
from: dateTime(from),
|
||||
to: dateTime(to),
|
||||
raw: { from: dateTime(from), to: dateTime(to) },
|
||||
from,
|
||||
to,
|
||||
raw: { from, to },
|
||||
};
|
||||
|
||||
describe('TimePicker', () => {
|
||||
it('renders buttons correctly', () => {
|
||||
const wrapper = mount(
|
||||
const container = render(
|
||||
<UnthemedTimeRangePicker
|
||||
onChangeTimeZone={() => {}}
|
||||
onChange={value => {}}
|
||||
@ -26,6 +26,7 @@ describe('TimePicker', () => {
|
||||
theme={dark}
|
||||
/>
|
||||
);
|
||||
expect(wrapper.exists('.navbar-button')).toBe(true);
|
||||
|
||||
expect(container.queryByLabelText(/timepicker open button/i)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
@ -56,7 +56,7 @@ const getLabelStyles = stylesFactory((theme: GrafanaTheme) => {
|
||||
};
|
||||
});
|
||||
|
||||
export interface Props extends Themeable {
|
||||
export interface TimeRangePickerProps extends Themeable {
|
||||
hideText?: boolean;
|
||||
value: TimeRange;
|
||||
timeZone?: TimeZone;
|
||||
@ -68,13 +68,14 @@ export interface Props extends Themeable {
|
||||
onMoveForward: () => void;
|
||||
onZoom: () => void;
|
||||
history?: TimeRange[];
|
||||
hideQuickRanges?: boolean;
|
||||
}
|
||||
|
||||
export interface State {
|
||||
isOpen: boolean;
|
||||
}
|
||||
|
||||
export class UnthemedTimeRangePicker extends PureComponent<Props, State> {
|
||||
export class UnthemedTimeRangePicker extends PureComponent<TimeRangePickerProps, State> {
|
||||
state: State = {
|
||||
isOpen: false,
|
||||
};
|
||||
@ -107,6 +108,7 @@ export class UnthemedTimeRangePicker extends PureComponent<Props, State> {
|
||||
theme,
|
||||
history,
|
||||
onChangeTimeZone,
|
||||
hideQuickRanges,
|
||||
} = this.props;
|
||||
|
||||
const { isOpen } = this.state;
|
||||
@ -146,6 +148,7 @@ export class UnthemedTimeRangePicker extends PureComponent<Props, State> {
|
||||
history={history}
|
||||
showHistory
|
||||
onChangeTimeZone={onChangeTimeZone}
|
||||
hideQuickRanges={hideQuickRanges}
|
||||
/>
|
||||
</ClickOutsideWrapper>
|
||||
)}
|
||||
@ -192,7 +195,7 @@ const TimePickerTooltip = ({ timeRange, timeZone }: { timeRange: TimeRange; time
|
||||
);
|
||||
};
|
||||
|
||||
type LabelProps = Pick<Props, 'hideText' | 'value' | 'timeZone'>;
|
||||
type LabelProps = Pick<TimeRangePickerProps, 'hideText' | 'value' | 'timeZone'>;
|
||||
|
||||
export const TimePickerButtonLabel = memo<LabelProps>(({ hideText, value, timeZone }) => {
|
||||
const theme = useTheme();
|
||||
|
@ -1,59 +1,167 @@
|
||||
import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
import { TimePickerContentWithScreenSize } from './TimePickerContent';
|
||||
import { dateTime, TimeRange } from '@grafana/data';
|
||||
import { render, RenderResult, screen } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
import { PropsWithScreenSize, TimePickerContentWithScreenSize } from './TimePickerContent';
|
||||
|
||||
describe('TimePickerContent', () => {
|
||||
it('renders correctly in full screen', () => {
|
||||
const value = createTimeRange('2019-12-17T07:48:27.433Z', '2019-12-18T07:48:27.433Z');
|
||||
const wrapper = shallow(
|
||||
<TimePickerContentWithScreenSize
|
||||
onChangeTimeZone={() => {}}
|
||||
onChange={value => {}}
|
||||
timeZone="utc"
|
||||
value={value}
|
||||
isFullscreen={true}
|
||||
/>
|
||||
);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
const absoluteValue = createAbsoluteTimeRange('2019-12-17T07:48:27.433Z', '2019-12-18T07:49:27.433Z');
|
||||
const relativeValue = createRelativeTimeRange();
|
||||
const history = [
|
||||
createAbsoluteTimeRange('2019-12-17T07:48:27.433Z', '2019-12-17T07:49:27.433Z'),
|
||||
createAbsoluteTimeRange('2019-10-18T07:50:27.433Z', '2019-10-18T07:51:27.433Z'),
|
||||
];
|
||||
|
||||
describe('Wide Screen', () => {
|
||||
it('renders with history', () => {
|
||||
renderComponent({ value: absoluteValue, history });
|
||||
expect(screen.queryByText(/recently used absolute ranges/i)).toBeInTheDocument();
|
||||
expect(screen.queryByText(/2019-12-17 07:48:27 to 2019-12-17 07:49:27/i)).toBeInTheDocument();
|
||||
expect(screen.queryByText(/2019-10-18 07:50:27 to 2019-10-18 07:51:27/i)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders with empty history', () => {
|
||||
renderComponent({ value: absoluteValue });
|
||||
expect(screen.queryByText(/recently used absolute ranges/i)).not.toBeInTheDocument();
|
||||
expect(
|
||||
screen.queryByText(
|
||||
/it looks like you haven't used this time picker before\. as soon as you enter some time intervals, recently used intervals will appear here\./i
|
||||
)
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders without history', () => {
|
||||
renderComponent({ value: absoluteValue, history, showHistory: false });
|
||||
expect(screen.queryByText(/recently used absolute ranges/i)).not.toBeInTheDocument();
|
||||
expect(screen.queryByText(/2019-12-17 07:48:27 to 2019-12-17 07:49:27/i)).not.toBeInTheDocument();
|
||||
expect(screen.queryByText(/2019-10-18 07:50:27 to 2019-10-18 07:51:27/i)).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders with relative picker', () => {
|
||||
renderComponent({ value: absoluteValue });
|
||||
expect(screen.queryByText(/relative time ranges/i)).toBeInTheDocument();
|
||||
expect(screen.queryByText(/other quick ranges/i)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders without relative picker', () => {
|
||||
renderComponent({ value: absoluteValue, hideQuickRanges: true });
|
||||
expect(screen.queryByText(/relative time ranges/i)).not.toBeInTheDocument();
|
||||
expect(screen.queryByText(/other quick ranges/i)).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders with timezone picker', () => {
|
||||
renderComponent({ value: absoluteValue, hideTimeZone: false });
|
||||
expect(screen.queryByText(/coordinated universal time/i)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders without timezone picker', () => {
|
||||
renderComponent({ value: absoluteValue, hideTimeZone: true });
|
||||
expect(screen.queryByText(/coordinated universal time/i)).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
it('renders correctly in narrow screen', () => {
|
||||
const value = createTimeRange('2019-12-17T07:48:27.433Z', '2019-12-18T07:48:27.433Z');
|
||||
const wrapper = shallow(
|
||||
<TimePickerContentWithScreenSize
|
||||
onChangeTimeZone={() => {}}
|
||||
onChange={value => {}}
|
||||
timeZone="utc"
|
||||
value={value}
|
||||
isFullscreen={false}
|
||||
/>
|
||||
);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
describe('Narrow Screen', () => {
|
||||
it('renders with history', () => {
|
||||
renderComponent({ value: absoluteValue, history, isFullscreen: false });
|
||||
expect(screen.queryByText(/recently used absolute ranges/i)).toBeInTheDocument();
|
||||
expect(screen.queryByText(/2019-12-17 07:48:27 to 2019-12-17 07:49:27/i)).toBeInTheDocument();
|
||||
expect(screen.queryByText(/2019-10-18 07:50:27 to 2019-10-18 07:51:27/i)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders recent absolute ranges correctly', () => {
|
||||
const value = createTimeRange('2019-12-17T07:48:27.433Z', '2019-12-18T07:48:27.433Z');
|
||||
const history = [
|
||||
createTimeRange('2019-12-17T07:48:27.433Z', '2019-12-18T07:48:27.433Z'),
|
||||
createTimeRange('2019-10-17T07:48:27.433Z', '2019-10-18T07:48:27.433Z'),
|
||||
];
|
||||
it('renders with empty history', () => {
|
||||
renderComponent({ value: absoluteValue, isFullscreen: false });
|
||||
expect(screen.queryByText(/recently used absolute ranges/i)).not.toBeInTheDocument();
|
||||
expect(
|
||||
screen.queryByText(
|
||||
/it looks like you haven't used this time picker before\. as soon as you enter some time intervals, recently used intervals will appear here\./i
|
||||
)
|
||||
).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
const wrapper = shallow(
|
||||
<TimePickerContentWithScreenSize
|
||||
onChangeTimeZone={() => {}}
|
||||
onChange={value => {}}
|
||||
timeZone="utc"
|
||||
value={value}
|
||||
isFullscreen={true}
|
||||
history={history}
|
||||
/>
|
||||
);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
it('renders without history', () => {
|
||||
renderComponent({ value: absoluteValue, isFullscreen: false, history, showHistory: false });
|
||||
expect(screen.queryByText(/recently used absolute ranges/i)).not.toBeInTheDocument();
|
||||
expect(screen.queryByText(/2019-12-17 07:48:27 to 2019-12-17 07:49:27/i)).not.toBeInTheDocument();
|
||||
expect(screen.queryByText(/2019-10-18 07:50:27 to 2019-10-18 07:51:27/i)).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders with relative picker', () => {
|
||||
renderComponent({ value: absoluteValue, isFullscreen: false });
|
||||
expect(screen.queryByText(/relative time ranges/i)).toBeInTheDocument();
|
||||
expect(screen.queryByText(/other quick ranges/i)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders without relative picker', () => {
|
||||
renderComponent({ value: absoluteValue, isFullscreen: false, hideQuickRanges: true });
|
||||
expect(screen.queryByText(/relative time ranges/i)).not.toBeInTheDocument();
|
||||
expect(screen.queryByText(/other quick ranges/i)).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders with absolute picker when absolute value and quick ranges are visible', () => {
|
||||
renderComponent({ value: absoluteValue, isFullscreen: false });
|
||||
expect(screen.queryByLabelText(/timepicker from field/i)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders with absolute picker when absolute value and quick ranges are hidden', () => {
|
||||
renderComponent({ value: absoluteValue, isFullscreen: false, hideQuickRanges: true });
|
||||
expect(screen.queryByLabelText(/timepicker from field/i)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders without absolute picker when narrow screen and quick ranges are visible', () => {
|
||||
renderComponent({ value: relativeValue, isFullscreen: false });
|
||||
expect(screen.queryByLabelText(/timepicker from field/i)).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders with absolute picker when narrow screen and quick ranges are hidden', () => {
|
||||
renderComponent({ value: relativeValue, isFullscreen: false, hideQuickRanges: true });
|
||||
expect(screen.queryByLabelText(/timepicker from field/i)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders without timezone picker', () => {
|
||||
renderComponent({ value: absoluteValue, hideTimeZone: true });
|
||||
expect(screen.queryByText(/coordinated universal time/i)).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function createTimeRange(from: string, to: string): TimeRange {
|
||||
function noop(): {} {
|
||||
return {};
|
||||
}
|
||||
|
||||
function renderComponent({
|
||||
value,
|
||||
isFullscreen = true,
|
||||
showHistory = true,
|
||||
history = [],
|
||||
hideQuickRanges = false,
|
||||
hideTimeZone = false,
|
||||
}: Pick<PropsWithScreenSize, 'value'> & Partial<PropsWithScreenSize>): RenderResult {
|
||||
return render(
|
||||
<TimePickerContentWithScreenSize
|
||||
onChangeTimeZone={noop}
|
||||
onChange={noop}
|
||||
timeZone="utc"
|
||||
value={value}
|
||||
isFullscreen={isFullscreen}
|
||||
showHistory={showHistory}
|
||||
history={history}
|
||||
hideQuickRanges={hideQuickRanges}
|
||||
hideTimeZone={hideTimeZone}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function createRelativeTimeRange(): TimeRange {
|
||||
const now = dateTime();
|
||||
const now5m = now.subtract(5, 'm');
|
||||
|
||||
return {
|
||||
from: now5m,
|
||||
to: now,
|
||||
raw: { from: 'now-5m', to: 'now' },
|
||||
};
|
||||
}
|
||||
|
||||
function createAbsoluteTimeRange(from: string, to: string): TimeRange {
|
||||
return {
|
||||
from: dateTime(from),
|
||||
to: dateTime(to),
|
||||
|
@ -11,7 +11,7 @@ import { TimeRangeForm } from './TimeRangeForm';
|
||||
import { TimeRangeList } from './TimeRangeList';
|
||||
import { TimePickerFooter } from './TimePickerFooter';
|
||||
|
||||
const getStyles = stylesFactory((theme: GrafanaTheme, isReversed) => {
|
||||
const getStyles = stylesFactory((theme: GrafanaTheme, isReversed, hideQuickRanges, isContainerTall) => {
|
||||
const containerBorder = theme.isDark ? theme.palette.dark9 : theme.palette.gray5;
|
||||
|
||||
return {
|
||||
@ -19,12 +19,12 @@ const getStyles = stylesFactory((theme: GrafanaTheme, isReversed) => {
|
||||
background: ${theme.colors.bodyBg};
|
||||
box-shadow: 0px 0px 20px ${theme.colors.dropdownShadow};
|
||||
position: absolute;
|
||||
z-index: ${theme.zIndex.modal};
|
||||
z-index: ${theme.zIndex.dropdown};
|
||||
width: 546px;
|
||||
top: 116%;
|
||||
border-radius: 2px;
|
||||
border: 1px solid ${containerBorder};
|
||||
right: ${isReversed ? 'unset' : 0};
|
||||
${isReversed ? 'left' : 'right'}: 0;
|
||||
|
||||
@media only screen and (max-width: ${theme.breakpoints.lg}) {
|
||||
width: 262px;
|
||||
@ -32,19 +32,15 @@ const getStyles = stylesFactory((theme: GrafanaTheme, isReversed) => {
|
||||
`,
|
||||
body: css`
|
||||
display: flex;
|
||||
height: 381px;
|
||||
height: ${isContainerTall ? '381px' : '217px'};
|
||||
`,
|
||||
leftSide: css`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border-right: ${isReversed ? 'none' : `1px solid ${theme.colors.border1}`};
|
||||
width: 60%;
|
||||
width: ${!hideQuickRanges ? '60%' : '100%'};
|
||||
overflow: hidden;
|
||||
order: ${isReversed ? 1 : 0};
|
||||
|
||||
@media only screen and (max-width: ${theme.breakpoints.lg}) {
|
||||
display: none;
|
||||
}
|
||||
`,
|
||||
rightSide: css`
|
||||
width: 40% !important;
|
||||
@ -61,8 +57,6 @@ const getStyles = stylesFactory((theme: GrafanaTheme, isReversed) => {
|
||||
});
|
||||
|
||||
const getNarrowScreenStyles = stylesFactory((theme: GrafanaTheme) => {
|
||||
const formBackground = theme.isDark ? theme.palette.gray15 : theme.palette.gray98;
|
||||
|
||||
return {
|
||||
header: css`
|
||||
display: flex;
|
||||
@ -74,7 +68,6 @@ const getNarrowScreenStyles = stylesFactory((theme: GrafanaTheme) => {
|
||||
`,
|
||||
body: css`
|
||||
border-bottom: 1px solid ${theme.colors.border1};
|
||||
background: ${formBackground};
|
||||
box-shadow: inset 0px 2px 2px ${theme.colors.dropdownShadow};
|
||||
`,
|
||||
form: css`
|
||||
@ -83,12 +76,12 @@ const getNarrowScreenStyles = stylesFactory((theme: GrafanaTheme) => {
|
||||
};
|
||||
});
|
||||
|
||||
const getFullScreenStyles = stylesFactory((theme: GrafanaTheme) => {
|
||||
const getFullScreenStyles = stylesFactory((theme: GrafanaTheme, hideQuickRanges?: boolean) => {
|
||||
return {
|
||||
container: css`
|
||||
padding-top: 9px;
|
||||
padding-left: 11px;
|
||||
padding-right: 20%;
|
||||
padding-right: ${!hideQuickRanges ? '20%' : '11px'};
|
||||
`,
|
||||
title: css`
|
||||
margin-bottom: 11px;
|
||||
@ -135,51 +128,74 @@ interface Props {
|
||||
hideTimeZone?: boolean;
|
||||
/** Reverse the order of relative and absolute range pickers. Used to left align the picker in forms */
|
||||
isReversed?: boolean;
|
||||
hideQuickRanges?: boolean;
|
||||
}
|
||||
|
||||
interface PropsWithScreenSize extends Props {
|
||||
export interface PropsWithScreenSize extends Props {
|
||||
isFullscreen: boolean;
|
||||
}
|
||||
|
||||
interface FormProps extends Omit<Props, 'history'> {
|
||||
visible: boolean;
|
||||
historyOptions?: TimeOption[];
|
||||
}
|
||||
|
||||
export const TimePickerContentWithScreenSize: React.FC<PropsWithScreenSize> = props => {
|
||||
const {
|
||||
quickOptions = [],
|
||||
otherOptions = [],
|
||||
isReversed,
|
||||
isFullscreen,
|
||||
hideQuickRanges,
|
||||
timeZone,
|
||||
value,
|
||||
onChange,
|
||||
history,
|
||||
showHistory,
|
||||
className,
|
||||
hideTimeZone,
|
||||
onChangeTimeZone,
|
||||
} = props;
|
||||
const isHistoryEmpty = !history?.length;
|
||||
const isContainerTall =
|
||||
(isFullscreen && showHistory) || (!isFullscreen && ((showHistory && !isHistoryEmpty) || !hideQuickRanges));
|
||||
const theme = useTheme();
|
||||
const styles = getStyles(theme, props.isReversed);
|
||||
const historyOptions = mapToHistoryOptions(props.history, props.timeZone);
|
||||
const { quickOptions = [], otherOptions = [], isFullscreen } = props;
|
||||
const styles = getStyles(theme, isReversed, hideQuickRanges, isContainerTall);
|
||||
const historyOptions = mapToHistoryOptions(history, timeZone);
|
||||
|
||||
return (
|
||||
<div className={cx(styles.container, props.className)}>
|
||||
<div className={cx(styles.container, className)}>
|
||||
<div className={styles.body}>
|
||||
<div className={styles.leftSide}>
|
||||
<FullScreenForm {...props} visible={isFullscreen} historyOptions={historyOptions} />
|
||||
</div>
|
||||
<CustomScrollbar className={styles.rightSide}>
|
||||
<NarrowScreenForm {...props} visible={!isFullscreen} historyOptions={historyOptions} />
|
||||
<TimeRangeList
|
||||
title="Relative time ranges"
|
||||
options={quickOptions}
|
||||
onSelect={props.onChange}
|
||||
value={props.value}
|
||||
timeZone={props.timeZone}
|
||||
/>
|
||||
<div className={styles.spacing} />
|
||||
<TimeRangeList
|
||||
title="Other quick ranges"
|
||||
options={otherOptions}
|
||||
onSelect={props.onChange}
|
||||
value={props.value}
|
||||
timeZone={props.timeZone}
|
||||
/>
|
||||
</CustomScrollbar>
|
||||
{isFullscreen && (
|
||||
<div className={styles.leftSide}>
|
||||
<FullScreenForm {...props} historyOptions={historyOptions} />
|
||||
</div>
|
||||
)}
|
||||
{(!isFullscreen || !hideQuickRanges) && (
|
||||
<CustomScrollbar className={styles.rightSide}>
|
||||
{!isFullscreen && <NarrowScreenForm {...props} historyOptions={historyOptions} />}
|
||||
{!hideQuickRanges && (
|
||||
<>
|
||||
<TimeRangeList
|
||||
title="Relative time ranges"
|
||||
options={quickOptions}
|
||||
onSelect={onChange}
|
||||
value={value}
|
||||
timeZone={timeZone}
|
||||
/>
|
||||
<div className={styles.spacing} />
|
||||
<TimeRangeList
|
||||
title="Other quick ranges"
|
||||
options={otherOptions}
|
||||
onSelect={onChange}
|
||||
value={value}
|
||||
timeZone={timeZone}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</CustomScrollbar>
|
||||
)}
|
||||
</div>
|
||||
{!props.hideTimeZone && isFullscreen && (
|
||||
<TimePickerFooter timeZone={props.timeZone} onChangeTimeZone={props.onChangeTimeZone} />
|
||||
)}
|
||||
{!hideTimeZone && isFullscreen && <TimePickerFooter timeZone={timeZone} onChangeTimeZone={onChangeTimeZone} />}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@ -192,43 +208,40 @@ export const TimePickerContent: React.FC<Props> = props => {
|
||||
};
|
||||
|
||||
const NarrowScreenForm: React.FC<FormProps> = props => {
|
||||
const { value, hideQuickRanges, onChange, timeZone, historyOptions = [], showHistory } = props;
|
||||
const theme = useTheme();
|
||||
const styles = getNarrowScreenStyles(theme);
|
||||
const isAbsolute = isDateTime(props.value.raw.from) || isDateTime(props.value.raw.to);
|
||||
const [collapsed, setCollapsed] = useState(isAbsolute);
|
||||
|
||||
if (!props.visible) {
|
||||
return null;
|
||||
}
|
||||
const isAbsolute = isDateTime(value.raw.from) || isDateTime(value.raw.to);
|
||||
const [collapsedFlag, setCollapsedFlag] = useState(!isAbsolute);
|
||||
const collapsed = hideQuickRanges ? false : collapsedFlag;
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
aria-label="TimePicker absolute time range"
|
||||
className={styles.header}
|
||||
onClick={() => setCollapsed(!collapsed)}
|
||||
onClick={() => {
|
||||
if (!hideQuickRanges) {
|
||||
setCollapsedFlag(!collapsed);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<TimePickerTitle>Absolute time range</TimePickerTitle>
|
||||
{<Icon name={collapsed ? 'angle-up' : 'angle-down'} />}
|
||||
{!hideQuickRanges && <Icon name={!collapsed ? 'angle-up' : 'angle-down'} />}
|
||||
</div>
|
||||
{collapsed && (
|
||||
{!collapsed && (
|
||||
<div className={styles.body}>
|
||||
<div className={styles.form}>
|
||||
<TimeRangeForm
|
||||
value={props.value}
|
||||
onApply={props.onChange}
|
||||
timeZone={props.timeZone}
|
||||
isFullscreen={false}
|
||||
/>
|
||||
<TimeRangeForm value={value} onApply={onChange} timeZone={timeZone} isFullscreen={false} />
|
||||
</div>
|
||||
{props.showHistory && (
|
||||
{showHistory && (
|
||||
<TimeRangeList
|
||||
title="Recently used absolute ranges"
|
||||
options={props.historyOptions || []}
|
||||
onSelect={props.onChange}
|
||||
value={props.value}
|
||||
options={historyOptions}
|
||||
onSelect={onChange}
|
||||
value={value}
|
||||
placeholderEmpty={null}
|
||||
timeZone={props.timeZone}
|
||||
timeZone={timeZone}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
@ -239,11 +252,7 @@ const NarrowScreenForm: React.FC<FormProps> = props => {
|
||||
|
||||
const FullScreenForm: React.FC<FormProps> = props => {
|
||||
const theme = useTheme();
|
||||
const styles = getFullScreenStyles(theme);
|
||||
|
||||
if (!props.visible) {
|
||||
return null;
|
||||
}
|
||||
const styles = getFullScreenStyles(theme, props.hideQuickRanges);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
@ -32,7 +32,7 @@ interface InputState {
|
||||
const errorMessage = 'Please enter a past date or "now"';
|
||||
|
||||
export const TimeRangeForm: React.FC<Props> = props => {
|
||||
const { value, isFullscreen = false, timeZone, onApply: onApplyFromProps } = props;
|
||||
const { value, isFullscreen = false, timeZone, onApply: onApplyFromProps, isReversed } = props;
|
||||
|
||||
const [from, setFrom] = useState<InputState>(valueToState(value.raw.from, false, timeZone));
|
||||
const [to, setTo] = useState<InputState>(valueToState(value.raw.to, true, timeZone));
|
||||
@ -122,7 +122,7 @@ export const TimeRangeForm: React.FC<Props> = props => {
|
||||
onClose={() => setOpen(false)}
|
||||
onChange={onChange}
|
||||
timeZone={timeZone}
|
||||
isReversed={props.isReversed}
|
||||
isReversed={isReversed}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
@ -1,370 +0,0 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`TimePickerContent renders correctly in full screen 1`] = `
|
||||
<div
|
||||
className="css-ajr8sn"
|
||||
>
|
||||
<div
|
||||
className="css-ooqtr4"
|
||||
>
|
||||
<div
|
||||
className="css-1f2wc71"
|
||||
>
|
||||
<FullScreenForm
|
||||
historyOptions={Array []}
|
||||
isFullscreen={true}
|
||||
onChange={[Function]}
|
||||
onChangeTimeZone={[Function]}
|
||||
timeZone="utc"
|
||||
value={
|
||||
Object {
|
||||
"from": "2019-12-17T07:48:27.433Z",
|
||||
"raw": Object {
|
||||
"from": "2019-12-17T07:48:27.433Z",
|
||||
"to": "2019-12-18T07:48:27.433Z",
|
||||
},
|
||||
"to": "2019-12-18T07:48:27.433Z",
|
||||
}
|
||||
}
|
||||
visible={true}
|
||||
/>
|
||||
</div>
|
||||
<CustomScrollbar
|
||||
autoHeightMax="100%"
|
||||
autoHeightMin="0"
|
||||
autoHide={false}
|
||||
autoHideDuration={200}
|
||||
autoHideTimeout={200}
|
||||
className="css-10t714z"
|
||||
hideTracksWhenNotNeeded={false}
|
||||
setScrollTop={[Function]}
|
||||
>
|
||||
<NarrowScreenForm
|
||||
historyOptions={Array []}
|
||||
isFullscreen={true}
|
||||
onChange={[Function]}
|
||||
onChangeTimeZone={[Function]}
|
||||
timeZone="utc"
|
||||
value={
|
||||
Object {
|
||||
"from": "2019-12-17T07:48:27.433Z",
|
||||
"raw": Object {
|
||||
"from": "2019-12-17T07:48:27.433Z",
|
||||
"to": "2019-12-18T07:48:27.433Z",
|
||||
},
|
||||
"to": "2019-12-18T07:48:27.433Z",
|
||||
}
|
||||
}
|
||||
visible={false}
|
||||
/>
|
||||
<TimeRangeList
|
||||
onSelect={[Function]}
|
||||
options={Array []}
|
||||
timeZone="utc"
|
||||
title="Relative time ranges"
|
||||
value={
|
||||
Object {
|
||||
"from": "2019-12-17T07:48:27.433Z",
|
||||
"raw": Object {
|
||||
"from": "2019-12-17T07:48:27.433Z",
|
||||
"to": "2019-12-18T07:48:27.433Z",
|
||||
},
|
||||
"to": "2019-12-18T07:48:27.433Z",
|
||||
}
|
||||
}
|
||||
/>
|
||||
<div
|
||||
className="css-1ogeuxc"
|
||||
/>
|
||||
<TimeRangeList
|
||||
onSelect={[Function]}
|
||||
options={Array []}
|
||||
timeZone="utc"
|
||||
title="Other quick ranges"
|
||||
value={
|
||||
Object {
|
||||
"from": "2019-12-17T07:48:27.433Z",
|
||||
"raw": Object {
|
||||
"from": "2019-12-17T07:48:27.433Z",
|
||||
"to": "2019-12-18T07:48:27.433Z",
|
||||
},
|
||||
"to": "2019-12-18T07:48:27.433Z",
|
||||
}
|
||||
}
|
||||
/>
|
||||
</CustomScrollbar>
|
||||
</div>
|
||||
<TimePickerFooter
|
||||
onChangeTimeZone={[Function]}
|
||||
timeZone="utc"
|
||||
/>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`TimePickerContent renders correctly in narrow screen 1`] = `
|
||||
<div
|
||||
className="css-ajr8sn"
|
||||
>
|
||||
<div
|
||||
className="css-ooqtr4"
|
||||
>
|
||||
<div
|
||||
className="css-1f2wc71"
|
||||
>
|
||||
<FullScreenForm
|
||||
historyOptions={Array []}
|
||||
isFullscreen={false}
|
||||
onChange={[Function]}
|
||||
onChangeTimeZone={[Function]}
|
||||
timeZone="utc"
|
||||
value={
|
||||
Object {
|
||||
"from": "2019-12-17T07:48:27.433Z",
|
||||
"raw": Object {
|
||||
"from": "2019-12-17T07:48:27.433Z",
|
||||
"to": "2019-12-18T07:48:27.433Z",
|
||||
},
|
||||
"to": "2019-12-18T07:48:27.433Z",
|
||||
}
|
||||
}
|
||||
visible={false}
|
||||
/>
|
||||
</div>
|
||||
<CustomScrollbar
|
||||
autoHeightMax="100%"
|
||||
autoHeightMin="0"
|
||||
autoHide={false}
|
||||
autoHideDuration={200}
|
||||
autoHideTimeout={200}
|
||||
className="css-10t714z"
|
||||
hideTracksWhenNotNeeded={false}
|
||||
setScrollTop={[Function]}
|
||||
>
|
||||
<NarrowScreenForm
|
||||
historyOptions={Array []}
|
||||
isFullscreen={false}
|
||||
onChange={[Function]}
|
||||
onChangeTimeZone={[Function]}
|
||||
timeZone="utc"
|
||||
value={
|
||||
Object {
|
||||
"from": "2019-12-17T07:48:27.433Z",
|
||||
"raw": Object {
|
||||
"from": "2019-12-17T07:48:27.433Z",
|
||||
"to": "2019-12-18T07:48:27.433Z",
|
||||
},
|
||||
"to": "2019-12-18T07:48:27.433Z",
|
||||
}
|
||||
}
|
||||
visible={true}
|
||||
/>
|
||||
<TimeRangeList
|
||||
onSelect={[Function]}
|
||||
options={Array []}
|
||||
timeZone="utc"
|
||||
title="Relative time ranges"
|
||||
value={
|
||||
Object {
|
||||
"from": "2019-12-17T07:48:27.433Z",
|
||||
"raw": Object {
|
||||
"from": "2019-12-17T07:48:27.433Z",
|
||||
"to": "2019-12-18T07:48:27.433Z",
|
||||
},
|
||||
"to": "2019-12-18T07:48:27.433Z",
|
||||
}
|
||||
}
|
||||
/>
|
||||
<div
|
||||
className="css-1ogeuxc"
|
||||
/>
|
||||
<TimeRangeList
|
||||
onSelect={[Function]}
|
||||
options={Array []}
|
||||
timeZone="utc"
|
||||
title="Other quick ranges"
|
||||
value={
|
||||
Object {
|
||||
"from": "2019-12-17T07:48:27.433Z",
|
||||
"raw": Object {
|
||||
"from": "2019-12-17T07:48:27.433Z",
|
||||
"to": "2019-12-18T07:48:27.433Z",
|
||||
},
|
||||
"to": "2019-12-18T07:48:27.433Z",
|
||||
}
|
||||
}
|
||||
/>
|
||||
</CustomScrollbar>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`TimePickerContent renders recent absolute ranges correctly 1`] = `
|
||||
<div
|
||||
className="css-ajr8sn"
|
||||
>
|
||||
<div
|
||||
className="css-ooqtr4"
|
||||
>
|
||||
<div
|
||||
className="css-1f2wc71"
|
||||
>
|
||||
<FullScreenForm
|
||||
history={
|
||||
Array [
|
||||
Object {
|
||||
"from": "2019-12-17T07:48:27.433Z",
|
||||
"raw": Object {
|
||||
"from": "2019-12-17T07:48:27.433Z",
|
||||
"to": "2019-12-18T07:48:27.433Z",
|
||||
},
|
||||
"to": "2019-12-18T07:48:27.433Z",
|
||||
},
|
||||
Object {
|
||||
"from": "2019-10-17T07:48:27.433Z",
|
||||
"raw": Object {
|
||||
"from": "2019-10-17T07:48:27.433Z",
|
||||
"to": "2019-10-18T07:48:27.433Z",
|
||||
},
|
||||
"to": "2019-10-18T07:48:27.433Z",
|
||||
},
|
||||
]
|
||||
}
|
||||
historyOptions={
|
||||
Array [
|
||||
Object {
|
||||
"display": "2019-12-17 07:48:27 to 2019-12-18 07:48:27",
|
||||
"from": "2019-12-17 07:48:27",
|
||||
"section": 3,
|
||||
"to": "2019-12-18 07:48:27",
|
||||
},
|
||||
Object {
|
||||
"display": "2019-10-17 07:48:27 to 2019-10-18 07:48:27",
|
||||
"from": "2019-10-17 07:48:27",
|
||||
"section": 3,
|
||||
"to": "2019-10-18 07:48:27",
|
||||
},
|
||||
]
|
||||
}
|
||||
isFullscreen={true}
|
||||
onChange={[Function]}
|
||||
onChangeTimeZone={[Function]}
|
||||
timeZone="utc"
|
||||
value={
|
||||
Object {
|
||||
"from": "2019-12-17T07:48:27.433Z",
|
||||
"raw": Object {
|
||||
"from": "2019-12-17T07:48:27.433Z",
|
||||
"to": "2019-12-18T07:48:27.433Z",
|
||||
},
|
||||
"to": "2019-12-18T07:48:27.433Z",
|
||||
}
|
||||
}
|
||||
visible={true}
|
||||
/>
|
||||
</div>
|
||||
<CustomScrollbar
|
||||
autoHeightMax="100%"
|
||||
autoHeightMin="0"
|
||||
autoHide={false}
|
||||
autoHideDuration={200}
|
||||
autoHideTimeout={200}
|
||||
className="css-10t714z"
|
||||
hideTracksWhenNotNeeded={false}
|
||||
setScrollTop={[Function]}
|
||||
>
|
||||
<NarrowScreenForm
|
||||
history={
|
||||
Array [
|
||||
Object {
|
||||
"from": "2019-12-17T07:48:27.433Z",
|
||||
"raw": Object {
|
||||
"from": "2019-12-17T07:48:27.433Z",
|
||||
"to": "2019-12-18T07:48:27.433Z",
|
||||
},
|
||||
"to": "2019-12-18T07:48:27.433Z",
|
||||
},
|
||||
Object {
|
||||
"from": "2019-10-17T07:48:27.433Z",
|
||||
"raw": Object {
|
||||
"from": "2019-10-17T07:48:27.433Z",
|
||||
"to": "2019-10-18T07:48:27.433Z",
|
||||
},
|
||||
"to": "2019-10-18T07:48:27.433Z",
|
||||
},
|
||||
]
|
||||
}
|
||||
historyOptions={
|
||||
Array [
|
||||
Object {
|
||||
"display": "2019-12-17 07:48:27 to 2019-12-18 07:48:27",
|
||||
"from": "2019-12-17 07:48:27",
|
||||
"section": 3,
|
||||
"to": "2019-12-18 07:48:27",
|
||||
},
|
||||
Object {
|
||||
"display": "2019-10-17 07:48:27 to 2019-10-18 07:48:27",
|
||||
"from": "2019-10-17 07:48:27",
|
||||
"section": 3,
|
||||
"to": "2019-10-18 07:48:27",
|
||||
},
|
||||
]
|
||||
}
|
||||
isFullscreen={true}
|
||||
onChange={[Function]}
|
||||
onChangeTimeZone={[Function]}
|
||||
timeZone="utc"
|
||||
value={
|
||||
Object {
|
||||
"from": "2019-12-17T07:48:27.433Z",
|
||||
"raw": Object {
|
||||
"from": "2019-12-17T07:48:27.433Z",
|
||||
"to": "2019-12-18T07:48:27.433Z",
|
||||
},
|
||||
"to": "2019-12-18T07:48:27.433Z",
|
||||
}
|
||||
}
|
||||
visible={false}
|
||||
/>
|
||||
<TimeRangeList
|
||||
onSelect={[Function]}
|
||||
options={Array []}
|
||||
timeZone="utc"
|
||||
title="Relative time ranges"
|
||||
value={
|
||||
Object {
|
||||
"from": "2019-12-17T07:48:27.433Z",
|
||||
"raw": Object {
|
||||
"from": "2019-12-17T07:48:27.433Z",
|
||||
"to": "2019-12-18T07:48:27.433Z",
|
||||
},
|
||||
"to": "2019-12-18T07:48:27.433Z",
|
||||
}
|
||||
}
|
||||
/>
|
||||
<div
|
||||
className="css-1ogeuxc"
|
||||
/>
|
||||
<TimeRangeList
|
||||
onSelect={[Function]}
|
||||
options={Array []}
|
||||
timeZone="utc"
|
||||
title="Other quick ranges"
|
||||
value={
|
||||
Object {
|
||||
"from": "2019-12-17T07:48:27.433Z",
|
||||
"raw": Object {
|
||||
"from": "2019-12-17T07:48:27.433Z",
|
||||
"to": "2019-12-18T07:48:27.433Z",
|
||||
},
|
||||
"to": "2019-12-18T07:48:27.433Z",
|
||||
}
|
||||
}
|
||||
/>
|
||||
</CustomScrollbar>
|
||||
</div>
|
||||
<TimePickerFooter
|
||||
onChangeTimeZone={[Function]}
|
||||
timeZone="utc"
|
||||
/>
|
||||
</div>
|
||||
`;
|
@ -1,11 +1,11 @@
|
||||
import React from 'react';
|
||||
import { LocalStorageValueProvider } from '../LocalStorageValueProvider';
|
||||
import { TimeRange, isDateTime, toUtc } from '@grafana/data';
|
||||
import { Props as TimePickerProps, TimeRangePicker } from '@grafana/ui/src/components/TimePicker/TimeRangePicker';
|
||||
import { TimeRangePickerProps, TimeRangePicker } from '@grafana/ui/src/components/TimePicker/TimeRangePicker';
|
||||
|
||||
const LOCAL_STORAGE_KEY = 'grafana.dashboard.timepicker.history';
|
||||
|
||||
interface Props extends Omit<TimePickerProps, 'history' | 'theme'> {}
|
||||
interface Props extends Omit<TimeRangePickerProps, 'history' | 'theme'> {}
|
||||
|
||||
export const TimePickerWithHistory: React.FC<Props> = props => {
|
||||
return (
|
||||
|
23
yarn.lock
23
yarn.lock
@ -5731,6 +5731,20 @@
|
||||
lodash "^4.17.15"
|
||||
redent "^3.0.0"
|
||||
|
||||
"@testing-library/jest-dom@5.11.9":
|
||||
version "5.11.9"
|
||||
resolved "https://registry.yarnpkg.com/@testing-library/jest-dom/-/jest-dom-5.11.9.tgz#e6b3cd687021f89f261bd53cbe367041fbd3e975"
|
||||
integrity sha512-Mn2gnA9d1wStlAIT2NU8J15LNob0YFBVjs2aEQ3j8rsfRQo+lAs7/ui1i2TGaJjapLmuNPLTsrm+nPjmZDwpcQ==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.9.2"
|
||||
"@types/testing-library__jest-dom" "^5.9.1"
|
||||
aria-query "^4.2.2"
|
||||
chalk "^3.0.0"
|
||||
css "^3.0.0"
|
||||
css.escape "^1.5.1"
|
||||
lodash "^4.17.15"
|
||||
redent "^3.0.0"
|
||||
|
||||
"@testing-library/react-hooks@^3.2.1":
|
||||
version "3.2.1"
|
||||
resolved "https://registry.yarnpkg.com/@testing-library/react-hooks/-/react-hooks-3.2.1.tgz#19b6caa048ef15faa69d439c469033873ea01294"
|
||||
@ -22933,15 +22947,6 @@ rollup@^0.25.8:
|
||||
minimist "^1.2.0"
|
||||
source-map-support "^0.3.2"
|
||||
|
||||
rollup@^0.25.8:
|
||||
version "0.25.8"
|
||||
resolved "https://registry.yarnpkg.com/rollup/-/rollup-0.25.8.tgz#bf6ce83b87510d163446eeaa577ed6a6fc5835e0"
|
||||
integrity sha1-v2zoO4dRDRY0Ru6qV37WpvxYNeA=
|
||||
dependencies:
|
||||
chalk "^1.1.1"
|
||||
minimist "^1.2.0"
|
||||
source-map-support "^0.3.2"
|
||||
|
||||
rollup@^0.63.4:
|
||||
version "0.63.5"
|
||||
resolved "https://registry.yarnpkg.com/rollup/-/rollup-0.63.5.tgz#5543eecac9a1b83b7e1be598b5be84c9c0a089db"
|
||||
|
Loading…
Reference in New Issue
Block a user