Skip to content

Commit 9fc52ad

Browse files
committed
add filter
1 parent 22dc549 commit 9fc52ad

File tree

2 files changed

+121
-49
lines changed

2 files changed

+121
-49
lines changed

src/components/changelogs/Header.astro

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,35 @@
11
---
22
import { Image } from "astro:assets";
3+
import { z } from "astro:schema";
4+
import { getEntry, reference, type CollectionEntry } from "astro:content";
35
46
import { StarlightIcon } from "..";
57
import HeroImage from "~/assets/images/release-notes/hero.svg";
8+
import { releaseNotesSchema } from "~/schemas";
9+
10+
type Props = z.infer<typeof props>;
11+
12+
const props = z.object({
13+
entries: z.array(z.any()),
14+
});
15+
16+
const { entries } = props.parse(Astro.props);
17+
18+
async function uniqueProducts(
19+
entries: Array<CollectionEntry<"release-notes">>,
20+
) {
21+
const products = entries.flatMap((entry) => entry.data.products ?? []);
22+
23+
const unique = [...new Set(products)];
24+
25+
return Promise.all(
26+
unique.map(async (product) => {
27+
return getEntry(product);
28+
}),
29+
);
30+
}
31+
32+
const products = await uniqueProducts(entries);
633
---
734

835
<div
@@ -30,6 +57,22 @@ import HeroImage from "~/assets/images/release-notes/hero.svg";
3057
</a>
3158
</span>
3259
</p>
60+
<div>
61+
<select id="release-notes-filter" class="mt-2 h-8 w-52">
62+
<option value="all">All products</option>
63+
{
64+
products
65+
.sort((a, b) => a.id.localeCompare(b.id))
66+
.map(async (product) => {
67+
return (
68+
<option value={product.id}>
69+
{product.data.product.title}
70+
</option>
71+
);
72+
})
73+
}
74+
</select>
75+
</div>
3376
</div>
3477
<div class="!mt-0 hidden sm:block">
3578
<Image src={HeroImage} alt="hero image" height="240" />

src/pages/release-notes/index.astro

Lines changed: 78 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
import StarlightPage from "@astrojs/starlight/components/StarlightPage.astro";
3-
import { getCollection } from "astro:content";
3+
import { getCollection, type CollectionEntry } from "astro:content";
44
55
import Header from "~/components/changelogs/Header.astro";
66
import ProductPills from "~/components/changelogs/ProductPills.astro";
@@ -10,9 +10,7 @@ import { format } from "date-fns";
1010
1111
const notes = await getCollection("release-notes");
1212
13-
const days = Object.entries(
14-
Object.groupBy(notes, (note) => note.id.split("/").slice(0, 3).join("/")),
15-
);
13+
notes.sort((a, b) => b.data.date.getTime() - a.data.date.getTime());
1614
---
1715

1816
<StarlightPage
@@ -24,58 +22,89 @@ const days = Object.entries(
2422
hideTitle={true}
2523
hideBreadcrumbs={true}
2624
>
27-
<Header />
25+
<Header entries={notes} />
2826
<div
29-
class="max-w-[50rem] justify-self-center px-4 md:grid md:grid-cols-[15%_auto]"
27+
class="w-full max-w-[50rem] justify-self-center px-4 md:grid md:grid-cols-[15%_auto]"
3028
>
3129
{
32-
days
33-
.sort()
34-
.reverse()
35-
.map(([date, entries]) => {
36-
const formatted = format(date, "MMMM dd");
37-
38-
return (
39-
<>
40-
<div class="!mt-0">
41-
<p class="md:content-center md:pr-4 md:text-right md:leading-none">
42-
<strong class="text-xs">{formatted}</strong>
43-
<span class="text-xs">12:00PM</span>
44-
</p>
45-
</div>
46-
<Steps>
47-
<ol class="!mt-0">
48-
{entries?.map(async (entry) => {
49-
const { Content } = await entry.render();
50-
51-
return (
52-
<li>
53-
<div>
54-
<a
55-
href={`/release-notes/${entry.slug}/`}
56-
class="no-underline hover:underline"
57-
>
58-
<h3 class="mb-4 !leading-[28px] text-black">
59-
{entry.data.title}
60-
</h3>
61-
</a>
62-
<ProductPills products={entry.data.products} />
63-
</div>
64-
<p>
65-
<Content />
66-
</p>
67-
</li>
68-
);
69-
})}
70-
</ol>
71-
</Steps>
72-
</>
73-
);
74-
})
30+
notes.map(async (entry) => {
31+
const date = format(entry.data.date, "MMM dd, yyyy");
32+
const time = format(entry.data.date, "hh:mm a");
33+
const productIds = JSON.stringify(
34+
entry.data.products.map((product) => product.id),
35+
);
36+
37+
const { Content } = await entry.render();
38+
39+
return (
40+
<>
41+
<div class="!mt-0" data-products={productIds}>
42+
<p class="md:content-center md:pr-4 md:text-right md:leading-none">
43+
<strong class="text-xs">{date}</strong>
44+
<span class="text-xs">{time}</span>
45+
</p>
46+
</div>
47+
<Steps>
48+
<ol class="!mt-0" data-products={productIds}>
49+
<li>
50+
<div>
51+
<a
52+
href={`/release-notes/${entry.slug}/`}
53+
class="no-underline hover:underline"
54+
>
55+
<h3 class="mb-4 !leading-[28px] text-black">
56+
{entry.data.title}
57+
</h3>
58+
</a>
59+
<ProductPills products={entry.data.products} />
60+
</div>
61+
<p>
62+
<Content />
63+
</p>
64+
</li>
65+
</ol>
66+
</Steps>
67+
</>
68+
);
69+
})
7570
}
7671
</div>
7772
</StarlightPage>
7873

74+
<script>
75+
const filter = document.querySelector<HTMLSelectElement>(
76+
"#release-notes-filter",
77+
);
78+
79+
filter?.addEventListener("change", () => {
80+
const entries = document.querySelectorAll<HTMLElement>("[data-products]");
81+
82+
if (!entries) return;
83+
84+
const value = filter.value;
85+
86+
if (value === "all") {
87+
entries.forEach((entry) => (entry.style.display = ""));
88+
89+
return;
90+
}
91+
92+
for (const entry of entries) {
93+
if (!entry.dataset.products) continue;
94+
95+
const products = JSON.parse(entry.dataset.products);
96+
97+
const show = products.includes(value);
98+
99+
if (show) {
100+
entry.style.display = "";
101+
} else {
102+
entry.style.display = "none";
103+
}
104+
}
105+
});
106+
</script>
107+
79108
<style>
80109
:root {
81110
--sl-content-width: 100% !important;

0 commit comments

Comments
 (0)