Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion src/PickerInput/Popup/PopupPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export type PopupPanelProps<DateType extends object = any> = MustProp<DateType>
FooterProps<DateType> & {
multiplePanel?: boolean;
range?: boolean;
confirmOnDoubleClick: boolean;

onPickerValueChange: (date: DateType) => void;
};
Expand All @@ -27,6 +28,7 @@ export default function PopupPanel<DateType extends object = any>(
pickerValue,
onPickerValueChange,
needConfirm,
confirmOnDoubleClick,
onSubmit,
range,
hoverValue,
Expand Down Expand Up @@ -54,7 +56,7 @@ export default function PopupPanel<DateType extends object = any>(
// ======================= Context ========================
const sharedContext: PickerHackContextProps = {
onCellDblClick: () => {
if (needConfirm) {
if (needConfirm && confirmOnDoubleClick) {
onSubmit();
}
},
Expand Down
11 changes: 8 additions & 3 deletions src/PickerInput/RangePicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,10 @@ export type RangeValueType<DateType> = [
/** Used for change event, it should always be not undefined */
export type NoUndefinedRangeValueType<DateType> = [start: DateType | null, end: DateType | null];

export interface BaseRangePickerProps<DateType extends object>
extends Omit<SharedPickerProps<DateType>, 'showTime' | 'id'> {
export interface BaseRangePickerProps<DateType extends object> extends Omit<
SharedPickerProps<DateType>,
'showTime' | 'id'
> {
// Structure
id?: SelectorIdType;

Expand Down Expand Up @@ -132,7 +134,8 @@ export interface BaseRangePickerProps<DateType extends object>
}

export interface RangePickerProps<DateType extends object>
extends BaseRangePickerProps<DateType>,
extends
BaseRangePickerProps<DateType>,
Omit<RangeTimeProps<DateType>, 'format' | 'defaultValue' | 'defaultOpenValue'> {}

function getActiveRange(activeIndex: number) {
Expand Down Expand Up @@ -169,6 +172,7 @@ function RangePicker<DateType extends object = any>(
defaultValue,
value,
needConfirm,
confirmOnDoubleClick,
onKeyDown,

// Disabled
Expand Down Expand Up @@ -617,6 +621,7 @@ function RangePicker<DateType extends object = any>(
onHover={onPanelHover}
// Submit
needConfirm={needConfirm}
confirmOnDoubleClick={confirmOnDoubleClick}
onSubmit={triggerPartConfirm}
onOk={triggerOk}
// Preset
Expand Down
10 changes: 6 additions & 4 deletions src/PickerInput/SinglePicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,9 @@ import useSemantic from '../hooks/useSemantic';

// TODO: isInvalidateDate with showTime.disabledTime should not provide `range` prop

export interface BasePickerProps<DateType extends object = any>
extends SharedPickerProps<DateType> {
export interface BasePickerProps<
DateType extends object = any,
> extends SharedPickerProps<DateType> {
// Structure
id?: string;

Expand Down Expand Up @@ -100,8 +101,7 @@ export interface BasePickerProps<DateType extends object = any>
}

export interface PickerProps<DateType extends object = any>
extends BasePickerProps<DateType>,
Omit<SharedTimeProps<DateType>, 'format' | 'defaultValue'> {}
extends BasePickerProps<DateType>, Omit<SharedTimeProps<DateType>, 'format' | 'defaultValue'> {}

/** Internal usage. For cross function get same aligned props */
export type ReplacedPickerProps<DateType extends object = any> = {
Expand Down Expand Up @@ -135,6 +135,7 @@ function Picker<DateType extends object = any>(
defaultValue,
value,
needConfirm,
confirmOnDoubleClick,
onChange,
onKeyDown,

Expand Down Expand Up @@ -531,6 +532,7 @@ function Picker<DateType extends object = any>(
onHover={onPanelHover}
// Submit
needConfirm={needConfirm}
confirmOnDoubleClick={confirmOnDoubleClick}
onSubmit={triggerConfirm}
onOk={triggerOk}
// Preset
Expand Down
55 changes: 37 additions & 18 deletions src/PickerInput/hooks/useFilledProps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,22 @@ type GetGeneric<T> = T extends PickedProps<infer U> ? U : never;

type ToArrayType<T, DateType> = T extends any[] ? T : DateType[];

type FilledProps<
InProps extends PickedProps,
DateType extends GetGeneric<InProps>,
UpdaterProps extends object,
> = Omit<InProps, keyof UpdaterProps | 'showTime' | 'value' | 'defaultValue' | 'needConfirm'> &
UpdaterProps & {
picker: PickerMode;
showTime?: ExcludeBooleanType<InProps['showTime']>;
needConfirm: boolean;
confirmOnDoubleClick: boolean;
value?: ToArrayType<InProps['value'], DateType>;
defaultValue?: ToArrayType<InProps['value'], DateType>;
pickerValue?: ToArrayType<InProps['value'], DateType>;
defaultPickerValue?: ToArrayType<InProps['value'], DateType>;
};

function useList<T>(value: T | T[], fillMode = false) {
const values = React.useMemo(() => {
const list = value ? toArray(value) : value;
Expand Down Expand Up @@ -77,15 +93,7 @@ export default function useFilledProps<
props: InProps,
updater?: () => UpdaterProps,
): [
filledProps: Omit<InProps, keyof UpdaterProps | 'showTime' | 'value' | 'defaultValue'> &
UpdaterProps & {
picker: PickerMode;
showTime?: ExcludeBooleanType<InProps['showTime']>;
value?: ToArrayType<InProps['value'], DateType>;
defaultValue?: ToArrayType<InProps['value'], DateType>;
pickerValue?: ToArrayType<InProps['value'], DateType>;
defaultPickerValue?: ToArrayType<InProps['value'], DateType>;
},
filledProps: FilledProps<InProps, DateType, UpdaterProps>,
internalPicker: InternalMode,
complexPicker: boolean,
formatList: FormatType<DateType>[],
Expand Down Expand Up @@ -132,7 +140,10 @@ export default function useFilledProps<
/** The picker is `datetime` or `time` */
const multipleInteractivePicker = internalPicker === 'time' || internalPicker === 'datetime';
const complexPicker = multipleInteractivePicker || multiple;
const mergedNeedConfirm = needConfirm ?? multipleInteractivePicker;
const needConfirmConfig =
typeof needConfirm === 'object' && needConfirm !== null ? needConfirm : null;
const mergedNeedConfirm = needConfirmConfig ? true : (needConfirm ?? multipleInteractivePicker);
const mergedConfirmOnDoubleClick = needConfirmConfig?.confirmOnDoubleClick ?? true;

// ========================== Time ==========================
// Auto `format` need to check `showTime.showXXX` first.
Expand Down Expand Up @@ -206,14 +217,22 @@ export default function useFilledProps<
);

// ======================== Merged ========================
const mergedProps = React.useMemo(
() => ({
...filledProps,
needConfirm: mergedNeedConfirm,
inputReadOnly: mergedInputReadOnly,
disabledDate: disabledBoundaryDate,
}),
[filledProps, mergedNeedConfirm, mergedInputReadOnly, disabledBoundaryDate],
const mergedProps = React.useMemo<FilledProps<InProps, DateType, UpdaterProps>>(
() =>
({
...filledProps,
needConfirm: mergedNeedConfirm,
confirmOnDoubleClick: mergedConfirmOnDoubleClick,
inputReadOnly: mergedInputReadOnly,
disabledDate: disabledBoundaryDate,
}) as FilledProps<InProps, DateType, UpdaterProps>,
[
filledProps,
mergedNeedConfirm,
mergedConfirmOnDoubleClick,
mergedInputReadOnly,
disabledBoundaryDate,
],
);

return [mergedProps, internalPicker, complexPicker, formatList, maskFormat, isInvalidateDate];
Expand Down
12 changes: 10 additions & 2 deletions src/interface.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,12 @@ export type SemanticName = 'root' | 'prefix' | 'input' | 'suffix';

export type PreviewValueType = 'hover';

export type NeedConfirmConfig =
| boolean
| {
confirmOnDoubleClick?: boolean;
};

export type PanelSemanticName =
| 'root'
| 'header'
Expand All @@ -325,7 +331,8 @@ export type PanelSemanticName =
| 'container';

export interface SharedPickerProps<DateType extends object = any>
extends SharedHTMLAttrs,
extends
SharedHTMLAttrs,
Pick<
SharedPanelProps<DateType>,
// Icon
Expand Down Expand Up @@ -418,8 +425,9 @@ export interface SharedPickerProps<DateType extends object = any>
* By default. Only `time` or `datetime` show the confirm button in panel.
* `true` to make every picker need confirm.
* `false` to trigger change on every time panel closed by the mode = picker.
* Config mode can customize how explicit confirm behaves.
*/
needConfirm?: boolean;
needConfirm?: NeedConfirmConfig;

/**
* @deprecated. This is removed and not work anymore.
Expand Down
31 changes: 30 additions & 1 deletion tests/new-range.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -753,7 +753,9 @@ describe('NewPicker.Range', () => {
it('double click to confirm if needConfirm', () => {
const onChange = jest.fn();

const { container } = render(<DayRangePicker showTime onChange={onChange} />);
const { container } = render(
<DayRangePicker showTime needConfirm={{}} onChange={onChange} />,
);
openPicker(container);

fireEvent.click(findCell(5));
Expand All @@ -773,6 +775,33 @@ describe('NewPicker.Range', () => {
]);
});

it('double click should not confirm if confirmOnDoubleClick is false', () => {
const onChange = jest.fn();

const { container } = render(
<DayRangePicker
picker="time"
needConfirm={{ confirmOnDoubleClick: false }}
onChange={onChange}
/>,
);

openPicker(container);

const li = document.querySelector('.rc-picker-time-panel-column').querySelectorAll('li')[11];
fireEvent.click(li);
fireEvent.doubleClick(li);

act(() => {
jest.runAllTimers();
});

expect(container.querySelectorAll('input')[0]).toHaveValue('11:00:00');
expect(container.querySelectorAll('input')[1]).toHaveValue('');
expect(onChange).not.toHaveBeenCalled();
expect(isOpen()).toBeTruthy();
});

it('double click should not take action if !needConfirm', () => {
const { container } = render(<DayRangePicker />);

Expand Down
18 changes: 18 additions & 0 deletions tests/picker.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -520,6 +520,24 @@ describe('Picker.Basic', () => {
expect(isSame(onOk.mock.calls[0][0], '1990-09-03 13:22:33', 'second')).toBeTruthy();
expect(isSame(onChange.mock.calls[0][0], '1990-09-03 13:22:33', 'second')).toBeTruthy();
});

it('should not submit on double click when confirmOnDoubleClick is false', () => {
const onChange = jest.fn();
const { container } = render(
<DayPicker needConfirm={{ confirmOnDoubleClick: false }} onChange={onChange} />,
);

openPicker(container);
fireEvent.click(findCell(5));
fireEvent.doubleClick(findCell(5));

act(() => {
jest.runAllTimers();
});

expect(onChange).not.toHaveBeenCalled();
expect(isOpen()).toBeTruthy();
});
});

it('renderExtraFooter', () => {
Expand Down