diff --git a/packages/experimental/src/components/TagSet/TagSet.js b/packages/experimental/src/components/TagSet/TagSet.js
index f50d2ac35d..c835eb95e5 100644
--- a/packages/experimental/src/components/TagSet/TagSet.js
+++ b/packages/experimental/src/components/TagSet/TagSet.js
@@ -13,13 +13,12 @@ import cx from 'classnames';
// import { settings } from 'carbon-components';
// const { prefix } = settings;
-import { Tag, Tooltip } from 'carbon-components-react';
-
+import { Link, Modal, Search, Tag, Tooltip } from 'carbon-components-react';
import { expPrefix } from '../../global/js/settings';
import ReactResizeDetector from 'react-resize-detector';
-const blockClass = `${expPrefix}-tag-set`;
+export const blockClass = `${expPrefix}-tag-set`;
export const TagSet = ({
children,
@@ -27,16 +26,24 @@ export const TagSet = ({
maxVisibleTags,
rightAlign,
overflowDirection,
+ showAllModalHeading,
+ showAllSearchLabel,
+ showAllSearchPlaceHolderText,
+ showAllTagsLabel,
}) => {
const [displayCount, setDisplayCount] = useState(3);
const [displayedTags, setDisplayedTags] = useState([]);
const [overflowTags, setOverflowTags] = useState([]);
const [allTags, setAllTags] = useState([]);
+ const [filteredAllTags, setFilteredAllTags] = useState([]);
const [tipOpen, setTipOpen] = useState(false);
+ const [showAllModalOpen, setShowAllModalOpen] = useState(false);
+ const [search, setSearch] = useState('');
const tagSet = useRef(null);
const displayedArea = useRef(null);
const sizingTags = useRef([]);
const overflowTag = useRef(null);
+ const overflowTagContent = useRef(null);
useEffect(() => {
setAllTags(
@@ -64,13 +71,15 @@ export const TagSet = ({
);
} else {
- newOverflowTags.push(
-
- {React.cloneElement(child)}
-
- );
+ if (newOverflowTags.length < 10) {
+ newOverflowTags.push(
+
+ {React.cloneElement(child)}
+
+ );
+ }
}
});
@@ -78,6 +87,33 @@ export const TagSet = ({
setOverflowTags(newOverflowTags);
}, [children, displayCount]);
+ useEffect(() => {
+ if (showAllModalOpen) {
+ const newFilteredAllTags = [];
+ children.forEach((child) => {
+ const dataSearch = child.props['data-search']?.toLocaleLowerCase();
+ const contentsAsString = child.props.children
+ .toString()
+ .toLocaleLowerCase();
+ if (
+ (dataSearch && dataSearch.indexOf(search) > -1) ||
+ contentsAsString.indexOf(search) > -1
+ ) {
+ newFilteredAllTags.push(
+
+ {React.cloneElement(child)}
+
+ );
+ }
+ });
+ setFilteredAllTags(newFilteredAllTags);
+ } else {
+ setFilteredAllTags([]);
+ }
+ }, [showAllModalOpen, children, search]);
+
const checkFullyVisibleTags = () => {
// how many will fit?
let willFit = 0;
@@ -127,6 +163,43 @@ export const TagSet = ({
checkFullyVisibleTags();
};
+ const handleModalClose = () => {
+ setShowAllModalOpen(false);
+ };
+
+ const handleShowAllTagsClick = (ev) => {
+ ev.stopPropagation();
+ ev.preventDefault();
+ setTipOpen(false);
+ setShowAllModalOpen(true);
+ };
+
+ const handleSearch = (ev) => {
+ setSearch(ev.target.value);
+ };
+
+ const handleClickOutsideCheck = (ev) => {
+ const tooltipEl = overflowTagContent.current?.parentElement?.parentElement;
+ if (
+ tooltipEl !== undefined &&
+ (tooltipEl === ev.target || tooltipEl.contains(ev.target))
+ ) {
+ // inside click
+ return;
+ }
+ hideTip(ev);
+ };
+
+ useEffect(() => {
+ // Check for a click outside of the tooltip
+ document.addEventListener('mousedown', handleClickOutsideCheck);
+ // remove listener on destroy
+ return () => {
+ document.removeEventListener('mousedown', handleClickOutsideCheck);
+ };
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, []);
+
return (
@@ -148,24 +221,51 @@ export const TagSet = ({
+ })}
+ onFocus={showTip}>
+{overflowTags.length}}
- showIcon={false}
open={tipOpen}
+ triggerText={+{children.length - displayedTags.length}}
+ showIcon={false}
ref={overflowTag}>
- {overflowTags}
+
+ {overflowTags}
+ {overflowTags.length >= 10 && (
+
+ {showAllTagsLabel}
+
+ )}
+
+
+
+
+ {filteredAllTags}
+
+
);
@@ -192,8 +292,28 @@ TagSet.propTypes = {
* align tags to right of available space
*/
rightAlign: PropTypes.bool,
+ /**
+ * heading for the show all modal
+ */
+ showAllModalHeading: PropTypes.string,
+ /**
+ * label text for the show all search
+ */
+ showAllSearchLabel: PropTypes.string,
+ /**
+ * placeholder text for the show all search
+ */
+ showAllSearchPlaceHolderText: PropTypes.string,
+ /**
+ * label for the overflow show all tags link
+ */
+ showAllTagsLabel: PropTypes.string,
};
TagSet.defaultProps = {
overflowDirection: 'bottom',
+ showAllModalHeading: 'All tags',
+ showAllSearchLabel: 'Search all tags',
+ showAllSearchPlaceHolderText: 'Search all tags',
+ showAllTagsLabel: 'View all tags',
};
diff --git a/packages/experimental/src/components/TagSet/TagSet.stories.js b/packages/experimental/src/components/TagSet/TagSet.stories.js
index eee3eca0df..87071c816f 100644
--- a/packages/experimental/src/components/TagSet/TagSet.stories.js
+++ b/packages/experimental/src/components/TagSet/TagSet.stories.js
@@ -7,7 +7,7 @@
import React from 'react';
-import { TagSet } from './TagSet';
+import { TagSet, blockClass } from './TagSet';
import { Tag } from 'carbon-components-react';
import styles from './_storybook-styles.scss'; // import index in case more files are added later.
@@ -27,6 +27,101 @@ const TagItems = [
,
];
+const ManyTagItems = [
+ {
+ label: 'One',
+ type: 'blue',
+ dataSearch: 'one',
+ },
+ {
+ label: 'Two',
+ type: 'red',
+ filter: true,
+ },
+ {
+ label: 'Three',
+ type: 'cyan',
+ },
+ {
+ label: 'Four',
+ type: 'high-contrast',
+ },
+ {
+ label: 'Five',
+ type: 'blue',
+ },
+ {
+ label: 'Six',
+ type: 'red',
+ },
+ {
+ label: 'Seven',
+ type: 'cyan',
+ filter: true,
+ },
+ {
+ label: 'Eight',
+ type: 'high-contrast',
+ },
+ {
+ label: 'Nine',
+ type: 'red',
+ },
+ {
+ label: 'Ten',
+ type: 'blue',
+ filter: true,
+ },
+ {
+ label: 'Eleven',
+ type: 'cyan',
+ },
+ {
+ label: 'Twelve',
+ type: 'high-contrast',
+ dataSearch: 'twelve',
+ },
+ {
+ label: 'Thirteen',
+ type: 'red',
+ },
+ {
+ label: 'Fourteen',
+ type: 'cyan',
+ },
+ {
+ label: 'Fifteen',
+ type: 'blue',
+ filter: true,
+ },
+ {
+ label: 'Sixteen',
+ type: 'high-contrast',
+ filter: true,
+ },
+ {
+ label: 'Seventeen',
+ type: 'red',
+ },
+ {
+ label: 'Eighteen',
+ type: 'cyan',
+ filter: true,
+ },
+ {
+ label: 'Nineteen',
+ type: 'red',
+ },
+ {
+ label: 'Twenty',
+ type: 'high-contrast',
+ },
+].map(({ label, type, filter, dataSearch }) => (
+
+ {label}
+
+));
+
export default {
title: 'Experimental/TagSet',
component: TagSet,
@@ -37,7 +132,15 @@ export default {
},
},
decorators: [
- (story) =>
{story()}
,
+ (story) => (
+ <>
+
+ {story()}
+ >
+ ),
],
};
@@ -56,6 +159,12 @@ TagArray.args = {
containerWidth: 500,
};
+export const ManyTags = Template.bind({});
+ManyTags.args = {
+ children: ManyTagItems,
+ containerWidth: 500,
+};
+
const Template2 = (argsIn) => {
const { containerWidth, ...args } = { ...argsIn };
return (
diff --git a/packages/experimental/src/components/TagSet/_storybook-styles.scss b/packages/experimental/src/components/TagSet/_storybook-styles.scss
index cbd069c957..fd6373cf6d 100644
--- a/packages/experimental/src/components/TagSet/_storybook-styles.scss
+++ b/packages/experimental/src/components/TagSet/_storybook-styles.scss
@@ -7,12 +7,14 @@
@import '../../global/styles/carbon-settings'; // goes before index as it affects how carbon is used.
@import './index';
-.tag-set-story__viewport {
+$block-class: #{$exp-prefix}-tag-set;
+
+.#{$block-class}__viewport {
position: relative;
box-shadow: 0 0 0 $layout-02 $ui-01;
}
-.tag-set-story__viewport::before {
+.#{$block-class}__viewport::before {
position: absolute;
top: calc(-1 * #{$layout-02});
left: 0;
diff --git a/packages/experimental/src/components/TagSet/_tag-set.scss b/packages/experimental/src/components/TagSet/_tag-set.scss
index 819f23909c..79e166804c 100644
--- a/packages/experimental/src/components/TagSet/_tag-set.scss
+++ b/packages/experimental/src/components/TagSet/_tag-set.scss
@@ -7,6 +7,8 @@
@import '../../global/styles/carbon-settings'; // goes before index as it affects how carbon is used.
@import '../../global/styles/project-settings';
+@import 'carbon-components/scss/components/modal/modal';
+@import 'carbon-components/scss/components/search/search';
@import 'carbon-components/scss/components/tooltip/tooltip';
@import 'carbon-components/scss/components/tag/tag';
@@ -74,3 +76,17 @@ $block-class: #{$exp-prefix}-tag-set;
.#{$block-class}--overflow-tag .#{$prefix}--tag__close-icon:focus {
box-shadow: inset 0 0 0 2px $focus;
}
+
+.#{$block-class}--overflow-content {
+ margin: calc(-1 * #{$spacing-02});
+}
+
+.#{$block-class}--show-all-tags-link {
+ display: block;
+ margin: $spacing-02; // to match the tags
+ margin-top: $spacing-03;
+}
+
+.#{$block-class}--show-all-tags-search {
+ margin-bottom: $layout-01;
+}