diff --git a/e2e/components/FilterPanel/FilterPanel-test.avt.e2e.js b/e2e/components/FilterPanel/FilterPanel-test.avt.e2e.js new file mode 100644 index 0000000000..cd02c0d459 --- /dev/null +++ b/e2e/components/FilterPanel/FilterPanel-test.avt.e2e.js @@ -0,0 +1,31 @@ +/** + * Copyright IBM Corp. 2024, 2024 + * + * This source code is licensed under the Apache-2.0 license found in the + * LICENSE file in the root directory of this source tree. + */ + +'use strict'; + +import { expect, test } from '@playwright/test'; +import { visitStory } from '../../test-utils/storybook'; +import { carbon } from '../../../packages/ibm-products/src/settings'; + +test.describe('FilterPanel @avt', () => { + test('@avt-default-state', async ({ page }) => { + await visitStory(page, { + component: 'FilterPanel', + id: 'ibm-products-components-filter-panel-filterpanel--default', + globals: { + carbonTheme: 'white', + }, + }); + + await page.click(`.${carbon.prefix}--accordion__heading`); + await page + .locator(`.${carbon.prefix}--accordion__item--active`) + .screenshot({ animations: 'disabled' }); + + await expect(page).toHaveNoACViolations('FilterPanel @avt-default-state'); + }); +}); diff --git a/packages/ibm-products-styles/src/components/TagOverflow/_tag-overflow.scss b/packages/ibm-products-styles/src/components/TagOverflow/_tag-overflow.scss index aa042accc3..b152b5ae79 100644 --- a/packages/ibm-products-styles/src/components/TagOverflow/_tag-overflow.scss +++ b/packages/ibm-products-styles/src/components/TagOverflow/_tag-overflow.scss @@ -68,6 +68,11 @@ $block-class-modal: #{$block-class}-modal; max-width: $spacing-09; } +.#{c4p-settings.$carbon-prefix}--popover + .#{c4p-settings.$carbon-prefix}--popover-content { + white-space: normal; +} + .#{$block-class-overflow} { display: inline-block; vertical-align: bottom; @@ -139,8 +144,6 @@ $block-class-modal: #{$block-class}-modal; margin: 0; background-color: inherit; color: inherit; - text-overflow: ellipsis; - white-space: nowrap; } .#{$block-class-overflow}__tag diff --git a/packages/ibm-products-styles/src/components/_index-with-carbon.scss b/packages/ibm-products-styles/src/components/_index-with-carbon.scss index 467f3209af..f816182334 100644 --- a/packages/ibm-products-styles/src/components/_index-with-carbon.scss +++ b/packages/ibm-products-styles/src/components/_index-with-carbon.scss @@ -77,3 +77,4 @@ @use './FilterPanel/index-with-carbon' as *; @use './ConditionBuilder/index-with-carbon' as *; @use './GetStartedCard/index-with-carbon' as *; +@use './Guidebanner/index-with-carbon' as *; diff --git a/packages/ibm-products/src/components/TagOverflow/TagOverflow.js b/packages/ibm-products/src/components/TagOverflow/TagOverflow.js index eea31b3400..34fee58c90 100644 --- a/packages/ibm-products/src/components/TagOverflow/TagOverflow.js +++ b/packages/ibm-products/src/components/TagOverflow/TagOverflow.js @@ -24,8 +24,6 @@ const componentName = 'TagOverflow'; const allTagsModalSearchThreshold = 10; -// TODO: support prop overflowType - // Default values for props const defaults = { items: [], @@ -182,8 +180,8 @@ export let TagOverflow = React.forwardRef( } const hiddenItems = items?.slice(visibleItemsArr.length); - const overflowItemsArr = hiddenItems?.map((item) => { - return { type: item.tagType, label: item.label, id: item.id }; + const overflowItemsArr = hiddenItems?.map(({ tagType, ...other }) => { + return { type: tagType, ...other }; }); setVisibleItems(visibleItemsArr); @@ -197,6 +195,16 @@ export let TagOverflow = React.forwardRef( onOverflowTagChange, ]); + const handleTagOnClose = useCallback( + (onClose, index) => { + onClose?.(); + if (index <= visibleItems.length - 1) { + setPopoverOpen(false); + } + }, + [visibleItems] + ); + return (
{visibleItems.length > 0 && - visibleItems.map((item) => { + visibleItems.map((item, index) => { // Render custom components if (tagComponent) { return getCustomComponent(item); } else { + const { id, label, tagType, onClose, ...other } = item; // If there is no template prop, then render items as Tags return ( -
itemRefHandler(item.id, node)} - key={item.id} - > - +
itemRefHandler(id, node)} key={id}> + handleTagOnClose(onClose, index)} > - {item.label} + {label}
@@ -256,6 +264,7 @@ export let TagOverflow = React.forwardRef( open={showAllModalOpen} title={allTagsModalTitle} onClose={handleModalClose} + overflowType={overflowType} searchLabel={allTagsModalSearchLabel} searchPlaceholder={allTagsModalSearchPlaceholderText} portalTarget={allTagsModalTarget} diff --git a/packages/ibm-products/src/components/TagOverflow/TagOverflow.stories.jsx b/packages/ibm-products/src/components/TagOverflow/TagOverflow.stories.jsx index 06dc6a678d..578b59857f 100644 --- a/packages/ibm-products/src/components/TagOverflow/TagOverflow.stories.jsx +++ b/packages/ibm-products/src/components/TagOverflow/TagOverflow.stories.jsx @@ -5,7 +5,7 @@ * LICENSE file in the root directory of this source tree. */ -import React from 'react'; +import React, { useState, useRef } from 'react'; import { Theme } from '@carbon/react'; import { pkg } from '../../settings'; @@ -120,3 +120,40 @@ CustomComponentsWithOverflowModal.args = { tagComponent: IconComponent, ...overflowAndModalStrings, }; + +const TemplateWithClose = (argsIn) => { + const { containerWidth, allTagsModalTargetCustomDomNode, items, ...args } = { + ...argsIn, + }; + const [liveTags, setLiveTags] = useState( + items.map((item) => ({ + ...item, + filter: true, + onClose: () => handleTagClose(item.label), + })) + ); + + const handleTagClose = (key) => { + setLiveTags((prev) => prev.filter((item) => item.label !== key)); + }; + + const ref = useRef(); + return ( +
+ +
+ ); +}; + +export const InteractiveTags = TemplateWithClose.bind({}); +InteractiveTags.args = { + items: tags, + containerWidth: 500, + ...overflowAndModalStrings, +}; diff --git a/packages/ibm-products/src/components/TagOverflow/TagOverflowModal.tsx b/packages/ibm-products/src/components/TagOverflow/TagOverflowModal.tsx index cef0eab080..107826844f 100644 --- a/packages/ibm-products/src/components/TagOverflow/TagOverflowModal.tsx +++ b/packages/ibm-products/src/components/TagOverflow/TagOverflowModal.tsx @@ -39,6 +39,7 @@ interface TagOverflowModalProps { className?: string; onClose?: () => void; open?: boolean; + overflowType?: 'default' | 'tag'; portalTarget?: ReactNode; searchLabel?: string; searchPlaceholder?: string; @@ -53,6 +54,7 @@ export const TagOverflowModal = ({ title, onClose, open, + overflowType, portalTarget: portalTargetIn, searchLabel = defaults.searchLabel, searchPlaceholder, @@ -64,28 +66,12 @@ export const TagOverflowModal = ({ const renderPortalUse = usePortalTarget(portalTargetIn); const getFilteredItems = (): AllTags => { - let newFilteredModalTags: AllTags = []; - if (open) { - if (search === '' && allTags) { - newFilteredModalTags = allTags.slice(0); - } else { - const lCaseSearch = search.toLocaleLowerCase(); - - allTags?.forEach((tag) => { - const dataSearch = tag['data-search'] - ?.toLocaleLowerCase() - ?.indexOf(lCaseSearch); - const labelSearch = tag.label - ?.toLocaleLowerCase() - ?.indexOf(lCaseSearch); - - if (dataSearch > -1 || labelSearch > -1) { - newFilteredModalTags.push(tag); - } - }); - } + if (open && search && allTags) { + return allTags.filter((tag) => + tag.label?.toLocaleLowerCase()?.includes(search.toLocaleLowerCase()) + ); } - return newFilteredModalTags; + return allTags || []; }; const handleSearch = (evt) => { @@ -119,11 +105,14 @@ export const TagOverflowModal = ({ /> - {getFilteredItems().map(({ label, id, ...other }) => ( - - {label} - - ))} + {getFilteredItems().map(({ label, id, filter, ...other }) => { + const isFilterable = overflowType === 'tag' ? filter : false; + return ( + + {label} + + ); + })}
@@ -140,6 +129,7 @@ TagOverflowModal.propTypes = { className: PropTypes.string, onClose: PropTypes.func, open: PropTypes.bool, + overflowType: PropTypes.oneOf(['default', 'tag']), portalTarget: PropTypes.node, searchLabel: PropTypes.string, searchPlaceholder: PropTypes.string, diff --git a/packages/ibm-products/src/components/TagOverflow/TagOverflowPopover.js b/packages/ibm-products/src/components/TagOverflow/TagOverflowPopover.js index 3d6d596a1a..4aa960fed0 100644 --- a/packages/ibm-products/src/components/TagOverflow/TagOverflowPopover.js +++ b/packages/ibm-products/src/components/TagOverflow/TagOverflowPopover.js @@ -102,29 +102,39 @@ export const TagOverflowPopover = React.forwardRef(
    - {getOverflowPopoverItems().map((tag) => { - const tagProps = {}; - if (overflowType === 'tag') { - tagProps.type = 'high-contrast'; - } - if (overflowType === 'default') { - tagProps.filter = false; + {getOverflowPopoverItems().map( + ({ label, id, tagType, filter, onClose, ...other }) => { + const typeValue = + overflowType === 'tag' ? 'high-contrast' : tagType; + const isFilterable = + overflowType === 'tag' ? filter : false; + + return ( +
  • + {overflowType === 'tag' ? ( + onClose?.()} + type={typeValue} + filter={isFilterable} + > + {label} + + ) : ( + label + )} +
  • + ); } - return ( -
  • - {tag.label} - {/* {React.cloneElement(tag, tagProps)} */} -
  • - ); - })} + )}
{overflowTags.length > allTagsModalSearchThreshold && (