Skip to content

Commit 4813d25

Browse files
jakezateckysaltovo
andauthored
v6.0 Release (#253)
* Drop usage of `nanoid` * Drop Less.js support * Update style format dependencies * Add `getOptionLabel` and `getOptionValue` props Resolves #208. * Update links to latest React docs * Update options document * Organize links * Mention the loss of `id` on some children * Split `simpleValue={false}` into its own wrapper component Having both `simpleValue={true}` and simplevalue={false}` mixed into the core component logic made things messy and unwiedly. By separating concerns, the primary component becomes simpler and easier to maintain. * Fix lint error * Make default filter utilize `getOptionLabel` * Minor rearrangement * Merge missing keys in `icons` and `lang` with default * Split `filterPlaceholder` by control key * Add async search example This takes a lot of inspiration from #155 by @saltovo. Co-Authored-By: saltovo <46911969+saltovo@users.noreply.github.com> * Remove unnecessary state derivation * Remove extra lines * Prevent race conditions in `LazyFilterExample` * Add padding on no options text * Convert main component into function * Fix linting error * Favor selected/available over right/left Left/right are can be incorrect in certain arrangements. Consistent use of "available" and "selected" is unambiguous and precise, if wordy. This is unfortunately a major backwards compatibility breakage, but is necesary to make the underyling code and markup more precise and semantic. * Add migration guide * Simplify button positioning * Simplify filter component * Simplify Action component * Simplify `showHeaderLabels` logic * Add additional breaking change to CHANGELOG * Add more contrast and visibility of text * Make CHANGELOG language cleaner * Drop support for React before v16.8 Hooks did not exist before this version. * Fix bug when double-clicking optgroups * Make positive changes more prominent * Remove no longer necessary Unicode escaping * Default to `'fa6'` for `iconsClass` Also update CDN links to latest version. * Put breaking changes first * Fix matrix resolution * Drop usage of depreacted `defaultProps` This necessitated some clever workarounds to share default property values between the main component and `ObjectValueWrapper`. In the end, we have added back in the `simpleValue` property, thus reducing BC breakage. Resolves #248. * Improve accessibility of required error * Conform changelog to a more standardized format * Fix `required` test * Change filter input to `type="search"` Resolves #247. * Fix example buttons on smaller widths * Update dependencies * Slightly improve descriptions and names * Prefer order over index * Extract `mergeRefs` into its own function * Make `userSelections` return label, value, and index Resolves #230. * Update dependencies * Release v6.0 --------- Co-authored-by: saltovo <46911969+saltovo@users.noreply.github.com>
1 parent 2dc9f08 commit 4813d25

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+1811
-1924
lines changed

.github/workflows/main.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,15 @@ jobs:
88
strategy:
99
fail-fast: false
1010
matrix:
11-
node: ['14', '16', '18']
11+
node: [18, 20]
1212
steps:
1313
-
1414
uses: actions/checkout@v3
1515
-
16-
name: Use Node.js ${{ matrix.node-version }}
16+
name: Use Node.js ${{ matrix.node }}
1717
uses: actions/setup-node@v3
1818
with:
19-
node-version: ${{ matrix.node-version }}
19+
node-version: ${{ matrix.node }}
2020
-
2121
name: Install dependencies
2222
run: yarn install

.gitignore

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
/.css-compare
21
/examples/dist
32
/lib
43
/node_modules

CHANGELOG.md

Lines changed: 54 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,57 @@
1-
# CHANGELOG
1+
# Changelog
22

3-
## [v5.0.2]((https://github.com/jakezatecky/react-dual-listbox/compare/v5.0.1...v5.0.2)) (2023-02-08)
3+
## [6.0.0](https://github.com/jakezatecky/react-dual-listbox/compare/v5.0.2...v6.0.0) (2023-12-06)
4+
5+
_This new version includes a variety of breaking changes. Please review them before upgrading._
6+
7+
### Added
8+
9+
* Add `getOptionLabel` and `getOptionValue` properties to allow for custom keys beyond `label` and `value` (#208)
10+
* Add `LazyFilterExample.jsx` to demonstrate lazy loading for the `options` property by user search
11+
12+
### Changed
13+
14+
* **Breaking:** `onChange` property: the second argument now returns objects with `label`, `index`, and `value`, instead of just values (#230)
15+
* **Breaking:** `icons` property:
16+
* Will now merge any missing keys with the default icons
17+
* Rename `moveLeft`, `moveAllLeft`, `moveRight`, and `moveAllRight` to `moveToAvailable`, `moveAllToAvailable`, `moveToSelected`, and `moveAllToSelected`
18+
* **Breaking:** `iconsClass` property: now defaults to `'fa6'`
19+
* **Breaking:** `id` property: no longer defaults to a random UUID when null and no longer used for some child components
20+
* **Breaking:** `lang` property:
21+
* Will now merge any missing keys with the default language
22+
* Rename `moveLeft`, `moveAllLeft`, `moveRight`, and `moveAllRight` to `moveToAvailable`, `moveAllToAvailable`, `moveToSelected`, and `moveAllToSelected`
23+
* Split `filterPlaceholder` into `availableFilterPlaceholder` and `selectedFilterPlaceholder`
24+
* **Breaking:** `options` property: no longer has PropTypes validation for `label` and `value` (#208)
25+
* **Breaking:** Rename `*-right` and `*-left` classes to `*-to-selected` and `*-to-available`
26+
* Improve accessibility of required error
27+
* Change filter input to `type="search"` (#247)
28+
29+
### Removed
30+
31+
* **Breaking:** Drop support for Less.js styles
32+
* **Breaking:** Drop support for React before v16.8
33+
* **Breaking:** Remove `rdl-sr-only` class
34+
* Drop usage of deprecated `defaultProps` (#248)
35+
36+
### Fixed
37+
38+
* Fix positioning of action buttons relative to the list boxes
39+
* Prevent situation where double-clicking an `optgroup` moved any selected options under it
40+
41+
### `icons` and `lang` Migration Guide
42+
43+
The keys `moveLeft`, `moveAllLeft`, `moveRight`, and `moveAllRight` are now `moveToAvailable`, `moveAllToAvailable`, `moveToSelected`, and `moveAllToSelected` in all instances. Refer to the table below to rename any affected `icons` or `lang` keys:
44+
45+
| Old Key Name | New Key Name |
46+
| -------------- | -------------------- |
47+
| `moveLeft` | `moveToAvailable` |
48+
| `moveAllLeft` | `moveAllToAvailable` |
49+
| `moveRight` | `moveToSelected` |
50+
| `moveAllRight` | `moveAllToSelected` |
51+
52+
### Other
53+
54+
## [v5.0.2](https://github.com/jakezatecky/react-dual-listbox/compare/v5.0.1...v5.0.2) (2023-02-08)
455

556
### Bug Fixes
657

@@ -95,7 +146,7 @@
95146
### New Features
96147

97148
* [#80]: Add `disabled` support for elements in the `options` property
98-
* [#87]: Add `title` support for elements in the `options` property
149+
* [#87]: Add `title` support for elements in the `options` property
99150
* [#90]: Add `selection` argument to the `onChange` handler to track highlighted values
100151
* [#104]: Add `className` property to allow specification of a custom class on the root node
101152
* [#133]: Add `moveTop` and `moveBottom` buttons to `showOrderButtons` property

README.md

Lines changed: 20 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,9 @@ npm install react-dual-listbox --save
2828
2929
### Include CSS
3030

31-
For your convenience, the library's styles can be consumed utilizing one of the following files:
31+
The library's styles are available through one of the following files:
3232

3333
* `node_modules/react-dual-listbox/lib/react-dual-listbox.css`
34-
* `node_modules/react-dual-listbox/src/less/react-dual-listbox.less`
3534
* `node_modules/react-dual-listbox/src/scss/react-dual-listbox.scss`
3635

3736
Either include one of these files in your stylesheets or utilize a CSS loader:
@@ -42,13 +41,14 @@ import 'react-dual-listbox/lib/react-dual-listbox.css';
4241

4342
### Render Component
4443

45-
The `DualListBox` is a [controlled] component, so you have to update the `selected` property in conjunction with the `onChange` handler if you want the selected values to change.
44+
The `DualListBox` is a [controlled][react-controlled] component, so you have to update the `selected` property in conjunction with the `onChange` handler if you want the selected values to change.
4645

4746
Here is a minimal rendering of the component:
4847

4948
``` jsx
5049
import React, { useState } from 'react';
5150
import DualListBox from 'react-dual-listbox';
51+
import 'react-dual-listbox/lib/react-dual-listbox.css';
5252

5353
const options = [
5454
{ value: 'one', label: 'Option One' },
@@ -142,12 +142,12 @@ Optionally, you can also override the default filter placeholder text and the fi
142142
``` jsx
143143
<DualListBox
144144
canFilter
145-
filterCallback={(option, filterInput) => {
145+
filterCallback={(option, filterInput, { getOptionLabel }) => {
146146
if (filterInput === '') {
147147
return true;
148148
}
149149

150-
return (new RegExp(filterInput, 'i')).test(option.label);
150+
return (new RegExp(filterInput, 'i')).test(getOptionLabel(option));
151151
}}
152152
options={options}
153153
/>
@@ -244,19 +244,21 @@ const onChange = (selected, selection, controlKey) => {
244244
| Property | Type | Description | Default |
245245
| --------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------- | ---------------- |
246246
| `options` | array | **Required**. Specifies the list of options that may exist on either side of the dual list box. | |
247-
| `onChange` | function | **Required**. The handler called when options are moved to either side: `function(selected, selection, controlKey) {}`. | |
247+
| `onChange` | function | **Required**. The handler called when the selected options change: `function(selected, selection, controlKey) {}`. | |
248248
| `alignActions` | string | A value specifying whether to align the action buttons to the `'top'` or `'middle'`. | `middle` |
249249
| `allowDuplicates` | bool | If true, duplicate options will be allowed in the selected list box. | `false` |
250250
| `available` | array | A subset of the `options` array to optionally filter the available list box. | `undefined` |
251-
| `availableRef` | function | A React function [ref] to the "available" list box. | `null` |
251+
| `availableRef` | function | A React function [ref][react-ref] to the "available" list box. | `null` |
252252
| `canFilter` | bool | If true, search boxes will appear above both list boxes, allowing the user to filter the results. | `false` |
253253
| `className` | string | An optional `className` to apply to the root node. | `null` |
254254
| `disabled` | bool | If true, both "available" and "selected" list boxes will be disabled. | `false` |
255255
| `filter` | object | A key-value map of `{ available: [value], selected: [value] }` to control the filter values (defaults to uncontrolled). | `null` |
256256
| `filterCallback` | function | The filter function to run on a given option and input string: `function(option, filterInput) {}`. See **Filtering**. | `() => { ... }` |
257+
| `getOptionLabel` | function | The function to resolve the **label** from an option. Defaults to `option => option.label`. | `() => { ... }` |
258+
| `getOptionValue` | function | The function to resolve the **value** from an option. Defaults to `option => option.value`. | `() => { ... }` |
257259
| `htmlDir` | string | The [directionality][mdn-directionality] of the component's elements. Set to `'rtl'` if using a right-to-left language. | `'ltr'` |
258260
| `icons` | object | A key-value pairing of action icons and their React nodes. See **Changing the Default Icons** for further info. | `{ ... }` |
259-
| `iconsClass` | string | A value specifying which overarching icon class to use. Built-in support for `fa5`, `fa6`, and `native` icons. | `'fa5'` |
261+
| `iconsClass` | string | A value specifying which overarching icon class to use. Built-in support for `fa5`, `fa6`, and `native` icons. | `'fa6'` |
260262
| `id` | string | An HTML ID prefix for the various sub elements. | `null` |
261263
| `lang` | object | A key-value pairing of localized text. See [`src/js/lang/default.js`][lang-file] for a list of keys. | `{ ... }` |
262264
| `moveKeys` | array | A list of [keyboard keys][mdn-key] that will trigger a toggle of the highlighted options. | `[' ', 'Enter']` |
@@ -265,24 +267,24 @@ const onChange = (selected, selection, controlKey) => {
265267
| `preserveSelectOrder` | bool | If true, the order in which the available options are selected are preserved when the items are moved to the right. | `false` |
266268
| `required` | bool | If true, this component will require `selected` to be non-empty to pass a form validation | `false` |
267269
| `selected` | array | A list of the selected options appearing in the rightmost list box. | `[]` |
268-
| `selectedRef` | function | A React function [ref] to the "selected" list box. | `null` |
269-
| `simpleValue` | bool | If true, the `selected` value passed in `onChange` is an array of string values. Otherwise, it is an array of options. | `true` |
270+
| `selectedRef` | function | A React function [ref][react-ref] to the "selected" list box. | `null` |
270271
| `showHeaderLabels` | bool | If true, labels above both the available and selected list boxes will appear. These labels derive from `lang`. | `false` |
271272
| `showNoOptionsText` | bool | If true, text will appear in place of the available/selected list boxes when no options are present. | `false` |
272273
| `showOrderButtons` | bool | If true, a set of up/down buttons will appear near the selected list box to allow the user to re-arrange the items. | `false` |
274+
| `simpleValue` | bool | If true, the `selected` value passed in `onChange` is an array of string values. Otherwise, it is an array of options. | `true` |
273275
274276
#### Option Properties
275277
276-
| Property | Type | Description |
277-
| ---------- | ------ | ------------------------------------------------------------- |
278-
| `label` | string | **Required**. The text label for the given option. |
279-
| `value` | mixed | **Required**. The text or numeric value for the given option. |
280-
| `disabled` | bool | If true, disables the option from selection. |
281-
| `title` | string | Adds the HTML `title` attribute to the option. |
278+
| Property | Type | Description |
279+
| ---------- | ------ | ---------------------------------------------------------------------------------------------------------- |
280+
| `label` | string | **Required**. The text label for the given option. Use `getOptionLabel` to set a different key. |
281+
| `value` | mixed | **Required**. The text or numeric value for the given option. Use `getOptionValue` to set a different key. |
282+
| `disabled` | bool | If true, disables the option from selection. |
283+
| `title` | string | Adds the HTML `title` attribute to the option. |
282284
283-
[controlled]: https://facebook.github.io/react/docs/forms.html#controlled-components
285+
[react-controlled]: https://react.dev/learn/sharing-state-between-components#controlled-and-uncontrolled-components
286+
[react-ref]: https://react.dev/learn/manipulating-the-dom-with-refs
284287
[font-awesome]: https://fontawesome.com
285288
[lang-file]: https://github.com/jakezatecky/react-dual-listbox/blob/master/src/js/lang/default.js
286289
[mdn-directionality]: https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/dir
287290
[mdn-key]: https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key
288-
[ref]: https://reactjs.org/docs/refs-and-the-dom.html

examples/src/index.html

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,12 @@
66
<meta name="viewport" content="width=device-width, initial-scale=1.0">
77
<meta name="theme-color" content="#3498db">
88
<meta name="description" content="React Dual Listbox: A feature-rich dual listbox for React.">
9-
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/normalize/8.0.1/normalize.min.css">
10-
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/font-awesome/5.9.0/css/all.min.css">
11-
<link rel="stylesheet" href="//fonts.googleapis.com/css?family=Open+Sans:400,700">
9+
10+
<link rel="preconnect" href="https://fonts.googleapis.com">
11+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
12+
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;700&display=swap">
13+
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/normalize/8.0.1/normalize.min.css">
14+
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
1215
<link rel="stylesheet" href="./scss/style.scss">
1316

1417
<!-- Google tag (gtag.js) -->
@@ -33,8 +36,9 @@ <h2 class="project-tagline">A feature-rich dual listbox for React</h2>
3336
<section class="main-content">
3437
<p>
3538
<strong>React Dual Listbox</strong> is a lightweight React component for rendering dual listbox views.
36-
Checkout the examples below and then view <a href="https://github.com/jakezatecky/react-dual-listbox/tree/master/examples/src/js">source code</a>
37-
or main <a href="https://github.com/jakezatecky/react-dual-listbox">documentation page</a> when you are ready to try it out.
39+
Checkout the examples and <a href="https://github.com/jakezatecky/react-dual-listbox/tree/master/examples/src/js">source code</a>
40+
below. Head over to the main <a href="https://github.com/jakezatecky/react-dual-listbox">documentation page</a>
41+
to see the full list of options.
3842
</p>
3943

4044
<h1>Examples</h1>
@@ -59,6 +63,18 @@ <h3>Filter Example</h3>
5963
</p>
6064
<div id="filter-example"></div>
6165

66+
<h3>Lazy Filter Example</h3>
67+
<p>
68+
The component can support lazy filtering/searching, using the <code>onFilterCallback</code> property. Try
69+
searching for “europa” or “io”.
70+
</p>
71+
<p>
72+
See
73+
<a href="https://github.com/jakezatecky/react-dual-listbox/blob/master/examples/src/js/LazyFilterExample.jsx"><code>LazyFilterExample.jsx</code></a>
74+
for the sample code that includes a mock API request.
75+
</p>
76+
<div id="lazy-filter-example"></div>
77+
6278
<h3>Align Actions to Top Example</h3>
6379
<p>
6480
In addition to having the movement action aligned to the middle of the component, you can arrange them to the

examples/src/index.jsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import AllowDuplicatesExample from './js/AllowDuplicatesExample';
66
import BasicExample from './js/BasicExample';
77
import DisabledExample from './js/DisabledExample';
88
import FilterExample from './js/FilterExample';
9+
import LazyFilterExample from './js/LazyFilterExample';
910
import PreserveSelectOrderExample from './js/PreserveSelectOrderExample';
1011
import OptGroupExample from './js/OptGroupExample';
1112
import RequiredExample from './js/RequiredExample';
@@ -15,6 +16,7 @@ import RtlExample from './js/RtlExample';
1516
createRoot(document.getElementById('basic-example')).render(<BasicExample />);
1617
createRoot(document.getElementById('optgroup-example')).render(<OptGroupExample />);
1718
createRoot(document.getElementById('filter-example')).render(<FilterExample />);
19+
createRoot(document.getElementById('lazy-filter-example')).render(<LazyFilterExample />);
1820
createRoot(document.getElementById('align-top-example')).render(<AlignTopExample />);
1921
createRoot(document.getElementById('disabled-example')).render(<DisabledExample />);
2022
createRoot(document.getElementById('preserve-select-order-example')).render(<PreserveSelectOrderExample />);

examples/src/js/AlignTopExample.jsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ function AlignTopExample() {
1414
<DualListBox
1515
alignActions="top"
1616
canFilter
17+
id="align-top"
1718
options={options}
1819
selected={selected}
1920
onChange={onChange}

examples/src/js/AllowDuplicatesExample.jsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ function AllowDuplicatesExample() {
1313
return (
1414
<DualListBox
1515
allowDuplicates
16+
id="allow-duplicates"
1617
options={options}
1718
preserveSelectOrder
1819
selected={selected}

examples/src/js/BasicExample.jsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ function BasicExample() {
1212

1313
return (
1414
<DualListBox
15+
id="basic"
1516
options={options}
1617
selected={selected}
1718
onChange={onChange}

examples/src/js/DisabledExample.jsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ function DisabledExample() {
1414
<DualListBox
1515
canFilter
1616
disabled
17+
id="disabled"
1718
options={options}
1819
selected={selected}
1920
onChange={onChange}

0 commit comments

Comments
 (0)