From: Siegfried Ehret <49895321+siegfried-ehret-sonarsource@users.noreply.github.com> Date: Mon, 29 Apr 2019 14:46:35 +0000 (+0200) Subject: SONARCLOUD-578 import all checkbox X-Git-Tag: 7.8~315 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=046757fd861b7465e00e79c7d00b0ac4165b911e;p=sonarqube.git SONARCLOUD-578 import all checkbox * Fix redux devtools warning * Add checkbox to import all repositories --- diff --git a/server/sonar-web/src/main/js/apps/create/project/RemoteRepositories.tsx b/server/sonar-web/src/main/js/apps/create/project/RemoteRepositories.tsx index 6451e98e024..e1a6e2736a1 100644 --- a/server/sonar-web/src/main/js/apps/create/project/RemoteRepositories.tsx +++ b/server/sonar-web/src/main/js/apps/create/project/RemoteRepositories.tsx @@ -19,15 +19,18 @@ */ import * as React from 'react'; import * as classNames from 'classnames'; +import { keyBy } from 'lodash'; import AlmRepositoryItem from './AlmRepositoryItem'; import SetupProjectBox from './SetupProjectBox'; import DeferredSpinner from '../../../components/common/DeferredSpinner'; +import Checkbox from '../../../components/controls/Checkbox'; import SearchBox from '../../../components/controls/SearchBox'; import UpgradeOrganizationBox from '../components/UpgradeOrganizationBox'; import { Alert } from '../../../components/ui/Alert'; import { getRepositories } from '../../../api/alm-integration'; -import { isDefined } from '../../../helpers/types'; import { translateWithParameters, translate } from '../../../helpers/l10n'; +import { isPaidOrganization } from '../../../helpers/organizations'; +import { isDefined } from '../../../helpers/types'; interface Props { almApplication: T.AlmApplication; @@ -39,6 +42,7 @@ interface Props { type SelectedRepositories = T.Dict; interface State { + checkAllRepositories: boolean; highlight: boolean; loading: boolean; repositories: T.AlmRepository[]; @@ -50,6 +54,7 @@ interface State { export default class RemoteRepositories extends React.PureComponent { mounted = false; state: State = { + checkAllRepositories: false, highlight: false, loading: true, repositories: [], @@ -91,6 +96,9 @@ export default class RemoteRepositories extends React.PureComponent + repo.label.toLowerCase().includes(this.state.search.toLowerCase()); + handleHighlightUpgradeBox = (highlight: boolean) => { this.setState({ highlight }); }; @@ -120,7 +128,31 @@ export default class RemoteRepositories extends React.PureComponent { - this.setState({ search }); + this.setState({ search, checkAllRepositories: false, selectedRepositories: {} }); + }; + + onCheckAllRepositories = () => { + this.setState(({ checkAllRepositories, repositories, search }) => { + const { organization } = this.props; + + const isPaidOrg = isPaidOrganization(organization); + const filterByPlan = (repo: T.AlmRepository) => (isPaidOrg ? true : !repo.private); + + const nextState = { + selectedRepositories: {}, + checkAllRepositories: !checkAllRepositories + }; + + if (nextState.checkAllRepositories) { + const validRepositories = (search + ? repositories.filter(this.filterBySearch) + : repositories + ).filter(filterByPlan); + nextState.selectedRepositories = keyBy(validRepositories, 'installationKey'); + } + + return nextState; + }); }; toggleRepository = (repository: T.AlmRepository) => { @@ -137,27 +169,38 @@ export default class RemoteRepositories extends React.PureComponent Boolean(repository.private)); const showSearchBox = repositories.length > 5; + const showCheckAll = repositories.length > 1; const showUpgradebox = !isPaidOrg && hasPrivateRepositories && organization.actions && organization.actions.admin; - const filteredRepositories = repositories.filter( - repo => !search || repo.label.toLowerCase().includes(search.toLowerCase()) - ); + const filteredRepositories = search ? repositories.filter(this.filterBySearch) : repositories; + return (
- {showSearchBox && ( -
+
+
+ {showCheckAll && ( + + {translate('onboarding.create_project.select_all_repositories')} + + )} +
+ {showSearchBox && ( -
- )} + )} +
+ {this.state.successfullyUpgraded && ( {translateWithParameters( diff --git a/server/sonar-web/src/main/js/apps/create/project/__tests__/RemoteRepositories-test.tsx b/server/sonar-web/src/main/js/apps/create/project/__tests__/RemoteRepositories-test.tsx index 025209632ce..a4e7c3d0455 100644 --- a/server/sonar-web/src/main/js/apps/create/project/__tests__/RemoteRepositories-test.tsx +++ b/server/sonar-web/src/main/js/apps/create/project/__tests__/RemoteRepositories-test.tsx @@ -33,9 +33,7 @@ jest.mock('../../../../api/alm-integration', () => ({ repositories: [ { label: 'Cool Project', - installationKey: 'github/cool', - linkedProjectKey: 'proj_cool', - linkedProjectName: 'Proj Cool' + installationKey: 'github/cool' }, { label: 'Awesome Project', installationKey: 'github/awesome' } ] @@ -102,6 +100,41 @@ it('should display a search box to filter repositories', async () => { expect(wrapper.find('AlmRepositoryItem')).toHaveLength(1); }); +it('should allow to select all repositories', async () => { + (getRepositories as jest.Mock).mockResolvedValueOnce({ + repositories: times(6, i => ({ label: `Project ${i}`, installationKey: `key-${i}` })) + }); + + const wrapper = shallowRender(); + await waitAndUpdate(wrapper); + + expect(wrapper.find('Checkbox')).toHaveLength(1); + expect(wrapper.state('checkAllRepositories')).toBe(false); + expect(wrapper.state('selectedRepositories')).toEqual({}); +}); + +it('should select all repositories', async () => { + (getRepositories as jest.Mock).mockResolvedValueOnce({ + repositories: times(6, i => ({ label: `Project ${i}`, installationKey: `key-${i}` })) + }); + + const wrapper = shallowRender(); + const instance = wrapper.instance() as RemoteRepositories; + await waitAndUpdate(wrapper); + + instance.onCheckAllRepositories(); + await waitAndUpdate(wrapper); + + expect(wrapper.state('checkAllRepositories')).toBe(true); + expect(wrapper.state('selectedRepositories')).toMatchSnapshot(); + + instance.onCheckAllRepositories(); + await waitAndUpdate(wrapper); + + expect(wrapper.state('checkAllRepositories')).toBe(false); + expect(wrapper.state('selectedRepositories')).toEqual({}); +}); + function shallowRender(props: Partial = {}) { return shallow( +
+
+
+
+
+ + onboarding.create_project.select_all_repositories + +
+
`; + +exports[`should select all repositories 1`] = ` +Object { + "key-0": Object { + "installationKey": "key-0", + "label": "Project 0", + }, + "key-1": Object { + "installationKey": "key-1", + "label": "Project 1", + }, + "key-2": Object { + "installationKey": "key-2", + "label": "Project 2", + }, + "key-3": Object { + "installationKey": "key-3", + "label": "Project 3", + }, + "key-4": Object { + "installationKey": "key-4", + "label": "Project 4", + }, + "key-5": Object { + "installationKey": "key-5", + "label": "Project 5", + }, +} +`; diff --git a/server/sonar-web/src/main/js/apps/create/project/style.css b/server/sonar-web/src/main/js/apps/create/project/style.css index 1c394a4ea78..2bc60122458 100644 --- a/server/sonar-web/src/main/js/apps/create/project/style.css +++ b/server/sonar-web/src/main/js/apps/create/project/style.css @@ -111,3 +111,16 @@ border-color: var(--sonarcloudBlue600); background-color: var(--sonarcloudBlue600); } + +.create-project-actions { + min-width: 500px; + display: flex; + justify-content: space-between; + align-items: center; + padding: 8px 0 8px 16px; + border-left: 1px solid transparent; +} + +.create-project-actions .icon-checkbox { + margin-right: 8px; +} diff --git a/server/sonar-web/src/main/js/store/utils/configureStore.ts b/server/sonar-web/src/main/js/store/utils/configureStore.ts index 2d560719467..f163fd5f1e1 100644 --- a/server/sonar-web/src/main/js/store/utils/configureStore.ts +++ b/server/sonar-web/src/main/js/store/utils/configureStore.ts @@ -30,8 +30,8 @@ if (process.env.NODE_ENV === 'development') { const { createLogger } = require('redux-logger'); middlewares.push(createLogger()); - const { devToolsExtension } = window as any; - composed.push(devToolsExtension ? devToolsExtension() : (f: Function) => f); + const { __REDUX_DEVTOOLS_EXTENSION__ } = window as any; + composed.push(__REDUX_DEVTOOLS_EXTENSION__ ? __REDUX_DEVTOOLS_EXTENSION__() : (f: Function) => f); } const finalCreateStore = compose( diff --git a/sonar-core/src/main/resources/org/sonar/l10n/core.properties b/sonar-core/src/main/resources/org/sonar/l10n/core.properties index 7fb78674797..11bb38d2367 100644 --- a/sonar-core/src/main/resources/org/sonar/l10n/core.properties +++ b/sonar-core/src/main/resources/org/sonar/l10n/core.properties @@ -2824,6 +2824,7 @@ onboarding.create_project.display_name.help=Some scanners might override the val onboarding.create_project.repository_imported=Already imported: {link} onboarding.create_project.see_project=See the project onboarding.create_project.select_repositories=Select repositories +onboarding.create_project.select_all_repositories=Select all repositories onboarding.create_project.subscribe_to_import_private_repositories=You need to subscribe your organization to a paid plan to import private projects onboarding.create_project.encourage_to_subscribe=Subscribe your organization to our paid plan to get unlimited private projects. onboarding.create_project.subscribtion_success_x={0} has been successfully upgraded to paid plan. You can now import and analyze private projects.