@okta/odyssey-react-mui 1.14.3 → 1.14.5

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 (85) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/dist/DataTable/DataTable.js +450 -0
  3. package/dist/DataTable/DataTable.js.map +1 -0
  4. package/dist/DataTable/DataTableEmptyState.js +55 -0
  5. package/dist/DataTable/DataTableEmptyState.js.map +1 -0
  6. package/dist/DataTable/DataTablePagination.js +221 -0
  7. package/dist/DataTable/DataTablePagination.js.map +1 -0
  8. package/dist/DataTable/DataTableRowActions.js +99 -0
  9. package/dist/DataTable/DataTableRowActions.js.map +1 -0
  10. package/dist/DataTable/DataTableSettings.js +84 -0
  11. package/dist/DataTable/DataTableSettings.js.map +1 -0
  12. package/dist/DataTable/constants.js +15 -0
  13. package/dist/DataTable/constants.js.map +1 -0
  14. package/dist/DataTable/index.js +15 -0
  15. package/dist/DataTable/index.js.map +1 -0
  16. package/dist/DataTable/reorderDataRowsLocally.js +26 -0
  17. package/dist/DataTable/reorderDataRowsLocally.js.map +1 -0
  18. package/dist/DataTable/useRowReordering.js +179 -0
  19. package/dist/DataTable/useRowReordering.js.map +1 -0
  20. package/dist/DataTable/useScrollIndication.js +70 -0
  21. package/dist/DataTable/useScrollIndication.js.map +1 -0
  22. package/dist/index.js +1 -0
  23. package/dist/index.js.map +1 -1
  24. package/dist/labs/DataFilters.js +47 -15
  25. package/dist/labs/DataFilters.js.map +1 -1
  26. package/dist/labs/DataTable.js +19 -14
  27. package/dist/labs/DataTable.js.map +1 -1
  28. package/dist/labs/index.js +0 -1
  29. package/dist/labs/index.js.map +1 -1
  30. package/dist/properties/ts/odyssey-react-mui.js +27 -1
  31. package/dist/properties/ts/odyssey-react-mui.js.map +1 -1
  32. package/dist/src/DataTable/DataTable.d.ts +151 -0
  33. package/dist/src/DataTable/DataTable.d.ts.map +1 -0
  34. package/dist/src/DataTable/DataTableEmptyState.d.ts +21 -0
  35. package/dist/src/DataTable/DataTableEmptyState.d.ts.map +1 -0
  36. package/dist/src/DataTable/DataTablePagination.d.ts +33 -0
  37. package/dist/src/DataTable/DataTablePagination.d.ts.map +1 -0
  38. package/dist/src/DataTable/DataTableRowActions.d.ts +30 -0
  39. package/dist/src/DataTable/DataTableRowActions.d.ts.map +1 -0
  40. package/dist/src/DataTable/DataTableSettings.d.ts +27 -0
  41. package/dist/src/DataTable/DataTableSettings.d.ts.map +1 -0
  42. package/dist/src/DataTable/constants.d.ts +14 -0
  43. package/dist/src/DataTable/constants.d.ts.map +1 -0
  44. package/dist/src/DataTable/index.d.ts +16 -0
  45. package/dist/src/DataTable/index.d.ts.map +1 -0
  46. package/dist/src/DataTable/reorderDataRowsLocally.d.ts +26 -0
  47. package/dist/src/DataTable/reorderDataRowsLocally.d.ts.map +1 -0
  48. package/dist/src/DataTable/useRowReordering.d.ts +56 -0
  49. package/dist/src/DataTable/useRowReordering.d.ts.map +1 -0
  50. package/dist/src/DataTable/useScrollIndication.d.ts +22 -0
  51. package/dist/src/DataTable/useScrollIndication.d.ts.map +1 -0
  52. package/dist/src/OdysseyTranslationProvider.d.ts +1 -1
  53. package/dist/src/OdysseyTranslationProvider.d.ts.map +1 -1
  54. package/dist/src/index.d.ts +1 -0
  55. package/dist/src/index.d.ts.map +1 -1
  56. package/dist/src/labs/DataFilters.d.ts +5 -1
  57. package/dist/src/labs/DataFilters.d.ts.map +1 -1
  58. package/dist/src/labs/DataTable.d.ts +2 -2
  59. package/dist/src/labs/DataTable.d.ts.map +1 -1
  60. package/dist/src/labs/index.d.ts +3 -1
  61. package/dist/src/labs/index.d.ts.map +1 -1
  62. package/dist/src/properties/ts/odyssey-react-mui.d.ts +27 -1
  63. package/dist/src/properties/ts/odyssey-react-mui.d.ts.map +1 -1
  64. package/dist/src/theme/components.d.ts.map +1 -1
  65. package/dist/theme/components.js +37 -18
  66. package/dist/theme/components.js.map +1 -1
  67. package/dist/tsconfig.production.tsbuildinfo +1 -1
  68. package/package.json +3 -3
  69. package/src/DataTable/DataTable.tsx +746 -0
  70. package/src/DataTable/DataTableEmptyState.tsx +62 -0
  71. package/src/DataTable/DataTablePagination.tsx +289 -0
  72. package/src/DataTable/DataTableRowActions.tsx +122 -0
  73. package/src/DataTable/DataTableSettings.tsx +135 -0
  74. package/src/DataTable/constants.ts +14 -0
  75. package/src/DataTable/index.tsx +28 -0
  76. package/src/DataTable/reorderDataRowsLocally.tsx +48 -0
  77. package/src/DataTable/useRowReordering.tsx +235 -0
  78. package/src/DataTable/useScrollIndication.tsx +118 -0
  79. package/src/index.ts +1 -0
  80. package/src/labs/DataFilters.tsx +69 -17
  81. package/src/labs/DataTable.tsx +29 -20
  82. package/src/labs/index.ts +3 -1
  83. package/src/properties/odyssey-react-mui.properties +41 -2
  84. package/src/properties/ts/odyssey-react-mui.ts +1 -1
  85. package/src/theme/components.tsx +43 -16
@@ -0,0 +1,62 @@
1
+ /*!
2
+ * Copyright (c) 2024-present, Okta, Inc. and/or its affiliates. All rights reserved.
3
+ * The Okta software accompanied by this notice is provided pursuant to the Apache License, Version 2.0 (the "License.")
4
+ *
5
+ * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0.
6
+ * Unless required by applicable law or agreed to in writing, software
7
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
8
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9
+ *
10
+ * See the License for the specific language governing permissions and limitations under the License.
11
+ */
12
+
13
+ import { ReactNode, memo } from "react";
14
+ import { Heading4, Paragraph } from "../Typography";
15
+ import { Box } from "../Box";
16
+ import styled from "@emotion/styled";
17
+ import {
18
+ useOdysseyDesignTokens,
19
+ DesignTokens,
20
+ } from "../OdysseyDesignTokensContext";
21
+
22
+ const EmptyContainer = styled("div", {
23
+ shouldForwardProp: (prop) => prop !== "odysseyDesignTokens",
24
+ })(({ odysseyDesignTokens }: { odysseyDesignTokens: DesignTokens }) => ({
25
+ display: "flex",
26
+ flexDirection: "column",
27
+ marginBlock: odysseyDesignTokens.Spacing9,
28
+ padding: odysseyDesignTokens.Spacing5,
29
+ textAlign: "center",
30
+ width: "100%",
31
+ }));
32
+
33
+ export type DataTableEmptyStateProps = {
34
+ heading: string;
35
+ text: string;
36
+ primaryButton?: ReactNode;
37
+ secondaryButton?: ReactNode;
38
+ };
39
+
40
+ const DataTableEmptyState = ({
41
+ heading,
42
+ text,
43
+ primaryButton,
44
+ secondaryButton,
45
+ }: DataTableEmptyStateProps) => {
46
+ const odysseyDesignTokens = useOdysseyDesignTokens();
47
+ return (
48
+ <EmptyContainer odysseyDesignTokens={odysseyDesignTokens}>
49
+ <Heading4>{heading}</Heading4>
50
+ <Paragraph>{text}</Paragraph>
51
+ {(primaryButton || secondaryButton) && (
52
+ <Box sx={{ marginBlockStart: 5 }}>
53
+ {secondaryButton}
54
+ {primaryButton}
55
+ </Box>
56
+ )}
57
+ </EmptyContainer>
58
+ );
59
+ };
60
+
61
+ const MemoizedDataTableEmptyState = memo(DataTableEmptyState);
62
+ export { MemoizedDataTableEmptyState as DataTableEmptyState };
@@ -0,0 +1,289 @@
1
+ /*!
2
+ * Copyright (c) 2023-present, Okta, Inc. and/or its affiliates. All rights reserved.
3
+ * The Okta software accompanied by this notice is provided pursuant to the Apache License, Version 2.0 (the "License.")
4
+ *
5
+ * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0.
6
+ * Unless required by applicable law or agreed to in writing, software
7
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
8
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9
+ *
10
+ * See the License for the specific language governing permissions and limitations under the License.
11
+ */
12
+
13
+ import { InputBase } from "@mui/material";
14
+ import {
15
+ Dispatch,
16
+ SetStateAction,
17
+ memo,
18
+ useCallback,
19
+ useEffect,
20
+ useMemo,
21
+ useRef,
22
+ useState,
23
+ } from "react";
24
+ import { Paragraph } from "../Typography";
25
+ import { Button } from "../Button";
26
+ import { ArrowLeftIcon, ArrowRightIcon } from "../icons.generated";
27
+ import styled from "@emotion/styled";
28
+ import {
29
+ DesignTokens,
30
+ useOdysseyDesignTokens,
31
+ } from "../OdysseyDesignTokensContext";
32
+ import { Box } from "../Box";
33
+ import { Trans, useTranslation } from "react-i18next";
34
+ import { paginationTypeValues } from "./constants";
35
+
36
+ const PaginationContainer = styled("div")({
37
+ display: "flex",
38
+ alignItems: "center",
39
+ justifyContent: "space-between",
40
+ });
41
+
42
+ const PaginationSegment = styled("div", {
43
+ shouldForwardProp: (prop) => prop !== "odysseyDesignTokens",
44
+ })(({ odysseyDesignTokens }: { odysseyDesignTokens: DesignTokens }) => ({
45
+ display: "flex",
46
+ alignItems: "center",
47
+ gap: odysseyDesignTokens.Spacing4,
48
+ "& > div": {
49
+ display: "flex",
50
+ alignItems: "center",
51
+ gap: odysseyDesignTokens.Spacing2,
52
+ },
53
+ }));
54
+
55
+ const PaginationInput = styled(InputBase, {
56
+ shouldForwardProp: (prop) => prop !== "odysseyDesignTokens",
57
+ })(({ odysseyDesignTokens }: { odysseyDesignTokens: DesignTokens }) => ({
58
+ borderColor: odysseyDesignTokens.HueNeutral200,
59
+ borderRadius: odysseyDesignTokens.BorderRadiusTight,
60
+ height: odysseyDesignTokens.Spacing6,
61
+ width: "4.5714285714rem", // This is a hardcoded value, keep as string
62
+ "&:hover": {
63
+ borderColor: odysseyDesignTokens.HueNeutral400,
64
+ },
65
+ "&.Mui-focused:hover": {
66
+ borderColor: odysseyDesignTokens.PalettePrimaryMain,
67
+ },
68
+ }));
69
+
70
+ const PaginationButtonContainer = styled("div")({
71
+ "& > *": {
72
+ marginInlineStart: `0 !important`,
73
+ },
74
+ });
75
+
76
+ export type DataTablePaginationProps = {
77
+ pagination: {
78
+ pageIndex: number;
79
+ pageSize: number;
80
+ };
81
+ setPagination: Dispatch<
82
+ SetStateAction<{ pageIndex: number; pageSize: number }>
83
+ >;
84
+ totalRows?: number;
85
+ isDisabled?: boolean;
86
+ /**
87
+ * The type of pagination controls shown. Defaults to next/prev buttons, but can be
88
+ * set to a simple "Load more" button by setting to "loadMore".
89
+ */
90
+ variant?: (typeof paginationTypeValues)[number];
91
+ };
92
+
93
+ const DataTablePagination = ({
94
+ pagination,
95
+ setPagination,
96
+ totalRows,
97
+ isDisabled,
98
+ variant,
99
+ }: DataTablePaginationProps) => {
100
+ const odysseyDesignTokens = useOdysseyDesignTokens();
101
+ const { t } = useTranslation();
102
+
103
+ const [page, setPage] = useState<number>(pagination.pageIndex);
104
+ const [rowsPerPage, setRowsPerPage] = useState<number>(pagination.pageSize);
105
+ const initialRowsPerPage = useRef<number>(pagination.pageSize);
106
+
107
+ const firstRow = pagination.pageSize * (pagination.pageIndex - 1) + 1;
108
+ let lastRow = firstRow + (pagination.pageSize - 1);
109
+ // If the last eligible row is greater than the number of total rows,
110
+ // show the number of total rows instead (ie, if we're showing rows
111
+ // 180-200 but there are only 190 rows, show 180-190 instead)
112
+ lastRow = totalRows && lastRow > totalRows ? totalRows : lastRow;
113
+
114
+ useEffect(() => {
115
+ setPage(pagination.pageIndex);
116
+ setRowsPerPage(pagination.pageSize);
117
+ }, [pagination]);
118
+
119
+ const handlePaginationChange = useCallback(() => {
120
+ const updatedPage =
121
+ totalRows && page * totalRows > lastRow
122
+ ? Math.ceil(totalRows / rowsPerPage)
123
+ : page;
124
+ const updatedRowsPerPage =
125
+ totalRows && rowsPerPage > totalRows ? totalRows : rowsPerPage;
126
+
127
+ setPagination({
128
+ pageIndex: updatedPage,
129
+ pageSize: updatedRowsPerPage,
130
+ });
131
+ }, [page, rowsPerPage, lastRow, setPagination, totalRows]);
132
+
133
+ // The following handlers use React.KeyboardEvent (rather than just KeyboardEvent) becuase React.KeyboardEvent
134
+ // is generic, while plain KeyboardEvent is not. We need this generic so we can specify the HTMLInputElement,
135
+ // which allows us to use currentTarget.value
136
+ const handlePageSubmit = useCallback(
137
+ (event: React.KeyboardEvent<HTMLInputElement | HTMLTextAreaElement>) => {
138
+ if (event.key === "Enter") {
139
+ setPagination({
140
+ pageIndex: parseInt(event.currentTarget.value),
141
+ pageSize: rowsPerPage,
142
+ });
143
+ }
144
+ },
145
+ [rowsPerPage, setPagination],
146
+ );
147
+
148
+ const handleRowsPerPageSubmit = useCallback(
149
+ (event: React.KeyboardEvent<HTMLInputElement | HTMLTextAreaElement>) => {
150
+ if (event.key === "Enter") {
151
+ setPagination({
152
+ pageIndex: page,
153
+ pageSize: parseInt(event.currentTarget.value),
154
+ });
155
+ }
156
+ },
157
+ [page, setPagination],
158
+ );
159
+
160
+ const setPageFromEvent = useCallback(
161
+ (event: React.ChangeEvent<HTMLInputElement>) => {
162
+ setPage(parseInt(event.target.value));
163
+ },
164
+ [setPage],
165
+ );
166
+
167
+ const setRowsPerPageFromEvent = useCallback(
168
+ (event: React.ChangeEvent<HTMLInputElement>) => {
169
+ setRowsPerPage(parseInt(event.target.value));
170
+ },
171
+ [setRowsPerPage],
172
+ );
173
+
174
+ const handleLoadMore = useCallback(() => {
175
+ setPagination({
176
+ pageIndex: 1,
177
+ pageSize: rowsPerPage + initialRowsPerPage.current,
178
+ });
179
+ }, [rowsPerPage, setPagination]);
180
+
181
+ const handleNextButton = useCallback(() => {
182
+ setPagination({ pageIndex: page + 1, pageSize: rowsPerPage });
183
+ }, [setPagination, page, rowsPerPage]);
184
+
185
+ const handlePreviousButton = useCallback(() => {
186
+ setPagination({ pageIndex: page - 1, pageSize: rowsPerPage });
187
+ }, [setPagination, page, rowsPerPage]);
188
+
189
+ const loadMoreIsDisabled = useMemo(() => {
190
+ return totalRows ? rowsPerPage >= totalRows : false;
191
+ }, [rowsPerPage, totalRows]);
192
+
193
+ const nextButtonDisabled = useMemo(
194
+ () => (totalRows ? lastRow >= totalRows : false) || isDisabled,
195
+ [totalRows, lastRow, isDisabled],
196
+ );
197
+
198
+ const previousButtonDisabled = useMemo(
199
+ () => pagination.pageIndex <= 1 || isDisabled,
200
+ [pagination, isDisabled],
201
+ );
202
+
203
+ return variant === "paged" ? (
204
+ <PaginationContainer>
205
+ <PaginationSegment odysseyDesignTokens={odysseyDesignTokens}>
206
+ <Box>
207
+ <Paragraph component="span" color="textSecondary">
208
+ {t("table.pagination.rowsperpage")}
209
+ </Paragraph>
210
+ <PaginationInput
211
+ odysseyDesignTokens={odysseyDesignTokens}
212
+ type="number"
213
+ value={rowsPerPage}
214
+ onChange={setRowsPerPageFromEvent}
215
+ onBlur={handlePaginationChange}
216
+ onKeyDown={handleRowsPerPageSubmit}
217
+ disabled={isDisabled}
218
+ inputProps={{
219
+ "aria-label": t("table.pagination.rowsperpage"),
220
+ }}
221
+ />
222
+ </Box>
223
+ <Paragraph component="span" color="textSecondary">
224
+ {totalRows ? (
225
+ <Trans
226
+ i18nKey="table.pagination.rowswithtotal"
227
+ values={{ firstRow, lastRow, totalRows }}
228
+ />
229
+ ) : (
230
+ <Trans
231
+ i18nKey="table.pagination.rowswithouttotal"
232
+ values={{ firstRow, lastRow }}
233
+ />
234
+ )}
235
+ </Paragraph>
236
+ </PaginationSegment>
237
+
238
+ <PaginationSegment odysseyDesignTokens={odysseyDesignTokens}>
239
+ {totalRows && (
240
+ <Box>
241
+ <Paragraph component="span" color="textSecondary">
242
+ {t("table.pagination.page")}
243
+ </Paragraph>
244
+ <PaginationInput
245
+ odysseyDesignTokens={odysseyDesignTokens}
246
+ type="number"
247
+ value={page}
248
+ onChange={setPageFromEvent}
249
+ onBlur={handlePaginationChange}
250
+ onKeyDown={handlePageSubmit}
251
+ disabled={isDisabled}
252
+ inputProps={{
253
+ "aria-label": t("table.pagination.page"),
254
+ }}
255
+ />
256
+ </Box>
257
+ )}
258
+ <PaginationButtonContainer>
259
+ <Button
260
+ startIcon={<ArrowLeftIcon />}
261
+ variant="floating"
262
+ size="small"
263
+ ariaLabel={t("table.pagination.previous")}
264
+ onClick={handlePreviousButton}
265
+ isDisabled={previousButtonDisabled}
266
+ />
267
+ <Button
268
+ endIcon={<ArrowRightIcon />}
269
+ variant="floating"
270
+ size="small"
271
+ ariaLabel={t("table.pagination.next")}
272
+ onClick={handleNextButton}
273
+ isDisabled={nextButtonDisabled}
274
+ />
275
+ </PaginationButtonContainer>
276
+ </PaginationSegment>
277
+ </PaginationContainer>
278
+ ) : (
279
+ <Button
280
+ variant="secondary"
281
+ label={t("table.pagination.loadmore")}
282
+ onClick={handleLoadMore}
283
+ isDisabled={loadMoreIsDisabled}
284
+ />
285
+ );
286
+ };
287
+
288
+ const MemoizedDataTablePagination = memo(DataTablePagination);
289
+ export { MemoizedDataTablePagination as DataTablePagination };
@@ -0,0 +1,122 @@
1
+ /*!
2
+ * Copyright (c) 2023-present, Okta, Inc. and/or its affiliates. All rights reserved.
3
+ * The Okta software accompanied by this notice is provided pursuant to the Apache License, Version 2.0 (the "License.")
4
+ *
5
+ * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0.
6
+ * Unless required by applicable law or agreed to in writing, software
7
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
8
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9
+ *
10
+ * See the License for the specific language governing permissions and limitations under the License.
11
+ */
12
+
13
+ import { MRT_Row, MRT_RowData } from "material-react-table";
14
+ import { Fragment, ReactElement, memo, useCallback } from "react";
15
+ import { Button } from "../Button";
16
+ import { MenuItem } from "../MenuItem";
17
+ import { Box as MuiBox } from "@mui/material";
18
+ import { MenuButton, MenuButtonProps } from "../MenuButton";
19
+ import {
20
+ ArrowBottomIcon,
21
+ ArrowDownIcon,
22
+ ArrowTopIcon,
23
+ ArrowUpIcon,
24
+ MoreIcon,
25
+ } from "../icons.generated";
26
+ import { DataTableProps } from "./DataTable";
27
+ import { Trans, useTranslation } from "react-i18next";
28
+
29
+ export type DataTableRowActionsProps = {
30
+ row: MRT_Row<MRT_RowData>;
31
+ rowIndex: number;
32
+ rowActionButtons?: (
33
+ row: MRT_RowData,
34
+ ) => ReactElement<typeof Button | typeof Fragment>;
35
+ rowActionMenuItems?: (row: MRT_RowData) => MenuButtonProps["children"];
36
+ totalRows?: DataTableProps["totalRows"];
37
+ updateRowOrder?: ({
38
+ rowId,
39
+ newRowIndex,
40
+ }: {
41
+ rowId: string;
42
+ newRowIndex: number;
43
+ }) => void;
44
+ };
45
+
46
+ const DataTableRowActions = ({
47
+ row,
48
+ rowIndex,
49
+ rowActionButtons,
50
+ rowActionMenuItems,
51
+ totalRows,
52
+ updateRowOrder,
53
+ }: DataTableRowActionsProps) => {
54
+ const { t } = useTranslation();
55
+
56
+ const handleToFrontClick = useCallback(() => {
57
+ updateRowOrder && updateRowOrder({ rowId: row.id, newRowIndex: 0 });
58
+ }, [row.id, updateRowOrder]);
59
+
60
+ const handleForwardClick = useCallback(() => {
61
+ updateRowOrder &&
62
+ updateRowOrder({ rowId: row.id, newRowIndex: Math.max(0, rowIndex - 1) });
63
+ }, [row.id, rowIndex, updateRowOrder]);
64
+
65
+ const handleBackwardClick = useCallback(() => {
66
+ updateRowOrder &&
67
+ updateRowOrder({ rowId: row.id, newRowIndex: rowIndex + 1 });
68
+ }, [row.id, rowIndex, updateRowOrder]);
69
+
70
+ const handleToBackClick = useCallback(() => {
71
+ updateRowOrder &&
72
+ updateRowOrder({
73
+ rowId: row.id,
74
+ newRowIndex: totalRows ? totalRows - 1 : rowIndex,
75
+ });
76
+ }, [row.id, rowIndex, totalRows, updateRowOrder]);
77
+
78
+ return (
79
+ <MuiBox display="flex">
80
+ {rowActionButtons?.(row)}
81
+ {(rowActionMenuItems || updateRowOrder) && (
82
+ <MenuButton
83
+ endIcon={<MoreIcon />}
84
+ size="small"
85
+ buttonVariant="floating"
86
+ ariaLabel={t("table.moreactions.arialabel")}
87
+ menuAlignment="right"
88
+ >
89
+ {rowActionMenuItems && <>{rowActionMenuItems(row)}</>}
90
+ {rowActionMenuItems && updateRowOrder && <hr />}
91
+ {updateRowOrder && (
92
+ <>
93
+ <MenuItem isDisabled={rowIndex <= 0} onClick={handleToFrontClick}>
94
+ <ArrowTopIcon /> <Trans i18nKey="table.reorder.tofront" />
95
+ </MenuItem>
96
+ <MenuItem isDisabled={rowIndex <= 0} onClick={handleForwardClick}>
97
+ <ArrowUpIcon /> <Trans i18nKey="table.reorder.forward" />
98
+ </MenuItem>
99
+ <MenuItem
100
+ isDisabled={totalRows ? rowIndex >= totalRows - 1 : false}
101
+ onClick={handleBackwardClick}
102
+ >
103
+ <ArrowDownIcon /> <Trans i18nKey="table.reorder.backward" />
104
+ </MenuItem>
105
+ {totalRows && (
106
+ <MenuItem
107
+ isDisabled={rowIndex >= totalRows - 1}
108
+ onClick={handleToBackClick}
109
+ >
110
+ <ArrowBottomIcon /> <Trans i18nKey="table.reorder.toback" />
111
+ </MenuItem>
112
+ )}
113
+ </>
114
+ )}
115
+ </MenuButton>
116
+ )}
117
+ </MuiBox>
118
+ );
119
+ };
120
+
121
+ const MemoizedDataTableRowActions = memo(DataTableRowActions);
122
+ export { MemoizedDataTableRowActions as DataTableRowActions };
@@ -0,0 +1,135 @@
1
+ /*!
2
+ * Copyright (c) 2023-present, Okta, Inc. and/or its affiliates. All rights reserved.
3
+ * The Okta software accompanied by this notice is provided pursuant to the Apache License, Version 2.0 (the "License.")
4
+ *
5
+ * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0.
6
+ * Unless required by applicable law or agreed to in writing, software
7
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
8
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9
+ *
10
+ * See the License for the specific language governing permissions and limitations under the License.
11
+ */
12
+
13
+ import { Dispatch, SetStateAction, memo, useCallback, useMemo } from "react";
14
+ import { Checkbox as MuiCheckbox } from "@mui/material";
15
+ import { MenuButton } from "../MenuButton";
16
+ import { MenuItem } from "../MenuItem";
17
+ import { ListIcon, ShowIcon } from "../icons.generated";
18
+ import { densityValues } from "./constants";
19
+ import { DataTableProps } from "./DataTable";
20
+ import { MRT_VisibilityState } from "material-react-table";
21
+ import { useTranslation } from "react-i18next";
22
+
23
+ export type DataTableSettingsProps = {
24
+ hasChangeableDensity: DataTableProps["hasChangeableDensity"];
25
+ rowDensity: (typeof densityValues)[number];
26
+ setRowDensity: Dispatch<SetStateAction<(typeof densityValues)[number]>>;
27
+ hasColumnVisibility: DataTableProps["hasColumnVisibility"];
28
+ columns: DataTableProps["columns"];
29
+ columnVisibility?: MRT_VisibilityState;
30
+ setColumnVisibility: Dispatch<
31
+ SetStateAction<MRT_VisibilityState | undefined>
32
+ >;
33
+ };
34
+
35
+ const DataTableSettings = ({
36
+ hasChangeableDensity,
37
+ rowDensity,
38
+ setRowDensity,
39
+ hasColumnVisibility,
40
+ columns,
41
+ columnVisibility,
42
+ setColumnVisibility,
43
+ }: DataTableSettingsProps) => {
44
+ const { t } = useTranslation();
45
+
46
+ const changeRowDensity = useCallback(
47
+ (value: (typeof densityValues)[number]) =>
48
+ (_event: React.MouseEvent<HTMLLIElement>) => {
49
+ // This is necessary to avoid linter errors, while the _event is necessary to satisfy the onClick type
50
+ if (process.env.NODE_ENV === "development") console.debug(_event);
51
+
52
+ setRowDensity(value);
53
+ },
54
+ [setRowDensity],
55
+ );
56
+
57
+ const changeColumnVisibility = useCallback(
58
+ (columnId: string) => (_event: React.MouseEvent<HTMLLIElement>) => {
59
+ // This is necessary to avoid linter errors, while the _event is necessary to satisfy the onClick type
60
+ if (process.env.NODE_ENV === "development") console.debug(_event);
61
+
62
+ setColumnVisibility((prevVisibility) => ({
63
+ ...prevVisibility,
64
+ [columnId]: prevVisibility ? prevVisibility[columnId] === false : false,
65
+ }));
66
+ },
67
+ [setColumnVisibility],
68
+ );
69
+
70
+ const isColumnVisibilityChecked = useMemo(() => {
71
+ return columns.reduce(
72
+ (acc, column) => {
73
+ const isChecked = columnVisibility
74
+ ? columnVisibility[column.accessorKey!] !== false
75
+ : true;
76
+ acc[column.accessorKey!] = isChecked;
77
+ return acc;
78
+ },
79
+ {} as Record<string, boolean>,
80
+ );
81
+ }, [columns, columnVisibility]);
82
+
83
+ return (
84
+ <>
85
+ {hasChangeableDensity && (
86
+ <MenuButton
87
+ endIcon={<ListIcon />}
88
+ ariaLabel={t("table.density.arialabel")}
89
+ menuAlignment="right"
90
+ shouldCloseOnSelect={false}
91
+ >
92
+ <>
93
+ {densityValues.map((value: (typeof densityValues)[number]) => (
94
+ <MenuItem
95
+ key={value}
96
+ isSelected={rowDensity === value}
97
+ onClick={changeRowDensity(value)}
98
+ >
99
+ {`${value.charAt(0).toUpperCase()}${value.slice(1)}`}
100
+ </MenuItem>
101
+ ))}
102
+ </>
103
+ </MenuButton>
104
+ )}
105
+
106
+ {hasColumnVisibility && (
107
+ <MenuButton
108
+ endIcon={<ShowIcon />}
109
+ ariaLabel={t("table.columnvisibility.arialabel")}
110
+ menuAlignment="right"
111
+ shouldCloseOnSelect={false}
112
+ >
113
+ <>
114
+ {columns
115
+ .filter((column) => column.enableHiding !== false)
116
+ .map((column) => (
117
+ <MenuItem
118
+ key={column.accessorKey}
119
+ onClick={changeColumnVisibility(column.id as string)}
120
+ >
121
+ <MuiCheckbox
122
+ checked={isColumnVisibilityChecked[column.accessorKey!]}
123
+ />
124
+ {column.header}
125
+ </MenuItem>
126
+ ))}
127
+ </>
128
+ </MenuButton>
129
+ )}
130
+ </>
131
+ );
132
+ };
133
+
134
+ const MemoizedDataTableSettings = memo(DataTableSettings);
135
+ export { MemoizedDataTableSettings as DataTableSettings };
@@ -0,0 +1,14 @@
1
+ /*!
2
+ * Copyright (c) 2023-present, Okta, Inc. and/or its affiliates. All rights reserved.
3
+ * The Okta software accompanied by this notice is provided pursuant to the Apache License, Version 2.0 (the "License.")
4
+ *
5
+ * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0.
6
+ * Unless required by applicable law or agreed to in writing, software
7
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
8
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9
+ *
10
+ * See the License for the specific language governing permissions and limitations under the License.
11
+ */
12
+
13
+ export const densityValues = ["comfortable", "spacious", "compact"] as const;
14
+ export const paginationTypeValues = ["paged", "loadMore"] as const;
@@ -0,0 +1,28 @@
1
+ /*!
2
+ * Copyright (c) 2023-present, Okta, Inc. and/or its affiliates. All rights reserved.
3
+ * The Okta software accompanied by this notice is provided pursuant to the Apache License, Version 2.0 (the "License.")
4
+ *
5
+ * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0.
6
+ * Unless required by applicable law or agreed to in writing, software
7
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
8
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9
+ *
10
+ * See the License for the specific language governing permissions and limitations under the License.
11
+ */
12
+
13
+ export {
14
+ DataTable,
15
+ type DataTableProps,
16
+ type DataTableGetDataType,
17
+ type DataTableOnReorderRowsType,
18
+ } from "./DataTable";
19
+ export { DataTableEmptyState } from "./DataTableEmptyState";
20
+ export { densityValues } from "./constants";
21
+ export type {
22
+ MRT_ColumnFiltersState as DataTableFiltersState,
23
+ MRT_SortingState as DataTableSortingState,
24
+ MRT_RowSelectionState as DataTableRowSelectionState,
25
+ MRT_ColumnDef as DataTableColumn,
26
+ MRT_RowData as DataTableRowData,
27
+ MRT_Row as DataTableRow,
28
+ } from "material-react-table";