Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions apps/prs/angular/src/app/app.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
<a href="/bugs/3072">3072</a>
<a href="/bugs/3118">3118</a>
<a href="/bugs/3156">3156</a>
<a href="/bugs/3248">3248</a>
</goab-side-menu-group>
<goab-side-menu-group heading="Features">
<a href="/features/1328">1328</a>
Expand Down
3 changes: 3 additions & 0 deletions apps/prs/angular/src/app/app.routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ import { Bug2991Component } from "../routes/bugs/2991/bug2991.component";
import { Bug3072Component } from "../routes/bugs/3072/bug3072.component";
import { Bug3118Component } from "../routes/bugs/3118/bug3118.component";
import { Bug3156Component } from "../routes/bugs/3156/bug3156.component";
import { Bug3248Component } from "../routes/bugs/3248/bug3248.component";

import { Feat1328Component } from "../routes/features/feat1328/feat1328.component";
import { Feat1547Component } from "../routes/features/feat1547/feat1547.component";
import { Feat1813Component } from "../routes/features/feat1813/feat1813.component";
Expand Down Expand Up @@ -80,6 +82,7 @@ export const appRoutes: Route[] = [
{ path: "bugs/3072", component: Bug3072Component },
{ path: "bugs/3118", component: Bug3118Component },
{ path: "bugs/3156", component: Bug3156Component },
{ path: "bugs/3248", component: Bug3248Component },

{ path: "features/1328", component: Feat1328Component },
{ path: "features/1547", component: Feat1547Component },
Expand Down
61 changes: 61 additions & 0 deletions apps/prs/angular/src/routes/bugs/3248/bug3248.component.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
<div style="width: 1024px; margin: 0 auto; padding: 2rem">
<goab-text size="heading-l" mb="xl"> Bug #2333: Dropdown Reset Test </goab-text>

<goab-text size="body-m" mb="2xl">
This test demonstrates the dropdown reset issue. When dropdown items are dynamically removed,
the dropdown should properly sync its internal state to reflect the updated list of options.
</goab-text>

<goab-text size="heading-m" mb="l"> Test Scenario </goab-text>

<goab-text size="body-s" mb="m"> 1. Select a color from the dropdown below </goab-text>
<goab-text size="body-s" mb="m">
2. Click one of the buttons to reduce the number of available options
</goab-text>
<goab-text size="body-s" mb="m">
3. Open the dropdown again - it should only show the remaining options
</goab-text>
<goab-text size="body-s" mb="2xl">
4. The bug occurred when the filtered options weren't synced after items were destroyed
</goab-text>

<goab-text size="body-m" mb="m">
Currently showing {{ colorsCount }} color(s): {{ colorsList }}
</goab-text>

<goab-text size="body-m" mb="m">
Selected value: {{ selectedColor || "None" }}
</goab-text>

<goab-dropdown
name="favcolor"
placeholder="Select a color"
[value]="selectedColor"
(onChange)="onChange($event)"
mb="xl"
>
@for (color of colors; track color) {
<goab-dropdown-item [label]="color" [value]="color"></goab-dropdown-item>
}
</goab-dropdown>

<div style="display: flex; gap: 1rem; margin-bottom: 1rem">
<goab-button type="secondary" (click)="reduceToOne()">
Reduce to 1 item (blue)
</goab-button>
<goab-button type="secondary" (click)="reduceToTwo()">
Reduce to 2 items (green, yellow)
</goab-button>
<goab-button type="primary" (click)="resetToAll()"> Reset to all items </goab-button>
</div>

<goab-text size="body-s" mb="m" mt="2xl">
<strong>Expected behavior:</strong> After clicking a reduction button, opening the dropdown
should only display the items that remain in the list.
</goab-text>
<goab-text size="body-s">
<strong>Bug behavior (before fix):</strong> The dropdown would still show all original items
even after they were removed, because syncFilteredOptions() wasn't called when child items
were destroyed.
</goab-text>
</div>
45 changes: 45 additions & 0 deletions apps/prs/angular/src/routes/bugs/3248/bug3248.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { Component } from "@angular/core";
import { CommonModule } from "@angular/common";
import {
GoabButton,
GoabDropdown,
GoabDropdownItem,
GoabDropdownOnChangeDetail,
GoabText,
} from "@abgov/angular-components";

@Component({
standalone: true,
selector: "abgov-bug3248",
templateUrl: "./bug3248.component.html",
imports: [CommonModule, GoabButton, GoabDropdown, GoabDropdownItem, GoabText],
})
export class Bug3248Component {
colors = ["red", "blue", "green", "yellow", "purple"];
selectedColor = "";

reduceToOne(): void {
this.colors = ["blue"];
}

reduceToTwo(): void {
this.colors = ["green", "yellow"];
}

resetToAll(): void {
this.colors = ["red", "blue", "green", "yellow", "purple"];
}

onChange(detail: GoabDropdownOnChangeDetail): void {
console.log("Dropdown changed:", detail);
this.selectedColor = Array.isArray(detail.value) ? detail.value[0] : detail.value;
}

get colorsList(): string {
return this.colors.join(", ");
}

get colorsCount(): number {
return this.colors.length;
}
}
1 change: 1 addition & 0 deletions apps/prs/react/src/app/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ export function App() {
<Link to="/bugs/2943">2943</Link>
<Link to="/bugs/2948">2948</Link>
<Link to="/bugs/3118">3118</Link>
<Link to="/bugs/3248">3248</Link>
</GoabSideMenuGroup>
<GoabSideMenuGroup heading="Features">
<Link to="/features/1547">1547</Link>
Expand Down
3 changes: 3 additions & 0 deletions apps/prs/react/src/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@ import { Bug2892Route } from "./routes/bugs/bug2892";
import { Bug2922Route } from "./routes/bugs/bug2922";
import { Bug2943Route } from "./routes/bugs/bug2943";
import { Bug2948Route } from "./routes/bugs/bug2948";
import { Bug3248Route } from "./routes/bugs/bug3248";
import Bug3118Route from "./routes/bugs/bug3118";

import { EverythingRoute } from "./routes/everything";
import { Feat1547Route } from "./routes/features/feat1547";
import { Feat1813Route } from "./routes/features/feat1813";
Expand Down Expand Up @@ -84,6 +86,7 @@ root.render(
<Route path="bugs/2943" element={<Bug2943Route />} />
<Route path="bugs/2948" element={<Bug2948Route />} />
<Route path="bugs/3118" element={<Bug3118Route />} />
<Route path="bugs/3248" element={<Bug3248Route />} />

<Route path="features/1547" element={<Feat1547Route />} />
<Route path="features/1813" element={<Feat1813Route />} />
Expand Down
103 changes: 103 additions & 0 deletions apps/prs/react/src/routes/bugs/bug3248.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import React, { useState } from "react";
import {
GoabButton,
GoabDropdown,
GoabDropdownItem,
GoabText,
} from "@abgov/react-components";
import { GoabDropdownOnChangeDetail } from "@abgov/ui-components-common";

export function Bug3248Route() {
const [colors, setColors] = useState<string[]>(["red", "blue", "green", "yellow", "purple"]);
const [selectedColor, setSelectedColor] = useState<string>("");

const reduceToOne = () => {
setColors(["blue"]);
};

const reduceToTwo = () => {
setColors(["green", "yellow"]);
};

const resetToAll = () => {
setColors(["red", "blue", "green", "yellow", "purple"]);
};

const onChange = (detail: GoabDropdownOnChangeDetail) => {
console.log("Dropdown changed:", detail);
setSelectedColor(detail.value || "");
};

return (
<div style={{ width: "1024px", margin: "0 auto", padding: "2rem" }}>
<GoabText size="heading-l" mb="xl">
Bug #2333: Dropdown Reset Test
</GoabText>

<GoabText size="body-m" mb="2xl">
This test demonstrates the dropdown reset issue. When dropdown items are dynamically
removed, the dropdown should properly sync its internal state to reflect the updated
list of options.
</GoabText>

<GoabText size="heading-m" mb="l">
Test Scenario
</GoabText>

<GoabText size="body-s" mb="m">
1. Select a color from the dropdown below
</GoabText>
<GoabText size="body-s" mb="m">
2. Click one of the buttons to reduce the number of available options
</GoabText>
<GoabText size="body-s" mb="m">
3. Open the dropdown again - it should only show the remaining options
</GoabText>
<GoabText size="body-s" mb="2xl">
4. The bug occurred when the filtered options weren't synced after items were destroyed
</GoabText>

<GoabText size="body-m" mb="m">
Currently showing {colors.length} color(s): {colors.join(", ")}
</GoabText>

<GoabText size="body-m" mb="m">
Selected value: {selectedColor || "None"}
</GoabText>

<GoabDropdown
name="favcolor"
placeholder="Select a color"
value={selectedColor}
onChange={onChange}
mb="xl"
>
{colors.map((color) => (
<GoabDropdownItem key={color} label={color} value={color} />
))}
</GoabDropdown>

<div style={{ display: "flex", gap: "1rem", marginBottom: "1rem" }}>
<GoabButton type="secondary" onClick={reduceToOne}>
Reduce to 1 item (blue)
</GoabButton>
<GoabButton type="secondary" onClick={reduceToTwo}>
Reduce to 2 items (green, yellow)
</GoabButton>
<GoabButton type="primary" onClick={resetToAll}>
Reset to all items
</GoabButton>
</div>

<GoabText size="body-s" mb="m" mt="2xl">
<strong>Expected behavior:</strong> After clicking a reduction button, opening the
dropdown should only display the items that remain in the list.
</GoabText>
<GoabText size="body-s">
<strong>Bug behavior (before fix):</strong> The dropdown would still show all original
items even after they were removed, because syncFilteredOptions() wasn't called when
child items were destroyed.
</GoabText>
</div>
);
}
44 changes: 44 additions & 0 deletions libs/react-components/specs/dropdown.browser.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -585,4 +585,48 @@ describe("Dropdown", () => {
});
})
})

describe("Dropdown reset", () => {
it("should reduce the number of element displayed within the dropdown", async () => {
let values: string[] = ["red", "blue", "green"]

const Component = () => {
return (
<GoabDropdown name="favcolor" onChange={noop}>
{values.map((item) =>
<GoabDropdownItem label={item} value={item} key={item} />
)}
</GoabDropdown>
);
};

const result = render(<Component />);
const input = result.getByRole("combobox");
const items = result.getByRole("option");

// Initial state

await vi.waitFor(async () => {
const inputEl = input.element() as HTMLInputElement
inputEl.click();
expect(items.elements().length).toBe(values.length);
items.elements().forEach((el, index) => {
expect(el.innerHTML.trim()).toBe(values[index]);
})
});

// Reduce to 1 item

values = ["blue"]; // the previous failure happened with this item, was one of the previous items
result.rerender(<Component />)

await vi.waitFor(async () => {
const inputEl = input.element() as HTMLInputElement
inputEl.click();
const items = result.getByRole("option");
expect(items.elements().length).toBe(1);
expect(items.element().innerHTML.trim()).toBe("blue");
});
})
})
});
3 changes: 1 addition & 2 deletions libs/web-components/src/components/dropdown/Dropdown.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -108,8 +108,6 @@
let _bindTimeoutId: any;
let _mountStatus: "active" | "ready" = "ready";
let _mountTimeoutId: any = undefined;
let _error = toBoolean(error);
let _prevError = _error;
Expand Down Expand Up @@ -308,6 +306,7 @@
*/
function onChildDestroyed(detail: DropdownItemDestroyRelayDetail) {
_options = _options.filter((option) => option.value !== detail.value);
syncFilteredOptions();
}
function setSelected() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@
})
}

onDestroy(async () => {
onDestroy(() => {
relay<DropdownItemDestroyRelayDetail>(
_parentEl,
DropdownItemDestroyMsg,
Expand Down