Skip to content

Commit

Permalink
Add RelatedSources Core Data component (#286)
Browse files Browse the repository at this point in the history
* add relatedworks component

* export RelatedWorks

* refactor into RelatedSources

* fix bad import path

* add RelatedItems back in
  • Loading branch information
camdendotlol authored May 21, 2024
1 parent a26e09b commit 4b81f04
Show file tree
Hide file tree
Showing 13 changed files with 297 additions and 5 deletions.
1 change: 1 addition & 0 deletions packages/core-data/src/components/RelatedItems.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ type Props = {

/**
* This component render a list of related items.
* @deprecated
*/
const RelatedItems = (props: Props) => {
const { data: { items } = {}, loading } = useLoader(props.onLoad, []);
Expand Down
110 changes: 110 additions & 0 deletions packages/core-data/src/components/RelatedSources.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
// @flow

import React from 'react';
import _ from 'underscore';
import type { Source as SourceType } from '../types/Source';
import LoadAnimation from './LoadAnimation';
import { useLoader } from '../hooks/CoreData';

type Props = {
/**
* Name of the class(es) to apply to the `ul` element.
*/
className?: string,

/**
* Callback fired when a source in the list is clicked.
*/
onClick: (source: SourceType) => void,

/**
* Callback fired on mount to load the list of items.
*/
onLoad: () => Promise<any>,

/**
* Function used to render the description element.
*/
renderDescription?: (source: SourceType) => JSX.Element,

/**
* Function used to render the header element.
*/
renderHeader?: (source: SourceType) => JSX.Element,

/**
* Function used to render the image element.
*/
renderImage?: (source: SourceType) => JSX.Element,

/**
* Type of the source being fetched.
*/
sourceType: 'instances' | 'items' | 'works'
};

/**
* This component render a list of related items.
*/
const RelatedSources = (props: Props) => {
const { data = {}, loading } = useLoader(props.onLoad, []);

if (loading) {
return (
<LoadAnimation />
);
}

return (
<ul
className={props.className}
>
{ _.map(data[props.sourceType], (item) => (
<li>
<div
className='min-h-[5.5em] flex flex-col justify-start'
>
<button
className='my-0.5 flex-grow text-left inline-flex rounded-none bg-gray-100'
onClick={() => props.onClick(item)}
type='button'
>
{ props.renderImage && (
<div
className='w-[160px] h-[120px]'
>
{ props.renderImage(item) }
</div>
)}
<div
className='py-3 px-5 flex-grow'
>
{ props.renderHeader && (
<div
className='py-0.5 font-semibold uppercase text-sm'
>
{ props.renderHeader(item) }
</div>
)}
<h2
className='py-0.5 font-semibold text-lg'
>
{ item.primary_name?.name?.name }
</h2>
{ props.renderDescription && (
<div
className='py-0.5 font-light text-sm'
>
{ props.renderDescription(item) }
</div>
)}
</div>
</button>
</div>
</li>
))}
</ul>
);
};

export default RelatedSources;
2 changes: 0 additions & 2 deletions packages/core-data/src/hooks/CoreData.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,6 @@ export const useLoader = (onLoad, params = {}, deps = []) => {
const [loading, setLoading] = useState(false);
const [page, setPage] = useState(DEFAULT_PAGE);

const { baseUrl, projectIds } = useContext(CoreDataContext);

/**
* Memo-izes the list metadata.
*
Expand Down
1 change: 1 addition & 0 deletions packages/core-data/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export { default as RelatedOrganizations } from './components/RelatedOrganizatio
export { default as RelatedPeople } from './components/RelatedPeople';
export { default as RelatedPlaces } from './components/RelatedPlaces';
export { default as RelatedPlacesLayer } from './components/RelatedPlacesLayer';
export { default as RelatedSources } from './components/RelatedSources';
export { default as RelatedTaxonomies } from './components/RelatedTaxonomies';
export { default as SearchResultsLayer } from './components/SearchResultsLayer';

Expand Down
26 changes: 26 additions & 0 deletions packages/core-data/src/services/BaseService.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,19 @@ class BaseService {
return fetch(url).then((response) => response.json());
}

/**
* Calls the GET /core_data/public/<version>/<route>/:id/instances API endpoint.
*
* @param id
* @param params
*
* @returns {Promise<any>}
*/
fetchRelatedInstances(id, params = {}) {
const url = Api.buildNestedUrl(this.baseUrl, this.getRoute(), id, 'instances', this.getSearchParams(params));
return fetch(url).then((response) => response.json());
}

/**
* Calls the GET /core_data/public/<version>/<route>/:id/items API endpoint.
*
Expand Down Expand Up @@ -131,6 +144,19 @@ class BaseService {
return fetch(url).then((response) => response.json());
}

/**
* Calls the GET /core_data/public/<version>/<route>/:id/works API endpoint.
*
* @param id
* @param params
*
* @returns {Promise<any>}
*/
fetchRelatedWorks(id, params = {}) {
const url = Api.buildNestedUrl(this.baseUrl, this.getRoute(), id, 'works', this.getSearchParams(params));
return fetch(url).then((response) => response.json());
}

// protected

getRoute() {
Expand Down
9 changes: 9 additions & 0 deletions packages/core-data/src/types/Instance.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// @flow

import type { SourceTitle } from './SourceTitle';

export type Instance = {
uuid: string,
primary_name: SourceTitle,
source_titles: Array<SourceTitle>
};
7 changes: 7 additions & 0 deletions packages/core-data/src/types/Source.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// @flow

import type { Instance } from './Instance';
import type { Item } from './Item';
import type { Work } from './Work';

export type Source = Instance | Item | Work;
9 changes: 9 additions & 0 deletions packages/core-data/src/types/Work.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// @flow

import type { SourceTitle } from './SourceTitle';

export type Work = {
uuid: string,
primary_name: SourceTitle,
source_titles: Array<SourceTitle>
};
35 changes: 35 additions & 0 deletions packages/storybook/.storybook/api/core-data/Instances.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// @flow

import { faker } from '@faker-js/faker';
import Base from './Base';

class Instances extends Base {
/**
* Returns a single instance.
*/
buildItem() {
return {
uuid: faker.string.uuid(),
primary_name: {
name: {
name: faker.lorem.words({ min: 1, max: 3 })
}
}
};
}

/**
* Returns the instances parameter name.
*
* @returns {string}
*/
getIndexAttribute() {
return 'instances';
}

getShowAttribute() {
return 'instance';
}
}

export default new Instances();
35 changes: 35 additions & 0 deletions packages/storybook/.storybook/api/core-data/Works.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// @flow

import { faker } from '@faker-js/faker';
import Base from './Base';

class Works extends Base {
/**
* Returns a single work.
*/
buildItem() {
return {
uuid: faker.string.uuid(),
primary_name: {
name: {
name: faker.lorem.words({ min: 1, max: 3 })
}
}
};
}

/**
* Returns the works parameter name.
*
* @returns {string}
*/
getIndexAttribute() {
return 'works';
}

getShowAttribute() {
return 'work';
}
}

export default new Works();
14 changes: 13 additions & 1 deletion packages/storybook/.storybook/routes/CoreData.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@

import { BASE_URL } from '../api/core-data/Base';
import Events from '../api/core-data/Events';
import Instances from '../api/core-data/Instances';
import Items from '../api/core-data/Items';
import Places from '../api/core-data/Places';
import Organizations from '../api/core-data/Organizations';
import People from '../api/core-data/People';
import Taxonomies from '../api/core-data/Taxonomies';
import Manifests from '../api/core-data/Manifests';
import MediaContents from '../api/core-data/MediaContents';
import Taxonomies from '../api/core-data/Taxonomies';
import Works from '../api/core-data/Works';

/**
* Adds the Core Data dummy routes.
Expand All @@ -25,6 +27,11 @@ const addRoutes = (router) => {
response.end();
});

router.get(`${BASE_URL}/events/:id/instances`, (request, response) => {
response.send(Instances.fetchItems(5));
response.end();
});

router.get(`${BASE_URL}/events/:id/items`, (request, response) => {
response.send(Items.fetchItems(5));
response.end();
Expand All @@ -45,6 +52,11 @@ const addRoutes = (router) => {
response.end();
});

router.get(`${BASE_URL}/events/:id/works`, (request, response) => {
response.send(Works.fetchItems(5));
response.end();
});

router.get(`${BASE_URL}/places/:id`, (request, response) => {
response.send(Places.fetchItem());
response.end();
Expand Down
5 changes: 3 additions & 2 deletions packages/storybook/src/core-data/EventDetails.stories.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import React from 'react';
import _ from 'underscore';
import EventDetails from '../../../core-data/src/components/EventDetails';
import mapStyle from '../data/MapStyles.json';
import RelatedItems from '../../../core-data/src/components/RelatedItems';
import RelatedSources from '../../../core-data/src/components/RelatedSources';
import RelatedMedia from '../../../core-data/src/components/RelatedMedia';
import RelatedPeople from '../../../core-data/src/components/RelatedPeople';
import RelatedPlacesLayer from '../../../core-data/src/components/RelatedPlacesLayer';
Expand Down Expand Up @@ -75,7 +75,7 @@ export const RelatedRecords = withCoreDataContextProvider(() => {
>
Related Resources
</h2>
<RelatedItems
<RelatedSources
onClick={(item) => action('click')(item)}
onLoad={(params) => EventsService.fetchRelatedItems('1', params)}
renderDescription={() => faker.date.anytime().toLocaleDateString()}
Expand All @@ -86,6 +86,7 @@ export const RelatedRecords = withCoreDataContextProvider(() => {
src={`https://picsum.photos/800/600?random=${item.uuid}`}
/>
)}
sourceType='items'
/>
</div>
<div
Expand Down
48 changes: 48 additions & 0 deletions packages/storybook/src/core-data/RelatedSources.stories.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// @flow

import { action } from '@storybook/addon-actions';
import React from 'react';
import RelatedSources from '../../../core-data/src/components/RelatedSources';
import { useEventsService } from '../../../core-data/src/hooks/CoreData';
import withCoreDataContextProvider from '../hooks/CoreDataContextProvider';

export default {
title: 'Components/Core Data/RelatedSources',
component: RelatedSources
};

export const Default = withCoreDataContextProvider(() => {
const EventsService = useEventsService();

return (
<RelatedSources
onClick={action('click')}
onLoad={(params) => EventsService.fetchRelatedItems('1', params)}
sourceType='items'
/>
);
});

export const Instances = withCoreDataContextProvider(() => {
const EventsService = useEventsService();

return (
<RelatedSources
onClick={action('click')}
onLoad={(params) => EventsService.fetchRelatedInstances('1', params)}
sourceType='instances'
/>
);
});

export const Works = withCoreDataContextProvider(() => {
const EventsService = useEventsService();

return (
<RelatedSources
onClick={action('click')}
onLoad={(params) => EventsService.fetchRelatedWorks('1', params)}
sourceType='works'
/>
);
});

0 comments on commit 4b81f04

Please sign in to comment.