@okta/odyssey-react-mui 1.14.7 → 1.15.8
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/CHANGELOG.md +54 -0
- package/dist/Autocomplete.js +84 -2
- package/dist/Autocomplete.js.map +1 -1
- package/dist/Breadcrumbs.js +13 -3
- package/dist/Breadcrumbs.js.map +1 -1
- package/dist/Button.js +2 -2
- package/dist/Button.js.map +1 -1
- package/dist/Callout.js.map +1 -1
- package/dist/Checkbox.js +8 -4
- package/dist/Checkbox.js.map +1 -1
- package/dist/CheckboxGroup.js.map +1 -1
- package/dist/DataTable/DataTable.js +37 -9
- package/dist/DataTable/DataTable.js.map +1 -1
- package/dist/DataTable/DataTableEmptyState.js +1 -0
- package/dist/DataTable/DataTableEmptyState.js.map +1 -1
- package/dist/DataTable/DataTablePagination.js +1 -0
- package/dist/DataTable/DataTablePagination.js.map +1 -1
- package/dist/DataTable/DataTableRowActions.js +1 -0
- package/dist/DataTable/DataTableRowActions.js.map +1 -1
- package/dist/DataTable/DataTableSettings.js +1 -0
- package/dist/DataTable/DataTableSettings.js.map +1 -1
- package/dist/MenuButton.js.map +1 -1
- package/dist/RadioGroup.js.map +1 -1
- package/dist/Select.js +3 -4
- package/dist/Select.js.map +1 -1
- package/dist/Tabs.js +21 -0
- package/dist/Tabs.js.map +1 -1
- package/dist/Tile.js +14 -18
- package/dist/Tile.js.map +1 -1
- package/dist/labs/DataFilters.js +98 -97
- package/dist/labs/DataFilters.js.map +1 -1
- package/dist/labs/StaticTable.js +1 -0
- package/dist/labs/StaticTable.js.map +1 -1
- package/dist/labs/index.js +0 -1
- package/dist/labs/index.js.map +1 -1
- package/dist/src/Autocomplete.d.ts +7 -1
- package/dist/src/Autocomplete.d.ts.map +1 -1
- package/dist/src/Breadcrumbs.d.ts +1 -1
- package/dist/src/Breadcrumbs.d.ts.map +1 -1
- package/dist/src/Button.d.ts +31 -15
- package/dist/src/Button.d.ts.map +1 -1
- package/dist/src/Callout.d.ts +1 -1
- package/dist/src/Checkbox.d.ts.map +1 -1
- package/dist/src/CheckboxGroup.d.ts +2 -3
- package/dist/src/CheckboxGroup.d.ts.map +1 -1
- package/dist/src/DataTable/DataTable.d.ts +6 -2
- package/dist/src/DataTable/DataTable.d.ts.map +1 -1
- package/dist/src/DataTable/DataTableEmptyState.d.ts.map +1 -1
- package/dist/src/DataTable/DataTablePagination.d.ts.map +1 -1
- package/dist/src/DataTable/DataTableRowActions.d.ts.map +1 -1
- package/dist/src/DataTable/DataTableSettings.d.ts.map +1 -1
- package/dist/src/MenuButton.d.ts +3 -4
- package/dist/src/MenuButton.d.ts.map +1 -1
- package/dist/src/RadioGroup.d.ts +3 -3
- package/dist/src/RadioGroup.d.ts.map +1 -1
- package/dist/src/Select.d.ts.map +1 -1
- package/dist/src/Tabs.d.ts.map +1 -1
- package/dist/src/Tile.d.ts.map +1 -1
- package/dist/src/labs/DataFilters.d.ts +9 -1
- package/dist/src/labs/DataFilters.d.ts.map +1 -1
- package/dist/src/labs/StaticTable.d.ts.map +1 -1
- package/dist/src/labs/index.d.ts +0 -1
- package/dist/src/labs/index.d.ts.map +1 -1
- package/dist/src/theme/components.d.ts.map +1 -1
- package/dist/theme/components.js +15 -7
- package/dist/theme/components.js.map +1 -1
- package/dist/tsconfig.production.tsbuildinfo +1 -1
- package/package.json +9 -7
- package/src/Autocomplete.tsx +124 -1
- package/src/Breadcrumbs.tsx +16 -3
- package/src/Button.tsx +32 -16
- package/src/Callout.tsx +1 -1
- package/src/Checkbox.tsx +4 -3
- package/src/CheckboxGroup.tsx +2 -5
- package/src/DataTable/DataTable.tsx +75 -13
- package/src/DataTable/DataTableEmptyState.tsx +2 -0
- package/src/DataTable/DataTablePagination.tsx +2 -0
- package/src/DataTable/DataTableRowActions.tsx +2 -0
- package/src/DataTable/DataTableSettings.tsx +2 -0
- package/src/MenuButton.tsx +11 -21
- package/src/RadioGroup.tsx +3 -3
- package/src/Select.tsx +5 -2
- package/src/Tabs.tsx +40 -1
- package/src/Tile.tsx +12 -14
- package/src/labs/DataFilters.tsx +211 -177
- package/src/labs/StaticTable.tsx +4 -0
- package/src/labs/index.ts +0 -1
- package/src/theme/components.tsx +16 -7
- package/dist/labs/VirtualizedAutocomplete.js +0 -134
- package/dist/labs/VirtualizedAutocomplete.js.map +0 -1
- package/dist/src/labs/VirtualizedAutocomplete.d.ts +0 -90
- package/dist/src/labs/VirtualizedAutocomplete.d.ts.map +0 -1
- package/src/labs/VirtualizedAutocomplete.tsx +0 -365
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@okta/odyssey-react-mui",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.15.8",
|
|
4
4
|
"description": "React MUI components for Odyssey, Okta's design system",
|
|
5
5
|
"author": "Okta, Inc.",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -43,25 +43,27 @@
|
|
|
43
43
|
},
|
|
44
44
|
"dependencies": {
|
|
45
45
|
"@emotion/cache": "^11.11.0",
|
|
46
|
-
"@emotion/react": "^11.11.
|
|
46
|
+
"@emotion/react": "^11.11.4",
|
|
47
47
|
"@emotion/styled": "^11.11.0",
|
|
48
48
|
"@mui/icons-material": "^5.15.10",
|
|
49
49
|
"@mui/lab": "^5.0.0-alpha.165",
|
|
50
|
-
"@mui/material": "^5.15.
|
|
50
|
+
"@mui/material": "^5.15.12",
|
|
51
51
|
"@mui/system": "^5.15.9",
|
|
52
52
|
"@mui/utils": "^5.15.9",
|
|
53
53
|
"@mui/x-date-pickers": "^5.0.15",
|
|
54
|
-
"@okta/odyssey-design-tokens": "^1.
|
|
54
|
+
"@okta/odyssey-design-tokens": "^1.15.8",
|
|
55
55
|
"date-fns": "^2.30.0",
|
|
56
56
|
"i18next": "^23.8.2",
|
|
57
57
|
"material-react-table": "^2.11.3",
|
|
58
58
|
"react-i18next": "^14.0.5",
|
|
59
|
+
"react-virtualized-auto-sizer": "^1.0.22",
|
|
60
|
+
"react-window": "^1.8.10",
|
|
59
61
|
"tsx": "^4.7.1",
|
|
60
62
|
"word-wrap": "^1.2.5"
|
|
61
63
|
},
|
|
62
64
|
"peerDependencies": {
|
|
63
|
-
"react": "
|
|
64
|
-
"react-dom": "
|
|
65
|
+
"react": "^18.2.0",
|
|
66
|
+
"react-dom": "^18.2.0"
|
|
65
67
|
},
|
|
66
|
-
"gitHead": "
|
|
68
|
+
"gitHead": "7ce4fb11fd0b464ca6f1476e9cfaa0d320bc7d2e"
|
|
67
69
|
}
|
package/src/Autocomplete.tsx
CHANGED
|
@@ -18,7 +18,29 @@ import {
|
|
|
18
18
|
AutocompleteValue,
|
|
19
19
|
AutocompleteRenderInputParams,
|
|
20
20
|
} from "@mui/material";
|
|
21
|
-
import {
|
|
21
|
+
import {
|
|
22
|
+
createContext,
|
|
23
|
+
FC,
|
|
24
|
+
forwardRef,
|
|
25
|
+
HTMLAttributes,
|
|
26
|
+
memo,
|
|
27
|
+
ReactElement,
|
|
28
|
+
useCallback,
|
|
29
|
+
useContext,
|
|
30
|
+
useEffect,
|
|
31
|
+
useMemo,
|
|
32
|
+
useRef,
|
|
33
|
+
} from "react";
|
|
34
|
+
import styled from "@emotion/styled";
|
|
35
|
+
import { VariableSizeList, ListChildComponentProps } from "react-window";
|
|
36
|
+
import _AutoSizer, {
|
|
37
|
+
Props as AutoSizerProps,
|
|
38
|
+
Size as AutoSizerSize,
|
|
39
|
+
} from "react-virtualized-auto-sizer";
|
|
40
|
+
|
|
41
|
+
// This is required to get around a react-types issue for "AutoSizer is not a valid JSX element."
|
|
42
|
+
// @see https://github.com/bvaughn/react-virtualized/issues/1739#issuecomment-1291444246
|
|
43
|
+
const AutoSizer = _AutoSizer as unknown as FC<AutoSizerProps>;
|
|
22
44
|
|
|
23
45
|
import { Field } from "./Field";
|
|
24
46
|
import { FieldComponentProps } from "./FieldComponentProps";
|
|
@@ -160,6 +182,13 @@ export type AutocompleteProps<
|
|
|
160
182
|
* You will need to implement this function if your `option` items are objects.
|
|
161
183
|
*/
|
|
162
184
|
getIsOptionEqualToValue?: (option: OptionType, value: OptionType) => boolean;
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* If this component is required to display a virtualized list of options,
|
|
188
|
+
* then this value needs to be set to true.
|
|
189
|
+
* It is recommended if you're options are on the order of 10's of hundreds or more.
|
|
190
|
+
*/
|
|
191
|
+
isVirtualized?: boolean;
|
|
163
192
|
} & Pick<
|
|
164
193
|
FieldComponentProps,
|
|
165
194
|
| "errorMessage"
|
|
@@ -173,6 +202,11 @@ export type AutocompleteProps<
|
|
|
173
202
|
> &
|
|
174
203
|
Pick<HtmlProps, "ariaDescribedBy" | "testId" | "translate">;
|
|
175
204
|
|
|
205
|
+
const ListboxContainer = styled.div`
|
|
206
|
+
width: 100%;
|
|
207
|
+
height: 100%;
|
|
208
|
+
`;
|
|
209
|
+
|
|
176
210
|
const Autocomplete = <
|
|
177
211
|
OptionType,
|
|
178
212
|
HasMultipleChoices extends boolean | undefined,
|
|
@@ -191,6 +225,7 @@ const Autocomplete = <
|
|
|
191
225
|
isLoading,
|
|
192
226
|
isOptional = false,
|
|
193
227
|
isReadOnly,
|
|
228
|
+
isVirtualized: isVirtualizedProp = false,
|
|
194
229
|
hint,
|
|
195
230
|
HintLinkComponent,
|
|
196
231
|
label,
|
|
@@ -211,6 +246,8 @@ const Autocomplete = <
|
|
|
211
246
|
uncontrolledValue: defaultValue,
|
|
212
247
|
}),
|
|
213
248
|
);
|
|
249
|
+
|
|
250
|
+
const isVirtualized = useRef(Boolean(isVirtualizedProp));
|
|
214
251
|
const defaultValueProp = useMemo<
|
|
215
252
|
| AutocompleteValue<
|
|
216
253
|
OptionType,
|
|
@@ -261,6 +298,7 @@ const Autocomplete = <
|
|
|
261
298
|
hasVisibleLabel
|
|
262
299
|
//@ts-expect-error htmlFor does not exist ont he InputLabelProps for autocomplete
|
|
263
300
|
id={InputLabelProps.htmlFor}
|
|
301
|
+
isFullWidth={isFullWidth}
|
|
264
302
|
hint={hint}
|
|
265
303
|
HintLinkComponent={HintLinkComponent}
|
|
266
304
|
label={label}
|
|
@@ -294,12 +332,95 @@ const Autocomplete = <
|
|
|
294
332
|
errorMessageList,
|
|
295
333
|
hint,
|
|
296
334
|
HintLinkComponent,
|
|
335
|
+
isFullWidth,
|
|
297
336
|
isOptional,
|
|
298
337
|
label,
|
|
299
338
|
nameOverride,
|
|
300
339
|
testId,
|
|
301
340
|
],
|
|
302
341
|
);
|
|
342
|
+
|
|
343
|
+
const renderVirtualizedRow = ({
|
|
344
|
+
data,
|
|
345
|
+
index,
|
|
346
|
+
style,
|
|
347
|
+
}: ListChildComponentProps) => {
|
|
348
|
+
const baseOption = data[index];
|
|
349
|
+
/**
|
|
350
|
+
* react-window calculates the absolute positions of the list items, via an inline style, so
|
|
351
|
+
* we need to add it to each list item that is being rendered in the viewable list window.
|
|
352
|
+
* See here if you need to know more: https://github.com/bvaughn/react-window?tab=readme-ov-file#why-is-my-list-blank-when-i-scroll
|
|
353
|
+
*/
|
|
354
|
+
const optionItem = { ...baseOption, props: { ...baseOption.props, style } };
|
|
355
|
+
return optionItem;
|
|
356
|
+
};
|
|
357
|
+
|
|
358
|
+
const OuterListboxContext = createContext({});
|
|
359
|
+
|
|
360
|
+
const OuterListboxElementType = forwardRef<HTMLDivElement>((props, ref) => {
|
|
361
|
+
const outerProps = useContext(OuterListboxContext);
|
|
362
|
+
return <div ref={ref} {...props} {...outerProps} />;
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
function useResetCache(length: number) {
|
|
366
|
+
const ref = useRef<VariableSizeList>(null);
|
|
367
|
+
useEffect(() => {
|
|
368
|
+
if (ref.current) {
|
|
369
|
+
ref.current.resetAfterIndex(0, true);
|
|
370
|
+
}
|
|
371
|
+
}, [length]);
|
|
372
|
+
return ref;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
const ListboxComponent = forwardRef<
|
|
376
|
+
HTMLDivElement,
|
|
377
|
+
HTMLAttributes<HTMLElement>
|
|
378
|
+
>(function (props, ref) {
|
|
379
|
+
const { children, ...other } = props;
|
|
380
|
+
const itemData: ReactElement[] = (children as ReactElement[]).flatMap(
|
|
381
|
+
(item: ReactElement & { children?: ReactElement[] }) =>
|
|
382
|
+
[item].concat(item.children || []),
|
|
383
|
+
);
|
|
384
|
+
|
|
385
|
+
// the height of an Odyssey autocomplete option item that is used to calculate height of window
|
|
386
|
+
const optionHeight = 45; //px
|
|
387
|
+
|
|
388
|
+
// The number of items (rows or columns) to render outside of the visible area for performance and scrolling reasons
|
|
389
|
+
const overscanRowCount = 8;
|
|
390
|
+
|
|
391
|
+
const itemSize = useCallback(() => optionHeight, []);
|
|
392
|
+
|
|
393
|
+
const gridRef = useResetCache(itemData.length);
|
|
394
|
+
|
|
395
|
+
const renderWindow = useCallback(
|
|
396
|
+
({ height, width }: AutoSizerSize) => (
|
|
397
|
+
<VariableSizeList
|
|
398
|
+
innerElementType="ul"
|
|
399
|
+
itemData={itemData}
|
|
400
|
+
itemCount={itemData.length}
|
|
401
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
402
|
+
itemSize={itemSize}
|
|
403
|
+
height={height}
|
|
404
|
+
width={width}
|
|
405
|
+
ref={gridRef}
|
|
406
|
+
outerElementType={OuterListboxElementType}
|
|
407
|
+
overscanCount={overscanRowCount}
|
|
408
|
+
>
|
|
409
|
+
{renderVirtualizedRow}
|
|
410
|
+
</VariableSizeList>
|
|
411
|
+
),
|
|
412
|
+
[itemData, gridRef, itemSize],
|
|
413
|
+
);
|
|
414
|
+
|
|
415
|
+
return (
|
|
416
|
+
<ListboxContainer ref={ref}>
|
|
417
|
+
<OuterListboxContext.Provider value={other}>
|
|
418
|
+
<AutoSizer>{renderWindow}</AutoSizer>
|
|
419
|
+
</OuterListboxContext.Provider>
|
|
420
|
+
</ListboxContainer>
|
|
421
|
+
);
|
|
422
|
+
});
|
|
423
|
+
|
|
303
424
|
const onChange = useCallback<
|
|
304
425
|
NonNullable<
|
|
305
426
|
UseAutocompleteProps<
|
|
@@ -336,6 +457,8 @@ const Autocomplete = <
|
|
|
336
457
|
<MuiAutocomplete
|
|
337
458
|
{...valueProps}
|
|
338
459
|
{...inputValueProp}
|
|
460
|
+
// conditionally provide the ListboxComponent if this needs to be virtualized
|
|
461
|
+
{...(isVirtualized.current && { ListboxComponent })}
|
|
339
462
|
// AutoComplete is wrapped in a div within MUI which does not get the disabled attr. So this aria-disabled gets set in the div
|
|
340
463
|
aria-disabled={isDisabled}
|
|
341
464
|
disableCloseOnSelect={hasMultipleChoices}
|
package/src/Breadcrumbs.tsx
CHANGED
|
@@ -30,6 +30,7 @@ import { GroupIcon, HomeIcon, UserIcon } from "./icons.generated";
|
|
|
30
30
|
import { Subordinate } from "./Typography";
|
|
31
31
|
import { useTranslation } from "react-i18next";
|
|
32
32
|
import { HtmlProps } from "./HtmlProps";
|
|
33
|
+
import styled from "@emotion/styled";
|
|
33
34
|
|
|
34
35
|
export type BreadcrumbType = "listItem" | "menuItem" | "currentPage";
|
|
35
36
|
|
|
@@ -53,6 +54,13 @@ export const BreadcrumbContext = createContext<BreadcrumbContextType>({
|
|
|
53
54
|
breadcrumbType: "listItem",
|
|
54
55
|
});
|
|
55
56
|
|
|
57
|
+
const BreadcrumbContent = styled.span`
|
|
58
|
+
white-space: nowrap;
|
|
59
|
+
overflow: hidden;
|
|
60
|
+
max-width: 10rem;
|
|
61
|
+
text-overflow: ellipsis;
|
|
62
|
+
`;
|
|
63
|
+
|
|
56
64
|
export const Breadcrumb = ({ children, href, iconName }: BreadcrumbProps) => {
|
|
57
65
|
const { breadcrumbType } = useContext(BreadcrumbContext);
|
|
58
66
|
|
|
@@ -63,7 +71,7 @@ export const Breadcrumb = ({ children, href, iconName }: BreadcrumbProps) => {
|
|
|
63
71
|
) : iconName === "user" ? (
|
|
64
72
|
<UserIcon />
|
|
65
73
|
) : null}
|
|
66
|
-
{children}
|
|
74
|
+
<BreadcrumbContent>{children}</BreadcrumbContent>
|
|
67
75
|
</>
|
|
68
76
|
);
|
|
69
77
|
|
|
@@ -101,7 +109,7 @@ const defaultTruncationValue = 5;
|
|
|
101
109
|
const BreadcrumbList = ({
|
|
102
110
|
children,
|
|
103
111
|
homeHref,
|
|
104
|
-
maxVisibleItems = defaultTruncationValue,
|
|
112
|
+
maxVisibleItems: maxVisibleItemsProp = defaultTruncationValue,
|
|
105
113
|
testId,
|
|
106
114
|
translate,
|
|
107
115
|
}: BreadcrumbsProps) => {
|
|
@@ -109,6 +117,11 @@ const BreadcrumbList = ({
|
|
|
109
117
|
|
|
110
118
|
const [anchorEl, setAnchorEl] = useState<HTMLElement | null>(null);
|
|
111
119
|
|
|
120
|
+
const maxVisibleItems = useMemo(
|
|
121
|
+
() => Math.min(Math.max(maxVisibleItemsProp, 0), children.length),
|
|
122
|
+
[maxVisibleItemsProp, children],
|
|
123
|
+
);
|
|
124
|
+
|
|
112
125
|
const breadcrumbSections = useMemo(() => {
|
|
113
126
|
if (children.length <= maxVisibleItems) {
|
|
114
127
|
return {
|
|
@@ -155,7 +168,7 @@ const BreadcrumbList = ({
|
|
|
155
168
|
</BreadcrumbContext.Provider>
|
|
156
169
|
))}
|
|
157
170
|
|
|
158
|
-
{breadcrumbSections.insideMenu && (
|
|
171
|
+
{breadcrumbSections.insideMenu.length > 0 && (
|
|
159
172
|
<>
|
|
160
173
|
<ButtonBase onClick={onMenuButtonClick}>...</ButtonBase>
|
|
161
174
|
<Menu
|
package/src/Button.tsx
CHANGED
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
import { Button as MuiButton } from "@mui/material";
|
|
14
14
|
import type { ButtonProps as MuiButtonProps } from "@mui/material";
|
|
15
15
|
import {
|
|
16
|
+
HTMLAttributes,
|
|
16
17
|
memo,
|
|
17
18
|
ReactElement,
|
|
18
19
|
useCallback,
|
|
@@ -45,10 +46,6 @@ export type ButtonProps = {
|
|
|
45
46
|
* The ref forwarded to the Button
|
|
46
47
|
*/
|
|
47
48
|
buttonRef?: React.RefObject<FocusHandle>;
|
|
48
|
-
/**
|
|
49
|
-
* The icon element to display at the end of the Button
|
|
50
|
-
*/
|
|
51
|
-
endIcon?: ReactElement;
|
|
52
49
|
/**
|
|
53
50
|
* The ID of the Button
|
|
54
51
|
*/
|
|
@@ -61,10 +58,6 @@ export type ButtonProps = {
|
|
|
61
58
|
* Determines whether the Button should take up the full available width
|
|
62
59
|
*/
|
|
63
60
|
isFullWidth?: boolean;
|
|
64
|
-
/**
|
|
65
|
-
* The text content of the Button
|
|
66
|
-
*/
|
|
67
|
-
label?: string;
|
|
68
61
|
/**
|
|
69
62
|
* The click event handler for the Button
|
|
70
63
|
*/
|
|
@@ -73,10 +66,7 @@ export type ButtonProps = {
|
|
|
73
66
|
* The size of the button
|
|
74
67
|
*/
|
|
75
68
|
size?: (typeof buttonSizeValues)[number];
|
|
76
|
-
|
|
77
|
-
* The icon element to display at the start of the Button
|
|
78
|
-
*/
|
|
79
|
-
startIcon?: ReactElement;
|
|
69
|
+
tabIndex?: HTMLAttributes<HTMLElement>["tabIndex"];
|
|
80
70
|
/**
|
|
81
71
|
* The tooltip text for the Button if it's icon-only
|
|
82
72
|
*/
|
|
@@ -91,18 +81,45 @@ export type ButtonProps = {
|
|
|
91
81
|
variant: (typeof buttonVariantValues)[number] | "tertiary";
|
|
92
82
|
} & (
|
|
93
83
|
| {
|
|
84
|
+
/**
|
|
85
|
+
* The icon element to display at the end of the Button
|
|
86
|
+
*/
|
|
94
87
|
endIcon?: ReactElement;
|
|
88
|
+
/**
|
|
89
|
+
* The text content of the Button
|
|
90
|
+
*/
|
|
95
91
|
label: string;
|
|
92
|
+
/**
|
|
93
|
+
* The icon element to display at the start of the Button
|
|
94
|
+
*/
|
|
96
95
|
startIcon?: ReactElement;
|
|
97
96
|
}
|
|
98
97
|
| {
|
|
98
|
+
/**
|
|
99
|
+
* The icon element to display at the end of the Button
|
|
100
|
+
*/
|
|
99
101
|
endIcon?: ReactElement;
|
|
100
|
-
|
|
102
|
+
/**
|
|
103
|
+
* The text content of the Button
|
|
104
|
+
*/
|
|
105
|
+
label?: string | "" | undefined;
|
|
106
|
+
/**
|
|
107
|
+
* The icon element to display at the start of the Button
|
|
108
|
+
*/
|
|
101
109
|
startIcon: ReactElement;
|
|
102
110
|
}
|
|
103
111
|
| {
|
|
112
|
+
/**
|
|
113
|
+
* The icon element to display at the end of the Button
|
|
114
|
+
*/
|
|
104
115
|
endIcon: ReactElement;
|
|
105
|
-
|
|
116
|
+
/**
|
|
117
|
+
* The text content of the Button
|
|
118
|
+
*/
|
|
119
|
+
label?: never;
|
|
120
|
+
/**
|
|
121
|
+
* The icon element to display at the start of the Button
|
|
122
|
+
*/
|
|
106
123
|
startIcon?: ReactElement;
|
|
107
124
|
}
|
|
108
125
|
) &
|
|
@@ -184,7 +201,7 @@ const Button = ({
|
|
|
184
201
|
data-se={testId}
|
|
185
202
|
disabled={isDisabled}
|
|
186
203
|
endIcon={endIcon}
|
|
187
|
-
fullWidth={
|
|
204
|
+
fullWidth={isFullWidth}
|
|
188
205
|
id={id}
|
|
189
206
|
onClick={onClick}
|
|
190
207
|
ref={localButtonRef}
|
|
@@ -210,7 +227,6 @@ const Button = ({
|
|
|
210
227
|
id,
|
|
211
228
|
isDisabled,
|
|
212
229
|
isFullWidth,
|
|
213
|
-
buttonContext.isFullWidth,
|
|
214
230
|
label,
|
|
215
231
|
onClick,
|
|
216
232
|
size,
|
package/src/Callout.tsx
CHANGED
|
@@ -34,7 +34,7 @@ export const calloutSeverityValues = [
|
|
|
34
34
|
|
|
35
35
|
export type CalloutProps = {
|
|
36
36
|
/**
|
|
37
|
-
*
|
|
37
|
+
* Used to optionally pass a text list to the component
|
|
38
38
|
*/
|
|
39
39
|
children?: ReactNode;
|
|
40
40
|
/**
|
package/src/Checkbox.tsx
CHANGED
|
@@ -115,7 +115,7 @@ const Checkbox = ({
|
|
|
115
115
|
const label = useMemo(() => {
|
|
116
116
|
return (
|
|
117
117
|
<>
|
|
118
|
-
{labelProp}
|
|
118
|
+
<Typography component="span">{labelProp}</Typography>
|
|
119
119
|
{isRequired && (
|
|
120
120
|
<>
|
|
121
121
|
{" "}
|
|
@@ -164,10 +164,11 @@ const Checkbox = ({
|
|
|
164
164
|
inputProps={{
|
|
165
165
|
"data-se": testId,
|
|
166
166
|
}}
|
|
167
|
+
disabled={isDisabled}
|
|
167
168
|
inputRef={localInputRef}
|
|
168
|
-
sx={
|
|
169
|
+
sx={{
|
|
169
170
|
marginBlockStart: "2px",
|
|
170
|
-
}
|
|
171
|
+
}}
|
|
171
172
|
/>
|
|
172
173
|
}
|
|
173
174
|
disabled={isDisabled}
|
package/src/CheckboxGroup.tsx
CHANGED
|
@@ -11,9 +11,8 @@
|
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
import { FormGroup as MuiFormGroup } from "@mui/material";
|
|
14
|
-
import { memo,
|
|
14
|
+
import { memo, ReactNode, useCallback } from "react";
|
|
15
15
|
|
|
16
|
-
import { Checkbox } from "./Checkbox";
|
|
17
16
|
import { Field } from "./Field";
|
|
18
17
|
import {
|
|
19
18
|
FieldComponentProps,
|
|
@@ -25,9 +24,7 @@ export type CheckboxGroupProps = {
|
|
|
25
24
|
/**
|
|
26
25
|
* A single Checkbox element or an array of Checkbox elements
|
|
27
26
|
*/
|
|
28
|
-
children:
|
|
29
|
-
| ReactElement<typeof Checkbox>
|
|
30
|
-
| Array<ReactElement<typeof Checkbox>>;
|
|
27
|
+
children: ReactNode;
|
|
31
28
|
/**
|
|
32
29
|
* If `true`, the CheckboxGroup is required
|
|
33
30
|
*/
|
|
@@ -49,7 +49,11 @@ import { useRowReordering } from "./useRowReordering";
|
|
|
49
49
|
import { DataTableSettings } from "./DataTableSettings";
|
|
50
50
|
import { MenuButton, MenuButtonProps } from "../MenuButton";
|
|
51
51
|
import { Box } from "../Box";
|
|
52
|
-
import {
|
|
52
|
+
import {
|
|
53
|
+
DataTableColumn,
|
|
54
|
+
DataTableRowData,
|
|
55
|
+
DataTableRowSelectionState,
|
|
56
|
+
} from ".";
|
|
53
57
|
import {
|
|
54
58
|
DesignTokens,
|
|
55
59
|
useOdysseyDesignTokens,
|
|
@@ -201,6 +205,10 @@ export type DataTableProps = {
|
|
|
201
205
|
* The component to display when the query returns no results
|
|
202
206
|
*/
|
|
203
207
|
noResultsPlaceholder?: ReactNode;
|
|
208
|
+
/**
|
|
209
|
+
* An optional set of filters to render in the filters menu
|
|
210
|
+
*/
|
|
211
|
+
filters?: Array<DataFilter | DataTableColumn<DataTableRowData> | string>;
|
|
204
212
|
};
|
|
205
213
|
|
|
206
214
|
const displayColumnDefOptions = {
|
|
@@ -344,6 +352,7 @@ const DataTable = ({
|
|
|
344
352
|
errorMessage: errorMessageProp,
|
|
345
353
|
emptyPlaceholder,
|
|
346
354
|
noResultsPlaceholder,
|
|
355
|
+
filters: filtersProp,
|
|
347
356
|
}: DataTableProps) => {
|
|
348
357
|
const [data, setData] = useState<MRT_RowData[]>([]);
|
|
349
358
|
const [pagination, setPagination] = useState({
|
|
@@ -445,21 +454,74 @@ const DataTable = ({
|
|
|
445
454
|
],
|
|
446
455
|
);
|
|
447
456
|
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
457
|
+
/**
|
|
458
|
+
* This hack is to provide compatibility with Material-React-Table's
|
|
459
|
+
* filterOptions format, which allows for strings and { label: string, value: string }
|
|
460
|
+
*/
|
|
461
|
+
const convertFilterSelectOptions = useCallback(
|
|
462
|
+
(options: DataTableColumn<DataTableRowData>["filterSelectOptions"]) =>
|
|
463
|
+
options?.map((option) =>
|
|
464
|
+
typeof option === "string"
|
|
465
|
+
? {
|
|
466
|
+
label: option,
|
|
467
|
+
value: option,
|
|
468
|
+
}
|
|
469
|
+
: {
|
|
470
|
+
// If the option isn't a string, it must have value and/or option defined
|
|
471
|
+
// If either is undefined, use the other
|
|
472
|
+
label: option.label ?? option.value,
|
|
473
|
+
value: option.value ?? option.label,
|
|
474
|
+
},
|
|
475
|
+
),
|
|
476
|
+
[],
|
|
477
|
+
);
|
|
478
|
+
|
|
479
|
+
const convertColumnToFilter = useCallback(
|
|
480
|
+
(column: DataTableColumn<DataTableRowData>) =>
|
|
481
|
+
column.enableColumnFilter !== false && column.accessorKey
|
|
482
|
+
? ({
|
|
483
|
+
id: column.accessorKey,
|
|
455
484
|
label: column.header,
|
|
456
|
-
variant: column.filterVariant
|
|
457
|
-
options: column.filterSelectOptions,
|
|
458
|
-
} as DataFilter
|
|
459
|
-
|
|
460
|
-
[
|
|
485
|
+
variant: column.filterVariant,
|
|
486
|
+
options: convertFilterSelectOptions(column.filterSelectOptions),
|
|
487
|
+
} satisfies DataFilter as DataFilter)
|
|
488
|
+
: null,
|
|
489
|
+
[convertFilterSelectOptions],
|
|
461
490
|
);
|
|
462
491
|
|
|
492
|
+
/**
|
|
493
|
+
* Filters default to the columns, but can be overridden
|
|
494
|
+
* with the `filters` prop. `filters` should be an array
|
|
495
|
+
* of column accessorKeys, column defs, or DataFilters.
|
|
496
|
+
*/
|
|
497
|
+
const dataTableFilters = useMemo(() => {
|
|
498
|
+
const providedFilters = filtersProp || columns;
|
|
499
|
+
return providedFilters.reduce<DataFilter[]>((accumulator, item) => {
|
|
500
|
+
if (typeof item === "string") {
|
|
501
|
+
const foundColumn = columns.find(
|
|
502
|
+
(column) => column.accessorKey === item,
|
|
503
|
+
);
|
|
504
|
+
if (foundColumn) {
|
|
505
|
+
const filter = convertColumnToFilter(foundColumn);
|
|
506
|
+
if (filter) {
|
|
507
|
+
return accumulator.concat(filter);
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
} else if ("accessorKey" in item) {
|
|
511
|
+
// Checks if it's a column
|
|
512
|
+
const filter = convertColumnToFilter(item);
|
|
513
|
+
if (filter) {
|
|
514
|
+
return accumulator.concat(filter);
|
|
515
|
+
}
|
|
516
|
+
} else if ("label" in item) {
|
|
517
|
+
// Checks if it's a DataFilter
|
|
518
|
+
return accumulator.concat(item);
|
|
519
|
+
}
|
|
520
|
+
// If none of the conditions match, item is ignored (not mapping to undefined)
|
|
521
|
+
return accumulator;
|
|
522
|
+
}, []);
|
|
523
|
+
}, [columns, filtersProp, convertColumnToFilter]);
|
|
524
|
+
|
|
463
525
|
const defaultCell = useCallback(
|
|
464
526
|
({ cell }: { cell: MRT_Cell<MRT_RowData> }) => {
|
|
465
527
|
const value = cell.getValue<string>();
|
package/src/MenuButton.tsx
CHANGED
|
@@ -10,21 +10,17 @@
|
|
|
10
10
|
* See the License for the specific language governing permissions and limitations under the License.
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
-
import { memo, type ReactElement, useCallback, useMemo, useState } from "react";
|
|
14
13
|
import {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
MenuItem,
|
|
26
|
-
useUniqueId,
|
|
27
|
-
} from "./";
|
|
14
|
+
memo,
|
|
15
|
+
type ReactElement,
|
|
16
|
+
useCallback,
|
|
17
|
+
useMemo,
|
|
18
|
+
useState,
|
|
19
|
+
ReactNode,
|
|
20
|
+
} from "react";
|
|
21
|
+
import { Menu as MuiMenu, PopoverOrigin } from "@mui/material";
|
|
22
|
+
|
|
23
|
+
import { Button, buttonSizeValues, buttonVariantValues, useUniqueId } from "./";
|
|
28
24
|
import { ChevronDownIcon, MoreIcon } from "./icons.generated";
|
|
29
25
|
import { FieldComponentProps } from "./FieldComponentProps";
|
|
30
26
|
import { MenuContext, MenuContextType } from "./MenuContext";
|
|
@@ -45,13 +41,7 @@ export type MenuButtonProps = {
|
|
|
45
41
|
/**
|
|
46
42
|
* The <MenuItem> components within the Menu.
|
|
47
43
|
*/
|
|
48
|
-
children:
|
|
49
|
-
| ReactElement<typeof MenuItem | typeof Divider | typeof ListSubheader>
|
|
50
|
-
| Array<
|
|
51
|
-
| ReactElement<typeof MenuItem | typeof Divider | typeof ListSubheader>
|
|
52
|
-
| NullElement
|
|
53
|
-
>
|
|
54
|
-
| NullElement;
|
|
44
|
+
children: ReactNode | NullElement;
|
|
55
45
|
/**
|
|
56
46
|
* The end Icon on the trigggering Button
|
|
57
47
|
*/
|
package/src/RadioGroup.tsx
CHANGED
|
@@ -14,9 +14,9 @@ import {
|
|
|
14
14
|
RadioGroup as MuiRadioGroup,
|
|
15
15
|
type RadioGroupProps as MuiRadioGroupProps,
|
|
16
16
|
} from "@mui/material";
|
|
17
|
-
import { memo,
|
|
17
|
+
import { memo, ReactNode, useCallback, useRef } from "react";
|
|
18
18
|
|
|
19
|
-
import {
|
|
19
|
+
import { RadioProps } from "./Radio";
|
|
20
20
|
import { Field } from "./Field";
|
|
21
21
|
import {
|
|
22
22
|
FieldComponentProps,
|
|
@@ -29,7 +29,7 @@ export type RadioGroupProps = {
|
|
|
29
29
|
/**
|
|
30
30
|
* The Radio components within the group. Must include two or more.
|
|
31
31
|
*/
|
|
32
|
-
children:
|
|
32
|
+
children: ReactNode;
|
|
33
33
|
/**
|
|
34
34
|
* The text value of the Radio that should be selected by default
|
|
35
35
|
*/
|