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

[terra-tag] Create tag-list subcomponent #3854

Merged
merged 80 commits into from
Nov 7, 2023
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
80 commits
Select commit Hold shift + click to select a range
04ce503
Create new component: terra-tag-list
swetasingh9714 Jul 21, 2023
d595654
Remove unwanted comments
swetasingh9714 Jul 21, 2023
ef7b942
Design changes
swetasingh9714 Jul 25, 2023
b7819a7
Remove subcomponent folder and unwanted css file
swetasingh9714 Jul 25, 2023
9a94843
Remove tag-list example
swetasingh9714 Jul 25, 2023
53c049c
Add wdio tests
swetasingh9714 Jul 27, 2023
a780d1c
Design changes
swetasingh9714 Jul 31, 2023
5749fe0
Styling changes
swetasingh9714 Aug 1, 2023
49fd2dc
resolve lint errors and update CHANGELOG.md
swetasingh9714 Sep 1, 2023
a6f83e1
Merge branch 'main' into terra-tag_keyboard-navigation
swetasingh9714 Sep 1, 2023
66f9026
Update wdio snapshots and resolve lint errors
swetasingh9714 Sep 4, 2023
b92fdb5
Update wdio and resolve lint
swetasingh9714 Sep 4, 2023
2c3eefe
update wdio and fix lint
swetasingh9714 Sep 4, 2023
7165cce
Fix lint and wdio
swetasingh9714 Sep 4, 2023
0e0b1d3
Update jest
swetasingh9714 Sep 4, 2023
962c3c4
Changes for mouse click
swetasingh9714 Sep 6, 2023
1ccc5b2
update jest snapshot
swetasingh9714 Sep 6, 2023
f99d6ab
Remove role listItem from tag.jsx"
swetasingh9714 Sep 6, 2023
097129b
Update aria-label
swetasingh9714 Sep 8, 2023
7ee7d3e
Update role and changes for safari browser
swetasingh9714 Sep 14, 2023
c3b2bfc
Update jest snapshot
swetasingh9714 Sep 14, 2023
c810a31
Add Jest test for RollupTag
swetasingh9714 Sep 15, 2023
5788b59
Resolve lint and add test for TagList
swetasingh9714 Sep 15, 2023
bce8b90
Lint resolve
swetasingh9714 Sep 15, 2023
2169ca9
Change role
swetasingh9714 Sep 15, 2023
19a10d3
Address PR comments, Update wdio, Add jest
swetasingh9714 Sep 20, 2023
2035775
Merge branch 'main' into terra-tag_keyboard-navigation
swetasingh9714 Sep 20, 2023
19ba557
Update: Review comments Addresed
MadanKumarGovindaswamy Sep 25, 2023
0d622f1
Merge branch 'main' into terra-tag_keyboard-navigation
MadanKumarGovindaswamy Sep 25, 2023
5e4d2fa
Updated: reference path
MadanKumarGovindaswamy Sep 25, 2023
a3882ae
Updated: wdio snapshots
MadanKumarGovindaswamy Sep 25, 2023
3abbd01
Update jest test case
MadanKumarGovindaswamy Sep 28, 2023
9e33b29
Update: remove expose of rolluptag and tagutils
MadanKumarGovindaswamy Sep 29, 2023
0ceee58
Update: File Rename
MadanKumarGovindaswamy Oct 3, 2023
0543a4a
Update: file rename
MadanKumarGovindaswamy Oct 3, 2023
df0f78c
Update: remove shared util file
MadanKumarGovindaswamy Oct 3, 2023
0a27a00
Update: Added check
MadanKumarGovindaswamy Oct 3, 2023
c2c0873
Update: Translations file
MadanKumarGovindaswamy Oct 3, 2023
8507a1c
Review comment: Remove Tab index set to 0
MadanKumarGovindaswamy Oct 3, 2023
111bd34
Update: Jest file for removing tabindex
MadanKumarGovindaswamy Oct 3, 2023
9c27d95
Update: stop propogation is not required here
MadanKumarGovindaswamy Oct 3, 2023
7d79d70
Update : WDIO
MadanKumarGovindaswamy Oct 4, 2023
38abc90
Update: Remove ariaLive and role in Tag and TagList
MadanKumarGovindaswamy Oct 4, 2023
09e7c02
Update: onTagClick additional internal prop
MadanKumarGovindaswamy Oct 4, 2023
0748ed1
Update: remove hidden text component
MadanKumarGovindaswamy Oct 5, 2023
98c77e4
CSS for RollupTag
swetasingh9714 Oct 5, 2023
a6eebe9
Update: Review comment remove settimeout
MadanKumarGovindaswamy Oct 5, 2023
a19a838
Update: Add aria live for rollup button
MadanKumarGovindaswamy Oct 5, 2023
bab6156
Update: Added terra-button in rolluptag
MadanKumarGovindaswamy Oct 9, 2023
919032c
Update: Jest for replacing terra-button
MadanKumarGovindaswamy Oct 9, 2023
16d118c
Update: WDIO
MadanKumarGovindaswamy Oct 9, 2023
fb7b0b5
Update: Changelog
MadanKumarGovindaswamy Oct 10, 2023
2bbda78
Update: Theme specific css changes
MadanKumarGovindaswamy Oct 10, 2023
363808d
Update: Added css files for rollup tag
MadanKumarGovindaswamy Oct 10, 2023
4b65516
Update: Review comment remove isfocused state
MadanKumarGovindaswamy Oct 11, 2023
26570cc
Updated: WDIO
MadanKumarGovindaswamy Oct 12, 2023
219b228
Update: Remove support of Id's
MadanKumarGovindaswamy Oct 12, 2023
f60ccd8
Update: Remove div wrapper
MadanKumarGovindaswamy Oct 12, 2023
51636dc
Update: JEST
MadanKumarGovindaswamy Oct 12, 2023
39a35e1
Update: Lint error
MadanKumarGovindaswamy Oct 12, 2023
201f716
Merge branch 'main' into terra-tag_keyboard-navigation
supreethmr Oct 17, 2023
8ded50f
updated missed screenshot and removed carry forwarded code from pills
Oct 17, 2023
8a7b382
clean up
Oct 17, 2023
1e89129
replaced target by currentTarget
Oct 17, 2023
f1f4680
Update: review comment
MadanKumarGovindaswamy Oct 19, 2023
b6dcb40
Update RollupTag.module.scss
MadanKumarGovindaswamy Oct 19, 2023
350d6c7
Update: remove border, box shadow
MadanKumarGovindaswamy Oct 19, 2023
e43a8de
Update: remove border box shadow
MadanKumarGovindaswamy Oct 19, 2023
3977947
Update: remove border box shadow
MadanKumarGovindaswamy Oct 19, 2023
aae5ba1
Update: tag background color
MadanKumarGovindaswamy Oct 19, 2023
4db0350
Update: WDIO
MadanKumarGovindaswamy Oct 19, 2023
f19007e
Update: allow consumer onClick Event for tag
MadanKumarGovindaswamy Oct 20, 2023
2228c2c
Update: Review comment remove padding set to 0
MadanKumarGovindaswamy Oct 24, 2023
fe908b3
Update CHANGELOG.md
sdadn Oct 25, 2023
0562656
Merged main into terra-tag_keyboard-navigation
sdadn Oct 25, 2023
21176e0
Merge branch 'main' into terra-tag_keyboard-navigation
MadanKumarGovindaswamy Oct 27, 2023
f8bde41
Merge branch 'main' into terra-tag_keyboard-navigation
MadanKumarGovindaswamy Oct 30, 2023
5fdded6
Merge branch 'main' into terra-tag_keyboard-navigation
sdadn Oct 31, 2023
0192b50
Merge branch 'main' into terra-tag_keyboard-navigation
supreethmr Nov 2, 2023
1dddf2b
Merge branch 'main' into terra-tag_keyboard-navigation
MadanKumarGovindaswamy Nov 6, 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
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { Badge } from 'terra-tag/package.json?dev-site-package';
import TagListExample from './example/TagListExample?dev-site-example';
import TagListPropsTable from 'terra-tag-list/src/TagList?dev-site-props-table';

<Badge />

# Terra Tag List

The Terra Tag List component is a component that groups tags and allows consumers to manage the selection state of each tag in the group.

## Getting Started

- Install with [npmjs](https://www.npmjs.com):
- `npm install terra-tag-list`

<!-- AUTO-GENERATED-CONTENT:START Peer Dependencies -->
## Peer Dependencies

This component requires the following peer dependencies be installed in your app for the component to properly function.

| Peer Dependency | Version |
|-|-|
| react | ^16.8.5 |
| react-dom | ^16.8.5 |

<!-- AUTO-GENERATED-CONTENT:END -->

## Usage

```jsx
import TagList from 'terra-tag-list';
```

## Component Features
* [Cross-Browser Support](https://engineering.cerner.com/terra-ui/about/terra-ui/component-standards#cross-browser-support)
* [Responsive Support](https://engineering.cerner.com/terra-ui/about/terra-ui/component-standards#responsive-support)
* [Mobile Support](https://engineering.cerner.com/terra-ui/about/terra-ui/component-standards#mobile-support)

## Examples
<TagListExample />

## Tag List Props
<TagListPropsTable />
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import React, { useState } from 'react';
import TagList from '../../../../../../terra-tag-list/src/TagList';
import Tag from '../../../../../../terra-tag-list/src/subcomponents/Tag';

const TagListExample = () => {
return (
<div>
<h1>Tag List Example</h1>
<TagList ariaLabel="It is incorrect to remove the parent FilterPills">
<Tag text="Test Tag 1" onClick={() => window.alert('Tag1 has been clicked!')} id={"Tag1"}/>
<Tag text="Test Tag 2" onClick={() => window.alert('Tag2 has been clicked!')} id={"Tag2"}/>
<Tag text="Test Tag 3" onClick={() => window.alert('Tag3 has been clicked!')} id={"Tag3"}/>
<Tag text="Test Tag 4" onClick={() => window.alert('Tag4 has been clicked!')} id={"Tag4"}/>
</TagList>
<TagList ariaLabel="It is incorrect to remove the parent FilterPills">
<Tag text="Test Tag 1" onClick={() => window.alert('Tag1 has been clicked!')} id={"Tag1"}/>
<Tag text="Test Tag 2" onClick={() => window.alert('Tag2 has been clicked!')} id={"Tag2"}/>
<Tag text="Test Tag 3" onClick={() => window.alert('Tag3 has been clicked!')} id={"Tag3"}/>
<Tag text="Test Tag 4" onClick={() => window.alert('Tag4 has been clicked!')} id={"Tag4"}/>
</TagList>
</div>
);
};

export default TagListExample;
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import React from 'react';
import IconTag from 'terra-icon/lib/icon/IconTag';
import Tag from 'terra-tag';
import TagList from 'terra-tag/src/subcomponent/TagList';

const HrefTag = () => (
<div>
<Tag href="http://google.com" text="HREF Tag" />
<Tag icon={<IconTag />} href="http://google.com" text="Icon &amp; Text HREF Tag" />
<TagList>
<Tag href="http://google.com" text="HREF Tag" id={"Tag1"}/>
<Tag icon={<IconTag />} href="http://google.com" text="Icon &amp; Text HREF Tag" id={"Tag2"}/>
</TagList>
</div>
);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
import React from 'react';
import IconTag from 'terra-icon/lib/icon/IconTag';
import Tag from 'terra-tag';
import TagList from 'terra-tag/src/subcomponent/TagList';

const TagDefault = () => (
<div>
{/* eslint-disable no-alert */}
<Tag text="Default OnClick Tag" onClick={() => window.alert('Tag has been clicked!')} />
<Tag icon={<IconTag />} onClick={() => window.alert('Tag has been clicked!')} text="Icon &amp; Text OnClick Tag" />
{/* eslint-enable no-alert */}
<TagList>
{/* eslint-disable no-alert */}
<Tag text="Default OnClick Tag" onClick={() => window.alert('Tag1 has been clicked!')} id={"Tag1"} />
<Tag icon={<IconTag />} onClick={() => window.alert('Tag2 has been clicked!')} text="Icon &amp; Text OnClick Tag" id={"Tag2"} />
<Tag icon={<IconTag />} onClick={() => window.alert('Tag 3')} text="Icon &amp; Tag 3" id={"Tag3"} />
<Tag icon={<IconTag />} onClick={() => window.alert('Tag 4')} text="Icon &amp; Tag 4" id={"Tag4"} />
<Tag text="Tag 5" onClick={() => window.alert('Tag 5')} id={"Tag5"} />
{/* eslint-enable no-alert */}
</TagList>
</div>
);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import React from 'react';
import IconTag from 'terra-icon/lib/icon/IconTag';
import Tag from 'terra-tag';
import TagList from 'terra-tag/src/subcomponent/TagList';

const TagFallbacks = () => (
<div>
<Tag text="No OnClick/HREF Tag" />
<Tag icon={<IconTag />} text="Icon &amp; Text - No OnClick/HREF Tag" />
<TagList>
<Tag text="No OnClick/HREF Tag" id={"Tag1"}/>
<Tag icon={<IconTag />} text="Icon &amp; Text - No OnClick/HREF Tag" id={"Tag2"}/>
</TagList>
</div>
);

Expand Down
5 changes: 5 additions & 0 deletions packages/terra-tag/src/Tag.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,10 @@ class Tag extends React.Component {
// Apply focus styles for keyboard navigation
if (event.nativeEvent.keyCode === KeyCode.KEY_TAB) {
this.setState({ focused: true });
}else if (event.nativeEvent.keyCode === KeyCode.KEY_RIGHT) {
this.setState({ focused: true });
}else if (event.nativeEvent.keyCode === KeyCode.KEY_LEFT) {
this.setState({ focused: true });
swetasingh9714 marked this conversation as resolved.
Show resolved Hide resolved
}

if (this.props.onKeyUp) {
Expand Down Expand Up @@ -106,6 +110,7 @@ class Tag extends React.Component {
onClick={onClick}
onFocus={onFocus}
href={href}
data-terra-tag
MadanKumarGovindaswamy marked this conversation as resolved.
Show resolved Hide resolved
>
{tagIcon}
{text}
Expand Down
177 changes: 177 additions & 0 deletions packages/terra-tag/src/subcomponent/TagList.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
import React, {
useCallback, useLayoutEffect, useRef, useState
} from 'react';
import {
KEY_LEFT,
KEY_RIGHT,
} from 'keycode-js';
import classNames from 'classnames';
import classNamesBind from 'classnames/bind';
import PropTypes from 'prop-types';
import ResizeObserver from 'resize-observer-polyfill';
import ThemeContext from 'terra-theme-context';
import { injectIntl } from 'react-intl';
import styles from './TagList.module.scss';

const cx = classNamesBind.bind(styles);

const propTypes = {
/**
* The content to be shown in the tags container.
*/
children: PropTypes.node,
};

const TagList = (props) => {
const {
children,
...customProps
} = props;

const theme = React.useContext(ThemeContext);
const [containerTabindex, setContainerTabindex] = useState('-1');
const [updatedCount, setUpdatedCount] = useState(React.Children.count(children));
const currentTag = useRef(); // ID of the tag that will receive focus
const filterTagsRef = useRef();
const focusNode = useRef(0);

// Modifies the tabindex of the tag
const setTabIndex = (val) => {
const currentNode = currentTag.current ? filterTagsRef.current.querySelector(`#${currentTag.current}`) : null;
if (currentNode) {
currentNode.setAttribute('tabIndex', val);
}
};

// Sets focus to the current tag with tabindex 0
const focusCurrentNode = () => {
const currentNode = currentTag.current ? filterTagsRef.current.querySelector(`#${currentTag.current}`) : null;
if (currentNode) {
currentNode.focus();
}
};

/**
* Takes 'tag' element and tabindex 'val' as inputs and sets the tag elements tabindex.
*/
const setTagsTabIndex = (tags, val) => {
for (let i = 0; i < tags.length; i += 1) {
tags[i].setAttribute('tabindex', val);
}
};

// resets all tag nodes tabindex to -1, except for the one tag that receives focus.
const resetTabIndex = useCallback(() => {
const tags = [...filterTagsRef.current.querySelectorAll('[data-terra-tag]')];

if (tags.length > 0 && focusNode.current < tags.length) {
setTagsTabIndex(tags, '-1');
currentTag.current = tags[focusNode.current].id;
setTabIndex('0');
}
}, []);

const handleResize = useCallback((entries) => {
if (!Array.isArray(entries)) {
return;
}

setUpdatedCount(React.Children.count(children));

resetTabIndex();
}, [children, resetTabIndex]);

useLayoutEffect(() => {
let observer = new ResizeObserver((entries) => {
handleResize(entries);
});
observer.observe(filterTagsRef.current.parentNode);

return () => {
observer.disconnect();
observer = null;
};
}, [children, handleResize]);

const focusNextNode = (tags) => {
if (focusNode.current + 1 <= tags.length) {
setTabIndex('-1');
if (focusNode.current + 1 < tags.length) { // focus the next available tag
focusNode.current += 1;
currentTag.current = children[focusNode.current].props.id;
}
setTabIndex('0');
focusCurrentNode();
}
};

const focusPreviousNode = () => {
if (focusNode.current >= 1) {
setTabIndex('-1');
focusNode.current -= 1;
currentTag.current = children[focusNode.current].props.id;
setTabIndex('0');
focusCurrentNode();
}
};

const handleTagListKeyDown = (event) => {
const tags = [...filterTagsRef.current.querySelectorAll('button, a')];

switch (event.keyCode) {
case KEY_RIGHT:
event.preventDefault();
focusNextNode(tags);
break;
case KEY_LEFT:
event.preventDefault();
focusPreviousNode(tags);
break;
default:
break;
}
};

const handleTagListOnblur = () => setContainerTabindex('-1');

const filterTagsProps = {};
filterTagsProps.onKeyDown = handleTagListKeyDown;
filterTagsProps.onBlur = handleTagListOnblur;

const tagListClassNames = classNames(
cx([
'tags-group',
theme.className,
]),
customProps.className,
);

const renderChildren = (items) => {
const tags = React.Children.map(items, (tag) => {
if (React.isValidElement(tag)) {
return React.cloneElement(tag);
}
return undefined;
});

const reducedArray = tags.splice(0, updatedCount);
return reducedArray;
};

return (
<div
{...customProps}
{...filterTagsProps}
className={tagListClassNames}
ref={filterTagsRef}
role="list"
tabIndex={containerTabindex}
>
{children ? renderChildren(children) : []}
</div>
);
};

TagList.propTypes = propTypes;

export default injectIntl(TagList);
Loading