-
Notifications
You must be signed in to change notification settings - Fork 47
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Move useSort (now useTableSort) into its own module
- Loading branch information
1 parent
06f8ebe
commit 7b6e066
Showing
2 changed files
with
98 additions
and
104 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
import React, { useCallback, useState, ReactNode } from 'react' | ||
import { TooltipAnchor, TooltipHint } from '@gnomad/ui' | ||
|
||
export type RowCompareFunction<RowData> = (a: RowData, b: RowData) => number | ||
|
||
export type ColumnSpecifier<RowData> = { | ||
key: keyof RowData | ||
label: string | ||
tooltip: string | null | ||
compareValueFunction: RowCompareFunction<RowData> | ||
} | ||
|
||
const renderColumnHeader = <RowData,>( | ||
key: keyof RowData, | ||
sortBy: keyof RowData, | ||
setSortBy: (key: keyof RowData) => void, | ||
sortAscending: boolean, | ||
label: string, | ||
tooltip: string | null | ||
) => { | ||
let ariaSortAttr: React.AriaAttributes['aria-sort'] = 'none' | ||
if (sortBy === key) { | ||
ariaSortAttr = sortAscending ? 'ascending' : 'descending' | ||
} | ||
|
||
return tooltip ? ( | ||
<th aria-sort={ariaSortAttr} scope="col"> | ||
{/* @ts-expect-error TS(2322) FIXME: Type '{ children: Element; tooltip: any; }' is not... Remove this comment to see the full error message */} | ||
<TooltipAnchor tooltip={tooltip}> | ||
<button type="button" onClick={() => setSortBy(key)}> | ||
{/* @ts-expect-error TS(2745) FIXME: This JSX tag's 'children' prop expects type 'never... Remove this comment to see the full error message */} | ||
<TooltipHint>{label}</TooltipHint> | ||
</button> | ||
</TooltipAnchor> | ||
</th> | ||
) : ( | ||
<th aria-sort={ariaSortAttr} scope="col"> | ||
<button type="button" onClick={() => setSortBy(key)}> | ||
{label} | ||
</button> | ||
</th> | ||
) | ||
} | ||
|
||
const useTableSort = <RowData,>( | ||
columnSpecifiers: ColumnSpecifier<RowData>[], | ||
defaultSortKey: keyof RowData, | ||
rowData: RowData[] | ||
): { headers: ReactNode; sortedRowData: RowData[] } => { | ||
const [key, setKey] = useState<keyof RowData>(defaultSortKey) | ||
const [ascending, setAscending] = useState<boolean>(false) | ||
|
||
const setSortKey = useCallback( | ||
(newKey: keyof RowData) => { | ||
setKey(newKey) | ||
setAscending(newKey === key ? !ascending : false) | ||
}, | ||
[key, ascending] | ||
) | ||
|
||
const { compareValueFunction } = columnSpecifiers.find((column) => column.key === key)! | ||
const sortedRowData = [...rowData].sort((a, b) => { | ||
const ascendingCompare = compareValueFunction(a, b) | ||
return ascending ? ascendingCompare : -ascendingCompare | ||
}) | ||
|
||
const headers = ( | ||
<> | ||
{columnSpecifiers.map((columnSpecifier) => | ||
renderColumnHeader( | ||
columnSpecifier.key, | ||
key, | ||
setSortKey, | ||
ascending, | ||
columnSpecifier.label, | ||
columnSpecifier.tooltip | ||
) | ||
)} | ||
</> | ||
) | ||
return { headers, sortedRowData } | ||
} | ||
|
||
type NumberHolder<Key extends string> = { | ||
[K in Key]: number | ||
} | ||
|
||
export const numericCompareFunction = | ||
<Key extends string>(key: Key) => | ||
<RowData extends NumberHolder<Key>>(a: RowData, b: RowData) => | ||
a[key] - b[key] | ||
|
||
export default useTableSort |