Skip to content

Commit 230327e

Browse files
beyackle2beyacklecompulim
authored
Fix ARIA role of transcript when connection is pending (#4420)
* let load state change ARIA role of transcript * fix some errors * fix CHANGELOG.md * fix changelog * apply fixes from PR * Hide element of role="feed" when empty * Update entry Co-authored-by: Benjamin Yackley <beyackle@microsoft.com> Co-authored-by: William Wong <compulim@users.noreply.github.com>
1 parent 4e07ca8 commit 230327e

5 files changed

Lines changed: 75 additions & 10 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
2626

2727
- Card action image alt text should use `imageAltText` field and fallback to `text` field, by [@compulim](https://github.com/compulim) in PR [#4333](https://github.com/microsoft/BotFramework-WebChat/pull/4333)
2828
- Fixes [#4472](https://github.com/microsoft/BotFramework-WebChat/issues/4472). Removed `role` attributes for notification bar and use `<div>` instead of `<ul>`/`<li>`, by [@compulim](https://github.com/compulim) in PR [#4475](https://github.com/microsoft/BotFramework-WebChat/pull/4475)
29+
- Fixes [#4393](https://github.com/microsoft/BotFramework-WebChat/issues/4393). Renders `<section role="feed">` only if there are one or more activities contained within, by [@beyackle2](https://github.com/beyackle2) and [@compulim](https://github.com/compulim), in PR [#4420](https://github.com/microsoft/BotFramework-WebChat/pull/4420)
2930

3031
### Changed
3132

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<!DOCTYPE html>
2+
<html lang="en-US">
3+
<head>
4+
<link href="/assets/index.css" rel="stylesheet" type="text/css" />
5+
<script crossorigin="anonymous" src="/test-harness.js"></script>
6+
<script crossorigin="anonymous" src="/test-page-object.js"></script>
7+
<script crossorigin="anonymous" src="/__dist__/webchat-es5.js"></script>
8+
</head>
9+
<body>
10+
<div id="webchat"></div>
11+
<script>
12+
run(async function () {
13+
const store = testHelpers.createStore();
14+
const directLine = testHelpers.createDirectLineEmulator(store);
15+
16+
// WHEN: Web Chat is rendered without any activities.
17+
WebChat.renderWebChat({ directLine, store }, document.getElementById('webchat'));
18+
19+
await pageConditions.uiConnected();
20+
21+
// THEN: It should not have any role="feed".
22+
expect(pageElements.transcript().querySelector('[role="feed"]')).toBeFalsy();
23+
24+
// THEN: It should not violating any accessibility rules.
25+
pageObjects.verifyDOMIntegrity();
26+
27+
// WHEN: An activity "Hello, World!" arrive.
28+
directLine.emulateIncomingActivity('Hello, World!');
29+
30+
// THEN: It should have 1 activity shown.
31+
await pageConditions.numActivitiesShown(1);
32+
33+
// THEN: It should have role="feed".
34+
expect(pageElements.transcript().querySelector('[role="feed"]')).toBeTruthy();
35+
36+
// THEN: The `role="feed"` element should have at least 1 <article> or `role="article"`.
37+
pageObjects.verifyDOMIntegrity();
38+
});
39+
</script>
40+
</body>
41+
</html>
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
/** @jest-environment ./packages/test/harness/src/host/jest/WebDriverEnvironment.js */
2+
3+
describe('accessibility requirement', () => {
4+
test('should always have at least one <article> under [role="feed"]', () =>
5+
runHTML('accessibility.transcript.initialEmpty.html'));
6+
});

packages/component/src/BasicTranscript.tsx

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import React, { forwardRef, Fragment, useCallback, useMemo, useRef } from 'react
1515

1616
import type { ActivityComponentFactory } from 'botframework-webchat-api';
1717
import type { ActivityElementMap } from './Transcript/types';
18-
import type { FC, KeyboardEventHandler, MutableRefObject, ReactNode, VFC } from 'react';
18+
import type { FC, KeyboardEventHandler, MutableRefObject, ReactNode } from 'react';
1919
import type { WebChatActivity } from 'botframework-webchat-core';
2020

2121
import { android } from './Utils/detectBrowser';
@@ -722,19 +722,23 @@ const InternalTranscriptScrollable: FC<InternalTranscriptScrollableProps> = ({
722722
unread
723723
});
724724

725+
const hasAnyChild = !!React.Children.count(children);
726+
725727
return (
726728
<React.Fragment>
727729
{renderScrollToEndButton && renderScrollToEndButton({ onClick: handleScrollToEndButtonClick })}
728-
{!!React.Children.count(children) && <FocusRedirector redirectRef={terminatorRef} />}
730+
{hasAnyChild && <FocusRedirector redirectRef={terminatorRef} />}
729731
<ReactScrollToBottomPanel className="webchat__basic-transcript__scrollable">
730732
<div aria-hidden={true} className="webchat__basic-transcript__filler" onFocus={onFocusFiller} />
731-
<section
732-
aria-roledescription={transcriptRoleDescription}
733-
className={classNames(activitiesStyleSet + '', 'webchat__basic-transcript__transcript')}
734-
role="feed"
735-
>
736-
{children}
737-
</section>
733+
{hasAnyChild && (
734+
<section
735+
aria-roledescription={transcriptRoleDescription}
736+
className={classNames(activitiesStyleSet + '', 'webchat__basic-transcript__transcript')}
737+
role="feed"
738+
>
739+
{children}
740+
</section>
741+
)}
738742
<BasicTypingIndicator />
739743
</ReactScrollToBottomPanel>
740744
</React.Fragment>
@@ -869,7 +873,7 @@ type BasicTranscriptProps = {
869873
className?: string;
870874
};
871875

872-
const BasicTranscript: VFC<BasicTranscriptProps> = ({ className }) => {
876+
const BasicTranscript: FC<BasicTranscriptProps> = ({ className }) => {
873877
const activityElementMapRef = useRef<ActivityElementMap>(new Map());
874878
const containerRef = useRef<HTMLDivElement>();
875879

packages/test/page-object/src/globals/pageObjects/verifyDOMIntegrity.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,4 +60,17 @@ export default function verifyDOMIntegrity() {
6060
throw new Error(message);
6161
}
6262
});
63+
64+
// Elements of `role="feed"` must have at least 1 child of <article> or `role="article"`
65+
[].forEach.call(document.querySelectorAll('[role="feed"]'), element => {
66+
const hasArticle = [].some.call(element.children, child => child.matches('article, [role="article"]'));
67+
68+
if (!hasArticle) {
69+
const message = 'Elements of role="feed" must have at least 1 children of <article> or role="article".';
70+
71+
console.warn(message, element);
72+
73+
throw new Error(message);
74+
}
75+
});
6376
}

0 commit comments

Comments
 (0)