*/
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;
type SelectedRepositories = T.Dict<T.AlmRepository | undefined>;
interface State {
+ checkAllRepositories: boolean;
highlight: boolean;
loading: boolean;
repositories: T.AlmRepository[];
export default class RemoteRepositories extends React.PureComponent<Props, State> {
mounted = false;
state: State = {
+ checkAllRepositories: false,
highlight: false,
loading: true,
repositories: [],
);
};
+ filterBySearch = (repo: T.AlmRepository) =>
+ repo.label.toLowerCase().includes(this.state.search.toLowerCase());
+
handleHighlightUpgradeBox = (highlight: boolean) => {
this.setState({ highlight });
};
};
handleSearch = (search: string) => {
- 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) => {
render() {
const { highlight, loading, repositories, search, selectedRepositories } = this.state;
const { almApplication, organization } = this.props;
- const isPaidOrg = organization.subscription === 'PAID';
+ const isPaidOrg = isPaidOrganization(organization);
const hasPrivateRepositories = repositories.some(repository => 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 (
<div className="create-project">
<div className="flex-1 huge-spacer-right">
- {showSearchBox && (
- <div className="spacer-bottom">
+ <div className="spacer-bottom create-project-actions">
+ <div>
+ {showCheckAll && (
+ <Checkbox
+ checked={this.state.checkAllRepositories}
+ disabled={filteredRepositories.length === 0}
+ onCheck={this.onCheckAllRepositories}>
+ {translate('onboarding.create_project.select_all_repositories')}
+ </Checkbox>
+ )}
+ </div>
+ {showSearchBox && (
<SearchBox
minLength={1}
onChange={this.handleSearch}
placeholder={translate('search.search_for_repositories')}
value={this.state.search}
/>
- </div>
- )}
+ )}
+ </div>
+
{this.state.successfullyUpgraded && (
<Alert variant="success">
{translateWithParameters(
repositories: [
{
label: 'Cool Project',
- installationKey: 'github/cool',
- linkedProjectKey: 'proj_cool',
- linkedProjectName: 'Proj Cool'
+ installationKey: 'github/cool'
},
{ label: 'Awesome Project', installationKey: 'github/awesome' }
]
expect(wrapper.find('AlmRepositoryItem')).toHaveLength(1);
});
+it('should allow to select all repositories', async () => {
+ (getRepositories as jest.Mock<any>).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<any>).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<RemoteRepositories['props']> = {}) {
return shallow(
<RemoteRepositories
<div
className="flex-1 huge-spacer-right"
>
+ <div
+ className="spacer-bottom create-project-actions"
+ >
+ <div />
+ </div>
<DeferredSpinner
loading={true}
timeout={100}
<div
className="flex-1 huge-spacer-right"
>
+ <div
+ className="spacer-bottom create-project-actions"
+ >
+ <div>
+ <Checkbox
+ checked={false}
+ disabled={false}
+ onCheck={[Function]}
+ thirdState={false}
+ >
+ onboarding.create_project.select_all_repositories
+ </Checkbox>
+ </div>
+ </div>
<DeferredSpinner
loading={false}
timeout={100}
Object {
"installationKey": "github/cool",
"label": "Cool Project",
- "linkedProjectKey": "proj_cool",
- "linkedProjectName": "Proj Cool",
}
}
selected={false}
}
/>
`;
+
+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",
+ },
+}
+`;