Skip to content
This repository has been archived by the owner on May 24, 2024. It is now read-only.

[terra-embedded-content-consumer] - Show focus indicator on iframe of the embedded content when it has focus #1827

Closed
wants to merge 21 commits into from

Conversation

kolkheang
Copy link
Member

@kolkheang kolkheang commented Oct 5, 2023

Summary

Show visual focus indicator on the frame when the embedded content has focus. This allows keyboard only users to see where focus is and use keyboard to scroll the embedded content when content is not fully visible within the viewport.

What was changed:
Showing outline style on the frame when it has focus and remove the outline style when it's not in focus (blur)

Why it was changed:
It's necessary for keyboard only users to see where the focus is and give users indication that while focus is on the frame, they can use arrow keys to scroll the content inside the frame when needed (if content isn't fully visible and scrolling is enabled)

Testing

This change was tested using:

  • WDIO
  • Jest
  • Visual testing (please attach a screenshot or recording)
  • Other (please describe below)
  • No tests are needed

Reviews

In addition to engineering reviews, this PR needs:

  • UX review
  • Accessibility review
  • Functional review

Additional Details

This library depends on the changes in xfc library which is currently under review with oracle-samples/xfc#95

Doc Site: https://engineering.cerner.com/terra-framework/pull/1827/

Screen Recording using keyboard navigation with tab key:
https://github.com/cerner/terra-framework/assets/3170314/71e86961-94a2-4ea9-bf53-a55e25c6bed6

This PR resolves:

UXPLATFORM-9564


Thank you for contributing to Terra.
@cerner/terra

@github-actions github-actions bot temporarily deployed to preview-pr-1827 October 5, 2023 14:04 Destroyed
@kolkheang kolkheang marked this pull request as ready for review October 11, 2023 01:34
@kolkheang kolkheang changed the title [terra-embedded-content-consumer] WIP - Show focus indicator on iframe of the embedded content when it has focus [terra-embedded-content-consumer] - Show focus indicator on iframe of the embedded content when it has focus Oct 11, 2023
@kolkheang kolkheang requested a review from a team October 11, 2023 15:38
package.json Show resolved Hide resolved
@@ -5,6 +5,9 @@
* Added
* Added documentation for FlowsheetDataGrid in `terra-data-grid`.

* Updated
* Updated examples, and tests to show the visual focus indicator on the iframe of the embedded content
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: We can add a . at the EOL.

@@ -4,14 +4,10 @@ import { Consumer } from 'xfc';
import '../providers/EmbeddedContentConsumerCommon.module.scss';

Consumer.init();

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: can we keep the newline here or remove it from the other doc files as well?

@@ -7,7 +7,8 @@ const cx = classNames.bind(styles);

const Provider = () => (
<ProviderTestTemplate>
<div className={cx('content-wrapper')}>
{/* eslint-disable-next-line */}
<div className={cx('content-wrapper')} tabIndex="0" style={{ outline: 'none' }}>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: can we add a new class to use here that includes the outline style?

@scottwilmarth
Copy link

scottwilmarth commented Oct 13, 2023

Allowing the consumers to set a "hasActionableContent" prop (that is named for intent rather than for result) would be good to allow consumers to disable the focus when they KNOW there is actionable content within the frame. BUT, I would not want the default to be the frame doesn't receive focus - would have to default to FALSE. That would provide a more accessible experience out of the box, even though it may not always be perfect.

@scottwilmarth
Copy link

@kolkheang if we add the "hasActionableContent" prop, in addition to removing the visible focus indicator, can we also remove the tabindex="0" to ensure keyboard focus is not set on the container?

@scottwilmarth
Copy link

Please disregard my comments about adding a "hasActionableContent" prop. @kolkheang and I discussed it more and determined this would be moot. From an accessibility perspective, we must show a visible focus indicator if we have something with a tabindex="0" Adding the actionable content prop would have potentially allowed for failures which we do not want to do. As is, the current behavior that Kol provides ensures (as best as possible) that the container can receive keyboard focus when there is scrolling AND that the container has the required visible focus indicator to meet WCAG 2.4.7 Focus Visible criterion.

},
"dependencies": {
"classnames": "^2.2.5",
"prop-types": "^15.5.8"
"prop-types": "^15.5.8",
"style-to-object": "^0.4.2"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should not need to add this third-party dependency.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call. With the current way, there is just 1 style for focus outline, and one style for blur outline and we have two variables for these. We can set the value for each into the frame's style directly. Previously, I was thinking that we could set multiple style attributes for focus or blur.

Comment on lines +7 to +14
$blur-outline: var(--terra-embedded-content-consumer-blur-outline, none);
$focus-outline: var(--terra-embedded-content-consumer-focus-outline, 2px dashed #000);

:export {
/* stylelint-disable property-no-unknown */
blurOutline: $blur-outline;
focusOutline: $focus-outline;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should be able to set the styles using classes. I don't think that this is the correct implementation.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are not for the classes to be applied to an html element like we normally do.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You would add the class to the iFrame element.

Comment on lines +114 to +127
this.xfcFrame?.iframe?.contentWindow?.addEventListener('focus', () => {
const styleObj = parse(focusStyleStr);
Object.entries(styleObj).forEach(([key, value]) => {
this.xfcFrame.iframe.style[key] = value;
});
}, true);

// Listen for blur event and callback function to apply the style
this.xfcFrame?.iframe?.contentWindow?.addEventListener('blur', () => {
const styleObj = parse(blurStyleStr);
Object.entries(styleObj).forEach(([key, value]) => {
this.xfcFrame.iframe.style[key] = value;
});
}, true);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of this logic, you should be able to use the ":focus" psuedo class.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you give an example on how to setup the style and apply to the iframe's style?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Like this?

iframe:focus {
  outline: 2px dashed #000;
}

Copy link
Contributor

@cm9361 cm9361 Oct 13, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would think that your iFrame would have an associated class name or id for styling. However, it would be a similar concept to what you have described here, yes.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The :focus pseudo-class isn't working for iframe. I'll check to see if we can update the classname on the fly for switching the style.

// Cover other scenario where xfc frame style doesn't apply
// such as when `srcdoc` attribute is used which doesn't work
// within xfc's JSONRPC communication.
this.xfcFrame?.iframe?.contentWindow?.addEventListener('focus', () => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The contentWindow gives you a reference to the window for the embedded content. You should be able to use document.documentElement (HTML tag) or document.body to handle the logic as necessary.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I'd like to handle the focus within the iframe element though, not everywhere else in the document body.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you can handle by styling the iFrame element. How are you giving focus to your iFrame? Did you set a tabIndex?

:local {
.clinical-lowlight-theme {
--terra-embedded-content-consumer-focus-outline: 2px dashed #b2b5b6;
--terra-embedded-content-consumer-blur-outline: none;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We shouldn't need a blur outline style. That should just be the normal state of the element.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you saying we can hard-code outline: none to remove the style from the frame when there is no focus anymore? Because we set the outline style when there is focus. And if we don't remove that style, it stays.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, I am saying that you would have CSS for the normal state. When the element is not focused, that CSS will no longer be applied.

@kolkheang
Copy link
Member Author

Based on the meeting several of us had on Monday, there is additional changes that are needed. The changes are specifically related to checking whether the embedded document (in the iframe) has any focusable/interactable element or not and if not (using querySelectorAll), there should be logic to set the tabIndex="0" for the embedded document so that focus can be set on the embedded document when scrolling is enabled, and content is scrollable.

I'll be issuing a new PR with these additional changes and include the existing change on this PR. I'll close this PR out for now.

@kolkheang kolkheang closed this Oct 20, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants