Skip to content

Commit b1e20e2

Browse files
sookmaxsnowystingerreidbarber
authored
[RAC] Add shouldCloseOnSelect prop to DatePicker (type & docs only) (#5316)
* add shouldCloseOnSelect prop to DatePicker (type & docs only) * add tests for prop we support * add to DateRangePickerProps * add test for DateRangePicker * fix test --------- Co-authored-by: Robert Snow <rsnow@adobe.com> Co-authored-by: Reid Barber <reid@reidbarber.com>
1 parent a23fd10 commit b1e20e2

File tree

4 files changed

+119
-3
lines changed

4 files changed

+119
-3
lines changed

packages/dev/parcel-packager-docs/DocsPackager.js

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,16 @@ module.exports = new Packager({
139139
return omit(application[0], application[1], nodes);
140140
}
141141

142+
if (t && t.type === 'identifier' && t.name === 'Pick' && application) {
143+
if (application[0]?.type === 'application') {
144+
// this condition was necessary to prevent an unwanted side-effect for other types.
145+
// For example, if you go to http://localhost:1234/react-aria/useTextField.html#anatomy
146+
// and click the link 'TextFieldIntrinsicElements', the popover won't show 'Pick' anymore if it is processed by `pick()` function here.
147+
// i.e., `keyof Pick<IntrinsicHTMLElements, 'input' | 'textarea'>` becomes `keyof <IntrinsicHTMLElements, 'input' | 'textarea'>`
148+
return pick(application[0], application[1], nodes);
149+
}
150+
}
151+
142152
if (t && t.type === 'identifier' && params && params[t.name]) {
143153
return params[t.name];
144154
}
@@ -522,6 +532,42 @@ function omit(obj, toOmit, nodes) {
522532
return obj;
523533
}
524534

535+
// Exactly the same as `omit()` above except for `keys.has(key)` instead of `!keys.has(key)`.
536+
function pick(obj, toPick, nodes) {
537+
obj = resolveValue(obj, nodes);
538+
539+
if (obj.type === 'interface' || obj.type === 'object') {
540+
let keys = new Set();
541+
if (toPick.type === 'string' && toPick.value) {
542+
keys.add(toPick.value);
543+
} else if (toPick.type === 'union') {
544+
for (let e of toPick.elements) {
545+
if (e.type === 'string' && e.value) {
546+
keys.add(e.value);
547+
}
548+
}
549+
}
550+
551+
if (keys.size === 0) {
552+
return obj;
553+
}
554+
555+
let properties = {};
556+
for (let key in obj.properties) {
557+
if (keys.has(key)) {
558+
properties[key] = obj.properties[key];
559+
}
560+
}
561+
562+
return {
563+
...obj,
564+
properties
565+
};
566+
}
567+
568+
return obj;
569+
}
570+
525571
function resolveValue(obj, nodes) {
526572
if (obj.type === 'link') {
527573
return resolveValue(nodes[obj.id], nodes);

packages/react-aria-components/src/DatePicker.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import {ButtonContext} from './Button';
1414
import {CalendarContext, RangeCalendarContext} from './Calendar';
1515
import {ContextValue, forwardRefType, Provider, RACValidation, removeDataAttributes, RenderProps, SlotProps, useContextProps, useRenderProps, useSlot} from './utils';
1616
import {DateFieldContext} from './DateField';
17-
import {DatePickerState, DateRangePickerState, useDatePickerState, useDateRangePickerState} from 'react-stately';
17+
import {DatePickerState, DatePickerStateOptions, DateRangePickerState, DateRangePickerStateOptions, useDatePickerState, useDateRangePickerState} from 'react-stately';
1818
import {DialogContext, OverlayTriggerStateContext} from './Dialog';
1919
import {FieldErrorContext} from './FieldError';
2020
import {filterDOMProps} from '@react-aria/utils';
@@ -62,8 +62,8 @@ export interface DateRangePickerRenderProps extends Omit<DatePickerRenderProps,
6262
state: DateRangePickerState
6363
}
6464

65-
export interface DatePickerProps<T extends DateValue> extends Omit<AriaDatePickerProps<T>, 'label' | 'description' | 'errorMessage' | 'validationState' | 'validationBehavior'>, RACValidation, RenderProps<DatePickerRenderProps>, SlotProps {}
66-
export interface DateRangePickerProps<T extends DateValue> extends Omit<AriaDateRangePickerProps<T>, 'label' | 'description' | 'errorMessage' | 'validationState' | 'validationBehavior'>, RACValidation, RenderProps<DateRangePickerRenderProps>, SlotProps {}
65+
export interface DatePickerProps<T extends DateValue> extends Omit<AriaDatePickerProps<T>, 'label' | 'description' | 'errorMessage' | 'validationState'>, Pick<DatePickerStateOptions<T>, 'shouldCloseOnSelect'>, RACValidation, RenderProps<DatePickerRenderProps>, SlotProps {}
66+
export interface DateRangePickerProps<T extends DateValue> extends Omit<AriaDateRangePickerProps<T>, 'label' | 'description' | 'errorMessage' | 'validationState' | 'validationBehavior'>, Pick<DateRangePickerStateOptions<T>, 'shouldCloseOnSelect'>, RACValidation, RenderProps<DateRangePickerRenderProps>, SlotProps {}
6767

6868
export const DatePickerContext = createContext<ContextValue<DatePickerProps<any>, HTMLDivElement>>(null);
6969
export const DateRangePickerContext = createContext<ContextValue<DateRangePickerProps<any>, HTMLDivElement>>(null);

packages/react-aria-components/test/DatePicker.test.js

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,4 +221,38 @@ describe('DatePicker', () => {
221221
expect(getDescription()).not.toContain('Constraints not satisfied');
222222
expect(datepicker).not.toHaveAttribute('data-invalid');
223223
});
224+
225+
it('should support close on select = true', async () => {
226+
let {getByRole, getAllByRole} = render(<TestDatePicker value={new CalendarDate(2019, 2, 3)} />);
227+
228+
let button = getByRole('button');
229+
230+
await user.click(button);
231+
232+
let dialog = getByRole('dialog');
233+
234+
let cells = getAllByRole('gridcell');
235+
let selected = cells.find(cell => cell.getAttribute('aria-selected') === 'true');
236+
expect(selected.children[0]).toHaveAttribute('aria-label', 'Sunday, February 3, 2019 selected');
237+
238+
await user.click(selected.nextSibling.children[0]);
239+
expect(dialog).not.toBeInTheDocument();
240+
});
241+
242+
it('should support close on select = false', async () => {
243+
let {getByRole, getAllByRole} = render(<TestDatePicker value={new CalendarDate(2019, 2, 3)} shouldCloseOnSelect={false} />);
244+
245+
let button = getByRole('button');
246+
247+
await user.click(button);
248+
249+
let dialog = getByRole('dialog');
250+
251+
let cells = getAllByRole('gridcell');
252+
let selected = cells.find(cell => cell.getAttribute('aria-selected') === 'true');
253+
expect(selected.children[0]).toHaveAttribute('aria-label', 'Sunday, February 3, 2019 selected');
254+
255+
await user.click(selected.nextSibling.children[0]);
256+
expect(dialog).toBeInTheDocument();
257+
});
224258
});

packages/react-aria-components/test/DateRangePicker.test.js

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,4 +242,40 @@ describe('DateRangePicker', () => {
242242
expect(getDescription()).not.toContain('Constraints not satisfied');
243243
expect(datepicker).not.toHaveAttribute('data-invalid');
244244
});
245+
246+
it('should support close on select = true', async () => {
247+
let {getByRole, getAllByRole} = render(<TestDateRangePicker value={{start: new CalendarDate(2023, 1, 10), end: new CalendarDate(2023, 1, 20)}} />);
248+
249+
let button = getByRole('button');
250+
251+
await user.click(button);
252+
253+
let dialog = getByRole('dialog');
254+
255+
let cells = getAllByRole('gridcell');
256+
let selected = cells.find(cell => cell.getAttribute('aria-selected') === 'true');
257+
expect(selected.children[0]).toHaveAttribute('aria-label', 'Selected Range: Tuesday, January 10 to Friday, January 20, 2023, Tuesday, January 10, 2023 selected');
258+
259+
await user.click(selected.nextSibling.children[0]);
260+
await user.click(selected.nextSibling.children[1]);
261+
expect(dialog).not.toBeInTheDocument();
262+
});
263+
264+
it('should support close on select = false', async () => {
265+
let {getByRole, getAllByRole} = render(<TestDateRangePicker value={{start: new CalendarDate(2023, 1, 10), end: new CalendarDate(2023, 1, 20)}} shouldCloseOnSelect={false} />);
266+
267+
let button = getByRole('button');
268+
269+
await user.click(button);
270+
271+
let dialog = getByRole('dialog');
272+
273+
let cells = getAllByRole('gridcell');
274+
let selected = cells.find(cell => cell.getAttribute('aria-selected') === 'true');
275+
expect(selected.children[0]).toHaveAttribute('aria-label', 'Selected Range: Tuesday, January 10 to Friday, January 20, 2023, Tuesday, January 10, 2023 selected');
276+
277+
await user.click(selected.nextSibling.children[0]);
278+
await user.click(selected.nextSibling.children[1]);
279+
expect(dialog).toBeInTheDocument();
280+
});
245281
});

0 commit comments

Comments
 (0)