Skip to content

Conversation

@compulim
Copy link
Contributor

@compulim compulim commented Jan 12, 2022

Fixes #3933. Fixes #3934. Fixes #3994. Fixes #4019.

Changelog Entry

Fixed

  • Fixes #3933, #3934, #3994 and #4019, for various accessibility improvements, by @compulim, in PR #4108
    • Added a new keyboard help screen
    • Reduce repetitions when reading message content and briefier readings
    • Separated hints for links and interactive widgets
    • Focus trap when focus is on interactive attachments or Adaptive Cards
    • Using role="feed"/role="article" for chat history and its messages
    • Always assign a message to aria-activedescendant for chat history
    • Updated verbiage from "transcript" to "chat history"
    • Fixed overlapping hit zone causing clicking on bottom edge of message bubble may focus on the next activity instead
    • Fixed typings of useFocus and useLocalizer

Description

Reworked our transcript (a.k.a. chat history) to resolve several issues related to accessibility experience.

<BasicTranscript> is one of our biggest codebase and it also grown organically huge.

Adding a keyboard help screen

Fixes #3933.

Similar to "Skip to content" accessible link, we are adding a keyboard help screen to better aid users to use our UI.

The help screen does not applies to mobile devices as these devices generally does not have arrow keys. To reduce confusion, we will hide the help screen when on mobile devices.

The help screen is the first UI to support light/dark theme, as long as high contrast mode (white-on-black).

image

Three different ways to read the activity

Fixes #3934. Fixes #4019.

We reworked our chat history component to clearly identify 3 different ways to read messages:

  • Live region
    • This will read when one or more messages arrives
    • This will read the message text, and its attachments, e.g. "Yes button";
    • This will NOT read the timestamp as the message just arrived, the timestamp is redundant
    • When one or more messages arrive, it will check if any of them contains links or interactive elements (batched)
      • If links are detected in the message or its attachments, it will read hints about the links, otherwise;
      • If interactive elements, such as button, is detected, it will read hints about interactive content
  • Focus landing on the message (via aria-activedescendant)
    • This will read the message text, and count of attachments, e.g. "2 attachments"
    • This will NOT read the timestamp to make the readings brief
    • If links are detected in the message or its attachments, it will read hints about the links, otherwise;
    • If interactive elements, such as button, is detected, it will read hints about interactive content
  • Browse mode (a.k.a. scan mode)
    • This will read as much details as the message contains
    • When in browse mode, we no longer need to "click to interact", as the user can browser all attachments freely

Wraparound interactive attachments

Yesterday, once focus is land inside the interactive attachment (e.g. Adaptive Cards), pressing TAB pass the last widget of the interactive attachment will focus back to the chat history. Pressing ESC will focus back to the chat history immediately.

Today, after focused inside the interactive attachment, TAB will wraparound until pressing ESC key. This is to align with other HTML elements such as <audio controls>.

role="feed"/role="article"

Yesterday, we were using <ul>/<li> for each activity (a.k.a. message bubble).

Today, we are using role="feed" and role="article". For a few reasons:

  • role="feed" is designed for very long content and also support virtual scrolling via aria-busy
  • We should not use <ul> as it will push the content into second level, despite list-style-type: none is set
  • In WAI-ARIA guideline, role="feed"/role="article" is designed to contain nested interactive elements, such as comment box, etc. This is more aligned to our Adaptive Cards and interactive attachment story

Always have an activity as active, unless the chat history is empty

Fixes #3994.

When the chat history is empty, regardless of whether the chat history is focused or not, we will assign an activity to aria-activedescendant of the chat history. It will be default to the last activity.

Updated verbiage

Yesterday, we refer to the message bubble area as "transcript".

Today, we refer to the message bubble area as "chat history".

Design

Keying activities

Yesterday, when identifying an individual activity, we need to use 2 different pieces of ID: client activity ID and activity ID. This makes our code complex as we often need to deal with two different IDs.

Today, we are keying activities by using a local permanent key. This key will unify both IDs as a unique key. However, this key is local to the browser instance (i.e. refresh on F5).

A new <ActivityKeyer> component is added with several hooks for looking up activities and keys in different ways.

Although we have exposed the React Hooks from this service, they are experimental and not documented. Their signatures may change without notices until we are satisfied with them.

Componentizing services for <BasicTranscript>

Yesterday, we put a lot of business logics inside <BasicTranscript>. It organically grown into a file > 1,000 lines of code and very difficult to maintain.

Today, we separate different business logics as services:

  • Mark activity as read and acknowledged
    • Read/unread is for "New messages" button
    • Acknowledges is for "snap on auto scroll" feature
  • Live region twin
    • Hooks to queue text or rich content to live region for reading by screen reader
    • Remove contents from live region after queued for a short period of time
  • Activity focus
    • Helps manage aria-activedescendant and descendant IDs for every activity
    • Ensures aria-activedescendant must point to one activity and the activity must be in the chat history, otherwise, default to the last activity
    • Hooks to control what activity to focus, including both absolute (by key/ID) and relative (one up/down)
  • Activity tree and renderers
    • Filter out only activities which will be rendered
    • Grouping activities which will be rendered on screen, in a 3D array
    • Should improve performance by memoizing the tree properly
    • Hooks to access the 3D array or as a flattened 1D array
  • Focus trap

Hooks exposed by these services are experimental and not documented for now.

Tweaked bounding box of activities

Yesterday, when clicking on the very bottom edge of every message bubble. It may selected the next activity. This is because the bounding box of message bubbles overlaps with each other.

Today, we make sure the message bubbles are not overlap with each others.

Using compute-scroll-into-view package

Instead of brewing our own calculations for scrollIntoView polyfill, we are using the popular compute-scroll-into-view package, which has about 4M weekly downloads by now.

Other smaller changes

  • Fixed wrong useFocus and useLocalizer typings
  • In hooks, we started freezing arrays that are not supposed to be modified
  • New useMemoWithPrevious hook for further optimization
    • When returning an array with useMemo, sometimes, the array content could be exactly the same
    • Extra code needed to handle useMemo for arrays and they are cumbersome
    • const value = useMemoWithPrevious((prevValue) => { ... }, []) makes it easier to look at previously memoized value to optimize memoization
  • Test harness updates
    • focusedActivity means chat history is focused, and the currently focusing activity
    • activeActivity means the currently activity set as aria-activedescendant, does not necessarily means the chat history is focused
    • getActivityBoundingBoxes returns the bounding boxes of activity DOM elements. They are designed for active descendant focus and is overlapping, so they could be scrolled into view perfectly

Specific Changes

Please see above.

  • I have added tests and executed them locally
  • I have updated CHANGELOG.md
  • I have updated documentation

Review Checklist

This section is for contributors to review your work.

  • Accessibility reviewed (tab order, content readability, alt text, color contrast)
  • Browser and platform compatibilities reviewed
  • CSS styles reviewed (minimal rules, no z-index)
  • Documents reviewed (docs, samples, live demo)
  • Internationalization reviewed (strings, unit formatting)
  • package.json and package-lock.json reviewed
  • Security reviewed (no data URIs, check for nonce leak)
  • Tests reviewed (coverage, legitimacy)

@compulim compulim force-pushed the fix-a11y-transcript branch from 37ab40e to 40c93f0 Compare February 1, 2022 12:11
@compulim compulim marked this pull request as ready for review February 8, 2022 21:24
@compulim compulim force-pushed the fix-a11y-transcript branch from 67fd47e to 669bb64 Compare February 8, 2022 21:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants