diff --git a/apps/genome-page/components/Loader.tsx b/apps/genome-page/components/Loader.tsx index eab6bade58..6ab83c282a 100644 --- a/apps/genome-page/components/Loader.tsx +++ b/apps/genome-page/components/Loader.tsx @@ -1,16 +1,31 @@ import { makeBy as AmakeBy } from "fp-ts/Array" import { Skeleton } from "@material-ui/lab" import Box from "@material-ui/core/Box" +import Paper from "@material-ui/core/Paper" +import { makeStyles } from "@material-ui/core/styles" +import { blue } from "@material-ui/core/colors" + +const useStyles = makeStyles({ + container: { + backgroundColor: blue[50], + padding: "1rem", + }, +}) /** * Loader is the default loading skeleton component. */ -const Loader = () => ( - - {AmakeBy(12, (key) => ( - - ))} - -) +const Loader = () => { + const classes = useStyles() + return ( + + + {AmakeBy(12, (key) => ( + + ))} + + + ) +} export { Loader } diff --git a/apps/genome-page/components/NoDataDisplay.tsx b/apps/genome-page/components/NoDataDisplay.tsx new file mode 100644 index 0000000000..6132e89038 --- /dev/null +++ b/apps/genome-page/components/NoDataDisplay.tsx @@ -0,0 +1,47 @@ +import Paper from "@material-ui/core/Paper" +import Typography from "@material-ui/core/Typography" +import Grid from "@material-ui/core/Grid" +import RemoveCircleOutlineIcon from "@material-ui/icons/RemoveCircleOutline" +import { makeStyles } from "@material-ui/core/styles" +import { blue, grey } from "@material-ui/core/colors" + +const useStyles = makeStyles({ + container: { + backgroundColor: blue[50], + color: grey[600], + }, + grid: { + minHeight: "10rem", + }, + icon: { + fontSize: "4rem", + }, +}) + +type NoDataDisplayProperties = { + query: string + geneId: string +} + +const NoDataDisplay = ({ query, geneId }: NoDataDisplayProperties) => { + const classes = useStyles() + return ( + + + + + + + {`No ${query} for ${geneId}`} + + + + ) +} + +export { NoDataDisplay } diff --git a/apps/genome-page/components/errors/GraphQLErrorPage.tsx b/apps/genome-page/components/errors/GraphQLErrorPage.tsx new file mode 100644 index 0000000000..6f21fc0527 --- /dev/null +++ b/apps/genome-page/components/errors/GraphQLErrorPage.tsx @@ -0,0 +1,82 @@ +/* eslint-disable unicorn/filename-case */ +import { ApolloError } from "@apollo/client" +import { pipe } from "fp-ts/function" +import { head as RAhead } from "fp-ts/ReadonlyArray" +import { + map as Omap, + flatMap as OflatMap, + fromNullable as OfromNullable, + getOrElse as OgetOrElse, +} from "fp-ts/Option" +import { match, P } from "ts-pattern" +import Paper from "@material-ui/core/Paper" +import { makeStyles } from "@material-ui/core/styles" +import { blue, grey } from "@material-ui/core/colors" +import { ServerError } from "./ServerError" +import { NotFoundError } from "./NotFoundError" +import { OtherError } from "./OtherError" + +const useStyles = makeStyles({ + container: { + backgroundColor: blue[50], + color: grey[600], + }, + grid: { + minHeight: "10rem", + }, + icon: { + fontSize: "4rem", + }, +}) + +type GraphQlErrorPageProperties = { + /** GraphQL error object */ + error: ApolloError +} + +/** + * Displays any errors found when issuing a GraphQL query or mutation. + * Returns one of the other error components based on the error code. + */ + +const GraphQLErrorPage = ({ error }: GraphQlErrorPageProperties) => { + const classes = useStyles() + + return ( + + {match(error) + .with({ networkError: P.not(P.nullish) }, () => ) + .with({ graphQLErrors: P.select(P.not(P.nullish)) }, (errors) => { + const primaryError = pipe(errors, RAhead) + const primaryErrorCode = pipe( + primaryError, + OflatMap(({ extensions }) => + pipe( + extensions, + OfromNullable, + // eslint-disable-next-line dot-notation + Omap((extension) => extension["code"] as string), + ), + ), + OgetOrElse(() => ""), + ) + const primaryErrorMessage = pipe( + primaryError, + Omap(({ message }) => message), + OgetOrElse(() => ""), + ) + return match(primaryErrorCode) + .with("Unavailable", () => ) + .with("NotFound", () => ( + + )) + .otherwise(() => ) + }) + .otherwise(() => ( + + ))} + + ) +} + +export { GraphQLErrorPage } diff --git a/apps/genome-page/components/errors/NotFoundError.tsx b/apps/genome-page/components/errors/NotFoundError.tsx index e46f8eb7fc..866570c4d3 100644 --- a/apps/genome-page/components/errors/NotFoundError.tsx +++ b/apps/genome-page/components/errors/NotFoundError.tsx @@ -17,11 +17,11 @@ const NotFoundError = ({ error }: Properties) => { const classes = useStyles() return ( - +
Sad Dicty Logo { const classes = useStyles() return ( - +
Sad Dicty Logo -

- Error -

+ Sorry, something went wrong.
diff --git a/apps/genome-page/components/errors/ServerError.tsx b/apps/genome-page/components/errors/ServerError.tsx index 82fa361a97..04f6b75c0d 100644 --- a/apps/genome-page/components/errors/ServerError.tsx +++ b/apps/genome-page/components/errors/ServerError.tsx @@ -11,7 +11,7 @@ const ServerError = () => { const classes = useStyles() return ( - +

Sorry! There was a server error.

diff --git a/apps/genome-page/components/features/Ontology/OntologyContainer.tsx b/apps/genome-page/components/features/Ontology/OntologyContainer.tsx index 263db1fb9a..eeec439d58 100644 --- a/apps/genome-page/components/features/Ontology/OntologyContainer.tsx +++ b/apps/genome-page/components/features/Ontology/OntologyContainer.tsx @@ -1,7 +1,5 @@ import Typography from "@material-ui/core/Typography" -import { Layout } from "components/layout/Layout" import { GeneOntologyAnnotationQuery } from "dicty-graphql-schema" -import { useRouter } from "next/router" import { OntologyTabLayout } from "./OntologyTabLayout" /** * Container component that issues a GraphQL query to get gene data for the @@ -11,20 +9,10 @@ import { OntologyTabLayout } from "./OntologyTabLayout" interface OntologyContainerProperties { goas: NonNullable } -const OntologyContainer = ({ goas }: OntologyContainerProperties) => { - const { query } = useRouter() - const geneId = query.id as string - - return ( - - - {" "} - - - ) -} +const OntologyContainer = ({ goas }: OntologyContainerProperties) => ( + + + +) export { OntologyContainer } diff --git a/apps/genome-page/components/features/Ontology/OntologyLoader.md b/apps/genome-page/components/features/Ontology/OntologyLoader.md deleted file mode 100644 index 741f723331..0000000000 --- a/apps/genome-page/components/features/Ontology/OntologyLoader.md +++ /dev/null @@ -1,3 +0,0 @@ -```jsx - -``` diff --git a/apps/genome-page/components/features/Ontology/OntologyLoader.test.tsx b/apps/genome-page/components/features/Ontology/OntologyLoader.test.tsx deleted file mode 100644 index 482ab1279a..0000000000 --- a/apps/genome-page/components/features/Ontology/OntologyLoader.test.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import React from "react" -import { render, screen } from "@testing-library/react" -import { OntologyLoader } from "./OntologyLoader" - -jest.mock("next/router", () => { - const useRouter = jest.fn(() => ({ - query: { id: "DDB_G123456" }, - pathname: "", - })) - return { useRouter } -}) - -describe("components/OntologyLoader", () => { - it("should render skeleton loader", () => { - render() - expect(screen.getByTestId("skeleton-loader")).toBeInTheDocument() - }) -}) diff --git a/apps/genome-page/components/features/Ontology/OntologyLoader.tsx b/apps/genome-page/components/features/Ontology/OntologyLoader.tsx deleted file mode 100644 index 7769e206aa..0000000000 --- a/apps/genome-page/components/features/Ontology/OntologyLoader.tsx +++ /dev/null @@ -1,60 +0,0 @@ -import AppBar from "@material-ui/core/AppBar" -import Tabs from "@material-ui/core/Tabs" -import Tab from "@material-ui/core/Tab" -import Box from "@material-ui/core/Box" -import { createTheme, MuiThemeProvider } from "@material-ui/core/styles" -import { useRouter } from "next/router" -import { Layout } from "components/layout/Layout" -import { Loader } from "components/Loader" - -const skeletonTheme = createTheme({ - overrides: { - MuiTab: { - root: { - textTransform: "none", - }, - }, - MuiTabs: { - root: { - backgroundColor: "#DFE8F6", - color: "#000", - }, - indicator: { - backgroundColor: "#858780", - height: "3px", - }, - }, - }, -}) - -/** - * Loading screen for GO page - */ -const OntologyLoader = () => { - const { query } = useRouter() - const geneId = query.id as string - return ( - - - - - - - - - - - - - - - - - - ) -} - -export { OntologyLoader } diff --git a/apps/genome-page/components/features/Phenotypes/PhenotypesContainer.tsx b/apps/genome-page/components/features/Phenotypes/PhenotypesContainer.tsx index a80558d34a..e31999ab3a 100644 --- a/apps/genome-page/components/features/Phenotypes/PhenotypesContainer.tsx +++ b/apps/genome-page/components/features/Phenotypes/PhenotypesContainer.tsx @@ -1,26 +1,14 @@ import Typography from "@material-ui/core/Typography" -import { Layout } from "components/layout/Layout" import { ListStrainsWithGeneQuery } from "dicty-graphql-schema" -import { useRouter } from "next/router" import { PhenotypesDataTable } from "./PhenotypesDataTable" interface PhenotypesContainerProperties { strains: NonNullable } -const PhenotypesContainer = ({ strains }: PhenotypesContainerProperties) => { - const { query } = useRouter() - const geneId = query.id as string - - return ( - - - - - - ) -} +const PhenotypesContainer = ({ strains }: PhenotypesContainerProperties) => ( + + + +) export { PhenotypesContainer } diff --git a/apps/genome-page/components/features/Phenotypes/PhenotypesLoader.test.tsx b/apps/genome-page/components/features/Phenotypes/PhenotypesLoader.test.tsx deleted file mode 100644 index 3fd3395b77..0000000000 --- a/apps/genome-page/components/features/Phenotypes/PhenotypesLoader.test.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import { render, screen } from "@testing-library/react" -import React from "react" -import { PhenotypesLoader } from "./PhenotypesLoader" - -jest.mock("next/router", () => { - const useRouter = jest.fn(() => ({ - query: { id: "DDB_G123456" }, - pathname: "", - })) - return { useRouter } -}) - -describe("features/Phenotypes/PhenotypesLoader", () => { - it("should render loader", () => { - render() - - expect(screen.getByTestId("skeleton-loader")).toBeInTheDocument() - }) -}) diff --git a/apps/genome-page/components/features/Phenotypes/PhenotypesLoader.tsx b/apps/genome-page/components/features/Phenotypes/PhenotypesLoader.tsx deleted file mode 100644 index a63aa03061..0000000000 --- a/apps/genome-page/components/features/Phenotypes/PhenotypesLoader.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import { useRouter } from "next/router" -import { Layout } from "components/layout/Layout" -import { Loader } from "components/Loader" - -/** - * Loading screen for Summary page - */ -const PhenotypesLoader = () => { - const { query } = useRouter() - const geneId = query.id as string - return ( - - - - ) -} - -export { PhenotypesLoader } diff --git a/apps/genome-page/components/features/References/ReferencesLoader.tsx b/apps/genome-page/components/features/References/ReferencesLoader.tsx deleted file mode 100644 index da3ddcf69f..0000000000 --- a/apps/genome-page/components/features/References/ReferencesLoader.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import { useRouter } from "next/router" -import { Layout } from "components/layout/Layout" -import { Loader } from "components/Loader" - -/** - * Loading screen for Summary page - */ -const ReferencesLoader = () => { - const { query } = useRouter() - const geneId = query.id as string - return ( - - - - ) -} - -export { ReferencesLoader } diff --git a/apps/genome-page/components/features/Summary/SummaryContainer.tsx b/apps/genome-page/components/features/Summary/SummaryContainer.tsx index 5abd8f14c4..d03f19835c 100644 --- a/apps/genome-page/components/features/Summary/SummaryContainer.tsx +++ b/apps/genome-page/components/features/Summary/SummaryContainer.tsx @@ -1,11 +1,12 @@ import Typography from "@material-ui/core/Typography" import { PanelWrapper } from "components/panels/PanelWrapper" -import { Layout } from "components/layout/Layout" import { GeneSummaryQuery } from "dicty-graphql-schema" import { useRouter } from "next/router" +import { match } from "ts-pattern" import { GeneralInfoPanel } from "components/features/Summary/Panels/GeneralInfoPanel" import { GoaPanel } from "components/features/Summary/Panels/GoaPanel" import { ReferencesPanel } from "components/features/Summary/Panels/ReferencesPanel" +import { NoDataDisplay } from "components/NoDataDisplay" interface SummaryContainerProperties { geneSummary: GeneSummaryQuery @@ -25,26 +26,27 @@ const SummaryContainer = ({ geneSummary }: SummaryContainerProperties) => { ) const geneId = query.id as string return ( - - - - - - - - - - - - - + + + + + + + + + {match(partialPublicationsList) + .with([], () => ( + + )) + .otherwise((publications) => ( + + ))} + + ) } diff --git a/apps/genome-page/components/features/Summary/SummaryLoader.md b/apps/genome-page/components/features/Summary/SummaryLoader.md deleted file mode 100644 index 33facbb08a..0000000000 --- a/apps/genome-page/components/features/Summary/SummaryLoader.md +++ /dev/null @@ -1,3 +0,0 @@ -```jsx - -``` diff --git a/apps/genome-page/components/features/Summary/SummaryLoader.test.tsx b/apps/genome-page/components/features/Summary/SummaryLoader.test.tsx deleted file mode 100644 index c4e6de9edf..0000000000 --- a/apps/genome-page/components/features/Summary/SummaryLoader.test.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import React from "react" -import { render, screen } from "@testing-library/react" -import { SummaryLoader } from "./SummaryLoader" - -jest.mock("next/router", () => { - const useRouter = jest.fn(() => ({ - query: { id: "DDB_G123456" }, - pathname: "", - })) - return { useRouter } -}) - -describe("components/SummaryLoader", () => { - it("should render skeleton loader", () => { - render() - expect(screen.getByTestId("skeleton-loader")).toBeInTheDocument() - }) -}) diff --git a/apps/genome-page/components/features/Summary/SummaryLoader.tsx b/apps/genome-page/components/features/Summary/SummaryLoader.tsx deleted file mode 100644 index cb02146290..0000000000 --- a/apps/genome-page/components/features/Summary/SummaryLoader.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import { useRouter } from "next/router" -import { Layout } from "components/layout/Layout" -import { Loader } from "components/Loader" - -/** - * Loading screen for Summary page - */ -const SummaryLoader = () => { - const { query } = useRouter() - const geneId = query.id as string - return ( - - - - ) -} - -export { SummaryLoader } diff --git a/apps/genome-page/pages/[id]/goannotations.tsx b/apps/genome-page/pages/[id]/goannotations.tsx index 3b18790950..3e6bd81e91 100644 --- a/apps/genome-page/pages/[id]/goannotations.tsx +++ b/apps/genome-page/pages/[id]/goannotations.tsx @@ -1,6 +1,8 @@ import { OntologyContainer } from "components/features/Ontology/OntologyContainer" -import { GraphQLErrorPage } from "@dictybase/ui-common" -import { OntologyLoader } from "components/features/Ontology/OntologyLoader" +import { GraphQLErrorPage } from "components/errors/GraphQLErrorPage" +import { Layout } from "components/layout/Layout" +import { NoDataDisplay } from "components/NoDataDisplay" +import { Loader } from "components/Loader" import { useRouter } from "next/router" import { useGeneOntologyAnnotationQuery } from "dicty-graphql-schema" import { match, P } from "ts-pattern" @@ -19,20 +21,37 @@ const OntologyPageWrapper = () => { nextFetchPolicy: "cache-only", }) - return match(result) - .with( - { - data: { - geneOntologyAnnotation: P.select(P.array({ id: P.string })), - }, - }, - (goas) => , - ) - .with({ loading: true }, () => ) - .with({ error: P.select(P.not(undefined)) }, (error) => ( - - )) - .otherwise(() => <> This message should not appear. ) + return ( + + {match(result) + .with( + { + data: { + geneOntologyAnnotation: [], + }, + }, + () => , + ) + .with( + { + data: { + geneOntologyAnnotation: P.select(P.array({ id: P.string })), + }, + }, + (goas) => , + ) + .with({ loading: true }, () => ) + .with({ error: P.select(P.not(undefined)) }, (error) => ( + + )) + .otherwise(() => ( + <> This message should not appear. + ))} + + ) } // eslint-disable-next-line import/no-default-export diff --git a/apps/genome-page/pages/[id]/index.tsx b/apps/genome-page/pages/[id]/index.tsx index bf56548f73..641b2c07a4 100644 --- a/apps/genome-page/pages/[id]/index.tsx +++ b/apps/genome-page/pages/[id]/index.tsx @@ -1,6 +1,8 @@ import { SummaryContainer } from "components/features/Summary/SummaryContainer" -import { SummaryLoader } from "components/features/Summary/SummaryLoader" -import { GraphQLErrorPage } from "@dictybase/ui-common" +import { Loader } from "components/Loader" +import { GraphQLErrorPage } from "components/errors/GraphQLErrorPage" +import { Layout } from "components/layout/Layout" +import { NoDataDisplay } from "components/NoDataDisplay" import { useRouter } from "next/router" import { useGeneSummaryQuery } from "dicty-graphql-schema" import { match, P } from "ts-pattern" @@ -18,18 +20,33 @@ const GenomePageWrapper = () => { fetchPolicy: "cache-and-network", nextFetchPolicy: "cache-only", }) - return match(result) - .with( - { - data: P.select(P.not(P.nullish)), - }, - (data) => , - ) - .with({ loading: true }, () => ) - .with({ error: P.select(P.not(undefined)) }, (error) => ( - - )) - .otherwise(() => <> This message should not appear. ) + return ( + + {match(result) + .with( + { + data: P.nullish, + }, + () => , + ) + .with( + { + data: P.select(P.not(P.nullish)), + }, + (data) => , + ) + .with({ loading: true }, () => ) + .with({ error: P.select(P.not(undefined)) }, (error) => ( + + )) + .otherwise(() => ( + <> This message should not appear. + ))} + + ) } // eslint-disable-next-line import/no-default-export diff --git a/apps/genome-page/pages/[id]/phenotypes.tsx b/apps/genome-page/pages/[id]/phenotypes.tsx index 550ec3f5fb..3ed7b1dd79 100644 --- a/apps/genome-page/pages/[id]/phenotypes.tsx +++ b/apps/genome-page/pages/[id]/phenotypes.tsx @@ -1,6 +1,8 @@ import { PhenotypesContainer } from "components/features/Phenotypes/PhenotypesContainer" -import { PhenotypesLoader } from "components/features/Phenotypes/PhenotypesLoader" -import { GraphQLErrorPage } from "@dictybase/ui-common" +import { Loader } from "components/Loader" +import { Layout } from "components/layout/Layout" +import { NoDataDisplay } from "components/NoDataDisplay" +import { GraphQLErrorPage } from "components/errors/GraphQLErrorPage" import { useListStrainsWithGeneQuery } from "dicty-graphql-schema" import { useRouter } from "next/router" import { match, P } from "ts-pattern" @@ -18,20 +20,37 @@ const PhenotypesPageWrapper = () => { nextFetchPolicy: "cache-only", }) - return match(result) - .with( - { - data: { - listStrainsWithGene: P.select(P.array({ id: P.string })), - }, - }, - (strains) => , - ) - .with({ loading: true }, () => ) - .with({ error: P.select(P.not(undefined)) }, (error) => ( - - )) - .otherwise(() => <> This message should not appear. ) + return ( + + {match(result) + .with( + { + data: { + listStrainsWithGene: P.union([], P.array({ phenotypes: [] })), + }, + }, + () => , + ) + .with( + { + data: { + listStrainsWithGene: P.select(P.array({ id: P.string })), + }, + }, + (strains) => , + ) + .with({ loading: true }, () => ) + .with({ error: P.select(P.not(undefined)) }, (error) => ( + + )) + .otherwise(() => ( + <> This message should not appear. + ))} + + ) } // eslint-disable-next-line import/no-default-export diff --git a/apps/genome-page/pages/[id]/references.tsx b/apps/genome-page/pages/[id]/references.tsx index f35e9e449e..e9636f9ad8 100644 --- a/apps/genome-page/pages/[id]/references.tsx +++ b/apps/genome-page/pages/[id]/references.tsx @@ -1,6 +1,8 @@ import { ReferencesContainer } from "components/features/References/ReferencesContainer" import { GraphQLErrorPage } from "@dictybase/ui-common" -import { ReferencesLoader } from "components/features/References/ReferencesLoader" +import { Layout } from "components/layout/Layout" +import { NoDataDisplay } from "components/NoDataDisplay" +import { Loader } from "components/Loader" import { useRouter } from "next/router" import { useListPublicationsWithGeneQuery } from "dicty-graphql-schema" import { match, P } from "ts-pattern" @@ -10,28 +12,45 @@ import { match, P } from "ts-pattern" */ const ReferencesPageWrapper = () => { const { query } = useRouter() - const gene = query.id as string + const geneId = query.id as string const result = useListPublicationsWithGeneQuery({ variables: { - gene, + gene: geneId, }, fetchPolicy: "cache-and-network", nextFetchPolicy: "cache-only", }) - return match(result) - .with( - { - data: { - listPublicationsWithGene: P.select(P.array({ id: P.string })), - }, - }, - (publications) => , - ) - .with({ loading: true }, () => ) - .with({ error: P.select(P.not(undefined)) }, (error) => ( - - )) - .otherwise(() => <> This message should not appear. ) + return ( + + {match(result) + .with( + { + data: { + listPublicationsWithGene: [], + }, + }, + () => , + ) + .with( + { + data: { + listPublicationsWithGene: P.select(P.array({ id: P.string })), + }, + }, + (publications) => , + ) + .with({ loading: true }, () => ) + .with({ error: P.select(P.not(undefined)) }, (error) => ( + + )) + .otherwise(() => ( + <> This message should not appear. + ))} + + ) } // eslint-disable-next-line import/no-default-export diff --git a/apps/genome-page/styles/errorStyles.tsx b/apps/genome-page/styles/errorStyles.tsx index 95aa6e0a36..0b29ee1f04 100644 --- a/apps/genome-page/styles/errorStyles.tsx +++ b/apps/genome-page/styles/errorStyles.tsx @@ -2,7 +2,6 @@ import { makeStyles } from "@material-ui/core/styles" const useStyles = makeStyles({ error400: { - backgroundColor: "#eff8fb", textAlign: "center", paddingTop: 30, paddingBottom: 30, @@ -14,8 +13,9 @@ const useStyles = makeStyles({ textAlign: "center", paddingTop: 40, paddingBottom: 40, - marginBottom: 30, borderRadius: 5, + marginTop: "3rem", + marginBottom: "3rem", color: "#e3e3e3", }, link500: {