@databiosphere/findable-ui 21.3.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.
Files changed (86) hide show
  1. package/.release-please-manifest.json +1 -1
  2. package/CHANGELOG.md +8 -0
  3. package/lib/components/Export/components/ExportForm/components/ExportButton/exportButton.js +6 -1
  4. package/lib/components/Export/components/ManifestDownload/components/ManifestDownloadEntity/components/FileManifestDownload/fileManifestDownload.js +5 -2
  5. package/lib/components/Index/components/AzulFileDownload/azulFileDownload.js +10 -5
  6. package/lib/components/Layout/components/Header/components/Content/components/Navigation/components/NavigationMenu/navigationMenu.js +1 -1
  7. package/lib/components/Layout/components/Header/components/Content/components/Navigation/components/NavigationMenuItems/navigationMenuItems.js +20 -21
  8. package/lib/components/Layout/components/Header/components/Content/components/Navigation/constants.d.ts +1 -0
  9. package/lib/components/Layout/components/Header/components/Content/components/Navigation/constants.js +1 -0
  10. package/lib/components/Layout/components/Header/components/Content/components/Navigation/navigation.d.ts +2 -1
  11. package/lib/components/Layout/components/Header/components/Content/components/Navigation/navigation.js +16 -17
  12. package/lib/components/Layout/components/Header/header.js +2 -1
  13. package/lib/components/Login/components/Button/types.d.ts +1 -1
  14. package/lib/components/Login/components/Buttons/buttons.d.ts +2 -0
  15. package/lib/components/Login/components/Buttons/buttons.js +5 -0
  16. package/lib/components/Login/components/Buttons/types.d.ts +8 -0
  17. package/lib/components/Login/components/Buttons/types.js +1 -0
  18. package/lib/components/Login/components/Section/components/Consent/consent.d.ts +3 -0
  19. package/lib/components/Login/components/Section/components/Consent/consent.js +14 -0
  20. package/lib/components/Login/components/Section/components/Consent/consent.styles.d.ts +7 -0
  21. package/lib/components/Login/components/Section/components/Consent/consent.styles.js +14 -0
  22. package/lib/components/Login/components/Section/components/Consent/types.d.ts +6 -0
  23. package/lib/components/Login/components/Section/components/Consent/types.js +1 -0
  24. package/lib/components/Login/components/Section/components/Warning/warning.d.ts +3 -0
  25. package/lib/components/Login/components/Section/components/Warning/warning.js +9 -0
  26. package/lib/components/Login/hooks/useUserConsent/types.d.ts +10 -0
  27. package/lib/components/Login/hooks/useUserConsent/types.js +1 -0
  28. package/lib/components/Login/hooks/useUserConsent/useUserConsent.d.ts +2 -0
  29. package/lib/components/Login/hooks/useUserConsent/useUserConsent.js +24 -0
  30. package/lib/components/Login/hooks/useUserLogin/types.d.ts +6 -0
  31. package/lib/components/Login/hooks/useUserLogin/types.js +1 -0
  32. package/lib/components/Login/hooks/useUserLogin/useUserLogin.d.ts +2 -0
  33. package/lib/components/Login/hooks/useUserLogin/useUserLogin.js +21 -0
  34. package/lib/components/common/CustomIcon/components/CloseIcon/closeIcon.d.ts +2 -0
  35. package/lib/components/common/CustomIcon/components/CloseIcon/closeIcon.js +6 -0
  36. package/lib/components/common/LoginDialog/constants.d.ts +6 -0
  37. package/lib/components/common/LoginDialog/constants.js +21 -0
  38. package/lib/components/common/LoginDialog/loginDialog.d.ts +2 -0
  39. package/lib/components/common/LoginDialog/loginDialog.js +27 -0
  40. package/lib/components/common/LoginDialog/loginDialog.styles.d.ts +3 -0
  41. package/lib/components/common/LoginDialog/loginDialog.styles.js +50 -0
  42. package/lib/components/common/LoginDialog/types.d.ts +4 -0
  43. package/lib/components/common/LoginDialog/types.js +1 -0
  44. package/lib/config/entities.d.ts +1 -0
  45. package/lib/providers/loginGuard/common/types.d.ts +18 -0
  46. package/lib/providers/loginGuard/common/types.js +1 -0
  47. package/lib/providers/loginGuard/context.d.ts +6 -0
  48. package/lib/providers/loginGuard/context.js +10 -0
  49. package/lib/providers/loginGuard/hook.d.ts +9 -0
  50. package/lib/providers/loginGuard/hook.js +12 -0
  51. package/lib/providers/loginGuard/provider.d.ts +11 -0
  52. package/lib/providers/loginGuard/provider.js +55 -0
  53. package/lib/styles/common/mui/typography.d.ts +1 -0
  54. package/lib/styles/common/mui/typography.js +7 -0
  55. package/package.json +1 -1
  56. package/src/components/Export/components/ExportForm/components/ExportButton/exportButton.tsx +8 -1
  57. package/src/components/Export/components/ManifestDownload/components/ManifestDownloadEntity/components/FileManifestDownload/fileManifestDownload.tsx +11 -3
  58. package/src/components/Index/components/AzulFileDownload/azulFileDownload.tsx +12 -5
  59. package/src/components/Layout/components/Header/components/Content/components/Navigation/components/NavigationMenu/navigationMenu.tsx +1 -1
  60. package/src/components/Layout/components/Header/components/Content/components/Navigation/components/NavigationMenuItems/navigationMenuItems.tsx +16 -15
  61. package/src/components/Layout/components/Header/components/Content/components/Navigation/constants.ts +1 -0
  62. package/src/components/Layout/components/Header/components/Content/components/Navigation/navigation.tsx +26 -18
  63. package/src/components/Layout/components/Header/header.tsx +6 -1
  64. package/src/components/Login/components/Button/types.ts +1 -1
  65. package/src/components/Login/components/Buttons/buttons.tsx +22 -0
  66. package/src/components/Login/components/Buttons/types.ts +9 -0
  67. package/src/components/Login/components/Section/components/Consent/consent.styles.ts +15 -0
  68. package/src/components/Login/components/Section/components/Consent/consent.tsx +30 -0
  69. package/src/components/Login/components/Section/components/Consent/types.ts +10 -0
  70. package/src/components/Login/components/Section/components/Warning/warning.tsx +24 -0
  71. package/src/components/Login/hooks/useUserConsent/types.ts +11 -0
  72. package/src/components/Login/hooks/useUserConsent/useUserConsent.ts +32 -0
  73. package/src/components/Login/hooks/useUserLogin/types.ts +8 -0
  74. package/src/components/Login/hooks/useUserLogin/useUserLogin.ts +29 -0
  75. package/src/components/common/CustomIcon/components/CloseIcon/closeIcon.tsx +17 -0
  76. package/src/components/common/LoginDialog/constants.ts +33 -0
  77. package/src/components/common/LoginDialog/loginDialog.styles.ts +51 -0
  78. package/src/components/common/LoginDialog/loginDialog.tsx +56 -0
  79. package/src/components/common/LoginDialog/types.ts +4 -0
  80. package/src/config/entities.ts +1 -0
  81. package/src/providers/loginGuard/common/types.ts +21 -0
  82. package/src/providers/loginGuard/context.ts +12 -0
  83. package/src/providers/loginGuard/hook.ts +14 -0
  84. package/src/providers/loginGuard/provider.tsx +76 -0
  85. package/src/styles/common/mui/typography.ts +8 -0
  86. package/tests/provider.test.tsx +191 -0
@@ -1,3 +1,3 @@
1
1
  {
2
- ".": "21.3.0"
2
+ ".": "21.4.0"
3
3
  }
package/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # Changelog
2
2
 
3
+ ## [21.4.0](https://github.com/DataBiosphere/findable-ui/compare/v21.3.0...v21.4.0) (2025-02-28)
4
+
5
+
6
+ ### Features
7
+
8
+ * added check login required for export/download [#283](https://github.com/DataBiosphere/findable-ui/issues/283) ([#324](https://github.com/DataBiosphere/findable-ui/issues/324)) ([badb6ab](https://github.com/DataBiosphere/findable-ui/commit/badb6abe9319625a7fdabfe290434a05115cc2a9))
9
+ * use anchor tags for all header and footer links ([#297](https://github.com/DataBiosphere/findable-ui/issues/297)) ([#322](https://github.com/DataBiosphere/findable-ui/issues/322)) ([95effbf](https://github.com/DataBiosphere/findable-ui/commit/95effbfcdb65d71d10141c287b34043086c261e5))
10
+
3
11
  ## [21.3.0](https://github.com/DataBiosphere/findable-ui/compare/v21.2.0...v21.3.0) (2025-02-14)
4
12
 
5
13
 
@@ -2,12 +2,17 @@ import { Tooltip } from "@mui/material";
2
2
  import React from "react";
3
3
  import { useDownloadStatus } from "../../../../../../hooks/useDownloadStatus";
4
4
  import { useFileManifestState } from "../../../../../../hooks/useFileManifestState";
5
+ import { useLoginGuard } from "../../../../../../providers/loginGuard/hook";
5
6
  import { ButtonPrimary } from "../../../../../common/Button/components/ButtonPrimary/buttonPrimary";
6
7
  export const ExportButton = ({ Button = ButtonPrimary, children, onClick, }) => {
7
8
  const downloadStatus = useDownloadStatus();
8
9
  const { fileManifestState: { isLoading }, } = useFileManifestState();
10
+ // Prompt user for login before export, if required.
11
+ const { requireLogin } = useLoginGuard();
9
12
  return (React.createElement(Tooltip, { arrow: true, title: isLoading ? null : downloadStatus.message },
10
13
  React.createElement("span", null,
11
- React.createElement(Button, { disabled: isLoading || downloadStatus.disabled || downloadStatus.isLoading, onClick: onClick },
14
+ React.createElement(Button, { disabled: isLoading || downloadStatus.disabled || downloadStatus.isLoading, onClick: () => {
15
+ requireLogin(onClick);
16
+ } },
12
17
  React.createElement("span", null, children)))));
13
18
  };
@@ -3,6 +3,7 @@ import copy from "copy-to-clipboard";
3
3
  import React, { useRef } from "react";
4
4
  import { useDownloadStatus } from "../../../../../../../../hooks/useDownloadStatus";
5
5
  import { useFileManifestDownload } from "../../../../../../../../hooks/useFileManifest/useFileManifestDownload";
6
+ import { useLoginGuard } from "../../../../../../../../providers/loginGuard/hook";
6
7
  import { ButtonGroup } from "../../../../../../../common/ButtonGroup/buttonGroup";
7
8
  import { ButtonGroupButton } from "../../../../../../../common/ButtonGroup/components/ButtonGroupButton/buttonGroupButton";
8
9
  import { ContentCopyIconSmall, DownloadIconSmall, } from "../../../../../../../common/CustomIcon/common/constants";
@@ -16,6 +17,8 @@ export const FileManifestDownload = ({ filters, }) => {
16
17
  const { fileName, isIdle, isLoading, manifestURL } = useFileManifestDownload(filters, disabled);
17
18
  const isInProgress = (isIdle || isLoading) && !disabled;
18
19
  const isReady = Boolean(manifestURL) || disabled;
20
+ // Prompt user for login before download and copy, if required.
21
+ const { requireLogin } = useLoginGuard();
19
22
  // Copies file manifest.
20
23
  const copyManifestURL = (url) => {
21
24
  if (!url)
@@ -39,8 +42,8 @@ export const FileManifestDownload = ({ filters, }) => {
39
42
  React.createElement(Tooltip, { arrow: true, title: message },
40
43
  React.createElement("span", null,
41
44
  React.createElement(ButtonGroup, { Buttons: [
42
- React.createElement(ButtonGroupButton, { key: "download", action: "Download file manifest", disabled: disabled, label: React.createElement(DownloadIconSmall, null), onClick: downloadManifestURL }),
43
- React.createElement(ButtonGroupButton, { key: "copy", action: "Copy file manifest", disabled: disabled, label: React.createElement(ContentCopyIconSmall, null), onClick: () => copyManifestURL(manifestURL) }),
45
+ React.createElement(ButtonGroupButton, { key: "download", action: "Download file manifest", disabled: disabled, label: React.createElement(DownloadIconSmall, null), onClick: () => requireLogin(downloadManifestURL) }),
46
+ React.createElement(ButtonGroupButton, { key: "copy", action: "Copy file manifest", disabled: disabled, label: React.createElement(ContentCopyIconSmall, null), onClick: () => requireLogin(() => copyManifestURL(manifestURL)) }),
44
47
  ] })))),
45
48
  React.createElement(TableCell, null, fileName))) : (React.createElement(TableCell, null, "The manifest is not available for this project.")))))))));
46
49
  };
@@ -1,6 +1,7 @@
1
1
  import { Box } from "@mui/material";
2
2
  import React, { Fragment, useEffect, useRef, useState } from "react";
3
3
  import { useFileLocation } from "../../../../hooks/useFileLocation";
4
+ import { useLoginGuard } from "../../../../providers/loginGuard/hook";
4
5
  import { DownloadIcon } from "../../../common/CustomIcon/components/DownloadIcon/downloadIcon";
5
6
  import { LoadingIcon } from "../../../common/CustomIcon/components/LoadingIcon/loadingIcon";
6
7
  import { IconButton } from "../../../common/IconButton/iconButton";
@@ -11,6 +12,8 @@ export const AzulFileDownload = ({ entityName, relatedEntityId, relatedEntityNam
11
12
  const { fileUrl, isLoading, run } = useFileLocation(url);
12
13
  const downloadRef = useRef(null);
13
14
  const [isRequestPending, setIsRequestPending] = useState(false);
15
+ // Prompt user for login before download, if required.
16
+ const { requireLogin } = useLoginGuard();
14
17
  // Initiates file download when file location request is successful.
15
18
  useEffect(() => {
16
19
  if (!fileUrl)
@@ -22,11 +25,13 @@ export const AzulFileDownload = ({ entityName, relatedEntityId, relatedEntityNam
22
25
  downloadEl.click();
23
26
  setIsRequestPending(false);
24
27
  }, [fileUrl]);
28
+ // Initiates file download when download button is clicked.
29
+ const handleDownloadClick = () => {
30
+ setIsRequestPending(true);
31
+ trackFileDownloaded(entityName, relatedEntityId, relatedEntityName);
32
+ run();
33
+ };
25
34
  return (React.createElement(Fragment, null,
26
- isRequestPending ? (React.createElement(StyledIconButton, { color: "primary", "data-testid": AZUL_FILE_REQUEST_DOWNLOAD_PENDING_TEST_ID, Icon: LoadingIcon, size: "medium" })) : (React.createElement(IconButton, { color: "primary", "data-testid": AZUL_FILE_REQUEST_DOWNLOAD_TEST_ID, disabled: !url, Icon: isLoading ? LoadingIcon : DownloadIcon, onClick: () => {
27
- setIsRequestPending(true);
28
- trackFileDownloaded(entityName, relatedEntityId, relatedEntityName);
29
- run();
30
- }, size: "medium" })),
35
+ isRequestPending ? (React.createElement(StyledIconButton, { color: "primary", "data-testid": AZUL_FILE_REQUEST_DOWNLOAD_PENDING_TEST_ID, Icon: LoadingIcon, size: "medium" })) : (React.createElement(IconButton, { color: "primary", "data-testid": AZUL_FILE_REQUEST_DOWNLOAD_TEST_ID, disabled: !url, Icon: isLoading ? LoadingIcon : DownloadIcon, onClick: () => requireLogin(handleDownloadClick), size: "medium" })),
31
36
  React.createElement(Box, { component: "a", "data-testid": AZUL_FILE_DOWNLOAD_TEST_ID, download: true, ref: downloadRef, sx: { display: "none" } })));
32
37
  };
@@ -26,7 +26,7 @@ export const NavigationMenu = ({ closeAncestor, isSelected = false, isSubMenu =
26
26
  } },
27
27
  React.createElement(MPaper, { variant: "menu" },
28
28
  React.createElement(MClickAwayListener, { onClickAway: onClose },
29
- React.createElement(MMenuList, null,
29
+ React.createElement(MMenuList, { component: "div" },
30
30
  React.createElement(NavigationMenuItems, { closeMenu: () => {
31
31
  onClose();
32
32
  closeAncestor?.();
@@ -1,5 +1,5 @@
1
- import { Divider, ListItemIcon, ListItemText, MenuItem as MMenuItem, } from "@mui/material";
2
- import { useRouter } from "next/router";
1
+ import { Divider, ListItemIcon, ListItemText, Link as MLink, MenuItem as MMenuItem, } from "@mui/material";
2
+ import Link from "next/link";
3
3
  import React, { Fragment } from "react";
4
4
  import { TEXT_BODY_400, TEXT_BODY_500, TEXT_BODY_SMALL_400_2_LINES, TEXT_UPPERCASE_500, } from "../../../../../../../../../../theme/common/typography";
5
5
  import { ANCHOR_TARGET, REL_ATTRIBUTE, } from "../../../../../../../../../Links/common/entities";
@@ -8,23 +8,22 @@ import { isNavigationLinkSelected } from "../../common/utils";
8
8
  import { NavigationMenu } from "../NavigationMenu/navigationMenu";
9
9
  import { POPPER_PROPS } from "./common/constants";
10
10
  export const NavigationMenuItems = ({ closeMenu, menuItems, pathname, }) => {
11
- const router = useRouter();
12
- return (React.createElement(React.Fragment, null, menuItems.map(({ description, divider, icon, label, menuItems: nestedMenuItems, selectedPatterns, target = ANCHOR_TARGET.SELF, url, }, i) => nestedMenuItems ? (React.createElement(NavigationMenu, { key: i, closeAncestor: closeMenu, isSelected: isNavigationLinkSelected(pathname, selectedPatterns), isSubMenu: true, menuItems: nestedMenuItems, menuLabel: label, pathname: pathname, popperProps: POPPER_PROPS })) : (React.createElement(Fragment, { key: i },
13
- React.createElement(MMenuItem, { disabled: !url, onClick: () => {
14
- closeMenu();
15
- isClientSideNavigation(url)
16
- ? router.push(url)
17
- : window.open(url, target, REL_ATTRIBUTE.NO_OPENER_NO_REFERRER);
18
- }, selected: isNavigationLinkSelected(pathname, selectedPatterns) },
19
- icon && React.createElement(ListItemIcon, null, icon),
20
- React.createElement(ListItemText, { primary: label, primaryTypographyProps: {
21
- variant: url
22
- ? description
23
- ? TEXT_BODY_500
24
- : TEXT_BODY_400
25
- : TEXT_UPPERCASE_500,
26
- }, secondary: description, secondaryTypographyProps: {
27
- variant: TEXT_BODY_SMALL_400_2_LINES,
28
- } })),
29
- divider && React.createElement(Divider, null))))));
11
+ return (React.createElement(React.Fragment, null, menuItems.map(({ description, divider, icon, label, menuItems: nestedMenuItems, selectedPatterns, target = ANCHOR_TARGET.SELF, url, }, i) => {
12
+ const isClientSide = isClientSideNavigation(url);
13
+ return nestedMenuItems ? (React.createElement(NavigationMenu, { key: i, closeAncestor: closeMenu, isSelected: isNavigationLinkSelected(pathname, selectedPatterns), isSubMenu: true, menuItems: nestedMenuItems, menuLabel: label, pathname: pathname, popperProps: POPPER_PROPS })) : (React.createElement(Fragment, { key: i },
14
+ React.createElement(MMenuItem, { component: isClientSide ? Link : MLink, disabled: !url, href: url, onClick: () => closeMenu(), rel: isClientSide
15
+ ? REL_ATTRIBUTE.NO_OPENER
16
+ : REL_ATTRIBUTE.NO_OPENER_NO_REFERRER, selected: isNavigationLinkSelected(pathname, selectedPatterns), target: target },
17
+ icon && React.createElement(ListItemIcon, null, icon),
18
+ React.createElement(ListItemText, { primary: label, primaryTypographyProps: {
19
+ variant: url
20
+ ? description
21
+ ? TEXT_BODY_500
22
+ : TEXT_BODY_400
23
+ : TEXT_UPPERCASE_500,
24
+ }, secondary: description, secondaryTypographyProps: {
25
+ variant: TEXT_BODY_SMALL_400_2_LINES,
26
+ } })),
27
+ divider && React.createElement(Divider, null)));
28
+ })));
30
29
  };
@@ -0,0 +1 @@
1
+ export declare const NAVIGATION_TEST_ID = "navigation";
@@ -0,0 +1 @@
1
+ export const NAVIGATION_TEST_ID = "navigation";
@@ -1,6 +1,7 @@
1
1
  import React, { CSSProperties, ReactNode } from "react";
2
2
  import { BreakpointKey } from "../../../../../../../../hooks/useBreakpointHelper";
3
3
  import { ANCHOR_TARGET } from "../../../../../../../Links/common/entities";
4
+ import { TestIdProps } from "../../../../../../../types";
4
5
  import { SelectedMatch } from "../../../../common/entities";
5
6
  import { HeaderProps } from "../../../../header";
6
7
  import { MenuItem } from "./components/NavigationMenuItems/navigationMenuItems";
@@ -15,7 +16,7 @@ export interface NavLinkItem {
15
16
  url: string;
16
17
  visible?: Partial<Record<BreakpointKey, boolean>>;
17
18
  }
18
- export interface NavigationProps {
19
+ export interface NavigationProps extends TestIdProps {
19
20
  className?: string;
20
21
  closeAncestor?: () => void;
21
22
  headerProps?: HeaderProps;
@@ -1,5 +1,5 @@
1
- import { Button, Divider } from "@mui/material";
2
- import { useRouter } from "next/router";
1
+ import { Button, Divider, Link as MLink } from "@mui/material";
2
+ import Link from "next/link";
3
3
  import React, { forwardRef, Fragment } from "react";
4
4
  import { ANCHOR_TARGET, REL_ATTRIBUTE, } from "../../../../../../../Links/common/entities";
5
5
  import { isClientSideNavigation } from "../../../../../../../Links/common/utils";
@@ -8,19 +8,18 @@ import { NavigationButtonLabel } from "./components/NavigationButtonLabel/naviga
8
8
  import { NavigationDrawer } from "./components/NavigationDrawer/navigationDrawer";
9
9
  import { NavigationMenu } from "./components/NavigationMenu/navigationMenu";
10
10
  import { Navigation as Links } from "./navigation.styles";
11
- export const Navigation = forwardRef(function Navigation({ className, closeAncestor, headerProps, isMenuIn = false, links, pathname, style, }, ref) {
12
- const router = useRouter();
13
- return (React.createElement(Links, { ref: ref, className: className, isMenuIn: isMenuIn, style: style }, links.map(({ divider, label, menuItems, selectedPatterns, target = ANCHOR_TARGET.SELF, url, }, i) => menuItems ? (React.createElement(Fragment, { key: i },
14
- isMenuIn ? (React.createElement(NavigationDrawer, { closeAncestor: closeAncestor, headerProps: headerProps, isMenuIn: isMenuIn, isSelected: isNavigationLinkSelected(pathname, selectedPatterns), menuItems: menuItems, menuLabel: label, pathname: pathname })) : (React.createElement(NavigationMenu, { closeAncestor: closeAncestor, isSelected: isNavigationLinkSelected(pathname, selectedPatterns), menuItems: menuItems, menuLabel: label, pathname: pathname })),
15
- divider && React.createElement(Divider, null))) : (React.createElement(Fragment, { key: i },
16
- React.createElement(Button, { disabled: !url, onClick: () => {
17
- closeAncestor?.();
18
- isClientSideNavigation(url)
19
- ? router.push(url)
20
- : window.open(url, target, REL_ATTRIBUTE.NO_OPENER_NO_REFERRER);
21
- }, variant: isNavigationLinkSelected(pathname, selectedPatterns)
22
- ? "activeNav"
23
- : "nav" },
24
- React.createElement(NavigationButtonLabel, { label: label })),
25
- divider && React.createElement(Divider, null))))));
11
+ export const Navigation = forwardRef(function Navigation({ className, closeAncestor, headerProps, isMenuIn = false, links, pathname, style, testId, }, ref) {
12
+ return (React.createElement(Links, { ref: ref, className: className, "data-testid": testId, isMenuIn: isMenuIn, style: style }, links.map(({ divider, label, menuItems, selectedPatterns, target = ANCHOR_TARGET.SELF, url, }, i) => {
13
+ const isClientSide = isClientSideNavigation(url);
14
+ return menuItems ? (React.createElement(Fragment, { key: i },
15
+ isMenuIn ? (React.createElement(NavigationDrawer, { closeAncestor: closeAncestor, headerProps: headerProps, isMenuIn: isMenuIn, isSelected: isNavigationLinkSelected(pathname, selectedPatterns), menuItems: menuItems, menuLabel: label, pathname: pathname })) : (React.createElement(NavigationMenu, { closeAncestor: closeAncestor, isSelected: isNavigationLinkSelected(pathname, selectedPatterns), menuItems: menuItems, menuLabel: label, pathname: pathname })),
16
+ divider && React.createElement(Divider, null))) : (React.createElement(Fragment, { key: i },
17
+ React.createElement(Button, { component: isClientSide ? Link : MLink, disabled: !url, href: url, onClick: () => closeAncestor?.(), rel: isClientSide
18
+ ? REL_ATTRIBUTE.NO_OPENER
19
+ : REL_ATTRIBUTE.NO_OPENER_NO_REFERRER, target: target, variant: isNavigationLinkSelected(pathname, selectedPatterns)
20
+ ? "activeNav"
21
+ : "nav" },
22
+ React.createElement(NavigationButtonLabel, { label: label })),
23
+ divider && React.createElement(Divider, null)));
24
+ })));
26
25
  });
@@ -7,6 +7,7 @@ import { Actions } from "./components/Content/components/Actions/actions";
7
7
  import { Authentication, renderButton as renderAuthenticationButton, renderIconButton as renderAuthenticationIconButton, } from "./components/Content/components/Actions/components/Authentication/authentication";
8
8
  import { Menu } from "./components/Content/components/Actions/components/Menu/menu";
9
9
  import { renderButton as renderSearchButton, renderIconButton as renderSearchIconButton, Search, } from "./components/Content/components/Actions/components/Search/search";
10
+ import { NAVIGATION_TEST_ID } from "./components/Content/components/Navigation/constants";
10
11
  import { Navigation as DXNavigation } from "./components/Content/components/Navigation/navigation";
11
12
  import { Slogan } from "./components/Content/components/Slogan/slogan";
12
13
  import { Divider } from "./components/Content/components/Slogan/slogan.styles";
@@ -38,7 +39,7 @@ export const Header = ({ ...headerProps }) => {
38
39
  isIn.isSloganIn && React.createElement(Slogan, { slogan: slogan }),
39
40
  isIn.isLeftNavigationIn && (React.createElement(DXNavigation, { ...navigationProps, links: navItemsL })))),
40
41
  React.createElement(Fade, { ...FADE_TRANSITION_PROPS, in: isIn.isCenterGroupIn },
41
- React.createElement(Center, null, isIn.isCenterNavigationIn && (React.createElement(DXNavigation, { ...navigationProps, links: navItemsC })))),
42
+ React.createElement(Center, null, isIn.isCenterNavigationIn && (React.createElement(DXNavigation, { ...navigationProps, testId: NAVIGATION_TEST_ID, links: navItemsC })))),
42
43
  React.createElement(Fade, { ...FADE_TRANSITION_PROPS, in: isIn.isRightGroupIn },
43
44
  React.createElement(Right, null,
44
45
  isIn.isRightNavigationIn && (React.createElement(DXNavigation, { ...navigationProps, links: navItemsR })),
@@ -1,3 +1,3 @@
1
- import { ButtonProps } from "@mui/material/Button/Button";
1
+ import { ButtonProps } from "@mui/material";
2
2
  import { BaseComponentProps } from "../../../../theme/common/entities";
3
3
  export type Props = BaseComponentProps & ButtonProps;
@@ -0,0 +1,2 @@
1
+ import { Props } from "./types";
2
+ export declare const Buttons: <P>({ className, handleLogin, providers, ...props }: Props<P>) => JSX.Element[];
@@ -0,0 +1,5 @@
1
+ import React from "react";
2
+ import { Button } from "../Button/button";
3
+ export const Buttons = ({ className, handleLogin, providers = [], ...props /* Mui ButtonProps */ }) => {
4
+ return providers?.map((provider) => (React.createElement(Button, { key: provider.id, className: className, endIcon: "icon" in provider && provider.icon, onClick: () => handleLogin(provider.id), ...props }, provider.name)));
5
+ };
@@ -0,0 +1,8 @@
1
+ import { ButtonProps } from "@mui/material";
2
+ import { ClientSafeProvider } from "next-auth/react";
3
+ import { OAuthProvider } from "../../../../config/entities";
4
+ import { BaseComponentProps } from "../../../../theme/common/entities";
5
+ export interface Props<P> extends BaseComponentProps, ButtonProps {
6
+ handleLogin: (providerId: string) => void;
7
+ providers?: ClientSafeProvider[] | OAuthProvider<P>[];
8
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,3 @@
1
+ import { BaseComponentProps } from "../../../../../types";
2
+ import { ConsentProps } from "./types";
3
+ export declare const Consent: ({ children, className, handleConsent, isDisabled, isError, ...props }: BaseComponentProps & ConsentProps) => JSX.Element | null;
@@ -0,0 +1,14 @@
1
+ import { Checkbox, Typography } from "@mui/material";
2
+ import React from "react";
3
+ import { TEXT_BODY_400 } from "../../../../../../theme/common/typography";
4
+ import { CheckedIcon } from "../../../../../common/CustomIcon/components/CheckedIcon/checkedIcon";
5
+ import { UncheckedErrorIcon } from "../../../../../common/CustomIcon/components/UncheckedErrorIcon/uncheckedErrorIcon";
6
+ import { UncheckedIcon } from "../../../../../common/CustomIcon/components/UncheckedIcon/uncheckedIcon";
7
+ import { StyledGrid2 } from "./consent.styles";
8
+ export const Consent = ({ children, className, handleConsent, isDisabled, isError, ...props /* Mui Grid2Props */ }) => {
9
+ if (isDisabled)
10
+ return null;
11
+ return (React.createElement(StyledGrid2, { className: className, ...props },
12
+ React.createElement(Checkbox, { checkedIcon: React.createElement(CheckedIcon, null), icon: isError ? React.createElement(UncheckedErrorIcon, null) : React.createElement(UncheckedIcon, null), onChange: handleConsent }),
13
+ React.createElement(Typography, { variant: TEXT_BODY_400 }, children)));
14
+ };
@@ -0,0 +1,7 @@
1
+ export declare const StyledGrid2: import("@emotion/styled").StyledComponent<import("@mui/material").GridBaseProps & {
2
+ sx?: import("@mui/material").SxProps<import("@mui/material").Theme>;
3
+ } & import("@mui/system").SystemProps<import("@mui/material").Theme> & Omit<Omit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref"> & {
4
+ ref?: ((instance: HTMLDivElement | null) => void | import("react").DO_NOT_USE_OR_YOU_WILL_BE_FIRED_CALLBACK_REF_RETURN_VALUES[keyof import("react").DO_NOT_USE_OR_YOU_WILL_BE_FIRED_CALLBACK_REF_RETURN_VALUES]) | import("react").RefObject<HTMLDivElement> | null | undefined;
5
+ }, ("fontSize" | "fontWeight" | "lineHeight" | "letterSpacing" | "textTransform" | "p" | "color" | "border" | "borderTop" | "borderRight" | "borderBottom" | "borderLeft" | "borderColor" | "borderRadius" | "display" | "displayPrint" | "overflow" | "textOverflow" | "visibility" | "whiteSpace" | "flexBasis" | "flexDirection" | "flexWrap" | "justifyContent" | "alignItems" | "alignContent" | "order" | "flex" | "flexGrow" | "flexShrink" | "alignSelf" | "justifyItems" | "justifySelf" | "gap" | "columnGap" | "rowGap" | "gridColumn" | "gridRow" | "gridAutoFlow" | "gridAutoColumns" | "gridAutoRows" | "gridTemplateColumns" | "gridTemplateRows" | "gridTemplateAreas" | "gridArea" | "bgcolor" | "zIndex" | "position" | "top" | "right" | "bottom" | "left" | "boxShadow" | "width" | "maxWidth" | "minWidth" | "height" | "maxHeight" | "minHeight" | "boxSizing" | "m" | "mt" | "mr" | "mb" | "ml" | "mx" | "my" | "pt" | "pr" | "pb" | "pl" | "px" | "py" | "margin" | "marginTop" | "marginRight" | "marginBottom" | "marginLeft" | "marginX" | "marginY" | "marginInline" | "marginInlineStart" | "marginInlineEnd" | "marginBlock" | "marginBlockStart" | "marginBlockEnd" | "padding" | "paddingTop" | "paddingRight" | "paddingBottom" | "paddingLeft" | "paddingX" | "paddingY" | "paddingInline" | "paddingInlineStart" | "paddingInlineEnd" | "paddingBlock" | "paddingBlockStart" | "paddingBlockEnd" | "typography" | "fontFamily" | "fontStyle" | "textAlign") | "sx" | keyof import("@mui/material").GridBaseProps> & {
6
+ theme?: import("@emotion/react").Theme;
7
+ }, {}, {}>;
@@ -0,0 +1,14 @@
1
+ import styled from "@emotion/styled";
2
+ import { Grid2 } from "@mui/material";
3
+ export const StyledGrid2 = styled(Grid2) `
4
+ align-items: center;
5
+ align-self: flex-start;
6
+ display: flex;
7
+ gap: 12px;
8
+
9
+ .MuiTypography-text-body-400 {
10
+ .MuiLink-root {
11
+ color: inherit;
12
+ }
13
+ }
14
+ `;
@@ -0,0 +1,6 @@
1
+ import { Grid2Props } from "@mui/material";
2
+ import { ReactNode } from "react";
3
+ import { UseUserConsent } from "../../../../hooks/useUserConsent/types";
4
+ export interface ConsentProps extends Grid2Props, Pick<UseUserConsent, "handleConsent">, Pick<UseUserConsent["state"], "isDisabled" | "isError"> {
5
+ children: ReactNode;
6
+ }
@@ -0,0 +1,3 @@
1
+ import { TypographyProps } from "@mui/material";
2
+ import { BaseComponentProps } from "../../../../../types";
3
+ export declare const Warning: ({ children, className, ...props }: BaseComponentProps & TypographyProps) => JSX.Element | null;
@@ -0,0 +1,9 @@
1
+ import { Typography } 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
+ export const Warning = ({ children, className, ...props /* Mui TypographyOwnProps */ }) => {
6
+ if (!children)
7
+ return null;
8
+ return (React.createElement(Typography, { className: className, color: COLOR.INK_LIGHT, mt: 6, variant: TEXT_BODY_SMALL_400, ...props }, children));
9
+ };
@@ -0,0 +1,10 @@
1
+ import { ChangeEvent } from "react";
2
+ export interface UseUserConsent {
3
+ handleConsent: (e: ChangeEvent<HTMLInputElement>) => void;
4
+ handleError: (error: boolean) => void;
5
+ state: {
6
+ isDisabled: boolean;
7
+ isError: boolean;
8
+ isValid: boolean;
9
+ };
10
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,2 @@
1
+ import { UseUserConsent } from "./types";
2
+ export declare const useUserConsent: () => UseUserConsent;
@@ -0,0 +1,24 @@
1
+ import { useCallback, useState } from "react";
2
+ import { useAuthenticationConfig } from "../../../../hooks/authentication/config/useAuthenticationConfig";
3
+ export const useUserConsent = () => {
4
+ const authConfig = useAuthenticationConfig();
5
+ const [isDisabled] = useState(Boolean(!authConfig?.termsOfService));
6
+ const [isError, setIsError] = useState(false);
7
+ const [isValid, setIsValid] = useState(false);
8
+ const handleError = useCallback((error) => {
9
+ setIsError(error);
10
+ }, []);
11
+ const handleConsent = useCallback((changeEvent) => {
12
+ handleError(false);
13
+ setIsValid(changeEvent.target.checked);
14
+ }, [handleError]);
15
+ return {
16
+ handleConsent,
17
+ handleError,
18
+ state: {
19
+ isDisabled,
20
+ isError,
21
+ isValid,
22
+ },
23
+ };
24
+ };
@@ -0,0 +1,6 @@
1
+ import { ProviderId } from "../../../../providers/authentication/common/types";
2
+ import { UseUserConsent } from "../useUserConsent/types";
3
+ export interface UseUserLogin extends Omit<UseUserConsent, "handleError" | "state"> {
4
+ consentState: Pick<UseUserConsent["state"], "isDisabled" | "isError">;
5
+ handleLogin: (providerId: ProviderId) => void;
6
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,2 @@
1
+ import { UseUserLogin } from "./types";
2
+ export declare const useUserLogin: () => UseUserLogin;
@@ -0,0 +1,21 @@
1
+ import { useCallback } from "react";
2
+ import { useAuth } from "../../../../providers/authentication/auth/hook";
3
+ import { useUserConsent } from "../useUserConsent/useUserConsent";
4
+ export const useUserLogin = () => {
5
+ const { service: { requestLogin } = {} } = useAuth();
6
+ const { handleConsent, handleError, state: consentState } = useUserConsent();
7
+ const { isDisabled, isError, isValid } = consentState; // Consent state: { isValid } is an indicator of whether the user has accepted the login terms.
8
+ const handleLogin = useCallback((providerId) => {
9
+ if (!isDisabled && !isValid) {
10
+ // If the user has not accepted terms, set error state to true.
11
+ handleError(true);
12
+ return;
13
+ }
14
+ requestLogin?.(providerId);
15
+ }, [handleError, isDisabled, isValid, requestLogin]);
16
+ return {
17
+ consentState: { isDisabled, isError },
18
+ handleConsent,
19
+ handleLogin,
20
+ };
21
+ };
@@ -0,0 +1,2 @@
1
+ import { SvgIconProps } from "@mui/material";
2
+ export declare const CloseIcon: ({ fontSize, viewBox, ...props }: SvgIconProps) => JSX.Element;
@@ -0,0 +1,6 @@
1
+ import { SvgIcon } from "@mui/material";
2
+ import React from "react";
3
+ export const CloseIcon = ({ fontSize = "xsmall", viewBox = "0 0 18 18", ...props /* Spread props to allow for Mui SvgIconProps specific prop overrides e.g. "htmlColor". */ }) => {
4
+ return (React.createElement(SvgIcon, { fontSize: fontSize, viewBox: viewBox, ...props },
5
+ React.createElement("path", { 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", fill: "currentColor" })));
6
+ };
@@ -0,0 +1,6 @@
1
+ import { DialogContentTextProps, DialogProps, DialogTitleProps, IconButtonProps, IconProps } from "@mui/material";
2
+ export declare const DIALOG_CONTENT_TEXT_PROPS: DialogContentTextProps;
3
+ export declare const DIALOG_PROPS: Partial<DialogProps>;
4
+ export declare const DIALOG_TITLE_PROPS: DialogTitleProps;
5
+ export declare const ICON_BUTTON_PROPS: IconButtonProps;
6
+ export declare const ICON_PROPS: Pick<IconProps, "fontSize">;
@@ -0,0 +1,21 @@
1
+ import { FONT_SIZE } from "../../../styles/common/mui/icon";
2
+ import { COLOR, VARIANT } from "../../../styles/common/mui/typography";
3
+ export const DIALOG_CONTENT_TEXT_PROPS = {
4
+ color: COLOR.INK_LIGHT,
5
+ component: "div",
6
+ variant: VARIANT.TEXT_BODY_400,
7
+ };
8
+ export const DIALOG_PROPS = {
9
+ PaperProps: { elevation: 0 },
10
+ };
11
+ export const DIALOG_TITLE_PROPS = {
12
+ variant: VARIANT.TEXT_HEADING_SMALL,
13
+ };
14
+ export const ICON_BUTTON_PROPS = {
15
+ color: "inkLight",
16
+ edge: "end",
17
+ size: "xsmall",
18
+ };
19
+ export const ICON_PROPS = {
20
+ fontSize: FONT_SIZE.SMALL,
21
+ };
@@ -0,0 +1,2 @@
1
+ import { LoginDialogProps } from "./types";
2
+ export declare const LoginDialog: ({ onClose, open, }: LoginDialogProps) => JSX.Element | null;
@@ -0,0 +1,27 @@
1
+ import { DialogActions, DialogContent, DialogContentText, DialogTitle, IconButton, } from "@mui/material";
2
+ import React from "react";
3
+ import { useAuthenticationConfig } from "../../../hooks/authentication/config/useAuthenticationConfig";
4
+ import { Buttons } from "../../Login/components/Buttons/buttons";
5
+ import { Consent } from "../../Login/components/Section/components/Consent/consent";
6
+ import { Warning } from "../../Login/components/Section/components/Warning/warning";
7
+ import { useUserLogin } from "../../Login/hooks/useUserLogin/useUserLogin";
8
+ import { CloseIcon } from "../CustomIcon/components/CloseIcon/closeIcon";
9
+ import { DIALOG_CONTENT_TEXT_PROPS, DIALOG_PROPS, DIALOG_TITLE_PROPS, ICON_BUTTON_PROPS, ICON_PROPS, } from "./constants";
10
+ import { StyledDialog } from "./loginDialog.styles";
11
+ export const LoginDialog = ({ onClose, open, }) => {
12
+ const authConfig = useAuthenticationConfig();
13
+ const { consentState, handleConsent, handleLogin } = useUserLogin();
14
+ if (!authConfig)
15
+ return null;
16
+ return (React.createElement(StyledDialog, { ...DIALOG_PROPS, onClose: onClose, open: open },
17
+ React.createElement(DialogTitle, { ...DIALOG_TITLE_PROPS },
18
+ React.createElement("div", null, "Sign In Required"),
19
+ React.createElement(IconButton, { ...ICON_BUTTON_PROPS, onClick: onClose },
20
+ React.createElement(CloseIcon, { ...ICON_PROPS }))),
21
+ React.createElement(DialogContent, null,
22
+ React.createElement(DialogContentText, { ...DIALOG_CONTENT_TEXT_PROPS }, "Please sign in to proceed with this action."),
23
+ React.createElement(Consent, { handleConsent: handleConsent, ...consentState }, authConfig.termsOfService)),
24
+ React.createElement(DialogActions, { disableSpacing: true },
25
+ React.createElement(Buttons, { handleLogin: handleLogin, providers: authConfig.providers })),
26
+ React.createElement(Warning, null, authConfig.warning)));
27
+ };
@@ -0,0 +1,3 @@
1
+ export declare const StyledDialog: import("@emotion/styled").StyledComponent<import("@mui/material").DialogProps & {
2
+ theme?: import("@emotion/react").Theme;
3
+ }, {}, {}>;
@@ -0,0 +1,50 @@
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
+ export const StyledDialog = styled(Dialog) `
6
+ &.MuiDialog-root {
7
+ .MuiBackdrop-root {
8
+ background-color: ${inkMain}${alpha80};
9
+ }
10
+
11
+ .MuiDialog-paper {
12
+ border-radius: 8px;
13
+ max-width: 400px;
14
+ padding: 32px;
15
+ position: relative; /* positions close icon */
16
+
17
+ .MuiDialogTitle-root,
18
+ .MuiDialogContent-root,
19
+ .MuiDialogActions-root {
20
+ padding: 0;
21
+ }
22
+
23
+ .MuiDialogTitle-root {
24
+ font-size: 20px;
25
+
26
+ .MuiIconButton-root {
27
+ position: absolute;
28
+ right: 12px;
29
+ top: 12px;
30
+ }
31
+ }
32
+
33
+ .MuiDialogContent-root {
34
+ .MuiDialogContentText-root {
35
+ margin: 8px 0;
36
+ }
37
+
38
+ .MuiGrid2-root {
39
+ margin: 24px 0;
40
+ }
41
+ }
42
+
43
+ .MuiDialogActions-root {
44
+ display: flex;
45
+ flex-direction: column;
46
+ gap: 16px 0;
47
+ }
48
+ }
49
+ }
50
+ `;
@@ -0,0 +1,4 @@
1
+ export interface LoginDialogProps {
2
+ onClose: () => void;
3
+ open: boolean;
4
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -303,6 +303,7 @@ export interface SiteConfig {
303
303
  entities: EntityConfig[];
304
304
  explorerTitle: HeroTitle;
305
305
  export?: ExportConfig;
306
+ exportsRequireAuth?: boolean;
306
307
  exportToTerraUrl?: string;
307
308
  gitHubUrl?: string;
308
309
  layout: {