Skip to content

Commit 22d4839

Browse files
Merge pull request #132 from communitiesuk/filter-panel-example
add simple client side example
2 parents a652d14 + fe15da8 commit 22d4839

File tree

3 files changed

+220
-2
lines changed

3 files changed

+220
-2
lines changed

src/lib/components/data-vis/table/Table.svelte

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
colourScale = undefined,
99
} = $props();
1010
11-
let localCopyOfData = $state([...data]);
11+
let localCopyOfData = $derived([...data]);
1212
1313
function hasUniqueValues(array, key) {
1414
const seen = new Set();

src/wrappers/components/ui/filter-panel/Examples.svelte

Lines changed: 119 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,11 @@
1010
enhancedFormExampleCode,
1111
basicExampleCode,
1212
advancedExampleCode,
13+
clientSideOnlyCode,
1314
} from "./codeBlocks.js";
1415
1516
import FilterPanel from "$lib/components/ui/FilterPanel.svelte";
17+
import Table from "$lib/components/data-vis/table/Table.svelte";
1618
1719
// Accept form prop from parent (runes mode)
1820
let { form } = $props();
@@ -35,6 +37,52 @@
3537
let clientResultsCount = $state("Ready to filter");
3638
let clientFormSubmitted = $state(false);
3739
40+
// Simple client-side only data
41+
const simpleItems = [
42+
{ id: 1, name: "Laptop", category: "electronics" },
43+
{ id: 2, name: "Mouse", category: "electronics" },
44+
{ id: 3, name: "Coffee Mug", category: "home" },
45+
{ id: 4, name: "Desk Chair", category: "home" },
46+
];
47+
48+
// Metadata for the Table component
49+
const simpleMetaData = {
50+
id: { shortLabel: "ID", explainer: "Item ID" },
51+
name: { shortLabel: "Name", explainer: "Item name" },
52+
category: { shortLabel: "Category", explainer: "Item category" },
53+
};
54+
55+
// Client-side state
56+
let filteredItems = $state(simpleItems);
57+
let selectedCategory = $state("all");
58+
59+
// One simple filter function
60+
function filterItems(category: string) {
61+
return category === "all"
62+
? simpleItems
63+
: simpleItems.filter((item) => item.category === category);
64+
}
65+
66+
// One filter section
67+
let oneFilterSection = [
68+
{
69+
id: "category",
70+
type: "radios" as "radios",
71+
title: "Category",
72+
ga4Section: "category_filter",
73+
ga4IndexSection: 1,
74+
ga4IndexSectionCount: 1,
75+
name: "category",
76+
legend: "Select category",
77+
options: [
78+
{ value: "all", label: "All items" },
79+
{ value: "electronics", label: "Electronics" },
80+
{ value: "home", label: "Home" },
81+
],
82+
selectedValue: selectedCategory,
83+
},
84+
];
85+
3886
// Basic filter sections for use in examples
3987
const basicFilterSections = [
4088
{
@@ -277,7 +325,13 @@
277325
{
278326
id: "5",
279327
heading:
280-
"5. Progressive Enhancement with use:enhance to cancel form submission and process client-side, server-side submission as fallback",
328+
"5. Client-Side Only with use:enhance cancel() - No Server Fallback",
329+
content: ClientSideOnlyExample,
330+
},
331+
{
332+
id: "6",
333+
heading:
334+
"6. Progressive Enhancement with use:enhance to cancel form submission and process client-side, server-side submission as fallback",
281335
content: EnhancedFormExample,
282336
},
283337
];
@@ -542,6 +596,70 @@
542596
></CodeBlock>
543597
{/snippet}
544598

599+
{#snippet ClientSideOnlyExample()}
600+
<div class="p-5 bg-white">
601+
<h3 class="text-xl font-bold mb-4">
602+
Client-Side Only with use:enhance cancel()
603+
</h3>
604+
<p class="mb-4">
605+
This example demonstrates <code>use:enhance</code> with
606+
<code>cancel()</code>
607+
to prevent server submission entirely. All processing happens client-side only.
608+
<strong>No server fallback</strong> - this requires JavaScript to work.
609+
</p>
610+
611+
<form
612+
method="POST"
613+
use:enhance={({ formData, cancel }) => {
614+
// Cancel server submission - process client-side only
615+
cancel();
616+
617+
// Extract form value
618+
const category = formData.get("category")?.toString() || "all";
619+
620+
// Update state
621+
selectedCategory = category;
622+
filteredItems = filterItems(category);
623+
624+
// No return needed - we cancelled the submission
625+
}}
626+
>
627+
<FilterPanel
628+
sectionsData={oneFilterSection}
629+
resultsCount={`${filteredItems.length} items found`}
630+
filterButtonText="Filter items"
631+
applyButtonText="Apply filter (Client-side only)"
632+
ga4BaseEvent={{ event_name: "filter_items", type: "client_only" }}
633+
/>
634+
</form>
635+
636+
<!-- Display current filter -->
637+
<p class="govuk-body">
638+
<strong>Selected Category:</strong>
639+
{selectedCategory === "all" ? "All items" : selectedCategory}
640+
</p>
641+
642+
<!-- Display results using Table component -->
643+
<div class="mt-8 border-t pt-4">
644+
<h4 class="text-lg font-semibold mb-4">
645+
Results ({filteredItems.length} items)
646+
</h4>
647+
<Table
648+
data={filteredItems}
649+
metaData={simpleMetaData}
650+
caption="Filtered Items"
651+
colourScale="Off"
652+
/>
653+
654+
{#if filteredItems.length === 0}
655+
<p class="text-gray-500 italic mt-4">No items match your filter.</p>
656+
{/if}
657+
</div>
658+
</div>
659+
660+
<CodeBlock code={clientSideOnlyCode} language="svelte"></CodeBlock>
661+
{/snippet}
662+
545663
{#snippet EnhancedFormExample()}
546664
<div class="p-5 bg-white">
547665
<h3 class="text-xl font-bold mb-4">

src/wrappers/components/ui/filter-panel/codeBlocks.js

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -747,3 +747,103 @@ export const advancedExampleCode = `
747747
ga4BaseEvent={{ event_name: "filter_listings", type: "advanced" }}
748748
/>
749749
`;
750+
751+
// Client-Side Only Example with use:enhance cancel()
752+
export const clientSideOnlyCode = `
753+
<script>
754+
import FilterPanel from '$lib/components/ui/FilterPanel.svelte';
755+
import Table from '$lib/components/data-vis/table/Table.svelte';
756+
import { enhance } from '$app/forms';
757+
758+
// Simple client-side only data
759+
const simpleItems = [
760+
{ id: 1, name: "Laptop", category: "electronics" },
761+
{ id: 2, name: "Mouse", category: "electronics" },
762+
{ id: 3, name: "Coffee Mug", category: "home" },
763+
{ id: 4, name: "Desk Chair", category: "home" },
764+
];
765+
766+
// Metadata for the Table component
767+
const simpleMetaData = {
768+
id: { shortLabel: "ID", explainer: "Item ID" },
769+
name: { shortLabel: "Name", explainer: "Item name" },
770+
category: { shortLabel: "Category", explainer: "Item category" },
771+
};
772+
773+
// Client-side state
774+
let filteredItems = $state(simpleItems);
775+
let selectedCategory = $state("all");
776+
777+
// One simple filter function
778+
function filterItems(category) {
779+
return category === "all" ? simpleItems : simpleItems.filter(item => item.category === category);
780+
}
781+
782+
// One filter section
783+
const oneFilterSection = [
784+
{
785+
id: "category",
786+
type: "radios",
787+
title: "Category",
788+
ga4Section: "category_filter",
789+
ga4IndexSection: 1,
790+
ga4IndexSectionCount: 1,
791+
name: "category",
792+
legend: "Select category",
793+
options: [
794+
{ value: "all", label: "All items" },
795+
{ value: "electronics", label: "Electronics" },
796+
{ value: "home", label: "Home" },
797+
],
798+
selectedValue: "all",
799+
},
800+
];
801+
</script>
802+
803+
<!-- Form with use:enhance and cancel() -->
804+
<form
805+
method="POST"
806+
use:enhance={({ formData, cancel }) => {
807+
// Cancel server submission - process client-side only
808+
cancel();
809+
810+
// Extract form value
811+
const category = formData.get("category")?.toString() || "all";
812+
813+
// Update state
814+
selectedCategory = category;
815+
filteredItems = filterItems(category);
816+
817+
// No return needed - we cancelled the submission
818+
}}
819+
>
820+
<FilterPanel
821+
sectionsData={oneFilterSection}
822+
resultsCount={\`\${filteredItems.length} items found\`}
823+
filterButtonText="Filter items"
824+
applyButtonText="Apply filter (Client-side only)"
825+
ga4BaseEvent={{ event_name: "filter_items", type: "client_only" }}
826+
/>
827+
</form>
828+
829+
<!-- Display current filter -->
830+
<p class="govuk-body">
831+
<strong>Selected Category:</strong>
832+
{selectedCategory === "all" ? "All items" : selectedCategory}
833+
</p>
834+
835+
<!-- Display results using Table component -->
836+
<div class="mt-8 border-t pt-4">
837+
<h4 class="text-lg font-semibold mb-4">Results ({filteredItems.length} items)</h4>
838+
<Table
839+
data={filteredItems}
840+
metaData={simpleMetaData}
841+
caption="Filtered Items"
842+
colourScale="Off"
843+
/>
844+
845+
{#if filteredItems.length === 0}
846+
<p class="text-gray-500 italic mt-4">No items match your filter.</p>
847+
{/if}
848+
</div>
849+
`;

0 commit comments

Comments
 (0)