Skip to content

Commit

Permalink
Allow implementors to change the WAI-ARIA role of the container (#3658)…
Browse files Browse the repository at this point in the history
… (#3669)

* Allow implementors to change the WAI-ARIA role of the container (#3658)

* Adding tests for `containerRole` and `useContainerRole` (#3658)

* Plus CHANGELOG entry

* Use role attribute

* Move role checks to BasicWebChat

Co-authored-by: William Wong <[email protected]>
Co-authored-by: William Wong <[email protected]>
  • Loading branch information
3 people authored Feb 18, 2021
1 parent 72c98bb commit 8f435f7
Show file tree
Hide file tree
Showing 7 changed files with 1,058 additions and 958 deletions.
1,899 changes: 950 additions & 949 deletions CHANGELOG.md

Large diffs are not rendered by default.

38 changes: 38 additions & 0 deletions __tests__/html/accessibility.landmarkRole.invalid.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<!DOCTYPE html>
<html lang="en-US">
<head>
<script crossorigin="anonymous" src="/__dist__/testharness.js"></script>
<script crossorigin="anonymous" src="/__dist__/webchat-es5.js"></script>
</head>
<body>
<div id="webchat"></div>
<script type="text/babel" data-presets="env,stage-3,react">
const {
WebChat: { createDirectLine },
WebChatTest: { conditions, createStore, expect, host, pageObjects, timeouts, token }
} = window;

(async function () {
window.WebChat.renderWebChat(
{
directLine: createDirectLine({ token: await token.fetchDirectLineToken() }),
role: 'invalid',
store: createStore()
},
document.getElementById('webchat')
);

await pageObjects.wait(conditions.uiConnected(), timeouts.directLine);

// Should fallback to "complementary" if the role specified is not a valid landmark role.
await expect(document.querySelector('#webchat > *').getAttribute('role')).toBe('complementary');

await host.done();
})().catch(async err => {
console.error(err);

await host.error(err);
});
</script>
</body>
</html>
8 changes: 8 additions & 0 deletions __tests__/html/accessibility.landmarkRole.invalid.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/**
* @jest-environment ./__tests__/html/__jest__/WebChatEnvironment.js
*/

describe('accessibility feature to support "role" attribute', () => {
test('should fallback to "complementary" if not a valid landmark role', () =>
runHTMLTest('accessibility.landmarkRole.invalid', { ignoreConsoleError: true }));
});
36 changes: 36 additions & 0 deletions __tests__/html/accessibility.landmarkRole.valid.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<!DOCTYPE html>
<html lang="en-US">
<head>
<script crossorigin="anonymous" src="/__dist__/testharness.js"></script>
<script crossorigin="anonymous" src="/__dist__/webchat-es5.js"></script>
</head>
<body>
<div id="webchat"></div>
<script type="text/babel" data-presets="env,stage-3,react">
const {
WebChat: { createDirectLine },
WebChatTest: { conditions, createStore, expect, host, pageObjects, timeouts, token }
} = window;

(async function () {
window.WebChat.renderWebChat(
{
directLine: createDirectLine({ token: await token.fetchDirectLineToken() }),
role: 'main',
store: createStore()
},
document.getElementById('webchat')
);

await pageObjects.wait(conditions.uiConnected(), timeouts.directLine);
await expect(document.querySelector('#webchat > *').getAttribute('role')).toBe('main');

await host.done();
})().catch(async err => {
console.error(err);

await host.error(err);
});
</script>
</body>
</html>
8 changes: 8 additions & 0 deletions __tests__/html/accessibility.landmarkRole.valid.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/**
* @jest-environment ./__tests__/html/__jest__/WebChatEnvironment.js
*/

describe('accessibility feature to support "role" attribute', () => {
test('should set "role" if it is valid landmark role', () =>
runHTMLTest('accessibility.landmarkRole.valid'));
});
21 changes: 14 additions & 7 deletions packages/component/src/BasicWebChat.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,10 @@ const TRANSCRIPT_STYLE = {
flex: 1
};

const BasicWebChat = ({ className }) => {
// Subset of landmark roles: https://w3.org/TR/wai-aria/#landmark_roles
const ARIA_LANDMARK_ROLES = ['complementary', 'contentinfo', 'form', 'main', 'region'];

const BasicWebChat = ({ className, role }) => {
const [{ root: rootStyleSet }] = useStyleSet();
const [options] = useStyleOptions();
const styleToEmotionObject = useStyleToEmotionObject();
Expand All @@ -48,11 +51,13 @@ const BasicWebChat = ({ className }) => {
const toasterClassName = styleToEmotionObject(TOASTER_STYLE) + '';
const transcriptClassName = styleToEmotionObject(TRANSCRIPT_STYLE) + '';

// Fallback to "complementary" if specified is not a valid landmark role.
if (!ARIA_LANDMARK_ROLES.includes(role)) {
role = 'complementary';
}

return (
<AccessKeySinkSurface
className={classNames(rootClassName, rootStyleSet + '', (className || '') + '')}
role="complementary"
>
<AccessKeySinkSurface className={classNames(rootClassName, rootStyleSet + '', (className || '') + '')} role={role}>
{!options.hideToaster && <BasicToaster className={toasterClassName} />}
<BasicTranscript className={transcriptClassName} />
<BasicConnectivityStatus className={connectivityStatusClassName} />
Expand All @@ -64,9 +69,11 @@ const BasicWebChat = ({ className }) => {
export default BasicWebChat;

BasicWebChat.defaultProps = {
className: ''
className: '',
role: 'complementary'
};

BasicWebChat.propTypes = {
className: PropTypes.string
className: PropTypes.string,
role: PropTypes.oneOf(ARIA_LANDMARK_ROLES)
};
6 changes: 4 additions & 2 deletions packages/component/src/ReactWebChat.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,22 @@ import Composer from './Composer';
// - They can run hooks outside of activity/attachment middleware
// - They will put <Composer> as very top of their page, and allow buttons on their existing page to send message to bot

const ReactWebChat = ({ className, ...composerProps }) => (
const ReactWebChat = ({ className, role, ...composerProps }) => (
<Composer {...composerProps}>
<BasicWebChat className={className} />
<BasicWebChat className={className} role={role} />
</Composer>
);

export default ReactWebChat;

ReactWebChat.defaultProps = {
className: undefined,
role: undefined,
...Composer.defaultProps
};

ReactWebChat.propTypes = {
className: PropTypes.string,
role: PropTypes.string,
...Composer.propTypes
};

0 comments on commit 8f435f7

Please sign in to comment.