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

Commit

Permalink
[Terra-List] Added prop to Disable Tab Key Navigation for List Item (#…
Browse files Browse the repository at this point in the history
…3944)

Co-authored-by: SM051274 <sm051274@cerner.net>
Co-authored-by: MadanKumarGovindaswamy <132886427+MadanKumarGovindaswamy@users.noreply.github.com>
  • Loading branch information
3 people authored Oct 23, 2023
1 parent 6b6dbdf commit f976f8a
Show file tree
Hide file tree
Showing 17 changed files with 197 additions and 75 deletions.
1 change: 1 addition & 0 deletions packages/terra-core-docs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
* Updated `terra-arrange` examples to use `terra-form-radio`.

* Updated
* Updated `terra-list` examples to include `isTabFocusDisabled` prop.
* Removed `terra-table` as a dependency as the docs have now moved to `terra-framework-docs`.

* Added
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ class SingleSelectList extends React.Component {
super(props);
this.createListItem = this.createListItem.bind(this);
this.handleItemSelection = this.handleItemSelection.bind(this);
this.handleOnClick = this.handleOnClick.bind(this);
this.setListNode = this.setListNode.bind(this);
this.state = { selectedKey: null };
}

Expand All @@ -22,29 +24,48 @@ class SingleSelectList extends React.Component {
}
}

createListItem(itemData) {
handleOnClick() {
this.firstListItem.focus();
}

setListNode(element) {
this.firstListItem = element;
}

createListItem(itemData, index) {
const listItemRef = (index === 0) ? this.setListNode : null;
return (
<Item
key={itemData.key}
isSelectable
isSelected={this.state.selectedKey === itemData.key}
metaData={{ key: itemData.key }}
onSelect={this.handleItemSelection}
refCallback={listItemRef}
>
<Placeholder title={itemData.title} className={cx('placeholder')} />
</Item>
);
}

createListItems(data) {
return data.map(childItem => this.createListItem(childItem));
return data.map((childItem, index) => this.createListItem(childItem, index));
}

render() {
return (
<List dividerStyle="standard" ariaSelectionStyle="single-select">
{this.createListItems(mockData)}
</List>
<div>
<p>
<b>Note</b>
{' '}
Tab key Navigation is disabled for below list items. Inorder to interact with list-item user should set programmatic focus to one of the list item depending on the required scenario.
{' '}
</p>
<button type="button" onClick={this.handleOnClick}>Select Item 0</button>
<List dividerStyle="standard" ariaSelectionStyle="single-select" isTabFocusDisabled>
{this.createListItems(mockData)}
</List>
</div>
);
}
}
Expand Down
3 changes: 3 additions & 0 deletions packages/terra-list/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

## Unreleased

* Added
* Added `isTabFocusDisabled` prop to disable tab key navigation.

* Fixed
* Fixed _property is undefined_ error while navigating with a keyboard.

Expand Down
24 changes: 19 additions & 5 deletions packages/terra-list/src/List.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,11 @@ const propTypes = {
* Whether or not the list item is draggable. List Item is draggable only when it is selectable.
*/
isDraggable: PropTypes.bool,
/**
* ![IMPORTANT](https://badgen.net/badge/UX/Accessibility/blue)
* Whether or not the list item is focusable with Tab key. Ensure alternative way of focusing list item when set to true for best accessibility experience.
*/
isTabFocusDisabled: PropTypes.bool,
/**
* Function callback when the Item is dropped. Parameters:
* @param {Object} result result
Expand All @@ -93,11 +98,12 @@ const propTypes = {
};

const defaultProps = {
ariaSelectionStyle: 'none',
children: [],
dividerStyle: 'none',
isTabFocusDisabled: false,
paddingStyle: 'none',
role: 'none',
ariaSelectionStyle: 'none',
};

const List = ({
Expand All @@ -112,6 +118,7 @@ const List = ({
role,
ariaSelectionStyle,
isDraggable,
isTabFocusDisabled,
onDragEnd,
...customProps
}) => {
Expand Down Expand Up @@ -238,24 +245,30 @@ const List = ({

const cloneListItem = (ListItem, provider) => React.cloneElement(ListItem, {
isDraggable: ListItem?.props?.isSelectable,
isTabFocusDisabled,
refCallback: provider.innerRef,
...provider.draggableProps,
...provider.dragHandleProps,
});

const clone = (object) => React.Children.map(object, (listitem) => React.cloneElement(listitem, {
isTabFocusDisabled,
}));

const renderListDom = () => (
// eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions, jsx-a11y/role-supports-aria-props
<ul
{...customProps}
{...attrSpread}
aria-describedby={ariaDescribedBy}
aria-description={ariaDescription} // eslint-disable-line jsx-a11y/aria-props
// eslint-disable-next-line jsx-a11y/aria-props
aria-description={ariaDescription}
aria-details={ariaDetails}
className={listClassNames}
ref={handleListRef}
onKeyDown={handleKeyDown}
>
{children}
{clone(children)}
</ul>
);

Expand All @@ -268,13 +281,14 @@ const List = ({
)}
>
{(provided) => (
// eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions, jsx-a11y/role-supports-aria-props
/* eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions, jsx-a11y/role-supports-aria-props */
<ul
{...provided.droppableProps}
{...customProps}
{...attrSpread}
aria-describedby={ariaDescribedBy}
aria-description={ariaDescription} // eslint-disable-line jsx-a11y/aria-props
// eslint-disable-next-line jsx-a11y/aria-props
aria-description={ariaDescription}
aria-details={ariaDetails}
className={listClassNames}
ref={(refobj) => {
Expand Down
9 changes: 7 additions & 2 deletions packages/terra-list/src/ListItem.jsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint-disable no-param-reassign */
import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
Expand Down Expand Up @@ -99,7 +100,8 @@ const ListItem = ({
),
customProps.className,
);
const { isDraggable } = customProps;
const { isDraggable, isTabFocusDisabled } = customProps;

const attrSpread = {};

const onFocusResponse = intl.formatMessage({ id: 'Terra.list.focus' });
Expand All @@ -108,7 +110,7 @@ const ListItem = ({
if (isSelectable) {
attrSpread.onClick = ListUtils.wrappedOnClickForItem(onClick, onSelect, metaData);
attrSpread.onKeyDown = ListUtils.wrappedOnKeyDownForItem(onKeyDown, onSelect, metaData);
attrSpread.tabIndex = '0';
attrSpread.tabIndex = (isTabFocusDisabled) ? '-1' : '0';
attrSpread.role = 'option';
attrSpread['aria-selected'] = isSelected;
attrSpread['data-item-show-focus'] = 'true';
Expand All @@ -119,6 +121,9 @@ const ListItem = ({
attrSpread['aria-describedby'] = responseId;
}

delete customProps?.isTabFocusDisabled;
delete customProps?.isDraggable;

return (
<li {...customProps} {...attrSpread} className={listItemClassNames} ref={refCallback}>
{(isDraggable) && (
Expand Down
11 changes: 11 additions & 0 deletions packages/terra-list/tests/jest/ListItem.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,17 @@ it('should render with callback functions', () => {
expect(mockCallBack.mock.calls.length).toEqual(3);
});

it('should render with tabindex -1 when isTabFocusDisabled is set to true', () => {
const mockCallBack = jest.fn();

const shallowComponent = shallowWithIntl(
<ListItem title="test" isSelectable isTabFocusDisabled onSelect={mockCallBack} refCallback={jest.fn()} />,
).dive();

expect(shallowComponent.prop('tabIndex')).toContain('-1');
expect(shallowComponent).toMatchSnapshot();
});

it('correctly applies the theme context className', () => {
const wrapper = mountWithIntl(
<ThemeContextProvider theme={{ className: 'orion-fusion-theme' }}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ exports[`should render List with items 1`] = `
}
}
isDraggable={true}
isTabFocusDisabled={false}
paddingStyle="none"
role="none"
>
Expand Down Expand Up @@ -219,6 +220,7 @@ exports[`should render List with items 1`] = `
draggable={false}
isDraggable={true}
isSelectable={true}
isTabFocusDisabled={false}
key="1"
onDragStart={[Function]}
onTransitionEnd={null}
Expand Down Expand Up @@ -270,6 +272,7 @@ exports[`should render List with items 1`] = `
isDraggable={true}
isSelectable={true}
isSelected={false}
isTabFocusDisabled={false}
onDragStart={[Function]}
onTransitionEnd={null}
refCallback={[Function]}
Expand All @@ -292,7 +295,6 @@ exports[`should render List with items 1`] = `
data-rbd-draggable-context-id="0"
data-rbd-draggable-id="1"
draggable={false}
isDraggable={true}
onBlur={[Function]}
onDragStart={[Function]}
onMouseDown={[Function]}
Expand Down Expand Up @@ -467,6 +469,7 @@ exports[`should render List with items 1`] = `
draggable={false}
isDraggable={true}
isSelectable={true}
isTabFocusDisabled={false}
key="2"
onDragStart={[Function]}
onTransitionEnd={null}
Expand Down Expand Up @@ -518,6 +521,7 @@ exports[`should render List with items 1`] = `
isDraggable={true}
isSelectable={true}
isSelected={false}
isTabFocusDisabled={false}
onDragStart={[Function]}
onTransitionEnd={null}
refCallback={[Function]}
Expand All @@ -540,7 +544,6 @@ exports[`should render List with items 1`] = `
data-rbd-draggable-context-id="0"
data-rbd-draggable-id="2"
draggable={false}
isDraggable={true}
onBlur={[Function]}
onDragStart={[Function]}
onMouseDown={[Function]}
Expand Down Expand Up @@ -674,15 +677,18 @@ exports[`should render List without Draggable items 1`] = `
>
<InjectIntl(ListItem)
isSelectable={true}
key="123"
isTabFocusDisabled={false}
key=".$123"
/>
<InjectIntl(ListItem)
isSelectable={true}
key="124"
isTabFocusDisabled={false}
key=".$124"
/>
<InjectIntl(ListItem)
isSelectable={true}
key="125"
isTabFocusDisabled={false}
key=".$125"
/>
</ul>
`;
Expand Down Expand Up @@ -1005,7 +1011,6 @@ exports[`should render Section with items 1`] = `
data-rbd-draggable-context-id="1"
data-rbd-draggable-id="123"
draggable={false}
isDraggable={true}
onBlur={[Function]}
onDragStart={[Function]}
onMouseDown={[Function]}
Expand Down Expand Up @@ -1253,7 +1258,6 @@ exports[`should render Section with items 1`] = `
data-rbd-draggable-context-id="1"
data-rbd-draggable-id="124"
draggable={false}
isDraggable={true}
onBlur={[Function]}
onDragStart={[Function]}
onMouseDown={[Function]}
Expand Down Expand Up @@ -1699,7 +1703,6 @@ exports[`should render subsection with items 1`] = `
data-rbd-draggable-context-id="2"
data-rbd-draggable-id="13"
draggable={false}
isDraggable={true}
onBlur={[Function]}
onDragStart={[Function]}
onMouseDown={[Function]}
Expand Down Expand Up @@ -1947,7 +1950,6 @@ exports[`should render subsection with items 1`] = `
data-rbd-draggable-context-id="2"
data-rbd-draggable-id="14"
draggable={false}
isDraggable={true}
onBlur={[Function]}
onDragStart={[Function]}
onMouseDown={[Function]}
Expand Down Expand Up @@ -2195,7 +2197,6 @@ exports[`should render subsection with items 1`] = `
data-rbd-draggable-context-id="2"
data-rbd-draggable-id="15"
draggable={false}
isDraggable={true}
onBlur={[Function]}
onDragStart={[Function]}
onMouseDown={[Function]}
Expand Down
Loading

0 comments on commit f976f8a

Please sign in to comment.