diff options
author | Pascal Mugnier <pascal.mugnier@sonarsource.com> | 2018-04-10 13:47:42 +0200 |
---|---|---|
committer | SonarTech <sonartech@sonarsource.com> | 2018-04-10 20:20:54 +0200 |
commit | 50766c5a770d31733a4f5f0f29a3cbf663473d77 (patch) | |
tree | 6aa335d1afd44ba45e43e6ccc254f192ad69d4d0 /server/sonar-web/src/main/js/apps | |
parent | f239ffffd3818fec2aa2d74b12875355324df73e (diff) | |
download | sonarqube-50766c5a770d31733a4f5f0f29a3cbf663473d77.tar.gz sonarqube-50766c5a770d31733a4f5f0f29a3cbf663473d77.zip |
SONAR-10133 Full-width banner to prompt restart after plugin change (#102)
Diffstat (limited to 'server/sonar-web/src/main/js/apps')
5 files changed, 17 insertions, 301 deletions
diff --git a/server/sonar-web/src/main/js/apps/marketplace/App.tsx b/server/sonar-web/src/main/js/apps/marketplace/App.tsx index 2dcf58ab2e0..9446bfe471c 100644 --- a/server/sonar-web/src/main/js/apps/marketplace/App.tsx +++ b/server/sonar-web/src/main/js/apps/marketplace/App.tsx @@ -24,17 +24,15 @@ import Helmet from 'react-helmet'; import Header from './Header'; import EditionBoxes from './EditionBoxes'; import Footer from './Footer'; -import PendingActions from './PendingActions'; import PluginsList from './PluginsList'; import Search from './Search'; import { filterPlugins, parseQuery, Query, serializeQuery } from './utils'; import { getAvailablePlugins, getInstalledPluginsWithUpdates, - getPendingPlugins, getPluginUpdates, Plugin, - PluginPending, + PluginPendingResult, getInstalledPlugins } from '../../api/plugins'; import { Edition, EditionStatus } from '../../api/marketplace'; @@ -46,20 +44,17 @@ export interface Props { editions?: Edition[]; editionsReadOnly: boolean; editionStatus?: EditionStatus; + fetchPendingPlugins: () => void; loadingEditions: boolean; location: { pathname: string; query: RawQuery }; + pendingPlugins: PluginPendingResult; standaloneMode: boolean; - updateCenterActive: boolean; setEditionStatus: (editionStatus: EditionStatus) => void; + updateCenterActive: boolean; } interface State { loadingPlugins: boolean; - pending: { - installing: PluginPending[]; - updating: PluginPending[]; - removing: PluginPending[]; - }; plugins: Plugin[]; } @@ -74,18 +69,13 @@ export default class App extends React.PureComponent<Props, State> { super(props); this.state = { loadingPlugins: true, - pending: { - installing: [], - updating: [], - removing: [] - }, plugins: [] }; } componentDidMount() { this.mounted = true; - this.fetchPendingPlugins(); + this.props.fetchPendingPlugins(); this.fetchQueryPlugins(); } @@ -127,16 +117,6 @@ export default class App extends React.PureComponent<Props, State> { ); }; - fetchPendingPlugins = () => - getPendingPlugins().then( - pending => { - if (this.mounted) { - this.setState({ pending }); - } - }, - () => {} - ); - updateQuery = (newQuery: Partial<Query>) => { const query = serializeQuery({ ...parseQuery(this.props.location.query), ...newQuery }); this.context.router.push({ pathname: this.props.location.pathname, query }); @@ -149,19 +129,14 @@ export default class App extends React.PureComponent<Props, State> { }; render() { - const { editions, editionStatus, standaloneMode } = this.props; - const { loadingPlugins, plugins, pending } = this.state; + const { editions, editionStatus, standaloneMode, pendingPlugins } = this.props; + const { loadingPlugins, plugins } = this.state; const query = parseQuery(this.props.location.query); const filteredPlugins = query.search ? filterPlugins(plugins, query.search) : plugins; return ( <div className="page page-limited" id="marketplace-page"> <Helmet title={translate('marketplace.page')} /> - <div className="page-notifs"> - {standaloneMode && ( - <PendingActions pending={pending} refreshPending={this.fetchPendingPlugins} /> - )} - </div> <Header /> <EditionBoxes canInstall={standaloneMode && !this.props.editionsReadOnly} @@ -180,10 +155,10 @@ export default class App extends React.PureComponent<Props, State> { {loadingPlugins && <i className="spinner" />} {!loadingPlugins && ( <PluginsList - pending={pending} + pending={pendingPlugins} plugins={filteredPlugins} readOnly={!standaloneMode} - refreshPending={this.fetchPendingPlugins} + refreshPending={this.props.fetchPendingPlugins} /> )} {!loadingPlugins && <Footer total={filteredPlugins.length} />} diff --git a/server/sonar-web/src/main/js/apps/marketplace/AppContainer.tsx b/server/sonar-web/src/main/js/apps/marketplace/AppContainer.tsx index 4f7676767ba..73611432c0b 100644 --- a/server/sonar-web/src/main/js/apps/marketplace/AppContainer.tsx +++ b/server/sonar-web/src/main/js/apps/marketplace/AppContainer.tsx @@ -24,11 +24,13 @@ import { getGlobalSettingValue, getMarketplaceState, getMarketplaceEditions, - getMarketplaceEditionStatus + getMarketplaceEditionStatus, + getMarketplacePendingPlugins } from '../../store/rootReducer'; import { Edition, EditionStatus } from '../../api/marketplace'; -import { setEditionStatus } from '../../store/marketplace/actions'; +import { setEditionStatus, fetchPendingPlugins } from '../../store/marketplace/actions'; import { RawQuery } from '../../helpers/query'; +import { PluginPendingResult } from '../../api/plugins'; interface OwnProps { location: { pathname: string; query: RawQuery }; @@ -39,12 +41,14 @@ interface StateToProps { editionsReadOnly: boolean; editionStatus?: EditionStatus; loadingEditions: boolean; + pendingPlugins: PluginPendingResult; standaloneMode: boolean; updateCenterActive: boolean; } interface DispatchToProps { setEditionStatus: (editionStatus: EditionStatus) => void; + fetchPendingPlugins: () => void; } const mapStateToProps = (state: any) => ({ @@ -52,12 +56,13 @@ const mapStateToProps = (state: any) => ({ editionsReadOnly: getMarketplaceState(state).readOnly, editionStatus: getMarketplaceEditionStatus(state), loadingEditions: getMarketplaceState(state).loading, + pendingPlugins: getMarketplacePendingPlugins(state), standaloneMode: getAppState(state).standalone, updateCenterActive: (getGlobalSettingValue(state, 'sonar.updatecenter.activate') || {}).value === 'true' }); -const mapDispatchToProps = { setEditionStatus }; +const mapDispatchToProps = { setEditionStatus, fetchPendingPlugins }; export default connect<StateToProps, DispatchToProps, OwnProps>( mapStateToProps, diff --git a/server/sonar-web/src/main/js/apps/marketplace/PendingActions.tsx b/server/sonar-web/src/main/js/apps/marketplace/PendingActions.tsx deleted file mode 100644 index d392d5efcc3..00000000000 --- a/server/sonar-web/src/main/js/apps/marketplace/PendingActions.tsx +++ /dev/null @@ -1,108 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2018 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 { FormattedMessage } from 'react-intl'; -import RestartForm from '../../components/common/RestartForm'; -import { cancelPendingPlugins, PluginPending } from '../../api/plugins'; -import { Button } from '../../components/ui/buttons'; -import { translate } from '../../helpers/l10n'; - -interface Props { - pending: { - installing: PluginPending[]; - updating: PluginPending[]; - removing: PluginPending[]; - }; - refreshPending: () => void; -} - -interface State { - openRestart: boolean; -} - -export default class PendingActions extends React.PureComponent<Props, State> { - state: State = { openRestart: false }; - - handleOpenRestart = () => { - this.setState({ openRestart: true }); - }; - - hanleCloseRestart = () => { - this.setState({ openRestart: false }); - }; - - handleRevert = () => { - cancelPendingPlugins().then(this.props.refreshPending, () => {}); - }; - - render() { - const { installing, updating, removing } = this.props.pending; - const hasPendingActions = installing.length || updating.length || removing.length; - if (!hasPendingActions) { - return null; - } - - return ( - <div className="js-pending alert alert-warning"> - <div className="display-inline-block"> - <p>{translate('marketplace.sonarqube_needs_to_be_restarted_to')}</p> - <ul className="list-styled spacer-top"> - {installing.length > 0 && ( - <li> - <FormattedMessage - defaultMessage={translate('marketplace.install_x_plugins')} - id="marketplace.install_x_plugins" - values={{ nb: <strong>{installing.length}</strong> }} - /> - </li> - )} - {updating.length > 0 && ( - <li> - <FormattedMessage - defaultMessage={translate('marketplace.update_x_plugins')} - id="marketplace.update_x_plugins" - values={{ nb: <strong>{updating.length}</strong> }} - /> - </li> - )} - {removing.length > 0 && ( - <li> - <FormattedMessage - defaultMessage={translate('marketplace.uninstall_x_plugins')} - id="marketplace.uninstall_x_plugins" - values={{ nb: <strong>{removing.length}</strong> }} - /> - </li> - )} - </ul> - </div> - <div className="pull-right"> - <Button className="js-restart little-spacer-right" onClick={this.handleOpenRestart}> - {translate('marketplace.restart')} - </Button> - <Button className="js-cancel-all button-red" onClick={this.handleRevert}> - {translate('marketplace.revert')} - </Button> - </div> - {this.state.openRestart && <RestartForm onClose={this.hanleCloseRestart} />} - </div> - ); - } -} diff --git a/server/sonar-web/src/main/js/apps/marketplace/__tests__/PendingActions-test.tsx b/server/sonar-web/src/main/js/apps/marketplace/__tests__/PendingActions-test.tsx deleted file mode 100644 index ff023270fac..00000000000 --- a/server/sonar-web/src/main/js/apps/marketplace/__tests__/PendingActions-test.tsx +++ /dev/null @@ -1,95 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2018 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. - */ -/* eslint-disable import/order */ -import * as React from 'react'; -import { shallow } from 'enzyme'; -import { click } from '../../../helpers/testUtils'; -import PendingActions from '../PendingActions'; - -jest.mock('../../../api/plugins', () => ({ - cancelPendingPlugins: jest.fn(() => Promise.resolve()) -})); - -const cancelPendingPlugins = require('../../../api/plugins').cancelPendingPlugins as jest.Mock<any>; - -beforeEach(() => { - cancelPendingPlugins.mockClear(); -}); - -it('should display pending actions', () => { - expect(getWrapper()).toMatchSnapshot(); -}); - -it('should not display anything', () => { - expect(getWrapper({ pending: { installing: [], updating: [], removing: [] } }).type()).toBeNull(); -}); - -it('should open the restart form', () => { - const wrapper = getWrapper(); - click(wrapper.find('.js-restart')); - expect(wrapper.find('RestartForm').exists()).toBeTruthy(); -}); - -it('should cancel all pending and refresh them', async () => { - const refreshPending = jest.fn(); - const wrapper = getWrapper({ refreshPending }); - click(wrapper.find('.js-cancel-all')); - expect(cancelPendingPlugins).toHaveBeenCalled(); - await new Promise(setImmediate); - - expect(refreshPending).toHaveBeenCalled(); -}); - -function getWrapper(props = {}) { - return shallow( - <PendingActions - pending={{ - installing: [ - { - key: 'foo', - name: 'Foo', - description: 'foo description', - version: 'fooversion', - implementationBuild: 'foobuild' - }, - { - key: 'bar', - name: 'Bar', - description: 'bar description', - version: 'barversion', - implementationBuild: 'barbuild' - } - ], - updating: [], - removing: [ - { - key: 'baz', - name: 'Baz', - description: 'baz description', - version: 'bazversion', - implementationBuild: 'bazbuild' - } - ] - }} - refreshPending={() => {}} - {...props} - /> - ); -} diff --git a/server/sonar-web/src/main/js/apps/marketplace/__tests__/__snapshots__/PendingActions-test.tsx.snap b/server/sonar-web/src/main/js/apps/marketplace/__tests__/__snapshots__/PendingActions-test.tsx.snap deleted file mode 100644 index c8688596746..00000000000 --- a/server/sonar-web/src/main/js/apps/marketplace/__tests__/__snapshots__/PendingActions-test.tsx.snap +++ /dev/null @@ -1,61 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`should display pending actions 1`] = ` -<div - className="js-pending alert alert-warning" -> - <div - className="display-inline-block" - > - <p> - marketplace.sonarqube_needs_to_be_restarted_to - </p> - <ul - className="list-styled spacer-top" - > - <li> - <FormattedMessage - defaultMessage="marketplace.install_x_plugins" - id="marketplace.install_x_plugins" - values={ - Object { - "nb": <strong> - 2 - </strong>, - } - } - /> - </li> - <li> - <FormattedMessage - defaultMessage="marketplace.uninstall_x_plugins" - id="marketplace.uninstall_x_plugins" - values={ - Object { - "nb": <strong> - 1 - </strong>, - } - } - /> - </li> - </ul> - </div> - <div - className="pull-right" - > - <Button - className="js-restart little-spacer-right" - onClick={[Function]} - > - marketplace.restart - </Button> - <Button - className="js-cancel-all button-red" - onClick={[Function]} - > - marketplace.revert - </Button> - </div> -</div> -`; |