A React component library providing themed collection views for Giving Tuesday's data embedding system. TypeScript, Rollup, CSS Modules, Storybook.
The library is transitioning from a Card + ItemPage model to a List composition model where each theme owns its full collection layout.
Legacy pattern — themes export a Card and ItemPage. The consuming app builds the surrounding UI (filters, sort, grid). Used by: gtrex, datamarts, datasets, african-giving-traditions, gtdc, posterchild, problems-solutions, viz-database.
New pattern — themes export a List component that composes a complete collection view: filters, sort, map, and card grid. The consuming app passes data and callbacks via ListProps; the theme controls the layout. Used by: map-theme. Legacy themes will transition to this pattern over time.
List (ListProps)
├── FilterPills — active filter tags with remove/clear
├── SortDropdown — passed in by consumer
├── FilterPane — sidebar of MultiSelect dropdowns
├── MapFilter — interactive map with pins + country clusters
└── ListItems — card grid
└── Card — theme-specific card
// Legacy — individual cards
const CardComponent = getThemeCard('gtrex');
const ItemPageComponent = getThemeItemPage('datasets');
// New — full list composition
const ListComponent = getThemeList('map-theme');Multiple aliases can map to one theme:
['giving-lab', 'givinglab', 'gtrex']; // all resolve to GtrexCard| Component | Location | Purpose |
|---|---|---|
| BaseCard / CardContent | src/BaseCard/, src/CardContent/ |
Card shell and content layout |
| MapFilter | src/MapFilter/ |
MapLibre GL map with pins and country counts |
| MultiSelect | src/MultiSelect/ |
Checkbox dropdown filter |
| FilterPills | src/FilterPills/ |
Active filter tag pills |
| ListItems | src/ListItems/ |
Card grid accepting a CardComponent |
| Widget | src/Widget/ |
Embeddable card grid with modal (legacy) |
src/
├── index.tsx # Package exports
├── types/index.ts # All type definitions
├── themes/
│ ├── cards.tsx # Card theme registry (legacy)
│ ├── item-pages.tsx # ItemPage theme registry (legacy)
│ ├── lists.tsx # List theme registry (new)
│ └── <theme-name>/
│ ├── List.tsx # List composition (new themes)
│ ├── Card.tsx # Card component
│ ├── ItemPage.tsx # Item page (legacy themes)
│ └── *.module.css # Scoped styles
├── MapFilter/ # Map visualization
├── MultiSelect/ # Filter dropdowns
├── FilterPills/ # Active filter pills
├── ListItems/ # Card grid
├── BaseCard/ # Card shell
├── CardContent/ # Card content layout
├── Widget/ # Embeddable widget (legacy)
├── utils/ # Theme resolution, text processing
└── hooks/ # useConvertToHtml, useItemModal
geo-config/ # Country ISO → name/subregion config
stories/ # Storybook stories and mock data
- Node.js 20+
- npm
git clone https://github.com/Giving-Tuesday/co-collection-themes.git
cd co-collection-themes
npm installnpm install triggers a build automatically via the prepare script.
# Development
npm run dev # Clean + rollup watch mode
npm run storybook # Storybook dev server on localhost:6006
# Build
npm run build # Clean + production build → dist/
npm run build:stories # Build static Storybook
# Quality
npm run lint # ESLint check
npm run lint:fix # ESLint auto-fix
npm run format # Prettier format
npm run type-check # TypeScript validation (tsc --noEmit)
# Utilities
npm run generate:geo-config # Regenerate geo-config/config.json- Run
npm run storybookfor component development - Run
npm run devto rebuild on changes - Validate before committing:
npm run lint && npm run type-check
NPM Package (GitHub Packages):
Can be published automatically with contained .github/publish.yml file.
For beta releases:
# Update version in package.json
npm version prepatch --preid beta # or patch, minor, etc.
npm version prerelease --preid beta
# This creates a tag like v0.6.1-beta.1
# Push the tag
git push --follow-tags
# Or push tag explicitly
git push origin v0.6.1-beta.1For stable releases:
# Update version in package.json
npm version patch # or minor, major
# Push the tag
git push --follow-tags
# Or push tag explicitly
git push origin v0.6.2- Registry: GitHub Packages (
https://npm.pkg.github.com) - Scope:
@giving-tuesday/co-collection-themes - Access: Public within Giving Tuesday organization
dist/index.cjs.js- CommonJS build for Node.js environmentsdist/index.esm.js- ES Module build for modern bundlers- Source maps for both builds
- CSS modules are inlined and scoped
npm install @giving-tuesday/co-collection-themes{
"react": ">=18 <20",
"react-dom": ">=18 <20",
"react-icons": ">=4.0.0",
"react-router-dom": ">=6.0.0"
}// New pattern — full list composition
import { getThemeList } from '@giving-tuesday/co-collection-themes';
const ListComponent = getThemeList('map-theme');
<ListComponent
listItems={{ items, buildHref, CustomLink }}
search={{ searchTerm, onSearch, onClear }}
filterPills={{ filterParams, onRemove, onReset }}
filterPane={{ filters, selectedValues, onChange, onReset }}
SortDropdown={SortDropdown}
defaultSort="title-asc"
mapFilter={{ locations, onCountryFilter, onClearFilter }}
/>
// Legacy — individual cards
import { getThemeCard, Widget } from '@giving-tuesday/co-collection-themes';
const CardComponent = getThemeCard('gtrex');
<CardComponent item={itemData} href="/items/123" />
<Widget items={collectionItems} widgetTheme="datasets" maxCardsPerRow={3} />// Item (for cards)
{
_id: string;
title: string;
author: string[];
desc: string;
custom_fields: { /* theme-specific */ };
}
// Location (for map)
{
_id: string;
title: string;
desc: string;
slug: string;
coordinates?: [number, number];
geoCountry?: string;
geoSubregion?: string;
}- Create
src/themes/<name>/withList.tsx,Card.tsx,index.tsx, CSS modules List.tsxacceptsListPropsand composes shared components- Register in
src/themes/lists.tsx(listThemeEntries) - Export from
src/themes/index.tsxandsrc/index.tsx - Add mock data and Storybook stories
- Create
src/themes/<name>/withCard.tsx,ItemPage.tsx,index.tsx, CSS modules - Register in
src/themes/cards.tsxandsrc/themes/item-pages.tsx - Export from
src/index.tsx - Add mock data and Storybook stories