Skip to content

Commit 028e05f

Browse files
committed
Merge pull request #11 from salesforce-ux/lookups-refactor
Lookups refactor
2 parents f33e84d + 68c4199 commit 028e05f

File tree

5 files changed

+86
-45
lines changed

5 files changed

+86
-45
lines changed

components/SLDSLookup/Menu/Item/index.jsx

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ class Item extends React.Component {
1717
}
1818

1919
componentWillReceiveProps(nextProps){
20-
if(nextProps.isActive !== this.props.isActive){
21-
this.props.setActiveDescendant(nextProps.id);
20+
if(nextProps.isActive !== this.props.isActive && (nextProps.isActive === true)){
21+
this.props.setFocus(this.props.id);
2222
}
2323
}
2424

@@ -42,17 +42,13 @@ class Item extends React.Component {
4242

4343
render(){
4444
let className = 'slds-lookup__item';
45-
46-
//TODO: make isActive styles into a class??
4745
let id = this.props.id;
48-
let styles = {};
4946
if(this.props.isActive) className += ' slds-theme--shade';
5047

5148
return (
52-
//IMPORTANT: id is used to set lookup's input's aria-activedescendant
49+
//IMPORTANT: anchor id is used to set lookup's input's aria-activedescendant
5350
<li
5451
className={className}
55-
style={styles}
5652
role="presentaion">
5753
<a
5854
href={this.props.href}
@@ -68,7 +64,17 @@ class Item extends React.Component {
6864
</li>
6965
)
7066
}
71-
7267
}
7368

69+
Item.propTypes = {
70+
id: React.PropTypes.string,
71+
setFocus: React.PropTypes.func,
72+
isActive: React.PropTypes.bool,
73+
onSelect: React.PropTypes.func,
74+
searchTerm: React.PropTypes.string,
75+
};
76+
77+
Item.defaultProps = {
78+
};
79+
7480
module.exports = Item;

components/SLDSLookup/Menu/index.js

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,20 @@ class Menu extends React.Component {
1818
this.state = {};
1919
}
2020

21+
componentDidUpdate(){
22+
let list = React.findDOMNode(this.refs.list).children.length;
23+
this.props.getListLength(list);
24+
}
25+
2126
filter(item){
2227
return this.props.filterWith(this.props.searchTerm, item);
2328
}
2429

2530
renderItems(){
2631
return this.props.items.filter(this.filter, this).map((c, i) => {
2732
//isActive means it is aria-activedescendant
28-
const isActive = this.props.activeIndex === i ? true : false;
29-
return <Item key={c.id} id={c.id} setActiveDescendant={this.props.setActiveDescendant} isActive={isActive} onSelect={this.props.onSelect} searchTerm={this.props.searchTerm}>{c}</Item>
33+
const isActive = this.props.focusIndex === i ? true : false;
34+
return <Item key={c.id} id={c.id} setFocus={this.props.setFocus} isActive={isActive} onSelect={this.props.onSelect} searchTerm={this.props.searchTerm}>{c}</Item>
3035
});
3136
}
3237

@@ -42,4 +47,20 @@ class Menu extends React.Component {
4247
)
4348
}
4449
}
50+
51+
Menu.propTypes = {
52+
searchTerm: React.PropTypes.string,
53+
filterWith: React.PropTypes.func,
54+
onSelect: React.PropTypes.func,
55+
label: React.PropTypes.string,
56+
items: React.PropTypes.array,
57+
setFocus: React.PropTypes.func,
58+
getListLength: React.PropTypes.func,
59+
listLength: React.PropTypes.number,
60+
focusIndex: React.PropTypes.number,
61+
};
62+
63+
Menu.defaultProps = {
64+
};
65+
4566
module.exports = Menu;

components/SLDSLookup/index.jsx

Lines changed: 42 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -22,41 +22,46 @@ const defaultFilter = (term, item) => {
2222
class SLDSLookup extends React.Component {
2323
constructor(props) {
2424
super(props);
25-
this.props.items.map((item, index) => {
26-
return item.id = 'item-' + index;
27-
})
25+
26+
//Dynamically assign ids to list items to reference for focusing and selecting items
27+
this.props.items.map((item, index) => { return item.id = 'item-' + index; })
2828

2929
this.state = {
3030
searchTerm: '',
3131
isOpen:false,
32-
activeItem:null,
32+
currentFocus:null,
33+
focusIndex:null,
3334
selectedIndex: null,
34-
activeIndex:null,
35+
listLength:this.props.items.length
3536
};
3637
}
3738

3839
//=================================================
39-
// Set Active Descendant (on key down/up, set currently focused/hovered item in list)
40+
// Using down/up keys, set Focus on list item and assign it to aria-activedescendant attribute in input.
41+
// Need to keep track of filtered list length to be able to increment/decrement the focus index so it's contained to the number of available list items.
4042
increaseIndex(){
41-
this.setState({
42-
activeIndex: this.state.activeIndex <= this.props.items.length ? this.state.activeIndex + 1 : 0
43-
})
43+
let items = this.state.listLength - 1;
44+
this.setState({ focusIndex: this.state.focusIndex < items ? this.state.focusIndex + 1 : 0 })
4445
}
4546

4647
decreaseIndex(){
47-
this.setState({
48-
activeIndex: this.state.activeIndex > 0 ? this.state.activeIndex - 1 : this.props.items.length
49-
})
48+
let items = this.state.listLength - 1;
49+
this.setState({ focusIndex: this.state.focusIndex > 0 ? this.state.focusIndex - 1 : items })
5050
}
5151

52-
setActiveDescendant(id){
53-
this.setState({activeItem:id});
52+
setFocus(id){
53+
this.setState({currentFocus:id});
54+
}
55+
56+
getListLength(qty){
57+
if(qty !== this.state.listLength){
58+
this.setState({listLength:qty});
59+
}
5460
}
5561

5662
//=================================================
5763
// Select menu item (onClick or on key enter/space)
5864
selectItem(itemId){
59-
//console.log('selectItem fired');
6065
let index = itemId.replace('item-', '');
6166
this.setState({
6267
selectedIndex: index,
@@ -76,7 +81,7 @@ class SLDSLookup extends React.Component {
7681
handleClose() {
7782
this.setState({
7883
isOpen:false,
79-
activeIndex:null
84+
focusIndex:null
8085
})
8186
}
8287

@@ -103,25 +108,25 @@ class SLDSLookup extends React.Component {
103108
event.keyCode === KEYS.ESCAPE ? this.handleClose() : this.handleClick();
104109

105110
//If user hits tab key, move aria activedescendant to first menu item
106-
if(event.keyCode === KEYS.TAB && this.state.activeIndex === null){
107-
this.setState({activeIndex: 0});
111+
if(event.keyCode === KEYS.TAB && this.state.focusIndex === null){
112+
this.setState({focusIndex: 0});
108113
EventUtil.trapImmediate(event);
109114
}
110115
//If user hits down key, advance aria activedescendant to next item
111-
else if(event.keyCode === KEYS.DOWN && this.state.activeIndex !== null){
116+
else if(event.keyCode === KEYS.DOWN && this.state.focusIndex !== null){
112117
EventUtil.trapImmediate(event);
113118
this.increaseIndex();
114119
}
115120
//If user hits up key, advance aria activedescendant to previous item
116-
else if(event.keyCode === KEYS.UP && this.state.activeIndex !== null){
121+
else if(event.keyCode === KEYS.UP && this.state.focusIndex !== null){
117122
EventUtil.trapImmediate(event);
118123
this.decreaseIndex();
119124
}
120125

121126
//If user hits enter/space key, select current activedescendant item
122-
else if((event.keyCode === KEYS.ENTER || event.keyCode === KEYS.SPACE) && this.state.activeIndex !== null){
127+
else if((event.keyCode === KEYS.ENTER || event.keyCode === KEYS.SPACE) && this.state.focusIndex !== null){
123128
EventUtil.trapImmediate(event);
124-
this.selectItem(this.state.activeItem);
129+
this.selectItem(this.state.currentFocus);
125130
}
126131
}
127132
}
@@ -136,8 +141,10 @@ class SLDSLookup extends React.Component {
136141
onSelect={this.selectItem.bind(this)}
137142
label={this.props.label}
138143
items={this.props.items}
139-
setActiveDescendant={this.setActiveDescendant.bind(this)}
140-
activeIndex={this.state.activeIndex}/>;
144+
setFocus={this.setFocus.bind(this)}
145+
getListLength={this.getListLength.bind(this)}
146+
listLength={this.state.listLength}
147+
focusIndex={this.state.focusIndex}/>;
141148
}else{
142149
return null;
143150
}
@@ -162,10 +169,10 @@ class SLDSLookup extends React.Component {
162169

163170
render(){
164171
let inputClasses = this.state.selectedIndex === null ? 'slds-input':'slds-input slds-hide';
165-
let compClasses = this.state.selectedIndex === null ? "slds-lookup ignore-react-onclickoutside":"slds-lookup ignore-react-onclickoutside slds-has-selection";
172+
let componentClasses = this.state.selectedIndex === null ? "slds-lookup ignore-react-onclickoutside":"slds-lookup ignore-react-onclickoutside slds-has-selection";
166173

167174
return (
168-
<div className={compClasses} data-select="single" data-scope="single" data-typeahead="true">
175+
<div className={componentClasses} data-select="single" data-scope="single" data-typeahead="true">
169176
<section className="slds-form-element">
170177
<label className="slds-form-element__label" forHTML="lookup">{this.props.label}</label>
171178

@@ -179,7 +186,7 @@ class SLDSLookup extends React.Component {
179186
aria-label="lookup"
180187
aria-haspopup="true"
181188
aria-autocomplete="list"
182-
aria-activedescendant={this.state.activeItem ? this.state.activeItem:""}
189+
aria-activedescendant={this.state.currentFocus ? this.state.currentFocus:""}
183190
aria-expanded={this.state.isOpen}
184191
role="combobox"
185192
onChange={this.handleChange.bind(this)}
@@ -197,11 +204,18 @@ class SLDSLookup extends React.Component {
197204
}
198205
}
199206

207+
208+
SLDSLookup.propTypes = {
209+
items: React.PropTypes.array,
210+
label: React.PropTypes.string,
211+
};
212+
200213
SLDSLookup.defaultProps = {
201214
filterWith: defaultFilter,
202215
onItemSelect: function(item){
203-
//console.log('onItemSelect should be defined');
216+
//console.log('onItemSelect should be defined');
204217
}
205218
};
219+
206220
module.exports = SLDSLookup;
207221

components/SLDSModal/index.jsx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ module.exports = React.createClass( {
5959

6060
componentDidMount () {
6161
Modal.setAppElement(SLDSSettings.getAppElement());
62-
console.log('!!! window.activeElement !!! ',document.activeElement);
62+
//console.log('!!! window.activeElement !!! ',document.activeElement);
6363
this.setState({returnFocusTo:document.activeElement})
6464
if(!this.state.revealed){
6565
setTimeout(()=>{
@@ -101,14 +101,14 @@ module.exports = React.createClass( {
101101
},
102102

103103
getModal() {
104-
return <div
105-
className={'slds-modal' +(this.state.revealed?' slds-fade-in-open':'')}
104+
return <div
105+
className={'slds-modal' +(this.state.revealed?' slds-fade-in-open':'')}
106106
style={{pointerEvents:'inherit'}}
107107
onClick={this.closeModal}
108108
>
109-
<div
109+
<div
110110
role='dialog'
111-
className='slds-modal__container'
111+
className='slds-modal__container'
112112
onClick={this.handleModalClick}
113113
>
114114
<div className='slds-modal__header'>
@@ -157,7 +157,7 @@ module.exports = React.createClass( {
157157

158158

159159
if(this.state.isClosing){
160-
console.log('CLOSING: ');
160+
//console.log('CLOSING: ');
161161

162162
if(this.isMounted()){
163163
const el = this.getDOMNode().parentNode;

demo/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ const { Route, DefaultRoute, RouteHandler, Link } = Router;
77
import { SLDSSettings } from '../components/';
88
SLDSSettings.setAssetsPath('demo/assets/');
99
SLDSSettings.setAppElement('#root');
10-
console.log('SLDSSettings.getAssetsPath: '+SLDSSettings.getAssetsPath());
10+
//console.log('SLDSSettings.getAssetsPath: '+SLDSSettings.getAssetsPath());
1111

1212
import App from './App';
1313
import HomePage from './pages/HomePage';

0 commit comments

Comments
 (0)