@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.
Files changed (109) hide show
  1. package/.release-please-manifest.json +1 -1
  2. package/CHANGELOG.md +15 -0
  3. package/lib/common/entities.d.ts +33 -0
  4. package/lib/components/DataDictionary/common/utils.d.ts +38 -0
  5. package/lib/components/DataDictionary/common/utils.js +122 -0
  6. package/lib/components/Export/components/ExportForm/components/ExportButton/exportButton.js +6 -1
  7. package/lib/components/Export/components/ManifestDownload/components/ManifestDownloadEntity/components/FileManifestDownload/fileManifestDownload.js +5 -2
  8. package/lib/components/Filter/components/Filter/filter.js +1 -1
  9. package/lib/components/Filter/components/FilterLabel/filterLabel.d.ts +3 -1
  10. package/lib/components/Filter/components/FilterLabel/filterLabel.js +4 -2
  11. package/lib/components/Index/components/AzulFileDownload/azulFileDownload.js +10 -5
  12. package/lib/components/Index/components/Tabs/common/utils.js +2 -1
  13. package/lib/components/Layout/components/Header/components/Content/components/Navigation/components/NavigationMenu/navigationMenu.js +1 -1
  14. package/lib/components/Layout/components/Header/components/Content/components/Navigation/components/NavigationMenuItems/navigationMenuItems.js +20 -21
  15. package/lib/components/Layout/components/Header/components/Content/components/Navigation/constants.d.ts +1 -0
  16. package/lib/components/Layout/components/Header/components/Content/components/Navigation/constants.js +1 -0
  17. package/lib/components/Layout/components/Header/components/Content/components/Navigation/navigation.d.ts +2 -1
  18. package/lib/components/Layout/components/Header/components/Content/components/Navigation/navigation.js +16 -17
  19. package/lib/components/Layout/components/Header/header.js +2 -1
  20. package/lib/components/Login/components/Button/types.d.ts +1 -1
  21. package/lib/components/Login/components/Buttons/buttons.d.ts +2 -0
  22. package/lib/components/Login/components/Buttons/buttons.js +5 -0
  23. package/lib/components/Login/components/Buttons/types.d.ts +8 -0
  24. package/lib/components/Login/components/Buttons/types.js +1 -0
  25. package/lib/components/Login/components/Section/components/Consent/consent.d.ts +3 -0
  26. package/lib/components/Login/components/Section/components/Consent/consent.js +14 -0
  27. package/lib/components/Login/components/Section/components/Consent/consent.styles.d.ts +7 -0
  28. package/lib/components/Login/components/Section/components/Consent/consent.styles.js +14 -0
  29. package/lib/components/Login/components/Section/components/Consent/types.d.ts +6 -0
  30. package/lib/components/Login/components/Section/components/Consent/types.js +1 -0
  31. package/lib/components/Login/components/Section/components/Warning/warning.d.ts +3 -0
  32. package/lib/components/Login/components/Section/components/Warning/warning.js +9 -0
  33. package/lib/components/Login/hooks/useUserConsent/types.d.ts +10 -0
  34. package/lib/components/Login/hooks/useUserConsent/types.js +1 -0
  35. package/lib/components/Login/hooks/useUserConsent/useUserConsent.d.ts +2 -0
  36. package/lib/components/Login/hooks/useUserConsent/useUserConsent.js +24 -0
  37. package/lib/components/Login/hooks/useUserLogin/types.d.ts +6 -0
  38. package/lib/components/Login/hooks/useUserLogin/types.js +1 -0
  39. package/lib/components/Login/hooks/useUserLogin/useUserLogin.d.ts +2 -0
  40. package/lib/components/Login/hooks/useUserLogin/useUserLogin.js +21 -0
  41. package/lib/components/Table/components/TableHead/tableHead.js +4 -1
  42. package/lib/components/common/CustomIcon/components/CloseIcon/closeIcon.d.ts +2 -0
  43. package/lib/components/common/CustomIcon/components/CloseIcon/closeIcon.js +6 -0
  44. package/lib/components/common/LoginDialog/constants.d.ts +6 -0
  45. package/lib/components/common/LoginDialog/constants.js +21 -0
  46. package/lib/components/common/LoginDialog/loginDialog.d.ts +2 -0
  47. package/lib/components/common/LoginDialog/loginDialog.js +27 -0
  48. package/lib/components/common/LoginDialog/loginDialog.styles.d.ts +3 -0
  49. package/lib/components/common/LoginDialog/loginDialog.styles.js +50 -0
  50. package/lib/components/common/LoginDialog/types.d.ts +4 -0
  51. package/lib/components/common/LoginDialog/types.js +1 -0
  52. package/lib/components/common/Tabs/tabs.d.ts +2 -0
  53. package/lib/components/common/Tabs/tabs.js +14 -1
  54. package/lib/config/entities.d.ts +6 -1
  55. package/lib/hooks/useCategoryFilter.js +1 -0
  56. package/lib/providers/config.js +9 -2
  57. package/lib/providers/loginGuard/common/types.d.ts +18 -0
  58. package/lib/providers/loginGuard/common/types.js +1 -0
  59. package/lib/providers/loginGuard/context.d.ts +6 -0
  60. package/lib/providers/loginGuard/context.js +10 -0
  61. package/lib/providers/loginGuard/hook.d.ts +9 -0
  62. package/lib/providers/loginGuard/hook.js +12 -0
  63. package/lib/providers/loginGuard/provider.d.ts +11 -0
  64. package/lib/providers/loginGuard/provider.js +55 -0
  65. package/lib/styles/common/mui/typography.d.ts +1 -0
  66. package/lib/styles/common/mui/typography.js +7 -0
  67. package/package.json +1 -1
  68. package/src/common/entities.ts +37 -0
  69. package/src/components/DataDictionary/common/utils.ts +160 -0
  70. package/src/components/Export/components/ExportForm/components/ExportButton/exportButton.tsx +8 -1
  71. package/src/components/Export/components/ManifestDownload/components/ManifestDownloadEntity/components/FileManifestDownload/fileManifestDownload.tsx +11 -3
  72. package/src/components/Filter/components/Filter/filter.tsx +1 -0
  73. package/src/components/Filter/components/FilterLabel/filterLabel.tsx +16 -10
  74. package/src/components/Index/components/AzulFileDownload/azulFileDownload.tsx +12 -5
  75. package/src/components/Index/components/Tabs/common/utils.ts +2 -0
  76. package/src/components/Layout/components/Header/components/Content/components/Navigation/components/NavigationMenu/navigationMenu.tsx +1 -1
  77. package/src/components/Layout/components/Header/components/Content/components/Navigation/components/NavigationMenuItems/navigationMenuItems.tsx +16 -15
  78. package/src/components/Layout/components/Header/components/Content/components/Navigation/constants.ts +1 -0
  79. package/src/components/Layout/components/Header/components/Content/components/Navigation/navigation.tsx +26 -18
  80. package/src/components/Layout/components/Header/header.tsx +6 -1
  81. package/src/components/Login/components/Button/types.ts +1 -1
  82. package/src/components/Login/components/Buttons/buttons.tsx +22 -0
  83. package/src/components/Login/components/Buttons/types.ts +9 -0
  84. package/src/components/Login/components/Section/components/Consent/consent.styles.ts +15 -0
  85. package/src/components/Login/components/Section/components/Consent/consent.tsx +30 -0
  86. package/src/components/Login/components/Section/components/Consent/types.ts +10 -0
  87. package/src/components/Login/components/Section/components/Warning/warning.tsx +24 -0
  88. package/src/components/Login/hooks/useUserConsent/types.ts +11 -0
  89. package/src/components/Login/hooks/useUserConsent/useUserConsent.ts +32 -0
  90. package/src/components/Login/hooks/useUserLogin/types.ts +8 -0
  91. package/src/components/Login/hooks/useUserLogin/useUserLogin.ts +29 -0
  92. package/src/components/Table/components/TableHead/tableHead.tsx +26 -15
  93. package/src/components/common/CustomIcon/components/CloseIcon/closeIcon.tsx +17 -0
  94. package/src/components/common/LoginDialog/constants.ts +33 -0
  95. package/src/components/common/LoginDialog/loginDialog.styles.ts +51 -0
  96. package/src/components/common/LoginDialog/loginDialog.tsx +56 -0
  97. package/src/components/common/LoginDialog/types.ts +4 -0
  98. package/src/components/common/Tabs/tabs.tsx +33 -3
  99. package/src/config/entities.ts +11 -1
  100. package/src/hooks/useCategoryFilter.ts +1 -0
  101. package/src/providers/config.tsx +10 -2
  102. package/src/providers/loginGuard/common/types.ts +21 -0
  103. package/src/providers/loginGuard/context.ts +12 -0
  104. package/src/providers/loginGuard/hook.ts +14 -0
  105. package/src/providers/loginGuard/provider.tsx +76 -0
  106. package/src/styles/common/mui/typography.ts +8 -0
  107. package/tests/dataDictionary_utils.test.ts +153 -0
  108. package/tests/provider.test.tsx +191 -0
  109. package/types/data-explorer-ui.d.ts +2 -0
@@ -0,0 +1,160 @@
1
+ import {
2
+ Attribute,
3
+ Class,
4
+ DataDictionary,
5
+ DataDictionaryAnnotation,
6
+ } from "../../../common/entities";
7
+ import { CategoryGroupConfig, SiteConfig } from "../../../config/entities";
8
+
9
+ /**
10
+ * Annotate each entity column configuration with data dictionary values. Specifically,
11
+ * look up label and description for each column key.
12
+ * @param siteConfig - Site configuration to annotate.
13
+ * @param annotationsByKey - Data dictionary annotations keyed by key.
14
+ */
15
+ export function annotateColumnConfig(
16
+ siteConfig: SiteConfig,
17
+ annotationsByKey: Record<string, DataDictionaryAnnotation>
18
+ ): void {
19
+ // Annotate every column in every entity.
20
+ siteConfig.entities.forEach((entity) => {
21
+ entity.list.columns.forEach((columnConfig) => {
22
+ // Find the annotation for the column key.
23
+ const annotation = annotationsByKey[columnConfig.id];
24
+ if (!annotation) {
25
+ return;
26
+ }
27
+
28
+ if (!columnConfig.meta) {
29
+ columnConfig.meta = {};
30
+ }
31
+ columnConfig.meta.annotation = annotation;
32
+ });
33
+ });
34
+ }
35
+
36
+ /**
37
+ * Annotate filter and colummn configuration with data dictionary values. Note this
38
+ * functionality mutates the site config. A possible future improvement would be to
39
+ * create either a specific "raw" or "annotated" type to indicate clearly the point
40
+ * at which the config has been annotated.
41
+ * @param siteConfig - The site configuration to annotate.
42
+ */
43
+ export function annotateSiteConfig(siteConfig: SiteConfig): void {
44
+ // Build and map data dictionary annotations by key.
45
+ const { dataDictionary } = siteConfig;
46
+ if (!dataDictionary) {
47
+ return;
48
+ }
49
+ const annotationsByKey = keyAnnotationsByKey(dataDictionary);
50
+
51
+ // Annotate elements of site config.
52
+ annotateEntityConfig(siteConfig, annotationsByKey);
53
+ annotateDefaultCategoryConfig(siteConfig, annotationsByKey);
54
+ annotateEntityCategoryConfig(siteConfig, annotationsByKey);
55
+ annotateColumnConfig(siteConfig, annotationsByKey);
56
+ }
57
+
58
+ /**
59
+ * Annotate entity configuration with data dictionary values. Specifically, look
60
+ * up label and description for each entity key.
61
+ * @param siteConfig - The site configuration to annotate.
62
+ * @param annotationsByKey - Data dictionary annotations keyed by key.
63
+ */
64
+ export function annotateEntityConfig(
65
+ siteConfig: SiteConfig,
66
+ annotationsByKey: Record<string, DataDictionaryAnnotation>
67
+ ): void {
68
+ // Annotate every entity.
69
+ siteConfig.entities.forEach((entityConfig) => {
70
+ // Check entity for a data dictionary key.
71
+ const { key } = entityConfig;
72
+ if (!key) {
73
+ return;
74
+ }
75
+
76
+ // Find corresponding annotation for the key and set on entity config.
77
+ entityConfig.annotation = annotationsByKey[key];
78
+ });
79
+ }
80
+
81
+ /**
82
+ * Annotate top-level (app-wide) category config with data dictionary values.
83
+ * Specifically, look up label and description for each filter key.
84
+ * @param siteConfig - Site configuration to annotate.
85
+ * @param annotationsByKey - Data dictionary annotations keyed by key.
86
+ */
87
+ export function annotateDefaultCategoryConfig(
88
+ siteConfig: SiteConfig,
89
+ annotationsByKey: Record<string, DataDictionaryAnnotation>
90
+ ): void {
91
+ const { categoryGroupConfig } = siteConfig;
92
+ if (categoryGroupConfig) {
93
+ annotateCategoryGroupConfig(categoryGroupConfig, annotationsByKey);
94
+ }
95
+ }
96
+
97
+ /**
98
+ * Annotate entity-specific category config with data dictionary values. Specifically,
99
+ * look up label and description for each category key.
100
+ * @param siteConfig - Site configuration to annotate.
101
+ * @param annotationsByKey - Data dictionary annotations keyed by key.
102
+ */
103
+ export function annotateEntityCategoryConfig(
104
+ siteConfig: SiteConfig,
105
+ annotationsByKey: Record<string, DataDictionaryAnnotation>
106
+ ): void {
107
+ // Annotate every category in every entity.
108
+ siteConfig.entities.forEach((entityConfig) => {
109
+ const { categoryGroupConfig } = entityConfig;
110
+ if (categoryGroupConfig) {
111
+ annotateCategoryGroupConfig(categoryGroupConfig, annotationsByKey);
112
+ }
113
+ });
114
+ }
115
+
116
+ /**
117
+ * Annonate category group configuration with data dictionary values.
118
+ * @param categoryGroupConfig - Category group to annotate.
119
+ * @param annotationsByKey - Data dictionary annotations keyed by key.
120
+ */
121
+ function annotateCategoryGroupConfig(
122
+ categoryGroupConfig: CategoryGroupConfig,
123
+ annotationsByKey: Record<string, DataDictionaryAnnotation>
124
+ ): void {
125
+ categoryGroupConfig.categoryGroups.forEach((categoryGroup) => {
126
+ categoryGroup.categoryConfigs.forEach((categorConfig) => {
127
+ categorConfig.annotation = annotationsByKey[categorConfig.key];
128
+ });
129
+ });
130
+ }
131
+
132
+ /**
133
+ * Transform a data dictionary into a key-annotation map. Build annotations for both
134
+ * classes and attributes and add to map.
135
+ * @param dataDictionary - Data dictionary to transform into a key-annotation map.
136
+ * @returns Key-annotation map.
137
+ */
138
+ function keyAnnotationsByKey(
139
+ dataDictionary: DataDictionary
140
+ ): Record<string, DataDictionaryAnnotation> {
141
+ return dataDictionary.classes.reduce(
142
+ (acc: Record<string, DataDictionaryAnnotation>, cls: Class) => {
143
+ // Add class to map.
144
+ acc[cls.key] = {
145
+ description: cls.description,
146
+ label: cls.label,
147
+ };
148
+
149
+ // Add each class attribute to the map.
150
+ cls.attributes.forEach((attribute: Attribute) => {
151
+ acc[attribute.key] = {
152
+ description: attribute.description,
153
+ label: attribute.label,
154
+ };
155
+ });
156
+ return acc;
157
+ },
158
+ {} as Record<string, DataDictionaryAnnotation>
159
+ );
160
+ }
@@ -2,6 +2,7 @@ import { Tooltip } from "@mui/material";
2
2
  import React, { ElementType, ReactNode } 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
 
7
8
  export interface ExportButtonProps {
@@ -19,6 +20,10 @@ export const ExportButton = ({
19
20
  const {
20
21
  fileManifestState: { isLoading },
21
22
  } = useFileManifestState();
23
+
24
+ // Prompt user for login before export, if required.
25
+ const { requireLogin } = useLoginGuard();
26
+
22
27
  return (
23
28
  <Tooltip arrow title={isLoading ? null : downloadStatus.message}>
24
29
  <span>
@@ -26,7 +31,9 @@ export const ExportButton = ({
26
31
  disabled={
27
32
  isLoading || downloadStatus.disabled || downloadStatus.isLoading
28
33
  }
29
- onClick={onClick}
34
+ onClick={() => {
35
+ requireLogin(onClick);
36
+ }}
30
37
  >
31
38
  <span>{children}</span>
32
39
  </Button>
@@ -10,6 +10,7 @@ import React, { useRef } from "react";
10
10
  import { Filters } from "../../../../../../../../common/entities";
11
11
  import { useDownloadStatus } from "../../../../../../../../hooks/useDownloadStatus";
12
12
  import { useFileManifestDownload } from "../../../../../../../../hooks/useFileManifest/useFileManifestDownload";
13
+ import { useLoginGuard } from "../../../../../../../../providers/loginGuard/hook";
13
14
  import { ButtonGroup } from "../../../../../../../common/ButtonGroup/buttonGroup";
14
15
  import { ButtonGroupButton } from "../../../../../../../common/ButtonGroup/components/ButtonGroupButton/buttonGroupButton";
15
16
  import {
@@ -46,6 +47,9 @@ export const FileManifestDownload = ({
46
47
  const isInProgress = (isIdle || isLoading) && !disabled;
47
48
  const isReady = Boolean(manifestURL) || disabled;
48
49
 
50
+ // Prompt user for login before download and copy, if required.
51
+ const { requireLogin } = useLoginGuard();
52
+
49
53
  // Copies file manifest.
50
54
  const copyManifestURL = (url?: string): void => {
51
55
  if (!url) return;
@@ -89,15 +93,19 @@ export const FileManifestDownload = ({
89
93
  action="Download file manifest"
90
94
  disabled={disabled}
91
95
  label={<DownloadIconSmall />}
92
- onClick={downloadManifestURL}
96
+ onClick={() =>
97
+ requireLogin(downloadManifestURL)
98
+ }
93
99
  />,
94
100
  <ButtonGroupButton
95
101
  key="copy"
96
102
  action="Copy file manifest"
97
103
  disabled={disabled}
98
104
  label={<ContentCopyIconSmall />}
99
- onClick={(): void =>
100
- copyManifestURL(manifestURL)
105
+ onClick={() =>
106
+ requireLogin((): void =>
107
+ copyManifestURL(manifestURL)
108
+ )
101
109
  }
102
110
  />,
103
111
  ]}
@@ -77,6 +77,7 @@ export const Filter = ({
77
77
  return (
78
78
  <>
79
79
  <FilterLabel
80
+ annotation={categoryView.annotation}
80
81
  count={categoryView.values.length}
81
82
  disabled={categoryView.isDisabled}
82
83
  isOpen={isOpen}
@@ -1,8 +1,11 @@
1
1
  import ArrowDropDownRoundedIcon from "@mui/icons-material/ArrowDropDownRounded";
2
2
  import React, { MouseEvent } from "react";
3
+ import { DataDictionaryAnnotation } from "../../../../common/entities";
4
+ import { Tooltip } from "../../../DataDictionary/components/Tooltip/tooltip";
3
5
  import { FilterLabel as Label } from "./filterLabel.styles";
4
6
 
5
7
  export interface FilterLabelProps {
8
+ annotation?: DataDictionaryAnnotation;
6
9
  count?: number;
7
10
  disabled?: boolean;
8
11
  isOpen: boolean;
@@ -11,6 +14,7 @@ export interface FilterLabelProps {
11
14
  }
12
15
 
13
16
  export const FilterLabel = ({
17
+ annotation,
14
18
  count,
15
19
  disabled = false,
16
20
  isOpen,
@@ -19,15 +23,17 @@ export const FilterLabel = ({
19
23
  }: FilterLabelProps): JSX.Element => {
20
24
  const filterLabel = count ? `${label}\xa0(${count})` : label; // When the count is present, a non-breaking space is used to prevent it from being on its own line
21
25
  return (
22
- <Label
23
- color="inherit"
24
- disabled={disabled}
25
- endIcon={<ArrowDropDownRoundedIcon fontSize="small" />}
26
- fullWidth
27
- isOpen={isOpen}
28
- onClick={onClick}
29
- >
30
- {filterLabel}
31
- </Label>
26
+ <Tooltip description={annotation?.description} title={annotation?.label}>
27
+ <Label
28
+ color="inherit"
29
+ disabled={disabled}
30
+ endIcon={<ArrowDropDownRoundedIcon fontSize="small" />}
31
+ fullWidth
32
+ isOpen={isOpen}
33
+ onClick={onClick}
34
+ >
35
+ {filterLabel}
36
+ </Label>
37
+ </Tooltip>
32
38
  );
33
39
  };
@@ -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";
@@ -29,6 +30,9 @@ export const AzulFileDownload = ({
29
30
  const downloadRef = useRef<HTMLAnchorElement>(null);
30
31
  const [isRequestPending, setIsRequestPending] = useState(false);
31
32
 
33
+ // Prompt user for login before download, if required.
34
+ const { requireLogin } = useLoginGuard();
35
+
32
36
  // Initiates file download when file location request is successful.
33
37
  useEffect(() => {
34
38
  if (!fileUrl) return;
@@ -39,6 +43,13 @@ export const AzulFileDownload = ({
39
43
  setIsRequestPending(false);
40
44
  }, [fileUrl]);
41
45
 
46
+ // Initiates file download when download button is clicked.
47
+ const handleDownloadClick = (): void => {
48
+ setIsRequestPending(true);
49
+ trackFileDownloaded(entityName, relatedEntityId, relatedEntityName);
50
+ run();
51
+ };
52
+
42
53
  return (
43
54
  <Fragment>
44
55
  {isRequestPending ? (
@@ -54,11 +65,7 @@ export const AzulFileDownload = ({
54
65
  data-testid={AZUL_FILE_REQUEST_DOWNLOAD_TEST_ID}
55
66
  disabled={!url}
56
67
  Icon={isLoading ? LoadingIcon : DownloadIcon}
57
- onClick={(): void => {
58
- setIsRequestPending(true);
59
- trackFileDownloaded(entityName, relatedEntityId, relatedEntityName);
60
- run();
61
- }}
68
+ onClick={() => requireLogin(handleDownloadClick)}
62
69
  size="medium"
63
70
  />
64
71
  )}
@@ -11,6 +11,7 @@ export function getEntityListTabs(entities: EntityConfig[]): Tab[] {
11
11
  (
12
12
  acc: Tab[],
13
13
  {
14
+ annotation,
14
15
  label,
15
16
  listView: { enableTab = true } = {},
16
17
  route,
@@ -20,6 +21,7 @@ export function getEntityListTabs(entities: EntityConfig[]): Tab[] {
20
21
  ) => {
21
22
  if (enableTab) {
22
23
  acc.push({
24
+ annotation,
23
25
  icon,
24
26
  iconPosition,
25
27
  label,
@@ -78,7 +78,7 @@ export const NavigationMenu = ({
78
78
  >
79
79
  <MPaper variant="menu">
80
80
  <MClickAwayListener onClickAway={onClose}>
81
- <MMenuList>
81
+ <MMenuList component="div">
82
82
  <NavigationMenuItems
83
83
  closeMenu={(): void => {
84
84
  onClose();
@@ -2,9 +2,10 @@ import {
2
2
  Divider,
3
3
  ListItemIcon,
4
4
  ListItemText,
5
+ Link as MLink,
5
6
  MenuItem as MMenuItem,
6
7
  } from "@mui/material";
7
- import { useRouter } from "next/router";
8
+ import Link from "next/link";
8
9
  import React, { Fragment, ReactNode } from "react";
9
10
  import {
10
11
  TEXT_BODY_400,
@@ -38,7 +39,6 @@ export const NavigationMenuItems = ({
38
39
  menuItems,
39
40
  pathname,
40
41
  }: NavLinkMenuProps): JSX.Element => {
41
- const router = useRouter();
42
42
  return (
43
43
  <>
44
44
  {menuItems.map(
@@ -54,8 +54,9 @@ export const NavigationMenuItems = ({
54
54
  url,
55
55
  },
56
56
  i
57
- ) =>
58
- nestedMenuItems ? (
57
+ ) => {
58
+ const isClientSide = isClientSideNavigation(url);
59
+ return nestedMenuItems ? (
59
60
  <NavigationMenu
60
61
  key={i}
61
62
  closeAncestor={closeMenu}
@@ -69,18 +70,17 @@ export const NavigationMenuItems = ({
69
70
  ) : (
70
71
  <Fragment key={i}>
71
72
  <MMenuItem
73
+ component={isClientSide ? Link : MLink}
72
74
  disabled={!url}
73
- onClick={(): void => {
74
- closeMenu();
75
- isClientSideNavigation(url)
76
- ? router.push(url)
77
- : window.open(
78
- url,
79
- target,
80
- REL_ATTRIBUTE.NO_OPENER_NO_REFERRER
81
- );
82
- }}
75
+ href={url}
76
+ onClick={(): void => closeMenu()}
77
+ rel={
78
+ isClientSide
79
+ ? REL_ATTRIBUTE.NO_OPENER
80
+ : REL_ATTRIBUTE.NO_OPENER_NO_REFERRER
81
+ }
83
82
  selected={isNavigationLinkSelected(pathname, selectedPatterns)}
83
+ target={target}
84
84
  >
85
85
  {icon && <ListItemIcon>{icon}</ListItemIcon>}
86
86
  <ListItemText
@@ -100,7 +100,8 @@ export const NavigationMenuItems = ({
100
100
  </MMenuItem>
101
101
  {divider && <Divider />}
102
102
  </Fragment>
103
- )
103
+ );
104
+ }
104
105
  )}
105
106
  </>
106
107
  );
@@ -0,0 +1 @@
1
+ export const NAVIGATION_TEST_ID = "navigation";
@@ -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, { CSSProperties, forwardRef, Fragment, ReactNode } from "react";
4
4
  import { BreakpointKey } from "../../../../../../../../hooks/useBreakpointHelper";
5
5
  import {
@@ -7,6 +7,7 @@ import {
7
7
  REL_ATTRIBUTE,
8
8
  } from "../../../../../../../Links/common/entities";
9
9
  import { isClientSideNavigation } from "../../../../../../../Links/common/utils";
10
+ import { TestIdProps } from "../../../../../../../types";
10
11
  import { SelectedMatch } from "../../../../common/entities";
11
12
  import { HeaderProps } from "../../../../header";
12
13
  import { isNavigationLinkSelected } from "./common/utils";
@@ -28,7 +29,7 @@ export interface NavLinkItem {
28
29
  visible?: Partial<Record<BreakpointKey, boolean>>;
29
30
  }
30
31
 
31
- export interface NavigationProps {
32
+ export interface NavigationProps extends TestIdProps {
32
33
  className?: string;
33
34
  closeAncestor?: () => void;
34
35
  headerProps?: HeaderProps;
@@ -48,12 +49,18 @@ export const Navigation = forwardRef<HTMLDivElement, NavigationProps>(
48
49
  links,
49
50
  pathname,
50
51
  style,
52
+ testId,
51
53
  }: NavigationProps,
52
54
  ref
53
55
  ): JSX.Element {
54
- const router = useRouter();
55
56
  return (
56
- <Links ref={ref} className={className} isMenuIn={isMenuIn} style={style}>
57
+ <Links
58
+ ref={ref}
59
+ className={className}
60
+ data-testid={testId}
61
+ isMenuIn={isMenuIn}
62
+ style={style}
63
+ >
57
64
  {links.map(
58
65
  (
59
66
  {
@@ -65,8 +72,9 @@ export const Navigation = forwardRef<HTMLDivElement, NavigationProps>(
65
72
  url,
66
73
  },
67
74
  i
68
- ) =>
69
- menuItems ? (
75
+ ) => {
76
+ const isClientSide = isClientSideNavigation(url);
77
+ return menuItems ? (
70
78
  <Fragment key={i}>
71
79
  {isMenuIn ? (
72
80
  <NavigationDrawer
@@ -98,17 +106,16 @@ export const Navigation = forwardRef<HTMLDivElement, NavigationProps>(
98
106
  ) : (
99
107
  <Fragment key={i}>
100
108
  <Button
109
+ component={isClientSide ? Link : MLink}
101
110
  disabled={!url}
102
- onClick={(): void => {
103
- closeAncestor?.();
104
- isClientSideNavigation(url)
105
- ? router.push(url)
106
- : window.open(
107
- url,
108
- target,
109
- REL_ATTRIBUTE.NO_OPENER_NO_REFERRER
110
- );
111
- }}
111
+ href={url}
112
+ onClick={(): void => closeAncestor?.()}
113
+ rel={
114
+ isClientSide
115
+ ? REL_ATTRIBUTE.NO_OPENER
116
+ : REL_ATTRIBUTE.NO_OPENER_NO_REFERRER
117
+ }
118
+ target={target}
112
119
  variant={
113
120
  isNavigationLinkSelected(pathname, selectedPatterns)
114
121
  ? "activeNav"
@@ -119,7 +126,8 @@ export const Navigation = forwardRef<HTMLDivElement, NavigationProps>(
119
126
  </Button>
120
127
  {divider && <Divider />}
121
128
  </Fragment>
122
- )
129
+ );
130
+ }
123
131
  )}
124
132
  </Links>
125
133
  );
@@ -21,6 +21,7 @@ import {
21
21
  renderIconButton as renderSearchIconButton,
22
22
  Search,
23
23
  } from "./components/Content/components/Actions/components/Search/search";
24
+ import { NAVIGATION_TEST_ID } from "./components/Content/components/Navigation/constants";
24
25
  import { Navigation as DXNavigation } from "./components/Content/components/Navigation/navigation";
25
26
  import { Slogan } from "./components/Content/components/Slogan/slogan";
26
27
  import { Divider } from "./components/Content/components/Slogan/slogan.styles";
@@ -92,7 +93,11 @@ export const Header = ({ ...headerProps }: HeaderProps): JSX.Element => {
92
93
  <Center>
93
94
  {/* Center navigation */}
94
95
  {isIn.isCenterNavigationIn && (
95
- <DXNavigation {...navigationProps} links={navItemsC} />
96
+ <DXNavigation
97
+ {...navigationProps}
98
+ testId={NAVIGATION_TEST_ID}
99
+ links={navItemsC}
100
+ />
96
101
  )}
97
102
  </Center>
98
103
  </Fade>
@@ -1,4 +1,4 @@
1
- import { ButtonProps } from "@mui/material/Button/Button";
1
+ import { ButtonProps } from "@mui/material";
2
2
  import { BaseComponentProps } from "../../../../theme/common/entities";
3
3
 
4
4
  export type Props = BaseComponentProps & ButtonProps;
@@ -0,0 +1,22 @@
1
+ import React from "react";
2
+ import { Button } from "../Button/button";
3
+ import { Props } from "./types";
4
+
5
+ export const Buttons = <P,>({
6
+ className,
7
+ handleLogin,
8
+ providers = [],
9
+ ...props /* Mui ButtonProps */
10
+ }: Props<P>): JSX.Element[] => {
11
+ return providers?.map((provider) => (
12
+ <Button
13
+ key={provider.id}
14
+ className={className}
15
+ endIcon={"icon" in provider && provider.icon}
16
+ onClick={() => handleLogin(provider.id)}
17
+ {...props}
18
+ >
19
+ {provider.name}
20
+ </Button>
21
+ ));
22
+ };
@@ -0,0 +1,9 @@
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
+
6
+ export interface Props<P> extends BaseComponentProps, ButtonProps {
7
+ handleLogin: (providerId: string) => void;
8
+ providers?: ClientSafeProvider[] | OAuthProvider<P>[];
9
+ }
@@ -0,0 +1,15 @@
1
+ import styled from "@emotion/styled";
2
+ import { Grid2 } from "@mui/material";
3
+
4
+ export const StyledGrid2 = styled(Grid2)`
5
+ align-items: center;
6
+ align-self: flex-start;
7
+ display: flex;
8
+ gap: 12px;
9
+
10
+ .MuiTypography-text-body-400 {
11
+ .MuiLink-root {
12
+ color: inherit;
13
+ }
14
+ }
15
+ `;
@@ -0,0 +1,30 @@
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 { BaseComponentProps } from "../../../../../types";
8
+ import { StyledGrid2 } from "./consent.styles";
9
+ import { ConsentProps } from "./types";
10
+
11
+ export const Consent = ({
12
+ children,
13
+ className,
14
+ handleConsent,
15
+ isDisabled,
16
+ isError,
17
+ ...props /* Mui Grid2Props */
18
+ }: BaseComponentProps & ConsentProps): JSX.Element | null => {
19
+ if (isDisabled) return null;
20
+ return (
21
+ <StyledGrid2 className={className} {...props}>
22
+ <Checkbox
23
+ checkedIcon={<CheckedIcon />}
24
+ icon={isError ? <UncheckedErrorIcon /> : <UncheckedIcon />}
25
+ onChange={handleConsent}
26
+ />
27
+ <Typography variant={TEXT_BODY_400}>{children}</Typography>
28
+ </StyledGrid2>
29
+ );
30
+ };
@@ -0,0 +1,10 @@
1
+ import { Grid2Props } from "@mui/material";
2
+ import { ReactNode } from "react";
3
+ import { UseUserConsent } from "../../../../hooks/useUserConsent/types";
4
+
5
+ export interface ConsentProps
6
+ extends Grid2Props,
7
+ Pick<UseUserConsent, "handleConsent">,
8
+ Pick<UseUserConsent["state"], "isDisabled" | "isError"> {
9
+ children: ReactNode;
10
+ }