Skip to content

Commit

Permalink
feat: add basic study table
Browse files Browse the repository at this point in the history
  • Loading branch information
fboulnois committed Aug 10, 2023
1 parent 61631df commit e2add21
Show file tree
Hide file tree
Showing 3 changed files with 374 additions and 1 deletion.
205 changes: 205 additions & 0 deletions src/components/CollapsibleTable.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
import * as React from 'react';
import Box from '@mui/material/Box';
import Collapse from '@mui/material/Collapse';
import IconButton from '@mui/material/IconButton';
import { styled } from '@mui/material/styles';
import Table from '@mui/material/Table';
import TableBody from '@mui/material/TableBody';
import TableCell, { tableCellClasses } from '@mui/material/TableCell';
import TableContainer from '@mui/material/TableContainer';
import TableHead from '@mui/material/TableHead';
import TableRow from '@mui/material/TableRow';
import Paper from '@mui/material/Paper';
import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown';
import KeyboardArrowUpIcon from '@mui/icons-material/KeyboardArrowUp';
import { useEffect } from 'react';
import { isEmpty } from 'lodash';

/*
The data should follow the following format:
const table = {
headers: [
{
value: 'Header 1',
},
{
value: 'Header 2',
},
{
value: 'Header 3',
},
],
rows: [
{
id: 'Row 1',
data: [
{
value: 'Row 1, Cell 1',
},
{
value: 'Row 1, Cell 2',
},
{
value: 'Row 1, Cell 3',
}
],
table: {
headers: [
{
value: 'Sub header 1',
},
{
value: 'Sub header 2',
},
],
rows: [
{
id: 'Sub Row 1',
data: [
{
value: 'Sub Row 1, Cell 1',
},
{
value: 'Sub Row 1, Cell 2',
},
],
},
{
id: 'Sub Row 2',
data: [
{
value: 'Sub Row 2, Cell 1',
},
{
value: 'Sub Row 2, Cell 2',
},
],
},
],
}
},
{
id: 'Row 2',
data: [
{
value: 'Row 2, Cell 1',
},
{
value: 'Row 2, Cell 2',
},
{
value: 'Row 2, Cell 3',
}
],
},
],
};
*/

const StyledTableCell = styled(TableCell)(({}) => ({

Check notice on line 100 in src/components/CollapsibleTable.js

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

src/components/CollapsibleTable.js#L100

Unexpected empty object pattern.
[`&.${tableCellClasses.head}`]: {
fontSize: '1.4rem',
fontFamily: 'Montserrat',
fontWeight: 600,
backgroundColor: '#e2e8f4',
textTransform: 'uppercase',
},
[`&.${tableCellClasses.body}`]: {
fontSize: '1.4rem',
fontFamily: 'Montserrat',
fontWeight: 400,
},
}));

const CollapsibleRow = (props) => {
const { row } = props;
const [open, setOpen] = React.useState(false);

return (
<React.Fragment>
<TableRow sx={{ '& > *': { borderBottom: 'unset' } }}>
<StyledTableCell component="th">
<IconButton
aria-label="expand row"
size="small"
onClick={() => setOpen(!open)}
>
{open ? <KeyboardArrowUpIcon /> : <KeyboardArrowDownIcon />}
</IconButton>
</StyledTableCell>
{row.data.map((cell) => (
<StyledTableCell key={cell.id} component="th">
{cell.value}
</StyledTableCell>
))}
</TableRow>
<TableRow>
<StyledTableCell style={{ paddingBottom: 0, paddingTop: 0 }} colSpan={6} component="th">
<Collapse in={open} timeout="auto" unmountOnExit>
<Box sx={{ margin: 2 }}>
<Table>
<TableHead>
<TableRow>
{row.table.headers.map((header) => (
<StyledTableCell key={header.value}>{header.value}</StyledTableCell>
))}
</TableRow>
</TableHead>
<TableBody>
{row.table.rows.map((subRow) => (
<TableRow key={subRow.id}>
{subRow.data.map((cell) => (
<StyledTableCell key={cell.id} component="th">
{cell.value}
</StyledTableCell>
))}
</TableRow>
))}
</TableBody>
</Table>
</Box>
</Collapse>
</StyledTableCell>
</TableRow>
</React.Fragment>
);
};

export const CollapsibleTable = (props) => {
const { data, summary, isLoading } = props;

const [table, setTable] = React.useState([]);

useEffect(() => {
if (isLoading || isEmpty(data)) {
return;
}

const innerTable = (
<TableContainer component={Paper}>
<Table aria-label={summary}>
<TableHead>
<TableRow>
<StyledTableCell />
{data.headers.map((header) => (
<StyledTableCell key={header.value}>{header.value}</StyledTableCell>
))}
</TableRow>
</TableHead>
<TableBody>
{data.rows.map((row) => (
<CollapsibleRow key={row.id} row={row} />
))}
</TableBody>
</Table>
</TableContainer>
);

setTable(innerTable);
}, [isLoading, data, summary]);

return table;
};

export default CollapsibleTable;
43 changes: 43 additions & 0 deletions src/components/TableHeaderSection.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { Styles } from '../libs/theme';
import { div, img } from 'react-hyperscript-helpers';

export const TableHeaderSection = (props) => {
const { icon, title, description } = props;

return div({ style: { display: 'flex', justifyContent: 'space-between', width: '112%', marginLeft: '-6%', padding: '0 2.5%' } }, [
div(
{ className: 'left-header-section', style: Styles.LEFT_HEADER_SECTION },
[
div({ style: Styles.ICON_CONTAINER }, [
img({
id: 'dataset-icon',
src: icon,
style: Styles.HEADER_IMG,
}),
]),
div({ style: Styles.HEADER_CONTAINER }, [
div({
style: {
fontFamily: 'Montserrat',
fontWeight: 600,
fontSize: '2.8rem'
}
}, [
title,
]),
div(
{
style: {
fontFamily: 'Montserrat',
fontSize: '1.4rem',
},
},
[description]
),
]),
]
),
]);
};

export default TableHeaderSection;
127 changes: 126 additions & 1 deletion src/components/data_search/DatasetSearchTable.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,128 @@
export const DatasetSearchTable = () => {};
import { useEffect, useState } from 'react';
import { h, div } from 'react-hyperscript-helpers';
import datasetIcon from '../../images/icon_dataset_.png';
import { Styles } from '../../libs/theme';
import { groupBy } from 'lodash';
import CollapsibleTable from '../CollapsibleTable';
import TableHeaderSection from '../TableHeaderSection';

const studyTableHeader = [
'Study Name',
'Description',
'Datasets',
'Participants',
'Phenotype',
'Species',
'PI Name',
'Data Submitter',
'Data Custodian',
];

const datasetTableHeader = [
'DUOS ID',
'Data Use',
'Data Types',
'Participants',
'Data Location',
'DAC',
];

export const DatasetSearchTable = (props) => {
const { datasets, isLoading } = props;
const [tableData, setTableData] = useState({});

useEffect(() => {
if (isLoading) {
return;
}

const studies = groupBy(datasets, 'study.studyId');
const table = {
headers: studyTableHeader.map((header) => ({ value: header })),
rows: Object.values(studies).map((entry) => {
const sum = entry.reduce((acc, dataset) => {
return acc + dataset.participantCount;
}, 0);
return {
id: entry[0].study.studyId,
data: [
{
value: entry[0].study.studyName,
},
{
value: entry[0].study.description,
},
{
value: entry.length,
},
{
value: isNaN(sum) ? undefined : sum,
},
{
value: entry[0].study.phenotype,
},
{
value: entry[0].study.species,
},
{
value: entry[0].study.piName,
},
{
value: entry[0].study.dataSubmitterId,
},
{
value: entry[0].study.dataCustodian,
},
],
table: {
headers: datasetTableHeader.map((header) => ({ value: header })),
rows: entry.map((dataset) => {
return {
id: dataset.datasetId,
data: [
{
value: dataset.datasetIdentifier,
},
{
value: dataset.dataUse?.primary.map((use) => use.code).join(', ')
},
{
value: dataset.dataTypes,
},
{
value: dataset.participantCount,
},
{
value: dataset.dataLocation,
},
{
value: dataset.dacId,
},
],
};
}),
}
};
}),
};

setTableData(table);
}, [datasets, isLoading]);

return div({ style: Styles.PAGE }, [
h(TableHeaderSection, {
icon: datasetIcon,
title: 'Data Catalog',
description: 'Search, filter, and select datasets, then click \'Apply for Access\' to request access'
}),
div({ style: { paddingTop: '2rem' } }, [
h(CollapsibleTable, {
data: tableData,
isLoading,
summary: 'faceted study search table'
}),
]),
]);
};

export default DatasetSearchTable;

0 comments on commit e2add21

Please sign in to comment.