Skip to content

Commit

Permalink
Export useOrderedRows hook
Browse files Browse the repository at this point in the history
  • Loading branch information
acelaya committed Apr 18, 2024
1 parent 21ceb38 commit 4177ebd
Show file tree
Hide file tree
Showing 6 changed files with 164 additions and 32 deletions.
7 changes: 1 addition & 6 deletions src/components/data/DataTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { useCallback, useContext, useEffect, useMemo } from 'preact/hooks';
import { useArrowKeyNavigation } from '../../hooks/use-arrow-key-navigation';
import { useStableCallback } from '../../hooks/use-stable-callback';
import { useSyncedRef } from '../../hooks/use-synced-ref';
import type { CompositeProps } from '../../types';
import type { CompositeProps, Order } from '../../types';
import { downcastRef } from '../../util/typing';
import { ArrowDownIcon, ArrowUpIcon, SpinnerSpokesIcon } from '../icons';
import { Button } from '../input';
Expand All @@ -22,11 +22,6 @@ export type TableColumn<Field> = {
classes?: string;
};

export type Order<Field> = {
field: Field;
direction: 'ascending' | 'descending';
};

type ComponentProps<Row> = {
rows: Row[];
columns: TableColumn<keyof Row>[];
Expand Down
129 changes: 129 additions & 0 deletions src/hooks/test/use-ordered-rows-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
import { mount } from 'enzyme';
import { useState } from 'preact/hooks';

import { useOrderedRows } from '../use-ordered-rows';

const starWarsCharacters = [
{ name: 'Luck Skywalker', age: 20 },
{ name: 'Princess Leia Organa', age: 20 },
{ name: 'Han Solo', age: 25 },
];

describe('useOrderedRows', () => {
function FakeComponent() {
const [order, setOrder] = useState();
const orderedRows = useOrderedRows(starWarsCharacters, order);

return (
<div>
{orderedRows.map((character, index) => (
<div key={`${character.name}${index}`}>
<span data-testid={`name-${index}`}>{character.name}</span>
<span data-testid={`age-${index}`}>{character.age}</span>
</div>
))}
<button
data-testid="button-order-by-name-asc"
onClick={() => setOrder({ field: 'name', direction: 'ascending' })}
>
By name ASC
</button>
<button
data-testid="button-order-by-name-desc"
onClick={() => setOrder({ field: 'name', direction: 'descending' })}
>
By name DES
</button>
<button
data-testid="button-order-by-age-asc"
onClick={() => setOrder({ field: 'age', direction: 'ascending' })}
>
By age ASC
</button>
<button
data-testid="button-order-by-age-desc"
onClick={() => setOrder({ field: 'age', direction: 'descending' })}
>
By age DES
</button>
<button
data-testid="button-reset-order"
onClick={() => setOrder(undefined)}
>
Reset order
</button>
</div>
);
}

function createComponent() {
return mount(<FakeComponent />);
}

function assertDefaultOrder(wrapper) {
assertOrder(wrapper, starWarsCharacters);
}

function assertOrder(wrapper, rows) {
rows.forEach((character, index) => {
assert.equal(
character.name,
wrapper.find(`[data-testid="name-${index}"]`).text(),
);
assert.equal(
character.age,
wrapper.find(`[data-testid="age-${index}"]`).text(),
);
});
}

[
{
orderId: 'button-order-by-name-asc',
expectedRows: [
{ name: 'Han Solo', age: 25 },
{ name: 'Luck Skywalker', age: 20 },
{ name: 'Princess Leia Organa', age: 20 },
],
},
{
orderId: 'button-order-by-name-desc',
expectedRows: [
{ name: 'Princess Leia Organa', age: 20 },
{ name: 'Luck Skywalker', age: 20 },
{ name: 'Han Solo', age: 25 },
],
},
{
orderId: 'button-order-by-age-asc',
expectedRows: [
{ name: 'Luck Skywalker', age: 20 },
{ name: 'Princess Leia Organa', age: 20 },
{ name: 'Han Solo', age: 25 },
],
},
{
orderId: 'button-order-by-age-desc',
expectedRows: [
{ name: 'Han Solo', age: 25 },
{ name: 'Luck Skywalker', age: 20 },
{ name: 'Princess Leia Organa', age: 20 },
],
},
].forEach(({ orderId, expectedRows }) => {
it('orders rows based on field and direction', () => {
const wrapper = createComponent();

// Rows are initially not ordered
assertDefaultOrder(wrapper);

// Click button to order
wrapper.find(`[data-testid="${orderId}"]`).simulate('click');
assertOrder(wrapper, expectedRows);

// Order can be reset
wrapper.find('[data-testid="button-reset-order"]').simulate('click');
assertDefaultOrder(wrapper);
});
});
});
23 changes: 23 additions & 0 deletions src/hooks/use-ordered-rows.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { useMemo } from 'preact/hooks';

import type { Order } from '../types';

export function useOrderedRows<Row>(rows: Row[], order?: Order<keyof Row>) {
return useMemo(() => {
if (!order) {
return rows;
}

return [...rows].sort((a, b) => {
if (a[order.field] === b[order.field]) {
return 0;
}

if (order.direction === 'ascending') {
return a[order.field] > b[order.field] ? 1 : -1;
}

return a[order.field] > b[order.field] ? -1 : 1;
});
}, [order, rows]);
}
2 changes: 2 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ export { useArrowKeyNavigation } from './hooks/use-arrow-key-navigation';
export { useClickAway } from './hooks/use-click-away';
export { useFocusAway } from './hooks/use-focus-away';
export { useKeyPress } from './hooks/use-key-press';
export { useOrderedRows } from './hooks/use-ordered-rows';
export { useStableCallback } from './hooks/use-stable-callback';
export { useSyncedRef } from './hooks/use-synced-ref';
export { useToastMessages } from './hooks/use-toast-messages';
Expand Down Expand Up @@ -74,6 +75,7 @@ export type {
BaseProps,
CompositeProps,
IconComponent,
Order,
PresentationalProps,
TransitionComponent,
} from './types';
Expand Down
30 changes: 4 additions & 26 deletions src/pattern-library/components/patterns/data/DataTablePage.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { useCallback, useMemo, useRef, useState } from 'preact/hooks';
import { useCallback, useRef, useState } from 'preact/hooks';

import { Button, DataTable, type DataTableProps, Scroll } from '../../../../';
import type { Order } from '../../../../components/data/DataTable';
import { Button, DataTable, Scroll } from '../../../../';
import type { DataTableProps, Order } from '../../../../';
import { useOrderedRows } from '../../../../hooks/use-ordered-rows';
import Library from '../../Library';
import { nabokovNovels } from '../samples';
import type { NabokovNovel } from '../samples';
Expand All @@ -15,29 +16,6 @@ const nabokovColumns = [

type SimpleNabokovNovel = Omit<NabokovNovel, 'translatedTitle'>;

function useOrderedRows(
rows: SimpleNabokovNovel[],
order?: Order<keyof SimpleNabokovNovel>,
) {
return useMemo(() => {
if (!order) {
return rows;
}

return [...rows].sort((a, b) => {
if (a[order.field] === b[order.field]) {
return 0;
}

if (order.direction === 'ascending') {
return a[order.field] > b[order.field] ? 1 : -1;
}

return a[order.field] > b[order.field] ? -1 : 1;
});
}, [order, rows]);
}

function ClientOrderableDataTable({
rows,
// By default, all columns are orderable
Expand Down
5 changes: 5 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,8 @@ export type TransitionComponent = FunctionComponent<{
direction?: 'in' | 'out';
onTransitionEnd?: (direction: 'in' | 'out') => void;
}>;

export type Order<Field> = {
field: Field;
direction: 'ascending' | 'descending';
};

0 comments on commit 4177ebd

Please sign in to comment.