Merged in dropdown-accesibility (pull request #48)
Dropdown accesibility
This commit is contained in:
commit
be9919baef
|
@ -66,7 +66,7 @@
|
|||
"react-document-title": "^1.0.2",
|
||||
"react-dom": "^15.0.1",
|
||||
"react-google-recaptcha": "^0.5.2",
|
||||
"react-motion": "^0.3.0",
|
||||
"react-motion": "^0.4.4",
|
||||
"react-redux": "^4.4.5",
|
||||
"react-router": "^2.4.0",
|
||||
"react-router-redux": "^4.0.5",
|
||||
|
|
|
@ -0,0 +1,260 @@
|
|||
// LIBS
|
||||
const {Motion} = require('react-motion');
|
||||
const _ = require('lodash');
|
||||
|
||||
// MOCKS
|
||||
const Menu = ReactMock();
|
||||
const Icon = ReactMock();
|
||||
|
||||
// COMPONENT
|
||||
const DropDown = requireUnit('core-components/drop-down', {
|
||||
'core-components/menu': Menu,
|
||||
'core-components/icon': Icon
|
||||
});
|
||||
|
||||
describe('DropDown component', function () {
|
||||
let dropdown, menu, currentItem, menuMotion;
|
||||
|
||||
function renderDropDown(props) {
|
||||
let defaultProps = {
|
||||
items: [
|
||||
{content: 'First Item', icon: 'ICON_1'},
|
||||
{content: 'Second Item', icon: 'ICON_2'},
|
||||
{content: 'Third Item', icon: 'ICON_3'},
|
||||
{content: 'Fourth Item', icon: 'ICON_4'}
|
||||
],
|
||||
onChange: stub()
|
||||
};
|
||||
|
||||
|
||||
dropdown = TestUtils.renderIntoDocument(
|
||||
<DropDown {..._.extend(defaultProps, props)} />
|
||||
);
|
||||
menu = TestUtils.scryRenderedComponentsWithType(dropdown, Menu)[0];
|
||||
menuMotion = TestUtils.scryRenderedComponentsWithType(dropdown, Motion)[0];
|
||||
currentItem = TestUtils.scryRenderedDOMComponentsWithClass(dropdown, 'drop-down__current-item')[0];
|
||||
}
|
||||
|
||||
beforeEach(function() {
|
||||
renderDropDown();
|
||||
});
|
||||
|
||||
it('should render a current item and a Menu of items', function () {
|
||||
expect(ReactDOM.findDOMNode(dropdown).className).to.contain('drop-down');
|
||||
expect(ReactDOM.findDOMNode(dropdown).className).to.contain('drop-down_closed');
|
||||
|
||||
expect(currentItem.textContent).to.equal('First Item');
|
||||
expect(currentItem.getAttribute('aria-expanded')).to.equal('false');
|
||||
expect(currentItem.getAttribute('aria-autocomplete')).to.equal('list');
|
||||
expect(currentItem.getAttribute('role')).to.equal('combobox');
|
||||
expect(currentItem.getAttribute('tabindex')).to.equal('0');
|
||||
|
||||
expect(menuMotion.props.style.opacity.val).to.equal(0);
|
||||
expect(menu.props.role).to.equal('listbox');
|
||||
expect(menu.props.itemsRole).to.equal('option');
|
||||
expect(menu.props.items).to.equal(dropdown.props.items);
|
||||
expect(menu.props.selectedIndex).to.equal(0);
|
||||
});
|
||||
|
||||
it('should open/close list when click on current item', function () {
|
||||
TestUtils.Simulate.click(currentItem);
|
||||
|
||||
expect(menuMotion.props.style.opacity.val).to.equal(1);
|
||||
expect(ReactDOM.findDOMNode(dropdown).className).to.not.contain('drop-down_closed');
|
||||
expect(currentItem.getAttribute('aria-expanded')).to.equal('true');
|
||||
|
||||
TestUtils.Simulate.click(currentItem);
|
||||
|
||||
expect(menuMotion.props.style.opacity.val).to.equal(0);
|
||||
expect(ReactDOM.findDOMNode(dropdown).className).to.contain('drop-down_closed');
|
||||
expect(currentItem.getAttribute('aria-expanded')).to.equal('false');
|
||||
});
|
||||
|
||||
it('should open/close list when pressing Enter on current item', function () {
|
||||
TestUtils.Simulate.keyDown(currentItem, {key: 'Enter', keyCode: 13, which: 13});
|
||||
|
||||
expect(menuMotion.props.style.opacity.val).to.equal(1);
|
||||
expect(ReactDOM.findDOMNode(dropdown).className).to.not.contain('drop-down_closed');
|
||||
expect(currentItem.getAttribute('aria-expanded')).to.equal('true');
|
||||
|
||||
TestUtils.Simulate.keyDown(currentItem, {key: 'Enter', keyCode: 13, which: 13});
|
||||
|
||||
expect(menuMotion.props.style.opacity.val).to.equal(0);
|
||||
expect(ReactDOM.findDOMNode(dropdown).className).to.contain('drop-down_closed');
|
||||
expect(currentItem.getAttribute('aria-expanded')).to.equal('false');
|
||||
});
|
||||
|
||||
it('should open list but no close when pressing Space on current item', function () {
|
||||
TestUtils.Simulate.keyDown(currentItem, {key: 'Space', keyCode: 32, which: 32});
|
||||
|
||||
expect(menuMotion.props.style.opacity.val).to.equal(1);
|
||||
expect(ReactDOM.findDOMNode(dropdown).className).to.not.contain('drop-down_closed');
|
||||
expect(currentItem.getAttribute('aria-expanded')).to.equal('true');
|
||||
|
||||
TestUtils.Simulate.keyDown(currentItem, {key: 'Space', keyCode: 32, which: 32});
|
||||
|
||||
expect(menuMotion.props.style.opacity.val).to.equal(1);
|
||||
expect(ReactDOM.findDOMNode(dropdown).className).to.not.contain('drop-down_closed');
|
||||
expect(currentItem.getAttribute('aria-expanded')).to.equal('true');
|
||||
});
|
||||
|
||||
it('should close the list with escape on current item', function () {
|
||||
TestUtils.Simulate.keyDown(currentItem, {key: 'Space', keyCode: 32, which: 32});
|
||||
|
||||
expect(menuMotion.props.style.opacity.val).to.equal(1);
|
||||
expect(ReactDOM.findDOMNode(dropdown).className).to.not.contain('drop-down_closed');
|
||||
expect(currentItem.getAttribute('aria-expanded')).to.equal('true');
|
||||
|
||||
TestUtils.Simulate.keyDown(currentItem, {key: 'Esc', keyCode: 27, which: 27});
|
||||
|
||||
expect(menuMotion.props.style.opacity.val).to.equal(0);
|
||||
expect(ReactDOM.findDOMNode(dropdown).className).to.contain('drop-down_closed');
|
||||
expect(currentItem.getAttribute('aria-expanded')).to.equal('false');
|
||||
});
|
||||
|
||||
it('should close the list when current item loses focus', function () {
|
||||
TestUtils.Simulate.keyDown(currentItem, {key: 'Enter', keyCode: 13, which: 13});
|
||||
|
||||
expect(menuMotion.props.style.opacity.val).to.equal(1);
|
||||
expect(ReactDOM.findDOMNode(dropdown).className).to.not.contain('drop-down_closed');
|
||||
expect(currentItem.getAttribute('aria-expanded')).to.equal('true');
|
||||
|
||||
TestUtils.Simulate.blur(currentItem);
|
||||
|
||||
expect(menuMotion.props.style.opacity.val).to.equal(0);
|
||||
expect(ReactDOM.findDOMNode(dropdown).className).to.contain('drop-down_closed');
|
||||
expect(currentItem.getAttribute('aria-expanded')).to.equal('false');
|
||||
});
|
||||
|
||||
it('should change selection, close and call onChange when a menu item is clicked', function () {
|
||||
dropdown.props.onChange.reset();
|
||||
TestUtils.Simulate.keyDown(currentItem, {key: 'Space', keyCode: 32, which: 32});
|
||||
menu.props.onItemClick(2);
|
||||
|
||||
// Should be closed
|
||||
expect(menuMotion.props.style.opacity.val).to.equal(0);
|
||||
expect(ReactDOM.findDOMNode(dropdown).className).to.contain('drop-down_closed');
|
||||
expect(currentItem.getAttribute('aria-expanded')).to.equal('false');
|
||||
|
||||
expect(currentItem.textContent).to.equal('Third Item');
|
||||
expect(menu.props.selectedIndex).to.equal(2);
|
||||
expect(dropdown.props.onChange).to.have.been.calledWith({index: 2});
|
||||
});
|
||||
|
||||
it('should only change menu section when using arrow keys', function () {
|
||||
dropdown.props.onChange.reset();
|
||||
|
||||
TestUtils.Simulate.keyDown(currentItem, {key: 'Enter', keyCode: 13, which: 13});
|
||||
expect(menu.props.selectedIndex).to.equal(0);
|
||||
TestUtils.Simulate.keyDown(currentItem, {key: 'Down', keyCode: 40, which: 40});
|
||||
expect(menu.props.selectedIndex).to.equal(1);
|
||||
TestUtils.Simulate.keyDown(currentItem, {key: 'Down', keyCode: 40, which: 40});
|
||||
expect(menu.props.selectedIndex).to.equal(2);
|
||||
TestUtils.Simulate.keyDown(currentItem, {key: 'Down', keyCode: 40, which: 40});
|
||||
expect(menu.props.selectedIndex).to.equal(3);
|
||||
TestUtils.Simulate.keyDown(currentItem, {key: 'Down', keyCode: 40, which: 40});
|
||||
expect(menu.props.selectedIndex).to.equal(0);
|
||||
TestUtils.Simulate.keyDown(currentItem, {key: 'Down', keyCode: 40, which: 40});
|
||||
expect(menu.props.selectedIndex).to.equal(1);
|
||||
TestUtils.Simulate.keyDown(currentItem, {key: 'Up', keyCode: 38, which: 38});
|
||||
expect(menu.props.selectedIndex).to.equal(0);
|
||||
TestUtils.Simulate.keyDown(currentItem, {key: 'Up', keyCode: 38, which: 38});
|
||||
expect(menu.props.selectedIndex).to.equal(3);
|
||||
TestUtils.Simulate.keyDown(currentItem, {key: 'Up', keyCode: 38, which: 38});
|
||||
expect(menu.props.selectedIndex).to.equal(2);
|
||||
|
||||
expect(dropdown.props.onChange).to.have.not.been.called;
|
||||
expect(currentItem.textContent).to.equal('First Item');
|
||||
});
|
||||
|
||||
it('should not change menu selection if it is closed', function () {
|
||||
dropdown.props.onChange.reset();
|
||||
|
||||
TestUtils.Simulate.keyDown(currentItem, {key: 'Down', keyCode: 40, which: 40});
|
||||
expect(menu.props.selectedIndex).to.equal(0);
|
||||
TestUtils.Simulate.keyDown(currentItem, {key: 'Down', keyCode: 40, which: 40});
|
||||
expect(menu.props.selectedIndex).to.equal(0);
|
||||
TestUtils.Simulate.keyDown(currentItem, {key: 'Down', keyCode: 40, which: 40});
|
||||
expect(menu.props.selectedIndex).to.equal(0);
|
||||
|
||||
expect(dropdown.props.onChange).to.have.not.been.called;
|
||||
expect(currentItem.textContent).to.equal('First Item');
|
||||
});
|
||||
|
||||
it('should change selection to the menu\'s one, if Enter key is pressed', function () {
|
||||
dropdown.props.onChange.reset();
|
||||
|
||||
TestUtils.Simulate.keyDown(currentItem, {key: 'Enter', keyCode: 13, which: 13});
|
||||
TestUtils.Simulate.keyDown(currentItem, {key: 'Down', keyCode: 40, which: 40});
|
||||
TestUtils.Simulate.keyDown(currentItem, {key: 'Down', keyCode: 40, which: 40});
|
||||
expect(menu.props.selectedIndex).to.equal(2);
|
||||
|
||||
TestUtils.Simulate.keyDown(currentItem, {key: 'Enter', keyCode: 13, which: 13});
|
||||
|
||||
expect(currentItem.textContent).to.equal('Third Item');
|
||||
expect(menu.props.selectedIndex).to.equal(2);
|
||||
expect(dropdown.props.onChange).to.have.been.calledWith({index: 2});
|
||||
});
|
||||
|
||||
it('should not change selection with esc, blur or space', function () {
|
||||
TestUtils.Simulate.keyDown(currentItem, {key: 'Enter', keyCode: 13, which: 13});
|
||||
TestUtils.Simulate.keyDown(currentItem, {key: 'Down', keyCode: 40, which: 40});
|
||||
TestUtils.Simulate.keyDown(currentItem, {key: 'Down', keyCode: 40, which: 40});
|
||||
TestUtils.Simulate.keyDown(currentItem, {key: 'Enter', keyCode: 13, which: 13});
|
||||
expect(menu.props.selectedIndex).to.equal(2);
|
||||
|
||||
dropdown.props.onChange.reset();
|
||||
TestUtils.Simulate.keyDown(currentItem, {key: 'Enter', keyCode: 13, which: 13});
|
||||
TestUtils.Simulate.keyDown(currentItem, {key: 'Down', keyCode: 40, which: 40});
|
||||
TestUtils.Simulate.keyDown(currentItem, {key: 'Esc', keyCode: 27, which: 27});
|
||||
expect(currentItem.textContent).to.equal('Third Item');
|
||||
expect(dropdown.props.onChange).to.have.not.been.called;
|
||||
|
||||
dropdown.props.onChange.reset();
|
||||
TestUtils.Simulate.keyDown(currentItem, {key: 'Enter', keyCode: 13, which: 13});
|
||||
TestUtils.Simulate.keyDown(currentItem, {key: 'Down', keyCode: 40, which: 40});
|
||||
TestUtils.Simulate.keyDown(currentItem, {key: 'Space', keyCode: 32, which: 32});
|
||||
expect(currentItem.textContent).to.equal('Third Item');
|
||||
expect(dropdown.props.onChange).to.have.not.been.called;
|
||||
|
||||
dropdown.props.onChange.reset();
|
||||
TestUtils.Simulate.keyDown(currentItem, {key: 'Down', keyCode: 40, which: 40});
|
||||
TestUtils.Simulate.blur(currentItem);
|
||||
expect(currentItem.textContent).to.equal('Third Item');
|
||||
expect(dropdown.props.onChange).to.have.not.been.called;
|
||||
});
|
||||
|
||||
it('should start selecting defaultSelectedIndex', function () {
|
||||
dropdown.props.onChange.reset();
|
||||
renderDropDown({defaultSelectedIndex: 2});
|
||||
|
||||
expect(currentItem.textContent).to.equal('Third Item');
|
||||
expect(menu.props.selectedIndex).to.equal(2);
|
||||
expect(dropdown.props.onChange).to.have.not.been.called;
|
||||
|
||||
TestUtils.Simulate.keyDown(currentItem, {key: 'Enter', keyCode: 13, which: 13});
|
||||
TestUtils.Simulate.keyDown(currentItem, {key: 'Down', keyCode: 40, which: 40});
|
||||
TestUtils.Simulate.keyDown(currentItem, {key: 'Enter', keyCode: 13, which: 13});
|
||||
|
||||
expect(currentItem.textContent).to.equal('Fourth Item');
|
||||
expect(menu.props.selectedIndex).to.equal(3);
|
||||
expect(dropdown.props.onChange).to.have.been.calledWith({index: 3});
|
||||
});
|
||||
|
||||
it('should only show selectedIndex prop on the current selection', function () {
|
||||
dropdown.props.onChange.reset();
|
||||
renderDropDown({selectedIndex: 2});
|
||||
|
||||
expect(currentItem.textContent).to.equal('Third Item');
|
||||
expect(menu.props.selectedIndex).to.equal(2);
|
||||
expect(dropdown.props.onChange).to.have.not.been.called;
|
||||
|
||||
TestUtils.Simulate.keyDown(currentItem, {key: 'Enter', keyCode: 13, which: 13});
|
||||
TestUtils.Simulate.keyDown(currentItem, {key: 'Down', keyCode: 40, which: 40});
|
||||
TestUtils.Simulate.keyDown(currentItem, {key: 'Enter', keyCode: 13, which: 13});
|
||||
|
||||
expect(currentItem.textContent).to.equal('Third Item');
|
||||
expect(menu.props.selectedIndex).to.equal(3);
|
||||
expect(dropdown.props.onChange).to.have.been.calledWith({index: 3});
|
||||
});
|
||||
});
|
|
@ -37,7 +37,8 @@ describe('Menu component', function () {
|
|||
{content: 'Second Item', icon: 'ICON_2'},
|
||||
{content: 'Third Item', icon: 'ICON_3'},
|
||||
{content: 'Fourth Item', icon: 'ICON_4'}
|
||||
]
|
||||
],
|
||||
itemsRole: 'some_role'
|
||||
});
|
||||
|
||||
expect(items.length).to.equal(4);
|
||||
|
@ -47,6 +48,7 @@ describe('Menu component', function () {
|
|||
expect(items[3].textContent).to.equal('Fourth Item');
|
||||
|
||||
items.forEach((item, index) => {
|
||||
expect(item.getAttribute('role')).to.equal('some_role');
|
||||
expect(item.className).to.contain('menu__list-item');
|
||||
expect(item.childNodes[0]).to.equal(ReactDOM.findDOMNode(icons[index]));
|
||||
});
|
||||
|
|
|
@ -5,12 +5,21 @@
|
|||
&_primary,
|
||||
&_secondary,
|
||||
&_tertiary {
|
||||
background-color: $primary-red;
|
||||
border: solid transparent;
|
||||
border-radius: 4px;
|
||||
color: white;
|
||||
height: 47px;
|
||||
text-transform: uppercase;
|
||||
transition: background-color 0.2s ease;
|
||||
}
|
||||
|
||||
&_primary {
|
||||
background-color: $primary-red;
|
||||
|
||||
&:focus, &:hover {
|
||||
background-color: lighten($primary-red, 5%);
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
|
||||
&_secondary {
|
||||
|
|
|
@ -21,6 +21,10 @@
|
|||
&_checked {
|
||||
.checkbox--icon {
|
||||
color: $primary-red;
|
||||
|
||||
&:focus {
|
||||
color: lighten($primary-red, 5%);;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,6 +1,8 @@
|
|||
import React from 'react';
|
||||
import _ from 'lodash';
|
||||
import classNames from 'classnames';
|
||||
import {Motion, spring} from 'react-motion';
|
||||
import keyCode from 'keycode';
|
||||
|
||||
import Menu from 'core-components/menu';
|
||||
import Icon from 'core-components/icon';
|
||||
|
@ -11,18 +13,21 @@ class DropDown extends React.Component {
|
|||
defaultSelectedIndex: React.PropTypes.number,
|
||||
selectedIndex: React.PropTypes.number,
|
||||
items: Menu.propTypes.items,
|
||||
onChange: React.PropTypes.func,
|
||||
size: React.PropTypes.oneOf(['small', 'medium', 'large'])
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
defaultSelectedIndex: 2
|
||||
defaultSelectedIndex: 0
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
selectedIndex: 0,
|
||||
menuId: _.uniqueId('drop-down-menu_'),
|
||||
selectedIndex: props.selectedIndex || props.defaultSelectedIndex,
|
||||
highlightedIndex: props.selectedIndex || props.defaultSelectedIndex,
|
||||
opened: false
|
||||
};
|
||||
}
|
||||
|
@ -38,7 +43,7 @@ class DropDown extends React.Component {
|
|||
};
|
||||
|
||||
return {
|
||||
defaultStyle: closedStyle,
|
||||
defaultStyle: {opacity: 0, translateY: 20},
|
||||
style: (this.state.opened) ? openedStyle : closedStyle
|
||||
};
|
||||
}
|
||||
|
@ -50,7 +55,7 @@ class DropDown extends React.Component {
|
|||
return (
|
||||
<div className={this.getClass()}>
|
||||
{this.renderCurrentItem(selectedItem)}
|
||||
<Motion defaultStyle={animation.defaultStyle} style={animation.style}>
|
||||
<Motion defaultStyle={animation.defaultStyle} style={animation.style} onRest={this.onAnimationFinished.bind(this)}>
|
||||
{this.renderList.bind(this)}
|
||||
</Motion>
|
||||
</div>
|
||||
|
@ -59,16 +64,10 @@ class DropDown extends React.Component {
|
|||
|
||||
renderList({opacity, translateY}) {
|
||||
let style = { opacity: opacity, transform: `translateY(${translateY}px)`};
|
||||
let menuProps = {
|
||||
items: this.props.items,
|
||||
onItemClick: this.handleItemClick.bind(this),
|
||||
onMouseDown: this.handleListMouseDown.bind(this),
|
||||
selectedIndex: this.getSelectedIndex()
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="drop-down__list-container" style={style}>
|
||||
<Menu {...menuProps} />
|
||||
<Menu {...this.getMenuProps()} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -81,7 +80,7 @@ class DropDown extends React.Component {
|
|||
}
|
||||
|
||||
return (
|
||||
<div className="drop-down__current-item" onBlur={this.handleBlur.bind(this)} onClick={this.handleClick.bind(this)} tabIndex="0">
|
||||
<div {...this.getCurrentItemProps()}>
|
||||
{iconNode}{item.content}
|
||||
</div>
|
||||
);
|
||||
|
@ -92,13 +91,103 @@ class DropDown extends React.Component {
|
|||
'drop-down': true,
|
||||
'drop-down_closed': !this.state.opened,
|
||||
|
||||
['drop-down_' + this.props.size]: true,
|
||||
['drop-down_' + this.props.size]: (this.props.size),
|
||||
[this.props.className]: (this.props.className)
|
||||
};
|
||||
|
||||
return classNames(classes);
|
||||
}
|
||||
|
||||
getCurrentItemProps() {
|
||||
return {
|
||||
'aria-expanded': this.state.opened,
|
||||
'aria-autocomplete': 'list',
|
||||
'aria-owns': this.state.menuId,
|
||||
'aria-activedescendant': this.state.menuId + '__' + this.state.highlightedIndex,
|
||||
className: 'drop-down__current-item',
|
||||
onClick: this.handleClick.bind(this),
|
||||
onKeyDown: this.onKeyDown.bind(this),
|
||||
onBlur: this.handleBlur.bind(this),
|
||||
role: 'combobox',
|
||||
tabIndex: 0
|
||||
};
|
||||
}
|
||||
|
||||
getMenuProps() {
|
||||
return {
|
||||
id: this.state.menuId,
|
||||
itemsRole: 'option',
|
||||
items: this.props.items,
|
||||
onItemClick: this.handleItemClick.bind(this),
|
||||
onMouseDown: this.handleListMouseDown.bind(this),
|
||||
selectedIndex: this.state.highlightedIndex,
|
||||
role: 'listbox'
|
||||
};
|
||||
}
|
||||
|
||||
onKeyDown(event) {
|
||||
const keyActions = this.getKeyActions(event);
|
||||
const keyAction = keyActions[keyCode(event)];
|
||||
|
||||
if (keyAction) {
|
||||
keyAction();
|
||||
}
|
||||
}
|
||||
|
||||
getKeyActions(event) {
|
||||
const {highlightedIndex, opened} = this.state;
|
||||
const itemsQuantity = this.props.items.length;
|
||||
|
||||
return {
|
||||
'up': () => {
|
||||
if (opened) {
|
||||
event.preventDefault();
|
||||
|
||||
this.setState({
|
||||
highlightedIndex: this.modulo(highlightedIndex - 1, itemsQuantity)
|
||||
});
|
||||
}
|
||||
},
|
||||
'down': () => {
|
||||
if (opened) {
|
||||
event.preventDefault();
|
||||
|
||||
this.setState({
|
||||
highlightedIndex: this.modulo(highlightedIndex + 1, itemsQuantity)
|
||||
});
|
||||
}
|
||||
},
|
||||
'enter': () => {
|
||||
if (opened) {
|
||||
this.onIndexSelected(highlightedIndex);
|
||||
} else {
|
||||
this.setState({
|
||||
opened: true
|
||||
});
|
||||
}
|
||||
},
|
||||
'space': () => {
|
||||
event.preventDefault();
|
||||
|
||||
this.setState({
|
||||
opened: true
|
||||
});
|
||||
},
|
||||
'esc': () => {
|
||||
this.setState({
|
||||
opened: false
|
||||
});
|
||||
},
|
||||
'tab': () => {
|
||||
if (this.state.opened) {
|
||||
event.preventDefault();
|
||||
|
||||
this.onIndexSelected(highlightedIndex)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
handleBlur() {
|
||||
this.setState({
|
||||
opened: false
|
||||
|
@ -112,9 +201,14 @@ class DropDown extends React.Component {
|
|||
}
|
||||
|
||||
handleItemClick(index) {
|
||||
this.onIndexSelected(index);
|
||||
}
|
||||
|
||||
onIndexSelected(index) {
|
||||
this.setState({
|
||||
opened: false,
|
||||
selectedIndex: index
|
||||
selectedIndex: index,
|
||||
highlightedIndex: index
|
||||
});
|
||||
|
||||
if (this.props.onChange) {
|
||||
|
@ -128,9 +222,21 @@ class DropDown extends React.Component {
|
|||
event.preventDefault();
|
||||
}
|
||||
|
||||
onAnimationFinished() {
|
||||
if (!this.state.opened && this.state.highlightedIndex !== this.getSelectedIndex()) {
|
||||
this.setState({
|
||||
highlightedIndex: this.getSelectedIndex()
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
getSelectedIndex() {
|
||||
return (this.props.selectedIndex !== undefined) ? this.props.selectedIndex : this.state.selectedIndex;
|
||||
}
|
||||
|
||||
modulo(number, mod) {
|
||||
return ((number % mod) + mod) % mod;
|
||||
}
|
||||
}
|
||||
|
||||
export default DropDown;
|
||||
|
|
|
@ -8,6 +8,8 @@ import Icon from 'core-components/icon';
|
|||
class Menu extends React.Component {
|
||||
|
||||
static propTypes = {
|
||||
id: React.PropTypes.string,
|
||||
itemsRole: React.PropTypes.string,
|
||||
header: React.PropTypes.string,
|
||||
type: React.PropTypes.oneOf(['primary', 'secondary']),
|
||||
items: React.PropTypes.arrayOf(React.PropTypes.shape({
|
||||
|
@ -64,6 +66,7 @@ class Menu extends React.Component {
|
|||
|
||||
props.className = 'menu__list';
|
||||
|
||||
delete props.itemsRole;
|
||||
delete props.header;
|
||||
delete props.items;
|
||||
delete props.onItemClick;
|
||||
|
@ -87,10 +90,12 @@ class Menu extends React.Component {
|
|||
|
||||
getItemProps(index) {
|
||||
return {
|
||||
id: this.props.id + '__' + index,
|
||||
className: this.getItemClass(index),
|
||||
onClick: this.onItemClick.bind(this, index),
|
||||
tabIndex: (this.props.tabbable) ? '0' : null,
|
||||
onKeyDown: this.onKeyDown.bind(this, index),
|
||||
role: this.props.itemsRole,
|
||||
key: index
|
||||
};
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
|
||||
&__list-item {
|
||||
padding: 8px;
|
||||
transition: background-color 0.3s ease, color 0.3s ease;
|
||||
|
||||
&_selected,
|
||||
&:hover {
|
||||
|
|
|
@ -18,8 +18,8 @@ class Modal extends React.Component {
|
|||
getAnimations() {
|
||||
return {
|
||||
defaultStyle: {
|
||||
scale: spring(0.7),
|
||||
fade: spring(0.5)
|
||||
scale: 0.7,
|
||||
fade: 0.5
|
||||
},
|
||||
style: {
|
||||
scale: spring(1),
|
||||
|
|
|
@ -27,9 +27,15 @@ class ConfigReducer extends Reducer {
|
|||
}
|
||||
|
||||
onInitConfigs(state, payload) {
|
||||
sessionStore.storeConfigs(payload.data);
|
||||
const currentLanguage = sessionStore.getItem('language');
|
||||
|
||||
return _.extend({}, state, payload.data);
|
||||
sessionStore.storeConfigs(_.extend(payload.data, {
|
||||
language: currentLanguage || payload.language
|
||||
}));
|
||||
|
||||
return _.extend({}, state, payload.data, {
|
||||
language: currentLanguage || payload.language
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue