Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 917ed28

Browse files
committedJan 19, 2020
fix: Append clientId to tag, remove button to make them unique
Currently, having multiple component instances on same page renders same remove-button, tag ids. Fixes #315
1 parent c5bfa8e commit 917ed28

File tree

10 files changed

+34
-27
lines changed

10 files changed

+34
-27
lines changed
 

‎__snapshots__/src/tag/index.test.js.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,14 @@ Generated by [AVA](https://ava.li).
1111
<span
1212
"aria-label"="hello"
1313
className="tag"
14-
id="abc_tag"
14+
id="rdts_abc_tag"
1515
>
1616
hello
1717
<button
1818
"aria-label"="Remove"
19-
"aria-labelledby"="abc_button abc_tag"
19+
"aria-labelledby"="rdts_abc_button rdts_abc_tag"
2020
className="tag-remove"
21-
id="abc_button"
21+
id="rdts_abc_button"
2222
onClick={Function {}}
2323
onKeyDown={Function {}}
2424
onKeyUp={Function {}}
5 Bytes
Binary file not shown.

‎src/index.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -153,11 +153,15 @@ class DropdownTreeSelect extends Component {
153153
}
154154

155155
onTagRemove = (id, isKeyboardEvent) => {
156-
const { tags: prevTags } = this.state
156+
const {
157+
clientId,
158+
searchInput,
159+
state: { tags: prevTags },
160+
} = this
157161
this.onCheckboxChange(id, false, tags => {
158162
if (!isKeyboardEvent) return
159163

160-
keyboardNavigation.getNextFocusAfterTagDelete(id, prevTags, tags, this.searchInput).focus()
164+
keyboardNavigation.getNextFocusAfterTagDelete({ id, prevTags, tags, fallback: searchInput, clientId }).focus()
161165
})
162166
}
163167

‎src/index.keyboardNav.test.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -134,15 +134,15 @@ test('can select with enter on keyboardNavigation', t => {
134134

135135
test('can delete tags with backspace/delete on keyboardNavigation', t => {
136136
const data = [{ ...node('a', 'a'), checked: true }, { ...node('b', 'b'), checked: true }]
137-
const wrapper = mount(<DropdownTreeSelect data={data} />)
137+
const wrapper = mount(<DropdownTreeSelect id="rdts" data={data} />)
138138
const event = {
139139
type: 'keydown',
140140
stopPropagation: spy(),
141141
nativeEvent: { stopImmediatePropagation: spy() },
142142
}
143-
wrapper.find('#a_tag > .tag-remove').simulate('keyDown', { ...event, key: 'Backspace' })
143+
wrapper.find('#rdts_a_tag > .tag-remove').simulate('keyDown', { ...event, key: 'Backspace' })
144144
t.deepEqual(wrapper.state().tags.length, 1)
145-
wrapper.find('#b_tag > .tag-remove').simulate('keyUp', { ...event, key: 'Delete' })
145+
wrapper.find('#rdts_b_tag > .tag-remove').simulate('keyUp', { ...event, key: 'Delete' })
146146
t.deepEqual(wrapper.state().tags.length, 0)
147147
})
148148

‎src/index.test.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -301,14 +301,14 @@ test('appends selected tags to aria-labelledby with provided aria-labelledby', t
301301
const { tree } = t.context
302302
tree[0].checked = true
303303
const wrapper = mount(<DropdownTreeSelect id="rdts" data={tree} texts={{ label: '#hello #world' }} />)
304-
t.deepEqual(wrapper.find('.dropdown-trigger').prop('aria-labelledby'), 'hello world rdts-0_tag')
304+
t.deepEqual(wrapper.find('.dropdown-trigger').prop('aria-labelledby'), 'hello world rdts_rdts-0_tag')
305305
t.deepEqual(wrapper.find('.dropdown-trigger').prop('aria-label'), undefined)
306306
})
307307

308308
test('appends selected tags to aria-labelledby with text label', t => {
309309
const { tree } = t.context
310310
tree[0].checked = true
311311
const wrapper = mount(<DropdownTreeSelect id="rdts" data={tree} texts={{ label: 'hello world' }} />)
312-
t.deepEqual(wrapper.find('.dropdown-trigger').prop('aria-labelledby'), 'rdts_trigger rdts-0_tag')
312+
t.deepEqual(wrapper.find('.dropdown-trigger').prop('aria-labelledby'), 'rdts_trigger rdts_rdts-0_tag')
313313
t.deepEqual(wrapper.find('.dropdown-trigger').prop('aria-label'), 'hello world')
314314
})

‎src/tag/index.js

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,12 @@ import React, { PureComponent } from 'react'
33

44
import './index.css'
55

6-
export const getTagId = id => `${id}_tag`
6+
export const getTagId = ({ id, clientId }) => `${clientId}_${id}_tag`
77

88
class Tag extends PureComponent {
99
static propTypes = {
1010
id: PropTypes.string.isRequired,
11+
clientId: PropTypes.string.isRequired,
1112
label: PropTypes.string.isRequired,
1213
onDelete: PropTypes.func,
1314
readOnly: PropTypes.bool,
@@ -37,10 +38,10 @@ class Tag extends PureComponent {
3738
}
3839

3940
render() {
40-
const { id, label, labelRemove = 'Remove', readOnly, disabled } = this.props
41+
const { id, label, labelRemove = 'Remove', readOnly, disabled, clientId } = this.props
4142

42-
const tagId = getTagId(id)
43-
const buttonId = `${id}_button`
43+
const tagId = getTagId({ id, clientId })
44+
const buttonId = `${clientId}_${id}_button`
4445
const className = ['tag-remove', readOnly && 'readOnly', disabled && 'disabled'].filter(Boolean).join(' ')
4546
const isDisabled = readOnly || disabled
4647

‎src/tag/index.test.js

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,13 @@ import Tag from './index'
99
const mockEvent = { nativeEvent: { stopImmediatePropagation: spy() } }
1010

1111
test('renders label when passed in', t => {
12-
const wrapper = toJson(shallow(<Tag label="hello" id="abc" />))
12+
const wrapper = toJson(shallow(<Tag label="hello" clientId="rdts" id="abc" />))
1313
t.snapshot(wrapper)
1414
})
1515

1616
test('call stopPropagation and stopImmediatePropagation when pill is closed', t => {
1717
const onDelete = spy()
18-
const wrapper = mount(<Tag label="hello" id="abc" onDelete={onDelete} />)
18+
const wrapper = mount(<Tag label="hello" clientId="rdts" id="abc" onDelete={onDelete} />)
1919
const event = {
2020
type: 'click',
2121
stopPropagation: spy(),
@@ -28,21 +28,21 @@ test('call stopPropagation and stopImmediatePropagation when pill is closed', t
2828

2929
test('call onDelete handler when pill is closed', t => {
3030
const onDelete = spy()
31-
const wrapper = mount(<Tag label="hello" id="abc" onDelete={onDelete} />)
31+
const wrapper = mount(<Tag label="hello" clientId="rdts" id="abc" onDelete={onDelete} />)
3232
wrapper.find('.tag-remove').simulate('click', mockEvent)
3333
t.true(onDelete.calledWith('abc'))
3434
})
3535

3636
test('should not call onDelete when readOnly', t => {
3737
const onDelete = spy()
38-
const wrapper = mount(<Tag label="hello" id="abc" onDelete={onDelete} readOnly />)
38+
const wrapper = mount(<Tag label="hello" clientId="rdts" id="abc" onDelete={onDelete} readOnly />)
3939
wrapper.find('.tag-remove').simulate('click', mockEvent)
4040
t.true(onDelete.notCalled)
4141
})
4242

4343
test('should not call onDelete when disabled', t => {
4444
const onDelete = spy()
45-
const wrapper = mount(<Tag label="hello" id="abc" onDelete={onDelete} disabled />)
45+
const wrapper = mount(<Tag label="hello" clientId="rdts" id="abc" onDelete={onDelete} disabled />)
4646
wrapper.find('.tag-remove').simulate('click', mockEvent)
4747
t.true(onDelete.notCalled)
4848
})
@@ -52,7 +52,7 @@ test('should not cause form submit', t => {
5252
const onDelete = spy()
5353
const wrapper = mount(
5454
<form onSubmit={onSubmit}>
55-
<Tag label="hello" id="abc" onDelete={onDelete} />
55+
<Tag label="hello" clientId="rdts" id="abc" onDelete={onDelete} />
5656
</form>
5757
)
5858
wrapper.find('.tag-remove').simulate('click', mockEvent)

‎src/tags/index.js

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { getDataset } from '../utils'
55

66
import './index.css'
77

8-
const getTags = (tags = [], onDelete, readOnly, disabled, labelRemove) =>
8+
const getTags = (tags = [], onDelete, readOnly, disabled, labelRemove, clientId) =>
99
tags.map(tag => {
1010
const { _id, label, tagClassName, dataset } = tag
1111
return (
@@ -17,6 +17,7 @@ const getTags = (tags = [], onDelete, readOnly, disabled, labelRemove) =>
1717
<Tag
1818
label={label}
1919
id={_id}
20+
clientId={clientId}
2021
onDelete={onDelete}
2122
readOnly={readOnly}
2223
disabled={disabled}
@@ -34,15 +35,16 @@ class Tags extends PureComponent {
3435
disabled: PropTypes.bool,
3536
texts: PropTypes.object,
3637
children: PropTypes.node,
38+
clientId: PropTypes.string.isRequired,
3739
}
3840

3941
render() {
40-
const { tags, onTagRemove, texts = {}, disabled, readOnly, children } = this.props
42+
const { tags, onTagRemove, texts = {}, disabled, readOnly, children, clientId } = this.props
4143
const lastItem = children || <span className="placeholder">{texts.placeholder || 'Choose...'}</span>
4244

4345
return (
4446
<ul className="tag-list">
45-
{getTags(tags, onTagRemove, readOnly, disabled, texts.labelRemove)}
47+
{getTags(tags, onTagRemove, readOnly, disabled, texts.labelRemove, clientId)}
4648
<li className="tag-item">{lastItem}</li>
4749
</ul>
4850
)

‎src/tree-manager/keyboardNavigation.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -144,14 +144,14 @@ const getNextFocus = (tree, prevFocus, action, getNodeById, markSubTreeOnNonExpa
144144
return getRelativeFocus(nodes, prevFocus, action)
145145
}
146146

147-
const getNextFocusAfterTagDelete = (deletedId, prevTags, tags, fallback) => {
147+
const getNextFocusAfterTagDelete = ({ id, prevTags, tags, fallback, clientId }) => {
148148
// Sets new focus to next tag or returns fallback
149-
let index = prevTags && prevTags.findIndex(t => t._id === deletedId)
149+
let index = prevTags && prevTags.findIndex(t => t._id === id)
150150
if (index < 0 || !tags.length) return fallback
151151

152152
index = tags.length > index ? index : tags.length - 1
153153
const newFocusId = tags[index]._id
154-
const focusNode = document.getElementById(getTagId(newFocusId))
154+
const focusNode = document.getElementById(getTagId({ clientId, id: newFocusId }))
155155
if (focusNode) {
156156
return focusNode.firstElementChild || fallback
157157
}

‎src/trigger/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ class Trigger extends PureComponent {
2828
labelledBy.push(triggerId)
2929
}
3030
tags.forEach(t => {
31-
labelledBy.push(getTagId(t._id))
31+
labelledBy.push(getTagId({ clientId, id: t._id }))
3232
})
3333
labelAttributes = getAriaLabel(texts.label, labelledBy.join(' '))
3434
}

0 commit comments

Comments
 (0)
Please sign in to comment.