From 4352a26e90f7dacdcdd10b4e81ca1f5d5d861efb Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Mon, 16 Mar 2026 14:51:28 +0100
Subject: [PATCH] fix: a11y bug: aria-expanded not supported here
---
src/components/Chip/Chip.test.tsx | 6 +--
src/components/Chip/Chip.tsx | 11 ++++-
.../FilterPanelSection.test.tsx | 45 ++++++++++++++-----
.../FilterPanelSection/FilterPanelSection.tsx | 6 +--
.../FilterPanelSection.test.tsx.snap | 1 -
.../SearchAndFilter/SearchAndFilter.test.tsx | 24 +++++++---
.../SearchAndFilter/SearchAndFilter.tsx | 3 +-
.../SearchAndFilter.test.tsx.snap | 2 +-
8 files changed, 67 insertions(+), 31 deletions(-)
diff --git a/src/components/Chip/Chip.test.tsx b/src/components/Chip/Chip.test.tsx
index bdec3450e..889cf39dd 100644
--- a/src/components/Chip/Chip.test.tsx
+++ b/src/components/Chip/Chip.test.tsx
@@ -41,7 +41,7 @@ describe("Chip ", () => {
);
expect(
within(screen.getByTestId("chip")).getByRole("button", {
- name: Label.Dismiss,
+ name: `${Label.Dismiss} Bob`,
}),
).toBeInTheDocument();
});
@@ -59,7 +59,7 @@ describe("Chip ", () => {
const dismissButton = within(screen.getByTestId("chip")).getByRole(
"button",
{
- name: Label.Dismiss,
+ name: `${Label.Dismiss} Bob`,
},
);
await userEvent.click(dismissButton);
@@ -209,7 +209,7 @@ describe("Chip ", () => {
const dismissButton = within(screen.getByTestId("chip")).getByRole(
"button",
{
- name: Label.Dismiss,
+ name: `${Label.Dismiss} Bob`,
},
);
await userEvent.click(dismissButton);
diff --git a/src/components/Chip/Chip.tsx b/src/components/Chip/Chip.tsx
index 491dca5a9..c28395aad 100644
--- a/src/components/Chip/Chip.tsx
+++ b/src/components/Chip/Chip.tsx
@@ -150,8 +150,15 @@ const Chip = ({
return (
{chipContent}
-
- {Label.Dismiss}
+
+
+ {Label.Dismiss}
+
);
diff --git a/src/components/SearchAndFilter/FilterPanelSection/FilterPanelSection.test.tsx b/src/components/SearchAndFilter/FilterPanelSection/FilterPanelSection.test.tsx
index 499f9a887..f1346321b 100644
--- a/src/components/SearchAndFilter/FilterPanelSection/FilterPanelSection.test.tsx
+++ b/src/components/SearchAndFilter/FilterPanelSection/FilterPanelSection.test.tsx
@@ -10,6 +10,11 @@ const sampleData = {
chips: [{ value: "us-east1" }, { value: "us-east2" }, { value: "us-east3" }],
};
+const manyChipsData = {
+ ...sampleData,
+ chips: Array.from({ length: 30 }, (_, i) => ({ value: `us-east${i + 1}` })),
+};
+
describe("Filter panel section", () => {
it("renders", () => {
render(
@@ -100,25 +105,45 @@ describe("Filter panel section", () => {
});
it("all chips are shown when counter is clicked", async () => {
+ // Jest is unaware of layout so we must mock the offsetTop and offsetHeight
+ // of the chips to force the overflow counter to show.
+ Object.defineProperty(HTMLElement.prototype, "offsetHeight", {
+ configurable: true,
+ value: 40,
+ });
+ Object.defineProperty(HTMLElement.prototype, "offsetTop", {
+ configurable: true,
+ value: 100,
+ });
render(
,
);
+
+ const counter = document.querySelector(
+ ".p-filter-panel-section__counter",
+ ) as HTMLElement;
+ expect(counter).toBeInTheDocument();
+
+ await userEvent.click(counter);
+
expect(
- document.querySelector(".p-filter-panel-section__chips"),
- ).toHaveAttribute("aria-expanded", "false");
- await userEvent.click(
- // Use a query selector because the element's text is split up over
- // multiple elements so it can't be selected by its content.
- document.querySelector(".p-filter-panel-section__counter") as HTMLElement,
- );
+ document.querySelector(".p-filter-panel-section__counter"),
+ ).not.toBeInTheDocument();
+
expect(
- document.querySelector(".p-filter-panel-section__chips"),
- ).toHaveAttribute("aria-expanded", "true");
+ screen.getByRole("button", { name: "us-east1" }),
+ ).toBeInTheDocument();
+ expect(
+ screen.getByRole("button", { name: "us-east15" }),
+ ).toBeInTheDocument();
+ expect(
+ screen.getByRole("button", { name: "us-east30" }),
+ ).toBeInTheDocument();
});
});
diff --git a/src/components/SearchAndFilter/FilterPanelSection/FilterPanelSection.tsx b/src/components/SearchAndFilter/FilterPanelSection/FilterPanelSection.tsx
index 3eb94dbb6..3f4853516 100644
--- a/src/components/SearchAndFilter/FilterPanelSection/FilterPanelSection.tsx
+++ b/src/components/SearchAndFilter/FilterPanelSection/FilterPanelSection.tsx
@@ -104,11 +104,7 @@ const FilterPanelSection = ({
}}
/>
)}
-
+
{chips?.map((chip) => {
// If search term has been added to input, only matching chips
// should display
diff --git a/src/components/SearchAndFilter/FilterPanelSection/__snapshots__/FilterPanelSection.test.tsx.snap b/src/components/SearchAndFilter/FilterPanelSection/__snapshots__/FilterPanelSection.test.tsx.snap
index fde52b939..0630b7d03 100644
--- a/src/components/SearchAndFilter/FilterPanelSection/__snapshots__/FilterPanelSection.test.tsx.snap
+++ b/src/components/SearchAndFilter/FilterPanelSection/__snapshots__/FilterPanelSection.test.tsx.snap
@@ -10,7 +10,6 @@ exports[`Filter panel section renders 1`] = `
Regions
document.querySelector(".p-search-and-filter__panel");
-const getSearchContainer = () =>
- document.querySelector(".p-search-and-filter__search-container");
-
describe("Search and filter", () => {
it("renders", async () => {
const returnSearchData = jest.fn();
@@ -155,14 +152,27 @@ describe("Search and filter", () => {
returnSearchData={returnSearchData}
/>,
);
- expect(getSearchContainer()).toHaveAttribute("aria-expanded", "false");
await userEvent.click(
screen.getByRole("searchbox", { name: Label.SearchAndFilter }),
);
await userEvent.click(screen.getByRole("button", { name: "us-east1" }));
- await userEvent.click(screen.getByRole("button", { name: "+1" }));
+ const counter = screen.getByRole("button", { name: "+1" });
+ await userEvent.click(counter);
- expect(getSearchContainer()).toHaveAttribute("aria-expanded", "true");
+ // After expanding, the container should indicate it is expanded.
+ expect(
+ document.querySelector(".p-search-and-filter__search-container"),
+ ).toHaveAttribute("data-expanded", "true");
+
+ expect(
+ screen.getByRole("button", { name: "us-east1" }),
+ ).toBeInTheDocument();
+ expect(
+ screen.getByRole("button", { name: "us-east2" }),
+ ).toBeInTheDocument();
+ expect(
+ screen.getByRole("button", { name: "us-east3" }),
+ ).toBeInTheDocument();
});
it("search prompt appears when search field has search term", async () => {
@@ -388,7 +398,7 @@ describe("Search and filter", () => {
// Dismiss the Cloud: Google filter chip
const cloudChip: HTMLElement = screen.getByText("CLOUD").closest(".p-chip");
const dismissButton = within(cloudChip).getByRole("button", {
- name: "Dismiss",
+ name: "Dismiss Google",
});
await userEvent.click(dismissButton);
diff --git a/src/components/SearchAndFilter/SearchAndFilter.tsx b/src/components/SearchAndFilter/SearchAndFilter.tsx
index 1323f0b71..f62c6fef0 100644
--- a/src/components/SearchAndFilter/SearchAndFilter.tsx
+++ b/src/components/SearchAndFilter/SearchAndFilter.tsx
@@ -1,5 +1,4 @@
import React, { useState, useEffect, useRef, KeyboardEvent } from "react";
-
import FilterPanelSection from "./FilterPanelSection";
import Chip from "../Chip";
import { overflowingChipsCount, isChipInArray } from "./utils";
@@ -241,7 +240,7 @@ const SearchAndFilter = ({
>