@databiosphere/findable-ui 21.2.0 → 21.4.0
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/.release-please-manifest.json +1 -1
- package/CHANGELOG.md +15 -0
- package/lib/common/entities.d.ts +33 -0
- package/lib/components/DataDictionary/common/utils.d.ts +38 -0
- package/lib/components/DataDictionary/common/utils.js +122 -0
- package/lib/components/Export/components/ExportForm/components/ExportButton/exportButton.js +6 -1
- package/lib/components/Export/components/ManifestDownload/components/ManifestDownloadEntity/components/FileManifestDownload/fileManifestDownload.js +5 -2
- package/lib/components/Filter/components/Filter/filter.js +1 -1
- package/lib/components/Filter/components/FilterLabel/filterLabel.d.ts +3 -1
- package/lib/components/Filter/components/FilterLabel/filterLabel.js +4 -2
- package/lib/components/Index/components/AzulFileDownload/azulFileDownload.js +10 -5
- package/lib/components/Index/components/Tabs/common/utils.js +2 -1
- package/lib/components/Layout/components/Header/components/Content/components/Navigation/components/NavigationMenu/navigationMenu.js +1 -1
- package/lib/components/Layout/components/Header/components/Content/components/Navigation/components/NavigationMenuItems/navigationMenuItems.js +20 -21
- package/lib/components/Layout/components/Header/components/Content/components/Navigation/constants.d.ts +1 -0
- package/lib/components/Layout/components/Header/components/Content/components/Navigation/constants.js +1 -0
- package/lib/components/Layout/components/Header/components/Content/components/Navigation/navigation.d.ts +2 -1
- package/lib/components/Layout/components/Header/components/Content/components/Navigation/navigation.js +16 -17
- package/lib/components/Layout/components/Header/header.js +2 -1
- package/lib/components/Login/components/Button/types.d.ts +1 -1
- package/lib/components/Login/components/Buttons/buttons.d.ts +2 -0
- package/lib/components/Login/components/Buttons/buttons.js +5 -0
- package/lib/components/Login/components/Buttons/types.d.ts +8 -0
- package/lib/components/Login/components/Buttons/types.js +1 -0
- package/lib/components/Login/components/Section/components/Consent/consent.d.ts +3 -0
- package/lib/components/Login/components/Section/components/Consent/consent.js +14 -0
- package/lib/components/Login/components/Section/components/Consent/consent.styles.d.ts +7 -0
- package/lib/components/Login/components/Section/components/Consent/consent.styles.js +14 -0
- package/lib/components/Login/components/Section/components/Consent/types.d.ts +6 -0
- package/lib/components/Login/components/Section/components/Consent/types.js +1 -0
- package/lib/components/Login/components/Section/components/Warning/warning.d.ts +3 -0
- package/lib/components/Login/components/Section/components/Warning/warning.js +9 -0
- package/lib/components/Login/hooks/useUserConsent/types.d.ts +10 -0
- package/lib/components/Login/hooks/useUserConsent/types.js +1 -0
- package/lib/components/Login/hooks/useUserConsent/useUserConsent.d.ts +2 -0
- package/lib/components/Login/hooks/useUserConsent/useUserConsent.js +24 -0
- package/lib/components/Login/hooks/useUserLogin/types.d.ts +6 -0
- package/lib/components/Login/hooks/useUserLogin/types.js +1 -0
- package/lib/components/Login/hooks/useUserLogin/useUserLogin.d.ts +2 -0
- package/lib/components/Login/hooks/useUserLogin/useUserLogin.js +21 -0
- package/lib/components/Table/components/TableHead/tableHead.js +4 -1
- package/lib/components/common/CustomIcon/components/CloseIcon/closeIcon.d.ts +2 -0
- package/lib/components/common/CustomIcon/components/CloseIcon/closeIcon.js +6 -0
- package/lib/components/common/LoginDialog/constants.d.ts +6 -0
- package/lib/components/common/LoginDialog/constants.js +21 -0
- package/lib/components/common/LoginDialog/loginDialog.d.ts +2 -0
- package/lib/components/common/LoginDialog/loginDialog.js +27 -0
- package/lib/components/common/LoginDialog/loginDialog.styles.d.ts +3 -0
- package/lib/components/common/LoginDialog/loginDialog.styles.js +50 -0
- package/lib/components/common/LoginDialog/types.d.ts +4 -0
- package/lib/components/common/LoginDialog/types.js +1 -0
- package/lib/components/common/Tabs/tabs.d.ts +2 -0
- package/lib/components/common/Tabs/tabs.js +14 -1
- package/lib/config/entities.d.ts +6 -1
- package/lib/hooks/useCategoryFilter.js +1 -0
- package/lib/providers/config.js +9 -2
- package/lib/providers/loginGuard/common/types.d.ts +18 -0
- package/lib/providers/loginGuard/common/types.js +1 -0
- package/lib/providers/loginGuard/context.d.ts +6 -0
- package/lib/providers/loginGuard/context.js +10 -0
- package/lib/providers/loginGuard/hook.d.ts +9 -0
- package/lib/providers/loginGuard/hook.js +12 -0
- package/lib/providers/loginGuard/provider.d.ts +11 -0
- package/lib/providers/loginGuard/provider.js +55 -0
- package/lib/styles/common/mui/typography.d.ts +1 -0
- package/lib/styles/common/mui/typography.js +7 -0
- package/package.json +1 -1
- package/src/common/entities.ts +37 -0
- package/src/components/DataDictionary/common/utils.ts +160 -0
- package/src/components/Export/components/ExportForm/components/ExportButton/exportButton.tsx +8 -1
- package/src/components/Export/components/ManifestDownload/components/ManifestDownloadEntity/components/FileManifestDownload/fileManifestDownload.tsx +11 -3
- package/src/components/Filter/components/Filter/filter.tsx +1 -0
- package/src/components/Filter/components/FilterLabel/filterLabel.tsx +16 -10
- package/src/components/Index/components/AzulFileDownload/azulFileDownload.tsx +12 -5
- package/src/components/Index/components/Tabs/common/utils.ts +2 -0
- package/src/components/Layout/components/Header/components/Content/components/Navigation/components/NavigationMenu/navigationMenu.tsx +1 -1
- package/src/components/Layout/components/Header/components/Content/components/Navigation/components/NavigationMenuItems/navigationMenuItems.tsx +16 -15
- package/src/components/Layout/components/Header/components/Content/components/Navigation/constants.ts +1 -0
- package/src/components/Layout/components/Header/components/Content/components/Navigation/navigation.tsx +26 -18
- package/src/components/Layout/components/Header/header.tsx +6 -1
- package/src/components/Login/components/Button/types.ts +1 -1
- package/src/components/Login/components/Buttons/buttons.tsx +22 -0
- package/src/components/Login/components/Buttons/types.ts +9 -0
- package/src/components/Login/components/Section/components/Consent/consent.styles.ts +15 -0
- package/src/components/Login/components/Section/components/Consent/consent.tsx +30 -0
- package/src/components/Login/components/Section/components/Consent/types.ts +10 -0
- package/src/components/Login/components/Section/components/Warning/warning.tsx +24 -0
- package/src/components/Login/hooks/useUserConsent/types.ts +11 -0
- package/src/components/Login/hooks/useUserConsent/useUserConsent.ts +32 -0
- package/src/components/Login/hooks/useUserLogin/types.ts +8 -0
- package/src/components/Login/hooks/useUserLogin/useUserLogin.ts +29 -0
- package/src/components/Table/components/TableHead/tableHead.tsx +26 -15
- package/src/components/common/CustomIcon/components/CloseIcon/closeIcon.tsx +17 -0
- package/src/components/common/LoginDialog/constants.ts +33 -0
- package/src/components/common/LoginDialog/loginDialog.styles.ts +51 -0
- package/src/components/common/LoginDialog/loginDialog.tsx +56 -0
- package/src/components/common/LoginDialog/types.ts +4 -0
- package/src/components/common/Tabs/tabs.tsx +33 -3
- package/src/config/entities.ts +11 -1
- package/src/hooks/useCategoryFilter.ts +1 -0
- package/src/providers/config.tsx +10 -2
- package/src/providers/loginGuard/common/types.ts +21 -0
- package/src/providers/loginGuard/context.ts +12 -0
- package/src/providers/loginGuard/hook.ts +14 -0
- package/src/providers/loginGuard/provider.tsx +76 -0
- package/src/styles/common/mui/typography.ts +8 -0
- package/tests/dataDictionary_utils.test.ts +153 -0
- package/tests/provider.test.tsx +191 -0
- package/types/data-explorer-ui.d.ts +2 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { Typography, TypographyProps } from "@mui/material";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import { COLOR } from "../../../../../../styles/common/mui/typography";
|
|
4
|
+
import { TEXT_BODY_SMALL_400 } from "../../../../../../theme/common/typography";
|
|
5
|
+
import { BaseComponentProps } from "../../../../../types";
|
|
6
|
+
|
|
7
|
+
export const Warning = ({
|
|
8
|
+
children,
|
|
9
|
+
className,
|
|
10
|
+
...props /* Mui TypographyOwnProps */
|
|
11
|
+
}: BaseComponentProps & TypographyProps): JSX.Element | null => {
|
|
12
|
+
if (!children) return null;
|
|
13
|
+
return (
|
|
14
|
+
<Typography
|
|
15
|
+
className={className}
|
|
16
|
+
color={COLOR.INK_LIGHT}
|
|
17
|
+
mt={6}
|
|
18
|
+
variant={TEXT_BODY_SMALL_400}
|
|
19
|
+
{...props}
|
|
20
|
+
>
|
|
21
|
+
{children}
|
|
22
|
+
</Typography>
|
|
23
|
+
);
|
|
24
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { ChangeEvent } from "react";
|
|
2
|
+
|
|
3
|
+
export interface UseUserConsent {
|
|
4
|
+
handleConsent: (e: ChangeEvent<HTMLInputElement>) => void;
|
|
5
|
+
handleError: (error: boolean) => void;
|
|
6
|
+
state: {
|
|
7
|
+
isDisabled: boolean;
|
|
8
|
+
isError: boolean;
|
|
9
|
+
isValid: boolean;
|
|
10
|
+
};
|
|
11
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { ChangeEvent, useCallback, useState } from "react";
|
|
2
|
+
import { useAuthenticationConfig } from "../../../../hooks/authentication/config/useAuthenticationConfig";
|
|
3
|
+
import { UseUserConsent } from "./types";
|
|
4
|
+
|
|
5
|
+
export const useUserConsent = (): UseUserConsent => {
|
|
6
|
+
const authConfig = useAuthenticationConfig();
|
|
7
|
+
const [isDisabled] = useState<boolean>(Boolean(!authConfig?.termsOfService));
|
|
8
|
+
const [isError, setIsError] = useState<boolean>(false);
|
|
9
|
+
const [isValid, setIsValid] = useState<boolean>(false);
|
|
10
|
+
|
|
11
|
+
const handleError = useCallback((error: boolean) => {
|
|
12
|
+
setIsError(error);
|
|
13
|
+
}, []);
|
|
14
|
+
|
|
15
|
+
const handleConsent = useCallback(
|
|
16
|
+
(changeEvent: ChangeEvent<HTMLInputElement>): void => {
|
|
17
|
+
handleError(false);
|
|
18
|
+
setIsValid(changeEvent.target.checked);
|
|
19
|
+
},
|
|
20
|
+
[handleError]
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
return {
|
|
24
|
+
handleConsent,
|
|
25
|
+
handleError,
|
|
26
|
+
state: {
|
|
27
|
+
isDisabled,
|
|
28
|
+
isError,
|
|
29
|
+
isValid,
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { ProviderId } from "../../../../providers/authentication/common/types";
|
|
2
|
+
import { UseUserConsent } from "../useUserConsent/types";
|
|
3
|
+
|
|
4
|
+
export interface UseUserLogin
|
|
5
|
+
extends Omit<UseUserConsent, "handleError" | "state"> {
|
|
6
|
+
consentState: Pick<UseUserConsent["state"], "isDisabled" | "isError">;
|
|
7
|
+
handleLogin: (providerId: ProviderId) => void;
|
|
8
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { useCallback } from "react";
|
|
2
|
+
import { useAuth } from "../../../../providers/authentication/auth/hook";
|
|
3
|
+
import { ProviderId } from "../../../../providers/authentication/common/types";
|
|
4
|
+
import { useUserConsent } from "../useUserConsent/useUserConsent";
|
|
5
|
+
import { UseUserLogin } from "./types";
|
|
6
|
+
|
|
7
|
+
export const useUserLogin = (): UseUserLogin => {
|
|
8
|
+
const { service: { requestLogin } = {} } = useAuth();
|
|
9
|
+
const { handleConsent, handleError, state: consentState } = useUserConsent();
|
|
10
|
+
const { isDisabled, isError, isValid } = consentState; // Consent state: { isValid } is an indicator of whether the user has accepted the login terms.
|
|
11
|
+
|
|
12
|
+
const handleLogin = useCallback(
|
|
13
|
+
(providerId: ProviderId): void => {
|
|
14
|
+
if (!isDisabled && !isValid) {
|
|
15
|
+
// If the user has not accepted terms, set error state to true.
|
|
16
|
+
handleError(true);
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
requestLogin?.(providerId);
|
|
20
|
+
},
|
|
21
|
+
[handleError, isDisabled, isValid, requestLogin]
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
return {
|
|
25
|
+
consentState: { isDisabled, isError },
|
|
26
|
+
handleConsent,
|
|
27
|
+
handleLogin,
|
|
28
|
+
};
|
|
29
|
+
};
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
} from "@mui/material";
|
|
8
8
|
import { flexRender, RowData } from "@tanstack/react-table";
|
|
9
9
|
import React, { Fragment } from "react";
|
|
10
|
+
import { Tooltip } from "../../../DataDictionary/components/Tooltip/tooltip";
|
|
10
11
|
import { ROW_DIRECTION } from "../../common/entities";
|
|
11
12
|
import {
|
|
12
13
|
getTableCellAlign,
|
|
@@ -28,27 +29,37 @@ export const TableHead = <T extends RowData>({
|
|
|
28
29
|
<MTableRow>
|
|
29
30
|
{headerGroup.headers.map(({ column, getContext, id }) => {
|
|
30
31
|
const { columnDef, getIsGrouped, getIsSorted } = column;
|
|
32
|
+
const annotation = columnDef.meta?.annotation;
|
|
31
33
|
return getIsGrouped() ? null : (
|
|
32
34
|
<TableCell
|
|
33
35
|
key={id}
|
|
34
36
|
align={getTableCellAlign(column)}
|
|
35
37
|
padding={getTableCellPadding(id)}
|
|
36
38
|
>
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
39
|
+
<Tooltip
|
|
40
|
+
description={annotation?.description}
|
|
41
|
+
title={annotation?.label}
|
|
42
|
+
>
|
|
43
|
+
{shouldSortColumn(tableInstance, column) ? (
|
|
44
|
+
<TableSortLabel
|
|
45
|
+
IconComponent={SouthRoundedIcon}
|
|
46
|
+
active={Boolean(getIsSorted())}
|
|
47
|
+
direction={getIsSorted() || undefined}
|
|
48
|
+
disabled={isSortDisabled(tableInstance)}
|
|
49
|
+
onClick={(mouseEvent) =>
|
|
50
|
+
handleToggleSorting(
|
|
51
|
+
mouseEvent,
|
|
52
|
+
tableInstance,
|
|
53
|
+
column
|
|
54
|
+
)
|
|
55
|
+
}
|
|
56
|
+
>
|
|
57
|
+
{flexRender(columnDef.header, getContext())}
|
|
58
|
+
</TableSortLabel>
|
|
59
|
+
) : (
|
|
60
|
+
flexRender(columnDef.header, getContext())
|
|
61
|
+
)}
|
|
62
|
+
</Tooltip>
|
|
52
63
|
</TableCell>
|
|
53
64
|
);
|
|
54
65
|
})}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { SvgIcon, SvgIconProps } from "@mui/material";
|
|
2
|
+
import React from "react";
|
|
3
|
+
|
|
4
|
+
export const CloseIcon = ({
|
|
5
|
+
fontSize = "xsmall",
|
|
6
|
+
viewBox = "0 0 18 18",
|
|
7
|
+
...props /* Spread props to allow for Mui SvgIconProps specific prop overrides e.g. "htmlColor". */
|
|
8
|
+
}: SvgIconProps): JSX.Element => {
|
|
9
|
+
return (
|
|
10
|
+
<SvgIcon fontSize={fontSize} viewBox={viewBox} {...props}>
|
|
11
|
+
<path
|
|
12
|
+
d="M8.99994 10.1061L5.38104 13.725C5.23104 13.875 5.04984 13.947 4.83744 13.941C4.62504 13.9344 4.44384 13.8561 4.29384 13.7061C4.14384 13.5561 4.06884 13.3719 4.06884 13.1535C4.06884 12.9345 4.14384 12.75 4.29384 12.6L7.89384 9.00005L4.27494 5.38115C4.12494 5.23115 4.05294 5.04695 4.05894 4.82855C4.06554 4.60955 4.14384 4.42505 4.29384 4.27505C4.44384 4.12505 4.62804 4.05005 4.84644 4.05005C5.06544 4.05005 5.24994 4.12505 5.39994 4.27505L8.99994 7.89395L12.6188 4.27505C12.7688 4.12505 12.953 4.05005 13.1714 4.05005C13.3904 4.05005 13.5749 4.12505 13.7249 4.27505C13.8749 4.42505 13.9499 4.60955 13.9499 4.82855C13.9499 5.04695 13.8749 5.23115 13.7249 5.38115L10.106 9.00005L13.7249 12.6189C13.8749 12.7689 13.9499 12.9501 13.9499 13.1625C13.9499 13.3749 13.8749 13.5561 13.7249 13.7061C13.5749 13.8561 13.3904 13.9311 13.1714 13.9311C12.953 13.9311 12.7688 13.8561 12.6188 13.7061L8.99994 10.1061Z"
|
|
13
|
+
fill="currentColor"
|
|
14
|
+
/>
|
|
15
|
+
</SvgIcon>
|
|
16
|
+
);
|
|
17
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DialogContentTextProps,
|
|
3
|
+
DialogProps,
|
|
4
|
+
DialogTitleProps,
|
|
5
|
+
IconButtonProps,
|
|
6
|
+
IconProps,
|
|
7
|
+
} from "@mui/material";
|
|
8
|
+
import { FONT_SIZE } from "../../../styles/common/mui/icon";
|
|
9
|
+
import { COLOR, VARIANT } from "../../../styles/common/mui/typography";
|
|
10
|
+
|
|
11
|
+
export const DIALOG_CONTENT_TEXT_PROPS: DialogContentTextProps = {
|
|
12
|
+
color: COLOR.INK_LIGHT,
|
|
13
|
+
component: "div",
|
|
14
|
+
variant: VARIANT.TEXT_BODY_400,
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export const DIALOG_PROPS: Partial<DialogProps> = {
|
|
18
|
+
PaperProps: { elevation: 0 },
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export const DIALOG_TITLE_PROPS: DialogTitleProps = {
|
|
22
|
+
variant: VARIANT.TEXT_HEADING_SMALL,
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export const ICON_BUTTON_PROPS: IconButtonProps = {
|
|
26
|
+
color: "inkLight",
|
|
27
|
+
edge: "end",
|
|
28
|
+
size: "xsmall",
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export const ICON_PROPS: Pick<IconProps, "fontSize"> = {
|
|
32
|
+
fontSize: FONT_SIZE.SMALL,
|
|
33
|
+
};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import styled from "@emotion/styled";
|
|
2
|
+
import { Dialog } from "@mui/material";
|
|
3
|
+
import { inkMain } from "../../../styles/common/mixins/colors";
|
|
4
|
+
import { alpha80 } from "../../../theme/common/palette";
|
|
5
|
+
|
|
6
|
+
export const StyledDialog = styled(Dialog)`
|
|
7
|
+
&.MuiDialog-root {
|
|
8
|
+
.MuiBackdrop-root {
|
|
9
|
+
background-color: ${inkMain}${alpha80};
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
.MuiDialog-paper {
|
|
13
|
+
border-radius: 8px;
|
|
14
|
+
max-width: 400px;
|
|
15
|
+
padding: 32px;
|
|
16
|
+
position: relative; /* positions close icon */
|
|
17
|
+
|
|
18
|
+
.MuiDialogTitle-root,
|
|
19
|
+
.MuiDialogContent-root,
|
|
20
|
+
.MuiDialogActions-root {
|
|
21
|
+
padding: 0;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.MuiDialogTitle-root {
|
|
25
|
+
font-size: 20px;
|
|
26
|
+
|
|
27
|
+
.MuiIconButton-root {
|
|
28
|
+
position: absolute;
|
|
29
|
+
right: 12px;
|
|
30
|
+
top: 12px;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.MuiDialogContent-root {
|
|
35
|
+
.MuiDialogContentText-root {
|
|
36
|
+
margin: 8px 0;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.MuiGrid2-root {
|
|
40
|
+
margin: 24px 0;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.MuiDialogActions-root {
|
|
45
|
+
display: flex;
|
|
46
|
+
flex-direction: column;
|
|
47
|
+
gap: 16px 0;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
`;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DialogActions,
|
|
3
|
+
DialogContent,
|
|
4
|
+
DialogContentText,
|
|
5
|
+
DialogTitle,
|
|
6
|
+
IconButton,
|
|
7
|
+
} from "@mui/material";
|
|
8
|
+
import React from "react";
|
|
9
|
+
import { useAuthenticationConfig } from "../../../hooks/authentication/config/useAuthenticationConfig";
|
|
10
|
+
import { Buttons } from "../../Login/components/Buttons/buttons";
|
|
11
|
+
import { Consent } from "../../Login/components/Section/components/Consent/consent";
|
|
12
|
+
import { Warning } from "../../Login/components/Section/components/Warning/warning";
|
|
13
|
+
import { useUserLogin } from "../../Login/hooks/useUserLogin/useUserLogin";
|
|
14
|
+
import { CloseIcon } from "../CustomIcon/components/CloseIcon/closeIcon";
|
|
15
|
+
import {
|
|
16
|
+
DIALOG_CONTENT_TEXT_PROPS,
|
|
17
|
+
DIALOG_PROPS,
|
|
18
|
+
DIALOG_TITLE_PROPS,
|
|
19
|
+
ICON_BUTTON_PROPS,
|
|
20
|
+
ICON_PROPS,
|
|
21
|
+
} from "./constants";
|
|
22
|
+
import { StyledDialog } from "./loginDialog.styles";
|
|
23
|
+
import { LoginDialogProps } from "./types";
|
|
24
|
+
|
|
25
|
+
export const LoginDialog = ({
|
|
26
|
+
onClose,
|
|
27
|
+
open,
|
|
28
|
+
}: LoginDialogProps): JSX.Element | null => {
|
|
29
|
+
const authConfig = useAuthenticationConfig();
|
|
30
|
+
const { consentState, handleConsent, handleLogin } = useUserLogin();
|
|
31
|
+
|
|
32
|
+
if (!authConfig) return null;
|
|
33
|
+
|
|
34
|
+
return (
|
|
35
|
+
<StyledDialog {...DIALOG_PROPS} onClose={onClose} open={open}>
|
|
36
|
+
<DialogTitle {...DIALOG_TITLE_PROPS}>
|
|
37
|
+
<div>Sign In Required</div>
|
|
38
|
+
<IconButton {...ICON_BUTTON_PROPS} onClick={onClose}>
|
|
39
|
+
<CloseIcon {...ICON_PROPS} />
|
|
40
|
+
</IconButton>
|
|
41
|
+
</DialogTitle>
|
|
42
|
+
<DialogContent>
|
|
43
|
+
<DialogContentText {...DIALOG_CONTENT_TEXT_PROPS}>
|
|
44
|
+
Please sign in to proceed with this action.
|
|
45
|
+
</DialogContentText>
|
|
46
|
+
<Consent handleConsent={handleConsent} {...consentState}>
|
|
47
|
+
{authConfig.termsOfService}
|
|
48
|
+
</Consent>
|
|
49
|
+
</DialogContent>
|
|
50
|
+
<DialogActions disableSpacing>
|
|
51
|
+
<Buttons handleLogin={handleLogin} providers={authConfig.providers} />
|
|
52
|
+
</DialogActions>
|
|
53
|
+
<Warning>{authConfig.warning}</Warning>
|
|
54
|
+
</StyledDialog>
|
|
55
|
+
);
|
|
56
|
+
};
|
|
@@ -3,7 +3,9 @@ import {
|
|
|
3
3
|
Tabs as MTabs,
|
|
4
4
|
TabsProps as MTabsProps,
|
|
5
5
|
} from "@mui/material";
|
|
6
|
-
import React, { ReactNode } from "react";
|
|
6
|
+
import React, { ReactElement, ReactNode } from "react";
|
|
7
|
+
import { DataDictionaryAnnotation } from "../../../common/entities";
|
|
8
|
+
import { Tooltip } from "../../DataDictionary/components/Tooltip/tooltip";
|
|
7
9
|
import { Tab, TabScrollFuzz } from "./tabs.styles";
|
|
8
10
|
|
|
9
11
|
export type TabsValue = MTabsProps["value"]; // any
|
|
@@ -11,6 +13,7 @@ export type TabValue = MTabProps["value"]; // any
|
|
|
11
13
|
export type OnTabChangeFn = (tabValue: TabValue) => void; // Function invoked when selected tab value changes.
|
|
12
14
|
|
|
13
15
|
export interface Tab {
|
|
16
|
+
annotation?: DataDictionaryAnnotation;
|
|
14
17
|
count?: string;
|
|
15
18
|
icon?: MTabProps["icon"]; // element or string
|
|
16
19
|
iconPosition?: MTabProps["iconPosition"]; // "bottom" or "end" or "start" or "top
|
|
@@ -41,14 +44,21 @@ export const Tabs = ({
|
|
|
41
44
|
>
|
|
42
45
|
{tabs.map(
|
|
43
46
|
(
|
|
44
|
-
{
|
|
47
|
+
{
|
|
48
|
+
annotation,
|
|
49
|
+
count,
|
|
50
|
+
icon,
|
|
51
|
+
iconPosition = "start",
|
|
52
|
+
label,
|
|
53
|
+
value: tabValue,
|
|
54
|
+
},
|
|
45
55
|
t
|
|
46
56
|
) => (
|
|
47
57
|
<Tab
|
|
48
58
|
icon={icon}
|
|
49
59
|
iconPosition={icon ? iconPosition : undefined}
|
|
50
60
|
key={`${label}${t}`}
|
|
51
|
-
label={
|
|
61
|
+
label={buildTabLabel(label, count, annotation)}
|
|
52
62
|
value={tabValue}
|
|
53
63
|
/>
|
|
54
64
|
)
|
|
@@ -56,3 +66,23 @@ export const Tabs = ({
|
|
|
56
66
|
</MTabs>
|
|
57
67
|
);
|
|
58
68
|
};
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Build a tab value from a tab config. Specifically, display the tab label
|
|
72
|
+
* with a tooltip annotation if necessary.
|
|
73
|
+
* @param label - Tab display value.
|
|
74
|
+
* @param count - Optional count to display next to the tab label.
|
|
75
|
+
* @param annotation - Data dictionary annotation.
|
|
76
|
+
* @returns Tab label with optional count and tooltip.
|
|
77
|
+
*/
|
|
78
|
+
function buildTabLabel(
|
|
79
|
+
label: ReactNode,
|
|
80
|
+
count?: string,
|
|
81
|
+
annotation?: DataDictionaryAnnotation
|
|
82
|
+
): ReactElement {
|
|
83
|
+
return (
|
|
84
|
+
<Tooltip description={annotation?.description} title={annotation?.label}>
|
|
85
|
+
<span>{count ? `${label} (${count})` : label}</span>
|
|
86
|
+
</Tooltip>
|
|
87
|
+
);
|
|
88
|
+
}
|
package/src/config/entities.ts
CHANGED
|
@@ -10,7 +10,12 @@ import {
|
|
|
10
10
|
TableOptions,
|
|
11
11
|
} from "@tanstack/react-table";
|
|
12
12
|
import { JSXElementConstructor, ReactNode } from "react";
|
|
13
|
-
import {
|
|
13
|
+
import {
|
|
14
|
+
DataDictionary,
|
|
15
|
+
DataDictionaryAnnotation,
|
|
16
|
+
SelectCategoryValueView,
|
|
17
|
+
SelectedFilter,
|
|
18
|
+
} from "../common/entities";
|
|
14
19
|
import { HeroTitle } from "../components/common/Title/title";
|
|
15
20
|
import { FooterProps } from "../components/Layout/components/Footer/footer";
|
|
16
21
|
import { HeaderProps } from "../components/Layout/components/Header/header";
|
|
@@ -87,6 +92,7 @@ export interface CategoryGroup {
|
|
|
87
92
|
* Model of category configured in site config.
|
|
88
93
|
*/
|
|
89
94
|
export interface CategoryConfig {
|
|
95
|
+
annotation?: DataDictionaryAnnotation;
|
|
90
96
|
key: string;
|
|
91
97
|
label: string;
|
|
92
98
|
mapSelectCategoryValue?: (
|
|
@@ -170,6 +176,7 @@ export type EntityPath = string;
|
|
|
170
176
|
*/
|
|
171
177
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- This config model is part of a generic array
|
|
172
178
|
export interface EntityConfig<T = any, I = any> extends TabConfig {
|
|
179
|
+
annotation?: DataDictionaryAnnotation;
|
|
173
180
|
apiPath?: EntityPath;
|
|
174
181
|
categoryGroupConfig?: CategoryGroupConfig;
|
|
175
182
|
detail: BackPageConfig;
|
|
@@ -180,6 +187,7 @@ export interface EntityConfig<T = any, I = any> extends TabConfig {
|
|
|
180
187
|
getId?: GetIdFunction<T>;
|
|
181
188
|
getTitle?: GetTitleFunction<T>;
|
|
182
189
|
hideTabs?: boolean;
|
|
190
|
+
key?: string; // Optional data dictionary key
|
|
183
191
|
list: ListConfig<T>;
|
|
184
192
|
listView?: ListViewConfig;
|
|
185
193
|
options?: Options;
|
|
@@ -366,10 +374,12 @@ export interface SiteConfig {
|
|
|
366
374
|
categoryGroupConfig?: CategoryGroupConfig;
|
|
367
375
|
contentDir?: string;
|
|
368
376
|
contentThemeOptionsFn?: ThemeOptionsFn;
|
|
377
|
+
dataDictionary?: DataDictionary;
|
|
369
378
|
dataSource: DataSourceConfig;
|
|
370
379
|
entities: EntityConfig[];
|
|
371
380
|
explorerTitle: HeroTitle;
|
|
372
381
|
export?: ExportConfig;
|
|
382
|
+
exportsRequireAuth?: boolean;
|
|
373
383
|
exportToTerraUrl?: string; // TODO(cc) revist location; possibly nest inside "export"?
|
|
374
384
|
gitHubUrl?: string;
|
|
375
385
|
layout: {
|
|
@@ -77,6 +77,7 @@ function buildCategoryView(
|
|
|
77
77
|
const mapSelectCategoryValue =
|
|
78
78
|
categoryConfig?.mapSelectCategoryValue || getSelectCategoryValue;
|
|
79
79
|
return {
|
|
80
|
+
annotation: categoryConfig?.annotation,
|
|
80
81
|
isDisabled: false,
|
|
81
82
|
key: category.key,
|
|
82
83
|
label: getCategoryLabel(category.key, categoryConfig),
|
package/src/providers/config.tsx
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import React, { createContext, ReactNode } from "react";
|
|
1
|
+
import React, { createContext, ReactNode, useState } from "react";
|
|
2
|
+
import { annotateSiteConfig } from "../components/DataDictionary/common/utils";
|
|
2
3
|
import { EntityConfig, SiteConfig } from "../config/entities";
|
|
3
4
|
import {
|
|
4
5
|
getDefaultConfig,
|
|
@@ -31,7 +32,14 @@ export function ConfigProvider({
|
|
|
31
32
|
config,
|
|
32
33
|
entityListType = "",
|
|
33
34
|
}: ConfigProps): JSX.Element {
|
|
34
|
-
|
|
35
|
+
// Annote config on init. Note config is mutated but using state here to
|
|
36
|
+
// ensure annotated config is calculated once and is used rather than the raw config.
|
|
37
|
+
const [annotatedConfig] = useState(() => {
|
|
38
|
+
annotateSiteConfig(config);
|
|
39
|
+
return config;
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
const { entities } = annotatedConfig;
|
|
35
43
|
const defaultEntityListType = config.redirectRootToPath.slice(1);
|
|
36
44
|
const entityName = entityListType || defaultEntityListType;
|
|
37
45
|
const entityConfig = getEntityConfig(entities, entityName);
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { ReactNode } from "react";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* A callback function to be stored and then executed upon successful login.
|
|
5
|
+
*/
|
|
6
|
+
export type LoginGuardCallback = () => void;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* The shape of the LoginGuard context, provides a function to trigger the
|
|
10
|
+
* login process.
|
|
11
|
+
*/
|
|
12
|
+
export interface LoginGuardContextProps {
|
|
13
|
+
requireLogin: (callback?: LoginGuardCallback) => void;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* The properties for the LoginGuardProvider component.
|
|
18
|
+
*/
|
|
19
|
+
export interface LoginGuardProviderProps {
|
|
20
|
+
children: ReactNode;
|
|
21
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { createContext } from "react";
|
|
2
|
+
import { LoginGuardCallback, LoginGuardContextProps } from "./common/types";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* LoginGuardContext provides a way to trigger a login process. Default value is to
|
|
6
|
+
* call the callback immediately, if specified.
|
|
7
|
+
*/
|
|
8
|
+
export const LoginGuardContext = createContext<LoginGuardContextProps>({
|
|
9
|
+
requireLogin: (callback?: LoginGuardCallback) => {
|
|
10
|
+
callback?.();
|
|
11
|
+
},
|
|
12
|
+
});
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { useContext } from "react";
|
|
2
|
+
import { LoginGuardContextProps } from "./common/types";
|
|
3
|
+
import { LoginGuardContext } from "./context";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Custom hook to access the LoginGuard context. This hook returns an object
|
|
7
|
+
* containing the "requireLogin" function, which allows triggering the application's
|
|
8
|
+
* login process.
|
|
9
|
+
*
|
|
10
|
+
* @returns The current LoginGuard context value.
|
|
11
|
+
*/
|
|
12
|
+
export function useLoginGuard(): LoginGuardContextProps {
|
|
13
|
+
return useContext<LoginGuardContextProps>(LoginGuardContext);
|
|
14
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import React, { useCallback, useEffect, useRef, useState } from "react";
|
|
2
|
+
import { LoginDialog } from "../../components/common/LoginDialog/loginDialog";
|
|
3
|
+
import { useAuthenticationConfig } from "../../hooks/authentication/config/useAuthenticationConfig";
|
|
4
|
+
import { useConfig } from "../../hooks/useConfig";
|
|
5
|
+
import { useAuth } from "../authentication/auth/hook";
|
|
6
|
+
import { LoginGuardCallback, LoginGuardProviderProps } from "./common/types";
|
|
7
|
+
import { LoginGuardContext } from "./context";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* LoginGuardProvider is responsible for intercepting actions that require user authentication.
|
|
11
|
+
* It provides a "requireLogin" function via context. When a protected action is triggered while the
|
|
12
|
+
* user is unauthenticated, the LoginDialog is displayed. Upon successful authentication, the saved
|
|
13
|
+
* callback is invoked.
|
|
14
|
+
*
|
|
15
|
+
* @param {LoginGuardProviderProps} props - The provider props that include children.
|
|
16
|
+
* @returns The provider component.
|
|
17
|
+
*/
|
|
18
|
+
export function LoginGuardProvider({
|
|
19
|
+
children,
|
|
20
|
+
}: LoginGuardProviderProps): JSX.Element {
|
|
21
|
+
// Dialog open state.
|
|
22
|
+
const [open, setOpen] = useState(false);
|
|
23
|
+
|
|
24
|
+
// Use ref to store the callback without triggering re-render.
|
|
25
|
+
const callbackRef = useRef<LoginGuardCallback | undefined>(undefined);
|
|
26
|
+
|
|
27
|
+
// Determine if authentication is enabled.
|
|
28
|
+
const authConfig = useAuthenticationConfig();
|
|
29
|
+
|
|
30
|
+
// Determine if authentication is required for downloads and exports.
|
|
31
|
+
const {
|
|
32
|
+
config: { exportsRequireAuth },
|
|
33
|
+
} = useConfig();
|
|
34
|
+
|
|
35
|
+
// Get the user's authenticated state.
|
|
36
|
+
const {
|
|
37
|
+
authState: { isAuthenticated },
|
|
38
|
+
} = useAuth();
|
|
39
|
+
|
|
40
|
+
// If the user authenticates, close dialog then fire and clear callback.
|
|
41
|
+
useEffect(() => {
|
|
42
|
+
if (isAuthenticated) {
|
|
43
|
+
setOpen(false);
|
|
44
|
+
callbackRef.current?.();
|
|
45
|
+
// Clear callback after firing.
|
|
46
|
+
callbackRef.current = undefined;
|
|
47
|
+
}
|
|
48
|
+
}, [isAuthenticated]);
|
|
49
|
+
|
|
50
|
+
// Handler to close the dialog.
|
|
51
|
+
const onClose = useCallback(() => {
|
|
52
|
+
setOpen(false);
|
|
53
|
+
// Clear any stored callback.
|
|
54
|
+
callbackRef.current = undefined;
|
|
55
|
+
}, []);
|
|
56
|
+
|
|
57
|
+
// Block actions that require authentication, or fire callback if already authenticated.
|
|
58
|
+
const requireLogin = useCallback(
|
|
59
|
+
(cb?: LoginGuardCallback) => {
|
|
60
|
+
if (authConfig && exportsRequireAuth && !isAuthenticated) {
|
|
61
|
+
callbackRef.current = cb;
|
|
62
|
+
setOpen(true);
|
|
63
|
+
} else {
|
|
64
|
+
cb?.();
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
[authConfig, exportsRequireAuth, isAuthenticated]
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
return (
|
|
71
|
+
<LoginGuardContext.Provider value={{ requireLogin }}>
|
|
72
|
+
{children}
|
|
73
|
+
<LoginDialog open={open} onClose={onClose} />
|
|
74
|
+
</LoginGuardContext.Provider>
|
|
75
|
+
);
|
|
76
|
+
}
|
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
import { TypographyOwnProps } from "@mui/material";
|
|
2
2
|
|
|
3
|
+
export const COLOR: Record<string, TypographyOwnProps["color"]> = {
|
|
4
|
+
INHERIT: "inherit",
|
|
5
|
+
INK_LIGHT: "ink.light",
|
|
6
|
+
INK_MAIN: "ink.main",
|
|
7
|
+
};
|
|
8
|
+
|
|
3
9
|
export const VARIANT: Record<string, TypographyOwnProps["variant"]> = {
|
|
4
10
|
INHERIT: "inherit",
|
|
11
|
+
TEXT_BODY_400: "text-body-400",
|
|
12
|
+
TEXT_HEADING_SMALL: "text-heading-small",
|
|
5
13
|
};
|