Skip to content

Commit

Permalink
Merge pull request alibaba-fusion#170 from myronliu347/feat/a11y
Browse files Browse the repository at this point in the history
Feat Support a11y
  • Loading branch information
youluna authored Jan 25, 2019
2 parents f5b7d07 + 9e69d1f commit 954eeed
Show file tree
Hide file tree
Showing 25 changed files with 796 additions and 118 deletions.
19 changes: 18 additions & 1 deletion src/breadcrumb/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ class Breadcrumb extends Component {
* 样式类名的品牌前缀
*/
prefix: PropTypes.string,
/*eslint-disable*/
/**
* 面包屑子节点,需传入 Breadcrumb.Item
*/
Expand All @@ -26,6 +27,7 @@ class Breadcrumb extends Component {
}
});
},
/*eslint-enable*/
/**
* 面包屑最多显示个数,超出部分会被隐藏
*/
Expand Down Expand Up @@ -54,35 +56,50 @@ class Breadcrumb extends Component {
items = [];

Children.forEach(children, (item, i) => {
const ariaProps = {};

if (i === length - 1) {
ariaProps['aria-current'] = 'page';
}

if (i && i === breakpointer) {
items.push(React.cloneElement(item, {
separator,
prefix,
key: i,
activated: i === length - 1,
...ariaProps,
className: `${prefix}breadcrumb-text-ellipsis`
}, '...'));
} else if (!i || i > breakpointer) {
items.push(React.cloneElement(item, {
separator,
prefix,
key: i,
...ariaProps,
activated: i === length - 1
}));
}
});
} else {
items = Children.map(children, (item, i) => {
const ariaProps = {};

if (i === length - 1) {
ariaProps['aria-current'] = 'page';
}

return React.cloneElement(item, {
separator,
prefix,
activated: i === length - 1,
...ariaProps,
key: i
});
});
}

return <div className={clazz} {...others}>{items}</div>;
return <div aria-label="Breadcrumb" className={clazz} {...others}>{items}</div>;
}
}

Expand Down
41 changes: 39 additions & 2 deletions src/cascader-select/cascader-select.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
import Select from '../select';
import Cascader from '../cascader';
import Menu from '../menu';
import { func, obj, dom } from '../util';
import { func, obj, dom, KEYCODE } from '../util';

const { bindCtx } = func;
const { pickOthers } = obj;
Expand Down Expand Up @@ -253,7 +253,7 @@ export default class CascaderSelect extends Component {

bindCtx(this, [
'handleVisibleChange', 'handleAfterOpen', 'handleChange', 'handleClear', 'handleRemove',
'handleSearch', 'getPopup'
'handleSearch', 'getPopup', 'saveSelectRef', 'saveCascaderRef', 'handleKeyDown'
]);
}

Expand Down Expand Up @@ -412,6 +412,14 @@ export default class CascaderSelect extends Component {
return indeterminate;
}

saveSelectRef(ref) {
this.select = ref;
}

saveCascaderRef(ref) {
this.cascader = ref;
}

completeValue(value) {
const newValue = [];

Expand Down Expand Up @@ -443,9 +451,35 @@ export default class CascaderSelect extends Component {
});
}

if (['fromCascader', 'keyboard'].indexOf(type) !== -1 && !visible) {
this.select.focusInput();
}

this.props.onVisibleChange(visible, type);
}

handleKeyDown(e) {
const { onKeyDown } = this.props;
const { visible } = this.state;

if (onKeyDown) {
onKeyDown(e);
}

if (!visible) {
return;
}

switch (e.keyCode) {
case KEYCODE.UP:
case KEYCODE.DOWN:
this.cascader.setFocusValue();
e.preventDefault();
break;
default: break;
}
}

getPopup(ref) {
this.popup = ref;
if (typeof this.props.popupProps.ref === 'function') {
Expand Down Expand Up @@ -640,6 +674,7 @@ export default class CascaderSelect extends Component {
canOnlyCheckLeaf,
defaultExpandedValue,
expandTriggerType,
ref: this.saveCascaderRef,
onExpand,
listStyle,
listClassName,
Expand Down Expand Up @@ -711,6 +746,7 @@ export default class CascaderSelect extends Component {
hasClear,
label,
readOnly,
ref: this.saveSelectRef,
autoWidth: false,
mode: multiple ? 'multiple' : 'single',
value: multiple ? this.getMultipleData(value) : this.getSignleData(value),
Expand All @@ -721,6 +757,7 @@ export default class CascaderSelect extends Component {
showSearch,
searchValue,
onSearch: this.handleSearch,
onKeyDown: this.handleKeyDown,
popupContent,
popupStyle,
popupClassName,
Expand Down
111 changes: 94 additions & 17 deletions src/cascader/cascader.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,17 @@ export default class Cascader extends Component {
constructor(props, context) {
super(props, context);

const { defaultValue, value, defaultExpandedValue, expandedValue, dataSource, multiple, checkStrictly, canOnlyCheckLeaf, loadData } = props;
const {
defaultValue,
value,
defaultExpandedValue,
expandedValue,
dataSource,
multiple,
checkStrictly,
canOnlyCheckLeaf,
loadData
} = props;

this.updateCache(dataSource);

Expand All @@ -134,7 +144,9 @@ export default class Cascader extends Component {
// TODO loadData
const realExpandedValue = typeof expandedValue === 'undefined' ?
(
typeof defaultExpandedValue === 'undefined' ? this.getExpandedValue(normalizedValue[0]) : this.normalizeValue(defaultExpandedValue)
typeof defaultExpandedValue === 'undefined' ?
this.getExpandedValue(normalizedValue[0]) :
this.normalizeValue(defaultExpandedValue)
) :
this.normalizeValue(expandedValue);
const st = {
Expand Down Expand Up @@ -271,7 +283,7 @@ export default class Cascader extends Component {
return expandedMap[prev].split('-').length - expandedMap[next].split('-').length;
});
}

/*eslint-disable max-statements*/
completeValue(dataSource, value) {
const filterValue = value.filter(v => typeof this._v2n[v] !== 'undefined');
let flatValue = this.flatValue(filterValue);
Expand Down Expand Up @@ -315,7 +327,7 @@ export default class Cascader extends Component {

return newValue;
}

/*eslint-enable*/
flatValue(value) {
const getDepth = v => this.getPos(v).split('-').length;
const newValue = value.slice(0).sort((prev, next) => {
Expand Down Expand Up @@ -408,7 +420,7 @@ export default class Cascader extends Component {
this.lastExpandedValue = [...this.state.expandedValue];
}
}

/*eslint-disable max-statements*/
handleCheck(v, checked) {
const { checkStrictly, canOnlyCheckLeaf } = this.props;
const value = [...this.state.value];
Expand All @@ -431,12 +443,13 @@ export default class Cascader extends Component {
let parentChecked = true;
for (let j = 0; j < ps.length; j++) {
const p = ps[j];
if (this.isSiblingOrSelf(currentPos, p)) {
const v = this.getValue(p);
if (value.indexOf(v) === -1) {
parentChecked = false;
break;
}
if (!this.isSiblingOrSelf(currentPos, p)) {
continue;
}
const v = this.getValue(p);
if (value.indexOf(v) === -1) {
parentChecked = false;
break;
}
}
const parentPos = nums.slice(0, i - 1).join('-');
Expand Down Expand Up @@ -504,7 +517,7 @@ export default class Cascader extends Component {
const data = this._v2n[value];
return loadData(data, data._source).then(callback);
} else {
callback();
return callback();
}
}
}
Expand All @@ -525,6 +538,50 @@ export default class Cascader extends Component {
}
}

getFirstFocusKeyByDataSource(dataSource) {
if (!dataSource || dataSource.length === 0) {
return '';
}

for (let i = 0; i < dataSource.length; i++) {
if (dataSource[i] && !dataSource[i].disabled) {
return dataSource[i].value;
}
}

return '';
}

getFirstFocusKeyByFilteredPaths(filteredPaths) {
if (!filteredPaths || filteredPaths.length === 0) {
return '';
}

for (let i = 0; i < filteredPaths.length; i++) {
const path = filteredPaths[i];
if (!path.some(item => item.disabled)) {
const lastItem = path[path.length - 1];
return lastItem.value;
}
}

return '';
}

getFirstFocusKey () {
const { dataSource, searchValue, filteredPaths } = this.props;

return !searchValue ?
this.getFirstFocusKeyByDataSource(dataSource) :
this.getFirstFocusKeyByFilteredPaths(filteredPaths);
}

setFocusValue() {
this.setState({
focusedValue: this.getFirstFocusKey()
});
}

handleFocus(focusedValue) {
this.setState({
focusedValue
Expand All @@ -545,7 +602,7 @@ export default class Cascader extends Component {
getIndeterminate(value) {
const indeterminate = [];

const positions = this.flatValue(value).map(::this.getPos);
const positions = this.flatValue(value).map(value => this.getPos(value));
positions.forEach(pos => {
const nums = pos.split('-');
for (let i = nums.length; i > 2; i--) {
Expand Down Expand Up @@ -574,7 +631,17 @@ export default class Cascader extends Component {
const { value, expandedValue, focusedValue } = this.state;

return (
<CascaderMenu key={level} prefix={prefix} useVirtual={useVirtual} className={listClassName} style={listStyle} focusedKey={focusedValue} onItemFocus={this.handleFocus} onBlur={this.onBlur}>
<CascaderMenu
key={level}
prefix={prefix}
useVirtual={useVirtual}
className={listClassName}
style={listStyle}
ref={this.saveMenuRef}
focusedKey={focusedValue}
onItemFocus={this.handleFocus}
onBlur={this.onBlur}
>
{data.map(item => {
const disabled = !!item.disabled;
const canExpand = (!!item.children && !!item.children.length) || (!!loadData && !item.isLeaf);
Expand All @@ -592,7 +659,8 @@ export default class Cascader extends Component {
if (multiple) {
props.checkable = !(canOnlyCheckLeaf && canExpand);
props.checked = value.indexOf(item.value) > -1;
props.indeterminate = (checkStrictly || canOnlyCheckLeaf) ? false : this.indeterminate.indexOf(item.value) > -1;
props.indeterminate = (checkStrictly || canOnlyCheckLeaf) ?
false : this.indeterminate.indexOf(item.value) > -1;
props.checkboxDisabled = !!item.checkboxDisabled;
props.onCheck = this.handleCheck.bind(this, item.value);
} else {
Expand Down Expand Up @@ -654,7 +722,10 @@ export default class Cascader extends Component {
Item = Menu.CheckboxItem;
const { checkStrictly, canOnlyCheckLeaf } = this.props;
props.checked = value.indexOf(lastItem.value) > -1;
props.indeterminate = !checkStrictly && !canOnlyCheckLeaf && this.indeterminate.indexOf(lastItem.value) > -1;
props.indeterminate =
!checkStrictly &&
!canOnlyCheckLeaf &&
this.indeterminate.indexOf(lastItem.value) > -1;
props.checkboxDisabled = lastItem.checkboxDisabled;
props.onChange = this.handleCheck.bind(this, lastItem.value);
} else {
Expand All @@ -668,8 +739,14 @@ export default class Cascader extends Component {

renderFilteredList() {
const { prefix, filteredListStyle, filteredPaths } = this.props;
const { focusedValue } = this.state;
return (
<Menu className={`${prefix}cascader-filtered-list`} style={filteredListStyle}>
<Menu
focusedKey={focusedValue}
onItemFocus={this.handleFocus}
className={`${prefix}cascader-filtered-list`}
style={filteredListStyle}
>
{filteredPaths.map(path => this.renderFilteredItem(path))}
</Menu>
);
Expand Down
3 changes: 2 additions & 1 deletion src/cascader/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,6 @@ export default ConfigProvider.config(Cascader, {
}

return props;
}
},
exportNames: ['setFocusValue']
});
Loading

0 comments on commit 954eeed

Please sign in to comment.