Unverified Commit 100d5a25 authored by Andrey Azov's avatar Andrey Azov Committed by GitHub
Browse files

Add a page for unviewed BLAST submissions (#740)

parent bbd47e9f
Pipeline #276147 passed with stages
in 5 minutes and 28 seconds
......@@ -22,7 +22,12 @@ import loadable from '@loadable/component';
import useHasMounted from 'src/shared/hooks/useHasMounted';
const BlastForm = loadable(() => import('./views/blast-form/BlastForm'));
const BlastJobs = loadable(() => import('./views/blast-jobs/BlastJobs'));
const BlastUnviewedSubmissions = loadable(
() => import('./views/blast-unviewed-submissions/BlastUnviewedSubmissions')
);
const BlastJobs = loadable(
() => import('./views/blast-submissions/BlastSubmissions')
);
const pageDescription = `
BLAST stands for Basic Local Alignment Search Tool.
......@@ -41,7 +46,11 @@ const BrowserPage = () => {
{hasMounted && (
<Routes>
<Route index element={<BlastForm />} />
<Route path="jobs" element={<BlastJobs />} />
<Route
path="unviewed-submissions"
element={<BlastUnviewedSubmissions />}
/>
<Route path="submissions" element={<BlastJobs />} />
</Routes>
)}
</>
......
......@@ -29,7 +29,7 @@ import speciesSelectorReducer, {
SpeciesSelectorState
} from 'src/content/app/species-selector/state/speciesSelectorSlice';
import ToolsAppBar from './ToolsAppBar';
import BlastAppBar from './BlastAppBar';
jest.mock(
'src/shared/components/communication-framework/ConversationIcon',
......@@ -70,7 +70,7 @@ const renderComponent = () => {
const renderResult = render(
<Provider store={store}>
<MemoryRouter>
<ToolsAppBar />
<BlastAppBar view="blast-form" />
</MemoryRouter>
</Provider>
);
......@@ -81,7 +81,7 @@ const renderComponent = () => {
};
};
describe('ToolsAppBar', () => {
describe('BlastAppBar', () => {
describe('Species Lozenge click', () => {
it('updates the selectedSpecies state', async () => {
const { container, store } = renderComponent();
......
......@@ -17,6 +17,7 @@
import React, { useMemo } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { Link } from 'react-router-dom';
import noop from 'lodash/noop';
import * as urlFor from 'src/shared/helpers/urlHelper';
import { AppName } from 'src/global/globalConfig';
......@@ -32,7 +33,18 @@ import SpeciesTabsWrapper from 'src/shared/components/species-tabs-wrapper/Speci
import type { CommittedItem } from 'src/content/app/species-selector/types/species-search';
const ToolsAppBar = () => {
type BlastView =
| 'blast-form'
| 'unviewed-submissions'
| 'submissions-list'
| 'submission-results';
type Props = {
view: BlastView;
};
const BlastAppBar = (props: Props) => {
const { view } = props;
const speciesList = useSelector(getEnabledCommittedSpecies);
const speciesListIds = useSelector(getSelectedSpeciesIds);
const dispatch = useDispatch();
......@@ -50,13 +62,25 @@ const ToolsAppBar = () => {
}
};
const speciesTabs = speciesList.map((species, index) => (
const enabledSpecies = speciesList.map((species, index) => (
<SelectedSpecies
key={index}
species={species}
onClick={() => speciesLozengeClick(species)}
/>
));
const disabledSpecies = speciesList.map((species, index) => (
<SelectedSpecies
key={index}
isActive={true}
species={{ ...species, isEnabled: false }}
onClick={noop}
/>
));
const speciesTabs = view === 'blast-form' ? enabledSpecies : disabledSpecies;
const speciesSelectorLink = useMemo(() => {
return <Link to={urlFor.speciesSelector()}>Change</Link>;
}, []);
......@@ -68,4 +92,4 @@ const ToolsAppBar = () => {
return <AppBar appName={AppName.TOOLS} mainContent={wrappedSpecies} />;
};
export default ToolsAppBar;
export default BlastAppBar;
......@@ -15,13 +15,13 @@
*/
import React from 'react';
import { useNavigate } from 'react-router-dom';
import { useAppDispatch, useAppSelector } from 'src/store';
import * as urlFor from 'src/shared/helpers/urlHelper';
import { submitBlast } from 'src/content/app/tools/blast/state/blast-api/blastApiSlice';
import LoadingButton from 'src/shared/components/loading-button/LoadingButton';
import { useAppSelector } from 'src/store';
import { useSubmitBlastMutation } from 'src/content/app/tools/blast/state/blast-api/blastApiSlice';
import useBlastInputSequences from 'src/content/app/tools/blast/components/blast-input-sequences/useBlastInputSequences';
import { isBlastFormValid } from 'src/content/app/tools/blast/utils/blastFormValidator';
......@@ -30,6 +30,8 @@ import { getSelectedSpeciesIds } from 'src/content/app/tools/blast/state/blast-f
import { toFasta } from 'src/shared/helpers/formatters/fastaFormatter';
import { PrimaryButton } from 'src/shared/components/button/Button';
import type {
Species,
BlastFormState
......@@ -38,7 +40,6 @@ import type {
BlastParameterName,
SequenceType
} from 'src/content/app/tools/blast/types/blastSettings';
import type { BlastSubmission } from 'src/content/app/tools/blast/state/blast-results/blastResultsSlice';
export type PayloadParams = {
species: Species[];
......@@ -49,51 +50,27 @@ export type PayloadParams = {
};
};
type BlastSubmissionResponse = {
submissionId: string;
submission: BlastSubmission;
};
const BlastJobSubmit = () => {
const { sequences } = useBlastInputSequences();
const selectedSpeciesIds = useAppSelector(getSelectedSpeciesIds);
const dispatch = useAppDispatch();
const [submitBlast] = useSubmitBlastMutation();
const navigate = useNavigate();
const isDisabled = !isBlastFormValid(selectedSpeciesIds, sequences);
const blastFormData = useAppSelector(getBlastFormData);
const onBlastSubmit = async () => {
const onBlastSubmit = () => {
const payload = createBlastSubmissionData(blastFormData);
const submission = dispatch(submitBlast.initiate(payload));
submission.then((response) => {
submission.reset(); // prevent indefinite caching of subscription result
if ('data' in response) {
onSubmitSuccess(response.data);
}
});
};
const onSubmitSuccess = (response: BlastSubmissionResponse) => {
// TODO: change the temporary implementation of this function with a more permanent one
const firstJobId = response.submission.results[0].jobId;
if (!firstJobId) {
return;
}
const resultPageUrl = `https://wwwdev.ebi.ac.uk/Tools/services/web/toolresult.ebi?jobId=${firstJobId}`;
const resultTab = window.open(resultPageUrl, '_blank');
if (resultTab !== null) {
resultTab.focus();
}
const submission = submitBlast(payload);
submission.then(() => submission.reset());
navigate(urlFor.blastUnviewedSubmissions());
};
return (
<LoadingButton onClick={onBlastSubmit} isDisabled={isDisabled}>
<PrimaryButton onClick={onBlastSubmit} isDisabled={isDisabled}>
Run
</LoadingButton>
</PrimaryButton>
);
};
......
.blastViewsNavigation {
display: grid;
grid-template-columns: [left] auto [right] 1fr;
height: 74px; // this isn't ideal; but 100% won't work because the parent container only has a min-height
align-items: center;
max-width: 1810px; // TODO: this is max width of the two-column blast form; set it to a constant
padding-right: 20px; // same as in the app bar above
}
.title {
font-size: 16px; // TODO: this is a title repeated in another component; extract as a common variable
font-weight: bold;
margin: 0;
}
.wrapperLeft {
display: flex;
column-gap: 46px;
align-items: center;
}
.wrapperRight {
display: flex;
column-gap: 12px;
}
.leftColumn {
grid-column: left;
}
.rightColumn {
grid-column: right;
justify-self: end;
}
/**
* See the NOTICE file distributed with this work for additional information
* regarding copyright ownership.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React from 'react';
import * as urlFor from 'src/shared/helpers/urlHelper';
import ButtonLink from 'src/shared/components/button-link/ButtonLink';
import styles from './BlastViewsNavigation.scss';
const BlastViewsNavigation = () => {
return (
<div className={styles.blastViewsNavigation}>
<div className={styles.leftColumn}>
<div className={styles.wrapperLeft}>
<h1 className={styles.title}>Blast</h1>
<ButtonLink to={urlFor.blastForm()} end={true}>
New job
</ButtonLink>
</div>
</div>
<div className={styles.rightColumn}>
<div className={styles.wrapperRight}>
<ButtonLink to={urlFor.blastUnviewedSubmissions()}>
Unviewed jobs
</ButtonLink>
<ButtonLink to={urlFor.blastSubmissionsList()}>Jobs list</ButtonLink>
</div>
</div>
</div>
);
};
export default BlastViewsNavigation;
......@@ -100,5 +100,5 @@ const blastApiSlice = restApiSlice.injectEndpoints({
})
});
export const { useBlastConfigQuery } = blastApiSlice;
export const { useBlastConfigQuery, useSubmitBlastMutation } = blastApiSlice;
export const { submitBlast } = blastApiSlice.endpoints;
......@@ -22,7 +22,7 @@ import useMediaQuery from 'src/shared/hooks/useMediaQuery';
import { getStep } from 'src/content/app/tools/blast/state/blast-form/blastFormSelectors';
import ToolsAppBar from 'src/content/app/tools/shared/components/tools-app-bar/ToolsAppBar';
import BlastAppBar from 'src/content/app/tools/blast/components/blast-app-bar/BlastAppBar';
import ToolsTopBar from 'src/content/app/tools/shared/components/tools-top-bar/ToolsTopBar';
import BlastInputSequencesHeader from 'src/content/app/tools/blast/components/blast-input-sequences/BlastInputSequencesHeader';
......@@ -43,7 +43,7 @@ const BlastForm = () => {
return (
<div className={styles.container}>
<ToolsAppBar />
<BlastAppBar view="blast-form" />
<ToolsTopBar>
<BlastSettings config={config} />
</ToolsTopBar>
......
......@@ -16,17 +16,20 @@
import React from 'react';
import ToolsAppBar from 'src/content/app/tools/shared/components/tools-app-bar/ToolsAppBar';
import BlastAppBar from 'src/content/app/tools/blast/components/blast-app-bar/BlastAppBar';
import ToolsTopBar from 'src/content/app/tools/shared/components/tools-top-bar/ToolsTopBar';
import BlastViewsNavigation from 'src/content/app/tools/blast/components/blast-views-navigation/BlastViewsNavigation';
const BlastResults = () => {
const BlastSubmissions = () => {
return (
<div>
<ToolsAppBar />
<ToolsTopBar>stuff</ToolsTopBar>
<BlastAppBar view="submissions-list" />
<ToolsTopBar>
<BlastViewsNavigation />
</ToolsTopBar>
<div>This is Blast results view</div>
</div>
);
};
export default BlastResults;
export default BlastSubmissions;
/**
* See the NOTICE file distributed with this work for additional information
* regarding copyright ownership.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React from 'react';
import BlastAppBar from 'src/content/app/tools/blast/components/blast-app-bar/BlastAppBar';
import ToolsTopBar from 'src/content/app/tools/shared/components/tools-top-bar/ToolsTopBar';
import BlastViewsNavigation from 'src/content/app/tools/blast/components/blast-views-navigation/BlastViewsNavigation';
const BlastUnviewedSubmission = () => {
return (
<div>
<BlastAppBar view="unviewed-submissions" />
<ToolsTopBar>
<BlastViewsNavigation />
</ToolsTopBar>
<Main />
</div>
);
};
const Main = () => {
return <main>The submissions list will go here</main>;
};
export default BlastUnviewedSubmission;
......@@ -94,6 +94,12 @@ export const entityViewer = (params?: EntityViewerUrlParams) => {
return query ? `${path}?${query}` : path;
};
export const blastForm = () => '/blast';
export const blastUnviewedSubmissions = () => '/blast/unviewed-submissions';
export const blastSubmissionsList = () => '/blast/submissions';
type RefgetUrlParams = {
checksum: string;
start?: number;
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment