Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enhancement - collection tagfilters first #973

Merged
merged 21 commits into from
Jan 7, 2025
Merged
Show file tree
Hide file tree
Changes from 17 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
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,11 @@ export const BlogCard = ({
imageSrc,
itemTitle,
siteAssetsBaseUrl,
shouldShowCategory = true,
shouldShowDate = true,
tags = [],
}: CollectionCardProps & {
shouldShowCategory?: boolean
shouldShowDate?: boolean
siteAssetsBaseUrl: string | undefined
LinkComponent: CollectionPageSchemaType["LinkComponent"]
Expand Down Expand Up @@ -84,9 +86,11 @@ export const BlogCard = ({
</Text>
)}
{/* TODO: Feature enhancement? Filter by category when clicked */}
<Text className="prose-label-sm-medium text-base-content-subtle">
{category}
</Text>
{shouldShowCategory && (
<Text className="prose-label-sm-medium text-base-content-subtle">
{category}
</Text>
)}
</div>
</div>
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,11 @@ export const CollectionCard = ({
imageSrc,
itemTitle,
siteAssetsBaseUrl,
shouldShowCategory = true,
shouldShowDate = true,
tags = [],
}: CollectionCardProps & {
shouldShowCategory?: boolean
shouldShowDate?: boolean
siteAssetsBaseUrl: string | undefined
LinkComponent: CollectionPageSchemaType["LinkComponent"]
Expand Down Expand Up @@ -69,9 +71,11 @@ export const CollectionCard = ({
</Text>
)}
{/* TODO: Feature enhancement? Filter by category when clicked */}
<Text className="prose-label-md mt-3 text-base-content-subtle lg:mt-2">
{category}
</Text>
{shouldShowCategory && (
<Text className="prose-label-md mt-3 text-base-content-subtle lg:mt-2">
{category}
</Text>
)}
</div>
{image && (
<div className="relative mt-3 min-h-40 w-full shrink-0 lg:ml-4 lg:mt-0 lg:h-auto lg:w-1/4">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -406,6 +406,36 @@ export const AllResultsNoDate: Story = {
},
}

export const AllResultsSameCategory: Story = {
name: "Should not show category filter if all items have same category",
args: generateArgs({
collectionItems: COLLECTION_ITEMS.map((item) => ({
...item,
category: "The only categ0ry",
})),
}),
play: async ({ canvasElement }) => {
const screen = within(canvasElement)
const categoryFilter = screen.queryByText(/Category/i)
await expect(categoryFilter).not.toBeInTheDocument()
},
}

export const AllResultsSameYear: Story = {
name: "Should not show year filter if all items have same year",
args: generateArgs({
collectionItems: COLLECTION_ITEMS.map((item) => ({
...item,
date: "2026-05-07",
})),
}),
play: async ({ canvasElement }) => {
const screen = within(canvasElement)
const yearFilter = screen.queryByText(/Year/i)
await expect(yearFilter).not.toBeInTheDocument()
},
}

export const FileCard: Story = {
args: generateArgs({
collectionItems: [COLLECTION_ITEMS[1]] as IsomerSitemap[],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@ import {
} from "~/utils"
import { Skeleton } from "../Skeleton"
import CollectionClient from "./CollectionClient"
import { getAvailableFilters, shouldShowDate } from "./utils"
import { getAvailableFilters } from "./filterUtils"
import { shouldShowCategory, shouldShowDate } from "./utils"

const CATEGORY_OTHERS = "Others"

const getCollectionItems = (
site: IsomerSiteProps,
Expand Down Expand Up @@ -62,7 +65,7 @@ const getCollectionItems = (
type: "collectionCard" as const,
rawDate: date,
lastUpdated: date?.toISOString(),
category: item.category || "Others",
category: item.category || CATEGORY_OTHERS,
title: item.title,
description: item.summary,
image: item.image,
Expand Down Expand Up @@ -166,6 +169,7 @@ const CollectionLayout = ({
site.siteMap,
page.permalink.split("/").slice(1),
)
const availableFilters = getAvailableFilters(processedItems)

return (
<Skeleton
Expand All @@ -179,7 +183,8 @@ const CollectionLayout = ({
page={page}
breadcrumb={breadcrumb}
items={processedItems}
filters={getAvailableFilters(processedItems)}
filters={availableFilters}
shouldShowCategory={shouldShowCategory(availableFilters)}
shouldShowDate={shouldShowDate(processedItems)}
siteAssetsBaseUrl={site.assetsBaseUrl}
LinkComponent={LinkComponent}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ interface CollectionClientProps {
page: CollectionPagePageProps
items: ProcessedCollectionCardProps[]
filters: FilterType[]
shouldShowCategory: boolean
shouldShowDate: boolean
siteAssetsBaseUrl: string | undefined
breadcrumb: BreadcrumbProps
Expand Down Expand Up @@ -55,6 +56,7 @@ const CollectionClient = ({
page,
items,
filters,
shouldShowCategory,
shouldShowDate,
siteAssetsBaseUrl,
breadcrumb,
Expand Down Expand Up @@ -130,6 +132,7 @@ const CollectionClient = ({
paginatedItems={paginatedItems}
searchValue={searchValue}
totalCount={totalCount}
shouldShowCategory={shouldShowCategory}
shouldShowDate={shouldShowDate}
siteAssetsBaseUrl={siteAssetsBaseUrl}
LinkComponent={LinkComponent}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ interface CollectionResultProps
| "handleClearFilter"
| "totalCount"
> {
shouldShowCategory?: boolean
shouldShowDate?: boolean
variant?: CollectionVariant
siteAssetsBaseUrl: string | undefined
Expand Down Expand Up @@ -48,6 +49,7 @@ export const CollectionResults = ({
filteredCount,
handleClearFilter,
totalCount,
shouldShowCategory = true,
shouldShowDate = true,
siteAssetsBaseUrl,
LinkComponent,
Expand Down Expand Up @@ -88,6 +90,7 @@ export const CollectionResults = ({
<CollectionCard
key={`${item.title}-${item.category}`}
{...item}
shouldShowCategory={shouldShowCategory}
shouldShowDate={shouldShowDate}
siteAssetsBaseUrl={siteAssetsBaseUrl}
LinkComponent={LinkComponent}
Expand All @@ -96,6 +99,7 @@ export const CollectionResults = ({
<BlogCard
key={`${item.title}-${item.category}`}
{...item}
shouldShowCategory={shouldShowCategory}
shouldShowDate={shouldShowDate}
siteAssetsBaseUrl={siteAssetsBaseUrl}
LinkComponent={LinkComponent}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export const FILTER_ID_CATEGORY = "category"
export const FILTER_ID_YEAR = "year"
export const NO_SPECIFIED_YEAR_FILTER_ID = "not_specified"
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import type { Filter } from "../../../types/Filter"
import type { ProcessedCollectionCardProps } from "~/interfaces"
import { getCategoryFilter } from "./getCategoryFilter"
import { getTagFilters } from "./getTagFilters"
import { getYearFilter } from "./getYearFilter"

export const getAvailableFilters = (
items: ProcessedCollectionCardProps[],
): Filter[] => {
// TODO: Allow user to pass in order of filters to be shown
return [
...getTagFilters(items),
getCategoryFilter(items),
getYearFilter(items),
].filter((filter) => filter.items.length >= 2)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import type { ProcessedCollectionCardProps } from "~/interfaces"
import type { Filter } from "~/templates/next/types/Filter"
import { FILTER_ID_CATEGORY } from "./constants"

export const getCategoryFilter = (
items: ProcessedCollectionCardProps[],
): Filter => {
const categories: Record<string, number> = {}

items.forEach(({ category }) => {
if (category in categories && categories[category]) {
categories[category] += 1
} else {
categories[category] = 1
}
})

const categoryFilterItems = Object.entries(categories)
.map(([label, count]) => ({
id: label.toLowerCase(),
label: label.charAt(0).toUpperCase() + label.slice(1),
count,
}))
.sort((a, b) => a.label.localeCompare(b.label))

return {
id: FILTER_ID_CATEGORY,
label: "Category",
items: categoryFilterItems,
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import type { Filter, FilterItem } from "../../../types/Filter"
import type { ProcessedCollectionCardProps } from "~/interfaces"

export const getTagFilters = (
items: ProcessedCollectionCardProps[],
): Filter[] => {
// NOTE: Each tag is a mapping of a category to its
// associated set of values as well as the selected value.
// Hence, we store a map here of the category (eg: Body parts)
// to the number of occurences of each value (eg: { Brain: 3, Leg: 2})
const tagCategories: Record<string, Record<string, number>> = {}

items.forEach(({ tags }) => {
if (tags) {
tags.forEach(({ selected: selectedLabels, category }) => {
selectedLabels.forEach((label) => {
if (!tagCategories[category]) {
tagCategories[category] = {}
}
if (!tagCategories[category][label]) {
tagCategories[category][label] = 0
}

tagCategories[category][label] += 1
})
})
}
})

return Object.entries(tagCategories).reduce((acc: Filter[], curValue) => {
const [category, values] = curValue
const items: FilterItem[] = Object.entries(values).map(
([label, count]) => ({
label,
count,
id: label,
}),
)

const filters: Filter[] = [
...acc,
{
items,
id: category,
label: category,
},
]

return filters
}, [])
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import type { Filter } from "../../../types/Filter"
import type { ProcessedCollectionCardProps } from "~/interfaces"
import { getParsedDate } from "~/utils"
import { FILTER_ID_YEAR, NO_SPECIFIED_YEAR_FILTER_ID } from "./constants"

export const getYearFilter = (
items: ProcessedCollectionCardProps[],
): Filter => {
const years: Record<string, number> = {}
let numberOfUndefinedDates = 0

items.forEach(({ lastUpdated }) => {
if (lastUpdated) {
const year = getParsedDate(lastUpdated).getFullYear().toString()
if (year in years && years[year]) {
years[year] += 1
} else {
years[year] = 1
}
} else {
numberOfUndefinedDates += 1
}
})

const yearFilterItems = Object.entries(years)
.map(([label, count]) => ({
id: label.toLowerCase(),
label,
count,
}))
.sort((a, b) => parseInt(b.label) - parseInt(a.label))

return {
id: FILTER_ID_YEAR,
label: "Year",
// do not show "not specified" option if all items have undefined dates
items:
yearFilterItems.length === 0
? []
: numberOfUndefinedDates === 0
? yearFilterItems
: [
...yearFilterItems,
{
id: NO_SPECIFIED_YEAR_FILTER_ID,
label: "Not specified",
count: numberOfUndefinedDates,
},
],
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from "./constants"
export * from "./getAvailableFilters"
Loading
Loading