Skip to content

Commit 6859b56

Browse files
authored
fix(form): close searchable single-select dropdown (#1381)
* fix(form) :: close searchable single-select dropdown * fix and test properly
1 parent 71609f2 commit 6859b56

6 files changed

Lines changed: 68 additions & 18 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
- Added a `toast` component with plain-text or Markdown content, icons, colors, six screen placements, configurable auto-dismiss timing, optional manual dismissal, URL-fragment triggers, and automatic stacking of queued notifications.
77
- `sqlpage.send_mail` now supports rich email bodies. Use `body_html` for a caller-provided HTML alternative, or `body_md` to render Markdown as HTML. Messages retain a plain-text alternative; `body` may be omitted when `body_md` is used, and `body_md` and `body_html` cannot be combined.
88
- Form `options_source` URLs now preserve existing query parameters when adding the dynamic `search` parameter.
9+
- Searchable single-select form fields now close their dropdown after an option is selected.
910
- Map coordinates that are not a pair of numbers, like a latitude with no longitude, are now reported in the browser console and skipped, instead of breaking the whole map.
1011
- Stacked charts now stack their series by `x` value instead of by point order, which used to give wrong totals when a series was missing a point.
1112
- `line`, `area`, `scatter`, `bubble` and `heatmap` charts with text labels on the x axis now line their series up by label, leaving a gap where a series skips one.

examples/official-site/examples/form.sql

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,9 +57,17 @@ SELECT 'website' AS name, 'url' AS type, 'https://example.com' AS placeholder,
5757

5858
SELECT 'header' AS type, 'Selection Types' AS label;
5959

60-
SELECT 'country' AS name, 'select' AS type,
60+
SELECT 'country' AS name, 'select' AS type,
6161
'[{"label": "United States", "value": "US"}, {"label": "Canada", "value": "CA"}, {"label": "United Kingdom", "value": "GB"}]' AS options,
62-
'**Select** (SQLPage custom) - Dropdown menu. Use for single choice from many options. Add `multiple` for multi-select. Use `searchable` for long lists. Set `dropdown` for enhanced UI.' AS description_md;
62+
'**select**: basic dropdown menu. Use for single choice from many options' AS description_md;
63+
64+
SELECT 'region' AS name, 'select' AS type, true as searchable,
65+
'[{"label": "North America", "value": "NA"}, {"label": "South America", "value": "SA"}, {"label": "Europe", "value": "EU"}]' AS options,
66+
'**select** with searchable: dropdown menu with searchable options' AS description_md;
67+
68+
SELECT 'title' AS name, 'select' AS type, true as multiple, true as searchable,
69+
'[{"label": "professor", "value": "professor"}, {"label": "doctor", "value": "doctor"}, {"label": "lord", "value": "lord"}]' AS options,
70+
'**select** with multiple: dropdown menu with multiple selections' AS description_md;
6371

6472
SELECT 'gender' AS name, 'radio' AS type, 'Male' AS value, 'Male' AS label,
6573
'**Radio** - Radio button for mutually exclusive choices. Create multiple rows with same `name` for a radio group. One option can be selected. Use for 2-5 options.' AS description_md;

package-lock.json

Lines changed: 0 additions & 12 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

sqlpage/tomselect.js

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,8 @@ function sqlpage_select_dropdown_individual(s) {
3030
searchField: "label",
3131
create: s.dataset.create_new,
3232
maxOptions: null,
33-
onItemAdd: function () {
34-
this.setTextboxValue("");
35-
this.refreshOptions();
36-
},
33+
closeAfterSelect: !s.multiple,
34+
clearAfterSelect: true,
3735
});
3836
if (is_focused) tom.focus();
3937
s.form?.addEventListener("reset", async () => {

tests/end-to-end/globals.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ interface TomSelectInstance {
55
getValue(): string | string[];
66
setTextboxValue(value: string): void;
77
focus(): void;
8+
open(): void;
89
options: Record<string, { label?: string } | undefined>;
910
}
1011

tests/end-to-end/official-site.spec.ts

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -526,6 +526,60 @@ test("form select combines initial options with remote search results", async ({
526526
});
527527
});
528528

529+
test("form type=select searchable=true", async ({ page }) => {
530+
await page.goto(`${BASE}/examples/form`);
531+
532+
const form = page.locator("form").filter({
533+
has: page.locator('select[name="region"]'),
534+
});
535+
const regionSelect = form.locator('select[name="region"]');
536+
const regionField = form.locator("label").filter({
537+
has: page.locator('select[name="region"]'),
538+
});
539+
const regionCombobox = regionField.locator('input[role="combobox"]');
540+
const dropdown = regionField.getByRole("listbox");
541+
const selectedRegion = (name: string) =>
542+
regionField.getByText(name, { exact: true }).filter({ visible: true });
543+
544+
await expect(selectedRegion("North America")).toBeVisible();
545+
await expect(regionSelect).toHaveValue("NA");
546+
547+
await selectedRegion("North America").click();
548+
await expect(dropdown).toBeVisible();
549+
await expect(dropdown.getByRole("option")).toHaveCount(3);
550+
551+
await regionCombobox.fill("south");
552+
await expect(dropdown.getByRole("option")).toHaveCount(1);
553+
const southAmerica = dropdown.getByRole("option", {
554+
name: "South America",
555+
exact: true,
556+
});
557+
await expect(southAmerica).toBeVisible();
558+
559+
await southAmerica.click();
560+
await page.evaluate(
561+
() =>
562+
new Promise<void>((resolve) =>
563+
requestAnimationFrame(() => requestAnimationFrame(() => resolve())),
564+
),
565+
);
566+
await expect(dropdown).not.toBeVisible();
567+
await expect(regionCombobox).toHaveAttribute("aria-expanded", "false");
568+
await expect(regionSelect).toHaveValue("SA");
569+
await expect(selectedRegion("South America")).toBeVisible();
570+
571+
const terms = form.getByLabel("I accept the terms and conditions");
572+
await form
573+
.locator("label")
574+
.filter({ has: page.locator('input[name="terms"]') })
575+
.click();
576+
await expect(terms).toBeChecked();
577+
await form.getByRole("button", { name: /submit/i }).click();
578+
579+
await expect(page).toHaveURL(/\/examples\/show_variables\.sql$/);
580+
await expect(page.getByText(":region = SA", { exact: true })).toBeVisible();
581+
});
582+
529583
test("modal", async ({ page }) => {
530584
await page.goto(`${BASE}/documentation.sql?component=modal#component`);
531585
const openButton = page.getByRole("button", { name: "Open a simple modal" });

0 commit comments

Comments
 (0)