Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: configure "Datasets" Header (#3627) #3780

Merged
merged 2 commits into from
Nov 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,19 @@ import { Label } from "./labelIconMenuItem.styles";

export interface LabelIconMenuItemProps {
Icon?: ElementType;
iconFontSize?: string;
label: string;
}

export const LabelIconMenuItem = ({
Icon = OpenInNewIcon,
iconFontSize = "xsmall",
label,
}: LabelIconMenuItemProps): JSX.Element => {
return (
<Label>
<div>{label}</div>
<Icon color="inkLight" fontSize="xsmall" />
<Icon color="inkLight" fontSize={iconFontSize} />
</Label>
);
};
8 changes: 8 additions & 0 deletions explorer/app/hooks/useFeatureFlag/common/entities.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export enum FEATURES {
HEADER = "header",
}

export enum FLAG {
FALSE = "false",
TRUE = "true",
}
37 changes: 37 additions & 0 deletions explorer/app/hooks/useFeatureFlag/common/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { FEATURES } from "./entities";

const setOfFeatureFlags = new Set(Object.values(FEATURES) as string[]);

/**
* Set feature flags from URL.
*/
export function setFeatureFlags(): void {
if (typeof window === "undefined") return;
// Grab the search params from the URL.
const params = new URLSearchParams(window.location.search);
for (const [key, value] of params) {
if (setOfFeatureFlags.has(key)) {
setLocalStorage(key, value);
}
}
}

/**
* Return the value for the specified key.
* @param key - Key.
* @returns value.
*/
export function getLocalStorage(key: string): string | null {
if (typeof window === "undefined") return null;
return window?.localStorage?.getItem(key) ?? null;
}

/**
* Set the value for the specified key.
* @param key - Key.
* @param value - Value.
*/
export function setLocalStorage(key: string, value: string): void {
if (typeof window === "undefined") return;
window?.localStorage?.setItem(key, value);
}
21 changes: 21 additions & 0 deletions explorer/app/hooks/useFeatureFlag/useFeatureFlag.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { useEffect, useState } from "react";
import { FEATURES, FLAG } from "./common/entities";
import { getLocalStorage } from "./common/utils";

/**
* Determine if feature is available to user.
* @param featureFlag - Name of feature.
* @returns True if feature is available to user.
*/
export function useFeatureFlag(featureFlag: FEATURES): boolean {
/* Flag indicating if feature is available to user. */
const [isEnabled, setIsEnabled] = useState<boolean>(false);

/* Update state of enabled flag and redirect user if feature is not available to them. */
useEffect(() => {
const enabled = getLocalStorage(featureFlag) === FLAG.TRUE;
setIsEnabled(enabled);
}, [featureFlag]);

return isEnabled;
}
37 changes: 37 additions & 0 deletions explorer/app/shared/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { NavLinkItem as DXNavLinkItem } from "@clevercanary/data-explorer-ui/lib/components/Layout/components/Header/components/Content/components/Navigation/navigation";
import { HeaderProps as DXHeader } from "@clevercanary/data-explorer-ui/lib/components/Layout/components/Header/header";
import { NavLinkItem, SiteConfig } from "../../site-config/common/entities";

/**
* Returns the header properties for the site config and feature flag.
* @param siteConfig - Site config.
* @param isFeatureFlag - Flag indicating if feature is available to user.
* @returns header properties.
*/
export function configureHeader(
siteConfig: SiteConfig,
isFeatureFlag: boolean
): DXHeader {
const header = siteConfig.layout.header;
const navLinks = filterFeatureFlagNavigation(header.navLinks, isFeatureFlag);
return {
...header,
navLinks,
};
}

/**
* Returns the header navigation links for the site config and feature flag.
* @param navLinks - Nav links.
* @param isFeatureFlag - Flag indicating if feature is available to user.
* @returns navigation links.
*/
function filterFeatureFlagNavigation(
navLinks: NavLinkItem[],
isFeatureFlag: boolean
): DXNavLinkItem[] {
return navLinks.filter(
({ featureFlag }) =>
featureFlag === undefined || featureFlag === isFeatureFlag
);
}
15 changes: 13 additions & 2 deletions explorer/pages/_app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,29 @@ import { CssBaseline } from "@mui/material";
import { ThemeProvider } from "@mui/material/styles";
import { config } from "app/config/config";
import type { AppProps } from "next/app";
import { useEffect } from "react";
import { useEffect, useMemo } from "react";
import TagManager from "react-gtm-module";
import { FEATURES } from "../app/hooks/useFeatureFlag/common/entities";
import { setFeatureFlags } from "../app/hooks/useFeatureFlag/common/utils";
import { useFeatureFlag } from "../app/hooks/useFeatureFlag/useFeatureFlag";
import { configureHeader } from "../app/shared/utils";

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

setFeatureFlags();

function MyApp({ Component, pageProps }: AppProps): JSX.Element {
// Set up the site configuration, layout and theme.
const appConfig = config();
const { analytics, layout, redirectRootToPath, themeOptions } = appConfig;
const { gtmAuth, gtmId, gtmPreview } = analytics || {};
const theme = createAppTheme(themeOptions);
const { entityListType } = pageProps as AzulEntitiesStaticResponse;
const isFeatureFlag = useFeatureFlag(FEATURES.HEADER);
const configuredHeaderProps = useMemo(
() => configureHeader(appConfig, isFeatureFlag),
[appConfig, isFeatureFlag]
); // Configure header.

// Initialize Google Tag Manager.
useEffect(() => {
Expand All @@ -47,7 +58,7 @@ function MyApp({ Component, pageProps }: AppProps): JSX.Element {
<CssBaseline />
<AuthProvider sessionTimeout={SESSION_TIMEOUT}>
<AppLayout>
<Header {...layout.header} />
<Header {...configuredHeaderProps} />
<ExploreStateProvider entityListType={entityListType}>
<FileManifestStateProvider>
<Main>
Expand Down
58 changes: 43 additions & 15 deletions explorer/site-config/anvil-catalog/dev/config.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { ELEMENT_ALIGNMENT } from "@clevercanary/data-explorer-ui/lib/common/entities";
import { HEADER_NAVIGATION_LABEL } from "@clevercanary/data-explorer-ui/lib/components/Layout/components/Header/common/constants";
import { SiteConfig } from "@clevercanary/data-explorer-ui/lib/config/entities";
import { ANCHOR_TARGET } from "@clevercanary/data-explorer-ui/lib/components/Links/common/entities";
import * as C from "../../../app/components/index";
import anvilDevConfig from "../../anvil/dev/config";
import { socials } from "../../anvil/dev/constants";
import { SiteConfig } from "../../common/entities";
import {
ANVIL_CATALOG_CATEGORY_KEY,
ANVIL_CATALOG_CATEGORY_LABEL,
Expand All @@ -15,6 +16,9 @@ import { workspaceEntityConfig } from "./index/workspaceEntityConfig";
// Template constants
const APP_TITLE = "AnVIL Dataset Catalog";
const BROWSER_URL = process.env.NEXT_PUBLIC_SITEMAP_DOMAIN || "";
const EXPLORER_URL = "https://explore.anvilproject.dev.clevercanary.com";
const PORTAL_URL = process.env.NEXT_PUBLIC_SITEMAP_DOMAIN || "";
const ROOT_PATH = "/consortia";
const SLOGAN = "NHGRI Analysis Visualization and Informatics Lab-space";

const config: SiteConfig = {
Expand Down Expand Up @@ -76,11 +80,11 @@ const config: SiteConfig = {
navLinks: [
{
label: "Help",
url: `${BROWSER_URL}help`,
url: `${PORTAL_URL}help`,
},
{
label: "Privacy",
url: `${BROWSER_URL}privacy`,
url: `${PORTAL_URL}privacy`,
},
],
socials,
Expand All @@ -89,62 +93,86 @@ const config: SiteConfig = {
Logo: C.Logo({
alt: SLOGAN,
height: 40,
link: BROWSER_URL,
link: PORTAL_URL,
src: "/images/logoAnvil.png",
}),
authenticationEnabled: false,
navAlignment: ELEMENT_ALIGNMENT.CENTER,
navLinks: [
{
label: "Overview",
url: `${BROWSER_URL}overview`,
url: `${PORTAL_URL}overview`,
},
{
label: "Learn",
url: `${BROWSER_URL}learn`,
url: `${PORTAL_URL}learn`,
},
{
featureFlag: false,
label: "Datasets",
url: `/`,
url: ROOT_PATH,
},
{
featureFlag: true,
label: "Datasets",
menuItems: [
{
description:
"An open-access view of studies, workspaces, and consortia.",
label: "Catalog",
url: ROOT_PATH,
},
{
description:
"Build, download, and export cross-study cohorts of open and managed access data.",
label: C.LabelIconMenuItem({
iconFontSize: "small",
label: "Explorer",
}),
target: ANCHOR_TARGET.BLANK,
url: `${EXPLORER_URL}/datasets`,
},
],
url: "",
},
{
label: "Consortia",
url: `${BROWSER_URL}consortia`,
url: `${PORTAL_URL}consortia`,
},
{
label: "News",
url: `${BROWSER_URL}news`,
url: `${PORTAL_URL}news`,
},
{
label: "Events",
url: `${BROWSER_URL}events`,
url: `${PORTAL_URL}events`,
},
{
label: HEADER_NAVIGATION_LABEL.MORE,
menuItems: [
{
label: "Team",
url: `${BROWSER_URL}team`,
url: `${PORTAL_URL}team`,
},
{
label: "FAQ",
url: `${BROWSER_URL}faq`,
url: `${PORTAL_URL}faq`,
},
{
label: "Help",
url: `${BROWSER_URL}help`,
url: `${PORTAL_URL}help`,
},
],
url: "",
},
],
searchEnabled: true,
searchURL: `${BROWSER_URL}search`,
searchURL: `${PORTAL_URL}search`,
slogan: SLOGAN,
socials,
},
},
redirectRootToPath: "/consortia",
redirectRootToPath: ROOT_PATH,
summaryConfig: undefined,
};

Expand Down
Loading