Skip to content

Commit

Permalink
Port useSortAndFilter() to App Router style [#1066]
Browse files Browse the repository at this point in the history
* convert `export const`` to `export default function`
* add "use client"
* refactor and update code
  • Loading branch information
genehack committed Mar 6, 2025
1 parent 43ea816 commit 153b38c
Showing 1 changed file with 84 additions and 39 deletions.
123 changes: 84 additions & 39 deletions static-site/components/list-resources/use-sort-and-filter.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,54 @@
import { useMemo } from 'react';
import { convertVersionedGroup, FilterOption, Group, Resource, SortMethod, VersionedGroup } from './types';
"use client";

import { useMemo } from "react";

export const useSortAndFilter = (
sortMethod: SortMethod,
selectedFilterOptions: readonly FilterOption[],
setState: React.Dispatch<React.SetStateAction<Group[]>>,
originalData?: Group[],
) => {
import {
convertVersionedGroup,
FilterOption,
Group,
Resource,
SortMethod,
VersionedGroup,
} from "./types";

/**
* `useSortAndFilter` takes a `sortMethod` (which currently must be
* either `"lastUpdated"` or `"alphabetical"` in order for the function
* call to have any impact), and then uses the provided
* `selectedFilterOptions` to sort a copy of the provided
* `originalData`, which is then propagated back to the calling code
* via the provided `setState` callback.
*
* N.b., this code runs in a React Client Component context (i.e., not
* server side, but during the client-side hydration phase)
*
* @param sortMethod - either "lastUpdated" or "alphabetical""
* @param selectedFilterOptions - a list of `FilterOption` values
* @param setState - a setter for a React State
* @param originalData - a list of Group objects
*/
export default function useSortAndFilter(
sortMethod: SortMethod,
selectedFilterOptions: readonly FilterOption[],
setState: React.Dispatch<React.SetStateAction<Group[]>>,
originalData?: Group[],
): void {
useMemo(() => {
if (!originalData) return;
/* Following console log is really useful for development */
if (!originalData) {
return;
}

// Following console log is really useful for development
// console.log(`useSortAndFilter() sortMethod "${sortMethod}" ` + (selectedFilterOptions.length ? `filtering to ${selectedFilterOptions.map((el) => el.value).join(", ")}` : '(no filtering)'))

const searchValues = selectedFilterOptions.map((o) => o.value);
function _filterResources(resource: Resource) {
if (searchValues.length===0) return true;
return searchValues
.map((searchString) => resource.nameParts.includes(searchString))
.every((x) => x);

function _filterResources(resource: Resource): boolean {
return searchValues.length === 0
? true
: searchValues
.map((searchString) => resource.nameParts.includes(searchString))
.every((x) => x);
}

function sortAndFilter<T extends Group>(
Expand All @@ -31,48 +61,63 @@ export const useSortAndFilter = (
...group,
resources: group.resources
.filter(_filterResources)
.sort(sortResources)
.sort(sortResources),
}))
.filter((group) => !!group.resources.length)
.sort(sortGroups);
}

if (sortMethod === "lastUpdated") {
const groups = originalData.map((group: Group) => convertVersionedGroup(group))
const _sortGroups = (groupA: VersionedGroup, groupB: VersionedGroup) => _newestFirstSort(groupA.lastUpdated, groupB.lastUpdated)
const _sortResources = (a: Resource, b: Resource) => {
if (!a.lastUpdated || !b.lastUpdated || a.lastUpdated === b.lastUpdated) {
const groups = originalData.map((group: Group) =>
convertVersionedGroup(group),
);
const _sortGroups = (
groupA: VersionedGroup,
groupB: VersionedGroup,
): 0 | 1 | -1 => _newestFirstSort(groupA.lastUpdated, groupB.lastUpdated);
const _sortResources = (a: Resource, b: Resource): 0 | 1 | -1 => {
if (
!a.lastUpdated ||
!b.lastUpdated ||
a.lastUpdated === b.lastUpdated
) {
// resources updated on the same day or without a last updated date
// sort alphabetically
return _lexicographicSort(a.name, b.name)
}
else {
return _lexicographicSort(a.name, b.name);
} else {
return _newestFirstSort(a.lastUpdated, b.lastUpdated);
}
}
const resourceGroups = sortAndFilter(groups, _sortGroups, _sortResources)
setState(resourceGroups)
};
const resourceGroups = sortAndFilter(groups, _sortGroups, _sortResources);
setState(resourceGroups);
} else if (sortMethod === "alphabetical") {
const groups = originalData;
const _sortGroups = (groupA: Group, groupB: Group) => _lexicographicSort(groupA.groupName.toLowerCase(), groupB.groupName.toLowerCase())
const _sortResources = (a: Resource, b: Resource) => _lexicographicSort(a.name, b.name)
const resourceGroups = sortAndFilter(groups, _sortGroups, _sortResources)
setState(resourceGroups)
const _sortGroups = (groupA: Group, groupB: Group) =>
_lexicographicSort(
groupA.groupName.toLowerCase(),
groupB.groupName.toLowerCase(),
);
const _sortResources = (a: Resource, b: Resource) =>
_lexicographicSort(a.name, b.name);
const resourceGroups = sortAndFilter(groups, _sortGroups, _sortResources);
setState(resourceGroups);
}
}, [sortMethod, selectedFilterOptions, originalData, setState])
}, [sortMethod, selectedFilterOptions, originalData, setState]);
}


/* helper function to sort alphabetically. If provided with YYYY-MM-DD strings
* this is the same as a chronological sort (oldest to newest)
/**
* Helper function to sort strings alphabetically. If provided with
* YYYY-MM-DD strings this is the same as a chronological sort (oldest
* to newest)
*/
function _lexicographicSort(a: string, b: string): 1 | -1 | 0 {
return a<b ? -1 : a>b ? 1 : 0;
return a < b ? -1 : a > b ? 1 : 0;
}

/* If provided with YYYY-MM-DD strings this sorts chronologically with newest
* first (this is just reverse lexicographic sort)
/**
* Helper function to sort YYYY-MM-DD strings chronologically with
* newer dates first (which is equivalent to reverse lexigraphic sort)
*/
function _newestFirstSort(a: string, b: string): 1 | -1 | 0 {
return a<b ? 1 : a>b ? -1 : 0;
}
return a < b ? 1 : a > b ? -1 : 0;
}

0 comments on commit 153b38c

Please sign in to comment.