@@ -21,7 +21,6 @@ import * as React from 'react'; | |||
import { WithRouterProps } from 'react-router'; | |||
import Helmet from 'react-helmet'; | |||
import Details from './Details'; | |||
import Intro from './Intro'; | |||
import List from './List'; | |||
import ListHeader from './ListHeader'; | |||
import DeferredSpinner from '../../../components/common/DeferredSpinner'; | |||
@@ -60,6 +59,12 @@ class App extends React.PureComponent<Props, State> { | |||
addSideBarClass(); | |||
} | |||
componentDidUpdate(prevProps: Props) { | |||
if (prevProps.params.id !== undefined && this.props.params.id === undefined) { | |||
this.openDefault(this.state.qualityGates); | |||
} | |||
} | |||
componentWillUnmount() { | |||
this.mounted = false; | |||
removeWhitePageClass(); | |||
@@ -73,10 +78,8 @@ class App extends React.PureComponent<Props, State> { | |||
if (this.mounted) { | |||
this.setState({ canCreate: actions.create, loading: false, qualityGates }); | |||
if (qualityGates && qualityGates.length === 1 && !actions.create) { | |||
this.props.router.replace( | |||
getQualityGateUrl(String(qualityGates[0].id), organization && organization.key) | |||
); | |||
if (!this.props.params.id) { | |||
this.openDefault(qualityGates); | |||
} | |||
} | |||
}, | |||
@@ -88,6 +91,14 @@ class App extends React.PureComponent<Props, State> { | |||
); | |||
}; | |||
openDefault(qualityGates: T.QualityGate[]) { | |||
const defaultQualityGate = qualityGates.find(gate => Boolean(gate.isDefault))!; | |||
const { organization } = this.props; | |||
this.props.router.replace( | |||
getQualityGateUrl(String(defaultQualityGate.id), organization && organization.key) | |||
); | |||
} | |||
handleSetDefault = (qualityGate: T.QualityGate) => { | |||
this.setState(({ qualityGates }) => { | |||
return { | |||
@@ -101,31 +112,8 @@ class App extends React.PureComponent<Props, State> { | |||
}); | |||
}; | |||
renderContent() { | |||
const { id } = this.props.params; | |||
const organizationKey = this.props.organization && this.props.organization.key; | |||
if (id !== undefined) { | |||
return ( | |||
<Details | |||
id={id} | |||
onSetDefault={this.handleSetDefault} | |||
organization={organizationKey} | |||
qualityGates={this.state.qualityGates} | |||
refreshQualityGates={this.fetchQualityGates} | |||
/> | |||
); | |||
} else { | |||
return ( | |||
<Intro | |||
organization={organizationKey} | |||
qualityGates={this.state.qualityGates} | |||
router={this.props.router} | |||
/> | |||
); | |||
} | |||
} | |||
render() { | |||
const { id } = this.props.params; | |||
const { canCreate, qualityGates } = this.state; | |||
const defaultTitle = translate('quality_gates.page'); | |||
const organization = this.props.organization && this.props.organization.key; | |||
@@ -146,16 +134,24 @@ class App extends React.PureComponent<Props, State> { | |||
organization={organization} | |||
refreshQualityGates={this.fetchQualityGates} | |||
/> | |||
{qualityGates.length > 0 && ( | |||
<DeferredSpinner loading={this.state.loading}> | |||
<List organization={organization} qualityGates={qualityGates} /> | |||
)} | |||
</DeferredSpinner> | |||
</div> | |||
</div> | |||
</div> | |||
)} | |||
</ScreenPositionHelper> | |||
<DeferredSpinner loading={this.state.loading}>{this.renderContent()}</DeferredSpinner> | |||
{id !== undefined && ( | |||
<Details | |||
id={id} | |||
onSetDefault={this.handleSetDefault} | |||
organization={organization} | |||
qualityGates={this.state.qualityGates} | |||
refreshQualityGates={this.fetchQualityGates} | |||
/> | |||
)} | |||
</div> | |||
</> | |||
); |
@@ -26,6 +26,7 @@ import { getMetrics, Store } from '../../../store/rootReducer'; | |||
import { fetchMetrics } from '../../../store/rootActions'; | |||
import { fetchQualityGate } from '../../../api/quality-gates'; | |||
import { checkIfDefault, addCondition, replaceCondition, deleteCondition } from '../utils'; | |||
import DeferredSpinner from '../../../components/common/DeferredSpinner'; | |||
interface OwnProps { | |||
id: string; | |||
@@ -130,34 +131,34 @@ export class Details extends React.PureComponent<Props, State> { | |||
render() { | |||
const { organization, metrics, refreshQualityGates } = this.props; | |||
const { qualityGate } = this.state; | |||
if (!qualityGate) { | |||
return null; | |||
} | |||
const { loading, qualityGate } = this.state; | |||
return ( | |||
<> | |||
<Helmet title={qualityGate.name} /> | |||
<div className="layout-page-main"> | |||
<DetailsHeader | |||
onSetDefault={this.handleSetDefault} | |||
organization={organization} | |||
qualityGate={qualityGate} | |||
refreshItem={this.fetchDetails} | |||
refreshList={refreshQualityGates} | |||
/> | |||
<DetailsContent | |||
isDefault={checkIfDefault(qualityGate, this.props.qualityGates)} | |||
metrics={metrics} | |||
onAddCondition={this.handleAddCondition} | |||
onRemoveCondition={this.handleRemoveCondition} | |||
onSaveCondition={this.handleSaveCondition} | |||
organization={organization} | |||
qualityGate={qualityGate} | |||
/> | |||
</div> | |||
</> | |||
<div className="layout-page-main"> | |||
<DeferredSpinner loading={loading} timeout={200}> | |||
{qualityGate && ( | |||
<> | |||
<Helmet title={qualityGate.name} /> | |||
<DetailsHeader | |||
onSetDefault={this.handleSetDefault} | |||
organization={organization} | |||
qualityGate={qualityGate} | |||
refreshItem={this.fetchDetails} | |||
refreshList={refreshQualityGates} | |||
/> | |||
<DetailsContent | |||
isDefault={checkIfDefault(qualityGate, this.props.qualityGates)} | |||
metrics={metrics} | |||
onAddCondition={this.handleAddCondition} | |||
onRemoveCondition={this.handleRemoveCondition} | |||
onSaveCondition={this.handleSaveCondition} | |||
organization={organization} | |||
qualityGate={qualityGate} | |||
/> | |||
</> | |||
)} | |||
</DeferredSpinner> | |||
</div> | |||
); | |||
} | |||
} |
@@ -1,55 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2019 SonarSource SA | |||
* mailto:info AT sonarsource DOT com | |||
* | |||
* This program is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
import * as React from 'react'; | |||
import { InjectedRouter } from 'react-router'; | |||
import { translate } from '../../../helpers/l10n'; | |||
import { getQualityGateUrl } from '../../../helpers/urls'; | |||
interface Props { | |||
organization?: string; | |||
qualityGates: T.QualityGate[]; | |||
router: InjectedRouter; | |||
} | |||
export default class Intro extends React.PureComponent<Props> { | |||
componentDidMount() { | |||
const defaultQualityGate = this.props.qualityGates.find(qualityGate => | |||
Boolean(qualityGate.isDefault) | |||
); | |||
if (defaultQualityGate) { | |||
this.props.router.replace( | |||
getQualityGateUrl(String(defaultQualityGate.id), this.props.organization) | |||
); | |||
} | |||
} | |||
render() { | |||
return ( | |||
<div className="layout-page-main"> | |||
<div className="layout-page-main-inner"> | |||
<div className="search-navigator-intro markdown"> | |||
<p>{translate('quality_gates.intro.1')}</p> | |||
<p>{translate('quality_gates.intro.2')}</p> | |||
</div> | |||
</div> | |||
</div> | |||
); | |||
} | |||
} |
@@ -1,40 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2019 SonarSource SA | |||
* mailto:info AT sonarsource DOT com | |||
* | |||
* This program is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
import * as React from 'react'; | |||
import { shallow } from 'enzyme'; | |||
import Intro from '../Intro'; | |||
it('should redirect to detail of default quality gate', () => { | |||
const replace = jest.fn(); | |||
shallow( | |||
<Intro | |||
organization="foo" | |||
qualityGates={[{ id: 1, name: 'Bar', isDefault: true }]} | |||
router={{ replace } as any} | |||
/> | |||
); | |||
expect(replace).toHaveBeenCalledWith({ pathname: '/organizations/foo/quality_gates/show/1' }); | |||
}); | |||
it('should display the intro', () => { | |||
expect( | |||
shallow(<Intro organization="foo" qualityGates={[]} router={{} as any} />) | |||
).toMatchSnapshot(); | |||
}); |
@@ -1,22 +0,0 @@ | |||
// Jest Snapshot v1, https://goo.gl/fbAQLP | |||
exports[`should display the intro 1`] = ` | |||
<div | |||
className="layout-page-main" | |||
> | |||
<div | |||
className="layout-page-main-inner" | |||
> | |||
<div | |||
className="search-navigator-intro markdown" | |||
> | |||
<p> | |||
quality_gates.intro.1 | |||
</p> | |||
<p> | |||
quality_gates.intro.2 | |||
</p> | |||
</div> | |||
</div> | |||
</div> | |||
`; |