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

[Terra-Dropdown] VO does not announce Expanded state and Index of list items while navigating through arrow keys #3805

Merged
merged 44 commits into from
Jul 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
3c92a1a
Added aria-label to announce index of dropdown listitems
May 19, 2023
68f588e
Resolved merge conflicts
May 23, 2023
bc7b3f2
clear aria-label while dropdown opens
May 24, 2023
43f300d
added aria-haspopup
May 24, 2023
22eeb9b
updated jest testcases
May 26, 2023
6808c17
Merge branch 'main' of https://github.com/cerner/terra-core into AH10…
Jun 2, 2023
6430ad4
Added aria-label for expand/collapse state
Jun 2, 2023
1adf2f2
removes sticky focus from button on safari on VO mode.
Jun 2, 2023
2731c94
Default Dropdown Button
Jun 6, 2023
452467c
Implemented review comments
Jun 6, 2023
2ca0d2c
Update: voice over and focus fix for mac
MadanKumarGovindaswamy Jun 12, 2023
8c91883
Merge branch 'main' of https://github.com/cerner/terra-core into AH10…
Jun 12, 2023
c01ce54
Merge branch 'AH106586/Dropdown-announce-index' of https://github.com…
Jun 12, 2023
f81a8b5
Update: keyboard focus fix on edge browser
MadanKumarGovindaswamy Jun 12, 2023
dd64555
Removed openedViaKeyboard and buttonRef props
Jun 14, 2023
05892e7
Updated WDIO spec files
Jun 14, 2023
82755a4
Updated WDIO spec files
Jun 15, 2023
e77e019
updated Changelog
Jun 15, 2023
0b6bada
WDIO snapshots update
Jun 15, 2023
34742e0
WDIO Snapshots Update
Jun 16, 2023
c193201
Implemented review comments
Jun 19, 2023
cc6fbef
Merge branch 'main' of https://github.com/cerner/terra-core into AH10…
Jun 19, 2023
db727b8
Merge branches 'AH106586/Dropdown-announce-index' and 'AH106586/Dropd…
Jun 19, 2023
e90e80c
Updated translations and added testcase
Jun 19, 2023
7f9273b
Updating WDIO Snapshots
Jun 20, 2023
c9b83aa
Removed setButtonNode
Jun 20, 2023
bc318e2
Merge branch 'main' of https://github.com/cerner/terra-core into AH10…
Jun 28, 2023
120d514
Added translations
Jun 28, 2023
898507c
modified test-case
Jun 28, 2023
943af57
Update: Fix Focus issue when dropdown is closed
MadanKumarGovindaswamy Jul 7, 2023
5c14beb
Update: lint error
MadanKumarGovindaswamy Jul 7, 2023
9e77a10
Update: Added back buttonRef Prop
MadanKumarGovindaswamy Jul 7, 2023
123b00a
Update: handle outside click as a separate function
MadanKumarGovindaswamy Jul 10, 2023
ea0230c
Update: adding test case for dropdown outside click
MadanKumarGovindaswamy Jul 11, 2023
2f1374e
update: test case for dropdown focus
MadanKumarGovindaswamy Jul 11, 2023
16de4db
Merge branch 'main' of https://github.com/cerner/terra-core into AH10…
Jul 13, 2023
8efdc84
Merge branch 'AH106586/Dropdown-announce-index' of https://github.com…
Jul 13, 2023
df5c65e
updated testcase as per review comments
Jul 13, 2023
8ef8326
modified as per review comments
Jul 14, 2023
7430f42
Merge branch 'main' of https://github.com/cerner/terra-core into AH10…
Jul 14, 2023
bb91545
Removed Dot
Jul 14, 2023
275521f
Removed Dot for selected text and Added activeOption for de locale
Jul 17, 2023
5b8ffd3
modified fi-fi locale
Jul 17, 2023
3e7cc7e
worked on review comments
Jul 17, 2023
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
4 changes: 2 additions & 2 deletions packages/terra-dropdown-button/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

## Unreleased

* Added
* Added focus styles for dropdown list items.
* Added
* Added focus styles and screenreader support to announce expand/collapse state and index for dropdown list items.

## 1.34.0 - (May 9, 2023)

Expand Down
29 changes: 7 additions & 22 deletions packages/terra-dropdown-button/src/DropdownButton.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,20 +69,18 @@ class DropdownButton extends React.Component {
this.setListNode = this.setListNode.bind(this);
this.toggleDropDown = this.toggleDropDown.bind(this);
this.state = {
isOpen: false, isActive: false, openedViaKeyboard: false, selectText: '',
isOpen: false, isActive: false, selectText: '',
};
}

handleDropdownButtonClick(event) {
if (this.state.isOpen) {
this.setState({ openedViaKeyboard: false });
}
this.toggleDropDown(event);
this.setState({ selectText: '' });
}

handleDropdownRequestClose(callback) {
const onSelectCallback = typeof callback === 'function' ? callback : undefined;
this.setState({ isOpen: false, openedViaKeyboard: false, isActive: false }, onSelectCallback);
this.setState({ isOpen: false, isActive: false }, onSelectCallback);
}

handleKeyDown(event) {
Expand All @@ -91,25 +89,14 @@ class DropdownButton extends React.Component {
}
if (event.keyCode === KeyCode.KEY_SPACE || event.keyCode === KeyCode.KEY_RETURN) {
// In FireFox active styles don't get applied on space
this.setState({ isActive: true, openedViaKeyboard: true });
this.setState({ isActive: true });
/*
Prevent the callback from being called repeatedly if the RETURN or SPACE key is held down.
The keyDown event of native html button triggers Onclick() event on RETURN or SPACE key press.
where holding RETURN key for longer time will call dropdownClick() event repeatedly which would cause
the dropdown to open and close itself.
*/
event.preventDefault();
} else if (event.keyCode === KeyCode.KEY_DOWN && this.state.isOpen && !this.state.openedViaKeyboard) {
// set focus to first list element on down arrow key press only when dropdown is opened by mouse click.
const listOptions = this.dropdownList.querySelectorAll('[data-terra-dropdown-list-item]');
listOptions[0].focus();
// prevent handleFocus() callback of DropdownList.
event.preventDefault();
} else if (event.keyCode === KeyCode.KEY_UP && this.state.isOpen && !this.state.openedViaKeyboard) {
// set focus to last list element on up arrow key press only when dropdown is opened by mouse click
const listOptions = this.dropdownList.querySelectorAll('[data-terra-dropdown-list-item]');
listOptions[listOptions.length - 1].focus();
event.preventDefault();
} else if (event.keyCode === KeyCode.KEY_TAB) {
this.handleDropdownRequestClose();
}
Expand Down Expand Up @@ -166,7 +153,7 @@ class DropdownButton extends React.Component {
const theme = this.context;

const {
isOpen, isActive, openedViaKeyboard, selectText,
isOpen, isActive, selectText,
} = this.state;
const selectedLabel = intl.formatMessage({ id: 'Terra.dropdownButton.selected' });
const classnames = cx(
Expand All @@ -191,9 +178,8 @@ class DropdownButton extends React.Component {
isCompact={isCompact}
isDisabled={isDisabled}
requestClose={this.handleDropdownRequestClose}
openedViaKeyboard={openedViaKeyboard}
buttonRef={this.getButtonNode}
refCallback={this.setListNode}
buttonRef={this.getButtonNode}
getSelectedOptionText={this.getSelectedOptionText}
>
<button
Expand All @@ -205,9 +191,8 @@ class DropdownButton extends React.Component {
disabled={isDisabled}
tabIndex={isDisabled ? '-1' : undefined}
aria-disabled={isDisabled}
aria-expanded={isOpen}
aria-haspopup="menu"
ref={this.setButtonNode}
aria-expanded={isOpen}
aria-label={selectText ? `${selectText}, ${selectedLabel}` : ''}
onBlur={this.handleBlur}
>
Expand Down
16 changes: 6 additions & 10 deletions packages/terra-dropdown-button/src/Item.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,17 +53,13 @@ const Item = ({
}}
tabIndex="0"
role="menuitem"
className={cx(
'item',
{ active: isActive },
theme.className,
)}
>
<div
role="none"
className={cx(
'item',
{ active: isActive },
theme.className,
)}
>
{label}
</div>
{label}
</li>
);
};
Expand Down
29 changes: 5 additions & 24 deletions packages/terra-dropdown-button/src/SplitButton.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -80,21 +80,19 @@ class SplitButton extends React.Component {
this.handlePrimaryKeyUp = this.handlePrimaryKeyUp.bind(this);
this.handleCaretKeyDown = this.handleCaretKeyDown.bind(this);
this.handleCaretKeyUp = this.handleCaretKeyUp.bind(this);
this.setButtonNode = this.setButtonNode.bind(this);
this.getButtonNode = this.getButtonNode.bind(this);
this.setButtonNode = this.setButtonNode.bind(this);
this.setListNode = this.setListNode.bind(this);
this.toggleDropDown = this.toggleDropDown.bind(this);

this.state = {
isOpen: false, caretIsActive: false, primaryIsActive: false, openedViaKeyboard: false, selectText: '',
isOpen: false, caretIsActive: false, primaryIsActive: false, selectText: '',
};
}

handleDropdownButtonClick(event) {
if (this.state.isOpen) {
this.setState({ openedViaKeyboard: false });
}
this.toggleDropDown(event);
this.setState({ selectText: '' });
}

handlePrimaryButtonClick(event) {
Expand All @@ -108,7 +106,7 @@ class SplitButton extends React.Component {

handleDropdownRequestClose(callback) {
const onSelectCallback = typeof callback === 'function' ? callback : undefined;
this.setState({ isOpen: false, openedViaKeyboard: false, caretIsActive: false }, onSelectCallback);
this.setState({ isOpen: false, caretIsActive: false }, onSelectCallback);
}

/*
Expand All @@ -132,25 +130,14 @@ class SplitButton extends React.Component {
}
if (event.keyCode === KeyCode.KEY_SPACE || event.keyCode === KeyCode.KEY_RETURN) {
// In FireFox active styles don't get applied onKeyDown
this.setState({ caretIsActive: true, openedViaKeyboard: true });
this.setState({ caretIsActive: true });
/*
Prevent the callback from being called repeatedly if the RETURN or SPACE key is held down.
The keyDown event of native html button triggers Onclick() event on RETURN or SPACE key press.
where holding RETURN key for longer time will call dropdownClick() event repeatedly which would cause
the dropdown to open and close itself.
*/
event.preventDefault();
} else if (event.keyCode === KeyCode.KEY_DOWN && this.state.isOpen && !this.state.openedViaKeyboard) {
// set focus to first list element on down arrow key press only when dropdown is opened by mouse click.
const listOptions = this.dropdownList.querySelectorAll('[data-terra-dropdown-list-item]');
listOptions[0].focus();
// prevent handleFocus() callback of DropdownList.
event.preventDefault();
} else if (event.keyCode === KeyCode.KEY_UP && this.state.isOpen && !this.state.openedViaKeyboard) {
// set focus to last list element on up arrow key press only when dropdown is opened by mouse click
const listOptions = this.dropdownList.querySelectorAll('[data-terra-dropdown-list-item]');
listOptions[listOptions.length - 1].focus();
event.preventDefault();
} else if (event.keyCode === KeyCode.KEY_TAB) {
this.handleDropdownRequestClose();
}
Expand Down Expand Up @@ -180,10 +167,6 @@ class SplitButton extends React.Component {
this.setState({ selectText: selectedOptionText });
}

handleFocus = () => {
this.setState({ selectText: '' });
};

handleBlur = () => {
this.setState({ selectText: '' });
};
Expand Down Expand Up @@ -215,7 +198,6 @@ class SplitButton extends React.Component {
isOpen,
primaryIsActive,
caretIsActive,
openedViaKeyboard,
selectText,
} = this.state;

Expand Down Expand Up @@ -253,7 +235,6 @@ class SplitButton extends React.Component {
isBlock={isBlock}
isCompact={isCompact}
isDisabled={isDisabled}
openedViaKeyboard={openedViaKeyboard}
buttonRef={this.getButtonNode}
refCallback={this.setListNode}
getSelectedOptionText={this.getSelectedOptionText}
Expand Down
79 changes: 45 additions & 34 deletions packages/terra-dropdown-button/src/_Dropdown.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from 'react';
import React, { useRef, useEffect } from 'react';
import PropTypes from 'prop-types';
import FocusTrap from 'focus-trap-react';
import Hookshot from 'terra-hookshot';
Expand Down Expand Up @@ -27,52 +27,63 @@ const propTypes = {
*/
width: PropTypes.string,
/**
* Whether or not dropdown is opened using keyboard.
* Ref callback for the dropdown list DOM element.
*/
openedViaKeyboard: PropTypes.bool,
refCallback: PropTypes.func,
/**
* Callback for reference of the dropdown button
*/
buttonRef: PropTypes.func,
/**
* Ref callback for the dropdown list DOM element.
*/
refCallback: PropTypes.func,
/**
* Callback for the dropdown list selected option.
*/
getSelectedOptionText: PropTypes.func,
};

const Dropdown = ({
requestClose, isOpen, targetRef, children, width, openedViaKeyboard, buttonRef, refCallback, getSelectedOptionText,
}) => (
<Hookshot
isOpen={isOpen}
isEnabled
targetRef={targetRef}
attachmentBehavior="flip"
contentAttachment={{ vertical: 'top', horizontal: 'start' }}
targetAttachment={{ vertical: 'bottom', horizontal: 'start' }}
>
<Hookshot.Content
onEsc={requestClose}
onOutsideClick={requestClose}
>
<FocusTrap focusTrapOptions={{ returnFocusOnDeactivate: true, initialFocus: openedViaKeyboard ? '' : buttonRef, clickOutsideDeactivates: true }}>
<DropdownList
requestClose={requestClose}
width={width}
refCallback={refCallback}
getSelectedOptionText={getSelectedOptionText}
>
{children}
</DropdownList>
</FocusTrap>
</Hookshot.Content>
</Hookshot>
);
requestClose, isOpen, targetRef, children, width, refCallback, buttonRef, getSelectedOptionText,
}) => {
const buttonFocused = useRef(false);
useEffect(() => {
// added this change to bring focus back to button when dropdown list is closed.
if (buttonFocused.current && buttonRef) {
buttonFocused.current = false;
buttonRef().focus();
}
}, [isOpen, buttonRef]);

const handleClose = () => {
buttonFocused.current = true;
requestClose();
};

return (
<Hookshot
isOpen={isOpen}
isEnabled
targetRef={targetRef}
attachmentBehavior="flip"
contentAttachment={{ vertical: 'top', horizontal: 'start' }}
targetAttachment={{ vertical: 'bottom', horizontal: 'start' }}
>
<Hookshot.Content
onEsc={requestClose}
onOutsideClick={handleClose}
>
<FocusTrap focusTrapOptions={{ returnFocusOnDeactivate: true, clickOutsideDeactivates: true }}>
<DropdownList
requestClose={requestClose}
width={width}
refCallback={refCallback}
getSelectedOptionText={getSelectedOptionText}
>
{children}
</DropdownList>
</FocusTrap>
</Hookshot.Content>
</Hookshot>
);
};
Dropdown.propTypes = propTypes;

export default Dropdown;
13 changes: 3 additions & 10 deletions packages/terra-dropdown-button/src/_DropdownButtonBase.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,17 +38,13 @@ const propTypes = {
*/
isDisabled: PropTypes.bool,
/**
* Whether or not dropdown is opened using keyboard.
* Ref callback for the dropdown list DOM element.
*/
openedViaKeyboard: PropTypes.bool,
refCallback: PropTypes.func,
/**
* Callback for reference of the dropdown button
*/
buttonRef: PropTypes.func,
/**
* Ref callback for the dropdown list DOM element.
*/
refCallback: PropTypes.func,
/**
* Callback for the dropdown list selected option.
*/
Expand All @@ -59,7 +55,6 @@ const defaultProps = {
isBlock: false,
isCompact: false,
isDisabled: false,
openedViaKeyboard: false,
};

class DropdownButtonBase extends React.Component {
Expand Down Expand Up @@ -89,9 +84,8 @@ class DropdownButtonBase extends React.Component {
isBlock,
isCompact,
isDisabled,
openedViaKeyboard,
buttonRef,
refCallback,
buttonRef,
getSelectedOptionText,
...customProps
} = this.props;
Expand Down Expand Up @@ -126,7 +120,6 @@ class DropdownButtonBase extends React.Component {
isOpen={isOpen}
requestClose={requestClose}
width={calcWidth}
openedViaKeyboard={openedViaKeyboard}
buttonRef={buttonRef}
refCallback={refCallback}
getSelectedOptionText={getSelectedOptionText}
Expand Down
Loading
Loading