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

add onKeyDown Props that supports prevent default behavior #138

Merged
merged 14 commits into from
Dec 21, 2020
Merged
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
9 changes: 5 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# rc-picker
# rc-picker

[![NPM version][npm-image]][npm-url]
[![build status][github-actions-image]][github-actions-url]
Expand Down Expand Up @@ -76,8 +76,9 @@ render(<Picker />, mountNode);
| getPopupContainer | function(trigger) | | to set the container of the floating layer, while the default is to create a div element in body |
| onChange | Function(date: moment, dateString: string) | | a callback function, can be executed when the selected time is changing |
| onOpenChange | Function(open:boolean) | | called when open/close picker |
| onFocus | (evnet:React.FocusEventHandler<HTMLInputElement>) => void | | called like input's on focus |
| onBlur | (evnet:React.FocusEventHandler<HTMLInputElement>) => void | | called like input's on blur |
| onFocus | (event:React.FocusEvent<HTMLInputElement>) => void | | called like input's on focus |
| onBlur | (event:React.FocusEvent<HTMLInputElement>) => void | | called like input's on blur |
| onKeyDown | (event:React.KeyboardEvent<HTMLInputElement>, preventDefault: () => void) => void | | input on keydown event |
| direction | String: ltr or rtl | | Layout direction of picker component, it supports RTL direction too. |

### PickerPanel
Expand All @@ -102,7 +103,7 @@ render(<Picker />, mountNode);
| renderExtraFooter | (mode) => React.Node | | extra footer |
| onSelect | Function(date: moment) | | a callback function, can be executed when the selected time |
| onPanelChange | Function(value: moment, mode) | | callback when picker panel mode is changed |
| onMouseDown | (evnet:React.MouseEventHandler<HTMLInputElement>) => void | | callback when executed onMouseDown evnent |
| onMouseDown | (event:React.MouseEvent<HTMLInputElement>) => void | | callback when executed onMouseDown evnent |
| direction | String: ltr or rtl | | Layout direction of picker component, it supports RTL direction too. |

### RangePicker
Expand Down
8 changes: 8 additions & 0 deletions examples/basic.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ export default () => {
onChange,
};

const keyDown = (e, preventDefault) => {
if (e.keyCode === 13) preventDefault();
};

return (
<div>
<h1>Value: {value ? value.format('YYYY-MM-DD HH:mm:ss') : 'null'}</h1>
Expand Down Expand Up @@ -125,6 +129,10 @@ export default () => {
<h3>Keyboard navigation (Tab key) disabled</h3>
<Picker<Moment> {...sharedProps} locale={enUS} tabIndex={-1} />
</div>
<div style={{ margin: '0 8px' }}>
<h3>Keyboard event with prevent default behaviors</h3>
<Picker<Moment> {...sharedProps} locale={enUS} onKeyDown={keyDown} />
</div>
</div>
</div>
);
Expand Down
5 changes: 5 additions & 0 deletions src/Picker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ export interface PickerSharedProps<DateType> extends React.AriaAttributes {
onMouseLeave?: React.MouseEventHandler<HTMLDivElement>;
onClick?: React.MouseEventHandler<HTMLDivElement>;
onContextMenu?: React.MouseEventHandler<HTMLDivElement>;
onKeyDown?: (event: React.KeyboardEvent<HTMLInputElement>, preventDefault: () => void) => void;

// Internal
/** @private Internal usage, do not use in production mode!!! */
Expand Down Expand Up @@ -170,6 +171,7 @@ function InnerPicker<DateType>(props: PickerProps<DateType>) {
onMouseLeave,
onContextMenu,
onClick,
onKeyDown,
onSelect,
direction,
autoComplete = 'off',
Expand Down Expand Up @@ -310,6 +312,9 @@ function InnerPicker<DateType>(props: PickerProps<DateType>) {
setSelectedValue(mergedValue);
resetText();
},
onKeyDown: (e, preventDefault) => {
onKeyDown?.(e, preventDefault);
},
onFocus,
onBlur,
});
Expand Down
7 changes: 7 additions & 0 deletions src/RangePicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,7 @@ function InnerRangePicker<DateType>(props: RangePickerProps<DateType>) {
onFocus,
onBlur,
onOk,
onKeyDown,
components,
order,
direction,
Expand Down Expand Up @@ -610,12 +611,18 @@ function InnerRangePicker<DateType>(props: RangePickerProps<DateType>) {
...getSharedInputHookProps(0, resetStartText),
open: startOpen,
value: startText,
onKeyDown: (e, preventDefault) => {
onKeyDown?.(e, preventDefault);
},
});

const [endInputProps, { focused: endFocused, typing: endTyping }] = usePickerInput({
...getSharedInputHookProps(1, resetEndText),
open: endOpen,
value: endText,
onKeyDown: (e, preventDefault) => {
onKeyDown?.(e, preventDefault);
},
});

// ========================== Click Picker ==========================
Expand Down
15 changes: 15 additions & 0 deletions src/hooks/usePickerInput.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export default function usePickerInput({
isClickOutside,
triggerOpen,
forwardKeyDown,
onKeyDown,
blurToCancel,
onSubmit,
onCancel,
Expand All @@ -20,6 +21,10 @@ export default function usePickerInput({
isClickOutside: (clickElement: EventTarget | null) => boolean;
triggerOpen: (open: boolean) => void;
forwardKeyDown: (e: React.KeyboardEvent<HTMLInputElement>) => boolean;
onKeyDown: (
e: React.KeyboardEvent<HTMLInputElement>,
preventDefault: () => void,
) => void;
blurToCancel?: boolean;
onSubmit: () => void | boolean;
onCancel: () => void;
Expand All @@ -37,12 +42,22 @@ export default function usePickerInput({

const valueChangedRef = useRef<boolean>(false);

const preventDefaultRef = useRef<boolean>(false);

const inputProps: React.DOMAttributes<HTMLInputElement> = {
onMouseDown: () => {
setTyping(true);
triggerOpen(true);
},
onKeyDown: e => {
const preventDefault = (): void => {
preventDefaultRef.current = true;
};

onKeyDown(e, preventDefault);

if (preventDefaultRef.current) return;

switch (e.which) {
case KeyCode.ENTER: {
if (!open) {
Expand Down
27 changes: 27 additions & 0 deletions tests/picker.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -866,4 +866,31 @@ describe('Picker.Basic', () => {
wrapper.unmount();
});
});

describe('prevent default on keydown', () => {
it('should open picker panel if no prevent default', () => {
const keyDown = jest.fn();
const wrapper = mount(<MomentPicker onKeyDown={keyDown} />);

wrapper.closePicker();
wrapper.keyDown(KeyCode.ENTER);
expect(wrapper.isOpen()).toBeTruthy();
});

it('should not open if prevent default is called', () => {
const keyDown = jest.fn(({ which }, preventDefault) => {
if(which === 13) preventDefault();
});
const wrapper = mount(<MomentPicker onKeyDown={keyDown} />);

wrapper.openPicker();
expect(wrapper.isOpen()).toBeTruthy();

wrapper.keyDown(KeyCode.ESC);
expect(wrapper.isOpen()).toBeFalsy();

wrapper.keyDown(KeyCode.ENTER);
expect(wrapper.isOpen()).toBeFalsy();
});
})
});