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

Feat/spike ant d strategy #1639

Draft
wants to merge 9 commits into
base: master
Choose a base branch
from
2 changes: 1 addition & 1 deletion docs/portal_config.md
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,7 @@ Below is an example, with inline comments describing what each JSON block config
"studyRegistration": true, // optional, whether to enable the study registration feature
"workspaceRegistration": true, // optional, whether to enable the workspace registration feature
"workspaceTokenServiceRefreshTokenAtLogin": true, // optional, whether to refresh the WTS token directly at portal login (recommended mode). If not set, this refresh happens only when the user enters the workspace section of the portal (default/old/previous mode).
"legacyVADCDataDictionary": false, // optional, VADC specific. Set to "true" to ensure the new version of the /analysis/AtlasDataDictionary data dictionary is displayed the user navigates to /analysis/AtlasDataDictionary, and when the user clicks on the data dictionary button in when in the Atlas app page.
},
"dataExplorerConfig": { // required only if featureFlags.explorer is true; configuration for the Data Explorer (/explorer); can be replaced by explorerConfig, see Multi Tab Explorer doc
"charts": { // optional; indicates which charts to display in the Data Explorer
Expand Down Expand Up @@ -749,7 +750,6 @@ Below is an example, with inline comments describing what each JSON block config
"description": "My app description", // App title/name, also displayed on the App card in the /analysis page
"image": "/src/img/analysis-icons/myapp-image.svg", // App logo/image to be displayed on the App card in the /analysis page
"needsTeamProject": true, // Optional. Whether the app needs a "team project" selection to be made by the user first. If true, it will force the user to select a "team project" first. See also https://github.com/uc-cdis/data-portal/pull/1445
"dataDictionaryVersion": "new", // Optional, for custom AtlasDataDictionary. Set to "new" to ensure the new version of the /analysis/AtlasDataDictionary data dictionary when the user navigates to /analysis/AtlasDataDictionary.
},
{
"title": "My other app",
Expand Down
8 changes: 6 additions & 2 deletions src/Analysis/AnalysisApp.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import CheckForTeamProjectApplication from './SharedUtils/TeamProject/Utils/Chec
import TeamProjectHeader from './SharedUtils/TeamProject/TeamProjectHeader/TeamProjectHeader';
import './AnalysisApp.css';
import AtlasDataDictionaryButton from './AtlasDataDictionary/AtlasDataDictionaryButton/AtlasDataDictionaryButton';
import AtlasLegacyDataDictionaryButton from './AtlasDataDictionary/AtlasLegacyDataDictionaryButton/AtlasLegacyDataDictionaryButton';
import isEnabled from '../helpers/featureFlags';

const queryClient = new QueryClient();

Expand Down Expand Up @@ -139,7 +141,7 @@ class AnalysisApp extends React.Component {
return (
<div className='analysis-app_flex_row'>
<AtlasDataDictionaryContainer
dataDictionaryVersion={analysisApps[app].dataDictionaryVersion}
useLegacyDataDictionary={isEnabled('legacyVADCDataDictionary')}
/>
</div>
);
Expand Down Expand Up @@ -168,7 +170,9 @@ class AnalysisApp extends React.Component {
<React.Fragment>
<div className='analysis-app__iframe-wrapper'>
{this.state.app.title === 'OHDSI Atlas' && (
<AtlasDataDictionaryButton />
isEnabled('legacyVADCDataDictionary')
? <AtlasLegacyDataDictionaryButton />
: <AtlasDataDictionaryButton />
)}
<iframe
className='analysis-app__iframe'
Expand Down
22 changes: 8 additions & 14 deletions src/Analysis/AtlasDataDictionary/AtlasDataDictionaryContainer.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,15 @@
import React from 'react';
import PropTypes from 'prop-types';
import { useLocation, useHistory, useRouteMatch } from 'react-router-dom';
import ProtectedContent from '../../Login/ProtectedContent';
import AtlasDataDictionaryLoading from './AtlasDataDictionaryTable/AtlasDataDictionaryLoading';
import AtlasDataDictionaryButton from './AtlasDataDictionaryButton/AtlasDataDictionaryButton';
import AtlasLegacyDataDictionaryButton from './AtlasLegacyDataDictionaryButton/AtlasLegacyDataDictionaryButton';
import './AtlasDataDictionary.css';

const AtlasDataDictionaryContainer = ({ dataDictionaryVersion }) => {
const location = useLocation();
const history = useHistory();
const match = useRouteMatch();

if (!dataDictionaryVersion || !dataDictionaryVersion.includes('new')) {
// Default legacy component: render a div with AtlasDataDictionaryButton when
// no dataDictionaryVersion is set or it does not include 'new':
const AtlasDataDictionaryContainer = ({ useLegacyDataDictionary }) => {
if (useLegacyDataDictionary) {
// Default legacy component: render a div with AtlasLegacyDataDictionaryButton when
// useLegacyDataDictionary is set to true:
return (
<div style={{ width: '100%' }}><AtlasDataDictionaryButton /></div>
<div style={{ width: '100%' }}><AtlasLegacyDataDictionaryButton /></div>
);
}
return (
Expand All @@ -26,11 +20,11 @@ const AtlasDataDictionaryContainer = ({ dataDictionaryVersion }) => {
};

AtlasDataDictionaryContainer.propTypes = {
dataDictionaryVersion: PropTypes.string,
useLegacyDataDictionary: PropTypes.bool,
};

AtlasDataDictionaryContainer.defaultProps = {
dataDictionaryVersion: null,
useLegacyDataDictionary: false,
};

export default AtlasDataDictionaryContainer;
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import React, { useState } from 'react';
import { Modal } from 'antd';
import Button from '@gen3/ui-component/dist/components/Button';
import '../AtlasDataDictionaryButton/AtlasDataDictionaryButton.css';

const AtlasLegacyDataDictionaryButton = () => {
const dataDictionaryURL = 'https://vincicentral.vinci.med.va.gov/SitePages/VINCI_University-VADC_Academy.aspx';
const [isModalOpen, setIsModalOpen] = useState(false);
const showModal = () => {
setIsModalOpen(true);
};
const handleCancel = () => {
setIsModalOpen(false);
};

return (
<div className='atlas-data-dictionary-button' data-testid='atlas-data-dictionary-button'>
<Modal
title="You're now leaving the VA Data Commons"
open={isModalOpen}
className='atlas-data-dictionary-button-modal'
onOk={() => {
window.open(dataDictionaryURL, '_blank');
handleCancel();
}}
onCancel={handleCancel}
>
<p>
The VADC website, privacy and security policies don&apos;t apply to the
site or app you&apos;re about to visit.
</p>
</Modal>
<Button
className='analysis-app__button'
onClick={showModal}
label='MVP Data Dictionary'
buttonType='secondary'
rightIcon='external-link'
/>
</div>
);
};
export default AtlasLegacyDataDictionaryButton;
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import React from 'react';
import { render, fireEvent } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';
import AtlasLegacyDataDictionaryButton from './AtlasLegacyDataDictionaryButton';

describe('AtlasLegacyDataDictionaryButton', () => {
it('renders the AtlasLegacyDataDictionaryButton component with an "MVP Data Dictionary" Button', () => {
const { getByRole, getByTestId } = render(<AtlasLegacyDataDictionaryButton />);
expect(getByRole('button')).toBeInTheDocument();
expect(getByRole('button')).toHaveTextContent('MVP Data Dictionary');
expect(getByTestId('atlas-data-dictionary-button')).toBeInTheDocument();
});

it('shows the Modal when the button is clicked', () => {
const { getByRole, queryByText } = render(<AtlasLegacyDataDictionaryButton />);
fireEvent.click(getByRole('button'));
expect(queryByText('You\'re now leaving the VA Data Commons')).toBeInTheDocument();
});
});
8 changes: 7 additions & 1 deletion src/Analysis/GWASApp/Components/Covariates/Covariates.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import queryConfig from '../../../SharedUtils/QueryConfig';
import { useFetch, useFilter } from '../../Utils/formHooks';
import { useSourceContext } from '../../Utils/Source';
import SearchBar from '../SearchBar/SearchBar';
import { addAriaLabelsToCovariatesTable } from '../../../SharedUtils/AccessibilityUtils/AntDAccessibilityFixes/index';

const Covariates = ({ selected, handleSelect, submittedCovariateIds }) => {
const { source } = useSourceContext();
Expand All @@ -30,7 +31,9 @@ const Covariates = ({ selected, handleSelect, submittedCovariateIds }) => {
);

// remove already selected Covariates from list
const displayedCovariates = filteredCovariates.filter((x) => !submittedCovariateIds.includes(x.concept_id));
const displayedCovariates = filteredCovariates.filter(
(x) => !submittedCovariateIds.includes(x.concept_id),
);

const covariateSelection = () => ({
type: 'radio',
Expand Down Expand Up @@ -76,6 +79,7 @@ const Covariates = ({ selected, handleSelect, submittedCovariateIds }) => {
}

if (covariates?.status === 'success') {
addAriaLabelsToCovariatesTable();
return (
<div data-tour='select-concept'>
<SearchBar
Expand All @@ -85,6 +89,7 @@ const Covariates = ({ selected, handleSelect, submittedCovariateIds }) => {
/>
<Table
className='GWASUI-table1'
onChange={addAriaLabelsToCovariatesTable}
data-tour='concept-table'
rowKey='concept_id'
size='middle'
Expand All @@ -111,6 +116,7 @@ const Covariates = ({ selected, handleSelect, submittedCovariateIds }) => {
Covariates.propTypes = {
selected: PropTypes.object,
handleSelect: PropTypes.func.isRequired,
submittedCovariateIds: PropTypes.object.isRequired,
};

Covariates.defaultProps = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { fetchCohortDefinitions } from '../../../Utils/cohortMiddlewareApi';
import queryConfig from '../../../../SharedUtils/QueryConfig';
import { useFetch, useFilter } from '../../../Utils/formHooks';
import { useSourceContext } from '../../../Utils/Source';
import { addAriaLabelsToCohortDefinationsTable } from '../../../../SharedUtils/AccessibilityUtils/AntDAccessibilityFixes/index';

const CohortDefinitions = ({
selectedCohort = undefined,
Expand Down Expand Up @@ -48,26 +49,31 @@ const CohortDefinitions = ({
];
if (cohorts?.status === 'error') return <React.Fragment>Error getting data for table</React.Fragment>;

return cohorts?.status === 'success' ? (
<Table
className='GWASUI-table1'
rowKey='cohort_definition_id'
size='middle'
pagination={{
defaultPageSize: 10,
showSizeChanger: true,
pageSizeOptions: ['10', '20', '50', '100', '500'],
}}
onRow={(selectedRow) => ({
onClick: () => {
handleCohortSelect(selectedRow);
},
})}
rowSelection={cohortSelection(selectedCohort)}
columns={cohortTableConfig}
dataSource={displayedCohorts}
/>
) : (
if (cohorts?.status === 'success') {
addAriaLabelsToCohortDefinationsTable();
return (
<Table
className='GWASUI-table1'
onChange={addAriaLabelsToCohortDefinationsTable}
rowKey='cohort_definition_id'
size='middle'
pagination={{
defaultPageSize: 10,
showSizeChanger: true,
pageSizeOptions: ['10', '20', '50', '100', '500'],
}}
onRow={(selectedRow) => ({
onClick: () => {
handleCohortSelect(selectedRow);
},
})}
rowSelection={cohortSelection(selectedCohort)}
columns={cohortTableConfig}
dataSource={displayedCohorts}
/>
);
}
return (
<React.Fragment>
<div className='GWASUI-spinnerContainer GWASUI-emptyTable'>
<Spin />
Expand Down
2 changes: 1 addition & 1 deletion src/Analysis/GWASApp/Utils/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export const GWASAppSteps = [
export const MESSAGES = {
OVERLAP_ERROR: {
title:
'None of the persons in the (remaining) population of your selected cohorts overlap with the study population',
'One or more groups have no overlap with the (remaining) population of any of the other groups',
messageType: 'warning',
},
NO_BINS_ERROR: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,30 +5,53 @@
border-color: #194C90;
}

.GWASUI-navBtn:not([disabled]):hover,
.GWASUI-navBtn:not([disabled]):focus {
background: #2466AC;
border-color: #2466AC;
.gwas-modal .ant-btn-primary:not([disabled]) {
background: #194C90;
border-color: #194C90;
}

.gwas-modal .ant-btn-primary:not([disabled]) {
.gwas-modal .ant-btn-default {
color: #194C90;
border-color: #194C90;
}

.euler-diagram-controls .ant-btn-primary:not([disabled]),
.euler-diagram-controls .ant-btn-primary:not([disabled]):focus {
background: #194C90;
border-color: #194C90;
}

.gwas-modal .ant-btn-primary:not([disabled]):hover,
.gwas-modal .ant-btn-primary:not([disabled]):focus {
.euler-diagram-controls .ant-btn-secondary {
color: #194C90;
border-color: #194C90;
}

.GWASUI-navBtn:not([disabled]):hover,
.GWASUI-navBtn:not([disabled]):focus {
background: #2466AC;
border-color: #2466AC;
}

.gwas-modal .ant-btn-default {
color: #194C90;
border-color: #194C90;
.gwas-modal .ant-btn-primary:not([disabled]):hover,
.gwas-modal .ant-btn-primary:not([disabled]):focus {
background: #2466AC;
border-color: #2466AC;
}

.gwas-modal .ant-btn-default:hover,
.gwas-modal .ant-btn-default:focus {
color: #2466AC;
border-color: #2466AC;
}

.euler-diagram-controls .ant-btn-primary:not([disabled]):hover {
background: #2466AC;
border-color: #2466AC;
}

.euler-diagram-controls .ant-btn-secondary:hover,
.euler-diagram-controls .ant-btn-secondary:focus {

color: #2466AC;
border-color: #2466AC;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*
* Used to update DOM attributes after component mounts
* example: useSetDOMAttribute('.ant-radio-input','aria-label', 'Select row for study population')
*/

const delayToAllowDOMRendering = 500;
const SetDOMAttribute = (selector, attribute, value) => {
setTimeout(() => {
const elements = document.querySelectorAll(selector);
elements?.forEach((element) => {
if (element) {
element.setAttribute(attribute, value);
} else {
/* eslint-disable no-console */
console.error('Unable to find selector in setDOMAttribute: ', selector);
/* eslint-enable no-console */
}
});
}, delayToAllowDOMRendering);
};

export default SetDOMAttribute;
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import SetDOMAttribute from './SetDOMAttribute';

const addAriaLabelsToCohortDefinationsTable = () => {
SetDOMAttribute(
'.GWASUI-mainTable input.ant-radio-input',
'aria-label',
'Select row for study population'
);
};

const addAriaLabelsToCovariatesTable = () => {
SetDOMAttribute(
'.continuous-covariates-table input.ant-radio-input',
'aria-label',
'Select row for outcome phenotype'
);
};

export {
addAriaLabelsToCohortDefinationsTable,
addAriaLabelsToCovariatesTable,
};
Loading