Skip to content
Open
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 @@ -9,6 +9,7 @@
- The cast is retained on SQLite and on ODBC connections to PostgreSQL, SQLite, Oracle, Snowflake and other databases where it is needed for correct comparisons.
- AWS Lambda builds and documentation now use the supported Amazon Linux 2023 custom runtime instead of the end-of-life Amazon Linux 2 runtime. Release artifacts include the configuration directory required on Lambda's read-only filesystem.
- 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.
- Added a `facet` component for SQL-generated category filters, with inline links or a compact dropdown, an optional link to clear the filter, and a configurable compact-mode title.
- `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.
Expand Down
126 changes: 126 additions & 0 deletions examples/official-site/sqlpage/migrations/77_facet.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
INSERT INTO component(name, icon, description, introduced_in_version) VALUES
('facet', 'filter', '
Navigation links for filtering a dataset by a category, status, owner, or other attribute.

This component only renders the filter navigation. **Your SQL query is responsible for filtering the data** based on the URL parameter selected by the user.

Use it alongside a [table](?component=table), [list](?component=list), or [card](?component=card). The links use GET parameters, so the selected filter can be bookmarked and shared.

Set `compact` to display the facets in a dropdown, which is useful for a moderate number of choices or on narrow screens. Use `dropdown_title` to show the active facet in the closed dropdown. `all_link` and `all_title` add a link that clears the filter.

The following portable pattern filters a table by category. `sqlpage.link` preserves the current page path while safely generating the URL.

```sql
select ''table'' as component;
select title, category
from my_table
where $category is null or category = $category;

select ''facet'' as component,
''Category'' as description,
sqlpage.link(sqlpage.path(), json_object(''category'', null)) as all_link,
$category is null as all_active;
select distinct category as title,
sqlpage.link(sqlpage.path(), json_object(''category'', category)) as link,
category = $category as active
from my_table
order by category;
```
', '0.46.0');

INSERT INTO parameter(component, name, description, type, top_level, optional) SELECT 'facet', * FROM (VALUES
-- Top-level parameters
('description','The facet category label. In compact mode, it is shown on the dropdown button when `dropdown_title` is omitted. If omitted, the button displays "Choose facet".','TEXT',TRUE,TRUE),
('dropdown_title','Text shown on the compact-mode dropdown button. Use it to show the active facet. Defaults to `description`.','TEXT',TRUE,TRUE),
('compact','Displays the facets in a dropdown instead of links.','BOOLEAN',TRUE,TRUE),
('all_link','URL that clears the filter. If omitted, no All link is displayed.','URL',TRUE,TRUE),
('all_title','Text for the link that clears the filter. Defaults to "ALL".','TEXT',TRUE,TRUE),
('all_active','Whether the link that clears the filter is active. Defaults to false.','BOOLEAN',TRUE,TRUE),
-- Item-level parameters (for each facet)
('title','Facet title.','TEXT',FALSE,FALSE),
('link','URL for the facet.','URL',FALSE,FALSE),
('active','Whether the link is active or not. Defaults to false.','BOOLEAN',FALSE,TRUE)
) x;

-- Insert example(s) for the component
INSERT INTO example(component, description, properties)
VALUES (
'facet',
'A category selector with an All link. In an application, the SQL pattern above uses the chosen URL parameter to filter the displayed data.',
JSON(
'[
{
"component": "table"
},
{
"name": "USS Enterprise (NCC-1701)",
"class": "Constitution"
},
{
"name": "USS Exeter (NCC-1672)",
"class": "Galaxy"
},
{
"name": "USS Exeter (NCC-1672)",
"class": "Constitution"
},
{
"component": "facet",
"description": "Classes",
"all_link": "?component=facet",
"all_title": "All",
"all_active": false
},
{
"title": "Constitution",
"link": "?component=facet&class=Constitution",
"active": true
},
{
"title": "Galaxy",
"link": "?component=facet&class=Galaxy"
}
]'
)
),
(
'facet',
'Compact mode displays the facet choices in a dropdown.',
JSON(
'[
{
"component": "table"
},
{
"name": "USS Enterprise (NCC-1701)",
"class": "Constitution"
},
{
"name": "USS Exeter (NCC-1672)",
"class": "Galaxy"
},
{
"name": "USS Exeter (NCC-1672)",
"class": "Constitution"
},
{
"component": "facet",
"description": "Classes",
"all_link": "?component=facet",
"all_title": "All",
"all_active": false,
"compact": true,
"dropdown_title": "Constitution"
},
{
"title": "Constitution",
"link": "?component=facet&class=Constitution",
"active": true
},
{
"title": "Galaxy",
"link": "?component=facet&class=Galaxy"
}
]'
)
);
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ FROM (VALUES
('text', TRUE),
('carousel', TRUE),
('login', TRUE),
('pagination', TRUE)
('pagination', TRUE),
('facet', TRUE)
);

INSERT INTO parameter(component, top_level, name, description, type, optional)
Expand Down Expand Up @@ -78,6 +79,6 @@ FROM (VALUES
('tracking', TRUE),
('carousel', TRUE),
('login', TRUE),
('pagination', TRUE)
('pagination', TRUE),
('facet', TRUE)
);

25 changes: 25 additions & 0 deletions sqlpage/templates/facet.handlebars
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<nav {{#if id}}id="{{id}}"{{/if}} aria-label="{{default description 'facets'}}" class="my-1 {{class}}">
{{#if (not (default ../compact false))}}
<div class="d-flex flex-wrap justify-content-center gap-2">
{{#if description}}<span class="fw-bold">{{description}}:</span>{{/if}}
{{#if all_link}}<a {{#if all_active}}class="fw-bold" aria-current="page"{{/if}} href="{{all_link}}">{{default all_title 'ALL'}}</a>{{/if}}
{{else}}
<div class="dropdown">
<button class="btn btn-primary dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false">
{{(default dropdown_title (default description "Choose facet"))}}
</button>
<div class="dropdown-menu">
{{#if all_link}}
<a class="dropdown-item{{#if all_active}} active{{/if}}" {{#if all_active}}aria-current="page"{{/if}} href="{{all_link}}">{{default all_title 'ALL'}}</a>
<div class="dropdown-divider"></div>
{{/if}}
{{/if}}
{{#each_row}}
{{#if (not (default ../compact false))}}
{{#if (gt @row_index 0)}}<span aria-hidden="true">|</span>{{else}}{{#if ../all_link}}<span aria-hidden="true">|</span>{{/if}}{{/if}}<a {{#if active}}class="fw-bold" aria-current="page"{{/if}} href="{{link}}">{{title}}</a>
{{else}}
<a class="dropdown-item{{#if active}} active{{/if}}" {{#if active}}aria-current="page"{{/if}} href="{{link}}">{{title}}</a>
{{/if}}
{{/each_row}}
</div>{{#if (default ../compact false)}}</div>{{/if}}
</nav>
6 changes: 6 additions & 0 deletions tests/sql_test_files/component_rendering/facet.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
SELECT 'facet' AS component, 'Categories' AS description, '/all' AS all_link, 'All categories' AS all_title, 0 AS all_active;
SELECT 'It works !' AS title, '/active' AS link, 1 AS active;
SELECT 'Other category' AS title, '/other' AS link, 0 AS active;

SELECT 'facet' AS component, 'Categories' AS description, 'It works !' AS dropdown_title, 1 AS compact, '/all' AS all_link, 1 AS all_active;
SELECT 'It works !' AS title, '/active' AS link, 0 AS active;