-
Notifications
You must be signed in to change notification settings - Fork 5
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
Decoupling tabs in the experiment page and display them after validating #375
base: develop
Are you sure you want to change the base?
Changes from 16 commits
f0677d3
a21e6ee
7a48932
a071970
aa55ced
585972d
25ef163
13e7ee2
532389c
1f9aa46
2c05e1a
ca946ac
ea37496
fcaab7c
05d2fb2
35bceb9
99088cf
b1cf7e5
5eaf719
dcf06f8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,6 @@ | ||
import React from 'react' | ||
import PropTypes from 'prop-types' | ||
import { BrowserRouter, Route, Redirect, Switch, NavLink, withRouter } from 'react-router-dom' | ||
import {BrowserRouter, Route, Redirect, Switch, NavLink, withRouter} from 'react-router-dom' | ||
|
||
import URI from 'urijs' | ||
|
||
|
@@ -10,130 +10,169 @@ import SupplementaryInformationRoute from './SupplementaryInformationRoute' | |
import DownloadsRoute from './DownloadsRoute' | ||
|
||
const RoutePropTypes = { | ||
match: PropTypes.object.isRequired, | ||
location: PropTypes.object.isRequired, | ||
history: PropTypes.object.isRequired | ||
match: PropTypes.object.isRequired, | ||
location: PropTypes.object.isRequired, | ||
history: PropTypes.object.isRequired | ||
} | ||
|
||
const TabCommonPropTypes = { | ||
atlasUrl: PropTypes.string.isRequired, | ||
experimentAccession: PropTypes.string.isRequired, | ||
species: PropTypes.string.isRequired, | ||
accessKey: PropTypes.string, | ||
resourcesUrl: PropTypes.string | ||
atlasUrl: PropTypes.string.isRequired, | ||
experimentAccession: PropTypes.string.isRequired, | ||
species: PropTypes.string.isRequired, | ||
accessKey: PropTypes.string, | ||
resourcesUrl: PropTypes.string | ||
} | ||
|
||
// What component each tab type should render, coupled to ExperimentController.java | ||
const tabTypeComponent = { | ||
'results' : TSnePlotViewRoute, | ||
'experiment-design' : ExperimentDesignRoute, | ||
'supplementary-information' : SupplementaryInformationRoute, | ||
'downloads' : DownloadsRoute | ||
} | ||
let tabTypeComponent = [] | ||
|
||
const TopRibbon = ({tabs, routeProps}) => | ||
<ul className={`tabs`}> | ||
{ | ||
tabs.map((tab) => | ||
<li title={tab.name} key={tab.type} className={`tabs-title`}> | ||
<NavLink to={{pathname:`/${tab.type}`, search: routeProps.location.search, hash: routeProps.location.hash}} | ||
activeClassName={`active`}> | ||
{tab.name} | ||
</NavLink> | ||
</li> | ||
)} | ||
</ul> | ||
function isThisTabType(tab, tabType) { | ||
return tab.type === tabType; | ||
} | ||
|
||
const isObjectEmpty = (objectName) => { | ||
return ( | ||
objectName && | ||
Object.keys(objectName).length === 0 && | ||
objectName.constructor === Object | ||
); | ||
}; | ||
|
||
const tabTypes = [ | ||
{ type: 'results', key: 'ks', component: TSnePlotViewRoute, optionsKey: 'plotTypesAndOptions' }, | ||
{ type: 'experiment-design', key: 'table.data', component: ExperimentDesignRoute }, | ||
{ type: 'supplementary-information', key: 'sections', component: SupplementaryInformationRoute }, | ||
{ type: 'resources', key: 'data', component: DownloadsRoute } | ||
]; | ||
|
||
const getNestedProperty = (obj, path) => path.split('.').reduce((acc, key) => acc?.[key], obj); | ||
|
||
const isNonEmptyArray = (value) => !isObjectEmpty(value) && Array.isArray(value); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we move this code: There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is one of the validation checks before displaying a relevant tab. We are getting empty for some experiments. |
||
|
||
const enableExperimentPageTab = (tab) => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This whole function is a bit too complex for me and hard to understand what is happening inside this function. It is really hard to follow. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok, we will. Thanks. We will do Monday. I will try to improve variable naming. |
||
for (let { type, key, component, optionsKey } of tabTypes) { | ||
if (isThisTabType(tab, type)) { | ||
const propValue = getNestedProperty(tab.props, key); | ||
const optionsValue = optionsKey ? getNestedProperty(tab.props, optionsKey) : true; | ||
|
||
if (isNonEmptyArray(propValue) && (!optionsKey || !isObjectEmpty(optionsValue))) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What happens in |
||
tabTypeComponent.push({ [type]: component }); | ||
return tab.name; | ||
} | ||
} | ||
} | ||
return null; | ||
}; | ||
|
||
const TopRibbon = ({tabs, routeProps}) => { | ||
tabTypeComponent = []; | ||
return <ul className={`tabs`}> | ||
{ | ||
tabs.map((tab) => ( | ||
<li title={tab.name} key={tab.type} className={`tabs-title`}> | ||
<NavLink to={{ | ||
pathname: `/${tab.type}`, | ||
search: routeProps.location.search, | ||
hash: routeProps.location.hash | ||
}} | ||
activeClassName={`active`}> | ||
{ enableExperimentPageTab(tab) } | ||
</NavLink> | ||
</li>)) | ||
} | ||
</ul> | ||
} | ||
TopRibbon.propTypes = { | ||
tabs: PropTypes.arrayOf(PropTypes.shape({ | ||
type: PropTypes.string.isRequired, | ||
name: PropTypes.string.isRequired, | ||
props: PropTypes.object | ||
})).isRequired, | ||
routeProps: PropTypes.shape(RoutePropTypes) | ||
tabs: PropTypes.arrayOf(PropTypes.shape({ | ||
type: PropTypes.string.isRequired, | ||
name: PropTypes.string.isRequired, | ||
props: PropTypes.object | ||
})).isRequired, | ||
routeProps: PropTypes.shape(RoutePropTypes) | ||
} | ||
|
||
|
||
const TabContent = ({type, tabProps, commonProps, routeProps}) => { | ||
// Pass in the search from location | ||
const Tab = tabTypeComponent[type] | ||
// Pass in the search from location | ||
const Tab = tabTypeComponent[type] | ||
|
||
return ( | ||
Tab ? <Tab {...tabProps} {...commonProps} {...routeProps}/> : null | ||
) | ||
return ( | ||
Tab ? <Tab {...tabProps} {...commonProps} {...routeProps}/> : null | ||
) | ||
} | ||
|
||
TabContent.propTypes = { | ||
type: PropTypes.string.isRequired, | ||
tabProps: PropTypes.object, | ||
commonProps: PropTypes.shape(TabCommonPropTypes), | ||
routeProps: PropTypes.shape(RoutePropTypes) | ||
type: PropTypes.string.isRequired, | ||
tabProps: PropTypes.object, | ||
commonProps: PropTypes.shape(TabCommonPropTypes), | ||
routeProps: PropTypes.shape(RoutePropTypes) | ||
} | ||
|
||
const RedirectWithSearchAndHash = (props) => | ||
<Redirect to={{ pathname: props.pathname, search: props.location.search, hash: props.location.hash}} /> | ||
<Redirect to={{pathname: props.pathname, search: props.location.search, hash: props.location.hash}}/> | ||
|
||
RedirectWithSearchAndHash.propTypes = { | ||
pathname: PropTypes.string.isRequired, | ||
location: PropTypes.shape({ | ||
search: PropTypes.string.isRequired, | ||
hash: PropTypes.string.isRequired | ||
}).isRequired | ||
pathname: PropTypes.string.isRequired, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree with you, I will try to do that. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
This one is fixed. Please have a look. |
||
location: PropTypes.shape({ | ||
search: PropTypes.string.isRequired, | ||
hash: PropTypes.string.isRequired | ||
}).isRequired | ||
} | ||
|
||
const RedirectWithLocation = withRouter(RedirectWithSearchAndHash) | ||
|
||
const ExperimentPageRouter = ({atlasUrl, resourcesUrl, experimentAccession, species, accessKey, tabs}) => { | ||
const tabCommonProps = { | ||
atlasUrl, | ||
resourcesUrl, | ||
experimentAccession, | ||
species, | ||
accessKey | ||
} | ||
|
||
return ( | ||
<BrowserRouter | ||
basename={URI(`experiments/${experimentAccession}`, URI(atlasUrl).path()).toString()}> | ||
<div> | ||
<Route | ||
path={`/`} | ||
render={ | ||
(routeProps) => | ||
<TopRibbon | ||
tabs={tabs} | ||
routeProps={routeProps} /> | ||
} /> | ||
<Switch> | ||
{ | ||
tabs.map((tab) => | ||
<Route | ||
key={tab.type} | ||
path={`/${tab.type}`} | ||
render={ | ||
(routeProps) => | ||
<TabContent | ||
type={tab.type} | ||
tabProps={tab.props} | ||
commonProps={tabCommonProps} | ||
routeProps={routeProps} /> | ||
} /> | ||
) | ||
} | ||
<RedirectWithLocation pathname={`/${tabs[0].type}`} /> | ||
</Switch> | ||
</div> | ||
</BrowserRouter> | ||
) | ||
|
||
const tabCommonProps = { | ||
atlasUrl, | ||
resourcesUrl, | ||
experimentAccession, | ||
species, | ||
accessKey | ||
} | ||
|
||
return ( | ||
<BrowserRouter | ||
basename={URI(`experiments/${experimentAccession}`, URI(atlasUrl).path()).toString()}> | ||
<div> | ||
<Route | ||
path={`/`} | ||
render={ | ||
(routeProps) => | ||
<TopRibbon | ||
tabs={tabs} | ||
routeProps={routeProps}/> | ||
}/> | ||
<Switch> | ||
{ | ||
tabs.map((tab) => | ||
<Route | ||
key={tab.type} | ||
path={`/${tab.type}`} | ||
render={ | ||
(routeProps) => | ||
<TabContent | ||
type={tab.type} | ||
tabProps={tab.props} | ||
commonProps={tabCommonProps} | ||
routeProps={routeProps}/> | ||
}/> | ||
) | ||
} | ||
<RedirectWithLocation pathname={`/${tabs[0].type}`}/> | ||
</Switch> | ||
</div> | ||
</BrowserRouter> | ||
) | ||
} | ||
|
||
ExperimentPageRouter.propTypes = { | ||
...TabCommonPropTypes, | ||
tabs: PropTypes.arrayOf(PropTypes.shape({ | ||
type: PropTypes.string.isRequired, | ||
name: PropTypes.string.isRequired, | ||
props: PropTypes.object.isRequired | ||
})).isRequired | ||
...TabCommonPropTypes, | ||
tabs: PropTypes.arrayOf(PropTypes.shape({ | ||
type: PropTypes.string.isRequired, | ||
name: PropTypes.string.isRequired, | ||
props: PropTypes.object.isRequired | ||
})).isRequired | ||
} | ||
|
||
export default ExperimentPageRouter |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To clean the code and make this file smaller, I would externalise all the
proptypes
to another file.