diff --git a/packages/components/filters/src/badge/badge.spec.tsx b/packages/components/filters/src/badge/badge.spec.tsx
index a67a52d5d5..d033dfaeb8 100644
--- a/packages/components/filters/src/badge/badge.spec.tsx
+++ b/packages/components/filters/src/badge/badge.spec.tsx
@@ -11,4 +11,18 @@ describe('Filters Badge', () => {
const badge = await screen.findByRole('status');
expect(badge.textContent).toEqual('+1');
});
+
+ it('should render the badge with a custom aria-label attribute if provided', async () => {
+ const ariaLabel = 'custom aria-label';
+
+ render();
+ const badge = await screen.findByRole('status', { name: ariaLabel });
+ expect(badge).toBeInTheDocument();
+ });
+
+ it('should apply disabled styles when isDisabled is true', () => {
+ render();
+ const badge = screen.getByRole('status');
+ expect(badge.className).toMatch(/disabledBadgeStyles/i);
+ });
});
diff --git a/packages/components/filters/src/badge/badge.tsx b/packages/components/filters/src/badge/badge.tsx
index 032875edd4..ed6688ac40 100644
--- a/packages/components/filters/src/badge/badge.tsx
+++ b/packages/components/filters/src/badge/badge.tsx
@@ -37,7 +37,7 @@ const badgeStyles = css`
height: ${designTokens.spacing40};
`;
-const disabledBageStyles = css`
+const disabledBadgeStyles = css`
background-color: ${designTokens.colorNeutral};
`;
@@ -45,7 +45,7 @@ function Badge(props: TBadgeProps) {
return (
diff --git a/packages/components/filters/src/filter-menu/chip/chip.spec.tsx b/packages/components/filters/src/filter-menu/chip/chip.spec.tsx
index 724c713c8e..54bbaed4a4 100644
--- a/packages/components/filters/src/filter-menu/chip/chip.spec.tsx
+++ b/packages/components/filters/src/filter-menu/chip/chip.spec.tsx
@@ -3,8 +3,14 @@ import Chip from './chip';
describe('FilterMenu Chip', () => {
it('should render the chip', async () => {
- await render();
+ render();
const chip = await screen.findByRole('listitem');
expect(chip.textContent).toEqual('test');
});
+
+ it('should apply disabled styles when isDisabled is true', () => {
+ render();
+ const chipElement = screen.getByRole('listitem');
+ expect(chipElement.className).toMatch(/disabledChipStyles/i);
+ });
});
diff --git a/packages/components/filters/src/filters.spec.tsx b/packages/components/filters/src/filters.spec.tsx
index fc48ea0275..07185fdaec 100644
--- a/packages/components/filters/src/filters.spec.tsx
+++ b/packages/components/filters/src/filters.spec.tsx
@@ -1,8 +1,18 @@
import { useState } from 'react';
-import { fireEvent, render, screen, within } from '../../../../test/test-utils';
import Filters, { TFiltersProps } from './filters';
-import { PrimaryColorsInput } from './fixtures/inputs';
import { FILTER_GROUP_KEYS } from './fixtures/constants';
+import { PrimaryColorsInput, ColorNameTextInput } from './fixtures/inputs';
+import { fireEvent, render, screen } from '../../../../test/test-utils';
+import {
+ getAddFilterButton,
+ getBadgeStatus,
+ getClearAllFiltersButton,
+ openAddFilterDialog,
+ displayedChips,
+ selectFilter,
+ selectFilterValues,
+ toggleFilterList,
+} from './filters.spec.utils';
const mockRenderSearchComponent = (
@@ -28,7 +38,10 @@ const FilterTestComponent = (
}
) => {
const [primaryColorValue, setPrimaryColorValue] = useState([]);
+ const [colorNameValue, setColorName] = useState('');
+
const clearPrimaryColorFilter = () => setPrimaryColorValue([]);
+ const clearColorNameFilter = () => setColorName('');
const appliedFilters = [];
@@ -41,6 +54,18 @@ const FilterTestComponent = (
})),
});
}
+
+ if (colorNameValue) {
+ appliedFilters.push({
+ filterKey: 'colorName',
+ values: [
+ {
+ value: colorNameValue,
+ label: colorNameValue,
+ },
+ ],
+ });
+ }
const filters = [
{
key: 'primaryColors',
@@ -57,73 +82,207 @@ const FilterTestComponent = (
onClearRequest: clearPrimaryColorFilter,
},
},
+ {
+ key: 'colorName',
+ label: 'Color Name',
+ filterMenuConfiguration: {
+ renderMenuBody: () => (
+
+ ),
+ onClearRequest: clearColorNameFilter,
+ },
+ },
];
+
return (
);
};
+//! should the add filter button be disabled once all possible filters are applied?
+//! check on 1 applied filter badge - posed q in figma
+//! user vs fireEvent?
+//? assert chips are visible even before we escape filter dialog
+
describe('Filters', () => {
it('should expand & collapse the filter list when `filters` button is clicked', async () => {
render();
- const filtersButton = await screen.findByRole('button', {
- name: /filters/i,
- });
- // initial click will expand the filter list
- fireEvent.click(filtersButton);
- const addFilterButton = await screen.findByRole('button', {
- name: /add filter/i,
- });
+ //Expand filter list & expect `Add filter` button to be visible
+ await toggleFilterList();
+ const addFilterButton = await getAddFilterButton();
expect(addFilterButton).toBeVisible();
- // second click will collapse the filter list
- fireEvent.click(filtersButton);
+ //Collapse filter list & expect `Add filter` button to be hidden
+ await toggleFilterList();
expect(addFilterButton).not.toBeVisible();
});
- it('should select a filter and a text input', async () => {
+ it('should apply values from multiple filters & display an applied filter count badge when collapsed', async () => {
render();
- // expand filterList
- const filtersButton = await screen.findByRole('button', {
- name: /filters/i,
- });
- fireEvent.click(filtersButton);
+ //Expand filter list
+ await toggleFilterList();
+ const addFilterDialog = await openAddFilterDialog();
- // open add filter dialog
- const addFilterButton = await screen.findByRole('button', {
- name: /add filter/i,
+ //Filter 1 - Select Primary Colors filter & apply a single value
+ await selectFilter(addFilterDialog, 'Primary Colors');
+ await selectFilterValues([/green/i]);
+
+ //Expect chips to display selected filter values
+ const chips = displayedChips('primaryColors');
+ expect(chips).toEqual(['green']);
+
+ //Filter 2 - Select Color Name filter & enter a value
+ const addFilterDialog2 = await openAddFilterDialog();
+ await selectFilter(addFilterDialog2, 'Color Name');
+ const textbox = await screen.findByPlaceholderText(/enter a color name/i);
+ fireEvent.change(textbox, {
+ target: {
+ value: 'cobalt',
+ },
});
- fireEvent.click(addFilterButton);
- // find dialog
- const addFilterDialog = await screen.findByRole('dialog');
- // find option for filter to add
- const option = within(addFilterDialog).getByText('Primary Colors');
- // select option
- fireEvent.click(option);
- // expect add filter dialog to close
- expect(addFilterDialog).not.toBeInTheDocument();
- // expect dialog for selected filter to open
- const selectFilterValuesDialog = await screen.findByRole('dialog');
- // get filter value to select
- const filterValueOption = within(selectFilterValuesDialog).getByText(
- /blue/i
- );
- // select value
- fireEvent.click(filterValueOption);
- // close filter dialog
- fireEvent.keyDown(filterValueOption, {
- key: 'Escape',
+
+ //Expect badge to not display if filter list is expanded
+ let filterTotalBadge = getBadgeStatus();
+ expect(filterTotalBadge).toBeNull();
+
+ //Collapse filter list & expect filterDialogs to be hidden
+ await toggleFilterList();
+ expect(addFilterDialog).not.toBeVisible();
+ expect(addFilterDialog2).not.toBeVisible();
+
+ //Recapture the badge el
+ filterTotalBadge = getBadgeStatus();
+
+ //Expect badge to display count of applied filters visible
+ expect(filterTotalBadge).toHaveTextContent('2');
+ expect(filterTotalBadge).toBeVisible();
+ });
+
+ it('should apply multiple values from a single filter, display as chips, but display no badge on collapse', async () => {
+ render();
+
+ //Expand filter list & add filter
+ await toggleFilterList();
+ const addFilterDialog = await openAddFilterDialog();
+
+ //Select Primary Colors filter & apply multiple values
+ await selectFilter(addFilterDialog, 'Primary Colors');
+ await selectFilterValues([
+ /blue/i,
+ /green/i,
+ /pink/i,
+ /lavender/i,
+ /azure/i,
+ ]);
+
+ //Expect chips to display selected filter values
+ const chips = displayedChips('primaryColors');
+ expect(chips).toEqual(['blue', 'green', 'pink', 'lavender', 'azure']);
+
+ //Collapse filter list & expect no badge to display
+ await toggleFilterList();
+ const filterTotalBadge = getBadgeStatus();
+ expect(filterTotalBadge).toBeNull();
+ });
+
+ //!is this needed?
+ it('should render search component', () => {
+ render();
+ const searchInput = screen.getByPlaceholderText('Search Placeholder');
+ expect(searchInput).toBeInTheDocument();
+ });
+
+ it('should render `clear all` filters button & clear selections when clicked - >=2 applied filters', async () => {
+ render();
+
+ //Expand filter list
+ await toggleFilterList();
+ const addFilterDialog = await openAddFilterDialog();
+
+ //Filter 1 - Select Primary Colors filter & apply a single value
+ await selectFilter(addFilterDialog, 'Primary Colors');
+ await selectFilterValues([/green/i]);
+
+ // Expect chips to display selected filter values
+ const chips = displayedChips('primaryColors');
+ expect(chips).toEqual(['green']);
+
+ //Filter 2 - Select Colors Name filter & enter a text value
+ const addFilterDialog2 = await openAddFilterDialog();
+ await selectFilter(addFilterDialog2, 'Color Name');
+ const textbox = await screen.findByPlaceholderText(/enter a color name/i);
+ fireEvent.change(textbox, {
+ target: {
+ value: 'Falu',
+ },
});
- expect(selectFilterValuesDialog).not.toBeInTheDocument();
- // check to make sure selected value is displayed in selected filter trigger button
- const selectedValues = screen.getByRole('list', {
+ fireEvent.keyDown(textbox, { key: 'Escape' });
+
+ //Expect chips to display selected filter values
+ const selectedValues = screen.queryByRole('list', {
name: 'primaryColors selected values',
});
- const valueChip = within(selectedValues).getByRole('listitem');
+ expect(selectedValues).toBeInTheDocument();
+
+ //Click `Clear all` (filters) button & expect applied filters to be cleared
+ const clearAllFiltersButton = getClearAllFiltersButton();
+ if (clearAllFiltersButton) {
+ fireEvent.click(clearAllFiltersButton);
+ }
+
+ expect(selectedValues).not.toBeInTheDocument();
+ });
+
+ it('should not render `clear all` filters button with a single applied filter', async () => {
+ render();
+
+ //Expand filter list
+ await toggleFilterList();
+ const addFilterDialog = await openAddFilterDialog();
+
+ //Filter 1 - select primary colors & apply a single value
+ await selectFilter(addFilterDialog, 'Primary Colors');
+ await selectFilterValues([/green/i]);
+
+ //Expect chips to display selected filter values
+ const chips = displayedChips('primaryColors');
+ expect(chips).toEqual(['green']);
+
+ //Expect Clear all` button to not be accessible
+ const clearAllFiltersButton = getClearAllFiltersButton();
+ expect(clearAllFiltersButton).not.toBeInTheDocument();
+ });
+
+ it('should remove applied filters individually', async () => {
+ render();
+
+ //Expand filter list
+ await toggleFilterList();
+ const addFilterDialog = await openAddFilterDialog();
+
+ //Select Primary Colors filter & apply a single value
+ await selectFilter(addFilterDialog, 'Primary Colors');
+ await selectFilterValues([/green/i]);
+
+ //Expect chips to display selected filter values
+ const chips = displayedChips('primaryColors');
+ expect(chips).toEqual(['green']);
+
+ //Click the filter remove button & expect filter to be removed from list
+ const removeFilterButton = screen.queryByRole('button', {
+ name: /remove Primary Colors filter/i,
+ });
+
+ if (removeFilterButton) {
+ fireEvent.click(removeFilterButton);
+ }
+
+ const primaryColorFilter = screen.queryByRole('button', {
+ name: /primary colors/i,
+ });
- expect(valueChip).toBeVisible();
- expect(valueChip).toHaveTextContent(/blue/i);
+ expect(primaryColorFilter).not.toBeInTheDocument();
});
});
diff --git a/packages/components/filters/src/filters.spec.utils.tsx b/packages/components/filters/src/filters.spec.utils.tsx
new file mode 100644
index 0000000000..85a9084b82
--- /dev/null
+++ b/packages/components/filters/src/filters.spec.utils.tsx
@@ -0,0 +1,69 @@
+import { fireEvent, screen, within } from '../../../../test/test-utils';
+
+/* Query & Utility Functions to be ued within filters.spec */
+const getFiltersButton = async (): Promise => {
+ return await screen.findByRole('button', {
+ name: /filters/i,
+ });
+};
+
+export const getAddFilterButton = async (): Promise => {
+ return await screen.findByRole('button', {
+ name: /add filter/i,
+ });
+};
+
+export const getClearAllFiltersButton = (): HTMLElement | null => {
+ return screen.queryByRole('button', {
+ name: /clear all/i,
+ });
+};
+
+export const getBadgeStatus = (): HTMLElement | null => {
+ // Query matcher used here to cover positive & negative assertions
+ return screen.queryByRole('status');
+};
+
+// Expand/ collapse filter list
+export const toggleFilterList = async () => {
+ const filtersButton = await getFiltersButton();
+ fireEvent.click(filtersButton);
+};
+
+//Click `Add filter` button & return dialog for interaction
+export const openAddFilterDialog = async (): Promise => {
+ const addFilterButton = await getAddFilterButton();
+ fireEvent.click(addFilterButton);
+ return await screen.findByRole('dialog');
+};
+
+//Select filter to apply
+export const selectFilter = async (dialog: HTMLElement, optionText: string) => {
+ const option = within(dialog).getByText(optionText);
+ fireEvent.click(option);
+};
+
+//Select filter values to apply
+export const selectFilterValues = async (
+ values: RegExp[]
+): Promise => {
+ const selectFilterValuesDialog = await screen.findByRole('dialog');
+ values.forEach(async (value) => {
+ const filterValueOption = within(selectFilterValuesDialog).getByText(value);
+ fireEvent.click(filterValueOption);
+ });
+ fireEvent.keyDown(selectFilterValuesDialog, { key: 'Escape' });
+ return !screen.queryByRole('dialog');
+};
+
+// Retrieves selected values (chips) for a given filter
+//! find better fn name
+export const displayedChips = (filterName: string | null): string[] => {
+ const selectedValues = screen.getByRole('list', {
+ name: `${filterName} selected values`,
+ });
+ const valueChips = within(selectedValues).getAllByRole('listitem');
+ return valueChips
+ .map((chip) => chip.textContent)
+ .filter((text): text is string => text !== null);
+};
diff --git a/packages/components/filters/src/fixtures/constants.tsx b/packages/components/filters/src/fixtures/constants.tsx
index 6e99a6c0e4..5924c00d8b 100644
--- a/packages/components/filters/src/fixtures/constants.tsx
+++ b/packages/components/filters/src/fixtures/constants.tsx
@@ -17,6 +17,11 @@ export const PRIMARY_COLOR_OPTIONS = [
{ label: 'Orange', value: 'orange' },
{ label: 'Yellow', value: 'yellow' },
{ label: 'Green', value: 'green' },
+ { label: 'Pink', value: 'pink' },
+ { label: 'Azure', value: 'azure' },
+ { label: 'Cobalt', value: 'cobalt' },
+ { label: 'Ivory', value: 'ivory' },
+ { label: 'Lavender', value: 'lavender' },
];
export const SECONDARY_COLOR_OPTIONS = [