Skip to content

Commit

Permalink
add onKeyDown Props that supports prevent default behavior (#138)
Browse files Browse the repository at this point in the history
* add onKeyDown props and disableKey props

* update readme and basic example

* remove disableKey

* update onkeydown preventDefaultBehaviors on Picker
update readme
update basic example

* fix range picker onKeyDown
fix preventDefaultBehaviors types

* update picker input preventdefault (state to ref)
update examples
add test case for prevent default
apply @kerm1it suggestions

* update on key down event type
rename all preventDefaultBehaviors to preventDefault
apply @kerm1it suggestions

* rename focuseventhandler and mouseeventhandler

* update example caption for on keydown
  • Loading branch information
conquera99 authored Dec 21, 2020
1 parent bf3ef16 commit 6b06190
Show file tree
Hide file tree
Showing 6 changed files with 67 additions and 4 deletions.
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();
});
})
});

0 comments on commit 6b06190

Please sign in to comment.