Skip to content
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
5 changes: 5 additions & 0 deletions static/src/js/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { Navigate, createBrowserRouter } from 'react-router-dom'
import CollectionAssociationFormPage from '@/js/pages/CollectionAssociationFormPage/CollectionAssociationFormPage'
import DraftListPage from '@/js/pages/DraftListPage/DraftListPage'
import DraftPage from '@/js/pages/DraftPage/DraftPage'
import GranulesListPage from '@/js/pages/GranulesListPage/GranulesListPage'
import GroupFormPage from '@/js/pages/GroupFormPage/GroupFormPage'
import GroupListPage from '@/js/pages/GroupListPage/GroupListPage'
import GroupPage from '@/js/pages/GroupPage/GroupPage'
Expand Down Expand Up @@ -111,6 +112,10 @@ export const App = () => {
path: '/:type/:conceptId/collection-association',
element: <ManageCollectionAssociationPage />
},
{
path: '/collections/:conceptId/granules',
element: <GranulesListPage />
},
{
path: '/collections/:conceptId/service-associations',
element: <ManageServiceAssociationsPage />
Expand Down
19 changes: 19 additions & 0 deletions static/src/js/__tests__/App.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,12 @@ vi.mock('@/js/pages/KeywordManagerPage/KeywordManagerPage', () => ({
))
}))

vi.mock('@/js/pages/GranulesListPage/GranulesListPage', () => ({
default: vi.fn(() => (
<div data-testid="mock-granules-list-page">Granules List Page</div>
))
}))

const setup = () => {
render(
<App />
Expand Down Expand Up @@ -229,4 +235,17 @@ describe('App component', () => {
window.history.pushState({}, '', '/')
})
})

describe('when rendering the "/collections/:conceptId/granules" route', () => {
test('renders the granules list page', async () => {
const mockConceptId = 'C1234567-TEST'
window.history.pushState({}, '', `/collections/${mockConceptId}/granules`)

setup()

expect(await screen.findByTestId('mock-granules-list-page')).toBeInTheDocument()

window.history.pushState({}, '', '/')
})
})
})
141 changes: 141 additions & 0 deletions static/src/js/components/GranulesList/GranulesList.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
import React, { useState } from 'react'
import { useSuspenseQuery } from '@apollo/client'
import { useParams } from 'react-router'
import moment from 'moment'
import pluralize from 'pluralize'
import Col from 'react-bootstrap/Col'
import Row from 'react-bootstrap/Row'

import { GET_GRANULES } from '@/js/operations/queries/getGranules'
import ControlledPaginatedContent from '@/js/components/ControlledPaginatedContent/ControlledPaginatedContent'
import getConceptTypeByConceptId from '../../utils/getConceptTypeByConceptId'
import { DATE_FORMAT } from '../../constants/dateFormat'
import Table from '../Table/Table'

const GranulesList = () => {
const { conceptId } = useParams()
const [activePage, setActivePage] = useState(1)

const limit = 10
const offset = (activePage - 1) * limit

const derivedConceptType = getConceptTypeByConceptId(conceptId)

const { data } = useSuspenseQuery(GET_GRANULES, {
variables: {
params: {
conceptId
},
granulesParams: {
limit,
offset
}
}
})

const { [derivedConceptType.toLowerCase()]: concept } = data
const { granules } = concept
const { count, items } = granules

const setPage = (nextPage) => {
setActivePage(nextPage)
}

const columns = [
{
dataKey: 'conceptId',
title: 'Concept Id',
className: 'col-auto'
},
{
dataKey: 'title',
title: 'Title',
className: 'col-auto'
},
{
dataKey: 'revisionDate',
title: 'Revision Date (UTC)',
className: 'col-auto',
dataAccessorFn: (cellData) => {
if (cellData === null || cellData === undefined) {
return 'N/A'
}

return moment.utc(cellData).format(DATE_FORMAT)
}
}
]

return (
<Row>
<Col sm={12}>
<ControlledPaginatedContent
activePage={activePage}
count={count}
limit={limit}
setPage={setPage}
>
{
({
totalPages,
pagination,
firstResultPosition,
lastResultPosition
}) => {
const paginationMessage = count > 0
? `Showing ${totalPages > 1 ? `${firstResultPosition}-${lastResultPosition} of` : ''} ${count} ${pluralize('granule', count)}`
: 'No granules found'

return (
<>
<Row className="d-flex justify-content-between align-items-center mb-4">
<Col className="mb-4 flex-grow-1" xs="auto">
{
(!!count) && (
<span className="text-secondary fw-bolder">{paginationMessage}</span>
)
}
</Col>
<Col className="mb-4 flex-grow-1" xs="auto" />
{
totalPages > 1 && (
<Col xs="auto">
{pagination}
</Col>
)
}
</Row>
<Table
id="granule-results-table"
columns={columns}
data={items}
generateCellKey={({ conceptId: granuleConceptId, revisionId }, dataKey) => `column_${dataKey}_${granuleConceptId}_${revisionId}`}
generateRowKey={({ conceptId: granuleConceptId, revisionId }) => `row_${granuleConceptId}_${revisionId}`}
noDataMessage="No granules found"
count={count}
setPage={setPage}
limit={limit}
offset={offset}
/>
{
totalPages > 1 && (
<Row>
<Col xs="12" className="pt-4 d-flex align-items-center justify-content-center">
<div>
{pagination}
</div>
</Col>
</Row>
)
}
</>
)
}
}
</ControlledPaginatedContent>
</Col>
</Row>
)
}

export default GranulesList
Loading