Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[@mantine/dates] Add range type to DateTimePicker #5081

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 6 additions & 0 deletions docs/src/pages/dates/date-time-picker.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@ read through [DatePicker](/dates/date-picker/) documentation to learn about all

<Demo data={DateTimePickerDemos.withSeconds} />

## Dates range

Set `type="range"` to allow user to pick dates range:

<Demo data={DateTimePickerDemos.range} />

## Value format

Use `valueFormat` prop to change [dayjs format](https://day.js.org/docs/en/display/format) of value label:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import React from 'react';
import { DateTimePicker } from '@mantine/dates';
import { MantineDemo } from '@mantinex/demo';

const code = `
import { DateTimePicker } from '@mantine/dates';

function Demo() {
return <DateTimePicker
label="Pick date and time"
placeholder="Pick date and time"
type="range"
/>;
}
`;

function Demo() {
return (
<DateTimePicker label="Pick date and time" placeholder="Pick date and time" type="range" />
);
}

export const range: MantineDemo = {
type: 'code',
centered: true,
maxWidth: 400,
component: Demo,
code,
};
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ export const DemoModal = {
render: renderDemo(demos.modal),
};

export const DemoRange = {
name: '⭐ Demo: range',
render: renderDemo(demos.range),
};

export const DemoConfigurator = {
name: '⭐ Demo: configurator',
render: renderDemo(demos.configurator),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export { usage } from './DateTimePicker.demo.usage';
export { withSeconds } from './DateTimePicker.demo.withSeconds';
export { modal } from './DateTimePicker.demo.modal';
export { range } from './DateTimePicker.demo.range';
export { configurator } from './DateTimePicker.demo.configurator';
export { clearable } from './DateTimePicker.demo.clearable';
export { format } from './DateTimePicker.demo.format';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ export interface DatePickerBaseProps<Type extends DatePickerType = 'default'>
/** Current level displayed to the user (decade, year, month), used for controlled component */
level?: CalendarLevel;

/** Allow same day for range selects */
allowSameDay?: boolean;

/** Called when level changes */
onLevelChange?: (level: CalendarLevel) => void;
}
Expand Down Expand Up @@ -67,6 +70,7 @@ export const DatePicker: DatePickerComponent = factory<DatePickerFactory>((_prop
const {
classNames,
styles,
allowSameDay,
vars,
type,
defaultValue,
Expand All @@ -88,6 +92,7 @@ export const DatePicker: DatePickerComponent = factory<DatePickerFactory>((_prop
const { onDateChange, onRootMouseLeave, onHoveredDateChange, getControlProps } = useDatesState({
type: type as any,
level: 'day',
allowSameDay,
allowDeselect,
allowSingleDateInRange,
value,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,14 @@ export function WithSeconds() {
);
}

export function Range() {
return (
<div style={{ padding: 40, maxWidth: 400 }}>
<DateTimePicker placeholder="Date time picker" withSeconds type="range" />
</div>
);
}

export function MinDate() {
return (
<div style={{ padding: 40, maxWidth: 400 }}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import {
import { DatesProvider } from '../DatesProvider';
import { DateTimePicker, DateTimePickerProps } from './DateTimePicker';

const defaultProps: DateTimePickerProps = {
const defaultProps = {
popoverProps: { withinPortal: false, transitionProps: { duration: 0 } },
modalProps: { withinPortal: false, transitionProps: { duration: 0 } },
timeInputProps: { 'aria-label': 'test-time-input' },
Expand All @@ -44,6 +44,7 @@ const defaultPropsWithInputProps: DateTimePickerProps = {
};

const getTimeInput = () => screen.getByLabelText('test-time-input');
const getTimeInputs = () => screen.getAllByLabelText('test-time-input');
const getSubmitButton = () => screen.getByLabelText('test-submit');
const getClearButton = () => screen.queryAllByLabelText('test-clear')[0];

Expand Down Expand Up @@ -182,6 +183,32 @@ describe('@mantine/dates/DateTimePicker', () => {
expectValue(container, '03/04/2022 14:45');
});

it('supports uncontrolled state with range', async () => {
const { container } = render(
<DateTimePicker
{...defaultProps}
defaultValue={[new Date(2022, 3, 11), new Date(2022, 3, 12)]}
type="range"
/>
);
expectValue(container, '11/04/2022 00:00 – 12/04/2022 00:00');

await clickInput(container);
await userEvent.click(container.querySelectorAll('table button')[6]);
expectValue(container, '03/04/2022 00:00 – ');

await userEvent.click(container.querySelectorAll('table button')[7]);
expectValue(container, '03/04/2022 00:00 – 04/04/2022 00:00');

await userEvent.clear(getTimeInputs()[0]);
await userEvent.type(getTimeInputs()[0], '14:45');
expectValue(container, '03/04/2022 14:45 – 04/04/2022 00:00');

await userEvent.clear(getTimeInputs()[1]);
await userEvent.type(getTimeInputs()[1], '12:35');
expectValue(container, '03/04/2022 14:45 – 04/04/2022 12:35');
});

it('supports uncontrolled state with timezone', async () => {
const { container } = render(
<DatesProvider settings={{ timezone: 'UTC' }}>
Expand Down Expand Up @@ -212,6 +239,24 @@ describe('@mantine/dates/DateTimePicker', () => {
expect(spy).toHaveBeenLastCalledWith(new Date(2022, 3, 3));
});

it('supports controlled state with range', async () => {
const spy = jest.fn();
const { container } = render(
<DateTimePicker
{...defaultProps}
value={[new Date(2022, 3, 11), new Date(2022, 3, 12)]}
onChange={spy}
type="range"
/>
);
expectValue(container, '11/04/2022 00:00 – 12/04/2022 00:00');

await clickInput(container);
await userEvent.click(container.querySelectorAll('table button')[6]);
expectValue(container, '11/04/2022 00:00 – 12/04/2022 00:00');
expect(spy).toHaveBeenLastCalledWith([new Date(2022, 3, 3), null]);
});

it('supports controlled state with timezone', async () => {
const spy = jest.fn();

Expand Down