Offer a long filter list as a popover multi-select instead of a wall of chips
CI / full-gate (push) Successful in 2m51s
CI / full-gate (push) Successful in 2m51s
This commit is contained in:
@@ -100,6 +100,7 @@ const messages = {
|
||||
"filter.remove": "Remove {{label}} filter",
|
||||
"filter.reset": "Reset",
|
||||
"filter.search": "Search",
|
||||
"filter.selected": "{{label}}, {{count}} selected",
|
||||
"filter.to": "To",
|
||||
"filter.toSeparator": "to",
|
||||
|
||||
|
||||
@@ -91,6 +91,7 @@ const messages: CoreMessages = {
|
||||
"filter.remove": "Ta bort filtret {{label}}",
|
||||
"filter.reset": "Återställ",
|
||||
"filter.search": "Sök",
|
||||
"filter.selected": "{{label}}, {{count}} valda",
|
||||
"filter.to": "Till",
|
||||
"filter.toSeparator": "till",
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ import { envName, type SettingDecl, type SettingsOf } from "./settings.ts";
|
||||
import type { StorageCredentials } from "./storage.ts";
|
||||
|
||||
// The Plainpages release this contract ships in — see README → Contract versioning.
|
||||
export const HOST_API_VERSION = "0.2.0";
|
||||
export const HOST_API_VERSION = "0.3.0";
|
||||
|
||||
export type HttpMethod = "DELETE" | "GET" | "HEAD" | "PATCH" | "POST" | "PUT";
|
||||
|
||||
|
||||
@@ -37,6 +37,14 @@ const config = {
|
||||
options: [{ value: "engineering", label: "Engineering" }, { value: "design", label: "Design" }, { value: "oncall", label: "On-call" }],
|
||||
},
|
||||
{ type: "daterange", legend: "Joined", from: { name: "joined_from", value: "2026-01-01", label: "Joined from" }, to: { name: "joined_to", value: "2026-06-14", label: "Joined to" } },
|
||||
{
|
||||
type: "multiselect",
|
||||
name: "owner",
|
||||
legend: "Owner",
|
||||
value: ["ann"],
|
||||
options: [{ value: "ann", label: "Ann Berg" }, { value: "bo", label: "Bo Falk" }],
|
||||
},
|
||||
{ type: "multiselect", name: "room", legend: "Room", options: [{ value: "lab", label: "Lab" }] },
|
||||
],
|
||||
],
|
||||
pills: [{ label: "Team", value: "Engineering", remove: "?tag=oncall" }],
|
||||
@@ -64,6 +72,14 @@ test("filter-bar renders a GET form with every control type, reflecting current
|
||||
assert.match(html, /<input type="checkbox" name="tag" value="oncall" checked>On-call/);
|
||||
assert.match(html, /<input type="checkbox" name="tag" value="design">Design/);
|
||||
|
||||
// multiselect — a disclosure: the trigger names the filter and counts what is chosen, the options
|
||||
// are checkboxes in a fieldset the popover holds, so a long list costs one line of the bar.
|
||||
assert.match(html, /<div class="menu"><button class="btn btn-menu" type="button" popovertarget="f-owner-menu" aria-label="Owner, 1 selected">Owner<span class="badge">1<\/span><\/button><div id="f-owner-menu" class="menu-pop left" popover>/);
|
||||
assert.match(html, /<fieldset class="menu-field"><legend class="menu-head">Owner<\/legend><label class="menu-check"><input type="checkbox" name="owner" value="ann" checked>Ann Berg<\/label><label class="menu-check"><input type="checkbox" name="owner" value="bo">Bo Falk<\/label><\/fieldset>/);
|
||||
|
||||
// Nothing chosen — no badge and no count in the accessible name.
|
||||
assert.match(html, /<button class="btn btn-menu" type="button" popovertarget="f-room-menu">Room<\/button>/);
|
||||
|
||||
// daterange — calendar icon + two date inputs with values.
|
||||
assert.match(html, /<div class="daterange"><svg class="ico ico-sm" aria-hidden="true"><use href="#i-cal"\s*\/?><\/svg>.*?<input type="date" id="f-joined_from" name="joined_from" value="2026-01-01">.*?<span class="to" aria-hidden="true">to<\/span>.*?<input type="date" id="f-joined_to" name="joined_to" value="2026-06-14">/);
|
||||
|
||||
|
||||
+2
-2
@@ -12,7 +12,7 @@ const flat = (s: string): string => s.replace(/>\s+</g, "><").replace(/\s+/g, "
|
||||
test("menu renders trigger, positioning, the item matrix and check groups", async () => {
|
||||
const html = flat(await render({
|
||||
id: "cols-menu",
|
||||
trigger: { icon: "i-cols", text: "Columns", label: "Column settings" },
|
||||
trigger: { count: 3, icon: "i-cols", text: "Columns", label: "Column settings" },
|
||||
align: "left", up: true, width: 240,
|
||||
items: [
|
||||
{ head: "Actions" },
|
||||
@@ -30,7 +30,7 @@ test("menu renders trigger, positioning, the item matrix and check groups", asyn
|
||||
|
||||
// Trigger: icon + text + aria-label, wired to the panel by id; popover carries align/up + width.
|
||||
// The panel is the trigger's next sibling inside the wrapper — the CSS open state reads that.
|
||||
assert.match(html, /<div class="menu"><button class="btn" type="button" popovertarget="cols-menu" aria-label="Column settings"><svg class="ico ico-sm"><use href="#i-cols"\s*\/?><\/svg>Columns<\/button><div id="cols-menu" class="menu-pop left up" popover style="min-width:240px">/);
|
||||
assert.match(html, /<div class="menu"><button class="btn" type="button" popovertarget="cols-menu" aria-label="Column settings"><svg class="ico ico-sm"><use href="#i-cols"\s*\/?><\/svg>Columns<span class="badge">3<\/span><\/button><div id="cols-menu" class="menu-pop left up" popover style="min-width:240px">/);
|
||||
|
||||
// Item matrix: head, button-with-icon, link, separator, danger button.
|
||||
assert.match(html, /<div class="menu-head">Actions<\/div>/);
|
||||
|
||||
Reference in New Issue
Block a user