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 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.
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
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 backendVITE_PANOPTES_IS_EMBEDDED—trueto run in embedded mode, elsefalsefor rendering a menu bar at the top of the pageVITE_PANOPTES_SEARCH_PATH— Route for search page; it must include the dataset parameter$datasetunless the dataset is configured globally via VITE_PANOPTES_DATASETVITE_PANOPTES_DETAIL_PATH— Route for detail page; it must include the dataset parameter$datasetunless the dataset is configured globally via VITE_PANOPTES_DATASET, and it must include the id parameter$idVITE_PANOPTES_DATASET— Optional dataset identifier to use globally for all routes
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).
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 | ||
searchComponent |
RouteComponent |
Replace the default Search component with a custom React component |
||
detailComponent |
RouteComponent |
Replace the default Detail component with a custom React component |
||
blocks |
Block[] |
(TODO) Add additional Blocks to Panoptes for customized rendering, see Blocks |
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} from '@knaw-huc/panoptes-react';
const root = createPanoptesRoot(document.getElementById('root')!, {
url: 'https://your-panoptes.example.org',
isEmbedded: false,
searchPath: '/search',
detailPath: '/detail/$id',
dataset: 'your-dataset-id'
});
root.render(<PanoptesRouterProvider/>);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.
const conf = usePanoptes()
Returns the configuration object from Panoptes. See Setup for the available parameters.
const [dataset, id] = useDataset(source: 'search' | 'detail')
Returns the dataset and the identifier for the current route.
const {searchFn, facets, pageSize} = useSearch(dataset: string)
Handles search functionality for the given dataset. Returns a searchFn function that can be used to fetch results,
a facets object with the applied facets for the dataset. The pageSize parameter is the page size for the search
results.`
const facets = useFacets()
Returns the configured facets for the current dataset.
const {items} = useFacet(name: string)
Returns the facet items for the given facet.
const details = useDetails()
Returns the details for the current item.
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 hierarchiescmdi: 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;
}A block consists of a type and a value. The type determines the rendering of the block. The value is passed to the
block component as a property. 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>
);
}