Skip to content

Commit 9147869

Browse files
committed
Add checkKeys property
1 parent 49c46d6 commit 9147869

File tree

7 files changed

+82
-37
lines changed

7 files changed

+82
-37
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@
1313
* Hide the pseudo-checkbox from the accessibility tree
1414
* Change the clickable label from `role="link"` to `role="button"`
1515

16+
### New Features
17+
18+
* Add `checkKeys` property to allow specification of JavaScript keys to trigger check behavior
19+
1620
## [v1.8.0](https://github.com/jakezatecky/react-checkbox-tree/compare/v1.7.3...v1.8.0) (2022-09-06)
1721

1822
### Other

README.md

Lines changed: 28 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -149,32 +149,33 @@ Returns:
149149

150150
### Properties
151151

152-
| Property | Type | Description | Default |
153-
| -------------------- | -------- | ---------------------------------------------------------------------------------------------------------------------- | ----------- |
154-
| `nodes` | array | **Required**. Specifies the tree nodes and their children. | |
155-
| `checkModel` | string | Specifies which checked nodes should be stored in the `checked` array. Accepts `'leaf'` or `'all'`. | `'leaf'` |
156-
| `checked` | array | An array of checked node values. | `[]` |
157-
| `direction` | string | A string that specify whether the direction of the component is left-to-right (`'ltr'`) or right-to-left (`'rtl'`). | `'ltr'` |
158-
| `disabled` | bool | If true, the component will be disabled and nodes cannot be checked. | `false` |
159-
| `expandDisabled` | bool | If true, the ability to expand nodes will be disabled. | `false` |
160-
| `expandOnClick` | bool | If true, nodes will be expanded by clicking on labels. Requires a non-empty `onClick` function. | `false` |
161-
| `expanded` | array | An array of expanded node values. | `[]` |
162-
| `icons` | object | An object containing the mappings for the various icons and their components. See **Changing the Default Icons**. | `{ ... }` |
163-
| `iconsClass` | string | A string that specifies which icons class to utilize. Currently, `'fa4'` and `'fa5'` are supported. | `'fa5'` |
164-
| `id` | string | A string to be used for the HTML ID of the rendered tree and its nodes. | `null` |
165-
| `lang` | object | An object containing the language mappings for the various text elements. | `{ ... }` |
166-
| `name` | string | Optional name for the hidden `<input>` element. | `undefined` |
167-
| `nameAsArray` | bool | If true, the hidden `<input>` will encode its values as an array rather than a joined string. | `false` |
168-
| `nativeCheckboxes` | bool | If true, native browser checkboxes will be used instead of pseudo-checkbox icons. | `false` |
169-
| `noCascade` | bool | If true, toggling a parent node will **not** cascade its check state to its children. | `false` |
170-
| `onlyLeafCheckboxes` | bool | If true, checkboxes will only be shown for leaf nodes. | `false` |
171-
| `optimisticToggle` | bool | If true, toggling a partially-checked node will select all children. If false, it will deselect. | `true` |
172-
| `showExpandAll` | bool | If true, buttons for expanding and collapsing all parent nodes will appear in the tree. | `false` |
173-
| `showNodeIcon` | bool | If true, each node will show a parent or leaf icon. | `true` |
174-
| `showNodeTitle` | bool | If true, the `label` of each node will become the `title` of the resulting DOM node. Overridden by `node.title`. | `false` |
175-
| `onCheck` | function | onCheck handler: `function(checked, targetNode) {}` | `() => {}` |
176-
| `onClick` | function | onClick handler: `function(targetNode) {}`. If set, `onClick` will be called when a node's label has been clicked. | `() => {}` |
177-
| `onExpand` | function | onExpand handler: `function(expanded, targetNode) {}` | `() => {}` |
152+
| Property | Type | Description | Default |
153+
| -------------------- | -------- | ---------------------------------------------------------------------------------------------------------------------- | ---------------- |
154+
| `nodes` | array | **Required**. Specifies the tree nodes and their children. | |
155+
| `checkKeys` | array | A list of [keyboard keys][mdn-key] that will trigger a toggle of the check status of a node. | `[' ', 'Enter']` |
156+
| `checkModel` | string | Specifies which checked nodes should be stored in the `checked` array. Accepts `'leaf'` or `'all'`. | `'leaf'` |
157+
| `checked` | array | An array of checked node values. | `[]` |
158+
| `direction` | string | A string that specify whether the direction of the component is left-to-right (`'ltr'`) or right-to-left (`'rtl'`). | `'ltr'` |
159+
| `disabled` | bool | If true, the component will be disabled and nodes cannot be checked. | `false` |
160+
| `expandDisabled` | bool | If true, the ability to expand nodes will be disabled. | `false` |
161+
| `expandOnClick` | bool | If true, nodes will be expanded by clicking on labels. Requires a non-empty `onClick` function. | `false` |
162+
| `expanded` | array | An array of expanded node values. | `[]` |
163+
| `icons` | object | An object containing the mappings for the various icons and their components. See **Changing the Default Icons**. | `{ ... }` |
164+
| `iconsClass` | string | A string that specifies which icons class to utilize. Currently, `'fa4'` and `'fa5'` are supported. | `'fa5'` |
165+
| `id` | string | A string to be used for the HTML ID of the rendered tree and its nodes. | `null` |
166+
| `lang` | object | An object containing the language mappings for the various text elements. | `{ ... }` |
167+
| `name` | string | Optional name for the hidden `<input>` element. | `undefined` |
168+
| `nameAsArray` | bool | If true, the hidden `<input>` will encode its values as an array rather than a joined string. | `false` |
169+
| `nativeCheckboxes` | bool | If true, native browser checkboxes will be used instead of pseudo-checkbox icons. | `false` |
170+
| `noCascade` | bool | If true, toggling a parent node will **not** cascade its check state to its children. | `false` |
171+
| `onlyLeafCheckboxes` | bool | If true, checkboxes will only be shown for leaf nodes. | `false` |
172+
| `optimisticToggle` | bool | If true, toggling a partially-checked node will select all children. If false, it will deselect. | `true` |
173+
| `showExpandAll` | bool | If true, buttons for expanding and collapsing all parent nodes will appear in the tree. | `false` |
174+
| `showNodeIcon` | bool | If true, each node will show a parent or leaf icon. | `true` |
175+
| `showNodeTitle` | bool | If true, the `label` of each node will become the `title` of the resulting DOM node. Overridden by `node.title`. | `false` |
176+
| `onCheck` | function | onCheck handler: `function(checked, targetNode) {}` | `() => {}` |
177+
| `onClick` | function | onClick handler: `function(targetNode) {}`. If set, `onClick` will be called when a node's label has been clicked. | `() => {}` |
178+
| `onExpand` | function | onExpand handler: `function(expanded, targetNode) {}` | `() => {}` |
178179

179180
#### `onCheck` and `onExpand`
180181

@@ -196,4 +197,5 @@ Individual nodes within the `nodes` property can have the following structure:
196197
[docs-controlled]: https://facebook.github.io/react/docs/forms.html#controlled-components
197198
[docs-state-hooks]: https://reactjs.org/docs/hooks-state.html
198199
[font-awesome]: https://fontawesome.com
200+
[mdn-key]: https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key
199201
[react-fontawesome]: https://github.com/FortAwesome/react-fontawesome

src/js/CheckboxTree.jsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import PropTypes from 'prop-types';
66
import React from 'react';
77

88
import Button from './Button';
9-
import { CHECK_MODEL } from './constants';
9+
import { CHECK_MODEL, KEYS } from './constants';
1010
import NodeModel from './NodeModel';
1111
import TreeNode from './TreeNode';
1212
import iconsShape from './shapes/iconsShape';
@@ -18,6 +18,7 @@ class CheckboxTree extends React.Component {
1818
static propTypes = {
1919
nodes: PropTypes.arrayOf(nodeShape).isRequired,
2020

21+
checkKeys: PropTypes.arrayOf(PropTypes.string),
2122
checkModel: PropTypes.oneOf([CHECK_MODEL.LEAF, CHECK_MODEL.ALL]),
2223
checked: listShape,
2324
direction: PropTypes.string,
@@ -44,6 +45,7 @@ class CheckboxTree extends React.Component {
4445
};
4546

4647
static defaultProps = {
48+
checkKeys: [KEYS.SPACEBAR, KEYS.ENTER],
4749
checkModel: CHECK_MODEL.LEAF,
4850
checked: [],
4951
direction: 'ltr',
@@ -220,6 +222,7 @@ class CheckboxTree extends React.Component {
220222

221223
renderTreeNodes(nodes, parent = {}) {
222224
const {
225+
checkKeys,
223226
expandDisabled,
224227
expandOnClick,
225228
icons,
@@ -257,6 +260,7 @@ class CheckboxTree extends React.Component {
257260
return (
258261
<TreeNode
259262
key={key}
263+
checkKeys={checkKeys}
260264
checked={flatNode.checkState}
261265
className={node.className}
262266
disabled={flatNode.disabled}

src/js/TreeNode.jsx

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,14 @@ import PropTypes from 'prop-types';
33
import React from 'react';
44

55
import Button from './Button';
6+
import { KEYS } from './constants';
67
import NativeCheckbox from './NativeCheckbox';
78
import iconsShape from './shapes/iconsShape';
89
import languageShape from './shapes/languageShape';
910

1011
class TreeNode extends React.PureComponent {
1112
static propTypes = {
13+
checkKeys: PropTypes.arrayOf(PropTypes.string).isRequired,
1214
checked: PropTypes.number.isRequired,
1315
disabled: PropTypes.bool.isRequired,
1416
expandDisabled: PropTypes.bool.isRequired,
@@ -60,22 +62,27 @@ class TreeNode extends React.PureComponent {
6062
onCheck() {
6163
const { value, onCheck } = this.props;
6264

63-
onCheck({ value, checked: this.getCheckState({ toggle: true }) });
65+
onCheck({
66+
value,
67+
checked: this.getCheckState({ toggle: true }),
68+
});
6469
}
6570

6671
onCheckboxKeyPress(event) {
67-
const { which } = event;
72+
const { checkKeys } = this.props;
73+
const { key } = event;
6874

6975
// Prevent browser scroll when pressing space on the checkbox
70-
if (which === 32) {
76+
if (key === KEYS.SPACEBAR && checkKeys.includes(key)) {
7177
event.preventDefault();
7278
}
7379
}
7480

7581
onCheckboxKeyUp(event) {
76-
const { keyCode } = event;
82+
const { checkKeys } = this.props;
83+
const { key } = event;
7784

78-
if ([13, 32].includes(keyCode)) {
85+
if (checkKeys.includes(key)) {
7986
this.onCheck();
8087
}
8188
}

src/js/constants.js

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,12 @@ const CHECK_MODEL = {
44
LEAF: 'leaf',
55
};
66

7-
// eslint-disable-next-line import/prefer-default-export
8-
export { CHECK_MODEL };
7+
const KEYS = {
8+
SPACEBAR: ' ',
9+
ENTER: 'Enter',
10+
};
11+
12+
export {
13+
CHECK_MODEL,
14+
KEYS,
15+
};

test/CheckboxTree.jsx

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import React from 'react';
22
import { assert } from 'chai';
33
import { render, screen } from '@testing-library/react';
4-
import { configure } from '@testing-library/dom';
4+
import { configure, fireEvent } from '@testing-library/dom';
55
import userEvent from '@testing-library/user-event';
66

77
import CheckboxTree from '../src/js/CheckboxTree';
@@ -156,6 +156,27 @@ describe('<CheckboxTree />', () => {
156156
});
157157
});
158158

159+
describe('checkKeys', () => {
160+
it('should trigger a check event when pressing one of the supplied values', async () => {
161+
let actual = null;
162+
163+
const { container } = render(
164+
<CheckboxTree
165+
checkKeys={['Shift']}
166+
checked={[]}
167+
nodes={[{ value: 'jupiter', label: 'Jupiter' }]}
168+
onCheck={(checked) => {
169+
actual = checked;
170+
}}
171+
/>,
172+
);
173+
174+
await fireEvent.keyUp(container.querySelector('.rct-checkbox'), { key: 'Shift' });
175+
176+
assert.deepEqual(actual, ['jupiter']);
177+
});
178+
});
179+
159180
describe('checked', () => {
160181
// https://github.com/jakezatecky/react-checkbox-tree/issues/69
161182
it('should not throw an exception if it contains values that are not in the `nodes` property', () => {

test/TreeNode.jsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import userEvent from '@testing-library/user-event';
77
import TreeNode from '../src/js/TreeNode';
88

99
const baseProps = {
10+
checkKeys: [' ', 'Enter'],
1011
checked: 0,
1112
disabled: false,
1213
expandDisabled: false,
@@ -391,8 +392,7 @@ describe('<TreeNode />', () => {
391392
/>,
392393
);
393394

394-
// TODO: Replace with `user.type` when migrating away from `keyCode`
395-
await fireEvent.keyUp(container.querySelector('.rct-checkbox'), { keyCode: 32 });
395+
await fireEvent.keyUp(container.querySelector('.rct-checkbox'), { key: 'Enter' });
396396

397397
assert.isTrue(actual.checked);
398398
});

0 commit comments

Comments
 (0)