Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Accessibility properties and tests to the component #383 #445

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 69 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,75 @@ 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}
// 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
30 changes: 23 additions & 7 deletions src/__tests__/RichTextEditor-test.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,34 @@
/* @flow */
const {describe, it} = global;
import React from 'react';
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