Skip to content

Commit 2ade386

Browse files
committed
refactors to functional component
- rewrites DataListInput as a functional component - changes version to 2.0.0 - describes versioning in README - adds more unit tests - adds snapshot tests - adds better a11y support by adding aria-labels and roles
1 parent 47d12af commit 2ade386

27 files changed

+6141
-2931
lines changed

.babelrc

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,13 @@
11
{
22
"presets": ["@babel/preset-react", "@babel/preset-env"],
33
"plugins": ["@babel/plugin-proposal-class-properties"],
4-
}
4+
"env": {
5+
"test": {
6+
"presets": [["@babel/preset-env", { "targets": { "node": "current" } }]],
7+
"plugins": [
8+
"@babel/plugin-transform-regenerator",
9+
"@babel/plugin-transform-runtime"
10+
]
11+
}
12+
}
13+
}

.editorconfig

Lines changed: 0 additions & 8 deletions
This file was deleted.

.eslintrc

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,4 @@
11
{
2-
"extends": "airbnb",
3-
"parser": "babel-eslint",
4-
"env": {
5-
"browser": true,
6-
"jasmine": true
7-
},
8-
"plugins": [
9-
"react",
10-
"jest-dom"
11-
],
12-
"rules": {
13-
"react/jsx-filename-extension": [1, { "extensions": [".js", ".jsx"] }]
14-
}
15-
}
2+
"extends": ["wesbos"],
3+
"plugins": ["jest-dom"]
4+
}

.vscode/settings.json

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
// These are all my auto-save configs
3+
"editor.formatOnSave": true,
4+
// turn it off for JS and JSX, we will do this via eslint
5+
"[javascript]": {
6+
"editor.formatOnSave": false
7+
},
8+
"[javascriptreact]": {
9+
"editor.formatOnSave": false
10+
},
11+
// tell the ESLint plugin to run on save
12+
"editor.codeActionsOnSave": {
13+
"source.fixAll": true
14+
},
15+
// Optional BUT IMPORTANT: If you have the prettier extension enabled for other languages like CSS and HTML, turn it off for JS since we are doing it through Eslint already
16+
"prettier.disableLanguages": ["javascript", "javascriptreact"]
17+
}

README.md

Lines changed: 83 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
## Info
22

3-
This package provides a single react component. The component contains an input field with a drop down menu to pick a possible option based on the current input as a react component.
3+
This package provides a single React component. The component contains an input field with a drop down menu to pick a possible option based on the current input as a React component.
44

5-
Have a look at [w3schools.com](https://www.w3schools.com/howto/howto_js_autocomplete.asp) to see how you can do something similar with pure html, css, and js. For more information about react and the ecosystem see this [guide](https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md).
5+
Have a look at [w3schools.com](https://www.w3schools.com/howto/howto_js_autocomplete.asp) to see how you can do something similar with pure html, css, and js. For more information about React and the ecosystem see this [guide](https://reactjs.org/docs/getting-started.html).
66

77
## Demo
88

@@ -12,121 +12,121 @@ Check it out on [my personal website](https://andre-landgraf.cool/uses)!
1212

1313
Feel free to get inspired and more importantly please provide [your feedback](https://github.com/andrelandgraf/react-datalist-input/issues) on structure and style.
1414

15-
### Using Gatsby or Next.js?
15+
### Using Gatsby or Next.js?
1616

1717
This component is not compatible with server-side rendering since it has css bundled with it.
1818

1919
I created a plain version of this package without css. Find more information [here](https://www.npmjs.com/package/react-plain-datalist-input).
2020

21+
## Versions
22+
23+
- Version 2.x.x serves a functional component using hooks
24+
- Version 1.x.x serves a class component
25+
26+
The documentation below mainly applies for both versions but will be updated based on version 2.x.x updates in the future.
27+
2128
## Installation
2229

2330
### Installation via npm
2431

2532
```bash
26-
npm install react-datalist-input --save
33+
npm i react-datalist-input
2734
```
2835

2936
### Basic Usage
3037

3138
```javascript
32-
import DataListInput from 'react-datalist-input';
33-
34-
/**
35-
* OPTIONAL, this packages comes with a simple default label matching function
36-
* but feel free to create your own match algorithm if you want to do so
37-
* @param {String} currentInput (the current user input)
38-
* @param {object} item (one item of the items array)
39-
* @returns {boolean}
40-
*/
41-
matchCurrentInput = (currentInput, item) => {
42-
const yourLogic = item.someAdditionalValue;
43-
return (yourLogic.substr(0, currentInput.length).toUpperCase() === currentInput.toUpperCase());
39+
import React, { useState, useMemo, useCallback } from "react";
40+
import DataListInput from "react-datalist-input";
41+
42+
const YourComponent = ({ myValues }) => {
43+
// selectedItem
44+
const [item, setItem] = useState();
45+
46+
/**
47+
* your callback function gets called if the user selects one option out of the drop down menu
48+
* @param selectedItem object (the selected item / option)
49+
*/
50+
const onSelect = useCallback((selectedItem) => {
51+
console.log("selectedItem", selectedItem);
52+
}, []);
53+
54+
// the array you want to pass to the react-data-list component
55+
// key and label are required properties
56+
const items = useMemo(
57+
() =>
58+
myValues.map((oneItem) => ({
59+
// required: what to show to the user
60+
label: oneItem.name,
61+
// required: key to identify the item within the array
62+
key: oneItem.id,
63+
// feel free to add your own app logic to access those properties in the onSelect function
64+
someAdditionalValue: oneItem.someAdditionalValue,
65+
// or just keep everything
66+
...oneItem,
67+
})),
68+
[myValues]
69+
);
70+
71+
return (
72+
<DataListInput
73+
placeholder="Select an option from the drop down menu..."
74+
items={items}
75+
onSelect={onSelect}
76+
/>
77+
);
4478
};
45-
46-
/**
47-
* your callback function gets called if the user selects one option out of the drop down menu
48-
* @param selectedItem object (the selected item / option)
49-
* @returns {*}
50-
*/
51-
onSelect = (selectedItem) => {
52-
this.doSomething(selectedItem);
53-
};
54-
55-
render() {
56-
// the array you want to pass to the react-data-list component
57-
// each element at least needs a key and a label
58-
const items = myValues.map((item, i) => {
59-
return {
60-
// what to show to the user
61-
label: item.id + ": " + item.name,
62-
// key to identify the item within the array
63-
key: item.id,
64-
// feel free to add your own app logic to access those properties in the onSelect function
65-
someAdditionalValue: item.someAdditionalValue,
66-
// or just keep everything
67-
...item,
68-
}
69-
});
70-
71-
return(
72-
<div>
73-
<DataListInput
74-
placeholder={"Select an option from the drop down menu..."}
75-
items={items}
76-
onSelect={this.onSelect}
77-
match={this.matchCurrentInput}
78-
/>
79-
</div>
80-
);
8179
```
8280

8381
## Properties
8482

85-
| Prop | Type | Required/Optional | Default Value
86-
|----------|------------- |------| ------|
87-
| [items](#markdown-header-items) | array | required | - |
88-
| [onSelect](#markdown-header-onSelect) | function | required | - |
89-
| [match](#markdown-header-match) | function | optional | internal matching function |
90-
| [onDropdownOpen](#markdown-header-onDropdownOpen) | function | optional | - |
91-
| [onDropdownClose](#markdown-header-onDropdownClose) | function | optional | - |
92-
| [placeholder](#markdown-header-placeholder) | string | optional | '' |
93-
| [itemClassName](#markdown-header-itemClassName) | string | optional | - |
94-
| [activeItemClassName](#markdown-header-activeItemClassName) | string | optional | - |
95-
| [inputClassName](#markdown-header-inputClassName) | string | optional | - |
96-
| [dropdownClassName](#markdown-header-dropdownClassName) | string | optional | - |
97-
| [requiredInputLength](#markdown-header-requiredInputLength) | number | optional | 0 |
98-
| [clearInputOnSelect](#markdown-header-clearInputOnSelect) | boolean | optional | false |
99-
| [suppressReselect](#markdown-header-suppressReselect) | boolean | optional | true |
100-
| [dropDownLength](#markdown-header-dropDownLength) | number | optional | infinite |
101-
| [initialValue](#markdown-header-initialValue) | string | optional | - |
102-
| [debounceTime](#markdown-header-debounceTime) | number | optional | 0 |
103-
| [debounceLoader](#markdown-header-debounceLoader) | string | optional | 'Loading...' |
104-
| [onInput](#markdown-header-onInput) | function | optional | - |
83+
| Prop | Type | Required/Optional | Default Value |
84+
| ----------------------------------------------------------- | -------- | ----------------- | -------------------------- |
85+
| [items](#markdown-header-items) | array | required | - |
86+
| [onSelect](#markdown-header-onSelect) | function | required | - |
87+
| [match](#markdown-header-match) | function | optional | internal matching function |
88+
| [onDropdownOpen](#markdown-header-onDropdownOpen) | function | optional | - |
89+
| [onDropdownClose](#markdown-header-onDropdownClose) | function | optional | - |
90+
| [placeholder](#markdown-header-placeholder) | string | optional | '' |
91+
| [itemClassName](#markdown-header-itemClassName) | string | optional | - |
92+
| [activeItemClassName](#markdown-header-activeItemClassName) | string | optional | - |
93+
| [inputClassName](#markdown-header-inputClassName) | string | optional | - |
94+
| [dropdownClassName](#markdown-header-dropdownClassName) | string | optional | - |
95+
| [requiredInputLength](#markdown-header-requiredInputLength) | number | optional | 0 |
96+
| [clearInputOnSelect](#markdown-header-clearInputOnSelect) | boolean | optional | false |
97+
| [suppressReselect](#markdown-header-suppressReselect) | boolean | optional | true |
98+
| [dropDownLength](#markdown-header-dropDownLength) | number | optional | infinite |
99+
| [initialValue](#markdown-header-initialValue) | string | optional | - |
100+
| [debounceTime](#markdown-header-debounceTime) | number | optional | 0 |
101+
| [debounceLoader](#markdown-header-debounceLoader) | string | optional | 'Loading...' |
102+
| [onInput](#markdown-header-onInput) | function | optional | - |
105103

106104
### <a name="markdown-header-items"></a>items
107105

108106
- <b>Required</b> property!
109107
- The array of options for the drop down menu.<br>
110108
- Every item inside the array needs to have following properties:
111-
- key : an id that identifies the item within the array
112-
- label: the label that will be shown in the drop down menu
109+
- key : an id that identifies the item within the array
110+
- label: the label that will be shown in the drop down menu
113111

114112
### <a name="markdown-header-onSelect"></a>onSelect
115113

116114
- <b>Required</b> property!
117115
- The callback function that will be called if the user selects one item of the drop down menu.
118116
- Gets only called if the item changes. Selecting the same item twice will only trigger the function once (the first time).
119117
- Parameter: (selectedKey)
120-
- selectedKey: the Key Property of the item that the user selected
118+
- selectedKey: the Key Property of the item that the user selected
121119

122120
### <a name="markdown-header-match"></a>match
123121

124122
- Pass a match function as stated above for creating your own matching algorithm for the autocomplete functionality.
125123
- Parameter: (currentInput, item)
126-
- currentInput: String, the current user input typed into the input field
127-
- item: Object, the item of the items array (with key and label properties)
124+
125+
- currentInput: String, the current user input typed into the input field
126+
- item: Object, the item of the items array (with key and label properties)
128127

129128
- Default:
129+
130130
```javascript
131131
/**
132132
* default function for matching the current input value (needle) and the values of the items array
@@ -135,13 +135,17 @@ render() {
135135
* @returns {boolean}
136136
*/
137137
match = (currentInput, item) => {
138-
return item.label.substr(0, currentInput.length).toUpperCase() === currentInput.toUpperCase();
138+
return (
139+
item.label.substr(0, currentInput.length).toUpperCase() ===
140+
currentInput.toUpperCase()
141+
);
139142
};
140143
```
141144

142145
### <a name="markdown-header-onDropdownOpen"></a>onDropdownOpen
143146

144147
- The callback function that will be called after opening the drop down menu.
148+
- It will fire only once and not be called again after new input.
145149

146150
### <a name="markdown-header-onDropdownClose"></a>onDropdownClose
147151

@@ -205,7 +209,7 @@ match = (currentInput, item) => {
205209
- For example, `initialValue={'hello world'}` will print `hello world` into the input field on first render.
206210
- Default is empty string.
207211
- Caution: Don't confuse this with a placeholder (see placerholder prop), this is an actual value in the input
208-
and supports uses cases like saving user state or suggesting a search value.
212+
and supports uses cases like saving user state or suggesting a search value.
209213

210214
### <a name="markdown-header-debounceTime"></a>debounceTime
211215

@@ -218,7 +222,6 @@ and supports uses cases like saving user state or suggesting a search value.
218222
- If you still have performance issues even when using a `debounceTime={3000}` or higher, you might want to consider using another package / user input instead. Think about a "search/look-up"-button next to your input field or even consider running the search functionality in a dedicated backend.
219223
- Default is zero which means no timeout/debouncing is used.
220224

221-
222225
### <a name="markdown-header-debounceLoader"></a>debounceLoader
223226

224227
- Only in use if debounceTime is set
@@ -229,5 +232,3 @@ and supports uses cases like saving user state or suggesting a search value.
229232

230233
- The callback function that will be called whenever the user types into the input field
231234
- Exposing this function supports use cases like resetting states on empty input field
232-
233-

0 commit comments

Comments
 (0)