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

SCRUM-3794 #1459

Merged
merged 11 commits into from
Mar 14, 2024
41 changes: 41 additions & 0 deletions src/main/cliapp/src/components/Editors/NotEditor.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import React, { useState } from 'react';
import { Dropdown } from "primereact/dropdown";

export function NotEditor({ props, value, editorChange }) {
const [selectedValue, setSelectedValue] = useState(value);
const textString = selectedValue ? "NOT" : "";
const options = [
{ label: "NOT", value: true },
];

const onChange = (e) => {
let event;
if(e.value === undefined){
event = {
target: {
value: false,
name: e.target.name
}
}
} else {
event = e;
}
setSelectedValue(event.target.value);
editorChange(event, props);
}

return (
<>
<Dropdown
aria-label='dropdown'
name="negated"
value={selectedValue}
options={options}
onChange={(e) => onChange(e)}
showClear={true}
placeholder={textString}
style={{ width: '100%' }}
/>
</>
);
}
64 changes: 64 additions & 0 deletions src/main/cliapp/src/components/Editors/__tests__/NotEditor.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { render, fireEvent, within } from '@testing-library/react';
import { NotEditor } from '../NotEditor';
import '../../../tools/jest/setupTests';

describe('NotEditor', () => {
it('should display "NOT" as the placeholder text when the initial value is true', () => {
const props = {};
const value = true;
const editorChange = jest.fn();

const result = render(<NotEditor props={props} value={value} editorChange={editorChange} />);

expect(result.getAllByText("NOT")).toHaveLength(2);
});

it('should render a Dropdown component with no options when value prop is undefined', () => {
const props = {};
const value = undefined;
const editorChange = jest.fn();

const result = render(<NotEditor props={props} value={value} editorChange={editorChange} />);


expect(result.getByText("empty")).toBeInTheDocument();
});

it('should update the selected value when an option is selected', () => {
const props = {};
const value = false;
const editorChange = jest.fn();


const result = render(<NotEditor props={props} value={value} editorChange={editorChange} />);
const span = result.container.getElementsByTagName('span')[0];

expect(within(span).getByText('empty')).toBeInTheDocument();

fireEvent.click(span);

const option = result.getAllByText('NOT');
fireEvent.click(option[0]);
const updatedSpan = result.container.getElementsByTagName('span')[0];


expect(within(updatedSpan).getByText('NOT')).toBeInTheDocument();
});

it('should call editorChange when an option is selected', () => {
const props = {};
const value = false;
const editorChange = jest.fn();
const result = render(<NotEditor props={props} value={value} editorChange={editorChange} />);
const span = result.container.getElementsByTagName('span')[0];

fireEvent.click(span);


const option = result.getAllByText('NOT');
fireEvent.click(option[0]);

expect(editorChange).toHaveBeenCalledTimes(1);
});

});
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React, { useRef } from "react";
import { Dropdown } from "primereact/dropdown";

export function FilterComponentBinaryDropDown({ isInEditMode, filterConfig, currentFilters, onFilter }) {
const options = useRef(["true", "false"]);
const options = useRef(filterConfig.options || ["true", "false"]);

const fieldSet = filterConfig.fieldSets[0];

Expand Down
8 changes: 8 additions & 0 deletions src/main/cliapp/src/components/Templates/NotTemplate.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import React from 'react'
import { EllipsisTableCell } from '../EllipsisTableCell';

export const NotTemplate = ({ value }) => {
if (value === null || value === undefined || typeof value !== 'boolean') return null;
const textString = value ? "NOT" : "";
return <EllipsisTableCell>{textString}</EllipsisTableCell>;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { render } from '@testing-library/react';
import { NotTemplate } from '../NotTemplate';
import '../../../tools/jest/setupTests';

describe('NotTemplate', () => {

it('should return null when value is null', () => {
const { container } = render(<NotTemplate value={null} />);
expect(container.firstChild).toBeNull();
});

it('should return a component with the text "NOT" when the value is true', () => {
const { getByText } = render(<NotTemplate value={true} />);
expect(getByText("NOT")).toBeInTheDocument();
});
it('should return a component with an empty string when the value is false', () => {
const { container } = render(<NotTemplate value={false} />);
expect(container.textContent).toBe('');
});

it('should return null when value is not a boolean', () => {
const { container } = render(<NotTemplate value={123} />);
expect(container.firstChild).toBeNull();
});
});
8 changes: 4 additions & 4 deletions src/main/cliapp/src/constants/FilterFields.js
Original file line number Diff line number Diff line change
Expand Up @@ -594,7 +594,7 @@ export const FILTER_CONFIGS = Object.freeze({
isExtinctFilterConfig: { filterComponentType: "dropdown", fieldSets: [FIELD_SETS.isExtinctFieldSet] },
obsoleteFilterConfig: { filterComponentType: "dropdown", fieldSets: [FIELD_SETS.obsoleteFieldSet] },
internalFilterConfig: { filterComponentType: "dropdown", fieldSets: [FIELD_SETS.internalFieldSet] },
negatedFilterConfig: { filterComponentType: "dropdown", fieldSets: [FIELD_SETS.negatedFieldSet] },
negatedFilterConfig: { filterComponentType: "dropdown", fieldSets: [FIELD_SETS.negatedFieldSet], options: [ { label: "NOT", value: "true" }, { label: "null", value: "false" } ] },
oblodgett marked this conversation as resolved.
Show resolved Hide resolved

annotationTypeFilterConfig: { filterComponentType: "multiselect", fieldSets: [FIELD_SETS.annotationTypeFieldSet], aggregationFieldSet: FIELD_SETS.daAggregationFieldSet, useKeywordFields: true },
diseaseDataProviderFilterConfig: { filterComponentType: "multiselect", fieldSets: [FIELD_SETS.dataProviderFieldSet], aggregationFieldSet: FIELD_SETS.daAggregationFieldSet, useKeywordFields: true },
Expand All @@ -605,12 +605,12 @@ export const FILTER_CONFIGS = Object.freeze({
agmDataProviderFilterConfig: { filterComponentType: "multiselect", fieldSets: [FIELD_SETS.dataProviderFieldSet], aggregationFieldSet: FIELD_SETS.agmAggregationFieldSet, useKeywordFields: true },
diseaseQualifiersFilterConfig: { filterComponentType: "multiselect", fieldSets: [FIELD_SETS.diseaseQualifiersFieldSet], aggregationFieldSet: FIELD_SETS.daAggregationFieldSet, useKeywordFields: true },
relationFilterConfig: { filterComponentType: "multiselect", fieldSets: [FIELD_SETS.relationFieldSet], aggregationFieldSet: FIELD_SETS.daAggregationFieldSet, useKeywordFields: true },
paRelationFilterConfig: { filterComponentType: "multiselect", fieldSets: [FIELD_SETS.relationFieldSet], aggregationFieldSet: FIELD_SETS.paAggregationFieldSet, useKeywordFields: true },
paRelationFilterConfig: { filterComponentType: "multiselect", fieldSets: [FIELD_SETS.relationFieldSet], aggregationFieldSet: FIELD_SETS.paAggregationFieldSet, useKeywordFields: true },
geneticModifierRelationFilterConfig: { filterComponentType: "multiselect", fieldSets: [FIELD_SETS.geneticModifierRelationFieldSet], aggregationFieldSet: FIELD_SETS.daAggregationFieldSet, useKeywordFields: true },
geneticSexFilterConfig: { filterComponentType: "multiselect", fieldSets: [FIELD_SETS.geneticSexFieldSet], aggregationFieldSet: FIELD_SETS.daAggregationFieldSet, useKeywordFields: true },
secondaryDataProviderFilterConfig: { filterComponentType: "multiselect", fieldSets: [FIELD_SETS.secondaryDataProviderFieldSet], aggregationFieldSet: FIELD_SETS.daAggregationFieldSet, useKeywordFields: true },
speciesDataProviderFilterConfig: { filterComponentType: "multiselect", fieldSets: [FIELD_SETS.dataProviderFieldSet], aggregationFieldSet: FIELD_SETS.speciesAggregationFieldSet, useKeywordFields: true },
evidenceCodesFilterConfig: { filterComponentType: "multiselect", fieldSets: [FIELD_SETS.evidenceCodesFieldSet], aggregationFieldSet: FIELD_SETS.daAggregationFieldSet, useKeywordFields: true },
speciesDataProviderFilterConfig: { filterComponentType: "multiselect", fieldSets: [FIELD_SETS.dataProviderFieldSet], aggregationFieldSet: FIELD_SETS.speciesAggregationFieldSet, useKeywordFields: true },
evidenceCodesFilterConfig: { filterComponentType: "multiselect", fieldSets: [FIELD_SETS.evidenceCodesFieldSet], aggregationFieldSet: FIELD_SETS.daAggregationFieldSet, useKeywordFields: true },
variantDataProviderFilterConfig: { filterComponentType: "multiselect", fieldSets: [FIELD_SETS.dataProviderFieldSet], aggregationFieldSet: FIELD_SETS.variantAggregationFieldSet, useKeywordFields: true },
variantStatusFilterConfig: { filterComponentType: "multiselect", fieldSets: [FIELD_SETS.variantStatusFieldSet], aggregationFieldSet: FIELD_SETS.variantAggregationFieldSet,useKeywordFields: true },

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ import { DiseaseTemplate } from '../../components/Templates/DiseaseTemplate';
import { GenomicEntityTemplate } from '../../components/Templates/genomicEntity/GenomicEntityTemplate';
import { GenomicEntityListTemplate } from '../../components/Templates/genomicEntity/GenomicEntityListTemplate';
import { BooleanTemplate } from '../../components/Templates/BooleanTemplate';
import { NotTemplate } from '../../components/Templates/NotTemplate';

import { NotEditor } from '../../components/Editors/NotEditor';

import { ControlledVocabularyDropdown } from '../../components/ControlledVocabularySelector';
import { ConditionRelationHandleDropdown } from '../../components/ConditionRelationHandleSelector';
Expand Down Expand Up @@ -428,26 +431,12 @@ export const DiseaseAnnotationsTable = () => {
);
};

const onNegatedEditorValueChange = (props, event) => {
let updatedAnnotations = [...props.props.value];
if (event.value || event.value === '') {
updatedAnnotations[props.rowIndex].negated = JSON.parse(event.value.name);
}
};
const onNegatedEditorValueChange = (event, props) => {
if(event.target.value === undefined || event.target.value === null) return;

const negatedEditor = (props) => {
return (
<>
<TrueFalseDropdown
options={booleanTerms}
editorChange={onNegatedEditorValueChange}
props={props}
field={"negated"}
/>
<ErrorMessageComponent errorMessages={errorMessagesRef.current[props.rowIndex]} errorField={"negated"} />
</>
);
};
let updatedAnnotations = [...props.props.value];
updatedAnnotations[props.rowIndex].negated = event.target.value;
}

const onInternalEditorValueChange = (props, event) => {
let updatedAnnotations = [...props.props.value];
Expand Down Expand Up @@ -934,11 +923,11 @@ export const DiseaseAnnotationsTable = () => {
},
{
field: "negated",
header: "Negated",
body: (rowData) => <BooleanTemplate value={rowData.negated}/>,
header: "NOT",
body: (rowData) => <NotTemplate value={rowData.negated}/>,
sortable: true,
filterConfig: FILTER_CONFIGS.negatedFilterConfig,
editor: (props) => negatedEditor(props)
editor: (props) => <NotEditor props={props} value={props.value} editorChange={onNegatedEditorValueChange}/>
},
{
field: "diseaseAnnotationObject.name",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { Toast } from "primereact/toast";
import { MultiSelect } from 'primereact/multiselect';
import { useMutation, useQueryClient } from "react-query";
import { FormErrorMessageComponent } from "../../components/Error/FormErrorMessageComponent";
import { NotEditor } from "../../components/Editors/NotEditor";
import { classNames } from "primereact/utils";
import { DiseaseAnnotationService } from "../../service/DiseaseAnnotationService";
import { Splitter, SplitterPanel } from "primereact/splitter";
Expand Down Expand Up @@ -75,7 +76,7 @@ export const NewAnnotationForm = ({
const geneticModifierRelationTerms = useControlledVocabularyService('disease_genetic_modifier_relation');
const [uiErrorMessages, setUiErrorMessages] = useState({});
const areUiErrors = useRef(false);
const newAnnotationOptionalFields = ["Asserted Genes", "Asserted Allele", "Negated", "With", "Related Notes", "Experimental Conditions", "Experiments", "Genetic Sex",
const newAnnotationOptionalFields = ["Asserted Genes", "Asserted Allele", "NOT", "With", "Related Notes", "Experimental Conditions", "Experiments", "Genetic Sex",
"Disease Qualifiers", "SGD Strain Background", "Annotation Type", "Genetic Modifier Relation", "Genetic Modifiers","Internal"];
let defaultUserSettings = getDefaultFormState("DiseaseAnnotations", newAnnotationOptionalFields, undefined);
const { settings: settingsKey , mutate: setSettingsKey } = useGetUserSettings('DiseaseAnnotationsFormSettings', defaultUserSettings, false);
Expand Down Expand Up @@ -566,22 +567,14 @@ export const NewAnnotationForm = ({
</div>
</div>

{selectedFormFields?.includes("Negated") && (
{selectedFormFields?.includes("NOT") && (
<>
<div className="grid">
<div className={labelColumnSize}>
<label htmlFor="negated">Negated</label>
<label htmlFor="negated">NOT</label>
</div>
<div className={widgetColumnSize}>
<Dropdown
name="negated"
value={newAnnotation.negated}
options={negatedTerms}
optionLabel='text'
optionValue='name'
onChange={onDropdownFieldChange}
className={classNames({'p-invalid': submitted && errorMessages.negated})}
/>
<NotEditor value={newAnnotation.negated} editorChange={onDropdownFieldChange}/>
</div>
<div className={fieldDetailsColumnSize}>
<FormErrorMessageComponent errorMessages={errorMessages} errorField={"negated"}/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ describe("<DiseaseAnnotationsPage />", () => {
const modInternalIdTd = await result.findByText("mockModInternalId");
const subjectTd = await result.findByText(/C57BL\/6J-Rfx3/i);
const relationTd = await result.findByText("is_model_of");
const negatedInternalObsoleteArray = await result.findAllByText("false");
const internalObsoleteArray = await result.findAllByText("false");
const NOTArray = await result.findAllByText("NOT");
const diseaseTd = await result.findByText(/visceral heterotaxy/i);
const referenceTd = await result.findByText(/MGI:5284969/i);
const evidenceCodeTd = await result.findByText(/TAS/i);
Expand All @@ -63,7 +64,8 @@ describe("<DiseaseAnnotationsPage />", () => {
expect(modInternalIdTd).toBeInTheDocument();
expect(subjectTd).toBeInTheDocument();
expect(relationTd).toBeInTheDocument();
expect(negatedInternalObsoleteArray.length).toEqual(3);
expect(internalObsoleteArray.length).toEqual(2);
expect(NOTArray.length).toEqual(2);
expect(diseaseTd).toBeInTheDocument();
expect(referenceTd).toBeInTheDocument();
expect(evidenceCodeTd).toBeInTheDocument();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ export const data = {
}
]
},
"negated": false,
"negated": true,
"relation": {
"dateCreated": "2022-01-26T09:40:54.020724Z",
"dateUpdated": "2022-01-26T09:40:54.020726Z",
Expand Down
Loading