Skip to content

Commit eb52108

Browse files
authored
Merge pull request #63 from DouglasNeuroInformatics/dev
use ZodTypeLike from libjs and improve DataTable
2 parents d87c850 + d148e76 commit eb52108

File tree

10 files changed

+54
-89
lines changed

10 files changed

+54
-89
lines changed

.storybook/index.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { Meta } from '@storybook/blocks';
22

33
<Meta title="Welcome" />
44

5-
# Welcome to the Douglasneuroinformatics UI Library
5+
# Welcome to the DouglasNeuroinformatics UI Library
66

77
This library includes generic UI components built using shadcn/ui with React and TailwindCSS.
88

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@
6868
"zod": "^3.25.x"
6969
},
7070
"dependencies": {
71-
"@douglasneuroinformatics/libjs": "^2.8.0",
71+
"@douglasneuroinformatics/libjs": "^3.0.1",
7272
"@douglasneuroinformatics/libui-form-types": "^0.11.0",
7373
"@radix-ui/react-accordion": "^1.2.3",
7474
"@radix-ui/react-alert-dialog": "^1.1.6",

pnpm-lock.yaml

Lines changed: 6 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/components/DataTable/DataTable.stories.tsx

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { range, toBasicISOString } from '@douglasneuroinformatics/libjs';
1+
import { range, toBasicISOString, unwrap } from '@douglasneuroinformatics/libjs';
22
import { faker } from '@faker-js/faker';
33
import type { Meta, StoryObj } from '@storybook/react';
44

@@ -31,27 +31,27 @@ const columns: DataTableColumn<User>[] = [
3131
}
3232
];
3333

34-
const data: User[] = range(60)
35-
.unwrap()
36-
.map(() => ({
37-
birthday: faker.date.birthdate(),
38-
email: faker.internet.email(),
39-
firstName: faker.person.firstName(),
40-
lastName: faker.person.lastName()
41-
}));
34+
const data: User[] = unwrap(range(60)).map(() => ({
35+
birthday: faker.date.birthdate(),
36+
email: faker.internet.email(),
37+
firstName: faker.person.firstName(),
38+
lastName: faker.person.lastName()
39+
}));
4240

4341
export default { component: DataTable } as Meta<typeof DataTable<User>>;
4442

4543
export const Default: Story = {
4644
args: {
4745
columns,
4846
data,
49-
headerAction: {
50-
label: 'Do Something',
51-
onClick: () => {
52-
alert('Something!');
47+
headerActions: [
48+
{
49+
label: 'Do Something',
50+
onClick: () => {
51+
alert('Something!');
52+
}
5353
}
54-
},
54+
],
5555
rowActions: [
5656
{
5757
destructive: true,

src/components/DataTable/DataTable.tsx

Lines changed: 24 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Fragment, useEffect, useMemo, useState } from 'react';
1+
import { useEffect, useMemo, useState } from 'react';
22

33
import {
44
flexRender,
@@ -45,10 +45,10 @@ type DataTableColumn<TData extends { [key: string]: unknown }> =
4545
type DataTableProps<TData extends { [key: string]: unknown }> = {
4646
columns: DataTableColumn<TData>[];
4747
data: TData[];
48-
headerAction?: {
48+
headerActions?: {
4949
label: string;
5050
onClick: () => void;
51-
};
51+
}[];
5252
rowActions?: RowAction<TData>[];
5353
search?: {
5454
key: Extract<keyof TData, string>;
@@ -65,7 +65,7 @@ function isStaticColumn<TData extends { [key: string]: unknown }>(
6565
export const DataTable = <TData extends { [key: string]: unknown }>({
6666
columns,
6767
data,
68-
headerAction,
68+
headerActions,
6969
rowActions,
7070
search
7171
}: DataTableProps<TData>) => {
@@ -173,7 +173,7 @@ export const DataTable = <TData extends { [key: string]: unknown }>({
173173
const pageIndexOptions = range(start, end);
174174

175175
return (
176-
<Fragment>
176+
<div className="flex flex-col">
177177
<DestructiveActionDialog
178178
destructiveActionPending={destructiveActionPending}
179179
setDestructiveActionPending={setDestructiveActionPending}
@@ -186,10 +186,14 @@ export const DataTable = <TData extends { [key: string]: unknown }>({
186186
value={searchValue}
187187
onValueChange={setSearchValue}
188188
/>
189-
{headerAction && (
190-
<Button type="button" variant="outline" onClick={headerAction.onClick}>
191-
{headerAction.label}
192-
</Button>
189+
{headerActions && (
190+
<div className="flex gap-2">
191+
{headerActions.map(({ label, onClick }, i) => (
192+
<Button key={i} type="button" variant="outline" onClick={onClick}>
193+
{label}
194+
</Button>
195+
))}
196+
</div>
193197
)}
194198
</div>
195199
)}
@@ -230,30 +234,29 @@ export const DataTable = <TData extends { [key: string]: unknown }>({
230234
</Table.Body>
231235
</Table>
232236
</div>
233-
<div className="mx-auto flex w-min pt-6 pb-4">
237+
<div className="flex w-min gap-0.5 py-4 [&>button]:h-9">
234238
<Button
235-
className="flex gap-1"
236239
disabled={!table.getCanPreviousPage()}
240+
size="icon"
237241
type="button"
238242
variant="ghost"
239243
onClick={() => table.firstPage()}
240244
>
241-
<ChevronsLeftIcon className="-ml-1 h-4 w-4" />
242-
<span>{t('pagination.first')}</span>
245+
<ChevronsLeftIcon className="h-4 w-4" />
243246
</Button>
244247
<Button
245-
className="mr-1 flex gap-1"
246248
disabled={!table.getCanPreviousPage()}
249+
size="icon"
247250
type="button"
248251
variant="ghost"
249252
onClick={() => table.previousPage()}
250253
>
251-
<ChevronLeftIcon className="-ml-1 h-4 w-4" />
252-
<span>{t('pagination.previous')}</span>
254+
<ChevronLeftIcon className="h-4 w-4" />
253255
</Button>
254256
{pageIndexOptions.map((index) => (
255257
<Button
256258
key={index}
259+
size="icon"
257260
type="button"
258261
variant={index === pagination.pageIndex ? 'outline' : 'ghost'}
259262
onClick={() => table.setPageIndex(index)}
@@ -262,27 +265,25 @@ export const DataTable = <TData extends { [key: string]: unknown }>({
262265
</Button>
263266
))}
264267
<Button
265-
className="ml-1 flex gap-1"
266268
disabled={!table.getCanNextPage()}
269+
size="icon"
267270
type="button"
268271
variant="ghost"
269272
onClick={() => table.nextPage()}
270273
>
271-
<span>{t('pagination.next')}</span>
272-
<ChevronRightIcon className="-mr-1 h-4 w-4" />
274+
<ChevronRightIcon className="h-4 w-4" />
273275
</Button>
274276
<Button
275-
className="flex gap-1"
276277
disabled={!table.getCanNextPage()}
278+
size="icon"
277279
type="button"
278280
variant="ghost"
279281
onClick={() => table.lastPage()}
280282
>
281-
<span>{t('pagination.last')}</span>
282-
<ChevronsRightIcon className="-mr-1 h-4 w-4" />
283+
<ChevronsRightIcon className="h-4 w-4" />
283284
</Button>
284285
</div>
285-
</Fragment>
286+
</div>
286287
);
287288
};
288289

src/components/DataTable/DestructiveActionDialog.tsx

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
/* eslint-disable @typescript-eslint/no-misused-promises */
2-
/* eslint-disable jsx-a11y/no-autofocus */
32

43
import type React from 'react';
54

@@ -26,7 +25,7 @@ export const DestructiveActionDialog: React.FC<{
2625
}
2726
}}
2827
>
29-
<Dialog.Content>
28+
<Dialog.Content onOpenAutoFocus={(event) => event.preventDefault()}>
3029
<Dialog.Header>
3130
<Dialog.Title>
3231
{t({
@@ -44,9 +43,8 @@ export const DestructiveActionDialog: React.FC<{
4443
<Dialog.Footer>
4544
<Button
4645
className="min-w-16"
47-
size="sm"
4846
type="button"
49-
variant="outline"
47+
variant="danger"
5048
onClick={async () => {
5149
await destructiveActionPending?.();
5250
setDestructiveActionPending(null);
@@ -55,9 +53,7 @@ export const DestructiveActionDialog: React.FC<{
5553
{t('libui.yes')}
5654
</Button>
5755
<Button
58-
autoFocus={true}
5956
className="min-w-16"
60-
size="sm"
6157
type="button"
6258
variant="primary"
6359
onClick={() => setDestructiveActionPending(null)}

src/components/Form/Form.stories.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import { useEffect, useState } from 'react';
44

55
import { sleep } from '@douglasneuroinformatics/libjs';
6+
import type { ZodTypeLike } from '@douglasneuroinformatics/libjs';
67
import type { FormFields } from '@douglasneuroinformatics/libui-form-types';
78
import type FormTypes from '@douglasneuroinformatics/libui-form-types';
89
import type { Meta, StoryObj } from '@storybook/react';
@@ -12,8 +13,6 @@ import { z } from 'zod/v4';
1213
import { Heading } from '../Heading';
1314
import { Form } from './Form';
1415

15-
import type { ZodTypeLike } from './types';
16-
1716
const DISABLED = false;
1817

1918
const $ExampleFormData = z.object({

src/components/Form/Form.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { useEffect, useState } from 'react';
22

3+
import type { ZodErrorLike, ZodTypeLike } from '@douglasneuroinformatics/libjs';
34
import type {
45
FormContent,
56
FormDataType,
@@ -21,9 +22,9 @@ import { ErrorMessage } from './ErrorMessage';
2122
import { FieldsComponent } from './FieldsComponent';
2223
import { getInitialValues } from './utils';
2324

24-
import type { FormErrors, ZodErrorLike, ZodTypeLike } from './types';
25+
import type { FormErrors } from './types';
2526

26-
type FormProps<TSchema extends ZodTypeLike<FormDataType>, TData extends TSchema['_input'] = TSchema['_input']> = {
27+
type FormProps<TSchema extends ZodTypeLike<FormDataType>, TData extends TSchema['_output'] = TSchema['_output']> = {
2728
[key: `data-${string}`]: unknown;
2829
additionalButtons?: {
2930
left?: React.ReactNode;
@@ -52,7 +53,7 @@ type FormProps<TSchema extends ZodTypeLike<FormDataType>, TData extends TSchema[
5253
validationSchema: ZodTypeLike<TData>;
5354
};
5455

55-
const Form = <TSchema extends ZodTypeLike<FormDataType>, TData extends TSchema['_input'] = TSchema['_input']>({
56+
const Form = <TSchema extends ZodTypeLike<FormDataType>, TData extends TSchema['_output'] = TSchema['_output']>({
5657
additionalButtons,
5758
className,
5859
content,

src/components/Form/types.ts

Lines changed: 0 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -25,36 +25,3 @@ export type BaseFieldComponentProps<TValue extends FormFieldValue = FormFieldVal
2525
export type FormErrors<TData extends FormDataType = FormDataType> = {
2626
[K in keyof TData]?: FieldError<TData[K]>;
2727
};
28-
29-
export type ZodIssueLike = {
30-
[key: string]: any;
31-
readonly code: string;
32-
readonly message: string;
33-
readonly path: PropertyKey[];
34-
};
35-
36-
export type ZodErrorLike = {
37-
cause?: unknown;
38-
issues: ZodIssueLike[];
39-
name: string;
40-
};
41-
42-
export type ZodSafeParseResultLike<T> = ZodSafeParseErrorLike | ZodSafeParseSuccessLike<T>;
43-
44-
export type ZodSafeParseSuccessLike<TOutput> = {
45-
data: TOutput;
46-
error?: never;
47-
success: true;
48-
};
49-
50-
export type ZodSafeParseErrorLike = {
51-
data?: never;
52-
error: ZodErrorLike;
53-
success: false;
54-
};
55-
56-
export type ZodTypeLike<TOutput, TInput = TOutput> = {
57-
readonly _input: TInput;
58-
readonly _output: TOutput;
59-
safeParseAsync: (data: unknown) => Promise<ZodSafeParseResultLike<TOutput>>;
60-
};

src/components/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ export * from './Collapsible';
1414
export * from './Command';
1515
export * from './ContextMenu';
1616
export * from './CopyButton';
17+
export * from './DataTable';
1718
export * from './DatePicker';
1819
export * from './Dialog';
1920
export * from './Drawer';

0 commit comments

Comments
 (0)