@purpurds/pagination 5.19.1

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.
Files changed (62) hide show
  1. package/dist/LICENSE.txt +73 -0
  2. package/dist/navigation.d.ts +2 -0
  3. package/dist/navigation.d.ts.map +1 -0
  4. package/dist/pagination-page-selector.d.ts +17 -0
  5. package/dist/pagination-page-selector.d.ts.map +1 -0
  6. package/dist/pagination-page-size-selector.d.ts +13 -0
  7. package/dist/pagination-page-size-selector.d.ts.map +1 -0
  8. package/dist/pagination-page-trigger.d.ts +14 -0
  9. package/dist/pagination-page-trigger.d.ts.map +1 -0
  10. package/dist/pagination-pages.d.ts +13 -0
  11. package/dist/pagination-pages.d.ts.map +1 -0
  12. package/dist/pagination-step-trigger.d.ts +14 -0
  13. package/dist/pagination-step-trigger.d.ts.map +1 -0
  14. package/dist/pagination-truncation-separator.d.ts +11 -0
  15. package/dist/pagination-truncation-separator.d.ts.map +1 -0
  16. package/dist/pagination.cjs.js +134 -0
  17. package/dist/pagination.cjs.js.map +1 -0
  18. package/dist/pagination.d.ts +24 -0
  19. package/dist/pagination.d.ts.map +1 -0
  20. package/dist/pagination.es.js +4041 -0
  21. package/dist/pagination.es.js.map +1 -0
  22. package/dist/styles.css +1 -0
  23. package/dist/types.d.ts +35 -0
  24. package/dist/types.d.ts.map +1 -0
  25. package/dist/use-page-options.hook.d.ts +8 -0
  26. package/dist/use-page-options.hook.d.ts.map +1 -0
  27. package/dist/use-page-size-options.hook.d.ts +8 -0
  28. package/dist/use-page-size-options.hook.d.ts.map +1 -0
  29. package/dist/use-pagination-page-selector-events.hook.d.ts +10 -0
  30. package/dist/use-pagination-page-selector-events.hook.d.ts.map +1 -0
  31. package/dist/use-pagination-pages.hook.d.ts +7 -0
  32. package/dist/use-pagination-pages.hook.d.ts.map +1 -0
  33. package/package.json +69 -0
  34. package/src/global.d.ts +4 -0
  35. package/src/navigation.ts +3 -0
  36. package/src/pagination-page-selector.module.scss +26 -0
  37. package/src/pagination-page-selector.test.tsx +78 -0
  38. package/src/pagination-page-selector.tsx +105 -0
  39. package/src/pagination-page-size-selector.module.scss +34 -0
  40. package/src/pagination-page-size-selector.test.tsx +73 -0
  41. package/src/pagination-page-size-selector.tsx +78 -0
  42. package/src/pagination-page-trigger.module.scss +41 -0
  43. package/src/pagination-page-trigger.test.tsx +172 -0
  44. package/src/pagination-page-trigger.tsx +93 -0
  45. package/src/pagination-pages.module.scss +8 -0
  46. package/src/pagination-pages.test.tsx +95 -0
  47. package/src/pagination-pages.tsx +72 -0
  48. package/src/pagination-step-trigger.module.scss +27 -0
  49. package/src/pagination-step-trigger.test.tsx +106 -0
  50. package/src/pagination-step-trigger.tsx +92 -0
  51. package/src/pagination-truncation-separator.module.scss +8 -0
  52. package/src/pagination-truncation-separator.test.tsx +25 -0
  53. package/src/pagination-truncation-separator.tsx +29 -0
  54. package/src/pagination.module.scss +105 -0
  55. package/src/pagination.stories.tsx +197 -0
  56. package/src/pagination.test.tsx +76 -0
  57. package/src/pagination.tsx +177 -0
  58. package/src/types.ts +43 -0
  59. package/src/use-page-options.hook.ts +21 -0
  60. package/src/use-page-size-options.hook.ts +21 -0
  61. package/src/use-pagination-page-selector-events.hook.ts +59 -0
  62. package/src/use-pagination-pages.hook.ts +41 -0
@@ -0,0 +1,172 @@
1
+ import React, { forwardRef } from "react";
2
+ import * as matchers from "@testing-library/jest-dom/matchers";
3
+ import { cleanup, render, screen } from "@testing-library/react";
4
+ import userEvent from "@testing-library/user-event";
5
+ import { afterEach, describe, expect, it, vi } from "vitest";
6
+
7
+ import { PaginationPageTrigger } from "./pagination-page-trigger";
8
+
9
+ expect.extend(matchers);
10
+
11
+ afterEach(() => {
12
+ cleanup();
13
+ vi.clearAllMocks();
14
+ });
15
+
16
+ describe("PaginationPageTrigger", () => {
17
+ const onPageChangeMock = vi.fn();
18
+ const hrefGetterMock = vi.fn((page: number) => `#page-${page}`);
19
+
20
+ it("renders a button when asLink is not set", () => {
21
+ render(
22
+ <PaginationPageTrigger
23
+ anchorTagElement="a"
24
+ onPageChange={onPageChangeMock}
25
+ pageNumber={1}
26
+ selected={false}
27
+ />
28
+ );
29
+
30
+ const button = screen.getByTestId(Selectors.BUTTON);
31
+ expect(button).toBeInTheDocument();
32
+ expect(button).toHaveTextContent("1");
33
+ });
34
+
35
+ it("calls onPageChange when button is clicked", async () => {
36
+ render(
37
+ <PaginationPageTrigger
38
+ anchorTagElement="a"
39
+ onPageChange={onPageChangeMock}
40
+ pageNumber={1}
41
+ selected={false}
42
+ />
43
+ );
44
+
45
+ const button = screen.getByTestId(Selectors.BUTTON);
46
+ await userEvent.click(button);
47
+ expect(onPageChangeMock).toHaveBeenCalledWith(1);
48
+ });
49
+
50
+ it("renders a link when asLink is true and not disabled", () => {
51
+ render(
52
+ <PaginationPageTrigger
53
+ anchorTagElement="a"
54
+ onPageChange={onPageChangeMock}
55
+ pageNumber={1}
56
+ selected={false}
57
+ asLink
58
+ hrefGetter={hrefGetterMock}
59
+ disabled={false}
60
+ />
61
+ );
62
+
63
+ const link = screen.getByTestId(Selectors.LINK);
64
+ expect(link).toBeInTheDocument();
65
+ expect(link).toHaveTextContent("1");
66
+ expect(link).toHaveAttribute("href", "#page-1");
67
+ });
68
+
69
+ it("renders a custom link when asLink is true and not disabled", () => {
70
+ const CustomTestLink = forwardRef(
71
+ ({ children, href }: { children?: React.ReactNode; href?: string }) => (
72
+ <a data-testid="custom-test-link" href={href}>
73
+ {children}
74
+ </a>
75
+ )
76
+ );
77
+ render(
78
+ <PaginationPageTrigger
79
+ anchorTagElement={CustomTestLink}
80
+ onPageChange={onPageChangeMock}
81
+ pageNumber={1}
82
+ selected={false}
83
+ asLink
84
+ hrefGetter={hrefGetterMock}
85
+ disabled={false}
86
+ />
87
+ );
88
+
89
+ const link = screen.getByTestId("custom-test-link");
90
+ expect(link).toBeInTheDocument();
91
+ expect(link).toHaveTextContent("1");
92
+ expect(link).toHaveAttribute("href", "#page-1");
93
+ });
94
+
95
+ it("calls onPageChange when link is clicked", async () => {
96
+ render(
97
+ <PaginationPageTrigger
98
+ anchorTagElement="a"
99
+ onPageChange={onPageChangeMock}
100
+ pageNumber={2}
101
+ selected={false}
102
+ asLink
103
+ hrefGetter={hrefGetterMock}
104
+ disabled={false}
105
+ />
106
+ );
107
+
108
+ const link = screen.getByTestId(Selectors.LINK);
109
+ await userEvent.click(link);
110
+ expect(onPageChangeMock).toHaveBeenCalledWith(2);
111
+ });
112
+
113
+ it("renders a disabled link when asLink is true and disabled", () => {
114
+ render(
115
+ <PaginationPageTrigger
116
+ anchorTagElement="a"
117
+ onPageChange={onPageChangeMock}
118
+ pageNumber={1}
119
+ selected={false}
120
+ asLink
121
+ hrefGetter={hrefGetterMock}
122
+ disabled={true}
123
+ />
124
+ );
125
+
126
+ const disabledLink = screen.getByTestId(Selectors.DISABLED_LINK);
127
+ expect(disabledLink).toBeInTheDocument();
128
+ expect(disabledLink).toHaveTextContent("1");
129
+ });
130
+
131
+ it("renders a disabled button when disabled is true", () => {
132
+ render(
133
+ <PaginationPageTrigger
134
+ anchorTagElement="a"
135
+ onPageChange={onPageChangeMock}
136
+ pageNumber={1}
137
+ selected={false}
138
+ disabled={true}
139
+ />
140
+ );
141
+
142
+ const button = screen.getByTestId(Selectors.BUTTON);
143
+ expect(button).toBeInTheDocument();
144
+ expect(button).toBeDisabled();
145
+ });
146
+
147
+ it.each([
148
+ { asLink: false as never, selector: Selectors.BUTTON },
149
+ { asLink: true as const, selector: Selectors.LINK },
150
+ ])("applies selected class when selected is true (asLink: $asLink)", ({ asLink, selector }) => {
151
+ render(
152
+ <PaginationPageTrigger
153
+ anchorTagElement="a"
154
+ onPageChange={onPageChangeMock}
155
+ pageNumber={1}
156
+ selected={true}
157
+ asLink={asLink}
158
+ hrefGetter={hrefGetterMock}
159
+ />
160
+ );
161
+
162
+ const element = screen.getByTestId(selector);
163
+ expect(element).toHaveClass("purpur-pagination-page-trigger__trigger-item--selected");
164
+ });
165
+ });
166
+
167
+ const Selectors = {
168
+ ROOT: "purpur-pagination-page-trigger",
169
+ DISABLED_LINK: "purpur-pagination-page-trigger-disabled-link",
170
+ LINK: "purpur-pagination-page-trigger-link",
171
+ BUTTON: "purpur-pagination-page-trigger-button",
172
+ };
@@ -0,0 +1,93 @@
1
+ import React, { ForwardedRef, forwardRef } from "react";
2
+ import c from "classnames/bind";
3
+
4
+ import styles from "./pagination-page-trigger.module.scss";
5
+ import { InternalLinkProps, InternalNoLinkProps, Link } from "./types";
6
+ const cx = c.bind(styles);
7
+
8
+ export type PaginationPageTriggerProps = {
9
+ ["data-testid"]?: string;
10
+ anchorTagElement: Link;
11
+ className?: string;
12
+ disabled?: boolean;
13
+ onPageChange: (page: number) => void;
14
+ pageNumber: number;
15
+ selected: boolean;
16
+ } & (InternalLinkProps | InternalNoLinkProps);
17
+
18
+ const rootClassName = "purpur-pagination-page-trigger";
19
+
20
+ export const PaginationPageTrigger = forwardRef(
21
+ (
22
+ {
23
+ ["data-testid"]: dataTestId = "purpur-pagination-page-trigger",
24
+ anchorTagElement: AnchorTagElement,
25
+ asLink,
26
+ className,
27
+ disabled,
28
+ hrefGetter,
29
+ onPageChange,
30
+ pageNumber,
31
+ selected,
32
+ ...props
33
+ }: PaginationPageTriggerProps,
34
+ ref: ForwardedRef<HTMLLIElement>
35
+ ) => {
36
+ const classes = cx([className, rootClassName]);
37
+
38
+ return (
39
+ <li className={classes} data-testid={dataTestId} ref={ref} {...props}>
40
+ {asLink ? (
41
+ disabled ? (
42
+ <span
43
+ className={cx([
44
+ `${rootClassName}__trigger-item`,
45
+ `${rootClassName}__trigger-item--page-trigger`,
46
+ `${rootClassName}__trigger-item--sm`,
47
+ `${rootClassName}__trigger-item--icon-only`,
48
+ `${rootClassName}__trigger-item--disabled`,
49
+ ])}
50
+ data-testid={`${dataTestId}-disabled-link`}
51
+ >
52
+ {pageNumber}
53
+ </span>
54
+ ) : (
55
+ <AnchorTagElement
56
+ className={cx([
57
+ `${rootClassName}__trigger-item`,
58
+ `${rootClassName}__trigger-item--page-trigger`,
59
+ `${rootClassName}__trigger-item--sm`,
60
+ `${rootClassName}__trigger-item--icon-only`,
61
+ { [`${rootClassName}__trigger-item--selected`]: selected },
62
+ ])}
63
+ data-testid={`${dataTestId}-link`}
64
+ href={hrefGetter(pageNumber)}
65
+ onClick={() => onPageChange(pageNumber)}
66
+ >
67
+ {pageNumber}
68
+ </AnchorTagElement>
69
+ )
70
+ ) : (
71
+ <button
72
+ aria-disabled={disabled}
73
+ className={cx([
74
+ `${rootClassName}__trigger-item`,
75
+ `${rootClassName}__trigger-item--page-trigger`,
76
+ `${rootClassName}__trigger-item--sm`,
77
+ `${rootClassName}__trigger-item--icon-only`,
78
+ { [`${rootClassName}__trigger-item--selected`]: selected },
79
+ ])}
80
+ data-testid={`${dataTestId}-button`}
81
+ disabled={disabled}
82
+ onClick={() => onPageChange(pageNumber)}
83
+ type="button"
84
+ >
85
+ {pageNumber}
86
+ </button>
87
+ )}
88
+ </li>
89
+ );
90
+ }
91
+ );
92
+
93
+ PaginationPageTrigger.displayName = "PaginationPageTrigger";
@@ -0,0 +1,8 @@
1
+ .purpur-pagination-pages {
2
+ display: flex;
3
+ gap: var(--purpur-spacing-100);
4
+ justify-content: center;
5
+ list-style-type: none;
6
+ margin: 0;
7
+ padding: 0;
8
+ }
@@ -0,0 +1,95 @@
1
+ import React from "react";
2
+ import * as matchers from "@testing-library/jest-dom/matchers";
3
+ import { cleanup, render, screen } from "@testing-library/react";
4
+ import userEvent from "@testing-library/user-event";
5
+ import { afterEach, describe, expect, it, vi } from "vitest";
6
+
7
+ import { PaginationPages } from "./pagination-pages";
8
+
9
+ expect.extend(matchers);
10
+
11
+ afterEach(() => {
12
+ cleanup();
13
+ vi.clearAllMocks();
14
+ });
15
+
16
+ const onPageChange = vi.fn();
17
+
18
+ describe("PaginationPages Component", () => {
19
+ it("should render the PaginationPages component", () => {
20
+ render(<PaginationPages pages={[1, 2, 3, 4, 5]} currentPage={1} onPageChange={onPageChange} />);
21
+ expect(screen.getByTestId(Selectors.PAGINATION_PAGES)).toBeInTheDocument();
22
+ });
23
+
24
+ it("should display the correct number of pages", () => {
25
+ render(<PaginationPages pages={[1, 2, 3, 4, 5]} currentPage={1} onPageChange={onPageChange} />);
26
+ for (let i = 1; i <= 5; i++) {
27
+ expect(screen.getByText(i)).toBeInTheDocument();
28
+ }
29
+ });
30
+
31
+ it("should display two truncation separators correctly", () => {
32
+ const currentPage = 23;
33
+ render(
34
+ <PaginationPages
35
+ pages={[1, 22, 23, 24, 50]}
36
+ currentPage={currentPage}
37
+ onPageChange={onPageChange}
38
+ />
39
+ );
40
+
41
+ expect(
42
+ screen.getByTestId("purpur-pagination-pages-truncation-separator-22")
43
+ ).toBeInTheDocument();
44
+ expect(
45
+ screen.getByTestId("purpur-pagination-pages-truncation-separator-50")
46
+ ).toBeInTheDocument();
47
+ expect(screen.getAllByText("...").length).toBe(2);
48
+ });
49
+
50
+ it("should display one truncation separator correctly at the beginning", () => {
51
+ const currentPage = 2;
52
+ render(
53
+ <PaginationPages
54
+ pages={[1, 2, 3, 4, 50]}
55
+ currentPage={currentPage}
56
+ onPageChange={onPageChange}
57
+ />
58
+ );
59
+
60
+ expect(
61
+ screen.getByTestId("purpur-pagination-pages-truncation-separator-50")
62
+ ).toBeInTheDocument();
63
+ expect(screen.getAllByText("...").length).toBe(1);
64
+ });
65
+
66
+ it("should display one truncation separator correctly at the end", () => {
67
+ const currentPage = 49;
68
+ render(
69
+ <PaginationPages
70
+ pages={[1, 47, 48, 49, 50]}
71
+ currentPage={currentPage}
72
+ onPageChange={onPageChange}
73
+ />
74
+ );
75
+
76
+ expect(
77
+ screen.getByTestId("purpur-pagination-pages-truncation-separator-47")
78
+ ).toBeInTheDocument();
79
+ expect(screen.getAllByText("...").length).toBe(1);
80
+ });
81
+
82
+ it("should handle page click events", async () => {
83
+ render(<PaginationPages pages={[1, 2, 3, 4, 5]} currentPage={1} onPageChange={onPageChange} />);
84
+
85
+ const pageButton = screen.getByText("2");
86
+ await userEvent.click(pageButton);
87
+
88
+ expect(onPageChange).toHaveBeenCalledWith(2);
89
+ });
90
+ });
91
+
92
+ const Selectors = {
93
+ PAGINATION_PAGES: "purpur-pagination-pages",
94
+ TRUNCATION_SEPARATOR: "truncation-separator",
95
+ };
@@ -0,0 +1,72 @@
1
+ import React, { ForwardedRef, forwardRef } from "react";
2
+ import c from "classnames/bind";
3
+
4
+ import { PaginationPageTrigger } from "./pagination-page-trigger";
5
+ import styles from "./pagination-pages.module.scss";
6
+ import { PaginationTruncationSeparator } from "./pagination-truncation-separator";
7
+ import { InternalLinkProps, InternalNoLinkProps, Link } from "./types";
8
+ const cx = c.bind(styles);
9
+
10
+ export type PaginationPagesProps = {
11
+ ["data-testid"]?: string;
12
+ anchorTagElement: Link;
13
+ className?: string;
14
+ currentPage: number;
15
+ onPageChange: (page: number) => void;
16
+ pages: number[];
17
+ } & (InternalNoLinkProps | InternalLinkProps);
18
+
19
+ const rootClassName = "purpur-pagination-pages";
20
+
21
+ export const PaginationPages = forwardRef(
22
+ (
23
+ {
24
+ ["data-testid"]: dataTestId = "purpur-pagination-pages",
25
+ anchorTagElement: AnchorTagElement,
26
+ asLink,
27
+ className,
28
+ currentPage,
29
+ hrefGetter,
30
+ onPageChange,
31
+ pages,
32
+ ...props
33
+ }: PaginationPagesProps,
34
+ ref: ForwardedRef<HTMLOListElement>
35
+ ) => {
36
+ const classes = cx([className, rootClassName]);
37
+
38
+ return (
39
+ <ol className={classes} data-testid={dataTestId} ref={ref} {...props}>
40
+ {pages.flatMap((pageNumber: number, idx: number, pages: number[]) => {
41
+ const TriggerComponent = (
42
+ <PaginationPageTrigger
43
+ {...(asLink
44
+ ? { asLink: true, hrefGetter }
45
+ : {
46
+ asLink: undefined as never,
47
+ hrefGetter: undefined as never,
48
+ })}
49
+ anchorTagElement={AnchorTagElement}
50
+ data-testid={`${dataTestId}-page-${pageNumber}`}
51
+ key={pageNumber}
52
+ onPageChange={onPageChange}
53
+ selected={pageNumber === currentPage}
54
+ pageNumber={pageNumber}
55
+ />
56
+ );
57
+ return idx === 0 || pages[idx - 1] + 1 === pageNumber
58
+ ? [TriggerComponent]
59
+ : [
60
+ <PaginationTruncationSeparator
61
+ data-testid={`${dataTestId}-truncation-separator-${pageNumber}`}
62
+ key={`${pageNumber}-separator`}
63
+ />,
64
+ TriggerComponent,
65
+ ];
66
+ })}
67
+ </ol>
68
+ );
69
+ }
70
+ );
71
+
72
+ PaginationPages.displayName = "PaginationPages";
@@ -0,0 +1,27 @@
1
+ @import "@purpurds/action/src/action.scss";
2
+ @import "@purpurds/tokens/breakpoint/variables";
3
+
4
+ .purpur-pagination-step-trigger {
5
+ &__trigger-item {
6
+ @include action();
7
+
8
+ &--step-trigger {
9
+ box-sizing: border-box;
10
+ height: calc(var(--purpur-spacing-150) + var(--purpur-spacing-400));
11
+ padding: var(--purpur-spacing-100);
12
+ width: calc(var(--purpur-spacing-150) + var(--purpur-spacing-400));
13
+
14
+ @media screen and (min-width: $purpur-breakpoint-lg) {
15
+ padding: var(--purpur-spacing-100) var(--purpur-spacing-250);
16
+ width: auto;
17
+ }
18
+ }
19
+
20
+ &--disabled {
21
+ background-color: var(--purpur-color-background-interactive-disabled);
22
+ box-shadow: none;
23
+ color: var(--purpur-color-text-weak);
24
+ cursor: not-allowed;
25
+ }
26
+ }
27
+ }
@@ -0,0 +1,106 @@
1
+ import React from "react";
2
+ import * as matchers from "@testing-library/jest-dom/matchers";
3
+ import { cleanup, render, screen } from "@testing-library/react";
4
+ import userEvent from "@testing-library/user-event";
5
+ import { afterEach, describe, expect, it, vi } from "vitest";
6
+
7
+ import { PaginationStepTrigger } from "./pagination-step-trigger";
8
+
9
+ expect.extend(matchers);
10
+
11
+ afterEach(() => {
12
+ vi.clearAllMocks();
13
+ cleanup();
14
+ });
15
+
16
+ const hrefGetter = vi.fn((page: number) => `#page-${page}`);
17
+ const onPageChange = vi.fn();
18
+
19
+ describe("PaginationStepTrigger", () => {
20
+ it("renders a disabled link when asLink is true and disabled is true", () => {
21
+ render(
22
+ <PaginationStepTrigger
23
+ anchorTagElement="a"
24
+ asLink
25
+ disabled
26
+ hrefGetter={hrefGetter}
27
+ onPageChange={onPageChange}
28
+ pageToNavigateTo={1}
29
+ >
30
+ Disabled Link
31
+ </PaginationStepTrigger>
32
+ );
33
+
34
+ const disabledLink = screen.getByTestId(Selectors.DISABLED_LINK);
35
+ expect(disabledLink).toBeInTheDocument();
36
+ expect(disabledLink).toHaveTextContent("Disabled Link");
37
+ });
38
+
39
+ it("renders an enabled link when asLink is true and disabled is false", async () => {
40
+ render(
41
+ <PaginationStepTrigger
42
+ anchorTagElement="a"
43
+ asLink
44
+ disabled={false}
45
+ hrefGetter={hrefGetter}
46
+ onPageChange={onPageChange}
47
+ pageToNavigateTo={2}
48
+ >
49
+ Enabled Link
50
+ </PaginationStepTrigger>
51
+ );
52
+
53
+ const link = screen.getByTestId(Selectors.LINK);
54
+ expect(link).toBeInTheDocument();
55
+ expect(link).toHaveTextContent("Enabled Link");
56
+ expect(link).toHaveAttribute("href", "#page-2");
57
+
58
+ await userEvent.click(link);
59
+ expect(onPageChange).toHaveBeenCalledWith(2);
60
+ });
61
+
62
+ it("renders a button when asLink is not set", async () => {
63
+ render(
64
+ <PaginationStepTrigger
65
+ anchorTagElement="a"
66
+ disabled={false}
67
+ onPageChange={onPageChange}
68
+ pageToNavigateTo={3}
69
+ >
70
+ Button
71
+ </PaginationStepTrigger>
72
+ );
73
+
74
+ const button = screen.getByTestId(Selectors.BUTTON);
75
+ expect(button).toBeInTheDocument();
76
+ expect(button).toHaveTextContent("Button");
77
+
78
+ await userEvent.click(button);
79
+ expect(onPageChange).toHaveBeenCalledWith(3);
80
+ });
81
+
82
+ it("renders a disabled button when asLink is not set and disabled is true", () => {
83
+ render(
84
+ <PaginationStepTrigger
85
+ anchorTagElement="a"
86
+ disabled
87
+ onPageChange={onPageChange}
88
+ pageToNavigateTo={1}
89
+ >
90
+ Disabled Button
91
+ </PaginationStepTrigger>
92
+ );
93
+
94
+ const button = screen.getByTestId(Selectors.BUTTON);
95
+ expect(button).toBeInTheDocument();
96
+ expect(button).toHaveTextContent("Disabled Button");
97
+ expect(button).toBeDisabled();
98
+ });
99
+ });
100
+
101
+ const Selectors = {
102
+ ROOT: "purpur-pagination-step-trigger",
103
+ DISABLED_LINK: "purpur-pagination-step-trigger-disabled-link",
104
+ LINK: "purpur-pagination-step-trigger-link",
105
+ BUTTON: "purpur-pagination-step-trigger-button",
106
+ };
@@ -0,0 +1,92 @@
1
+ import React, { ForwardedRef, forwardRef } from "react";
2
+ import c from "classnames/bind";
3
+
4
+ import styles from "./pagination-step-trigger.module.scss";
5
+ import { InternalLinkProps, InternalNoLinkProps, Link } from "./types";
6
+ const cx = c.bind(styles);
7
+
8
+ export type PaginationStepTriggerProps = {
9
+ ["data-testid"]?: string;
10
+ anchorTagElement: Link;
11
+ children: React.ReactNode;
12
+ className?: string;
13
+ disabled: boolean;
14
+ onPageChange: (page: number) => void;
15
+ pageToNavigateTo: number;
16
+ } & (InternalLinkProps | InternalNoLinkProps);
17
+
18
+ const rootClassName = "purpur-pagination-step-trigger";
19
+
20
+ export const PaginationStepTrigger = forwardRef(
21
+ (
22
+ {
23
+ ["data-testid"]: dataTestId = "purpur-pagination-step-trigger",
24
+ anchorTagElement: AnchorTagElement,
25
+ asLink,
26
+ children,
27
+ className,
28
+ disabled,
29
+ hrefGetter,
30
+ onPageChange,
31
+ pageToNavigateTo,
32
+ ...props
33
+ }: PaginationStepTriggerProps,
34
+ ref: ForwardedRef<HTMLDivElement>
35
+ ) => {
36
+ const classes = cx([className, rootClassName]);
37
+
38
+ return (
39
+ <div className={classes} data-testid={dataTestId} ref={ref} {...props}>
40
+ {asLink ? (
41
+ disabled ? (
42
+ <span
43
+ className={cx([
44
+ `${rootClassName}__trigger-item`,
45
+ `${rootClassName}__trigger-item--step-trigger`,
46
+ `${rootClassName}__trigger-item--secondary`,
47
+ `${rootClassName}__trigger-item--sm`,
48
+ `${rootClassName}__trigger-item--disabled`,
49
+ ])}
50
+ data-testid={`${dataTestId}-disabled-link`}
51
+ >
52
+ {children}
53
+ </span>
54
+ ) : (
55
+ <AnchorTagElement
56
+ className={cx([
57
+ `${rootClassName}__trigger-item`,
58
+ `${rootClassName}__trigger-item--step-trigger`,
59
+ `${rootClassName}__trigger-item--secondary`,
60
+ `${rootClassName}__trigger-item--sm`,
61
+ ])}
62
+ data-testid={`${dataTestId}-link`}
63
+ href={hrefGetter(pageToNavigateTo)}
64
+ onClick={() => onPageChange(pageToNavigateTo)}
65
+ >
66
+ {children}
67
+ </AnchorTagElement>
68
+ )
69
+ ) : (
70
+ <button
71
+ aria-disabled={disabled}
72
+ className={cx([
73
+ `${rootClassName}__trigger-item`,
74
+ `${rootClassName}__trigger-item--step-trigger`,
75
+ `${rootClassName}__trigger-item--secondary`,
76
+ `${rootClassName}__trigger-item--sm`,
77
+ { [`${rootClassName}__trigger-item--disabled`]: disabled },
78
+ ])}
79
+ data-testid={`${dataTestId}-button`}
80
+ disabled={disabled}
81
+ onClick={() => onPageChange(pageToNavigateTo)}
82
+ type="button"
83
+ >
84
+ {children}
85
+ </button>
86
+ )}
87
+ </div>
88
+ );
89
+ }
90
+ );
91
+
92
+ PaginationStepTrigger.displayName = "PaginationStepTrigger";
@@ -0,0 +1,8 @@
1
+ .purpur-pagination-truncation-separator {
2
+ box-sizing: border-box;
3
+ display: block;
4
+ height: calc(2 * var(--purpur-spacing-250));
5
+ padding: var(--purpur-spacing-150) 0;
6
+ text-align: center;
7
+ width: var(--purpur-spacing-250);
8
+ }