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

Use showcase on splash #901

Merged
merged 10 commits into from
Jun 13, 2024
Merged
Show file tree
Hide file tree
Changes from 7 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
2 changes: 1 addition & 1 deletion static-site/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ We may wish to rename this directory to avoid any doubt.
* "Edit" -> "Scale Image" to 250 x 250px
* "File" -> "Save Image" & use PNG
3. Save the file in `./static/splash_images`
4. Edit `./src/components/Cards/coreCards.js`, `./content/community-datasets.yaml` or `./src/components/Cards/narrativeCards.js` to include the card & title.
4. Edit `./src/components/splash/showcase.yaml` to include the card & title.
5. Edit `./src/components/Footer/index.jsx` to provide credit for the photo.

### Adding new team members
Expand Down
10 changes: 0 additions & 10 deletions static-site/content/community-datasets.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,6 @@ data:
maintainers: E. Kinganda Lusamaki, INRB & C. Pratt
date: 13 Oct 2021
title: Genomic epidemiology of the 2018-21 Ebola epidemic
card:
img: "ebola2.png"
url: "/community/inrb-drc/ebola-nord-kivu"
title: "DRC Ebola (2018-19)"
frontpage: true
- url: https://nextstrain.org/community/narratives/ESR-NZ/GenomicsNarrativeSARSCoV2/aotearoa-border-incursions
title: Real-time genomics to track COVID-19 post-elimination border incursions in Aotearoa New Zealand
maintainers: Jordan Douglas, PhD, Jemma L. Geoghegan, PhD, James Hadfield, PhD, Remco Bouckaert, PhD, Matthew Storey, MSc, Xiaoyun Ren, PhD, Joep de Ligt, PhD, Nigel French, PhD, David Welch, PhD
Expand All @@ -43,11 +38,6 @@ data:
maintainers: Katherine Eaton et al
date: '2022-06-13'
title: Yersinia Pestis (multiple datasets)
card:
img: "yersinia.png"
url: "/community/ktmeaton/yersinia-pestis/maximum-likelihood/all?m=div"
title: "Yersinia pestis"
frontpage: true
- url: https://nextstrain.org/community/pestdisplace/CMDAFRICA
maintainers: PestDisPlace
date: 07 Sep 2019
Expand Down
14 changes: 0 additions & 14 deletions static-site/src/components/Cards/coreCards.js

This file was deleted.

14 changes: 0 additions & 14 deletions static-site/src/components/Cards/nCoVCards.js

This file was deleted.

14 changes: 0 additions & 14 deletions static-site/src/components/Cards/narrativeCards.js

This file was deleted.

95 changes: 86 additions & 9 deletions static-site/src/components/ListResources/index.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,27 @@
import React, { useState, useRef, useEffect } from 'react';
import React, { useState, useRef, useEffect, useCallback, createContext, useContext } from 'react';
import styled from 'styled-components';
import Select, { MultiValue } from 'react-select';
import ScrollableAnchor from '../../../vendored/react-scrollable-anchor/index';
import ScrollableAnchor, { goToAnchor } from '../../../vendored/react-scrollable-anchor/index';
import {Tooltip} from 'react-tooltip-v5';
import { useFilterOptions } from './useFilterOptions';
import { createFilterOption, useFilterOptions } from './useFilterOptions';
import { useSortAndFilter } from "./useSortAndFilter";
import { useDataFetch } from "./useDataFetch";
import {Spinner} from '../Spinner/Spinner';
import { ResourceGroup } from './ResourceGroup';
import { ErrorContainer } from "../../pages/404";
import { TooltipWrapper } from "./IndividualResource";
import {ResourceModal, SetModalResourceContext} from "./Modal";
import { Showcase, useShowcaseCards} from "./Showcase";
import { Card, FilterOption, Group, GroupDisplayNames, QuickLink, Resource } from './types';
import { CardImgWrapper, CardOuter, Showcase } from "../Showcase";
import { FilterCard, FilterOption, Group, GroupDisplayNames, QuickLink, Resource } from './types';
import { CardInner, CardTitle } from '../Cards/styles';

interface ListResourcesProps extends ListResourcesResponsiveProps {
elWidth: number
}

export const LIST_ANCHOR = "list";
const LIST_ANCHOR = "list";

const SetSelectedFilterOptions = createContext<React.Dispatch<React.SetStateAction<readonly FilterOption[]>> | null>(null);

/**
* A React component to fetch data and display the available resources,
Expand Down Expand Up @@ -67,7 +70,13 @@ function ListResources({
return (
<ListResourcesContainer>

<Showcase cards={showcaseCards} setSelectedFilterOptions={setSelectedFilterOptions}/>
<Byline>
Showcase resources: click to filter the resources to a pathogen
</Byline>

<SetSelectedFilterOptions.Provider value={setSelectedFilterOptions}>
<Showcase cards={showcaseCards} CardComponent={FilterShowcaseTile} />
</SetSelectedFilterOptions.Provider>

<Filter options={availableFilterOptions} selectedFilterOptions={selectedFilterOptions} setSelectedFilterOptions={setSelectedFilterOptions}/>

Expand Down Expand Up @@ -107,7 +116,7 @@ interface ListResourcesResponsiveProps {
/** Should the group name itself be a url? (which we let the server redirect) */
defaultGroupLinks: boolean
groupDisplayNames: GroupDisplayNames
showcase: Card[]
showcase: FilterCard[]
}

/**
Expand Down Expand Up @@ -222,4 +231,72 @@ const SortContainer = styled.div`
margin-left: 20px;
margin-right: 5px;
}
`
`

const Byline = styled.div`
font-size: 1.6rem;
border-top: 1px rgb(230, 230, 230) solid;
`


/*** SHOWCASE ***/


interface FilterShowcaseTileProps {
card: FilterCard
}


const FilterShowcaseTile = ({ card }: FilterShowcaseTileProps) => {
const setSelectedFilterOptions = useContext(SetSelectedFilterOptions);

if (!setSelectedFilterOptions) {
throw new Error("Usage of this component requires the SetSelectedFilterOptions context to be set.")
}

const filter = useCallback(
() => {
setSelectedFilterOptions(card.filters.map(createFilterOption));
goToAnchor(LIST_ANCHOR);
},
[setSelectedFilterOptions, card]
)

return (
<CardOuter>
<CardInner>
<div onClick={filter}>
<CardTitle $squashed>
{card.name}
</CardTitle>
<CardImgWrapper filename={card.img}/>
</div>
</CardInner>
</CardOuter>
)
}


/**
* Given a set of user-defined cards, restrict them to the set of cards for
* which the filters are valid given the resources known to the resource listing
* UI
*/
const useShowcaseCards = (cards?: FilterCard[], groups?: Group[]) => {
const [restrictedCards, setRestrictedCards] = useState<FilterCard[]>([]);
useEffect(() => {
if (!cards || !groups) return;
const words = groups.reduce((words, group) => {
for (const resource of group.resources) {
for (const word of resource.nameParts) {
words.add(word);
}
}
return words;
}, new Set<string>());
setRestrictedCards(cards.filter((card) => {
return card.filters.every((word) => words.has(word))
}));
}, [cards, groups]);
return restrictedCards;
}
6 changes: 3 additions & 3 deletions static-site/src/components/ListResources/types.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { Card } from "../Showcase/types"

export interface FilterOption {
value: string
label: string
Expand Down Expand Up @@ -45,9 +47,7 @@ export interface UpdateCadence {
}

// See coreShowcase in static-site/content/resource-listing.yaml
export interface Card {
name: string
img: string
export interface FilterCard extends Card {
filters: string[]
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,25 +1,22 @@
/* eslint-disable react/prop-types */
import React, { useCallback, useEffect, useState } from 'react';
import React, { useState } from 'react';
import { FaChevronDown, FaChevronUp } from 'react-icons/fa';
import styled from 'styled-components';
import {CardInner, CardImg, CardTitle} from "../Cards/styles";
import { CardImg } from "../Cards/styles";
import { theme } from "../../layouts/theme";
import { goToAnchor } from '../../../vendored/react-scrollable-anchor/index';
import { createFilterOption } from "./useFilterOptions";
import { LIST_ANCHOR } from "./index";
import { Card, FilterOption, Group } from './types';
import { Card } from './types';

const cardWidthHeight = 160; // pixels
const expandPreviewHeight = 50 //pixels
const transitionDuration = "0.3s"
const transitionTimingFunction = "ease"

interface ShowcaseProps {
cards: Card[]
setSelectedFilterOptions: React.Dispatch<React.SetStateAction<readonly FilterOption[]>>
interface ShowcaseProps<AnyCard extends Card> {
cards: AnyCard[]
CardComponent: React.FunctionComponent<{ card: AnyCard }>
}

export const Showcase = ({cards, setSelectedFilterOptions}: ShowcaseProps) => {
export const Showcase = <AnyCard extends Card>({cards, CardComponent}: ShowcaseProps<AnyCard>) => {
victorlin marked this conversation as resolved.
Show resolved Hide resolved

const [cardsContainerHeight, setCardsContainerHeight] = useState<number>(0);
const [isExpanded, setIsExpanded] = useState<boolean>(false);
Expand All @@ -44,14 +41,11 @@ export const Showcase = ({cards, setSelectedFilterOptions}: ShowcaseProps) => {

return (
<div>
<Byline>
Showcase resources: click to filter the resources to a pathogen
</Byline>
<ShowcaseContainer className={!isExpandable ? "" : isExpanded ? "expanded" : "collapsed"} $expandedHeight={cardsContainerHeight}>
<CardsContainer ref={cardsContainerRef}>
{cards.map((el) => (
<ShowcaseTile card={el} key={el.name} setSelectedFilterOptions={setSelectedFilterOptions}/>
))}
{cards.map((el) => {
return <CardComponent card={el} key={el.name} />
})}
</CardsContainer>
<PreviewOverlay onClick={toggleExpand} className={!isExpandable || isExpanded ? "hidden" : "visible"} />
</ShowcaseContainer>
Expand All @@ -65,37 +59,9 @@ export const Showcase = ({cards, setSelectedFilterOptions}: ShowcaseProps) => {
)
}

interface ShowcaseTileProps {
card: Card
setSelectedFilterOptions: React.Dispatch<React.SetStateAction<readonly FilterOption[]>>
}

/**
* NOTE: Many of the React components here are taken from the existing Cards UI
*/
const ShowcaseTile = ({card, setSelectedFilterOptions}: ShowcaseTileProps) => {
const filter = useCallback(
() => {
setSelectedFilterOptions(card.filters.map(createFilterOption));
goToAnchor(LIST_ANCHOR);
},
[setSelectedFilterOptions, card]
)

return (
<CardOuter>
<CardInner>
<div onClick={filter}>
<CardTitle $squashed>
{card.name}
</CardTitle>
<CardImgWrapper filename={card.img}/>
</div>
</CardInner>
</CardOuter>
)
}


const ShowcaseContainer = styled.div<{$expandedHeight: number}>`
position: relative;
Expand Down Expand Up @@ -156,7 +122,7 @@ const Spacer = styled.div`
min-height: 25px;
`

const CardOuter = styled.div`
export const CardOuter = styled.div`
background-color: #FFFFFF;
padding: 0;
overflow: hidden;
Expand All @@ -175,7 +141,7 @@ const getColor = () => {
return themeColors.at(-1);
}

const CardImgWrapper = ({filename}) => {
export const CardImgWrapper = ({filename}) => {
let src;
try {
// eslint-disable-next-line @typescript-eslint/no-var-requires
Expand All @@ -186,30 +152,3 @@ const CardImgWrapper = ({filename}) => {
}
return <CardImg src={src} alt={""} color={getColor()}/>
}

const Byline = styled.div`
font-size: 1.6rem;
border-top: 1px rgb(230, 230, 230) solid;
`

/**
* Given a set of user-defined cards, restrict them to the set of cards for
* which the filters are valid given the resources known to the resource listing
* UI
*/
export const useShowcaseCards = (cards?: Card[], groups?: Group[]) => {
const [restrictedCards, setRestrictedCards] = useState<Card[]>([]);
useEffect(() => {
if (!cards || !groups) return;
const words = groups.reduce((words, group) => {
for (const resource of group.resources) {
for (const word of resource.nameParts) {
words.add(word);
}
}
return words;
}, new Set<string>());
setRestrictedCards(cards.filter((card) => card.filters.every((word) => words.has(word))));
}, [cards, groups]);
return restrictedCards;
}
4 changes: 4 additions & 0 deletions static-site/src/components/Showcase/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export interface Card {
name: string
img: string
}
2 changes: 1 addition & 1 deletion static-site/src/components/splash/groupCards.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import Cards from "../Cards";
import { theme } from "../../layouts/theme";
import { UserContext } from "../../layouts/userDataWrapper";

export const createGroupCards = (groups, colors = [...theme.titleColors]) => groups.map((group) => {
const createGroupCards = (groups, colors = [...theme.titleColors]) => groups.map((group) => {
const groupColor = colors[0];
colors.push(colors.shift());

Expand Down
Loading