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

Collections page UI #521

Merged
merged 10 commits into from
Jun 13, 2022
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
11 changes: 11 additions & 0 deletions frontend/i18n/en/collections.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"collectionsPage": {
"title": "Collections",
"intro": "Useful, awesome, and interesting plugins curated by the napari community.",
"protocols": "<bold>Protocols</bold> show how to use a set of plugins to complete an image analysis workflow",
"toolkits": "<bold>Toolkits</bold> are great for getting started within a research domain",
"assortments": "<bold>Assortments</bold> are connected by a central theme",
"createCollection": "Create a new collection",
"fetchError": "Server Error: Unable to get collections"
}
}
2 changes: 2 additions & 0 deletions frontend/i18n/en/common.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"about": "About",
"close": "Close",
"collections": "Collections",
"contact": "Contact",
"cziOss": "Chan Zuckerberg Initiative EOSS Program",
"download": "Download",
Expand All @@ -17,6 +18,7 @@
"napariMainWebsite": "napari main website",
"napariPlausible": "napari.dev analytics dashboard",
"plausiblePrivacy": "Plausible.io Privacy Policy",
"plugins": "Plugins",
"privacy": "Privacy",

"ariaLabels": {
Expand Down
1 change: 1 addition & 0 deletions frontend/i18n/en/pageTitles.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"base": "napari hub",
"home": "$t(base) | search",
"preview": "preview",
"collections": "$t(base) | collections",
"plugin": {
"base": "$t(base) | plugins",
"loading": "loading",
Expand Down
17 changes: 16 additions & 1 deletion frontend/mock-server.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@ if (process.env.MOCK_SERVER === 'false') {
}

const express = require('express');
const { set } = require('lodash');
const { set, pick } = require('lodash');

const napariPlugin = require('./src/fixtures/plugin.json');
const pluginIndex = require('./src/fixtures/index.json');
const collections = require('./src/fixtures/collections.json');

const app = express();

Expand Down Expand Up @@ -37,4 +38,18 @@ app.get('/plugins/:name', async (req, res) => {
}
});

app.get('/collections', async (_, res) => {
res.json(
collections.map((collection) =>
pick(collection, [
'title',
'cover_image',
'summary',
'curator',
'symbol',
]),
),
);
});

app.listen(8081, () => console.log('Started mock API server'));
6 changes: 5 additions & 1 deletion frontend/next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,11 @@ const previewOptions = isPreview
};
},
}
: {};
: {
images: {
domains: ['raw.githubusercontent.com', 'github.com'],
},
};

if (isPreview) {
console.log('Building preview page for plugin file', PREVIEW);
Expand Down
132 changes: 132 additions & 0 deletions frontend/scripts/create-collection-data.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
/**
* This script is used for generating the collection fixture data in a
* reproducible way.
*/

import { sample } from 'lodash-es';
import fs from 'fs';
import path from 'path';

const titles = [
'Time-saving segmentation plugins',
'Allen Cell Institute’s plugins',
'ABRF biology workflow',
'Point-detection plugins',
'The most essential readers and writers for biologists',
'Neuro-pathway detection',
'The best sample datasets for cell biology',
'napari themes',
'Segmentify any cell type',
'Contrast-enhancing plugins for microscopy analysis',
'Segmentify anything you can imagine',
'Feature detection plugins',
];

const summaries = [
'Hand-picked plugins to help you segment your cells ever so efficiently.',
'All plugins made public by the Allen Cell Institute; primarily aimed at cell biologists.',
'Plugins filling the gap in the ABRF workflow in napari.',
'The most accurate point-detection plugins for napari; list actively updated.',
"Open and save to most any file format you'll need as a biologist in napari with this list.",
'napari plugins specifically for neurobiologists looking to detect neural pathways.',
'Sample datasets with several pre-identified features to test workflows against.',
"Every plugin capable of changing napari's theme, from fonts to colors.",
'From plant cells to paramecia, these plugins have you and your obscure cell types covered',
'Reduce noise and enhance signal; some included plugins also de-blur impressively.',
'From pumpkins to penguins to jelly beans in jars, let the ML do the counting for you!',
'Just a few favorite featurization plugins from the community.',
];

const curatorNames = [
'Vivien Donovan',
'Quigley Schoolcraft',
'Moab Luminoso',
'Kinshasa Kosette Mickelthwait-Venkatesh',
'Fritz Curiel',
'Zed Wexler',
'Faisal Farooq',
'Brigitta Fleet',
'Guster Talltooth',
'Via Levine',
'Alfredo Angelopolous',
'Greggor Golightly',
];

const curatorInstitutions = [
'Imaging Scientist, Cell Core Research Facility',
'Machine Learning Engineer, Allen Cell Institute',
'Imaging Research Scientist, Association of Biomolecular Resource Facilities',
'Research Biologist, Low Anchor Center for Human Disease and Study',
'Cell Innovation Expert, University of Catania',
'Research Biologist, Cardiff College of Microbiology',
'Cell Disease Specialist, University of Washington Medical Center',
'Chan Zuckerberg Initiative',
'Orinda Medical Center Fellow, Orinda Medical Center',
'Imaging Specialist, Hobie Swingle Institute for Cell Science',
'Research Scientist, ML for the Cell',
'Cell Innovation Scientist, Carpati Institution',
];

const curatorTitles = [
'Head Research Engineer',
'Product Manager',
'Software Engineer',
'Imaging Scientist',
];

const images = [
'national-cancer-institute-4zA4w-dr5WM-unsplash.jpg',
'national-cancer-institute-J28Nn-CDbII-unsplash.jpg',
'national-cancer-institute-LnvCEXQwC-o-unsplash.jpg',
'national-cancer-institute-NbZQYileaOI-unsplash.jpg',
'national-cancer-institute-TX9pU27p6D0-unsplash.jpg',
'national-cancer-institute-W2OVh2w2Kpo-unsplash.jpg',
'national-cancer-institute-dMZC6hdobnk-unsplash.jpg',
'national-cancer-institute-evhLgfOjU5Y-unsplash.jpg',
'national-cancer-institute-irmUWEcjIz4-unsplash.jpg',
'national-cancer-institute-lsxKuARrQXI-unsplash.jpg',
'national-cancer-institute-mbL91Lg56zc-unsplash.jpg',
'national-cancer-institute-rvDeUG7YV64-unsplash.jpg',
];

const data = {
title: '',
updated_date: '2022-05-30',
summary: '',
cover_image: '',
curator: {
name: '',
title: '',
affiliation: {
institution: '',
website: 'https://example.com',
},
},
};

const result = [];

for (let i = 0; i < titles.length; i += 1) {
const coverImage = `https://raw.githubusercontent.com/chanzuckerberg/napari-hub-collections/main/images/${images[i]}`;

result.push({
...data,
title: titles[i],
symbol: titles[i].toLowerCase().replaceAll(' ', '-').replaceAll('’', ''),
summary: summaries[i],
cover_image: coverImage,
curator: {
...data.curator,
name: curatorNames[i],
title: sample(curatorTitles),
affiliation: {
...data.curator.affiliation,
institution: curatorInstitutions[i],
},
},
});
}

const rootDir = path.resolve(new URL(import.meta.url).pathname, '../..');
const indexFile = path.resolve(rootDir, 'src/fixtures/collections.json');
fs.writeFileSync(indexFile, JSON.stringify(result, null, 2));
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,15 @@ exports[`<AppBar /> should match snapshot 1`] = `
>
<a
class="ml-6"
href="/about"
href="/"
>
About
Plugins
</a>
<a
class="ml-6"
href="/faq"
href="/collections"
>
FAQ
Collections
</a>
</div>
</nav>
Expand Down
6 changes: 6 additions & 0 deletions frontend/src/components/AppBar/useAppBarLinks.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
import { useLinks } from '@/hooks/useLinks';
import { useIsFeatureFlagEnabled } from '@/utils/featureFlags';

export function useAppBarLinks() {
const links = useLinks();
const isCollectionsEnabled = useIsFeatureFlagEnabled('collections');

if (isCollectionsEnabled) {
return [links.PLUGINS, links.COLLECTIONS];
}

return [links.ABOUT, links.FAQ];
}
61 changes: 61 additions & 0 deletions frontend/src/components/CollectionsPage/CollectionCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import clsx from 'clsx';
import Image from 'next/image';

import { Link } from '@/components/Link';
import { CollectionIndexData } from '@/types/collections';

interface Props {
collection: CollectionIndexData;
}

/**
* Shared line height class names for text.
* TODO Extract this to design system
*/
const LINE_HEIGHT_CLASS_NAME = 'leading-[150%] screen-495:leading-[125%]';

/**
* Renders an image, title, summary, and curator for a collection on the
* collection home page.
*/
export function CollectionCard({ collection }: Props) {
return (
<Link
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How feasible would it be to move these into CSS rules in a separate file? e.g. having specific styling defined for a collection card, collection button, etc. Not sure if that is part of the "Extract this to design system" work or if there are other reasons not to do this, but might be worth considering to separate concerns & perhaps enable designers to more easily make updates.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ahh so for frontend we support both Tailwind and SCSS. Right now approach we're following is always use Tailwind and then SCSS if not possible. This helps us keep the resulting CSS bundle small and helps with building UIs fast

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

However, we do have a growing need to share certain styles from a design system. We're working with the SDS (Science Design System) team right now to get that going but it's out of scope for this PR 🤣

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@codemonkey800 Sounds good! I'm not too familiar with Tailwind so would love an overview of how that works some time, but definitely not a blocker or pressing need.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good, happy to give an overview of the frontend tech on the hub 😄

className="hover:bg-napari-hover-gray focus:bg-napari-hover-gray"
href={`/collections/${collection.symbol}`}
>
<div className="relative w-full h-[50px] screen-495:h-[100px]">
<Image
className="object-cover"
src={collection.cover_image}
alt={`${collection.title} cover image`}
layout="fill"
/>
</div>

<h2
className={clsx(
'my-[10px] screen-495:my-[20px]',
'font-semibold text-[11px] screen-495:text-[17px]',
LINE_HEIGHT_CLASS_NAME,
)}
>
{collection.title}
</h2>
<p className="text-[11px] screen-495:text-[14px] leading-[150%]">
{collection.summary}
</p>

<p
className={clsx(
'space-x-1 mt-[10px] screen-495:mt-[20px]',
'text-[9px] screen-495:text-[11px]',
LINE_HEIGHT_CLASS_NAME,
)}
>
<span className="font-semibold">{collection.curator.name}</span>
<span>{collection.curator.affiliation.institution}</span>
</p>
</Link>
);
}
Loading