Skip to content

Commit 5ed7538

Browse files
committed
Merge branch 'master' into feature-suppressRefError
2 parents 5513e2a + 22ed3d7 commit 5ed7538

9 files changed

Lines changed: 181 additions & 20 deletions

.all-contributorsrc

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -381,6 +381,44 @@
381381
"code",
382382
"test"
383383
]
384+
},
385+
{
386+
"login": "ifyoumakeit",
387+
"name": "Dave Garwacke",
388+
"avatar_url": "https://avatars0.githubusercontent.com/u/3998604?v=4",
389+
"profile": "http://www.warbyparker.com",
390+
"contributions": [
391+
"doc"
392+
]
393+
},
394+
{
395+
"login": "Drapegnik",
396+
"name": "Ivan Pazhitnykh",
397+
"avatar_url": "https://avatars3.githubusercontent.com/u/11758660?v=4",
398+
"profile": "http://linkedin.com/in/drapegnik",
399+
"contributions": [
400+
"code",
401+
"test"
402+
]
403+
},
404+
{
405+
"login": "Rendez",
406+
"name": "Luis Merino",
407+
"avatar_url": "https://avatars0.githubusercontent.com/u/61776?v=4",
408+
"profile": "https://github.com/Rendez",
409+
"contributions": [
410+
"doc"
411+
]
412+
},
413+
{
414+
"login": "arahansen",
415+
"name": "Andrew Hansen",
416+
"avatar_url": "https://avatars0.githubusercontent.com/u/8746094?v=4",
417+
"profile": "http://twitter.com/arahansen",
418+
"contributions": [
419+
"code",
420+
"test"
421+
]
384422
}
385423
]
386424
}

README.md

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ autocomplete/dropdown/select/combobox components</p>
1717
[![version][version-badge]][package]
1818
[![MIT License][license-badge]][LICENSE]
1919

20-
[![All Contributors](https://img.shields.io/badge/all_contributors-39-orange.svg?style=flat-square)](#contributors)
20+
[![All Contributors](https://img.shields.io/badge/all_contributors-43-orange.svg?style=flat-square)](#contributors)
2121
[![PRs Welcome][prs-badge]][prs]
2222
[![Chat][chat-badge]][chat]
2323
[![Code of Conduct][coc-badge]][coc]
@@ -85,8 +85,8 @@ npm install --save downshift
8585
> have those installed as well.
8686
8787
> Note also this library supports `preact` out of the box. If you are using
88-
> `preact` then look in the `dist/` folder and use the module you want with the
89-
> `preact` suffix.
88+
> `preact` then use the corresponding module in the `preact/dist` folder.
89+
> You can even `import Downshift from 'downshift/preact'` 👍
9090
9191
## Usage
9292

@@ -182,6 +182,13 @@ This is the initial `isOpen` value.
182182
Used to determine the string value for the selected item (which is used to
183183
compute the `inputValue`.
184184

185+
### selectedItemChanged
186+
187+
> `function(prevItem: any, item: any)` | defaults to: `(prevItem, item) => (prevItem !== item)`
188+
189+
Used to determine if the new `selectedItem` has changed compared to the
190+
previous `selectedItem` and properly update Downshift's internal state.
191+
185192
### getA11yStatusMessage
186193

187194
> `function({/* see below */})` | default messages provided in English
@@ -250,7 +257,7 @@ but differ slightly.
250257

251258
- `changes`: These are the properties that actually have changed since the last
252259
state change.
253-
- `stateAndHelpers`: This is the exact same thing you're `children` prop
260+
- `stateAndHelpers`: This is the exact same thing your `children` prop
254261
function is called with (see [Child Callback Function](#child-callback-function))
255262

256263
> Tip: This function will be called any time _any_ state is changed. The best
@@ -660,7 +667,8 @@ Thanks goes to these people ([emoji key][emojis]):
660667
| [<img src="https://avatars1.githubusercontent.com/u/7330124?v=4" width="100px;"/><br /><sub>Geoff Davis</sub>](https://geoffdavis.info)<br />[💡](#example-geoffdavis92 "Examples") | [<img src="https://avatars0.githubusercontent.com/u/3415488?v=4" width="100px;"/><br /><sub>Anup</sub>](https://github.com/reznord)<br />[📖](https://github.com/paypal/downshift/commits?author=reznord "Documentation") | [<img src="https://avatars0.githubusercontent.com/u/340520?v=4" width="100px;"/><br /><sub>Ferdinand Salis</sub>](http://ferdinandsalis.com)<br />[🐛](https://github.com/paypal/downshift/issues?q=author%3Aferdinandsalis "Bug reports") [💻](https://github.com/paypal/downshift/commits?author=ferdinandsalis "Code") | [<img src="https://avatars2.githubusercontent.com/u/662750?v=4" width="100px;"/><br /><sub>Kye Hohenberger</sub>](https://github.com/tkh44)<br />[🐛](https://github.com/paypal/downshift/issues?q=author%3Atkh44 "Bug reports") | [<img src="https://avatars0.githubusercontent.com/u/1443499?v=4" width="100px;"/><br /><sub>Julien Goux</sub>](https://github.com/jgoux)<br />[🐛](https://github.com/paypal/downshift/issues?q=author%3Ajgoux "Bug reports") [💻](https://github.com/paypal/downshift/commits?author=jgoux "Code") [⚠️](https://github.com/paypal/downshift/commits?author=jgoux "Tests") | [<img src="https://avatars2.githubusercontent.com/u/9586897?v=4" width="100px;"/><br /><sub>Joachim Seminck</sub>](https://github.com/jseminck)<br />[💻](https://github.com/paypal/downshift/commits?author=jseminck "Code") | [<img src="https://avatars3.githubusercontent.com/u/954596?v=4" width="100px;"/><br /><sub>Jesse Harlin</sub>](http://jesseharlin.net/)<br />[🐛](https://github.com/paypal/downshift/issues?q=author%3Athe-simian "Bug reports") [💡](#example-the-simian "Examples") |
661668
| [<img src="https://avatars0.githubusercontent.com/u/1402095?v=4" width="100px;"/><br /><sub>Matt Parrish</sub>](https://github.com/pbomb)<br />[🔧](#tool-pbomb "Tools") [👀](#review-pbomb "Reviewed Pull Requests") | [<img src="https://avatars1.githubusercontent.com/u/11661846?v=4" width="100px;"/><br /><sub>thom</sub>](http://thom.kr)<br />[💻](https://github.com/paypal/downshift/commits?author=thomhos "Code") | [<img src="https://avatars2.githubusercontent.com/u/1088312?v=4" width="100px;"/><br /><sub>Vu Tran</sub>](http://twitter.com/tranvu)<br />[💻](https://github.com/paypal/downshift/commits?author=vutran "Code") | [<img src="https://avatars1.githubusercontent.com/u/74193?v=4" width="100px;"/><br /><sub>Codie Mullins</sub>](https://github.com/codiemullins)<br />[💻](https://github.com/paypal/downshift/commits?author=codiemullins "Code") [💡](#example-codiemullins "Examples") | [<img src="https://avatars3.githubusercontent.com/u/12202757?v=4" width="100px;"/><br /><sub>Mohammad Rajabifard</sub>](https://morajabi.me)<br />[📖](https://github.com/paypal/downshift/commits?author=morajabi "Documentation") [🤔](#ideas-morajabi "Ideas, Planning, & Feedback") | [<img src="https://avatars3.githubusercontent.com/u/9488719?v=4" width="100px;"/><br /><sub>Frank Tan</sub>](https://github.com/tansongyang)<br />[💻](https://github.com/paypal/downshift/commits?author=tansongyang "Code") | [<img src="https://avatars3.githubusercontent.com/u/5093058?v=4" width="100px;"/><br /><sub>Kier Borromeo</sub>](https://kierb.com)<br />[💡](#example-srph "Examples") |
662669
| [<img src="https://avatars1.githubusercontent.com/u/8969456?v=4" width="100px;"/><br /><sub>Paul Veevers</sub>](https://github.com/paul-veevers)<br />[💻](https://github.com/paypal/downshift/commits?author=paul-veevers "Code") | [<img src="https://avatars2.githubusercontent.com/u/13622298?v=4" width="100px;"/><br /><sub>Ron Cruz</sub>](https://github.com/Ronolibert)<br />[📖](https://github.com/paypal/downshift/commits?author=Ronolibert "Documentation") | [<img src="https://avatars1.githubusercontent.com/u/13605633?v=4" width="100px;"/><br /><sub>Rick McGavin</sub>](http://rickmcgavin.github.io)<br />[📖](https://github.com/paypal/downshift/commits?author=rickMcGavin "Documentation") | [<img src="https://avatars0.githubusercontent.com/u/869669?v=4" width="100px;"/><br /><sub>Jelle Versele</sub>](http://twitter.com/vejersele)<br />[💡](#example-vejersele "Examples") | [<img src="https://avatars1.githubusercontent.com/u/202773?v=4" width="100px;"/><br /><sub>Brent Ertz</sub>](https://github.com/brentertz)<br />[🤔](#ideas-brentertz "Ideas, Planning, & Feedback") | [<img src="https://avatars3.githubusercontent.com/u/8015514?v=4" width="100px;"/><br /><sub>Justice Mba </sub>](https://github.com/Dajust)<br />[💻](https://github.com/paypal/downshift/commits?author=Dajust "Code") [📖](https://github.com/paypal/downshift/commits?author=Dajust "Documentation") [🤔](#ideas-Dajust "Ideas, Planning, & Feedback") | [<img src="https://avatars2.githubusercontent.com/u/3925281?v=4" width="100px;"/><br /><sub>Mark Ellis</sub>](http://mfellis.com)<br />[🤔](#ideas-ellismarkf "Ideas, Planning, & Feedback") |
663-
| [<img src="https://avatars1.githubusercontent.com/u/3241922?v=4" width="100px;"/><br /><sub>us͡an̸df͘rien͜ds͠</sub>](http://ronak.io/)<br />[🐛](https://github.com/paypal/downshift/issues?q=author%3Ausandfriends "Bug reports") [💻](https://github.com/paypal/downshift/commits?author=usandfriends "Code") [⚠️](https://github.com/paypal/downshift/commits?author=usandfriends "Tests") | [<img src="https://avatars0.githubusercontent.com/u/474248?v=4" width="100px;"/><br /><sub>Robin Drexler</sub>](https://www.robin-drexler.com/)<br />[🐛](https://github.com/paypal/downshift/issues?q=author%3Arobin-drexler "Bug reports") [💻](https://github.com/paypal/downshift/commits?author=robin-drexler "Code") | [<img src="https://avatars0.githubusercontent.com/u/7406639?v=4" width="100px;"/><br /><sub>Arturo Romero</sub>](http://arturoromero.info/)<br />[💡](#example-arturoromeroslc "Examples") | [<img src="https://avatars1.githubusercontent.com/u/275483?v=4" width="100px;"/><br /><sub>yp</sub>](http://algolab.eu/pirola)<br />[🐛](https://github.com/paypal/downshift/issues?q=author%3Ayp "Bug reports") [💻](https://github.com/paypal/downshift/commits?author=yp "Code") [⚠️](https://github.com/paypal/downshift/commits?author=yp "Tests") |
670+
| [<img src="https://avatars1.githubusercontent.com/u/3241922?v=4" width="100px;"/><br /><sub>us͡an̸df͘rien͜ds͠</sub>](http://ronak.io/)<br />[🐛](https://github.com/paypal/downshift/issues?q=author%3Ausandfriends "Bug reports") [💻](https://github.com/paypal/downshift/commits?author=usandfriends "Code") [⚠️](https://github.com/paypal/downshift/commits?author=usandfriends "Tests") | [<img src="https://avatars0.githubusercontent.com/u/474248?v=4" width="100px;"/><br /><sub>Robin Drexler</sub>](https://www.robin-drexler.com/)<br />[🐛](https://github.com/paypal/downshift/issues?q=author%3Arobin-drexler "Bug reports") [💻](https://github.com/paypal/downshift/commits?author=robin-drexler "Code") | [<img src="https://avatars0.githubusercontent.com/u/7406639?v=4" width="100px;"/><br /><sub>Arturo Romero</sub>](http://arturoromero.info/)<br />[💡](#example-arturoromeroslc "Examples") | [<img src="https://avatars1.githubusercontent.com/u/275483?v=4" width="100px;"/><br /><sub>yp</sub>](http://algolab.eu/pirola)<br />[🐛](https://github.com/paypal/downshift/issues?q=author%3Ayp "Bug reports") [💻](https://github.com/paypal/downshift/commits?author=yp "Code") [⚠️](https://github.com/paypal/downshift/commits?author=yp "Tests") | [<img src="https://avatars0.githubusercontent.com/u/3998604?v=4" width="100px;"/><br /><sub>Dave Garwacke</sub>](http://www.warbyparker.com)<br />[📖](https://github.com/paypal/downshift/commits?author=ifyoumakeit "Documentation") | [<img src="https://avatars3.githubusercontent.com/u/11758660?v=4" width="100px;"/><br /><sub>Ivan Pazhitnykh</sub>](http://linkedin.com/in/drapegnik)<br />[💻](https://github.com/paypal/downshift/commits?author=Drapegnik "Code") [⚠️](https://github.com/paypal/downshift/commits?author=Drapegnik "Tests") | [<img src="https://avatars0.githubusercontent.com/u/61776?v=4" width="100px;"/><br /><sub>Luis Merino</sub>](https://github.com/Rendez)<br />[📖](https://github.com/paypal/downshift/commits?author=Rendez "Documentation") |
671+
| [<img src="https://avatars0.githubusercontent.com/u/8746094?v=4" width="100px;"/><br /><sub>Andrew Hansen</sub>](http://twitter.com/arahansen)<br />[💻](https://github.com/paypal/downshift/commits?author=arahansen "Code") [⚠️](https://github.com/paypal/downshift/commits?author=arahansen "Tests") |
664672
<!-- ALL-CONTRIBUTORS-LIST:END -->
665673

666674
This project follows the [all-contributors][all-contributors] specification.

src/__tests__/downshift.lifecycle.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,22 @@ test('props update of selectedItem will update the inputValue state', () => {
9292
)
9393
})
9494

95+
test('props update of selectedItem will not update inputValue state', () => {
96+
const onInputValueChangeSpy = jest.fn(() => null)
97+
const wrapper = mount(
98+
<Downshift
99+
onInputValueChange={onInputValueChangeSpy}
100+
selectedItemChanged={(prevItem, item) => prevItem.id !== item.id}
101+
selectedItem={{id: '123', value: 'wow'}}
102+
>
103+
{() => null}
104+
</Downshift>,
105+
)
106+
onInputValueChangeSpy.mockClear()
107+
wrapper.setProps({selectedItem: {id: '123', value: 'not wow'}})
108+
expect(onInputValueChangeSpy).not.toHaveBeenCalled()
109+
})
110+
95111
function mouseDownAndUp(node) {
96112
node.dispatchEvent(new window.MouseEvent('mousedown', {bubbles: true}))
97113
node.dispatchEvent(new window.MouseEvent('mouseup', {bubbles: true}))

src/__tests__/downshift.misc.js

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,24 @@ test('selectItemAtIndex does nothing if there is no item at that index', () => {
3232
expect(childSpy).not.toHaveBeenCalled()
3333
})
3434

35+
test('selectItemAtIndex can select item that is an empty string', () => {
36+
const items = ['Chess', '']
37+
const children = ({getItemProps}) => (
38+
<div>
39+
{items.map((item, index) => (
40+
<div key={index} {...getItemProps({item})}>
41+
{item}
42+
</div>
43+
))}
44+
</div>
45+
)
46+
const {selectItemAtIndex, childSpy} = setup({children})
47+
selectItemAtIndex(1)
48+
expect(childSpy).toHaveBeenLastCalledWith(
49+
expect.objectContaining({selectedItem: ''}),
50+
)
51+
})
52+
3553
test('clearSelection with an input node focuses the input node', () => {
3654
const children = ({getInputProps}) => (
3755
<div>
@@ -57,8 +75,15 @@ test('toggleMenu can take no arguments at all', () => {
5775
})
5876

5977
test('clearItems clears the all items', () => {
60-
const items = ['Chess']
61-
const {wrapper, clearItems} = setup({items})
78+
const item = 'Chess'
79+
const children = ({getItemProps}) => (
80+
<div>
81+
<div key={item} {...getItemProps({item})}>
82+
{item}
83+
</div>
84+
</div>
85+
)
86+
const {wrapper, clearItems} = setup({children})
6287
clearItems()
6388
expect(wrapper.instance().items).toEqual([])
6489
})

src/__tests__/downshift.props.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ test('onStateChange called with changes and downshift state and helpers', () =>
3434
)
3535
})
3636

37-
test('onChange called when clearSelection is trigered', () => {
37+
test('onChange called when clearSelection is triggered', () => {
3838
const handleChange = jest.fn()
3939
const {clearSelection} = setup({
4040
selectedItem: 'foo',
@@ -127,6 +127,7 @@ test('onInputValueChange not called when changes do not contain inputValue', ()
127127
const {openMenu} = setup({
128128
onInputValueChange: handleInputValueChange,
129129
})
130+
130131
openMenu()
131132

132133
expect(handleInputValueChange).toHaveBeenCalledTimes(0)

src/__tests__/utils.findParent.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import {findParent} from '../utils'
2+
3+
test('return documentElement when founded node is document.body and scrollTop is 0', () => {
4+
const node = document.createElement('div')
5+
const parentNode = document.body
6+
parentNode.appendChild(node)
7+
const scrollParent = findParent(el => el === parentNode, node, parentNode)
8+
expect(scrollParent).toBe(document.documentElement)
9+
})

src/__tests__/utils.scroll-into-view.js

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,21 @@ test('does nothing if the node is within the scrollable area', () => {
2020
expect(scrollableNode.scrollTop).toBe(scrollableScrollTop)
2121
})
2222

23+
test('does nothing if parent.top is above view area and node within view', () => {
24+
const scrollableScrollTop = 1000
25+
const node = getNode({height: 40, top: 300})
26+
// parent bounds is [-1000 + 1000, -500 + 1000] = [0, 500]
27+
// node bounds is [300, 340] => node within visible area
28+
const scrollableNode = getScrollableNode({
29+
top: -1000,
30+
height: 500,
31+
scrollTop: scrollableScrollTop,
32+
children: [node],
33+
})
34+
scrollIntoView(node, scrollableNode)
35+
expect(scrollableNode.scrollTop).toBe(scrollableScrollTop)
36+
})
37+
2338
test('aligns to top when the node is above the scrollable parent', () => {
2439
// TODO: make this test a tiny bit more readable/maintainable...
2540
const nodeTop = 2
@@ -34,6 +49,29 @@ test('aligns to top when the node is above the scrollable parent', () => {
3449
expect(scrollableNode.scrollTop).toBe(5)
3550
})
3651

52+
test('aligns to top when the node is above view area', () => {
53+
const node = getNode({height: 40, top: -15})
54+
const scrollableNode = getScrollableNode({
55+
top: -50,
56+
scrollTop: 100,
57+
children: [node],
58+
})
59+
scrollIntoView(node, scrollableNode)
60+
expect(scrollableNode.scrollTop).toBe(85)
61+
})
62+
63+
test('aligns to bottom when the node is below the scrollable parent and parent top above view area', () => {
64+
const node = getNode({height: 40, top: 280})
65+
const scrollableNode = getScrollableNode({
66+
top: -60,
67+
height: 360,
68+
scrollTop: 28,
69+
children: [node],
70+
})
71+
scrollIntoView(node, scrollableNode)
72+
expect(scrollableNode.scrollTop).toBe(48)
73+
})
74+
3775
test('aligns to bottom when the node is below the scrollable parent', () => {
3876
const nodeTop = 115
3977
const node = getNode({height: 10, top: nodeTop})
@@ -88,7 +126,7 @@ function getNode(
88126
top,
89127
left: 0,
90128
right: 50,
91-
bottom: height,
129+
bottom: top + height,
92130
})
93131
div.style.borderTopWidth = borderTopWidth
94132
div.style.borderBottomWidth = borderBottomWidth

src/downshift.js

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ class Downshift extends Component {
3636
onUserAction: PropTypes.func,
3737
onClick: PropTypes.func,
3838
onOuterClick: PropTypes.func,
39+
selectedItemChanged: PropTypes.func,
3940
itemCount: PropTypes.number,
4041
id: PropTypes.string,
4142
environment: PropTypes.shape({
@@ -69,6 +70,7 @@ class Downshift extends Component {
6970
onChange: () => {},
7071
onSelect: () => {},
7172
onOuterClick: () => {},
73+
selectedItemChanged: (prevItem, item) => prevItem !== item,
7274
environment:
7375
typeof window === 'undefined' /* istanbul ignore next (ssr) */
7476
? {}
@@ -247,7 +249,7 @@ class Downshift extends Component {
247249

248250
selectItemAtIndex = (itemIndex, otherStateToSet, cb) => {
249251
const item = this.items[itemIndex]
250-
if (!item) {
252+
if (item == null) {
251253
return
252254
}
253255
this.selectItem(item, otherStateToSet, cb)
@@ -746,7 +748,10 @@ class Downshift extends Component {
746748
componentDidUpdate(prevProps) {
747749
if (
748750
this.isControlledProp('selectedItem') &&
749-
this.props.selectedItem !== prevProps.selectedItem
751+
this.props.selectedItemChanged(
752+
prevProps.selectedItem,
753+
this.props.selectedItem,
754+
)
750755
) {
751756
this.internalSetState({
752757
type: Downshift.stateChangeTypes.controlledPropUpdatedSelectedItem,

0 commit comments

Comments
 (0)