Skip to content

Commit

Permalink
TASK: Initial pass on WIA-ARIA attributes
Browse files Browse the repository at this point in the history
This changes and adds some WIA-ARIA attributes and additionally
makes the icon component a custom tag `icon` to avoid semantic
confusion with using the `i` tag.
  • Loading branch information
kitsunet committed Nov 16, 2017
1 parent 4b73c06 commit e5e8509
Show file tree
Hide file tree
Showing 16 changed files with 67 additions and 28 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@
"d3-time": "^1.0.7",
"debug": "^3.1.0",
"file-loader": "^0.10.0",
"hash-sum": "^1.0.2",
"hashlru": "2.2.0",
"he": "^1.1.1",
"immutable": "^3.8.0",
Expand Down
2 changes: 2 additions & 0 deletions packages/neos-ui/src/Containers/ContentCanvas/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,8 @@ export default class ContentCanvas extends PureComponent {
contentDidUpdate={this.onFrameChange}
onLoad={this.handleFrameAccess}
sandbox="allow-same-origin allow-scripts allow-forms"
role="region"
aria-live="assertive"
>
{InlineUI && <InlineUI/>}
</Frame>)
Expand Down
32 changes: 24 additions & 8 deletions packages/neos-ui/src/Containers/LeftSideBar/NodeTree/Node/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ import {stripTags, decodeHtml} from '@neos-project/utils-helpers';
import {actions, selectors} from '@neos-project/neos-ui-redux-store';
import {isNodeCollapsed} from '@neos-project/neos-ui-redux-store/src/CR/Nodes/helpers';
import {neos} from '@neos-project/neos-ui-decorators';

import animate from 'amator';
import hashSum from 'hash-sum';

const getContextPath = $get('contextPath');

Expand Down Expand Up @@ -65,6 +67,7 @@ export default class Node extends PureComponent {
isNodeDirty: PropTypes.bool.isRequired,

nodeTypesRegistry: PropTypes.object.isRequired,
i18nRegistry: PropTypes.object.isRequired,

getTreeNode: PropTypes.func,
onNodeToggle: PropTypes.func,
Expand Down Expand Up @@ -149,6 +152,13 @@ export default class Node extends PureComponent {
return $get('ui.icon', nodeTypesRegistry.get(nodeType));
}

getNodeTypeLabel() {
const {node, nodeTypesRegistry, i18nRegistry} = this.props;
const nodeType = $get('nodeType', node);
const nodeTypeLabel = $get('ui.label', nodeTypesRegistry.get(nodeType));
return i18nRegistry.translate(nodeTypeLabel, nodeTypeLabel);
}

isFocused() {
const {node, focusedNodeContextPath} = this.props;

Expand Down Expand Up @@ -215,7 +225,8 @@ export default class Node extends PureComponent {
onNodeFocus,
onNodeDrag,
onNodeDrop,
currentlyDraggedNode
currentlyDraggedNode,
isContentTreeNode
} = this.props;

if (this.isHidden()) {
Expand All @@ -226,10 +237,13 @@ export default class Node extends PureComponent {
};
const childNodesCount = childNodes.count();

const labelIdentifier = (isContentTreeNode ? 'content-' : '') + 'treeitem-' + hashSum($get('contextPath', node)) + '-label';

return (
<Tree.Node>
<Tree.Node aria-expanded={this.isCollapsed() ? 'false' : 'true'} aria-labelledby={labelIdentifier}>
<span ref={refHandler}/>
<Tree.Node.Header
labelIdentifier={labelIdentifier}
id={$get('contextPath', node)}
hasChildren={hasChildren}
nodeDndType={nodeDndType}
Expand All @@ -244,6 +258,7 @@ export default class Node extends PureComponent {
hasError={this.hasError()}
label={decodeLabel($get('label', node))}
icon={this.getIcon()}
iconLabel={this.getNodeTypeLabel()}
level={level}
onToggle={this.handleNodeToggle}
onClick={this.handleNodeClick}
Expand Down Expand Up @@ -287,12 +302,13 @@ export default class Node extends PureComponent {
}
}

const withNodeTypeRegistry = neos(globalRegistry => ({
nodeTypesRegistry: globalRegistry.get('@neos-project/neos-ui-contentrepository')
const withNodeTypeRegistryAndI18nRegistry = neos(globalRegistry => ({
nodeTypesRegistry: globalRegistry.get('@neos-project/neos-ui-contentrepository'),
i18nRegistry: globalRegistry.get('i18n')
}));

export const PageTreeNode = withNodeTypeRegistry(connect(
(state, {neos, nodeTypesRegistry}) => {
export const PageTreeNode = withNodeTypeRegistryAndI18nRegistry(connect(
(state, {neos, nodeTypesRegistry, i18nRegistry}) => {
const allowedNodeTypes = nodeTypesRegistry.getSubTypesOf(nodeTypesRegistry.getRole('document'));

const childrenOfSelector = selectors.CR.Nodes.makeChildrenOfSelector(allowedNodeTypes);
Expand Down Expand Up @@ -330,8 +346,8 @@ export const PageTreeNode = withNodeTypeRegistry(connect(
}
)(Node));

export const ContentTreeNode = withNodeTypeRegistry(connect(
(state, {neos, nodeTypesRegistry}) => {
export const ContentTreeNode = withNodeTypeRegistryAndI18nRegistry(connect(
(state, {neos, nodeTypesRegistry, i18nRegistry}) => {
const allowedNodeTypes = [].concat(
nodeTypesRegistry.getSubTypesOf(nodeTypesRegistry.getRole('content')),
nodeTypesRegistry.getSubTypesOf(nodeTypesRegistry.getRole('contentCollection'))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,23 +31,23 @@ export default class UserDropDown extends PureComponent {
</DropDown.Header>
<DropDown.Contents className={style.dropDown__contents}>
<li className={style.dropDown__item}>
<form title="Logout" action="/neos/logout" method="post">
<form title="Logout" action="/neos/logout" method="post" role="presentation">
<input type="hidden" name="__csrfToken" value={csrfToken}/>
<button type="submit" name="" value="logout">
<Icon icon="power-off" className={style.dropDown__itemIcon}/>
<Icon icon="power-off" aria-hidden="true" className={style.dropDown__itemIcon}/>
<I18n id="logout" fallback="Logout"/>
</button>
</form>
</li>
<li className={style.dropDown__item}>
<a title="User Settings" href="/neos!/deactivate">
<Icon icon="frown-o" className={style.dropDown__itemIcon}/>
<Icon icon="frown-o" aria-hidden="true" className={style.dropDown__itemIcon}/>
<I18n id="userSettings_swtichUi" sourceName="Modules" fallback="Switch to old UI"/>
</a>
</li>
<li className={style.dropDown__item}>
<a title="User Settings" href="/neos/user/usersettings">
<Icon icon="wrench" className={style.dropDown__itemIcon}/>
<Icon icon="wrench" aria-hidden="true" className={style.dropDown__itemIcon}/>
<I18n id="userSettings_label" sourceName="Modules" fallback="User Settings"/>
</a>
</li>
Expand Down
2 changes: 2 additions & 0 deletions packages/neos-ui/src/Containers/RightSideBar/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ export default class RightSideBar extends PureComponent {
<SideBar
position="right"
className={classNames}
role="form"
aria-live="assertive"
aria-hidden={isSideBarHidden ? 'true' : 'false'}
>
{toggle}
Expand Down
1 change: 0 additions & 1 deletion packages/react-ui-components/src/CheckBox/checkBox.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,6 @@ class CheckBox extends PureComponent {
{...rest}
className={theme.checkbox__input}
type="checkbox"
role="checkbox"
checked={isChecked}
aria-checked={isChecked}
onChange={this.handleChange}
Expand Down
2 changes: 1 addition & 1 deletion packages/react-ui-components/src/DropDown/contents.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ const ShallowDropDownContents = props => {
className={finalClassName}
aria-hidden={isOpen ? 'false' : 'true'}
aria-label="dropdown"
role="button"
role="listbox"
onClick={closeDropDown}
>
{children}
Expand Down
15 changes: 8 additions & 7 deletions packages/react-ui-components/src/Headline/headline.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ const Headline = props => {
type,
className,
children,
theme
theme,
...rest
} = props;
const classNames = mergeClassNames({
[theme.heading]: true,
Expand All @@ -27,27 +28,27 @@ const Headline = props => {

switch (type) {
case 'h1':
heading = <h1 className={classNames}>{children}</h1>;
heading = <h1 {...rest} className={classNames}>{children}</h1>;
break;

case 'h2':
heading = <h2 className={classNames}>{children}</h2>;
heading = <h2 {...rest} className={classNames}>{children}</h2>;
break;

case 'h3':
heading = <h3 className={classNames}>{children}</h3>;
heading = <h3 {...rest} className={classNames}>{children}</h3>;
break;

case 'h4':
heading = <h4 className={classNames}>{children}</h4>;
heading = <h4 {...rest} className={classNames}>{children}</h4>;
break;

case 'h5':
heading = <h5 className={classNames}>{children}</h5>;
heading = <h5 {...rest} className={classNames}>{children}</h5>;
break;

default:
heading = <h6 className={classNames}>{children}</h6>;
heading = <h6 {...rest} className={classNames}>{children}</h6>;
break;
}

Expand Down
9 changes: 7 additions & 2 deletions packages/react-ui-components/src/Icon/icon.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ http://fortawesome.github.io/Font-Awesome/icons/`);
};

const Icon = props => {
const {size, padded, theme, iconMap, _makeGetClassName} = props;
const {size, padded, theme, iconMap, label, _makeGetClassName, ...rest} = props;
const getClassName = _makeGetClassName(iconMap);
const iconClassName = getClassName(props.icon);
const classNames = mergeClassNames({
Expand All @@ -46,14 +46,19 @@ const Icon = props => {
[theme['icon--spin']]: props.spin
});

return <i className={classNames}/>;
return <icon {...rest} role="img" aria-label={label} className={classNames}/>;
};
Icon.propTypes = {
/**
* The ID of the icon to render.
*/
icon: iconPropValidator,

/**
* The (accessibility) label for this icon
*/
label: PropTypes.string,

/**
* Controls the rendered size of the icon.
*/
Expand Down
2 changes: 1 addition & 1 deletion packages/react-ui-components/src/SelectBox/selectBox.js
Original file line number Diff line number Diff line change
Expand Up @@ -345,7 +345,7 @@ export default class SelectBox extends PureComponent {

const OptionComponent = optionComponent;
// onMouseEnter doesn't work on OptionComponent
return <div key={index} onMouseEnter={setIndex}><OptionComponent className={className} isActive={isActive} option={option} key={index} onClick={onClick} theme={theme} IconComponent={IconComponent}/></div>;
return <div key={index} onMouseEnter={setIndex} role="option"><OptionComponent className={className} isActive={isActive} option={option} key={index} onClick={onClick} theme={theme} IconComponent={IconComponent}/></div>;
}

setIndex = index => {
Expand Down
2 changes: 2 additions & 0 deletions packages/react-ui-components/src/TextArea/textArea.js
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,8 @@ class TextArea extends PureComponent {
{...rest}
className={classNames}
role="textbox"
aria-multiline="true"
aria-disabled={disabled ? 'true' : 'false'}
placeholder={placeholder}
disabled={disabled}
onChange={this.handleValueChange}
Expand Down
2 changes: 2 additions & 0 deletions packages/react-ui-components/src/TextInput/textInput.js
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,8 @@ class TextInput extends PureComponent {
{...rest}
className={classNames}
role="textbox"
aria-multiline="false"
aria-disabled={disabled ? 'true' : 'false'}
type={type}
placeholder={placeholder}
disabled={disabled}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,6 @@ export class Header extends PureComponent {
<HeadlineComponent
className={finalClassName}
type="h1"
style="h4"
>
{children}
</HeadlineComponent>
Expand Down
11 changes: 8 additions & 3 deletions packages/react-ui-components/src/Tree/node.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import PropTypes from 'prop-types';
import {DragSource, DropTarget} from 'react-dnd';
import omit from 'lodash.omit';
import mergeClassNames from 'classnames';
import hashSum from 'hash-sum';

const spec = {
canDrop({dragAndDropContext, mode}) {
Expand All @@ -29,7 +30,7 @@ export class Node extends PureComponent {
const rest = omit(restProps, ['theme']);

return (
<div {...rest}>
<div {...rest} role="treeitem">
{children}
</div>
);
Expand Down Expand Up @@ -83,6 +84,7 @@ class NodeDropTarget extends PureComponent {
export class Header extends PureComponent {
static propTypes = {
id: PropTypes.string,
labelIdentifier: PropTypes.string,
nodeDndType: PropTypes.string.isRequired,
hasChildren: PropTypes.bool.isRequired,
isLastChild: PropTypes.bool,
Expand All @@ -96,6 +98,7 @@ export class Header extends PureComponent {
hasError: PropTypes.bool.isRequired,
label: PropTypes.string.isRequired,
icon: PropTypes.string,
iconLabel: PropTypes.string,
level: PropTypes.number.isRequired,
dragAndDropContext: PropTypes.shape({
accepts: PropTypes.func.isRequired,
Expand Down Expand Up @@ -136,6 +139,7 @@ export class Header extends PureComponent {
render() {
const {
id,
labelIdentifier,
nodeDndType,
IconComponent,
hasChildren,
Expand All @@ -147,6 +151,7 @@ export class Header extends PureComponent {
isDirty,
label,
icon,
iconLabel,
level,
onClick,
onLabelClick,
Expand Down Expand Up @@ -190,8 +195,8 @@ export class Header extends PureComponent {
onClick={onClick}
style={{paddingLeft: (level * 18) + 'px'}}
>
<IconComponent icon={icon || 'question'} role="button" className={theme.header__icon}/>
<span {...rest} className={theme.header__label} role="button" onClick={onLabelClick} data-neos-integrational-test="tree__item__nodeHeader__itemLabel">
<IconComponent icon={icon || 'question'} label={iconLabel} className={theme.header__icon}/>
<span {...rest} id={labelIdentifier} className={theme.header__label} onClick={onLabelClick} data-neos-integrational-test="tree__item__nodeHeader__itemLabel">
{label}
</span>
</div>
Expand Down
1 change: 1 addition & 0 deletions packages/react-ui-components/src/Tree/tree.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export class Tree extends PureComponent {
verticalStrength={verticalStrength}
className={classNames}
tabIndex="0"
role="tree"
>
<NodeComponent {...rest}>
{this.props.children}
Expand Down
4 changes: 4 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4028,6 +4028,10 @@ has@^1.0.1:
dependencies:
function-bind "^1.0.2"

hash-sum@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/hash-sum/-/hash-sum-1.0.2.tgz#33b40777754c6432573c120cc3808bbd10d47f04"

[email protected]:
version "2.2.0"
resolved "https://registry.yarnpkg.com/hashlru/-/hashlru-2.2.0.tgz#793a58943f902aea578177d7b0335f13f2694b71"
Expand Down

0 comments on commit e5e8509

Please sign in to comment.