Skip to content

Commit

Permalink
feat: add accessibility properties for ariaLabel, ariaDescribedBy and…
Browse files Browse the repository at this point in the history
… ariaLabelledBy
  • Loading branch information
svenliebig committed Jul 18, 2022
1 parent d0c62fb commit 61addc5
Show file tree
Hide file tree
Showing 5 changed files with 754 additions and 95 deletions.
70 changes: 70 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,76 @@ React.createClass({
});
```

## Accessibility

The Accessibility support is currently not that great, if you have the requirement in your project to match the axe accessility check, you can try to use an implementation like this:

```javascript
import React, {Component, PropTypes} from 'react';
import RichTextEditor from 'react-rte';

// You can use your necessary language translations here
const toolbar = {
display: ["INLINE_STYLE_BUTTONS", "BLOCK_TYPE_BUTTONS", "BLOCK_ALIGNMENT_BUTTONS", "HISTORY_BUTTONS"],
INLINE_STYLE_BUTTONS: [
{ label: "bold", style: "BOLD" },
{ label: "italic", style: "ITALIC" },
{ label: "underline", style: "UNDERLINE" },
{ label: "strikethrough", style: "STRIKETHROUGH" },
],
BLOCK_TYPE_BUTTONS: [
{ label: "unorderedList", style: "unordered-list-item" },
{ label: "orderedList", style: "ordered-list-item" },
],
HISTORY_BUTTONS: {
undo: { label: "undo" },
redo: { label: "redo" },
},
}

class MyStatefulEditor extends Component {
static propTypes = {
onChange: PropTypes.func
label: PropTypes.string
required: PropTypes.string
};

state = {
value: RichTextEditor.createEmptyValue()
}

onChange = (value) => {
this.setState({value});
if (this.props.onChange) {
// Send the changes up to the parent component as an HTML string.
// This is here to demonstrate using `.toString()` but in a real app it
// would be better to avoid generating a string on each change.
this.props.onChange(
value.toString('html')
);
}
};

render () {
return (
<>
<label aria-hidden="true">{this.props.label}</label>
<RichTextEditor
value={this.state.value}
onChange={this.onChange}
toolbarConfig={toolbar}
ariaLabel={this.props.label}
ariaRequired={this.props.required}
// You are also able to use these properties:
// ariaDescribedBy={...}
// ariaLabelledBy={...}
/>
</>
);
}
}
```

## TODO

- Support images
Expand Down
12 changes: 7 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,13 @@
"prepare": "npm run build",
"start": "webpack-dev-server --content-base assets/",
"test": "npm run lint && npm run typecheck && npm run test-src",
"test-src": "BABEL_ENV=test mocha \"src/**/__tests__/*.js\""
"test-src": "BABEL_ENV=test mocha --require global-jsdom/register \"src/**/__tests__/*.js\""
},
"dependencies": {
"babel-runtime": "^6.23.0",
"class-autobind": "^0.1.4",
"classnames": "^2.2.5",
"draft-js": ">=0.10.0",
"draft-js": ">=0.11.0",
"draft-js-export-html": ">=0.6.0",
"draft-js-export-markdown": ">=0.3.0",
"draft-js-import-html": ">=0.4.0",
Expand All @@ -35,6 +35,7 @@
"react-dom": "0.14.x || 15.x.x || 16.x.x || 17.x.x"
},
"devDependencies": {
"@testing-library/react": "^12.0.0",
"babel-cli": "^6.18.0",
"babel-core": "^6.18.2",
"babel-eslint": "^7.1.0",
Expand All @@ -52,11 +53,12 @@
"eslint-plugin-react": "^6.5.0",
"expect": "^1.20.2",
"flow-bin": "^0.32.0",
"global-jsdom": "^8.5.0",
"jsdom": "^20.0.0",
"mocha": "^3.1.2",
"raw-loader": "^0.5.1",
"react": "16.x.x",
"react-dom": "16.x.x",
"react-test-renderer": "^16.4.0",
"react": "16.14.x",
"react-dom": "16.14.x",
"rimraf": "^2.5.4",
"style-loader": "^0.18.2",
"webpack": "^3.4.0",
Expand Down
10 changes: 9 additions & 1 deletion src/RichTextEditor.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,9 @@ type Props = {
editorStyle?: Object;
toolbarStyle?: Object;
onBlur?: (event: Object) => void;
ariaLabel?: string;
ariaLabelledBy?: string;
ariaDescribedBy?: string;
};

export default class RichTextEditor extends Component {
Expand Down Expand Up @@ -108,6 +111,9 @@ export default class RichTextEditor extends Component {
rootStyle,
toolbarStyle,
editorStyle,
ariaLabel,
ariaDescribedBy,
ariaLabelledBy,
...otherProps // eslint-disable-line comma-dangle
} = this.props;
let editorState = value.getEditorState();
Expand Down Expand Up @@ -154,7 +160,9 @@ export default class RichTextEditor extends Component {
onTab={this._onTab}
onChange={this._onChange}
placeholder={placeholder}
ariaLabel={placeholder || 'Edit text'}
ariaLabel={ariaLabel || 'Edit text'}
ariaLabelledBy={ariaLabelledBy}
ariaDescribedBy={ariaDescribedBy}
ref={(el) => {
this.editor = el;
}}
Expand Down
31 changes: 24 additions & 7 deletions src/__tests__/RichTextEditor-test.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,35 @@
/* @flow */
const {describe, it} = global;
import React from 'react';
import ShallowRenderer from 'react-test-renderer/shallow';
// import ShallowRenderer from 'react-test-renderer/shallow';
import expect from 'expect';
import {render, screen} from '@testing-library/react';
import RichTextEditor, {createEmptyValue} from '../RichTextEditor';

describe('RichTextEditor', () => {
it('should render', () => {
let renderer = new ShallowRenderer();
let value = createEmptyValue();
renderer.render(<RichTextEditor value={value} />);
let output = renderer.getRenderOutput();
expect(output.type).toEqual('div');
expect(output.props.className).toBeA('string');
expect(output.props.className).toInclude('RichTextEditor__root');
const {container} = render(<RichTextEditor value={value} ariaLabel="Hello Westeros" />);
expect(container.querySelector('div').className).toInclude('RichTextEditor__root___');
});

describe('accessibility', () => {
it('should have the given ariaLabel', () => {
let value = createEmptyValue();
render(<RichTextEditor value={value} ariaLabel="Hello Westeros" />);
expect(screen.getByRole('textbox').getAttribute('aria-label')).toEqual('Hello Westeros');
});

it('should have the given ariaLabelledBy', () => {
let value = createEmptyValue();
render(<RichTextEditor value={value} ariaLabelledBy="jonsnow" />);
expect(screen.getByRole('textbox').getAttribute('aria-labelledby')).toEqual('jonsnow');
});

it('should have the given ariaDescribedBy', () => {
let value = createEmptyValue();
render(<RichTextEditor value={value} ariaDescribedBy="sunandmoon" />);
expect(screen.getByRole('textbox').getAttribute('aria-describedby')).toEqual('sunandmoon');
});
});
});
Loading

0 comments on commit 61addc5

Please sign in to comment.