Skip to content

Commit

Permalink
Feat/two attrition tables (#1350)
Browse files Browse the repository at this point in the history
* feat(no-access): add no access message (#1301)

* feat(twoAttritionTables): Created separate component to modularize attrition table

* feat(twoAttritionTables): Added prop types to Attrition table

* feat(twoAttritionTables): Renamed test for AttritionTableWrapper

* feat(twoAttritionTables): Refactored test for AttritionTableWrapper

* feat(twoAttritionTables): Resolved linting issues

* feat(twoAttritionTables): Reverted file to master

* feat(twoAttritionTables): Reverted file to sprint branch version

* feat(twoAttritionTables): Refactored test to validate correct output of data

* feat(twoAttritionTables): simplified error messaging logic

* feat(twoAttritionTables): Ran linter

* feat(twoAttritionTables): Added support for capitalized type columns and Outcome Phenotype label

* feat(twoAttritionTables): Ran linter

---------

Co-authored-by: Thanh Dang Nguyen <thanhnd@uchicago.edu>
  • Loading branch information
2 people authored and tianj7 committed Aug 10, 2023
1 parent 26b8bb3 commit e9e1139
Show file tree
Hide file tree
Showing 6 changed files with 160 additions and 61 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,57 @@ const AttritionTableJSON = [
},
],
},
{
table_type: 'control',
rows: [
{
type: 'cohort',
name: 'Catch All (do not run generate)',
size: 510647,
concept_breakdown: [
{
concept_value_name: 'non-Hispanic Black',
persons_in_cohort_with_value: 102054,
},
{
concept_value_name: 'non-Hispanic Asian',
persons_in_cohort_with_value: 102441,
},
{
concept_value_name: 'non-Hispanic White',
persons_in_cohort_with_value: 204213,
},
{
concept_value_name: 'Hispanic',
persons_in_cohort_with_value: 101939,
},
],
},
{
type: 'outcome',
name: 'phenotypeName',
size: 444,
concept_breakdown: [
{
concept_value_name: 'non-Hispanic Black',
persons_in_cohort_with_value: 95,
},
{
concept_value_name: 'non-Hispanic Asian',
persons_in_cohort_with_value: 91,
},
{
concept_value_name: 'non-Hispanic White',
persons_in_cohort_with_value: 169,
},
{
concept_value_name: 'Hispanic',
persons_in_cohort_with_value: 89,
},
],
},
],
},
];

export default AttritionTableJSON;
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,10 @@
height: 40px;
}

.attrition-table .row-type {
text-transform: capitalize;
}

.attrition-table table .attrition-table--leftpad {
padding-left: 26px;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@ import { render, waitFor, screen } from '@testing-library/react';
import '@testing-library/jest-dom';
import { useQuery } from 'react-query';
import SharedContext from '../../../Utils/SharedContext';
import AttritionTable from './AttrtitionTable';
import AttritionTableWrapper from './AttrtitionTableWrapper';
import PHASES from '../../../Utils/PhasesEnumeration';
import AttritionTableJSON from '../../../TestData/InputViewData/AttritionTableJSON';

jest.mock('react-query');

describe('Attrition Table', () => {
describe('Attrition Table Wrapper', () => {
const selectedRowData = {
name: 'workflow_name',
uid: 'workflow_id',
Expand All @@ -24,7 +24,7 @@ describe('Attrition Table', () => {

render(
<SharedContext.Provider value={{ selectedRowData }}>
<AttritionTable />
<AttritionTableWrapper />
</SharedContext.Provider>,
);
expect(screen.getByTestId('loading-error-message')).toBeInTheDocument();
Expand All @@ -38,7 +38,7 @@ describe('Attrition Table', () => {

render(
<SharedContext.Provider value={{ selectedRowData }}>
<AttritionTable />
<AttritionTableWrapper />
</SharedContext.Provider>,
);
expect(screen.getByTestId('spinner')).toBeInTheDocument();
Expand All @@ -52,34 +52,50 @@ describe('Attrition Table', () => {

render(
<SharedContext.Provider value={{ selectedRowData }}>
<AttritionTable />
<AttritionTableWrapper />
</SharedContext.Provider>,
);

await waitFor(() => expect(screen.getByTestId('loading-error-message')).toBeInTheDocument(),
);
});

it('renders the logs when data is fetched successfully', async () => {
it('renders the headers and data when data is fetched successfully', async () => {
useQuery.mockReturnValueOnce({
status: 'success',
data: AttritionTableJSON,
});
render(
<SharedContext.Provider value={{ selectedRowData }}>
<AttritionTable />
<AttritionTableWrapper />
</SharedContext.Provider>,
);

const checkForAtLeastOneInstanceOfText = (input) => {
const textArr = screen.getAllByText(input);
expect(textArr[0]).toBeInTheDocument();
};

await waitFor(() => {
expect(screen.getByText('Attrition Table')).toBeInTheDocument();
expect(screen.getByText('Type')).toBeInTheDocument();
expect(screen.getByText('Name')).toBeInTheDocument();
expect(screen.getByText('Size')).toBeInTheDocument();
expect(screen.getByText('Non-Hispanic Black')).toBeInTheDocument();
expect(screen.getByText('Non-Hispanic Asian')).toBeInTheDocument();
expect(screen.getByText('Non-Hispanic White')).toBeInTheDocument();
expect(screen.getByText('Hispanic')).toBeInTheDocument();
expect(screen.getByText('Case Cohort Attrition Table')).toBeInTheDocument();
expect(screen.getByText('Control Cohort Attrition Table')).toBeInTheDocument();
checkForAtLeastOneInstanceOfText('Type');
checkForAtLeastOneInstanceOfText('Name');
checkForAtLeastOneInstanceOfText('Size');
checkForAtLeastOneInstanceOfText('Non-Hispanic Black');
checkForAtLeastOneInstanceOfText('Non-Hispanic Asian');
checkForAtLeastOneInstanceOfText('Non-Hispanic White');
checkForAtLeastOneInstanceOfText('Hispanic');

AttritionTableJSON.forEach((tableObj) => {
tableObj.rows.forEach((rowObj) => {
checkForAtLeastOneInstanceOfText(rowObj.name);
checkForAtLeastOneInstanceOfText(rowObj.size);
rowObj.concept_breakdown.forEach((conceptObj) => {
checkForAtLeastOneInstanceOfText(conceptObj.persons_in_cohort_with_value);
});
});
});
});
});
});
Original file line number Diff line number Diff line change
@@ -1,62 +1,32 @@
import React, { useContext } from 'react';
import { useQuery } from 'react-query';
import { Spin, Collapse } from 'antd';
import {
getDataForWorkflowArtifact,
queryConfig,
} from '../../../Utils/gwasWorkflowApi';
import SharedContext from '../../../Utils/SharedContext';
import LoadingErrorMessage from '../../../Components/LoadingErrorMessage/LoadingErrorMessage';
import React from 'react';
import { Collapse } from 'antd';
import PropTypes from 'prop-types';
import './AttritionTable.css';

const { Panel } = Collapse;
const AttritionTable = () => {
const { selectedRowData } = useContext(SharedContext);
const { name, uid } = selectedRowData;
const { data, status } = useQuery(
[`getDataForWorkflowArtifact${name}`, name, uid, 'attrition_json_index'],
() => getDataForWorkflowArtifact(name, uid, 'attrition_json_index'),
queryConfig,
);

if (status === 'error') {
return (
<LoadingErrorMessage
data-testid='loading-error-message'
message='Error getting attrition table data'
/>
);
}

if (status === 'loading') {
return (
<div className='spinner-container' data-testid='spinner'>
<Spin />
</div>
);
}

if (!data || data.length === 0 || data.error) {
return (
<LoadingErrorMessage message='Issue Loading Data for Attrition Table' />
);
}

const AttritionTable = ({ tableData, title }) => {
const getBreakDownForGroup = (groupName, conceptBreakdownArray) => {
const matchingObject = conceptBreakdownArray.find(
(obj) => obj.concept_value_name === groupName,
);
return matchingObject?.persons_in_cohort_with_value || <h3></h3>;
};

const displayRowType = (rowType) => {
if (rowType) {
return rowType === 'outcome' ? 'Outcome Phenotype' : rowType;
}
return <h3></h3>;
};

return (
<section data-testid='attrition-table' className='attrition-table'>
<div className='attrition-table'>
<Collapse
defaultActiveKey={['1']}
onClick={(event) => event.stopPropagation()}
>
<Panel key='1' header='Attrition Table'>
<Panel key='1' header={title}>
<table>
<thead>
<tr>
Expand All @@ -76,9 +46,9 @@ const AttritionTable = () => {
</tr>
</thead>
<tbody>
{data[0].rows.map((row, index) => (
{tableData.rows.map((row, index) => (
<tr key={index}>
<td>{row?.type || <h3></h3>}</td>
<td className='row-type'>{displayRowType(row?.type)}</td>
<td>{row?.name || <h3></h3>}</td>
<td className='attrition-table--rightborder'>
{row?.size || <h3></h3>}
Expand Down Expand Up @@ -114,4 +84,9 @@ const AttritionTable = () => {
</section>
);
};
AttritionTable.propTypes = {
tableData: PropTypes.object.isRequired,
title: PropTypes.string.isRequired,
};

export default AttritionTable;
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import React, { useContext } from 'react';
import { useQuery } from 'react-query';
import { Spin } from 'antd';
import {
getDataForWorkflowArtifact,
queryConfig,
} from '../../../Utils/gwasWorkflowApi';
import SharedContext from '../../../Utils/SharedContext';
import LoadingErrorMessage from '../../../Components/LoadingErrorMessage/LoadingErrorMessage';
import './AttritionTable.css';
import AttritionTable from './AttrtitionTable';

const AttritionTableWrapper = () => {
const { selectedRowData } = useContext(SharedContext);
const { name, uid } = selectedRowData;
const { data, status } = useQuery(
[`getDataForWorkflowArtifact${name}`, name, uid, 'attrition_json_index'],
() => getDataForWorkflowArtifact(name, uid, 'attrition_json_index'),
queryConfig,
);

if (status === 'error') {
return (
<LoadingErrorMessage
data-testid='loading-error-message'
message='Error getting attrition table data'
/>
);
}

if (status === 'loading') {
return (
<div className='spinner-container' data-testid='spinner'>
<Spin />
</div>
);
}

if (!data || data.length === 0 || data[0].table_type !== 'case' || data.error) {
return (
<LoadingErrorMessage message='Error Getting Attrition Table Data' />
);
}

return (
<section data-testid='attrition-table-wrapper' className='attrition-table-wrapper'>
<AttritionTable tableData={data[0]} title='Case Cohort Attrition Table' />
{data[1]?.table_type === 'control'
&& <AttritionTable tableData={data[1]} title='Control Cohort Attrition Table' />}
</section>
);
};
export default AttritionTableWrapper;
4 changes: 2 additions & 2 deletions src/Analysis/GWASResults/Views/Input/Input.jsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from 'react';
import DetailPageHeader from '../../Components/DetailPageHeader/DetailPageHeader';
import JobDetails from './JobDetails/JobDetails';
import AttritionTable from './AttritionTable/AttrtitionTable';
import AttritionTableWrapper from './AttritionTable/AttrtitionTableWrapper';
import './Input.css';

const Input = () => {
Expand All @@ -18,7 +18,7 @@ const Input = () => {
return (
<div className='results-view'>
{displayTopSection()}
<AttritionTable />
<AttritionTableWrapper />
<JobDetails />
</div>
);
Expand Down

0 comments on commit e9e1139

Please sign in to comment.