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/vadc sprint22 #1451

Merged
merged 41 commits into from
Nov 14, 2023
Merged
Show file tree
Hide file tree
Changes from 30 commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
d9bffbe
feat: allow teamProject to be configured in gitops.json instead of ha…
pieterlukasse Nov 1, 2023
38deeeb
feat: allow for appId property in app definition
pieterlukasse Nov 1, 2023
5494f24
feat: added analysisTools section documentation to portal_config.md
pieterlukasse Nov 2, 2023
999cfc9
fix: revert back the old GWAS app names
pieterlukasse Nov 2, 2023
49bcf10
feat(addTeamProjecHeaderToAnalysisApps): Added header without logic t…
jarvisraymond-uchicago Oct 30, 2023
99d7d40
feat(addTeamProjectHeaderToAnalysisApps): added redirect logic
jarvisraymond-uchicago Oct 31, 2023
1c56169
feat(addTeamProjectHeaderToAnalysisApps): Got test to work for TeamPr…
jarvisraymond-uchicago Nov 1, 2023
11bc0f7
feat(addTeamProjectHeaderToAnalysisApps): Ran eslint-new
jarvisraymond-uchicago Nov 1, 2023
63da886
feat(addTeamProjectHeaderToAnalysisApps): Removed unneeded async decl…
jarvisraymond-uchicago Nov 1, 2023
9f619a1
feat(addTeamProjectHeaderToAnalysisApps): Removed unneeded waitFor im…
jarvisraymond-uchicago Nov 1, 2023
b3183ea
feat(addTeamProjectHeaderToAnalysisApps): Fixed lint issues
jarvisraymond-uchicago Nov 1, 2023
417e874
feat(addTeamProjectHeaderToAnalysisApps): Removed header element to m…
jarvisraymond-uchicago Nov 1, 2023
7b45be8
feat(addTeamProjectHeaderToAnalysisApps): Removed unneeded comments
jarvisraymond-uchicago Nov 1, 2023
1407d4e
feat(addTeamProjectHeaderToAnalysisApps): Ran linter
jarvisraymond-uchicago Nov 1, 2023
02eff62
feat(addTeamProjectHeaderToAnalysisApps): Changed all instances of va…
jarvisraymond-uchicago Nov 6, 2023
22f8cfc
feat(addTeamProjectHeaderToAnalysisApps): updated storybook for clarity
jarvisraymond-uchicago Nov 6, 2023
65dc4b0
Reload workspace options on paymodel change (#1447)
nss10 Nov 6, 2023
7856ed9
feat(teamProjectApiUpdate): Initial commit
jarvisraymond-uchicago Nov 6, 2023
97deb5e
feat(teamProjectApiUpdate): Added API update for results app
jarvisraymond-uchicago Nov 6, 2023
9361619
Chore(deps-dev): Bump @babel/traverse from 7.22.5 to 7.23.2 (#1440)
dependabot[bot] Nov 6, 2023
4d14bff
feat(teamProjectApiUpdate): Added conditional in AnalysisApp.jsx for …
jarvisraymond-uchicago Nov 7, 2023
857284b
feat(teamProjectApiUpdate): Refactored initializeCurrentState for ter…
jarvisraymond-uchicago Nov 7, 2023
9be5fd0
feat(teamProjectApiUpdate): updated API endpoint per change in BE
jarvisraymond-uchicago Nov 7, 2023
21ae52c
feat(teamProjectApiUpdate): updated data passed to HomeTable componen…
jarvisraymond-uchicago Nov 8, 2023
37d814e
feat(teamProjectApiUpdate): updated data passed to HomeTable test to …
jarvisraymond-uchicago Nov 8, 2023
e77ab75
feat(teamProjectApiUpdate): Reverted change on FE to deal with BE cha…
jarvisraymond-uchicago Nov 8, 2023
3c2fff6
feat(teamProjectApiUpdate): Reverted unintentionally changed files
jarvisraymond-uchicago Nov 8, 2023
ff43102
feat(teamProjectApiUpdate): Removed unneeded console log statement
jarvisraymond-uchicago Nov 8, 2023
bc98332
feat(teamProjectApiUpdate): Changed OHSDI iframe src string based on …
jarvisraymond-uchicago Nov 8, 2023
597ab2e
Merge branch 'master' into feat/vadc_sprint22
jarvisraymond-uchicago Nov 13, 2023
612e7f2
feat(vadc_sprint22): Added sanitize function for team project variable
jarvisraymond-uchicago Nov 14, 2023
d738941
feat(vadc_sprint22): Added const var for team project variable
jarvisraymond-uchicago Nov 14, 2023
156b691
feat(vadc_sprint22): Refactored to use class function getAtlastURLWit…
jarvisraymond-uchicago Nov 14, 2023
72a0895
feat(vadc_sprint22): Autoformatted code
jarvisraymond-uchicago Nov 14, 2023
4beda9d
feat(vadc_sprint22): Updated regex and test for regex
jarvisraymond-uchicago Nov 14, 2023
cb9ffd0
feat(vadc_sprint22): formatted Code for linter
jarvisraymond-uchicago Nov 14, 2023
6f8e32a
feat(vadc_sprint22): Updated regex and autoformatted code
jarvisraymond-uchicago Nov 14, 2023
2ec64b7
feat(vadc_sprint22): Reverted changes for snyk
jarvisraymond-uchicago Nov 14, 2023
04cd8af
feat(vadc_sprint22): updated regex and added throw error
jarvisraymond-uchicago Nov 14, 2023
6a72842
fix: let teamproject be added to Atlas only if the needsTeamProject f…
pieterlukasse Nov 14, 2023
2262b48
Merge branch 'master' into feat/vadc_sprint22
jarvisraymond-uchicago Nov 14, 2023
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
15 changes: 15 additions & 0 deletions docs/portal_config.md
Original file line number Diff line number Diff line change
Expand Up @@ -708,6 +708,21 @@ Below is an example, with inline comments describing what each JSON block config
"connectSrcCSPWhitelist": [ // optional; Array of urls to add to the header CSP (Content-Security-Policy) connect-src 'self'
"https://example.s3.amazonaws.com" // full url to be added
],
"analysisTools": [ // analysis apps to be diplayed at the /analysis/ page.
{
"appId": "myAppId", // Optional. Can be used to ensure the app path after the /analysis/ subpath is fixed, e.g. URL https://SERVER-DOMAIN/analysis/myAppId. If not set, then "title" (below) is used.
"title": "My app title", // App title/name, also displayed on the App card in the /analysis page
"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
},
{
"title": "My other app",
"description": "etc",
"image": "/src/img/analysis-icons/etc.svg",
},
...
],
"stridesPortalURL": "https://strides-admin-portal.org", // optional; If configured, will display a link on the workspace page which can direct user to the STRIDES admin portal,
"registrationConfigs": { // optional; Required when using Kayako integration with Study/Workspace registration
"features":{ // Optional; Required when using study/Workspace registration
Expand Down
2 changes: 1 addition & 1 deletion src/Analysis/Analysis.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ class Analysis extends React.Component {
{CheckForTeamProjectApplication(analysisApps) && (
<Col flex='1 0 auto'>
<QueryClientProvider client={new QueryClient()} contextSharing>
<TeamProjectHeader showButton />
<TeamProjectHeader isEditable />
</QueryClientProvider>
</Col>
)}
Expand Down
29 changes: 25 additions & 4 deletions src/Analysis/AnalysisApp.jsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
import React from 'react';
import PropTypes from 'prop-types'; // see https://github.com/facebook/prop-types#prop-types
import Select from 'react-select';
import { Spin } from 'antd';
import { Spin, Row, Col } from 'antd';
import Button from '@gen3/ui-component/dist/components/Button';
import { QueryClient, QueryClientProvider } from 'react-query';
import { TourProvider } from '@reactour/tour';
import { disableBodyScroll, enableBodyScroll } from 'body-scroll-lock';
import BackLink from '../components/BackLink';
import HIVCohortFilter from '../HIVCohortFilter/HIVCohortFilter';
import { analysisApps } from '../localconf';
import './AnalysisApp.css';
import sessionMonitor from '../SessionMonitor';
import GWASContainer from './GWASApp/GWASContainer';
import GWASResultsContainer from './GWASResults/GWASResultsContainer';
import CheckForTeamProjectApplication from './SharedUtils/TeamProject/Utils/CheckForTeamProjectApplication';
import TeamProjectHeader from './SharedUtils/TeamProject/TeamProjectHeader/TeamProjectHeader';
import './AnalysisApp.css';

const queryClient = new QueryClient();

Expand Down Expand Up @@ -146,7 +148,11 @@ class AnalysisApp extends React.Component {
className='analysis-app__iframe'
title='Analysis App'
frameBorder='0'
src={`${this.state.app.applicationUrl}`}
src={
this.state.app.title === 'OHDSI Atlas'
pieterlukasse marked this conversation as resolved.
Show resolved Hide resolved
? `${this.state.app.applicationUrl}#/home?teamproject=${localStorage.getItem('teamProject')}`
: `${this.state.app.applicationUrl}`
}
onLoad={this.handleIframeApp}
/>
</div>
Expand Down Expand Up @@ -227,7 +233,22 @@ class AnalysisApp extends React.Component {
<BackLink url='/analysis' label='Back to Apps' />
{loaded ? (
<div className='analysis-app'>
<h2 className='analysis-app__title'>{app.title}</h2>
<Row>
<Col flex='1 0 auto'>
<h2>{app.title}</h2>
</Col>
{CheckForTeamProjectApplication(analysisApps) && (
<Col flex='1 0 auto'>
<QueryClientProvider
client={new QueryClient()}
contextSharing
>
<TeamProjectHeader isEditable={false} />
</QueryClientProvider>
</Col>
)}
</Row>

<p className='analysis-app__description'>{app.description}</p>
<div
className={`${
Expand Down
4 changes: 2 additions & 2 deletions src/Analysis/GWASApp/GWASContainer.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { Space, Button } from 'antd';
import ProgressBar from './Components/ProgressBar/ProgressBar';
import { GWASAppSteps, checkFinalPopulationSizeZero } from './Utils/constants';
import { SourceContextProvider } from './Utils/Source';
import initialState from './Utils/StateManagement/InitialState';
import reducer from './Utils/StateManagement/reducer';
import ACTIONS from './Utils/StateManagement/Actions';
import AttritionTableWrapper from './Components/AttritionTableWrapper/AttritionTableWrapper';
Expand All @@ -13,10 +12,11 @@ import SelectOutcome from './Steps/SelectOutcome/SelectOutcome';
import SelectCovariates from './Steps/SelectCovariates/SelectCovariates';
import DismissibleMessagesList from './Components/DismissibleMessagesList/DismissibleMessagesList';
import MakeFullscreenButton from './Components/MakeFullscreenButton/MakeFullscreenButton';
import InitializeCurrentState from './Utils/StateManagement/InitializeCurrentState';
import './GWASApp.css';

const GWASContainer = () => {
const [state, dispatch] = useReducer(reducer, initialState);
const [state, dispatch] = useReducer(reducer, InitializeCurrentState());

const generateStep = () => {
switch (state.currentStep) {
Expand Down
2 changes: 1 addition & 1 deletion src/Analysis/GWASApp/Utils/StateManagement/InitialState.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const initialState = {
finalPopulationSizes: [],
selectionMode: '',
messages: [],
selectedTeamProject: 'temporary-test-teamproject01', // TODO - remove and leave '' when teamproject selection is ready
selectedTeamProject: localStorage.getItem('teamProject'),
};

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

const InitializeCurrentState = () => ({
...initialState,
selectedTeamProject: localStorage.getItem('teamProject'),
});

export default InitializeCurrentState;
3 changes: 2 additions & 1 deletion src/Analysis/GWASResults/Views/Home/Home.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ const Home = () => {
const refetchInterval = 5000;

async function fetchGwasWorkflows() {
const workflowsEndpoint = `${gwasWorkflowPath}workflows`;
const currentTeamProject = localStorage.getItem('teamProject');
const workflowsEndpoint = `${gwasWorkflowPath}workflows?team_projects=${currentTeamProject}`;
const getWorkflows = await fetch(workflowsEndpoint);
return getWorkflows.json();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
.analysis .team-project-header {
line-height: 80px;
}

.team-project-header {
text-align: right;
color: #2e77b8;
font-size: 20px;
line-height: 80px;
margin-top: 2px;
}

.team-project-header_modal-button {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,31 +1,36 @@
import React, { useState, useEffect } from 'react';
import PropTypes from 'prop-types';
import { useHistory } from 'react-router-dom';
import EditIcon from './Icons/EditIcon';
import isEnterOrSpace from '../../IsEnterOrSpace';
import TeamProjectModal from '../TeamProjectModal/TeamProjectModal';
import './TeamProjectHeader.css';

const TeamProjectHeader = ({ showButton }) => {
const TeamProjectHeader = ({ isEditable }) => {
const [isModalOpen, setIsModalOpen] = useState(false);
const [bannerText, setBannerText] = useState('- -');
const showModal = () => {
setIsModalOpen(true);
};
const history = useHistory();

useEffect(() => {
const storedTeamProject = localStorage.getItem('teamProject');
if (storedTeamProject) {
setBannerText(storedTeamProject);
} else {
} else if (isEditable) {
showModal();
} else if (!isEditable && !storedTeamProject) {
// non-editable view should redirect to app selection if user doesn't have a storedTeamProject
history.push('/analysis');
}
}, []);
}, [history, isEditable]);

return (
<React.Fragment>
<h3 className='team-project-header'>
<div className='team-project-header'>
<strong>Team Project</strong> / {bannerText}
{showButton && (
{isEditable && (
<span
className='team-project-header_modal-button'
tabIndex='0'
Expand All @@ -41,8 +46,8 @@ const TeamProjectHeader = ({ showButton }) => {
<EditIcon />
</span>
)}
</h3>
{showButton && (
</div>
{isEditable && (
<TeamProjectModal
isModalOpen={isModalOpen}
setIsModalOpen={setIsModalOpen}
Expand All @@ -54,11 +59,11 @@ const TeamProjectHeader = ({ showButton }) => {
};

TeamProjectHeader.propTypes = {
showButton: PropTypes.bool,
isEditable: PropTypes.bool,
};

TeamProjectHeader.defaultProps = {
showButton: false,
isEditable: false,
};

export default TeamProjectHeader;
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@ const Template = (args) => (
</div>
);

export const withButton = Template.bind({});
withButton.args = {
showButton: true,
export const isEditable = Template.bind({});
isEditable.args = {
isEditable: true,
};

export const withNoButton = Template.bind({});
withNoButton.args = {
showButton: false,
export const notEditable = Template.bind({});
notEditable.args = {
isEditable: false,
};
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import React from 'react';
import { render, screen, fireEvent } from '@testing-library/react';
import { createMemoryHistory } from 'history';
import { Router } from 'react-router-dom';
import { QueryClient, QueryClientProvider } from 'react-query';
import '@testing-library/jest-dom';
import TeamProjectHeader from './TeamProjectHeader';
Expand All @@ -17,22 +19,38 @@ beforeEach(() => {
});
});

test('renders TeamProjectHeader with default props', () => {
test('renders TeamProjectHeader with default props when isEditable is true and no local storage', () => {
localStorageMock.getItem.mockReturnValueOnce(null);
render(
<QueryClientProvider client={new QueryClient()} contextSharing>
<TeamProjectHeader />
<TeamProjectHeader isEditable />
</QueryClientProvider>,
);
// Assert that the component renders without crashing without button
expect(screen.getByText('Team Project')).toBeInTheDocument();
expect(screen.getByText('/ - -')).toBeInTheDocument();
});

test('renders TeamProjectHeader with edit button when showButton is true and can open modal', () => {
test(`Calls useHistory for redirect to analysis page when isEditable is
false and teamProject is not set in local storage`, () => {
localStorageMock.getItem.mockReturnValueOnce(null);
const history = createMemoryHistory();

render(
<Router history={history}>
<QueryClientProvider client={new QueryClient()} contextSharing>
<TeamProjectHeader isEditable={false} />
</QueryClientProvider>
</Router>,
);

expect(history.location.pathname).toBe('/analysis');
});

test('renders TeamProjectHeader with edit button when isEditable is true and can open modal', () => {
render(
<QueryClientProvider client={new QueryClient()} contextSharing>
<TeamProjectHeader showButton />
<TeamProjectHeader isEditable />
</QueryClientProvider>,
);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import React, { useState } from 'react';
import PropTypes from 'prop-types';
import { Button, Modal, Spin, Select } from 'antd';
import {
Button, Modal, Spin, Select,
} from 'antd';
import { useQuery } from 'react-query';
import queryConfig from '../../QueryConfig';
import LoadingErrorMessage from '../../LoadingErrorMessage/LoadingErrorMessage';
Expand All @@ -9,7 +11,7 @@ import './TeamProjectModal.css';

const TeamProjectModal = ({ isModalOpen, setIsModalOpen, setBannerText }) => {
const [selectedTeamProject, setSelectedTeamProject] = useState(
localStorage.getItem('teamProject')
localStorage.getItem('teamProject'),
);

const closeAndUpdateTeamProject = () => {
Expand All @@ -21,7 +23,7 @@ const TeamProjectModal = ({ isModalOpen, setIsModalOpen, setBannerText }) => {
const { data, status } = useQuery(
'teamprojects',
fetchArboristTeamProjectRoles,
queryConfig
queryConfig,
);

let modalContent = (
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import React from 'react';
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import {
render, screen, fireEvent, waitFor,
} from '@testing-library/react';
import '@testing-library/jest-dom';
import { useQuery } from 'react-query';
import TeamProjectModal from './TeamProjectModal';
Expand All @@ -16,12 +18,12 @@ describe('TeamProjectModal', () => {
isModalOpen
setIsModalOpen={() => {}}
setBannerText={() => {}}
/>
/>,
);

expect(screen.getByText(/Please wait.../i)).toBeInTheDocument();
expect(
screen.getByText(/Retrieving the list of team projects./i)
screen.getByText(/Retrieving the list of team projects./i),
).toBeInTheDocument();
});

Expand All @@ -40,17 +42,17 @@ describe('TeamProjectModal', () => {
isModalOpen
setIsModalOpen={setIsModalOpen}
setBannerText={setBannerText}
/>
/>,
);

await waitFor(() => screen.getByText(/Please select your team./i));
expect(
screen.getByText(/-select one of the team projects below-/i)
screen.getByText(/-select one of the team projects below-/i),
).toBeInTheDocument();

expect(screen.getByRole('combobox')).toBeInTheDocument();
expect(screen.getByText('Submit').closest('button')).toHaveAttribute(
'disabled'
'disabled',
);
});

Expand All @@ -72,19 +74,18 @@ describe('TeamProjectModal', () => {
isModalOpen
setIsModalOpen={setIsModalOpen}
setBannerText={setBannerText}
/>
/>,
);

await waitFor(() => screen.getByText(/Please select your team./i));

expect(() =>
screen.getByText('select one of the team projects below')
expect(() => screen.getByText('select one of the team projects below'),
).toThrow('Unable to find an element');
expect(screen.getByText('test string')).toBeInTheDocument();
expect(screen.getByRole('combobox')).toBeInTheDocument();
expect(screen.getByText('Submit')).toBeInTheDocument();
expect(screen.getByText('Submit').closest('button')).not.toHaveAttribute(
'disabled'
'disabled',
);
});

Expand All @@ -106,7 +107,7 @@ describe('TeamProjectModal', () => {
isModalOpen
setIsModalOpen={setIsModalOpen}
setBannerText={setBannerText}
/>
/>,
);

await waitFor(() => screen.getByText(/Please select your team./i));
Expand Down
Loading
Loading