Skip to content

Commit

Permalink
[DCJ-48] Make the DAR dataset selection remove only after draft DAR c…
Browse files Browse the repository at this point in the history
…reation (#2622)

Co-authored-by: rushtong <[email protected]>
  • Loading branch information
aarohinadkarni and rushtong authored Jul 29, 2024
1 parent 4c00bf6 commit e3c6640
Show file tree
Hide file tree
Showing 7 changed files with 306 additions and 48 deletions.
126 changes: 126 additions & 0 deletions cypress/component/DataAccessRequest/selectable_datasets.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
/* eslint-disable no-undef */
import {React} from 'react';
import {mount} from 'cypress/react';
import DataAccessRequestApplication from '../../../src/pages/dar_application/DataAccessRequestApplication';
import SelectableDatasets from '../../../src/pages/dar_application/SelectableDatasets.jsx';

const datasets = [
{
dataSetId: 123456,
datasetIdentifier: `DUOS-123456`,
datasetName: 'Some Dataset 1'
},
{
dataSetId: 234567,
datasetIdentifier: `DUOS-234567`,
datasetName: 'Some Dataset 2'
},
{
dataSetId: 345678,
datasetIdentifier: `DUOS-345678`,
datasetName: 'Some Dataset 3'
},
{
dataSetId: 456789,
datasetIdentifier: `DUOS-456789`,
datasetName: 'Some Dataset 4'
},
];

const props = {
datasets: datasets,
setSelectedDatasets: () => {},
disabled: false
};

const propsDisabled = {
datasets: datasets,
setSelectedDatasets: () => {},
disabled: true
};


describe('Selectable Datasets - Not Read Only', () => {

describe('With 4 Datasets', () => {
beforeEach(() => {
mount(<SelectableDatasets {...props} />);
});

it('Marks 2 datasets for removal', () => {
cy.get('#DUOS-123456_summary').click();
cy.get('#DUOS-345678_summary').click();
cy.get('#restore_dataset_123456').should('exist');
cy.get('#restore_dataset_345678').should('exist');
});

it('Unmark 1 of the previously marked for removal datasets', () => {
cy.get('#DUOS-123456_summary').click();
cy.get('#DUOS-345678_summary').click();
cy.get('#restore_dataset_123456').should('exist');
cy.get('#restore_dataset_345678').should('exist');
cy.get('#restore_dataset_345678').click();
cy.get('#remove_dataset_345678').should('exist');
});

it('Marks 2 more datasets for removal, leaving 1 dataset left not removed', () => {
cy.get('#DUOS-123456_summary').click();
cy.get('#DUOS-345678_summary').click();
cy.get('#restore_dataset_123456').should('exist');
cy.get('#restore_dataset_345678').should('exist');
cy.get('#restore_dataset_345678').click();
cy.get('#remove_dataset_345678').should('exist');
cy.get('#remove_dataset_345678').click();
cy.get('#DUOS-234567_summary').click();
cy.get('#restore_dataset_123456').should('exist');
cy.get('#restore_dataset_345678').should('exist');
cy.get('#restore_dataset_234567').should('exist');
cy.get('#remove_dataset_456789').should('exist');
});

it('Cannot delete last dataset', () => {
cy.get('#DUOS-123456_summary').click();
cy.get('#DUOS-345678_summary').click();
cy.get('#restore_dataset_123456').should('exist');
cy.get('#restore_dataset_345678').should('exist');
cy.get('#restore_dataset_345678').click();
cy.get('#remove_dataset_345678').should('exist');
cy.get('#remove_dataset_345678').click();
cy.get('#DUOS-234567_summary').click();
cy.get('#restore_dataset_123456').should('exist');
cy.get('#restore_dataset_345678').should('exist');
cy.get('#restore_dataset_234567').should('exist');
cy.get('#remove_dataset_456789').should('exist');
cy.get('#DUOS-456789_summary [data-testid="DeleteIcon"]').should('have.css', 'opacity', '0.5');
});
});

describe('Selectable Datasets - Read Only', () => {
beforeEach(() => {
mount(<SelectableDatasets {...propsDisabled} />);
});

it('Can not click on any dataset', () => {
cy.get('#DUOS-123456_summary').should('have.css', 'cursor', 'auto');
cy.get('#DUOS-234567_summary').should('have.css', 'cursor', 'auto');
cy.get('#DUOS-345678_summary').should('have.css', 'cursor', 'auto');
cy.get('#DUOS-456789_summary').should('have.css', 'cursor', 'auto');

cy.get('#DUOS-123456_summary').click();
cy.get('#DUOS-234567_summary').click();
cy.get('#DUOS-345678_summary').click();
cy.get('#DUOS-456789_summary').click();

cy.get('#restore_dataset_123456').should('not.exist');
cy.get('#restore_dataset_234567').should('not.exist');
cy.get('#restore_dataset_345678').should('not.exist');
cy.get('#restore_dataset_456789').should('not.exist');

cy.get('#DUOS-123456_summary').find('[data-testid="DeleteIcon"]').should('not.exist');
cy.get('#DUOS-234567_summary').find('[data-testid="DeleteIcon"]').should('not.exist');
cy.get('#DUOS-345678_summary').find('[data-testid="DeleteIcon"]').should('not.exist');
cy.get('#DUOS-456789_summary').find('[data-testid="DeleteIcon"]').should('not.exist');
});
});
});

3 changes: 2 additions & 1 deletion src/Routes.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,8 @@ const Routes = (props) => (
<AuthenticatedRoute path="/dar_application_review/:collectionId" component={DataAccessRequestApplication} props={Object.assign({}, props, {readOnlyMode: true})}
rolesAllowed={[USER_ROLES.researcher]} />
{/* Order is important for processing links with embedded dataRequestIds */}
<AuthenticatedRoute path="/dar_application/:dataRequestId" component={DataAccessRequestApplication} props={props} rolesAllowed={[USER_ROLES.researcher]} />
<AuthenticatedRoute path="/dar_application/:dataRequestId" component={DataAccessRequestApplication} props={Object.assign({}, props, {draftDar: true})}
rolesAllowed={[USER_ROLES.researcher]} />
<AuthenticatedRoute path="/dar_application" component={DataAccessRequestApplication} props={props} rolesAllowed={[USER_ROLES.researcher]} />
<AuthenticatedRoute path="/signing_official_console/researchers" component={ensureSoHasDaaAcknowledgement(SigningOfficialResearchers, true)} props={props} rolesAllowed={[USER_ROLES.admin, USER_ROLES.signingOfficial]} />
{DAAUtils.isEnabled() && <AuthenticatedRoute path="/signing_official_console/researchers_daa_associations" component={ensureSoHasDaaAcknowledgement(ManageResearcherDAAs, true)} props={props} rolesAllowed={[USER_ROLES.admin, USER_ROLES.signingOfficial]} />}
Expand Down
20 changes: 13 additions & 7 deletions src/libs/ajax/DAR.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { Config } from '../config';
import axios from 'axios';
import { isFileEmpty } from '../utils';
import { getApiUrl, fetchOk, getOntologyUrl, fetchAny } from '../ajax';

import {DAAUtils} from '../../utils/DAAUtils';

export const DAR = {
//v2 get for DARs
Expand All @@ -15,16 +15,20 @@ export const DAR = {
return await res.json();
},

//v2 update for dar partials
//v2, v3 Draft DAR Update
updateDarDraft: async (dar, referenceId) => {
const url = `${await getApiUrl()}/api/dar/v2/draft/${referenceId}`;
const url = DAAUtils.isEnabled() ?
`${await getApiUrl()}/api/dar/v3/draft/${referenceId}` :
`${await getApiUrl()}/api/dar/v2/draft/${referenceId}`;
const res = await axios.put(url, dar, Config.authOpts());
return res.data;
},

//api endpoint for v2 draft submission
//v2, v3 Draft DAR Creation
postDarDraft: async (dar) => {
const url = `${await getApiUrl()}/api/dar/v2/draft/`;
const url = DAAUtils.isEnabled() ?
`${await getApiUrl()}/api/dar/v3/draft` :
`${await getApiUrl()}/api/dar/v2/draft`;
const res = await axios.post(url, dar, Config.authOpts());
return res.data;
},
Expand All @@ -36,10 +40,12 @@ export const DAR = {
return await res;
},

//v2 endpoint for DAR POST
//v2, v3 DAR Creation
postDar: async (dar) => {
const filteredDar = fp.omit(['createDate', 'sortDate', 'data_access_request_id'])(dar);
const url = `${await getApiUrl()}/api/dar/v2`;
const url = DAAUtils.isEnabled() ?
`${await getApiUrl()}/api/dar/v3` :
`${await getApiUrl()}/api/dar/v2`;
const res = axios.post(url, filteredDar, Config.authOpts());
return await res.data;
},
Expand Down
79 changes: 47 additions & 32 deletions src/pages/dar_application/DataAccessRequest.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from 'react';
import React, { useState } from 'react';
import { DataSet } from '../../libs/ajax/DataSet';
import { DAR } from '../../libs/ajax/DAR';
import {FormField, FormFieldTitle, FormFieldTypes, FormValidators} from '../../components/forms/forms';
Expand All @@ -10,6 +10,8 @@ import {
needsGsoAcknowledgement,
newIrbDocumentExpirationDate,
} from '../../utils/darFormUtils';
import SelectableDatasets from './SelectableDatasets';
import {DAAUtils} from '../../utils/DAAUtils';

const formatOntologyForSelect = (ontology) => {
return {
Expand Down Expand Up @@ -88,20 +90,22 @@ export default function DataAccessRequest(props) {
uploadedCollaborationLetter,
updateCollaborationLetter,
setDatasets,
setSelectedDatasets,
validation,
readOnlyMode,
includeInstructions,
formValidationChange,
ariaLevel = 2
ariaLevel = 2,
draftDar
} = props;

const irbProtocolExpiration = formData.irbProtocolExpiration || newIrbDocumentExpirationDate();

// i need to figure out a way to only actually remove them without using onChange
const onChange = ({key, value}) => {
formFieldChange({key, value});
};


const onValidationChange = ({key, validation}) => {
formValidationChange({key, validation});
};
Expand Down Expand Up @@ -137,35 +141,46 @@ export default function DataAccessRequest(props) {
// eslint-disable-next-line react/no-unknown-property
<div datacy={'data-access-request'}>
<div className={'dar-step-card'}>
<FormField
id={'datasetIds'}
key={'datasetIds'}
type={FormFieldTypes.SELECT}
disabled={readOnlyMode}
isAsync={true}
isMulti={true}
title={'2.1 Select Dataset(s)'}
validators={[FormValidators.REQUIRED]}
validation={validation.datasetIds}
onValidationChange={onValidationChange}
description={includeInstructions ? 'Please start typing the Dataset Name, Sample Collection ID, or PI of the dataset(s) for which you would like to request access:' : ''}
defaultValue={datasets?.map((ds) => formatSearchDataset(ds))}
selectConfig={{
// return custom html for displaying dataset options
formatOptionLabel: (opt) => opt.label,
// return string value of dataset for accessibility / html keys
getOptionLabel: (opt) => opt.displayText,
}}
loadOptions={(query, callback) => searchDatasets(query, callback, datasets)}
placeholder={'Dataset Name, Sample Collection ID, or PI'}
onChange={async ({key, value}) => {
const datasets = value.map((val) => val.dataset);
const datasetIds = datasets?.map((ds) => ds.dataSetId);
const fullDatasets = await DataSet.getDatasetsByIds(datasetIds);
onChange({key, value: datasetIds});
setDatasets(fullDatasets);
}}
/>
{DAAUtils.isEnabled() ?
<div>
<label style={{ fontWeight: 'bold', display: 'block', marginBottom: '0.5rem' }} className="control-label">2.1 Select Dataset(s)</label>
<p style={{ marginBottom: '1rem' }}>Currently selected datasets:</p>
<SelectableDatasets
disabled={readOnlyMode}
datasets={datasets}
setSelectedDatasets={setSelectedDatasets}
/>
</div> :
<FormField
id={'datasetIds'}
key={'datasetIds'}
type={FormFieldTypes.SELECT}
disabled={readOnlyMode}
isAsync={true}
isMulti={true}
title={'2.1 Select Dataset(s)'}
validators={[FormValidators.REQUIRED]}
validation={validation.datasetIds}
onValidationChange={onValidationChange}
description={includeInstructions ? 'Please start typing the Dataset Name, Sample Collection ID, or PI of the dataset(s) for which you would like to request access:' : ''}
defaultValue={datasets?.map((ds) => formatSearchDataset(ds))}
selectConfig={{
// return custom html for displaying dataset options
formatOptionLabel: (opt) => opt.label,
// return string value of dataset for accessibility / html keys
getOptionLabel: (opt) => opt.displayText,
}}
loadOptions={(query, callback) => searchDatasets(query, callback, datasets)}
placeholder={'Dataset Name, Sample Collection ID, or PI'}
onChange={async ({key, value}) => {
const datasets = value.map((val) => val.dataset);
const datasetIds = datasets?.map((ds) => ds.dataSetId);
const fullDatasets = await DataSet.getDatasetsByIds(datasetIds);
onChange({key, value: datasetIds});
setDatasets(fullDatasets);
}}
/>
}

<FormField
id={'projectTitle'}
Expand Down
20 changes: 16 additions & 4 deletions src/pages/dar_application/DataAccessRequestApplication.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,7 @@ const DataAccessRequestApplication = (props) => {
};

const [datasets, setDatasets] = useState([]);
const [selectedDatasets, setSelectedDatasets] = useState([]);
const [dataUseTranslations, setDataUseTranslations] = useState([]);

useEffect(() => {
Expand Down Expand Up @@ -350,7 +351,7 @@ const DataAccessRequestApplication = (props) => {
const attemptSubmit = () => {
const validation = validateDARFormData({
formData,
datasets,
datasets: (props.draftDar && DAAUtils.isEnabled()) ? selectedDatasets : datasets,
dataUseTranslations,
irbDocument: uploadedIrbDocument,
collaborationLetter: uploadedCollaborationLetter,
Expand Down Expand Up @@ -444,13 +445,16 @@ const DataAccessRequestApplication = (props) => {
const saveDarDraft = async () => {
let formattedFormData = cloneDeep(formData);
// DAR datasetIds needs to be a list of ids
if (DAAUtils.isEnabled()) {
formattedFormData.datasetIds = selectedDatasets.map(d => d.dataSetId);
}

// Make sure we navigate back to the current DAR after saving.
const { dataRequestId } = props.match.params;
try {
let referenceId = formattedFormData.referenceId;

let darPartialResponse = await updateDraftResponse(formattedFormData, referenceId);
setDatasets(await DataSet.getDatasetsByIds(formData.datasetIds));
referenceId = darPartialResponse.referenceId;
if (isNil(dataRequestId)) {
props.history.replace('/dar_application/' + referenceId);
Expand Down Expand Up @@ -592,6 +596,8 @@ const DataAccessRequestApplication = (props) => {
uploadedIrbDocument={uploadedIrbDocument}
updateUploadedIrbDocument={updateIrbDocument}
setDatasets={setDatasets}
setSelectedDatasets={setSelectedDatasets}
draftDar={props.draftDar}
/>
</div>

Expand All @@ -610,7 +616,7 @@ const DataAccessRequestApplication = (props) => {
<div className='step-container'>
{DAAUtils.isEnabled() ?
<DataAccessAgreements
datasets={datasets}
datasets={selectedDatasets}
darCode={formData.darCode}
cancelAttest={() => setIsAttested(false)}
isAttested={isAttested}
Expand All @@ -629,7 +635,13 @@ const DataAccessRequestApplication = (props) => {

{isAttested &&
<div className='step-container'>
<DucAddendum doSubmit={doSubmit} save={() => setShowDialogSave(true)} isLoading={isLoading} formData={formData} datasets={datasets} dataUseTranslations={dataUseTranslations} />
<DucAddendum
doSubmit={doSubmit}
save={() => setShowDialogSave(true)}
isLoading={isLoading}
formData={formData}
datasets={DAAUtils.isEnabled() ? selectedDatasets : datasets}
dataUseTranslations={dataUseTranslations} />
</div>
}
</div>
Expand Down
8 changes: 4 additions & 4 deletions src/pages/dar_application/RequiredDAAs.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,20 @@ import React from 'react';
export default function RequiredDAAs(props) {
const { datasets, daas, daaDownload } = props;
const fileNames = new Set();
const daaDivs = datasets.map((dataset) => {
const daaDivs = datasets.map((dataset, index) => {
const datasetDacId = dataset.dacId;
if (!datasetDacId) {
return <div key={dataset.id}></div>;
return <div key={dataset.dataSetId + '-' + index}></div>;
}
const daa = daas.find((daa) => daa.dacs?.some((d) => d.dacId === datasetDacId));
const id = daa.daaId;
const fileName = daa.file.fileName.split('.')[0];
if (fileNames.has(fileName)) {
return <div key={id-dataset.name}></div>;
return <div key={'file-name-' + dataset.dataSetId}></div>;
}
fileNames.add(fileName);
return (
<div key={id}>
<div key={'download-daa-' + index}>
{daaDownload(id, fileName)}
</div>
);
Expand Down
Loading

0 comments on commit e3c6640

Please sign in to comment.