@arbor-education/design-system.components 0.1.1 → 0.1.2

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 (74) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/dist/components/button/Button.d.ts +1 -0
  3. package/dist/components/button/Button.d.ts.map +1 -1
  4. package/dist/components/button/Button.js +2 -1
  5. package/dist/components/button/Button.js.map +1 -1
  6. package/dist/components/formField/inputs/number/NumberInput.d.ts +2 -0
  7. package/dist/components/formField/inputs/number/NumberInput.d.ts.map +1 -1
  8. package/dist/components/formField/inputs/number/NumberInput.js +9 -4
  9. package/dist/components/formField/inputs/number/NumberInput.js.map +1 -1
  10. package/dist/components/table/BulkActionsDropdown.js +2 -2
  11. package/dist/components/table/BulkActionsDropdown.js.map +1 -1
  12. package/dist/components/table/GridApiContext.d.ts +1 -2
  13. package/dist/components/table/GridApiContext.d.ts.map +1 -1
  14. package/dist/components/table/GridApiContext.js +1 -1
  15. package/dist/components/table/GridApiContext.js.map +1 -1
  16. package/dist/components/table/Table.d.ts +9 -1
  17. package/dist/components/table/Table.d.ts.map +1 -1
  18. package/dist/components/table/Table.js +13 -5
  19. package/dist/components/table/Table.js.map +1 -1
  20. package/dist/components/table/Table.stories.d.ts +5 -1
  21. package/dist/components/table/Table.stories.d.ts.map +1 -1
  22. package/dist/components/table/Table.stories.js +36 -1
  23. package/dist/components/table/Table.stories.js.map +1 -1
  24. package/dist/components/table/Table.test.js +2 -0
  25. package/dist/components/table/Table.test.js.map +1 -1
  26. package/dist/components/table/pagination/PageSizeSelector.d.ts +7 -0
  27. package/dist/components/table/pagination/PageSizeSelector.d.ts.map +1 -0
  28. package/dist/components/table/pagination/PageSizeSelector.js +46 -0
  29. package/dist/components/table/pagination/PageSizeSelector.js.map +1 -0
  30. package/dist/components/table/pagination/Pagination.test.d.ts +2 -0
  31. package/dist/components/table/pagination/Pagination.test.d.ts.map +1 -0
  32. package/dist/components/table/pagination/Pagination.test.js +616 -0
  33. package/dist/components/table/pagination/Pagination.test.js.map +1 -0
  34. package/dist/components/table/pagination/PaginationControls.d.ts +6 -0
  35. package/dist/components/table/pagination/PaginationControls.d.ts.map +1 -0
  36. package/dist/components/table/pagination/PaginationControls.js +47 -0
  37. package/dist/components/table/pagination/PaginationControls.js.map +1 -0
  38. package/dist/components/table/pagination/PaginationPanel.d.ts +9 -0
  39. package/dist/components/table/pagination/PaginationPanel.d.ts.map +1 -0
  40. package/dist/components/table/pagination/PaginationPanel.js +11 -0
  41. package/dist/components/table/pagination/PaginationPanel.js.map +1 -0
  42. package/dist/components/table/pagination/RowCountInfo.d.ts +5 -0
  43. package/dist/components/table/pagination/RowCountInfo.d.ts.map +1 -0
  44. package/dist/components/table/pagination/RowCountInfo.js +53 -0
  45. package/dist/components/table/pagination/RowCountInfo.js.map +1 -0
  46. package/dist/index.css +28 -0
  47. package/dist/index.css.map +1 -1
  48. package/dist/utils/hooks/useGridApi.d.ts +5 -0
  49. package/dist/utils/hooks/useGridApi.d.ts.map +1 -0
  50. package/dist/utils/hooks/useGridApi.js +13 -0
  51. package/dist/utils/hooks/useGridApi.js.map +1 -0
  52. package/dist/utils/hooks/useIsMounted.d.ts +2 -0
  53. package/dist/utils/hooks/useIsMounted.d.ts.map +1 -0
  54. package/dist/utils/hooks/useIsMounted.js +12 -0
  55. package/dist/utils/hooks/useIsMounted.js.map +1 -0
  56. package/package.json +1 -1
  57. package/release/design-system.components.tgz +0 -0
  58. package/src/components/button/Button.tsx +3 -0
  59. package/src/components/button/button.scss +4 -0
  60. package/src/components/formField/inputs/number/NumberInput.tsx +39 -24
  61. package/src/components/table/BulkActionsDropdown.tsx +2 -2
  62. package/src/components/table/GridApiContext.ts +2 -2
  63. package/src/components/table/Table.stories.tsx +64 -2
  64. package/src/components/table/Table.test.tsx +2 -0
  65. package/src/components/table/Table.tsx +14 -4
  66. package/src/components/table/pagination/PageSizeSelector.tsx +73 -0
  67. package/src/components/table/pagination/Pagination.test.tsx +846 -0
  68. package/src/components/table/pagination/PaginationControls.tsx +116 -0
  69. package/src/components/table/pagination/PaginationPanel.tsx +30 -0
  70. package/src/components/table/pagination/RowCountInfo.tsx +67 -0
  71. package/src/components/table/pagination/pagination.scss +26 -0
  72. package/src/index.scss +1 -0
  73. package/src/utils/hooks/useGridApi.ts +15 -0
  74. package/src/utils/hooks/useIsMounted.ts +12 -0
@@ -0,0 +1,116 @@
1
+ import { Button } from 'Components/button/Button';
2
+ import { NumberInput } from 'Components/formField/inputs/number/NumberInput';
3
+ import { useEffect, useState } from 'react';
4
+ import { useGridApi } from 'Utils/hooks/useGridApi';
5
+
6
+ export type PaginationControlsProps = {
7
+ onPageChange?: (newPage: number) => void;
8
+ totalPages?: number;
9
+ };
10
+
11
+ export const PaginationControls = (props: PaginationControlsProps) => {
12
+ const { onPageChange, totalPages: passedTotalPages } = props;
13
+ const { gridApi, isGridApiReady } = useGridApi();
14
+ const [inputValue, setInputValue] = useState(1);
15
+
16
+ // default to 1, because no content is still one page
17
+ const [totalPages, setTotalPages] = useState(() => passedTotalPages ?? gridApi?.paginationGetTotalPages() ?? 1);
18
+
19
+ useEffect(() => {
20
+ if (isGridApiReady && !passedTotalPages) {
21
+ gridApi?.addEventListener('paginationChanged', () => {
22
+ const newPageCount = gridApi.paginationGetTotalPages();
23
+ if (newPageCount) {
24
+ setTotalPages(newPageCount);
25
+ }
26
+ });
27
+ }
28
+ }, [isGridApiReady]);
29
+
30
+ useEffect(() => {
31
+ if (passedTotalPages) {
32
+ setTotalPages(passedTotalPages);
33
+ }
34
+ }, [passedTotalPages]);
35
+
36
+ const goToPage = (page: number) => {
37
+ if (page === inputValue) {
38
+ return;
39
+ }
40
+ const pageZeroIndexed = page - 1;
41
+ if (onPageChange) {
42
+ onPageChange(pageZeroIndexed);
43
+ }
44
+ else {
45
+ gridApi?.paginationGoToPage(pageZeroIndexed);
46
+ }
47
+ setInputValue(page);
48
+ };
49
+
50
+ return (
51
+ <div className="ds-table__pagination-controls">
52
+ <Button
53
+ variant="secondary"
54
+ borderless
55
+ size="S"
56
+ color="black"
57
+ iconLeftName="chevrons-left"
58
+ iconLeftScreenReaderText="Go to first page"
59
+ onClick={() => goToPage(1)}
60
+ disabled={inputValue === 1}
61
+ />
62
+ <Button
63
+ variant="secondary"
64
+ borderless
65
+ size="S"
66
+ color="black"
67
+ iconLeftName="chevron-left"
68
+ onClick={() => goToPage(inputValue - 1)}
69
+ iconLeftScreenReaderText="Go to previous page"
70
+ disabled={inputValue === 1}
71
+ />
72
+ <span>
73
+ Page
74
+ {' '}
75
+ <NumberInput
76
+ value={inputValue}
77
+ onChange={(e) => {
78
+ const newPage = Number(e.currentTarget.value);
79
+ if (newPage > 0) {
80
+ goToPage(newPage);
81
+ }
82
+ }}
83
+ max={totalPages}
84
+ min={1}
85
+ disableSpinners
86
+ containerClassName="ds-table__pagination-controls__number-input-container"
87
+ className="ds-table__pagination-controls__number-input"
88
+ aria-label="Current page"
89
+ aria-current="page"
90
+ />
91
+ {' '}
92
+ of
93
+ {' '}
94
+ {totalPages}
95
+ </span>
96
+ <Button
97
+ variant="secondary"
98
+ borderless
99
+ color="black"
100
+ iconLeftName="chevron-right"
101
+ onClick={() => goToPage(inputValue + 1)}
102
+ disabled={inputValue === totalPages}
103
+ iconLeftScreenReaderText="Go to next page"
104
+ />
105
+ <Button
106
+ variant="secondary"
107
+ borderless
108
+ color="black"
109
+ iconLeftName="chevrons-right"
110
+ onClick={() => goToPage(totalPages)}
111
+ disabled={inputValue === totalPages}
112
+ iconLeftScreenReaderText="Go to last page"
113
+ />
114
+ </div>
115
+ );
116
+ };
@@ -0,0 +1,30 @@
1
+ import { PageSizeSelector, type PageSizeSelectorProps } from './PageSizeSelector';
2
+ import { RowCountInfo, type RowCountInfoProps } from './RowCountInfo';
3
+ import { PaginationControls, type PaginationControlsProps } from './PaginationControls';
4
+ import classNames from 'classnames';
5
+
6
+ type PaginationPanelProps = {
7
+ className?: string;
8
+ } & RowCountInfoProps & PaginationControlsProps & PageSizeSelectorProps;
9
+
10
+ export const PaginationPanel = (props: PaginationPanelProps) => {
11
+ const {
12
+ className = '',
13
+ totalRows,
14
+ totalPages,
15
+ onPageChange,
16
+ availableSizes,
17
+ initialPageSize,
18
+ onPageSizeChange,
19
+ } = props;
20
+
21
+ const classes = classNames('ds-table__pagination-panel', className);
22
+
23
+ return (
24
+ <nav className={classes} aria-label="Pagination">
25
+ <RowCountInfo totalRows={totalRows} />
26
+ <PaginationControls totalPages={totalPages} onPageChange={onPageChange} />
27
+ <PageSizeSelector availableSizes={availableSizes} initialPageSize={initialPageSize} onPageSizeChange={onPageSizeChange} />
28
+ </nav>
29
+ );
30
+ };
@@ -0,0 +1,67 @@
1
+ import { useEffect, useState } from 'react';
2
+ import { useGridApi } from 'Utils/hooks/useGridApi';
3
+
4
+ export type RowCountInfoProps = {
5
+ totalRows?: number;
6
+ };
7
+
8
+ export const RowCountInfo = (props: RowCountInfoProps) => {
9
+ const { totalRows: passedTotalRows } = props;
10
+
11
+ const { gridApi, isGridApiReady } = useGridApi();
12
+
13
+ const getTrueDisplayedRowCount = () => {
14
+ const displayedRowCount = gridApi?.getDisplayedRowCount() || 0;
15
+ const pageSize = gridApi?.paginationGetPageSize() || 0;
16
+ const result = Math.min(displayedRowCount, pageSize);
17
+ const currentPage = gridApi?.paginationGetCurrentPage() || 0;
18
+ const totalPages = gridApi?.paginationGetTotalPages();
19
+
20
+ if (totalPages === 1) {
21
+ return displayedRowCount;
22
+ }
23
+ if (gridApi && displayedRowCount && pageSize && currentPage + 1 === totalPages) {
24
+ return displayedRowCount % pageSize || pageSize;
25
+ }
26
+ return result;
27
+ };
28
+ const [displayedRowCount, setDisplayedRowCount] = useState(() => getTrueDisplayedRowCount());
29
+
30
+ useEffect(() => {
31
+ if (isGridApiReady) {
32
+ const result = getTrueDisplayedRowCount();
33
+ setDisplayedRowCount(result);
34
+ }
35
+ }, [isGridApiReady]);
36
+
37
+ if (!isGridApiReady) {
38
+ return null;
39
+ }
40
+
41
+ // TODO when writing tests, see how this behaves with or without pagination
42
+ let totalRows: number | undefined;
43
+ if (typeof passedTotalRows !== 'undefined') {
44
+ totalRows = passedTotalRows;
45
+ }
46
+ else if (gridApi && gridApi.paginationGetTotalPages() > 1) {
47
+ totalRows = gridApi.paginationGetRowCount();
48
+ }
49
+
50
+ gridApi?.addEventListener('paginationChanged', () => {
51
+ setDisplayedRowCount(getTrueDisplayedRowCount());
52
+ });
53
+ gridApi?.addEventListener('filterChanged', () => {
54
+ setDisplayedRowCount(getTrueDisplayedRowCount());
55
+ });
56
+ gridApi?.addEventListener('rowDataUpdated', () => {
57
+ setDisplayedRowCount(getTrueDisplayedRowCount());
58
+ });
59
+
60
+ const message = totalRows && totalRows !== displayedRowCount
61
+ ? `Showing ${displayedRowCount} of ${totalRows} results`
62
+ : `Showing ${displayedRowCount} results`;
63
+
64
+ return (
65
+ <span className="ds-table__row-count-info">{message}</span>
66
+ );
67
+ };
@@ -0,0 +1,26 @@
1
+ .ds-table__pagination-panel {
2
+ display: flex;
3
+ flex-direction: row;
4
+ align-items: center;
5
+ width: 100%;
6
+ justify-content: space-between;
7
+ }
8
+
9
+ .ds-table__pagination-controls {
10
+ display: flex;
11
+ flex-direction: row;
12
+ align-items: center;
13
+ gap: 3px;
14
+
15
+ &__number-input-container {
16
+ display: inline;
17
+ }
18
+
19
+ &__number-input {
20
+ width: 39px;
21
+ }
22
+ }
23
+
24
+ .ds-table__pagination-controls, .ds-table__row-count-info {
25
+ font-size: var(--font-size-2-13);
26
+ }
package/src/index.scss CHANGED
@@ -18,5 +18,6 @@
18
18
  @use "./components/slideoverManager/slideoverManager.scss";
19
19
  @use "./components/slideover/slideover.scss";
20
20
  @use "./components/table/table.scss";
21
+ @use "./components/table/pagination/pagination.scss";
21
22
  @use "./components/tooltip/tooltip.scss";
22
23
  @import 'https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap';
@@ -0,0 +1,15 @@
1
+ import { GridApiContext } from 'Components/table/GridApiContext';
2
+ import { useContext, useEffect, useState } from 'react';
3
+
4
+ export const useGridApi = () => {
5
+ const gridApi = useContext(GridApiContext);
6
+ const [isGridApiReady, setIsGridApiReady] = useState(false);
7
+
8
+ useEffect(() => {
9
+ if (gridApi) {
10
+ setIsGridApiReady(true);
11
+ }
12
+ }, [gridApi]);
13
+
14
+ return { gridApi, isGridApiReady };
15
+ };
@@ -0,0 +1,12 @@
1
+ import { useRef } from 'react';
2
+ import { useComponentDidMount } from './useComponentDidMount';
3
+
4
+ export const useIsMounted = () => {
5
+ const isMounted = useRef(true);
6
+ useComponentDidMount(() => {
7
+ return () => {
8
+ isMounted.current = false;
9
+ };
10
+ });
11
+ return isMounted;
12
+ };