Skip to content

Repository files navigation

Panoptes React

Panoptes React is a React-based dataset search/browse tool backed by Panoptes. It is both a reusable React library (ES module) that you can embed in your own React app and a standalone application if no further customization is needed.

The library is built upon @knaw-huc/faceted-search-react and exposes helpers to bootstrap a Panoptes-enabled React application and requires TanStack Query for data fetching and caching and TanStack Router for client-side routing.

Panoptes schema

Install the library using: npm install @knaw-huc/panoptes-react
Build the library using: npm run build
Build the application using: npm run build:app
Run for development: npm run dev

How to run the application

The application uses the following Vite env vars (prefix VITE_). You can set them in a .env, .env.local, or via the shell when running Vite. You can also set them when running the app as a Docker container.

  • VITE_PANOPTES_URL — Base URL of the Panoptes backend
  • VITE_PANOPTES_IS_EMBEDDEDtrue to run in embedded mode, else false for rendering a menu bar at the top of the page
  • VITE_PANOPTES_SEARCH_PATH — Route for search page; it must include the dataset parameter $dataset unless the dataset is configured globally via VITE_PANOPTES_DATASET
  • VITE_PANOPTES_DETAIL_PATH — Route for detail page; it must include the dataset parameter $dataset unless the dataset is configured globally via VITE_PANOPTES_DATASET, and it must include the id parameter $id
  • VITE_PANOPTES_DATASET — Optional dataset identifier to use globally for all routes
  • VITE_PANOPTES_THEME — Optional CSS theme name to load; allowed themes are: ineo, huygens, meertens and iisg

Mock behavior

If VITE_PANOPTES_URL resolves to https://example.org, the application will start a mock API using MSW (see src/serverMock.ts) so you can try the UI without a real backend. The MSW service worker is emitted into public/ (configured via the msw field in package.json).

How to use this library

Setup

To use this library, you will have to set up TanStack Query and wrap the application tree with the Panoptes context. This context contains the configuration for the application:

Parameter Value type Required? Default value Description
url string Base URL of the Panoptes backend
isEmbedded boolean false true to run in embedded mode, else false for rendering a menu bar at the top of the page
searchPath string Route for search page; it must include the dataset parameter $dataset unless the dataset is configured globally
detailPath string Route for detail page; it must include the dataset parameter $dataset unless the dataset is configured globally, and it must include the id parameter $id
dataset string Optional dataset identifier to use globally for all routes
pageSize number 10 The number of results per page
branding string Optional branding, this is the name of the application shown in the header bar
navItems NavItem[] {"label": "Home", "href": "/"} Configuration of the navigation items in the header. They have a label, href and optional labelKey (for translation).
searchComponent RouteComponent Replace the default Search component with a custom React component
detailComponent RouteComponent Replace the default Detail component with a custom React component
resultCardRenderer (result: S extends SearchResponseItem, link: string) => ReactNode Replace the default result card render function with a custom render function
translateFn TranslateFn I18N translation function
locale string | Intl.Locale Locale forwarded to @knaw-huc/faceted-search-react for locale-aware formatting (e.g. numbers, dates)
blocks Map<string, FC<{ block: B extends Block }>> Add additional Blocks to Panoptes for customized rendering using the Block type as key, see Blocks
routes (rootRoute: AnyRoute) => AnyRoute[] Factory returning additional TanStack Router routes to register alongside the built-in search and detail routes

You can use the createPanoptesRoot helper as an alternative for createRoot (see React docs) to create a root element for your application tree. It behaves just like createRoot but will set up TanStack Query and the Panoptes context provider for you in Strict Mode.

In addition to TanStack Query, the library also requires TanStack Router for client-side routing. You can set up the RouterProvider yourself or use the PanoptesRouterProvider component provided by this library. The PanoptesRouterProvider will configure the application routes based on the configuration provided in the Panoptes context.

Example setup:

import {createPanoptesRoot, PanoptesRouterProvider, Block} from '@knaw-huc/panoptes-react';

interface HelloBlock extends Block {
    type: 'hello';
    value: string;
}

function HelloBlockComponent({block}: { block: HelloBlock }) {
    return <p>Hello {block.value}!</p>;
}

const root = createPanoptesRoot(document.getElementById('root')!, {
    url: 'https://your-panoptes.example.org',
    isEmbedded: false,
    searchPath: '/search',
    detailPath: '/detail/$id',
    dataset: 'your-dataset-id',
    blocks: new Map([['hello', HelloBlockComponent]]),
});

root.render(<PanoptesRouterProvider/>);

i18n

Panoptes React accepts an optional translateFn in its configuration to translate UI labels. It is library-agnostic — you can plug in i18next, react-intl, or any other solution as long as it matches the signature:

type TranslateFn = (key: string, options?: Record<string, unknown>) => string;

If translateFn is not provided, all components fall back to hardcoded English strings, so the library works out of the box without any i18n configuration.

Translation keys owned by panoptes-react

Key Fallback
panoptes.noValue No value
panoptes.yes Yes
panoptes.no No
panoptes.mainSiteNavigation Main site navigation

The search interface (facets, pagination, filters, etc.) is powered by @knaw-huc/faceted-search-react, which manages its own translation keys (e.g. search.*, filter.*, facet.*, pagination.*). The same translateFn is forwarded to it automatically. When no function is provided, faceted-search-react auto-detects the browser language and supports English (en) and Dutch (nl), falling back to English for other locales.

You can also provide an optional locale (a string or Intl.Locale) in the Panoptes configuration. It is forwarded to faceted-search-react for locale-aware formatting and defaults to en when not provided.

Customization

The library exposes a set of hooks to use Panoptes in your own React components. Both the default Search and Detail components can be replaced with custom components (using these hooks) by providing custom components to the searchComponent and detailComponent parameters of the Panoptes context. That is one way of customizing the UI. The other way is by providing custom blocks for Panoptes to render.

Hooks

const conf = usePanoptes()

Returns the configuration object from Panoptes. See Setup for the available parameters.

const [dataset, id] = useDataset()

Returns the dataset and the identifier for the current route.

const {data: datasets} = useDatasets()

Returns the list of datasets available on the Panoptes backend. Each Dataset has a name, a data_type and a data_configuration (with id_property, base_url and home_url). This is a suspense query, so wrap the consuming component in a <Suspense> boundary. You can pass a custom configuration type to extend DatasetConfiguration: useDatasets<MyConfiguration>().

const searchFn = useSearch(dataset: string)

Handles search functionality for the given dataset. Returns a searchFn function that can be used to fetch results.

const facets = useFacets()

Returns the configured facets for the current dataset.

const {items} = useTextFacet(name: string)

Returns the facet items for the given facet.

const details = useDetails()

Returns the details for the current item.

Blocks

Blocks are plugin-like React components that can be used to customize the rendering of both search result card and the detail view. Through Panoptes, you can configure the list of blocks to be rendered for each dataset for both the search result cards as for the detail view. Panoptes come prepared with some default blocks, but you can also provide your own blocks (see Setup).

Panoptes provides the following blocks out of the box:

  • list: Renders a list of items, this may include deep hierarchies
  • cmdi: Renders a CMDI record
  • (TODO) More to come

If you want to provide your own custom blocks, you will have to know the Block interface which is defined as follows:

type BlockValue = string | object | Block[];

interface Block {
    type: string;
    value: BlockValue;
    config?: object;
}

A block consists of a type, a value and sometimes config. The type determines the rendering of the block. The value and the config are passed to the block component as properties. A simple block component could look like this:

interface HelloWorldBlock extends Block {
    type: 'hello_world';
    value: { hello?: string };
}

function RenderHelloWorld({block}: { block: HelloWorldBlock }) {
    return (
        <h1>Hello {block.value.hello || 'World'}</h1>
    );
}

About

Panoptes React is a React-based dataset browser using Panoptes.

Resources

Stars

Watchers

Forks

Releases

Packages

Used by

Contributors

Languages