Skip to content

Commit 5409775

Browse files
authored
Vep species selector (#1144)
1 parent 7c9bbaa commit 5409775

File tree

12 files changed

+435
-24
lines changed

12 files changed

+435
-24
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
.grid {
2+
display: grid;
3+
grid-template-rows: auto auto 1fr;
4+
}

src/content/app/tools/vep/VepPageContent.tsx

+22-18
Original file line numberDiff line numberDiff line change
@@ -19,38 +19,42 @@ import { Route, Routes } from 'react-router-dom';
1919
import VepAppBar from './components/vep-app-bar/VepAppBar';
2020
import VepTopBar from './components/vep-top-bar/VepTopBar';
2121
import VepForm from './views/vep-form/VepForm';
22+
import VepSpeciesSelector from './views/vep-species-selector/VepSpeciesSelector';
2223
import { NotFoundErrorScreen } from 'src/shared/components/error-screen';
2324

25+
import styles from './VepPageContent.module.css';
26+
2427
const VepPageContent = () => {
2528
return (
26-
<div>
29+
<div className={styles.grid}>
2730
<VepAppBar />
31+
<VepTopBar />
2832
<Main />
2933
</div>
3034
);
3135
};
3236

3337
const Main = () => {
3438
return (
35-
<div>
36-
<VepTopBar />
37-
38-
<Routes>
39-
<Route index={true} element={<VepForm />} />
39+
<Routes>
40+
<Route index={true} element={<VepForm />} />
41+
<Route
42+
path="species-selector"
43+
element={<VepSpeciesSelector />}
44+
/>
45+
<Route
46+
path="unviewed-submissions"
47+
element={<div>List of unviewed submissions</div>}
48+
/>
49+
<Route path="submissions">
50+
<Route index={true} element={<div>List of viewed submissions</div>} />
4051
<Route
41-
path="unviewed-submissions"
42-
element={<div>List of unviewed submissions</div>}
52+
path=":submissionId"
53+
element={<div>Results of a single VEP analysis</div>}
4354
/>
44-
<Route path="submissions">
45-
<Route index={true} element={<div>List of viewed submissions</div>} />
46-
<Route
47-
path=":submissionId"
48-
element={<div>Results of a single VEP analysis</div>}
49-
/>
50-
</Route>
51-
<Route path="*" element={<NotFoundErrorScreen />} />
52-
</Routes>
53-
</div>
55+
</Route>
56+
<Route path="*" element={<NotFoundErrorScreen />} />
57+
</Routes>
5458
);
5559
};
5660

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/**
2+
* See the NOTICE file distributed with this work for additional information
3+
* regarding copyright ownership.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import type { RootState } from 'src/store';
18+
19+
export const getSelectedSpecies = (state: RootState) =>
20+
state.vep.vepForm.selectedSpecies;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/**
2+
* See the NOTICE file distributed with this work for additional information
3+
* regarding copyright ownership.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
18+
19+
import type { CommittedItem } from 'src/content/app/species-selector/types/committedItem';
20+
21+
type VepSelectedSpecies = Omit<CommittedItem, 'isEnabled'>;
22+
23+
export type VepFormState = {
24+
selectedSpecies: VepSelectedSpecies | null;
25+
};
26+
27+
export const initialState: VepFormState = {
28+
selectedSpecies: null
29+
};
30+
31+
const vepFormSlice = createSlice({
32+
name: 'vep-form',
33+
initialState,
34+
reducers: {
35+
setSelectedSpecies: (
36+
state,
37+
action: PayloadAction<{ species: VepSelectedSpecies }>
38+
) => {
39+
state.selectedSpecies = action.payload.species;
40+
}
41+
}
42+
});
43+
44+
export const { setSelectedSpecies } = vepFormSlice.actions;
45+
46+
export default vepFormSlice.reducer;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/**
2+
* See the NOTICE file distributed with this work for additional information
3+
* regarding copyright ownership.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import { combineReducers } from 'redux';
18+
19+
import vepFormReducer from './vep-form/vepFormSlice';
20+
21+
export default combineReducers({
22+
vepForm: vepFormReducer
23+
});

src/content/app/tools/vep/views/vep-form/VepForm.tsx

+8-6
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@
1414
* limitations under the License.
1515
*/
1616

17+
import {
18+
VepFormSpecies,
19+
VepSpeciesSelectorNavButton
20+
} from './vep-form-species-section/VepFormSpeciesSection';
1721
import FormSection from 'src/content/app/tools/vep/components/form-section/FormSection';
1822
import PlusButton from 'src/shared/components/plus-button/PlusButton';
1923
import TextButton from 'src/shared/components/text-button/TextButton';
@@ -38,12 +42,10 @@ const VepForm = () => {
3842
<FormSection className={styles.formSection}>
3943
<div className={styles.topFormSectionRegularGrid}>
4044
<div className={styles.topFormSectionName}>Species</div>
41-
<div className={styles.topFormSectionMain}>
42-
<TextButton>Select a species / assembly</TextButton>
43-
</div>
44-
<div className={styles.topFormSectionToggle}>
45-
<PlusButton />
46-
</div>
45+
<VepFormSpecies className={styles.topFormSectionMain} />
46+
<VepSpeciesSelectorNavButton
47+
className={styles.topFormSectionToggle}
48+
/>
4749
</div>
4850
</FormSection>
4951
<FormSection className={styles.formSection}>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.commonName {
2+
font-weight: var(--font-weight-bold);
3+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/**
2+
* See the NOTICE file distributed with this work for additional information
3+
* regarding copyright ownership.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import { Link } from 'react-router-dom';
18+
19+
import * as urlFor from 'src/shared/helpers/urlHelper';
20+
21+
import { useAppSelector } from 'src/store';
22+
23+
import { getSelectedSpecies } from 'src/content/app/tools/vep/state/vep-form/vepFormSelectors';
24+
25+
import {
26+
ScientificName,
27+
AssemblyName
28+
} from 'src/shared/components/species-name-parts';
29+
import PlusButton from 'src/shared/components/plus-button/PlusButton';
30+
import TextButton from 'src/shared/components/text-button/TextButton';
31+
32+
import styles from './VepFormSpeciesSection.module.css';
33+
34+
/**
35+
* TODO: Consider the following
36+
* The component below displays a species label after a species has been selected for VEP analysis.
37+
* - Is this going to be a recurring pattern of displaying species info outside of tables?
38+
* - Does it need to show the type information for the selected assembly (is reference, population)?
39+
* - Does it need to consider user's selection of species info display (as chosen in species manager)
40+
* The answer may justify exxtracting this into a reusable component.
41+
*/
42+
43+
const vepSpeciesSelectorUrl = urlFor.vepSpeciesSelector();
44+
45+
export const VepFormSpecies = (props: { className?: string }) => {
46+
const selectedSpecies = useAppSelector(getSelectedSpecies);
47+
48+
if (!selectedSpecies) {
49+
return <Link to={vepSpeciesSelectorUrl}>Select a species / assembly</Link>;
50+
}
51+
52+
return (
53+
<div className={props.className}>
54+
{selectedSpecies.common_name && (
55+
<span className={styles.commonName}>{selectedSpecies.common_name}</span>
56+
)}
57+
{!selectedSpecies.common_name && <ScientificName {...selectedSpecies} />}{' '}
58+
<AssemblyName {...selectedSpecies} />
59+
</div>
60+
);
61+
};
62+
63+
export const VepSpeciesSelectorNavButton = (props: { className?: string }) => {
64+
const selectedSpecies = useAppSelector(getSelectedSpecies);
65+
66+
return (
67+
<Link to={vepSpeciesSelectorUrl} className={props.className}>
68+
{!selectedSpecies ? <PlusButton /> : <TextButton>Change</TextButton>}
69+
</Link>
70+
);
71+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
.grid {
2+
display: grid;
3+
grid-template-rows: auto auto 1fr;
4+
margin-left: var(--global-padding-left);
5+
height: 100%;
6+
}
7+
8+
.tableContainer {
9+
overflow: auto;
10+
padding-bottom: var(--global-padding-bottom);
11+
}
12+
13+
/* Question to consider: is the top section of a species selector
14+
a sufficiently stable and repeatable component
15+
to be extracted into a shared component?
16+
Note: so far, the top section in vep species selector
17+
does not have a filter element in the design.
18+
We will see if we will need to bring it in.
19+
*/
20+
.topSection {
21+
display: flex;
22+
flex-direction: column;
23+
row-gap: 32px;
24+
}
25+
26+
.loader {
27+
margin-top: 40px;
28+
margin-left: 60px;
29+
}

0 commit comments

Comments
 (0)