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 CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
- 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.
- `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.
- Form `options_source` URLs now preserve existing query parameters when adding the dynamic `search` parameter.
- Searchable single-select form fields now close their dropdown after an option is selected.
- 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.
- 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.
- `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.
Expand Down
12 changes: 10 additions & 2 deletions examples/official-site/examples/form.sql
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,17 @@ SELECT 'website' AS name, 'url' AS type, 'https://example.com' AS placeholder,

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

SELECT 'country' AS name, 'select' AS type,
SELECT 'country' AS name, 'select' AS type,
'[{"label": "United States", "value": "US"}, {"label": "Canada", "value": "CA"}, {"label": "United Kingdom", "value": "GB"}]' AS options,
'**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;
'**select**: basic dropdown menu. Use for single choice from many options' AS description_md;

SELECT 'region' AS name, 'select' AS type, true as searchable,
'[{"label": "North America", "value": "NA"}, {"label": "South America", "value": "SA"}, {"label": "Europe", "value": "EU"}]' AS options,
'**select** with searchable: dropdown menu with searchable options' AS description_md;

SELECT 'title' AS name, 'select' AS type, true as multiple, true as searchable,
'[{"label": "professor", "value": "professor"}, {"label": "doctor", "value": "doctor"}, {"label": "lord", "value": "lord"}]' AS options,
'**select** with multiple: dropdown menu with multiple selections' AS description_md;

SELECT 'gender' AS name, 'radio' AS type, 'Male' AS value, 'Male' AS label,
'**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;
Expand Down
12 changes: 0 additions & 12 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 2 additions & 4 deletions sqlpage/tomselect.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,8 @@ function sqlpage_select_dropdown_individual(s) {
searchField: "label",
create: s.dataset.create_new,
maxOptions: null,
onItemAdd: function () {
this.setTextboxValue("");
this.refreshOptions();
},
closeAfterSelect: !s.multiple,
clearAfterSelect: true,
});
if (is_focused) tom.focus();
s.form?.addEventListener("reset", async () => {
Expand Down
1 change: 1 addition & 0 deletions tests/end-to-end/globals.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ interface TomSelectInstance {
getValue(): string | string[];
setTextboxValue(value: string): void;
focus(): void;
open(): void;
options: Record<string, { label?: string } | undefined>;
}

Expand Down
54 changes: 54 additions & 0 deletions tests/end-to-end/official-site.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -526,6 +526,60 @@ test("form select combines initial options with remote search results", async ({
});
});

test("form type=select searchable=true", async ({ page }) => {
await page.goto(`${BASE}/examples/form`);

const form = page.locator("form").filter({
has: page.locator('select[name="region"]'),
});
const regionSelect = form.locator('select[name="region"]');
const regionField = form.locator("label").filter({
has: page.locator('select[name="region"]'),
});
const regionCombobox = regionField.locator('input[role="combobox"]');
const dropdown = regionField.getByRole("listbox");
const selectedRegion = (name: string) =>
regionField.getByText(name, { exact: true }).filter({ visible: true });

await expect(selectedRegion("North America")).toBeVisible();
await expect(regionSelect).toHaveValue("NA");

await selectedRegion("North America").click();
await expect(dropdown).toBeVisible();
await expect(dropdown.getByRole("option")).toHaveCount(3);

await regionCombobox.fill("south");
await expect(dropdown.getByRole("option")).toHaveCount(1);
const southAmerica = dropdown.getByRole("option", {
name: "South America",
exact: true,
});
await expect(southAmerica).toBeVisible();

await southAmerica.click();
await page.evaluate(
() =>
new Promise<void>((resolve) =>
requestAnimationFrame(() => requestAnimationFrame(() => resolve())),
),
);
await expect(dropdown).not.toBeVisible();
await expect(regionCombobox).toHaveAttribute("aria-expanded", "false");
await expect(regionSelect).toHaveValue("SA");
await expect(selectedRegion("South America")).toBeVisible();

const terms = form.getByLabel("I accept the terms and conditions");
await form
.locator("label")
.filter({ has: page.locator('input[name="terms"]') })
.click();
await expect(terms).toBeChecked();
await form.getByRole("button", { name: /submit/i }).click();

await expect(page).toHaveURL(/\/examples\/show_variables\.sql$/);
await expect(page.getByText(":region = SA", { exact: true })).toBeVisible();
});

test("modal", async ({ page }) => {
await page.goto(`${BASE}/documentation.sql?component=modal#component`);
const openButton = page.getByRole("button", { name: "Open a simple modal" });
Expand Down