Skip to content

Commit

Permalink
feat: add show all to tag-set
Browse files Browse the repository at this point in the history
  • Loading branch information
lee-chase committed Nov 23, 2020
1 parent 93d752d commit 1984990
Show file tree
Hide file tree
Showing 4 changed files with 269 additions and 22 deletions.
156 changes: 138 additions & 18 deletions packages/experimental/src/components/TagSet/TagSet.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,30 +13,37 @@ 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,
className,
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(
Expand Down Expand Up @@ -64,20 +71,49 @@ export const TagSet = ({
</div>
);
} else {
newOverflowTags.push(
<span
key={`overflow-tag-${child.key}`}
className={`${blockClass}--overflow-tag`}>
{React.cloneElement(child)}
</span>
);
if (newOverflowTags.length < 10) {
newOverflowTags.push(
<span
key={`overflow-tag-${child.key}`}
className={`${blockClass}--overflow-tag`}>
{React.cloneElement(child)}
</span>
);
}
}
});

setDisplayedTags(newDisplayedTags);
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(
<span
key={`overflow-tag-${child.key}`}
className={`${blockClass}-show-all-tags`}>
{React.cloneElement(child)}
</span>
);
}
});
setFilteredAllTags(newFilteredAllTags);
} else {
setFilteredAllTags([]);
}
}, [showAllModalOpen, children, search]);

const checkFullyVisibleTags = () => {
// how many will fit?
let willFit = 0;
Expand Down Expand Up @@ -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 (
<ReactResizeDetector onResize={handleResize}>
<div className={cx([blockClass, className])} ref={tagSet}>
Expand All @@ -148,24 +221,51 @@ export const TagSet = ({
</div>

<span
onMouseEnter={showTip}
onMouseLeave={hideTip}
onFocus={showTip}
onBlur={hideTip}
aria-hidden={overflowTags === 0}
className={cx(`${blockClass}--overflow`, {
[`${blockClass}--overflow--hidden`]: overflowTags.length === 0,
})}>
})}
onFocus={showTip}>
<Tooltip
direction={overflowDirection}
triggerText={<Tag>+{overflowTags.length}</Tag>}
showIcon={false}
open={tipOpen}
triggerText={<Tag>+{children.length - displayedTags.length}</Tag>}
showIcon={false}
ref={overflowTag}>
{overflowTags}
<div
ref={overflowTagContent}
className={`${blockClass}--overflow-content`}>
{overflowTags}
{overflowTags.length >= 10 && (
<Link
className={`${blockClass}--show-all-tags-link`}
href=""
onClick={handleShowAllTagsClick}>
{showAllTagsLabel}
</Link>
)}
</div>
</Tooltip>
</span>
</div>
<Modal
className={`${blockClass}--show-all-modal`}
open={showAllModalOpen}
passiveModal
size="sm"
modalHeading={showAllModalHeading}
onRequestClose={handleModalClose}>
<Search
className={`${blockClass}--show-all-tags-search`}
labelText={showAllSearchLabel}
placeHolderText={showAllSearchPlaceHolderText}
onChange={handleSearch}
size="lg"
/>
<div className={`${blockClass}--show-all-tags-content`}>
{filteredAllTags}
</div>
</Modal>
</div>
</ReactResizeDetector>
);
Expand All @@ -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',
};
113 changes: 111 additions & 2 deletions packages/experimental/src/components/TagSet/TagSet.stories.js
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand All @@ -27,6 +27,101 @@ const TagItems = [
</Tag>,
];

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 }) => (
<Tag key={label} data-search={dataSearch} {...{ filter, type }}>
{label}
</Tag>
));

export default {
title: 'Experimental/TagSet',
component: TagSet,
Expand All @@ -37,7 +132,15 @@ export default {
},
},
decorators: [
(story) => <div className="tag-set-story__viewport">{story()}</div>,
(story) => (
<>
<style>
{`.${blockClass}--show-all-modal { opacity: 0; visibility: hidden; /* prevents glitch storybook modal css load */ }`}
;
</style>
<div className="tag-set-story__viewport">{story()}</div>
</>
),
],
};

Expand All @@ -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 (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Loading

0 comments on commit 1984990

Please sign in to comment.