Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

X1070 history page with multiple search fields #394

Merged
merged 7 commits into from
Sep 8, 2023
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
80 changes: 49 additions & 31 deletions cypress/e2e/pages/history.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
FindHistoryForWorkNumberQueryVariables
} from '../../../src/types/sdk';
import { buildHistory } from '../../../src/mocks/handlers/historyHandlers';
import { shouldDisplaySelectedValue } from '../shared/customReactSelect.cy';
describe('History Page', () => {
context('when I visit the page with no URL params', () => {
before(() => cy.visit('/history'));
Expand All @@ -18,7 +19,9 @@ describe('History Page', () => {
before(() => cy.visit('/history?bad=params&no=search'));

it('does not use the params to fill in the form', () => {
cy.get("input[name='value']").invoke('val').should('eq', '');
cy.get("input[name='barcode']").invoke('val').should('eq', '');
cy.get("input[name='externalName']").invoke('val').should('eq', '');
cy.get("input[name='donorName']").invoke('val').should('eq', '');
});

it('does not perform a history search', () => {
Expand All @@ -28,16 +31,15 @@ describe('History Page', () => {

describe('By Labware Barcode', () => {
context('when I visit the page with good URL params', () => {
before(() => cy.visit('/history?kind=labwareBarcode&value=STAN-1001'));
before(() => cy.visit('/history?barcode=STAN-1001'));

it('uses the params to fill in the form', () => {
cy.get("input[name='value']").invoke('val').should('eq', 'STAN-1001');
cy.get("select[name='kind']").invoke('val').should('eq', 'labwareBarcode');
cy.get("input[name='barcode']").invoke('val').should('eq', 'STAN-1001');
});

it('performs a history search', () => {
cy.findByTestId('history').should('exist');
cy.findByTextContent('History for Labware Barcode STAN-1001');
cy.contains('History for barcode STAN-1001');
});

it('hightlights the searched barcode in the table', () => {
Expand All @@ -64,13 +66,32 @@ describe('History Page', () => {
});
});

describe('By multiple search fields', () => {
before(() => {
cy.visit('/history?barcode=STAN-10001F&donorName=DNR123&workNumber=SGP1008&externalName=EXT123');
});
it('uses the params to fill in the form', () => {
cy.get("input[name='barcode']").invoke('val').should('eq', 'STAN-10001F');
cy.get("input[name='donorName']").invoke('val').should('eq', 'DNR123');
cy.get("input[name='externalName']").invoke('val').should('eq', 'EXT123');
shouldDisplaySelectedValue('workNumber', 'SGP1008');
});
it('performs a history search', () => {
cy.findByTestId('history').should('exist');
cy.contains('History for barcode STAN-10001F, donorName DNR123, externalName EXT123, workNumber SGP1008');
});
it('displays uploaded files section', () => {
cy.findByText('Files Uploaded').should('be.visible');
});
});

describe('By Sample ID', () => {
context('when I visit the page with good URL params', () => {
before(() => cy.visit('/history?kind=sampleId&value=10'));
before(() => cy.visit('/history?sampleId=10'));

it('does performs a history search', () => {
cy.findByTestId('history').should('exist');
cy.findByTextContent('History for Sample ID 10');
cy.contains('History for sampleId 10');
});
it('displays uploaded files section', () => {
cy.findByText('Files Uploaded').should('be.visible');
Expand All @@ -80,16 +101,15 @@ describe('History Page', () => {

describe('By External ID', () => {
context('when I visit the page with good URL params', () => {
before(() => cy.visit('/history?kind=externalName&value=EXT123'));
before(() => cy.visit('/history?externalName=EXT123'));

it('uses the params to fill in the form', () => {
cy.get("input[name='value']").invoke('val').should('eq', 'EXT123');
cy.get("select[name='kind']").invoke('val').should('eq', 'externalName');
cy.get("input[name='externalName']").invoke('val').should('eq', 'EXT123');
});

it('does performs a history search', () => {
cy.findByTestId('history').should('exist');
cy.findByTextContent('History for External ID EXT123');
cy.contains('History for externalName EXT123');
});
it('displays uploaded files section', () => {
cy.findByText('Files Uploaded').should('be.visible');
Expand All @@ -99,16 +119,15 @@ describe('History Page', () => {

describe('By Donor Name', () => {
context('when I visit the page with good URL params', () => {
before(() => cy.visit('/history?kind=donorName&value=DNR123'));
before(() => cy.visit('/history?donorName=DNR123'));

it('uses the params to fill in the form', () => {
cy.get("input[name='value']").invoke('val').should('eq', 'DNR123');
cy.get("select[name='kind']").invoke('val').should('eq', 'donorName');
cy.get("input[name='donorName']").invoke('val').should('eq', 'DNR123');
});

it('does performs a history search', () => {
cy.findByTestId('history').should('exist');
cy.findByTextContent('History for Donor Name DNR123');
cy.contains('History for donorName DNR123');
});
it('displays uploaded files section', () => {
cy.findByText('Files Uploaded').should('be.visible');
Expand All @@ -118,16 +137,15 @@ describe('History Page', () => {

describe('By Work Number', () => {
context('when I visit the page with good URL params', () => {
before(() => cy.visit('/history?kind=workNumber&value=SGP1'));
before(() => cy.visit('/history?workNumber=SGP1008'));

it('uses the params to fill in the form', () => {
cy.get("input[name='value']").invoke('val').should('eq', 'SGP1');
cy.get("select[name='kind']").invoke('val').should('eq', 'workNumber');
shouldDisplaySelectedValue('workNumber', 'SGP1008');
});

it('does performs a history search', () => {
cy.findByTestId('history').should('exist');
cy.findByTextContent('History for Work Number SGP1');
cy.contains('History for workNumber SGP1008');
});
it('displays uploaded files section', () => {
cy.findByText('Files Uploaded').should('be.visible');
Expand All @@ -154,18 +172,18 @@ describe('History Page', () => {
});
context(' when clicking on uploaded files link for authenticated users', () => {
before(() => {
cy.visit('/history?kind=workNumber&value=SGP1008');
cy.visit('/history?workNumber=SGP1008');
cy.contains('Files for SGP1008').click();
});
it('goes to file manager page for SGP123', () => {
it('goes to file manager page for SGP1008', () => {
cy.url().should('be.equal', 'http://localhost:3000/file_manager?workNumber=SGP1008');
cy.findByText('Upload file').should('exist');
cy.findByText('Files').should('exist');
});
});
context('for non-authenticated users', () => {
before(() => {
cy.visitAsGuest('/history?kind=workNumber&value=SGP1008');
cy.visitAsGuest('/history?workNumber=SGP1008');
cy.contains('Files for SGP1008').click();
});
it('goes to file viewer page for SGP123', () => {
Expand All @@ -192,25 +210,25 @@ describe('History Page', () => {
)
);
});
cy.visit('/history?kind=workNumber&value=SGP1001');
cy.visit('/history?workNumber=SGP1008');
});
context(' when clicking on uploaded files link for authenticated users', () => {
before(() => {
cy.contains('Files for SGP1001').click();
cy.contains('Files for SGP1008').click();
});
it('goes to file manager page for SGP1001', () => {
cy.url().should('be.equal', 'http://localhost:3000/file_manager?workNumber=SGP1001');
cy.url().should('be.equal', 'http://localhost:3000/file_manager?workNumber=SGP1008');
cy.findByText('Upload file').should('exist');
cy.findByText('Files').should('exist');
});
});
context('for non-authenticated users', () => {
before(() => {
cy.visitAsGuest('/history?kind=workNumber&value=SGP1001');
cy.contains('Files for SGP1001').click();
cy.visitAsGuest('/history?workNumber=SGP1008');
cy.contains('Files for SGP1008').click();
});
it('goes to file viewer page for SGP1001', () => {
cy.url().should('be.equal', 'http://localhost:3000/file_viewer?workNumber=SGP1001');
it('goes to file viewer page for SGP1008', () => {
cy.url().should('be.equal', 'http://localhost:3000/file_viewer?workNumber=SGP1008');
cy.findByText('Upload file').should('not.exist');
cy.findByText('Files').should('exist');
});
Expand All @@ -220,12 +238,12 @@ describe('History Page', () => {

context('when a search errors', () => {
before(() => {
cy.visit('/history?kind=labwareBarcode&value=STAN-10001F');
cy.visit('/history?barcode=STAN-10001F');

cy.msw().then(({ worker, graphql }) => {
worker.use(
graphql.query<FindHistoryForLabwareBarcodeQuery, FindHistoryForLabwareBarcodeQueryVariables>(
'FindHistoryForLabwareBarcode',
'FindHistory',
(req, res, ctx) => {
return res.once(
ctx.errors([
Expand Down
50 changes: 25 additions & 25 deletions src/components/history/History.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import createHistoryMachine from './history.machine';
import { useMachine } from '@xstate/react';
import DataTable from '../DataTable';
import { Cell, Column } from 'react-table';
import { HistoryProps, HistoryTableEntry } from '../../types/stan';
import { HistoryTableEntry } from '../../types/stan';
import StyledLink from '../StyledLink';
import Warning from '../notifications/Warning';
import WhiteButton from '../buttons/WhiteButton';
Expand All @@ -16,11 +16,11 @@ import Heading from '../Heading';
import Table, { TableBody, TableCell } from '../Table';
import { useAuth } from '../../context/AuthContext';
import TopScrollingBar from '../TopScrollingBar';

import { HistoryUrlParams } from '../../pages/History';
/**
* Component for looking up and displaying the history of labware and samples
*/
export default function History(props: HistoryProps) {
export default function History(props: HistoryUrlParams) {
const historyMachine = React.useMemo(() => {
return createHistoryMachine(props);
}, [props]);
Expand Down Expand Up @@ -54,7 +54,7 @@ export default function History(props: HistoryProps) {
Cell: (props: Cell<HistoryTableEntry>) => {
const barcode = props.row.original.sourceBarcode;
let classes =
historyProps.kind === 'labwareBarcode' && barcode === historyProps.value
historyProps.barcode === barcode
? 'bg-yellow-400 text-sp-600 hover:text-sp-700 font-semibold hover:underline text-base tracking-wide'
: '';
return (
Expand All @@ -70,7 +70,7 @@ export default function History(props: HistoryProps) {
Cell: (props: Cell<HistoryTableEntry>) => {
const barcode = props.row.original.destinationBarcode;
let classes =
historyProps.kind === 'labwareBarcode' && barcode === historyProps.value
historyProps.barcode === barcode
? 'bg-yellow-400 text-sp-600 hover:text-sp-700 font-semibold hover:underline text-base tracking-wide'
: '';
return (
Expand Down Expand Up @@ -166,12 +166,14 @@ export default function History(props: HistoryProps) {
return workNumbers;
}, [history]);

const isValidInput = props.sampleId || props.workNumber || props.barcode || props.externalName || props.donorName;

/**
* If the props change, send an update event to the machine
*/
useEffect(() => {
send({ type: 'UPDATE_HISTORY_PROPS', props });
}, [props, send]);
}, [props, send, isValidInput]);

/**
* File upload option is only for authenticated users, so
Expand All @@ -183,8 +185,16 @@ export default function History(props: HistoryProps) {
});
return isAuthenticated() ? `/file_manager?${queryParamsStr}` : `/file_viewer?${queryParamsStr}`;
};

const searchString = (keyValSeparator: string, tokenSeparator: string) => {
return Object.keys(historyProps)
.sort()
.map((key) => `${key}${keyValSeparator}${historyProps[key as keyof HistoryUrlParams]}`)
.join(tokenSeparator);
};

return (
<div data-testid="history">
<div>
{current.matches('error') && serverError && (
<Warning message={'History Search Error'} error={serverError}>
<WhiteButton onClick={() => send({ type: 'RETRY' })}>Retry</WhiteButton>
Expand All @@ -196,7 +206,6 @@ export default function History(props: HistoryProps) {
<LoadingSpinner />
</div>
)}

{current.matches('found') &&
(history.length > 0 ? (
<>
Expand All @@ -209,7 +218,7 @@ export default function History(props: HistoryProps) {
<Heading level={4} showBorder={false}>
Files Uploaded
</Heading>
<div className={'flex flex-col mt-4 justify-center'}>
<div className={'flex flex-col mt-4 justify-center'} data-testid="history">
<Table>
<TableBody>
<TableCell className={'flex flex-col justify-center p-2'}>
Expand All @@ -226,31 +235,22 @@ export default function History(props: HistoryProps) {
</div>
)}
<div className="mt-6 mb-2 flex flex-row items-center justify-end space-x-3">
<p className="text-sm text-gray-700">
History for {historyDisplayValues[props.kind]} <span className="font-medium">{props.value}</span>
</p>
<a
href={downloadURL}
download={`${getTimestampStr()}_${historyProps.kind}_${historyProps.value}${extension}`}
>
History for
<>&nbsp;</>
<span className="text-gray-700">{`${searchString(' ', ', ')}`}</span>
<a href={downloadURL} download={`${getTimestampStr()}_${searchString('=', '&')}${extension}`}>
<DownloadIcon name="Download" className="h-4 w-4 text-sdb" />
</a>
</div>
<TopScrollingBar>
<DataTable data-testid={'history-table'} columns={historyColumns} data={history} fixedHeader={true} />
</TopScrollingBar>
</>
) : (
) : isValidInput ? (
<Warning message={'No results found.'} />
) : (
<></>
))}
</div>
);
}

export const historyDisplayValues: { [key in HistoryProps['kind']]: string } = {
labwareBarcode: 'Labware Barcode',
sampleId: 'Sample ID',
externalName: 'External ID',
donorName: 'Donor Name',
workNumber: 'Work Number'
};
40 changes: 40 additions & 0 deletions src/components/history/HistoryInput.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { useFormikContext } from 'formik';
import React from 'react';
import WorkNumberSelect from '../WorkNumberSelect';
import { HistoryUrlParams } from '../../pages/History';
import FormikInput from '../forms/Input';

export default function HistoryInput() {
const { values, setFieldValue } = useFormikContext<HistoryUrlParams>();

return (
<div
className="mx-auto max-w-screen-lg mb-6 border border-gray-200 bg-gray-100 p-6 rounded-md space-y-4 shadow-lg hover:shadow-2xl"
data-testid={'history-input'}
>
<div className={'grid grid-cols-2 gap-x-10 gap-y-6 border border-gray-200 bg-gray-100 p-6 rounded-md'}>
<div className={'flex flex-col '}>
<WorkNumberSelect
label={'SGP/R&D Number'}
emptyOption={true}
name={'workNumber'}
onWorkNumberChange={(workNumber) => setFieldValue('workNumber', workNumber)}
workNumber={values.workNumber}
multiple={false}
dataTestId={'workNumber'}
workNumberType={'ALL'}
/>
</div>
<div className={'flex flex-col '}>
<FormikInput name="barcode" label="Barcode" />
</div>
<div className={'flex flex-col '}>
<FormikInput name="externalName" label="External Name" />
</div>
<div className={'flex flex-col '}>
<FormikInput name="donorName" label="Donor Name" />
</div>
</div>
</div>
);
}
Loading
Loading