Skip to content
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
5 changes: 3 additions & 2 deletions explorer/app/apis/azul/anvil-cmg/common/responses.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { AzulHit } from "../../common/entities";
import { AzulHit, ResponseSource } from "../../common/entities";
import {
AggregatedActivityResponse,
AggregatedBioSampleResponse,
Expand Down Expand Up @@ -52,7 +52,8 @@ export type DatasetsResponse = AzulHit &
AggregatedDonorResponse &
AggregatedFileResponse &
AggregatedLibraryResponse &
AggregatedDiagnosisResponse;
AggregatedDiagnosisResponse &
ResponseSource;

/**
* Model of response returned from the /index/donors API endpoint.
Expand Down
8 changes: 8 additions & 0 deletions explorer/app/apis/azul/common/entities.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { RESPONSE_SOURCE } from "@clevercanary/data-explorer-ui/lib/apis/azul/common/entities";
import { FileEntityResponse } from "../anvil/common/entities";
import {
ActivitiesResponse,
Expand All @@ -24,3 +25,10 @@ export type AzulEntitiesResponses =
| DonorsResponse
| FileEntityResponse
| LibrariesResponse;

/**
* Response source.
*/
export interface ResponseSource {
responseSource?: RESPONSE_SOURCE;
}
24 changes: 24 additions & 0 deletions explorer/app/components/common/Fade/fade.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { Fade as MFade, FadeProps as MFadeProps } from "@mui/material";
import React, { ReactNode } from "react";

/**
* Basic Fade component for rendering with component configuration.
* Transition child requirements: see https://mui.com/material-ui/transitions/#child-requirement.
* A wrapper element around component configuration "children" is required as the "children" do not meet the child
* requirements for a transition component i.e. they are not defined as a single element.
*/

export interface FadeProps extends Omit<MFadeProps, "children"> {
children: ReactNode[]; // component configuration "children" are not defined as a single element.
}

export const Fade = ({
children,
...props /* Spread props to allow for Mui Fade specific prop overrides e.g. "appear" or "style". */
}: FadeProps): JSX.Element => {
return (
<MFade {...props}>
<div>{children}</div>
</MFade>
);
};
1 change: 1 addition & 0 deletions explorer/app/components/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ export { Publications } from "@clevercanary/data-explorer-ui/lib/components/Proj
export { SupplementaryLinks } from "@clevercanary/data-explorer-ui/lib/components/Project/components/SupplementaryLinks/supplementaryLinks";
export { TitledText } from "@clevercanary/data-explorer-ui/lib/components/Project/components/TitledText/titledText";
export { ExportMethodView } from "@clevercanary/data-explorer-ui/lib/views/ExportMethodView/exportMethodView";
export { Fade } from "./common/Fade/fade";
export { MdxMarkdown } from "./common/MDXMarkdown/mdxMarkdown";
export { ConsentCodeList } from "./Detail/components/ConsentCodeList/consentCodeList";
export { ConsentTooltip } from "./Detail/components/ConsentTooltip/consentTooltip";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import { LABEL } from "@clevercanary/data-explorer-ui/lib/apis/azul/common/entities";
import {
LABEL,
RESPONSE_SOURCE,
} from "@clevercanary/data-explorer-ui/lib/apis/azul/common/entities";
import {
Filters,
SelectedFilter,
Expand Down Expand Up @@ -230,14 +233,16 @@ export const buildDatasetDetails = (
/**
* Build props for BackPageHero component from the given datasets response.
* @param datasetsResponse - Response model return from datasets API.
* @param viewContext - View context.
* @returns model to be used as props for the BackPageHero component.
*/
export const buildDatasetHero = (
datasetsResponse: DatasetsResponse
datasetsResponse: DatasetsResponse,
viewContext: ViewContext
): React.ComponentProps<typeof C.BackPageHero> => {
return {
breadcrumbs: getDatasetBreadcrumbs(datasetsResponse),
callToAction: getDatasetCallToAction(datasetsResponse),
callToAction: getDatasetCallToAction(datasetsResponse, viewContext),
title: getDatasetTitle(datasetsResponse),
};
};
Expand Down Expand Up @@ -709,6 +714,23 @@ export const buildReportedEthnicities = (
};
};

/**
* Returns transition relating to accessibility from the given datasets response and authorization state.
* @param datasetsResponse - Response model return from datasets API.
* @param viewContext - View context.
* @returns model to be used as props for the Fade component.
*/
export function getAccessibleTransition(
datasetsResponse: DatasetsResponse,
viewContext: ViewContext
): Partial<React.ComponentProps<typeof C.Fade>> {
const isIn = isAccessibleTransitionIn(datasetsResponse, viewContext);
return {
in: isIn,
timeout: isIn ? 0 : 300,
};
}

/**
* Returns dataset related breadcrumbs.
* @param datasetsResponse - Response model return from datasets API.
Expand All @@ -726,14 +748,21 @@ export function getDatasetBreadcrumbs(
/**
* Returns the callToAction prop for the Hero component from the given datasets response.
* @param datasetsResponse - Response model return from datasets API.
* @param viewContext - View context.
* @returns model to be used as props for the CallToActionButton component.
*/
function getDatasetCallToAction(
datasetsResponse: DatasetsResponse
datasetsResponse: DatasetsResponse,
viewContext: ViewContext
): CallToAction | undefined {
const isReady = isResponseReady(datasetsResponse, viewContext);
const isAccessGranted = isDatasetAccessible(datasetsResponse);
const registeredIdentifier = getDatasetRegisteredIdentifier(datasetsResponse);
if (isAccessGranted || registeredIdentifier === LABEL.UNSPECIFIED) {
if (
!isReady ||
isAccessGranted ||
registeredIdentifier === LABEL.UNSPECIFIED
) {
return;
}
return {
Expand Down Expand Up @@ -816,6 +845,22 @@ export function getExportSelectedDataSummary(
]);
}

/**
* Returns true if the response is accessible, or the response source is appropriate for the authorization state.
* When a user is logged in, the component should only transition in when the client side request is available;
* a static request will not accurately reflect the accessibility of the given response.
* @param datasetsResponse - Response model return from datasets API.
* @param viewContext - View context.
* @returns true if the response is accessible, or the response source is appropriate for the authorization state.
*/
function isAccessibleTransitionIn(
datasetsResponse: DatasetsResponse,
viewContext: ViewContext
): boolean {
const isAccessible = isDatasetAccessible(datasetsResponse);
return isAccessible || isResponseReady(datasetsResponse, viewContext);
}

/**
* Returns true if dataset is accessible.
* @param datasetsResponse - Response model return from datasets API.
Expand All @@ -825,6 +870,26 @@ function isDatasetAccessible(datasetsResponse: DatasetsResponse): boolean {
return datasetsResponse.datasets[0].accessible;
}

/**
* Returns true if the response is ready (for use) for the given authorization state.
* When a user is logged in, the component should only transition in when the client side request is available;
* a static request will not accurately reflect properties like "accessibility" of the given response.
* @param datasetsResponse - Response model return from datasets API.
* @param viewContext - View context.
* @returns true if the response is ready.
*/
function isResponseReady(
datasetsResponse: DatasetsResponse,
viewContext: ViewContext
): boolean {
const {
authState: { isAuthorized },
} = viewContext;
const { responseSource } = datasetsResponse;
const isStatic = responseSource === RESPONSE_SOURCE.STATIC_GENERATION;
return !(isAuthorized && isStatic);
}

/**
* Returns current query for the given facet.
* @param filter - Selected filter.
Expand All @@ -842,6 +907,21 @@ function mapCurrentQuery(
];
}

/**
* Renders entity related status badge (with transition).
* @param datasetsResponse - Unused.
* @param viewContext - View context.
* @returns model to be used as props for the Fade component.
*/
export const renderDatasetStatusBadge = (
datasetsResponse: DatasetsResponse,
viewContext: ViewContext
): Partial<React.ComponentProps<typeof C.Fade>> => {
return {
...getAccessibleTransition(datasetsResponse, viewContext),
};
};

/**
* Renders configuration component children when the given authentication state is not authorized.
* @param _ - Unused.
Expand Down
12 changes: 9 additions & 3 deletions explorer/site-config/anvil-cmg/dev/detail/dataset/top.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,15 @@ export const top: ComponentsConfig = [
{
children: [
{
component: C.StatusBadge,
viewBuilder: V.buildDatasetStatus,
} as ComponentConfig<typeof C.StatusBadge, DatasetsResponse>,
children: [
{
component: C.StatusBadge,
viewBuilder: V.buildDatasetStatus,
} as ComponentConfig<typeof C.StatusBadge, DatasetsResponse>,
],
component: C.Fade,
viewBuilder: V.renderDatasetStatusBadge,
} as ComponentConfig<typeof C.Fade>,
],
component: C.BackPageHero,
viewBuilder: V.buildDatasetHero,
Expand Down