From 198499076708d14ffcb69202a226277faa20c0e4 Mon Sep 17 00:00:00 2001 From: Lee Chase Date: Mon, 23 Nov 2020 11:07:40 +0000 Subject: [PATCH] feat: add show all to tag-set --- .../src/components/TagSet/TagSet.js | 156 ++++++++++++++++-- .../src/components/TagSet/TagSet.stories.js | 113 ++++++++++++- .../components/TagSet/_storybook-styles.scss | 6 +- .../src/components/TagSet/_tag-set.scss | 16 ++ 4 files changed, 269 insertions(+), 22 deletions(-) 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; +}