@boxcustodia/library 2.0.0-alpha.12 → 2.0.0-alpha.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs.js +1 -138
- package/dist/index.d.ts +1087 -720
- package/dist/index.es.js +7011 -56097
- package/dist/theme.css +1 -1
- package/package.json +34 -26
- package/src/__doc__/Examples.tsx +1 -1
- package/src/__doc__/Intro.mdx +3 -3
- package/src/__doc__/Tabs.mdx +112 -0
- package/src/__doc__/V2.mdx +1246 -0
- package/src/components/accordion/accordion.stories.tsx +143 -0
- package/src/components/accordion/accordion.tsx +135 -0
- package/src/components/accordion/index.ts +1 -0
- package/src/components/alert/alert.stories.tsx +24 -4
- package/src/components/alert/alert.tsx +17 -9
- package/src/components/alert-dialog/alert-dialog.stories.tsx +24 -0
- package/src/components/alert-dialog/alert-dialog.test.tsx +1 -1
- package/src/components/alert-dialog/alert-dialog.tsx +58 -10
- package/src/components/auto-complete/auto-complete.stories.tsx +616 -200
- package/src/components/auto-complete/auto-complete.tsx +420 -68
- package/src/components/auto-complete/index.ts +0 -1
- package/src/components/avatar/avatar.stories.tsx +162 -21
- package/src/components/avatar/avatar.tsx +79 -20
- package/src/components/button/button.stories.tsx +219 -294
- package/src/components/button/button.test.tsx +10 -17
- package/src/components/button/button.tsx +78 -19
- package/src/components/button/components/base-button.tsx +30 -53
- package/src/components/button/index.ts +0 -1
- package/src/components/calendar/calendar.stories.tsx +1 -1
- package/src/components/calendar/calendar.tsx +4 -4
- package/src/components/card/card.stories.tsx +141 -69
- package/src/components/card/card.tsx +155 -54
- package/src/components/center/center.stories.tsx +22 -39
- package/src/components/checkbox/checkbox.stories.tsx +25 -5
- package/src/components/checkbox/checkbox.tsx +76 -15
- package/src/components/checkbox-group/checkbox-group.stories.tsx +116 -28
- package/src/components/checkbox-group/checkbox-group.tsx +84 -3
- package/src/components/combobox/combobox.stories.tsx +33 -23
- package/src/components/combobox/combobox.tsx +99 -77
- package/src/components/date-picker/date-input.stories.tsx +14 -6
- package/src/components/date-picker/date-input.tsx +2 -2
- package/src/components/date-picker/date-picker.model.ts +13 -4
- package/src/components/date-picker/date-picker.stories.tsx +38 -12
- package/src/components/date-picker/date-picker.tsx +28 -14
- package/src/components/dialog/dialog.stories.tsx +18 -0
- package/src/components/dialog/dialog.test.tsx +1 -1
- package/src/components/dialog/dialog.tsx +51 -20
- package/src/components/divider/divider.stories.tsx +126 -51
- package/src/components/divider/divider.tsx +16 -16
- package/src/components/dropzone/dropzone.stories.tsx +71 -90
- package/src/components/dropzone/dropzone.tsx +383 -105
- package/src/components/dropzone/index.ts +0 -1
- package/src/components/empty/empty.stories.tsx +165 -0
- package/src/components/empty/empty.tsx +156 -0
- package/src/components/empty/index.ts +1 -0
- package/src/components/field/field.stories.tsx +227 -4
- package/src/components/field/field.tsx +77 -42
- package/src/components/form/form.stories.tsx +320 -197
- package/src/components/form/form.tsx +3 -23
- package/src/components/index.ts +2 -6
- package/src/components/input/input.stories.tsx +5 -5
- package/src/components/input/input.tsx +4 -4
- package/src/components/kbd/kbd.stories.tsx +1 -0
- package/src/components/label/label.stories.tsx +16 -0
- package/src/components/label/label.tsx +13 -2
- package/src/components/loader/loader.stories.tsx +7 -5
- package/src/components/loader/loader.tsx +8 -3
- package/src/components/menu/menu-primitives.tsx +207 -196
- package/src/components/menu/menu.stories.tsx +276 -146
- package/src/components/menu/menu.tsx +146 -54
- package/src/components/number-input/number-input.stories.tsx +27 -4
- package/src/components/number-input/number-input.test.tsx +2 -2
- package/src/components/number-input/number-input.tsx +31 -33
- package/src/components/otp/index.ts +1 -0
- package/src/components/otp/otp.stories.tsx +209 -0
- package/src/components/otp/otp.tsx +100 -0
- package/src/components/pagination/index.ts +1 -0
- package/src/components/pagination/pagination.model.ts +2 -0
- package/src/components/pagination/pagination.stories.tsx +154 -59
- package/src/components/pagination/pagination.test.tsx +122 -57
- package/src/components/pagination/pagination.tsx +575 -77
- package/src/components/password/password.stories.tsx +18 -3
- package/src/components/password/password.tsx +29 -9
- package/src/components/popover/popover.stories.tsx +26 -5
- package/src/components/popover/popover.tsx +15 -23
- package/src/components/progress/progress.stories.tsx +1 -0
- package/src/components/radio-group/index.ts +1 -0
- package/src/components/radio-group/radio-group.stories.tsx +251 -0
- package/src/components/radio-group/radio-group.tsx +212 -0
- package/src/components/scroll-area/scroll-area.stories.tsx +1 -0
- package/src/components/select/select.stories.tsx +118 -19
- package/src/components/select/select.tsx +67 -62
- package/src/components/skeleton/skeleton.stories.tsx +1 -0
- package/src/components/stack/stack.stories.tsx +179 -89
- package/src/components/stack/stack.tsx +2 -2
- package/src/components/stepper/index.ts +1 -1
- package/src/components/stepper/stepper.stories.tsx +767 -83
- package/src/components/stepper/stepper.test.tsx +18 -18
- package/src/components/stepper/stepper.tsx +554 -0
- package/src/components/switch/switch.stories.tsx +15 -1
- package/src/components/switch/switch.tsx +17 -4
- package/src/components/table/index.ts +0 -2
- package/src/components/table/table.stories.tsx +131 -18
- package/src/components/table/table.test.tsx +1 -1
- package/src/components/table/table.tsx +183 -77
- package/src/components/tabs/tabs.stories.tsx +373 -155
- package/src/components/tabs/tabs.test.tsx +12 -12
- package/src/components/tabs/tabs.tsx +72 -149
- package/src/components/tag/index.ts +0 -1
- package/src/components/tag/tag.stories.tsx +155 -120
- package/src/components/tag/tag.tsx +47 -95
- package/src/components/textarea/textarea.stories.tsx +8 -22
- package/src/components/textarea/textarea.tsx +17 -79
- package/src/components/timeline/timeline.stories.tsx +323 -42
- package/src/components/timeline/timeline.tsx +359 -132
- package/src/components/toast/toast.stories.tsx +1 -0
- package/src/components/tooltip/tooltip.tsx +11 -9
- package/src/components/tree/index.ts +0 -1
- package/src/components/tree/tree.stories.tsx +365 -408
- package/src/components/tree/tree.test.tsx +163 -0
- package/src/components/tree/tree.tsx +212 -36
- package/src/hooks/useAsync/__doc__/useAsync.stories.tsx +5 -5
- package/src/hooks/useClipboard/__doc__/useClipboard.stories.tsx +1 -3
- package/src/hooks/useDebounceCallback/__doc__/useDebouncedCallback.stories.tsx +6 -6
- package/src/hooks/useDocumentTitle/__doc__/useDocumentTitle.stories.tsx +1 -1
- package/src/hooks/useEventListener/__test__/useEventListener.test.tsx +1 -1
- package/src/hooks/useLocalStorage/__doc__/useLocalStorage.stories.tsx +1 -1
- package/src/hooks/usePagination/usePagination.tsx +36 -24
- package/src/styles/theme.css +1 -1
- package/src/utils/form.tsx +67 -37
- package/src/utils/index.ts +1 -1
- package/src/__doc__/Migration.mdx +0 -475
- package/src/components/auto-complete/auto-complete-primitives.tsx +0 -155
- package/src/components/background-image/background-image.stories.tsx +0 -21
- package/src/components/background-image/background-image.test.tsx +0 -29
- package/src/components/background-image/background-image.tsx +0 -23
- package/src/components/background-image/index.ts +0 -1
- package/src/components/button/button.variants.ts +0 -44
- package/src/components/button/components/loader-overlay.tsx +0 -21
- package/src/components/button/components/loading-icon.tsx +0 -47
- package/src/components/dropzone/upload-primitives.tsx +0 -310
- package/src/components/dropzone/use-dropzone.ts +0 -122
- package/src/components/empty-state/empty-state.stories.tsx +0 -56
- package/src/components/empty-state/empty-state.tsx +0 -39
- package/src/components/empty-state/index.ts +0 -1
- package/src/components/heading/heading.stories.tsx +0 -74
- package/src/components/heading/heading.tsx +0 -28
- package/src/components/heading/heading.variants.ts +0 -27
- package/src/components/heading/index.ts +0 -1
- package/src/components/kbd/kbd.variants.ts +0 -26
- package/src/components/menu/util/render-menu-item.tsx +0 -54
- package/src/components/multi-select/hooks/use-multi-select.ts +0 -66
- package/src/components/multi-select/index.ts +0 -1
- package/src/components/multi-select/multi-select.stories.tsx +0 -294
- package/src/components/multi-select/multi-select.tsx +0 -300
- package/src/components/multi-select/multi-select.variants.ts +0 -22
- package/src/components/pagination/components/pagination-option.tsx +0 -27
- package/src/components/show/index.ts +0 -1
- package/src/components/show/show.stories.tsx +0 -197
- package/src/components/show/show.test.tsx +0 -41
- package/src/components/show/show.tsx +0 -16
- package/src/components/stepper/Stepper.tsx +0 -190
- package/src/components/stepper/context/stepper-context.tsx +0 -11
- package/src/components/table/table-primitives.tsx +0 -122
- package/src/components/table/table.model.ts +0 -20
- package/src/components/table-pagination/index.ts +0 -2
- package/src/components/table-pagination/table-pagination.model.ts +0 -2
- package/src/components/table-pagination/table-pagination.stories.tsx +0 -23
- package/src/components/table-pagination/table-pagination.test.tsx +0 -32
- package/src/components/table-pagination/table-pagination.tsx +0 -108
- package/src/components/tabs/context/tabs-context.tsx +0 -14
- package/src/components/tag/tag.variants.ts +0 -31
- package/src/components/timeline/timeline-status.ts +0 -5
- package/src/components/tree/hooks/use-controllable-tree-state.ts +0 -80
- package/src/components/tree/tree-primitives.tsx +0 -126
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { OTPFieldPreview as OTPPrimitive } from "@base-ui/react/otp-field";
|
|
2
|
+
import type React from "react";
|
|
3
|
+
import { Fragment } from "react";
|
|
4
|
+
|
|
5
|
+
import { cn } from "../../lib";
|
|
6
|
+
import { Divider } from "../divider/divider";
|
|
7
|
+
|
|
8
|
+
export function OTPRoot({
|
|
9
|
+
className,
|
|
10
|
+
size = "default",
|
|
11
|
+
...props
|
|
12
|
+
}: OTPPrimitive.Root.Props & {
|
|
13
|
+
size?: "default" | "lg";
|
|
14
|
+
}): React.ReactElement {
|
|
15
|
+
return (
|
|
16
|
+
<OTPPrimitive.Root
|
|
17
|
+
className={cn(
|
|
18
|
+
"flex items-center gap-2 has-disabled:opacity-64 has-disabled:**:data-[slot=otp-input]:shadow-none has-disabled:**:data-[slot=otp-input]:before:shadow-none!",
|
|
19
|
+
className,
|
|
20
|
+
)}
|
|
21
|
+
data-size={size}
|
|
22
|
+
data-slot="otp"
|
|
23
|
+
{...props}
|
|
24
|
+
/>
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function OTPInput({
|
|
29
|
+
className,
|
|
30
|
+
...props
|
|
31
|
+
}: OTPPrimitive.Input.Props): React.ReactElement {
|
|
32
|
+
return (
|
|
33
|
+
<OTPPrimitive.Input
|
|
34
|
+
className={cn(
|
|
35
|
+
"in-[[data-slot=otp][data-size=lg]]:size-10 size-9 min-w-0 rounded-lg border border-input bg-background text-center in-[[data-slot=otp][data-size=lg]]:text-lg text-base text-foreground in-[[data-slot=otp][data-size=lg]]:leading-10 leading-9 outline-none transition-shadow focus-visible:z-10 focus-visible:border-ring aria-invalid:border-error focus-visible:aria-invalid:border-error focus-visible:aria-invalid:ring-error/20 in-[[data-slot=field][data-invalid]]:border-error in-[[data-slot=field][data-invalid]]:focus-visible:border-error in-[[data-slot=field][data-invalid]]:focus-visible:ring-error/20 sm:in-[[data-slot=otp][data-size=lg]]:size-9 sm:size-8 sm:in-[[data-slot=otp][data-size=lg]]:text-base sm:text-sm sm:in-[[data-slot=otp][data-size=lg]]:leading-9 sm:leading-8",
|
|
36
|
+
className,
|
|
37
|
+
)}
|
|
38
|
+
data-slot="otp-input"
|
|
39
|
+
spellCheck={false}
|
|
40
|
+
{...props}
|
|
41
|
+
/>
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function OTPSeparator({
|
|
46
|
+
className,
|
|
47
|
+
...props
|
|
48
|
+
}: React.ComponentProps<typeof Divider>): React.ReactElement {
|
|
49
|
+
return (
|
|
50
|
+
<OTPPrimitive.Separator
|
|
51
|
+
render={
|
|
52
|
+
<Divider
|
|
53
|
+
className={cn(
|
|
54
|
+
"rounded-full bg-input data-[orientation=horizontal]:h-0.5 data-[orientation=horizontal]:w-3",
|
|
55
|
+
className,
|
|
56
|
+
)}
|
|
57
|
+
orientation="horizontal"
|
|
58
|
+
{...props}
|
|
59
|
+
/>
|
|
60
|
+
}
|
|
61
|
+
/>
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
interface OTPProps extends Omit<OTPPrimitive.Root.Props, "children"> {
|
|
66
|
+
size?: "default" | "lg";
|
|
67
|
+
/** Insert a separator after this slot (1-indexed). */
|
|
68
|
+
split?: number;
|
|
69
|
+
inputProps?: OTPPrimitive.Input.Props;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function OTP({
|
|
73
|
+
split,
|
|
74
|
+
inputProps,
|
|
75
|
+
size,
|
|
76
|
+
length,
|
|
77
|
+
"aria-invalid": ariaInvalid,
|
|
78
|
+
...props
|
|
79
|
+
}: OTPProps): React.ReactElement {
|
|
80
|
+
const slotProps: OTPPrimitive.Input.Props = {
|
|
81
|
+
...(ariaInvalid ? { "aria-invalid": true } : {}),
|
|
82
|
+
...inputProps,
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
return (
|
|
86
|
+
<OTPRoot size={size} length={length} aria-invalid={ariaInvalid} {...props}>
|
|
87
|
+
{Array.from({ length }, (_, i) => (
|
|
88
|
+
<Fragment key={i}>
|
|
89
|
+
<OTPInput
|
|
90
|
+
aria-label={`Character ${i + 1} of ${length}`}
|
|
91
|
+
{...slotProps}
|
|
92
|
+
/>
|
|
93
|
+
{split === i + 1 && <OTPSeparator />}
|
|
94
|
+
</Fragment>
|
|
95
|
+
))}
|
|
96
|
+
</OTPRoot>
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export { OTPPrimitive };
|
|
@@ -1,20 +1,57 @@
|
|
|
1
|
-
import { Meta, StoryObj } from "@storybook/react-vite";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/react-vite";
|
|
2
|
+
import { useState } from "react";
|
|
3
|
+
import {
|
|
4
|
+
Pagination,
|
|
5
|
+
PaginationEllipsis,
|
|
6
|
+
PaginationFirst,
|
|
7
|
+
PaginationLast,
|
|
8
|
+
PaginationLink,
|
|
9
|
+
PaginationNext,
|
|
10
|
+
PaginationPageInfo,
|
|
11
|
+
PaginationPages,
|
|
12
|
+
PaginationPrevious,
|
|
13
|
+
PaginationRange,
|
|
14
|
+
PaginationRoot,
|
|
15
|
+
PaginationSizeSelect,
|
|
16
|
+
PaginationTotal,
|
|
17
|
+
} from "../../components";
|
|
6
18
|
|
|
19
|
+
/**
|
|
20
|
+
* Composite + primitives for pagination UI. The composite renders the default table layout
|
|
21
|
+
* (size selector + range + prev/next). Use primitives via `PaginationRoot` to compose any
|
|
22
|
+
* layout — numbered pages, page info, total only, etc. State is controllable through
|
|
23
|
+
* `currentPage` / `defaultCurrentPage` and `pageSize` / `defaultPageSize`. Pass `getPageHref`
|
|
24
|
+
* (and optionally `linkComponent`) to render navigation as anchors instead of buttons.
|
|
25
|
+
*/
|
|
7
26
|
const meta: Meta<typeof Pagination> = {
|
|
8
|
-
title: "
|
|
27
|
+
title: "Components/Pagination",
|
|
9
28
|
component: Pagination,
|
|
10
|
-
|
|
29
|
+
subcomponents: {
|
|
30
|
+
PaginationRoot,
|
|
31
|
+
PaginationFirst,
|
|
32
|
+
PaginationPrevious,
|
|
33
|
+
PaginationNext,
|
|
34
|
+
PaginationLast,
|
|
35
|
+
PaginationLink,
|
|
36
|
+
PaginationPages,
|
|
37
|
+
PaginationEllipsis,
|
|
38
|
+
PaginationTotal,
|
|
39
|
+
PaginationPageInfo,
|
|
40
|
+
PaginationRange,
|
|
41
|
+
PaginationSizeSelect,
|
|
42
|
+
},
|
|
43
|
+
argTypes: {
|
|
44
|
+
onCurrentPageChange: { action: "onCurrentPageChange" },
|
|
45
|
+
onPageSizeChange: { action: "onPageSizeChange" },
|
|
46
|
+
},
|
|
11
47
|
args: {
|
|
12
|
-
siblingCount: 1,
|
|
13
48
|
totalItems: 100,
|
|
14
|
-
|
|
15
|
-
initialCurrentPage: 1,
|
|
16
|
-
onChange: action("onChange"),
|
|
49
|
+
defaultPageSize: 10,
|
|
17
50
|
},
|
|
51
|
+
parameters: {
|
|
52
|
+
layout: "centered",
|
|
53
|
+
},
|
|
54
|
+
tags: ["beta"],
|
|
18
55
|
};
|
|
19
56
|
|
|
20
57
|
export default meta;
|
|
@@ -22,59 +59,117 @@ type Story = StoryObj<typeof meta>;
|
|
|
22
59
|
|
|
23
60
|
export const Default: Story = {};
|
|
24
61
|
|
|
25
|
-
export const
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
},
|
|
62
|
+
export const NoSizes: Story = {
|
|
63
|
+
args: { sizes: false },
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
export const CustomSizes: Story = {
|
|
67
|
+
args: { sizes: [5, 20, 100, 1000] },
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Controlled `currentPage` via React state. The component never owns the page —
|
|
72
|
+
* useful when the page lives in a parent, URL params, or Zustand store.
|
|
73
|
+
*/
|
|
74
|
+
export const Controlled: Story = {
|
|
31
75
|
render: (args) => {
|
|
76
|
+
const [page, setPage] = useState(1);
|
|
32
77
|
return (
|
|
33
|
-
<
|
|
34
|
-
<h2>No hay suficientes elementos para mostrar puntos</h2>
|
|
35
|
-
<Pagination
|
|
36
|
-
{...(args as ComponentProps<typeof Pagination>)}
|
|
37
|
-
pageSize={20}
|
|
38
|
-
/>
|
|
39
|
-
<h2>Puntos a la derecha</h2>
|
|
40
|
-
<Pagination
|
|
41
|
-
{...(args as ComponentProps<typeof Pagination>)}
|
|
42
|
-
initialCurrentPage={1}
|
|
43
|
-
/>
|
|
44
|
-
<h2>Puntos de ambos lados</h2>
|
|
45
|
-
<Pagination
|
|
46
|
-
{...(args as ComponentProps<typeof Pagination>)}
|
|
47
|
-
initialCurrentPage={5}
|
|
48
|
-
/>
|
|
49
|
-
<h2>Puntos a la izquierda</h2>
|
|
50
|
-
<Pagination
|
|
51
|
-
{...(args as ComponentProps<typeof Pagination>)}
|
|
52
|
-
initialCurrentPage={10}
|
|
53
|
-
/>
|
|
54
|
-
</div>
|
|
78
|
+
<Pagination {...args} currentPage={page} onCurrentPageChange={setPage} />
|
|
55
79
|
);
|
|
56
80
|
},
|
|
57
81
|
};
|
|
58
82
|
|
|
59
83
|
/**
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
84
|
+
* Compose freely with primitives. Example: numbered pagination with first/last buttons
|
|
85
|
+
* and a DOTS-aware page list (the legacy `Pagination` numbered API).
|
|
86
|
+
*/
|
|
87
|
+
export const WithPageNumbers: Story = {
|
|
88
|
+
args: { totalItems: 200, defaultPageSize: 10 },
|
|
89
|
+
render: (args) => (
|
|
90
|
+
<PaginationRoot {...args}>
|
|
91
|
+
<PaginationFirst />
|
|
92
|
+
<PaginationPrevious />
|
|
93
|
+
<PaginationPages />
|
|
94
|
+
<PaginationNext />
|
|
95
|
+
<PaginationLast />
|
|
96
|
+
</PaginationRoot>
|
|
97
|
+
),
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Compact layout showing total + page info between prev/next, e.g. "25 resultados < Página 1 de 3 >".
|
|
102
|
+
*/
|
|
103
|
+
export const WithTotalAndPageInfo: Story = {
|
|
104
|
+
args: { totalItems: 25, defaultPageSize: 10 },
|
|
105
|
+
render: (args) => (
|
|
106
|
+
<PaginationRoot {...args}>
|
|
107
|
+
<PaginationTotal />
|
|
108
|
+
<PaginationPrevious />
|
|
109
|
+
<PaginationPageInfo />
|
|
110
|
+
<PaginationNext />
|
|
111
|
+
</PaginationRoot>
|
|
112
|
+
),
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Custom display content via render-prop children. `Total`, `Range`, and `PageInfo` accept
|
|
117
|
+
* either ReactNode or a function returning ReactNode.
|
|
118
|
+
*/
|
|
119
|
+
export const CustomDisplays: Story = {
|
|
120
|
+
args: { totalItems: 47, defaultPageSize: 5 },
|
|
121
|
+
render: (args) => (
|
|
122
|
+
<PaginationRoot {...args}>
|
|
123
|
+
<PaginationTotal>{(n) => <strong>{n} elementos</strong>}</PaginationTotal>
|
|
124
|
+
<PaginationRange>
|
|
125
|
+
{({ start, end, total }) => (
|
|
126
|
+
<em>
|
|
127
|
+
Mostrando {start}–{end} de {total}
|
|
128
|
+
</em>
|
|
129
|
+
)}
|
|
130
|
+
</PaginationRange>
|
|
131
|
+
<PaginationPrevious />
|
|
132
|
+
<PaginationPageInfo>
|
|
133
|
+
{({ page, max }) => (
|
|
134
|
+
<span>
|
|
135
|
+
{page}/{max}
|
|
136
|
+
</span>
|
|
137
|
+
)}
|
|
138
|
+
</PaginationPageInfo>
|
|
139
|
+
<PaginationNext />
|
|
140
|
+
</PaginationRoot>
|
|
141
|
+
),
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* URL-driven mode. Passing `getPageHref` makes Previous/Next/Pages/First/Last render as
|
|
146
|
+
* anchors. Default `linkComponent` is `<a>`; pass `linkComponent={Link}` for React Router /
|
|
147
|
+
* Next.js wrapping a component that accepts `href`. The `onCurrentPageChange` callback
|
|
148
|
+
* still fires on click, so you can sync your router state imperatively.
|
|
149
|
+
*/
|
|
150
|
+
export const WithUrl: Story = {
|
|
151
|
+
args: { totalItems: 100, defaultPageSize: 10 },
|
|
152
|
+
render: (args) => (
|
|
153
|
+
<PaginationRoot
|
|
154
|
+
{...args}
|
|
155
|
+
getPageHref={({ page, pageSize }) => `?page=${page}&size=${pageSize}`}
|
|
156
|
+
>
|
|
157
|
+
<PaginationPrevious />
|
|
158
|
+
<PaginationPages />
|
|
159
|
+
<PaginationNext />
|
|
160
|
+
</PaginationRoot>
|
|
161
|
+
),
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Standalone `PaginationSizeSelect` outside the composite — wire it however you want.
|
|
166
|
+
*/
|
|
167
|
+
export const SizeSelectOnly: Story = {
|
|
168
|
+
args: { totalItems: 100 },
|
|
169
|
+
render: (args) => (
|
|
170
|
+
<PaginationRoot {...args}>
|
|
171
|
+
<PaginationSizeSelect />
|
|
172
|
+
<PaginationRange />
|
|
173
|
+
</PaginationRoot>
|
|
174
|
+
),
|
|
80
175
|
};
|
|
@@ -1,76 +1,141 @@
|
|
|
1
|
-
import { render, screen
|
|
2
|
-
import { describe, expect, it } from "vitest";
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
1
|
+
import { render, screen } from "@testing-library/react";
|
|
2
|
+
import { describe, expect, it, vi } from "vitest";
|
|
3
|
+
import {
|
|
4
|
+
Pagination,
|
|
5
|
+
PaginationNext,
|
|
6
|
+
PaginationPageInfo,
|
|
7
|
+
PaginationPages,
|
|
8
|
+
PaginationPrevious,
|
|
9
|
+
PaginationRoot,
|
|
10
|
+
PaginationTotal,
|
|
11
|
+
} from "../../components";
|
|
12
|
+
import { click } from "../../utils/tests";
|
|
13
|
+
|
|
14
|
+
describe("Pagination composite", () => {
|
|
15
|
+
it("renders default range text", () => {
|
|
16
|
+
render(<Pagination defaultPageSize={10} totalItems={30} />);
|
|
17
|
+
expect(screen.getByText(/1 - 10 de 30/)).toBeInTheDocument();
|
|
10
18
|
});
|
|
11
19
|
|
|
12
|
-
it("
|
|
13
|
-
render(<Pagination
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
click(page);
|
|
17
|
-
|
|
18
|
-
waitFor(() => {
|
|
19
|
-
expect(page).toHaveAttribute("active", "true");
|
|
20
|
-
});
|
|
20
|
+
it("navigates to next page", () => {
|
|
21
|
+
render(<Pagination defaultPageSize={10} totalItems={30} />);
|
|
22
|
+
click(screen.getByRole("button", { name: /próxima página/i }));
|
|
23
|
+
expect(screen.getByText(/11 - 20 de 30/)).toBeInTheDocument();
|
|
21
24
|
});
|
|
22
25
|
|
|
23
|
-
it("
|
|
24
|
-
render(
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
});
|
|
26
|
+
it("navigates to previous page", () => {
|
|
27
|
+
render(
|
|
28
|
+
<Pagination
|
|
29
|
+
defaultCurrentPage={2}
|
|
30
|
+
defaultPageSize={10}
|
|
31
|
+
totalItems={30}
|
|
32
|
+
/>,
|
|
33
|
+
);
|
|
34
|
+
click(screen.getByRole("button", { name: /anterior/i }));
|
|
35
|
+
expect(screen.getByText(/1 - 10 de 30/)).toBeInTheDocument();
|
|
32
36
|
});
|
|
33
37
|
|
|
34
|
-
it("
|
|
35
|
-
render(<Pagination
|
|
36
|
-
|
|
37
|
-
const page = screen.getByText(/Ir a página anterior/i);
|
|
38
|
-
click(page);
|
|
39
|
-
|
|
40
|
-
waitFor(() => {
|
|
41
|
-
expect(page).toHaveAttribute("active", "true");
|
|
42
|
-
});
|
|
38
|
+
it("disables prev on first page", () => {
|
|
39
|
+
render(<Pagination defaultPageSize={10} totalItems={30} />);
|
|
40
|
+
expect(screen.getByRole("button", { name: /anterior/i })).toBeDisabled();
|
|
43
41
|
});
|
|
44
42
|
|
|
45
|
-
it("
|
|
46
|
-
render(
|
|
43
|
+
it("disables next on last page", () => {
|
|
44
|
+
render(
|
|
45
|
+
<Pagination
|
|
46
|
+
defaultCurrentPage={3}
|
|
47
|
+
defaultPageSize={10}
|
|
48
|
+
totalItems={30}
|
|
49
|
+
/>,
|
|
50
|
+
);
|
|
51
|
+
expect(screen.getByRole("button", { name: /próxima/i })).toBeDisabled();
|
|
52
|
+
});
|
|
47
53
|
|
|
48
|
-
|
|
49
|
-
|
|
54
|
+
it("hides size selector when sizes is false", () => {
|
|
55
|
+
render(<Pagination defaultPageSize={10} totalItems={30} sizes={false} />);
|
|
56
|
+
expect(screen.queryByRole("combobox")).not.toBeInTheDocument();
|
|
57
|
+
});
|
|
50
58
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
59
|
+
it("calls onCurrentPageChange controlled", () => {
|
|
60
|
+
const onChange = vi.fn();
|
|
61
|
+
render(
|
|
62
|
+
<Pagination
|
|
63
|
+
currentPage={1}
|
|
64
|
+
defaultPageSize={10}
|
|
65
|
+
totalItems={30}
|
|
66
|
+
onCurrentPageChange={onChange}
|
|
67
|
+
/>,
|
|
68
|
+
);
|
|
69
|
+
click(screen.getByRole("button", { name: /próxima/i }));
|
|
70
|
+
expect(onChange).toHaveBeenCalledWith(2);
|
|
54
71
|
});
|
|
72
|
+
});
|
|
55
73
|
|
|
56
|
-
|
|
57
|
-
|
|
74
|
+
describe("Pagination primitives", () => {
|
|
75
|
+
it("renders page numbers via PaginationPages", () => {
|
|
76
|
+
render(
|
|
77
|
+
<PaginationRoot defaultPageSize={10} totalItems={30}>
|
|
78
|
+
<PaginationPages />
|
|
79
|
+
</PaginationRoot>,
|
|
80
|
+
);
|
|
81
|
+
expect(
|
|
82
|
+
screen.getByRole("button", { name: /Ir a la página 1/i }),
|
|
83
|
+
).toBeInTheDocument();
|
|
84
|
+
expect(
|
|
85
|
+
screen.getByRole("button", { name: /Ir a la página 2/i }),
|
|
86
|
+
).toBeInTheDocument();
|
|
87
|
+
expect(
|
|
88
|
+
screen.getByRole("button", { name: /Ir a la página 3/i }),
|
|
89
|
+
).toBeInTheDocument();
|
|
90
|
+
});
|
|
58
91
|
|
|
59
|
-
|
|
60
|
-
|
|
92
|
+
it("renders DOTS when range overflows", () => {
|
|
93
|
+
render(
|
|
94
|
+
<PaginationRoot defaultPageSize={10} totalItems={200}>
|
|
95
|
+
<PaginationPages />
|
|
96
|
+
</PaginationRoot>,
|
|
97
|
+
);
|
|
98
|
+
expect(screen.getAllByText(/Más páginas/).length).toBeGreaterThan(0);
|
|
99
|
+
});
|
|
61
100
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
101
|
+
it("renders total + pageInfo + prev/next layout", () => {
|
|
102
|
+
render(
|
|
103
|
+
<PaginationRoot defaultPageSize={10} totalItems={25}>
|
|
104
|
+
<PaginationTotal />
|
|
105
|
+
<PaginationPrevious />
|
|
106
|
+
<PaginationPageInfo />
|
|
107
|
+
<PaginationNext />
|
|
108
|
+
</PaginationRoot>,
|
|
109
|
+
);
|
|
110
|
+
expect(screen.getByText(/25 resultados/)).toBeInTheDocument();
|
|
111
|
+
expect(screen.getByText(/Página 1 de 3/)).toBeInTheDocument();
|
|
65
112
|
});
|
|
66
113
|
|
|
67
|
-
it("
|
|
68
|
-
|
|
69
|
-
|
|
114
|
+
it("renders anchors when getPageHref is provided", () => {
|
|
115
|
+
const { container } = render(
|
|
116
|
+
<PaginationRoot
|
|
117
|
+
defaultPageSize={10}
|
|
118
|
+
defaultCurrentPage={2}
|
|
119
|
+
totalItems={30}
|
|
120
|
+
getPageHref={({ page }) => `/users?page=${page}`}
|
|
121
|
+
>
|
|
122
|
+
<PaginationPrevious />
|
|
123
|
+
<PaginationNext />
|
|
124
|
+
</PaginationRoot>,
|
|
125
|
+
);
|
|
126
|
+
const anchors = container.querySelectorAll("a[href]");
|
|
127
|
+
expect(anchors.length).toBe(2);
|
|
128
|
+
expect(anchors[0]).toHaveAttribute("href", "/users?page=1");
|
|
129
|
+
expect(anchors[1]).toHaveAttribute("href", "/users?page=3");
|
|
130
|
+
});
|
|
70
131
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
132
|
+
it("throws when used outside Root", () => {
|
|
133
|
+
const consoleError = vi
|
|
134
|
+
.spyOn(console, "error")
|
|
135
|
+
.mockImplementation(() => {});
|
|
136
|
+
expect(() => render(<PaginationNext />)).toThrow(
|
|
137
|
+
/must be used inside <PaginationRoot>/,
|
|
138
|
+
);
|
|
139
|
+
consoleError.mockRestore();
|
|
75
140
|
});
|
|
76
141
|
});
|