Skip to content

Commit 4955a4f

Browse files
frano-mFran McDade
andauthored
feat: configure "Datasets" Header (#3627) (#3780)
* feat: configure "Datasets" Header (#3627) * feat: udpated feature flag functionality (#3627) --------- Co-authored-by: Fran McDade <[email protected]>
1 parent e6f134b commit 4955a4f

File tree

9 files changed

+227
-31
lines changed

9 files changed

+227
-31
lines changed

explorer/app/components/Layout/components/Header/components/Content/components/Navigation/components/NavigationMenuItems/components/LabelIconMenuItem/labelIconMenuItem.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,19 @@ import { Label } from "./labelIconMenuItem.styles";
44

55
export interface LabelIconMenuItemProps {
66
Icon?: ElementType;
7+
iconFontSize?: string;
78
label: string;
89
}
910

1011
export const LabelIconMenuItem = ({
1112
Icon = OpenInNewIcon,
13+
iconFontSize = "xsmall",
1214
label,
1315
}: LabelIconMenuItemProps): JSX.Element => {
1416
return (
1517
<Label>
1618
<div>{label}</div>
17-
<Icon color="inkLight" fontSize="xsmall" />
19+
<Icon color="inkLight" fontSize={iconFontSize} />
1820
</Label>
1921
);
2022
};
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
export enum FEATURES {
2+
HEADER = "header",
3+
}
4+
5+
export enum FLAG {
6+
FALSE = "false",
7+
TRUE = "true",
8+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { FEATURES } from "./entities";
2+
3+
const setOfFeatureFlags = new Set(Object.values(FEATURES) as string[]);
4+
5+
/**
6+
* Set feature flags from URL.
7+
*/
8+
export function setFeatureFlags(): void {
9+
if (typeof window === "undefined") return;
10+
// Grab the search params from the URL.
11+
const params = new URLSearchParams(window.location.search);
12+
for (const [key, value] of params) {
13+
if (setOfFeatureFlags.has(key)) {
14+
setLocalStorage(key, value);
15+
}
16+
}
17+
}
18+
19+
/**
20+
* Return the value for the specified key.
21+
* @param key - Key.
22+
* @returns value.
23+
*/
24+
export function getLocalStorage(key: string): string | null {
25+
if (typeof window === "undefined") return null;
26+
return window?.localStorage?.getItem(key) ?? null;
27+
}
28+
29+
/**
30+
* Set the value for the specified key.
31+
* @param key - Key.
32+
* @param value - Value.
33+
*/
34+
export function setLocalStorage(key: string, value: string): void {
35+
if (typeof window === "undefined") return;
36+
window?.localStorage?.setItem(key, value);
37+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { useEffect, useState } from "react";
2+
import { FEATURES, FLAG } from "./common/entities";
3+
import { getLocalStorage } from "./common/utils";
4+
5+
/**
6+
* Determine if feature is available to user.
7+
* @param featureFlag - Name of feature.
8+
* @returns True if feature is available to user.
9+
*/
10+
export function useFeatureFlag(featureFlag: FEATURES): boolean {
11+
/* Flag indicating if feature is available to user. */
12+
const [isEnabled, setIsEnabled] = useState<boolean>(false);
13+
14+
/* Update state of enabled flag and redirect user if feature is not available to them. */
15+
useEffect(() => {
16+
const enabled = getLocalStorage(featureFlag) === FLAG.TRUE;
17+
setIsEnabled(enabled);
18+
}, [featureFlag]);
19+
20+
return isEnabled;
21+
}

explorer/app/shared/utils.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { NavLinkItem as DXNavLinkItem } from "@clevercanary/data-explorer-ui/lib/components/Layout/components/Header/components/Content/components/Navigation/navigation";
2+
import { HeaderProps as DXHeader } from "@clevercanary/data-explorer-ui/lib/components/Layout/components/Header/header";
3+
import { NavLinkItem, SiteConfig } from "../../site-config/common/entities";
4+
5+
/**
6+
* Returns the header properties for the site config and feature flag.
7+
* @param siteConfig - Site config.
8+
* @param isFeatureFlag - Flag indicating if feature is available to user.
9+
* @returns header properties.
10+
*/
11+
export function configureHeader(
12+
siteConfig: SiteConfig,
13+
isFeatureFlag: boolean
14+
): DXHeader {
15+
const header = siteConfig.layout.header;
16+
const navLinks = filterFeatureFlagNavigation(header.navLinks, isFeatureFlag);
17+
return {
18+
...header,
19+
navLinks,
20+
};
21+
}
22+
23+
/**
24+
* Returns the header navigation links for the site config and feature flag.
25+
* @param navLinks - Nav links.
26+
* @param isFeatureFlag - Flag indicating if feature is available to user.
27+
* @returns navigation links.
28+
*/
29+
function filterFeatureFlagNavigation(
30+
navLinks: NavLinkItem[],
31+
isFeatureFlag: boolean
32+
): DXNavLinkItem[] {
33+
return navLinks.filter(
34+
({ featureFlag }) =>
35+
featureFlag === undefined || featureFlag === isFeatureFlag
36+
);
37+
}

explorer/pages/_app.tsx

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,18 +19,29 @@ import { CssBaseline } from "@mui/material";
1919
import { ThemeProvider } from "@mui/material/styles";
2020
import { config } from "app/config/config";
2121
import type { AppProps } from "next/app";
22-
import { useEffect } from "react";
22+
import { useEffect, useMemo } from "react";
2323
import TagManager from "react-gtm-module";
24+
import { FEATURES } from "../app/hooks/useFeatureFlag/common/entities";
25+
import { setFeatureFlags } from "../app/hooks/useFeatureFlag/common/utils";
26+
import { useFeatureFlag } from "../app/hooks/useFeatureFlag/useFeatureFlag";
27+
import { configureHeader } from "../app/shared/utils";
2428

2529
const SESSION_TIMEOUT = 15 * 60 * 1000; // 15 minutes
2630

31+
setFeatureFlags();
32+
2733
function MyApp({ Component, pageProps }: AppProps): JSX.Element {
2834
// Set up the site configuration, layout and theme.
2935
const appConfig = config();
3036
const { analytics, layout, redirectRootToPath, themeOptions } = appConfig;
3137
const { gtmAuth, gtmId, gtmPreview } = analytics || {};
3238
const theme = createAppTheme(themeOptions);
3339
const { entityListType } = pageProps as AzulEntitiesStaticResponse;
40+
const isFeatureFlag = useFeatureFlag(FEATURES.HEADER);
41+
const configuredHeaderProps = useMemo(
42+
() => configureHeader(appConfig, isFeatureFlag),
43+
[appConfig, isFeatureFlag]
44+
); // Configure header.
3445

3546
// Initialize Google Tag Manager.
3647
useEffect(() => {
@@ -47,7 +58,7 @@ function MyApp({ Component, pageProps }: AppProps): JSX.Element {
4758
<CssBaseline />
4859
<AuthProvider sessionTimeout={SESSION_TIMEOUT}>
4960
<AppLayout>
50-
<Header {...layout.header} />
61+
<Header {...configuredHeaderProps} />
5162
<ExploreStateProvider entityListType={entityListType}>
5263
<FileManifestStateProvider>
5364
<Main>

explorer/site-config/anvil-catalog/dev/config.ts

Lines changed: 43 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import { ELEMENT_ALIGNMENT } from "@clevercanary/data-explorer-ui/lib/common/entities";
22
import { HEADER_NAVIGATION_LABEL } from "@clevercanary/data-explorer-ui/lib/components/Layout/components/Header/common/constants";
3-
import { SiteConfig } from "@clevercanary/data-explorer-ui/lib/config/entities";
3+
import { ANCHOR_TARGET } from "@clevercanary/data-explorer-ui/lib/components/Links/common/entities";
44
import * as C from "../../../app/components/index";
55
import anvilDevConfig from "../../anvil/dev/config";
66
import { socials } from "../../anvil/dev/constants";
7+
import { SiteConfig } from "../../common/entities";
78
import {
89
ANVIL_CATALOG_CATEGORY_KEY,
910
ANVIL_CATALOG_CATEGORY_LABEL,
@@ -15,6 +16,9 @@ import { workspaceEntityConfig } from "./index/workspaceEntityConfig";
1516
// Template constants
1617
const APP_TITLE = "AnVIL Dataset Catalog";
1718
const BROWSER_URL = process.env.NEXT_PUBLIC_SITEMAP_DOMAIN || "";
19+
const EXPLORER_URL = "https://explore.anvilproject.dev.clevercanary.com";
20+
const PORTAL_URL = process.env.NEXT_PUBLIC_SITEMAP_DOMAIN || "";
21+
const ROOT_PATH = "/consortia";
1822
const SLOGAN = "NHGRI Analysis Visualization and Informatics Lab-space";
1923

2024
const config: SiteConfig = {
@@ -76,11 +80,11 @@ const config: SiteConfig = {
7680
navLinks: [
7781
{
7882
label: "Help",
79-
url: `${BROWSER_URL}help`,
83+
url: `${PORTAL_URL}help`,
8084
},
8185
{
8286
label: "Privacy",
83-
url: `${BROWSER_URL}privacy`,
87+
url: `${PORTAL_URL}privacy`,
8488
},
8589
],
8690
socials,
@@ -89,62 +93,86 @@ const config: SiteConfig = {
8993
Logo: C.Logo({
9094
alt: SLOGAN,
9195
height: 40,
92-
link: BROWSER_URL,
96+
link: PORTAL_URL,
9397
src: "/images/logoAnvil.png",
9498
}),
9599
authenticationEnabled: false,
96100
navAlignment: ELEMENT_ALIGNMENT.CENTER,
97101
navLinks: [
98102
{
99103
label: "Overview",
100-
url: `${BROWSER_URL}overview`,
104+
url: `${PORTAL_URL}overview`,
101105
},
102106
{
103107
label: "Learn",
104-
url: `${BROWSER_URL}learn`,
108+
url: `${PORTAL_URL}learn`,
105109
},
106110
{
111+
featureFlag: false,
107112
label: "Datasets",
108-
url: `/`,
113+
url: ROOT_PATH,
114+
},
115+
{
116+
featureFlag: true,
117+
label: "Datasets",
118+
menuItems: [
119+
{
120+
description:
121+
"An open-access view of studies, workspaces, and consortia.",
122+
label: "Catalog",
123+
url: ROOT_PATH,
124+
},
125+
{
126+
description:
127+
"Build, download, and export cross-study cohorts of open and managed access data.",
128+
label: C.LabelIconMenuItem({
129+
iconFontSize: "small",
130+
label: "Explorer",
131+
}),
132+
target: ANCHOR_TARGET.BLANK,
133+
url: `${EXPLORER_URL}/datasets`,
134+
},
135+
],
136+
url: "",
109137
},
110138
{
111139
label: "Consortia",
112-
url: `${BROWSER_URL}consortia`,
140+
url: `${PORTAL_URL}consortia`,
113141
},
114142
{
115143
label: "News",
116-
url: `${BROWSER_URL}news`,
144+
url: `${PORTAL_URL}news`,
117145
},
118146
{
119147
label: "Events",
120-
url: `${BROWSER_URL}events`,
148+
url: `${PORTAL_URL}events`,
121149
},
122150
{
123151
label: HEADER_NAVIGATION_LABEL.MORE,
124152
menuItems: [
125153
{
126154
label: "Team",
127-
url: `${BROWSER_URL}team`,
155+
url: `${PORTAL_URL}team`,
128156
},
129157
{
130158
label: "FAQ",
131-
url: `${BROWSER_URL}faq`,
159+
url: `${PORTAL_URL}faq`,
132160
},
133161
{
134162
label: "Help",
135-
url: `${BROWSER_URL}help`,
163+
url: `${PORTAL_URL}help`,
136164
},
137165
],
138166
url: "",
139167
},
140168
],
141169
searchEnabled: true,
142-
searchURL: `${BROWSER_URL}search`,
170+
searchURL: `${PORTAL_URL}search`,
143171
slogan: SLOGAN,
144172
socials,
145173
},
146174
},
147-
redirectRootToPath: "/consortia",
175+
redirectRootToPath: ROOT_PATH,
148176
summaryConfig: undefined,
149177
};
150178

0 commit comments

Comments
 (0)