Skip to content

Commit

Permalink
feat: adding the ability to disable modals for TagSet component (#5753)
Browse files Browse the repository at this point in the history
* feat: adding the ability to disable modals for TagSet component

* feat: improving logic to remove additional uneeded values

* refactor: clean up code a little

* feat: addressed requested cahnges

* refactor: cleaned up code

* fix: spelling mistakes

* feat: addressed comments

* refactor: cleaned up code

* refactor: cleaned up code part 2

---------

Co-authored-by: Nandan Devadula <[email protected]>
  • Loading branch information
jeesonjohnson and devadula-nandan authored Oct 11, 2024
1 parent d08ef4b commit 29e960c
Show file tree
Hide file tree
Showing 4 changed files with 105 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,11 @@ export default {
description:
'This prop is only for storybook representation, and does not belong to `tagset` component, the size can be passed to each tag{} in tags[], the overflow tag takes the size of last tag{} in tags[]',
},
onOverflowClick: {
control: { type: 'function' },
description:
'An optional click handler that overrides the default functionality of displaying all tags in a modal',
},
allTagsModalTargetCustomDomNode: {
control: { type: 'boolean' },
description: 'Optional DOM node: Modal target defaults to document.body',
Expand Down
30 changes: 29 additions & 1 deletion packages/ibm-products/src/components/TagSet/TagSet.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,6 @@ describe(TagSet.displayName, () => {
const visibleTags = 5;
window.innerWidth = tagWidth * (visibleTags + 1) + 1; // + 1 for overflow

// const { container } =
render(<TagSet {...overflowAndModalStrings} tags={tags} />);

const overflow = screen.getByText(`+${tags.length - visibleTags}`);
Expand All @@ -193,6 +192,35 @@ describe(TagSet.displayName, () => {
expect(modal).not.toHaveClass('is-visible');
});

it('Tags set overflow trigger can be overridden, and does not show TagSetModal or overflow popup', async () => {
const visibleTags = 5;
window.innerWidth = tagWidth * (visibleTags + 1) + 1; // + 1 for overflow

const overflowClickSpy = jest.fn();

const { queryByText } = render(
<TagSet
{...overflowAndModalStrings}
onOverflowClick={overflowClickSpy}
tags={tags}
/>
);

const overFlowButton = queryByText(`+${tags.length - visibleTags}`);
// Ensure the number of visible elements are rendered on the screen
expect(overFlowButton).toBeInTheDocument();
// Clicking the overflow button causes the spyFunction to be called
await act(() => userEvent.click(overFlowButton));
expect(overflowClickSpy).toHaveBeenCalledTimes(1);

// Ensure the overflow popup is not rendered onto the screen
expect(queryByText('View all tags')).toBeNull();

// Ensure the modal is not rendered onto the screen
const modal = screen.queryByRole('presentation');
expect(modal).not.toBeInTheDocument();
});

it('Obeys max visible', async () => {
window.innerWidth = tagWidth * 10 + 1;

Expand Down
33 changes: 23 additions & 10 deletions packages/ibm-products/src/components/TagSet/TagSet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,10 @@ export interface TagSetProps extends PropsWithChildren {
* display tags in multiple lines
*/
multiline?: boolean;
/**
* An optional click handler that overrides the default functionality of displaying all tags in a modal
*/
onOverflowClick?: ((overFlowTags: ReactNode[]) => void) | undefined;
/**
* Handler to get overflow tags
*/
Expand Down Expand Up @@ -160,6 +164,7 @@ export let TagSet = React.forwardRef<HTMLDivElement, TagSetProps>(
allTagsModalSearchLabel = 'Search all tags',
allTagsModalSearchPlaceholderText = 'Search all tags',
showAllTagsLabel = 'View all tags',
onOverflowClick,
tags,
containingElementRef,
measurementOffset = defaults.measurementOffset,
Expand Down Expand Up @@ -281,6 +286,7 @@ export let TagSet = React.forwardRef<HTMLDivElement, TagSetProps>(
key="displayed-tag-overflow"
ref={overflowTag}
popoverOpen={popoverOpen}
onOverflowClick={onOverflowClick}
setPopoverOpen={setPopoverOpen}
/>
);
Expand All @@ -293,6 +299,7 @@ export let TagSet = React.forwardRef<HTMLDivElement, TagSetProps>(
overflowClassName,
overflowType,
showAllTagsLabel,
onOverflowClick,
tags,
onOverflowTagChange,
popoverOpen,
Expand Down Expand Up @@ -415,16 +422,18 @@ export let TagSet = React.forwardRef<HTMLDivElement, TagSetProps>(
{displayedTags}
</div>
</div>
<TagSetModal
allTags={tags}
open={showAllModalOpen}
title={allTagsModalTitle}
modalAriaLabel={allTagsModalAriaLabel}
onClose={handleModalClose}
searchLabel={allTagsModalSearchLabel}
searchPlaceholder={allTagsModalSearchPlaceholderText}
portalTarget={allTagsModalTarget}
/>
{!onOverflowClick && (
<TagSetModal
allTags={tags}
open={showAllModalOpen}
title={allTagsModalTitle}
onClose={handleModalClose}
modalAriaLabel={allTagsModalAriaLabel}
searchLabel={allTagsModalSearchLabel}
searchPlaceholder={allTagsModalSearchPlaceholderText}
portalTarget={allTagsModalTarget}
/>
)}
</div>
);
}
Expand Down Expand Up @@ -509,6 +518,10 @@ TagSet.propTypes = {
* display tags in multiple lines
*/
multiline: PropTypes.bool,
/**
* An optional click handler that overrides the default functionality of displaying all tags in a modal
*/
onOverflowClick: PropTypes.func,
/**
* Handler to get overflow tags
*/
Expand Down
54 changes: 48 additions & 6 deletions packages/ibm-products/src/components/TagSet/TagSetOverflow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ interface TagSetOverflowProps {
* className
*/
className?: string;
/**
* An optional click handler that overrides the default functionality of displaying all tags in a modal
*/
onOverflowClick?: ((overFlowTags: ReactNode[]) => void) | undefined;
/**
* function to execute on clicking show all
*/
Expand Down Expand Up @@ -78,7 +82,7 @@ interface TagSetOverflowProps {
/**
* Setter function for the popoverOpen state value
*/
setPopoverOpen?: ((value: boolean) => void) | undefined;
setPopoverOpen: (value: boolean) => void;
/**
* label for the overflow show all tags link
*/
Expand All @@ -95,6 +99,7 @@ export const TagSetOverflow = React.forwardRef(
// The component props, in alphabetical order (for consistency).

allTagsModalSearchThreshold = defaults.allTagsModalSearchThreshold,
onOverflowClick,
className,
onShowAllClick,
overflowAlign = 'bottom',
Expand All @@ -115,24 +120,57 @@ export const TagSetOverflow = React.forwardRef(

useClickOutside(ref || localRef, () => {
if (popoverOpen) {
setPopoverOpen?.(false);
setPopoverOpen(false);
}
});

const handleShowAllTagsClick = (ev) => {
ev.stopPropagation();
ev.preventDefault();
setPopoverOpen?.(false);
setPopoverOpen(false);
onShowAllClick();
};

const handleEscKeyPress = (event) => {
const { key } = event;
if (key === 'Escape') {
setPopoverOpen?.(false);
setPopoverOpen(false);
}
};

const handleOverflowClick = () => {
// If a custom overflow function is provided then trigger that function
// on clicking the overflow
if (onOverflowClick) {
onOverflowClick(overflowTags);
} else {
setPopoverOpen(!popoverOpen);
}
};

if (onOverflowClick) {
return (
<span
{
// Pass through any other property values as HTML attributes.
...rest
}
aria-hidden={overflowTags.length === 0}
className={cx(`${blockClass}`, {
[`${blockClass}--hidden`]: overflowTags.length === 0,
})}
ref={ref || localRef}
>
<OperationalTag
onClick={() => handleOverflowClick()}
className={`${blockClass}__popover-trigger`}
size={size}
text={`+${overflowTags.length}`}
/>
</span>
);
}

return (
<span
{
Expand All @@ -155,7 +193,7 @@ export const TagSetOverflow = React.forwardRef(
autoAlign={overflowAutoAlign}
>
<OperationalTag
onClick={() => setPopoverOpen?.(!popoverOpen)}
onClick={() => setPopoverOpen(!popoverOpen)}
className={cx(`${blockClass}__popover-trigger`)}
size={size}
text={`+${overflowTags.length}`}
Expand Down Expand Up @@ -224,6 +262,10 @@ TagSetOverflow.propTypes = {
* className
*/
className: PropTypes.string,
/**
* An optional click handler that overrides the default functionality of displaying all tags in a modal
*/
onOverflowClick: PropTypes.func,
/**
* function to execute on clicking show all
*/
Expand Down Expand Up @@ -265,7 +307,7 @@ TagSetOverflow.propTypes = {
/**
* Setter function for the popoverOpen state value
*/
setPopoverOpen: PropTypes.func,
setPopoverOpen: PropTypes.func.isRequired,
/**
* label for the overflow show all tags link
*/
Expand Down

0 comments on commit 29e960c

Please sign in to comment.