label?: string;
loading?: boolean;
onCheck: (checked: boolean, id?: string) => void;
- onClick?: (event: React.MouseEvent<HTMLInputElement>) => void;
+ onClick?: (event: React.MouseEvent<HTMLLabelElement>) => void;
onFocus?: VoidFunction;
right?: boolean;
thirdState?: boolean;
};
return (
- <CheckboxContainer className={className} disabled={disabled}>
+ <CheckboxContainer className={className} disabled={disabled} onClick={onClick}>
{right && children}
<AccessibleCheckbox
aria-label={label ?? title}
disabled={disabled ?? loading}
id={id}
onChange={handleChange}
- onClick={onClick}
onFocus={onFocus}
type="checkbox"
/>
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import { Accordion, Spinner, FlagMessage, Link, SearchHighlighter } from 'design-system';
+import { Accordion, FlagMessage, Link, SearchHighlighter, Spinner } from 'design-system';
import * as React from 'react';
import { FormattedMessage } from 'react-intl';
import ListFooter from '../../../../components/controls/ListFooter';
</FlagMessage>
) : (
<>
- <div className="sw-flex sw-flex-col sw-gap-3">
+ <ul className="sw-flex sw-flex-col sw-gap-3">
{limitedRepositories.map((r) => (
<AlmRepoItem
key={r.name}
}
/>
))}
- </div>
+ </ul>
<ListFooter
count={limitedRepositories.length}
total={repositories.length}
<LightPrimary className="sw-body-sm">{translate('no_results')}</LightPrimary>
</div>
) : (
- <div className="sw-flex sw-flex-col sw-gap-3">
+ <ul className="sw-flex sw-flex-col sw-gap-3">
{repositories.map((r) => (
<AlmRepoItem
key={r.uuid}
secondaryTextNode={<span title={r.projectKey}>{r.projectKey}</span>}
/>
))}
- </div>
+ </ul>
)}
<ListFooter
</FlagMessage>
)}
- <div className="sw-flex sw-flex-col sw-gap-3">
+ <ul className="sw-flex sw-flex-col sw-gap-3">
{repositories.map((r) => (
<AlmRepoItem
key={r.name}
primaryTextNode={<span>{r.name}</span>}
/>
))}
- </div>
+ </ul>
</div>
{!showingAllRepositories && repositoryCount > 0 && (
*/
/* eslint-disable react/no-unused-prop-types */
+import styled from '@emotion/styled';
import {
+ ButtonPrimary,
+ Checkbox,
DarkLabel,
FlagMessage,
InputSearch,
Link,
Spinner,
Title,
+ themeBorder,
+ themeColor,
} from 'design-system';
-import * as React from 'react';
+import React, { useState } from 'react';
import { FormattedMessage } from 'react-intl';
import ListFooter from '../../../../components/controls/ListFooter';
import { LabelValueSelectOption } from '../../../../components/controls/Select';
return { value: key, label: name };
}
-function renderRepositoryList(props: GitHubProjectCreateRendererProps) {
- const { loadingRepositories, repositories, repositoryPaging, searchQuery, selectedOrganization } =
- props;
+function RepositoryList(
+ props: GitHubProjectCreateRendererProps & {
+ selected: Set<string>;
+ checkAll: () => void;
+ uncheckAll: () => void;
+ onCheck: (key: string) => void;
+ },
+) {
+ const {
+ loadingRepositories,
+ repositories,
+ repositoryPaging,
+ searchQuery,
+ selectedOrganization,
+ selected,
+ } = props;
+
+ const areAllRepositoriesChecked = () => {
+ const nonImportedRepos = repositories?.filter((r) => !r.sqProjectKey) ?? [];
+ return nonImportedRepos.length > 0 && selected.size === nonImportedRepos.length;
+ };
+
+ const onCheckAllRepositories = () => {
+ const allSelected = areAllRepositoriesChecked();
+ if (allSelected) {
+ props.uncheckAll();
+ } else {
+ props.checkAll();
+ }
+ };
+
+ if (!selectedOrganization || !repositories) {
+ return null;
+ }
return (
- selectedOrganization &&
- repositories && (
- <div>
- <div className="sw-flex sw-items-center sw-mb-6">
- <InputSearch
- size="large"
- loading={loadingRepositories}
- onChange={props.onSearch}
- placeholder={translate('onboarding.create_project.search_repositories')}
- value={searchQuery}
- />
+ <div>
+ <div className="sw-mb-2 sw-py-2 sw-flex sw-items-center sw-justify-between sw-w-full">
+ <div>
+ <Checkbox
+ className="sw-ml-5"
+ checked={areAllRepositoriesChecked()}
+ disabled={repositories.length === 0}
+ onCheck={onCheckAllRepositories}
+ >
+ <span className="sw-ml-2">
+ {translate('onboarding.create_project.select_all_repositories')}
+ </span>
+ </Checkbox>
</div>
-
- {repositories.length === 0 ? (
- <div className="sw-py-6 sw-px-2">
- <LightPrimary className="sw-body-sm">{translate('no_results')}</LightPrimary>
- </div>
- ) : (
- <div className="sw-flex sw-flex-col sw-gap-3">
- {repositories.map((r) => (
- <AlmRepoItem
- key={r.key}
- almKey={r.key}
- almUrl={r.url}
- almUrlText={translate('onboarding.create_project.see_on_github')}
- almIconSrc={`${getBaseUrl()}/images/tutorials/github-actions.svg`}
- sqProjectKey={r.sqProjectKey}
- onImport={props.onImportRepository}
- primaryTextNode={<span title={r.name}>{r.name}</span>}
- />
- ))}
- </div>
- )}
-
- <ListFooter
- className="sw-mb-10"
- count={repositories.length}
- total={repositoryPaging.total}
- loadMore={props.onLoadMore}
+ <InputSearch
+ size="medium"
loading={loadingRepositories}
- useMIUIButtons
+ onChange={props.onSearch}
+ placeholder={translate('onboarding.create_project.search_repositories')}
+ value={searchQuery}
/>
</div>
- )
+
+ {repositories.length === 0 ? (
+ <div className="sw-py-6 sw-px-2">
+ <LightPrimary className="sw-body-sm">{translate('no_results')}</LightPrimary>
+ </div>
+ ) : (
+ <ul className="sw-flex sw-flex-col sw-gap-3">
+ {repositories.map(({ key, url, sqProjectKey, name }) => (
+ <AlmRepoItem
+ key={key}
+ almKey={key}
+ almUrl={url}
+ almUrlText={translate('onboarding.create_project.see_on_github')}
+ almIconSrc={`${getBaseUrl()}/images/tutorials/github-actions.svg`}
+ sqProjectKey={sqProjectKey}
+ multiple
+ selected={selected.has(key)}
+ onCheck={(key: string) => props.onCheck(key)}
+ primaryTextNode={<span title={name}>{name}</span>}
+ />
+ ))}
+ </ul>
+ )}
+
+ <ListFooter
+ className="sw-mb-10"
+ count={repositories.length}
+ total={repositoryPaging.total}
+ loadMore={props.onLoadMore}
+ loading={loadingRepositories}
+ useMIUIButtons
+ />
+ </div>
);
}
selectedOrganization,
almInstances,
selectedAlmInstance,
+ repositories,
} = props;
+ const [selected, setSelected] = useState<Set<string>>(new Set());
if (loadingBindings) {
return <Spinner />;
}
+ const handleImport = () => {
+ props.onImportRepository(Array.from(selected).toString()); // TBD
+ };
+
+ const handleCheckAll = () => {
+ setSelected(new Set(repositories?.filter((r) => !r.sqProjectKey).map((r) => r.key) ?? []));
+ };
+
+ const handleUncheckAll = () => {
+ setSelected(new Set());
+ };
+
+ const handleCheck = (key: string) => {
+ setSelected((prev) => new Set(prev.delete(key) ? prev : prev.add(key)));
+ };
+
return (
<>
<header className="sw-mb-10">
</FlagMessage>
)}
- <Spinner loading={loadingOrganizations && !error}>
- {!error && (
- <div className="sw-flex sw-flex-col">
- <DarkLabel htmlFor="github-choose-organization" className="sw-mb-2">
- {translate('onboarding.create_project.github.choose_organization')}
- </DarkLabel>
- {organizations.length > 0 ? (
- <InputSelect
- className="sw-w-abs-300 sw-mb-9"
- size="full"
- isSearchable
- inputId="github-choose-organization"
- options={organizations.map(orgToOption)}
- onChange={({ value }: LabelValueSelectOption) => props.onSelectOrganization(value)}
- value={selectedOrganization ? orgToOption(selectedOrganization) : null}
- />
- ) : (
- !loadingOrganizations && (
- <FlagMessage variant="error" className="sw-mb-2">
- <span>
- {canAdmin ? (
- <FormattedMessage
- id="onboarding.create_project.github.no_orgs_admin"
- defaultMessage={translate('onboarding.create_project.github.no_orgs_admin')}
- values={{
- link: (
- <Link to="/admin/settings?category=almintegration">
- {translate(
- 'onboarding.create_project.github.warning.message_admin.link',
- )}
- </Link>
- ),
- }}
- />
- ) : (
- translate('onboarding.create_project.github.no_orgs')
- )}
- </span>
- </FlagMessage>
- )
+ <div className="sw-flex sw-gap-12">
+ <LargeColumn>
+ <Spinner loading={loadingOrganizations && !error}>
+ {!error && (
+ <div className="sw-flex sw-flex-col">
+ <DarkLabel htmlFor="github-choose-organization" className="sw-mb-2">
+ {translate('onboarding.create_project.github.choose_organization')}
+ </DarkLabel>
+ {organizations.length > 0 ? (
+ <InputSelect
+ className="sw-w-full sw-mb-9"
+ size="full"
+ isSearchable
+ inputId="github-choose-organization"
+ options={organizations.map(orgToOption)}
+ onChange={({ value }: LabelValueSelectOption) =>
+ props.onSelectOrganization(value)
+ }
+ value={selectedOrganization ? orgToOption(selectedOrganization) : null}
+ />
+ ) : (
+ !loadingOrganizations && (
+ <FlagMessage variant="error" className="sw-mb-2">
+ <span>
+ {canAdmin ? (
+ <FormattedMessage
+ id="onboarding.create_project.github.no_orgs_admin"
+ defaultMessage={translate(
+ 'onboarding.create_project.github.no_orgs_admin',
+ )}
+ values={{
+ link: (
+ <Link to="/admin/settings?category=almintegration">
+ {translate(
+ 'onboarding.create_project.github.warning.message_admin.link',
+ )}
+ </Link>
+ ),
+ }}
+ />
+ ) : (
+ translate('onboarding.create_project.github.no_orgs')
+ )}
+ </span>
+ </FlagMessage>
+ )
+ )}
+ </div>
)}
- </div>
- )}
- </Spinner>
-
- {renderRepositoryList(props)}
+ </Spinner>
+ <RepositoryList
+ {...props}
+ selected={selected}
+ checkAll={handleCheckAll}
+ uncheckAll={handleUncheckAll}
+ onCheck={handleCheck}
+ />
+ </LargeColumn>
+ <SideColumn>
+ {selected.size > 0 && (
+ <SetupBox className="sw-rounded-2 sw-p-8 sw-mb-0">
+ <SetupBoxTitle className="sw-mb-2 sw-heading-md">
+ <FormattedMessage
+ id="onboarding.create_project.x_repositories_selected"
+ values={{ count: selected.size }}
+ />
+ </SetupBoxTitle>
+ <div>
+ <SetupBoxContent className="sw-pb-4">
+ <FormattedMessage
+ id="onboarding.create_project.x_repository_created"
+ values={{ count: selected.size }}
+ />
+ </SetupBoxContent>
+ <div className="sw-mt-4">
+ <ButtonPrimary onClick={handleImport} className="js-set-up-projects">
+ {translate('onboarding.create_project.import')}
+ </ButtonPrimary>
+ </div>
+ </div>
+ </SetupBox>
+ )}
+ </SideColumn>
+ </div>
</>
);
}
+
+const LargeColumn = styled.div`
+ flex: 6;
+`;
+
+const SideColumn = styled.div`
+ flex: 4;
+`;
+
+const SetupBox = styled.form`
+ max-height: 280px;
+ background: ${themeColor('highlightedSection')};
+ border: ${themeBorder('default', 'highlightedSectionBorder')};
+`;
+
+const SetupBoxTitle = styled.h2`
+ color: ${themeColor('pageTitle')};
+`;
+
+const SetupBoxContent = styled.div`
+ border-bottom: ${themeBorder('default')};
+`;
<LightPrimary className="sw-body-sm">{translate('no_results')}</LightPrimary>
</div>
) : (
- <div className="sw-flex sw-flex-col sw-gap-3">
+ <ul className="sw-flex sw-flex-col sw-gap-3">
{projects.map((project) => (
<AlmRepoItem
key={project.id}
}
/>
))}
- </div>
+ </ul>
)}
<ListFooter
className="sw-mb-10"
expect(screen.getByText('Azure project 2')).toBeInTheDocument();
expect(
- screen.getByRole('row', {
- name: 'Azure repo 1 onboarding.create_project.repository_imported',
+ screen.getByRole('listitem', {
+ name: 'Azure repo 1',
}),
).toBeInTheDocument();
expect(
- screen.getByRole('row', {
- name: 'Azure repo 2 onboarding.create_project.import',
+ screen.getByRole('listitem', {
+ name: 'Azure repo 2',
}),
).toBeInTheDocument();
await user.click(projectItem);
expect(
- screen.getByRole('row', {
- name: 'Bitbucket Repo 1 onboarding.create_project.repository_imported',
+ screen.getByRole('listitem', {
+ name: 'Bitbucket Repo 1',
}),
).toBeInTheDocument();
expect(
- screen.getByRole('row', {
- name: 'Bitbucket Repo 2 onboarding.create_project.import',
+ screen.getByRole('listitem', {
+ name: 'Bitbucket Repo 2',
}),
).toBeInTheDocument();
expect(screen.getByText('BitbucketCloud Repo 1')).toBeInTheDocument();
expect(screen.getByText('BitbucketCloud Repo 2')).toBeInTheDocument();
- projectItem = screen.getByRole('row', { name: /BitbucketCloud Repo 1/ });
+ projectItem = screen.getByRole('listitem', { name: /BitbucketCloud Repo 1/ });
expect(
within(projectItem).getByText('onboarding.create_project.repository_imported'),
).toBeInTheDocument();
'/dashboard?id=key',
);
- projectItem = screen.getByRole('row', { name: /BitbucketCloud Repo 2/ });
+ projectItem = screen.getByRole('listitem', { name: /BitbucketCloud Repo 2/ });
const setupButton = within(projectItem).getByRole('button', {
name: 'onboarding.create_project.import',
});
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import { screen, within } from '@testing-library/react';
+import { screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import * as React from 'react';
import AlmIntegrationsServiceMock from '../../../../api/mocks/AlmIntegrationsServiceMock';
import AlmSettingsServiceMock from '../../../../api/mocks/AlmSettingsServiceMock';
import NewCodeDefinitionServiceMock from '../../../../api/mocks/NewCodeDefinitionServiceMock';
+import { mockGitHubRepository } from '../../../../helpers/mocks/alm-integrations';
import { renderApp } from '../../../../helpers/testReactTestingUtils';
-import { byLabelText, byText } from '../../../../helpers/testSelector';
+import { byLabelText, byRole, byText } from '../../../../helpers/testSelector';
import CreateProjectPage from '../CreateProjectPage';
jest.mock('../../../../api/alm-integrations');
githubCreateProjectButton: byText('onboarding.create_project.select_method.github'),
instanceSelector: byLabelText(/alm.configuration.selector.label/),
organizationSelector: byLabelText('onboarding.create_project.github.choose_organization'),
+ project1: byRole('listitem', { name: 'Github repo 1' }),
+ project1Checkbox: byRole('listitem', { name: 'Github repo 1' }).byRole('checkbox'),
+ project2: byRole('listitem', { name: 'Github repo 2' }),
+ project2Checkbox: byRole('listitem', { name: 'Github repo 2' }).byRole('checkbox'),
+ project3: byRole('listitem', { name: 'Github repo 3' }),
+ project3Checkbox: byRole('listitem', { name: 'Github repo 3' }).byRole('checkbox'),
+ checkAll: byRole('checkbox', { name: 'onboarding.create_project.select_all_repositories' }),
+ importButton: byRole('button', { name: 'onboarding.create_project.import' }),
+ newCodeTitle: byRole('heading', { name: 'onboarding.create_project.new_code_definition.title' }),
+ createProjectButton: byRole('button', {
+ name: 'onboarding.create_project.new_code_definition.create_project',
+ }),
+ globalSettingRadio: byRole('radio', { name: 'new_code_definition.global_setting' }),
};
beforeAll(() => {
it('should show import project feature when the authentication is successfull', async () => {
const user = userEvent.setup();
- let repoItem;
renderCreateProject('project/create?mode=github&almInstance=conf-github-2&code=213321213');
await selectEvent.select(ui.organizationSelector.get(), [/org-1/]);
- expect(screen.getByText('Github repo 1')).toBeInTheDocument();
- expect(screen.getByText('Github repo 2')).toBeInTheDocument();
+ expect(await ui.project1.find()).toBeInTheDocument();
+ expect(ui.project2.get()).toBeInTheDocument();
+ expect(ui.checkAll.get()).not.toBeChecked();
- repoItem = screen.getByRole('row', {
- name: 'Github repo 1 onboarding.create_project.see_on_github onboarding.create_project.repository_imported',
- });
+ expect(ui.project1Checkbox.get()).toBeChecked();
+ expect(ui.project1Checkbox.get()).toBeDisabled();
expect(
- within(repoItem).getByText('onboarding.create_project.repository_imported'),
+ ui.project1.byText('onboarding.create_project.repository_imported').get(),
).toBeInTheDocument();
- expect(within(repoItem).getByRole('link', { name: /Github repo 1/ })).toBeInTheDocument();
- expect(within(repoItem).getByRole('link', { name: /Github repo 1/ })).toHaveAttribute(
+ expect(ui.project1.byRole('link', { name: /Github repo 1/ }).get()).toBeInTheDocument();
+ expect(ui.project1.byRole('link', { name: /Github repo 1/ }).get()).toHaveAttribute(
'href',
'/dashboard?id=key123',
);
- repoItem = screen.getByRole('row', {
- name: 'Github repo 2 onboarding.create_project.see_on_github onboarding.create_project.import',
- });
+ expect(ui.project2Checkbox.get()).not.toBeChecked();
+ expect(ui.project2Checkbox.get()).toBeEnabled();
- const importButton = screen.getByText('onboarding.create_project.import');
- await user.click(importButton);
+ expect(ui.importButton.query()).not.toBeInTheDocument();
+ await user.click(ui.project2Checkbox.get());
+ await waitFor(() => expect(ui.checkAll.get()).toBeChecked());
- expect(
- screen.getByRole('heading', { name: 'onboarding.create_project.new_code_definition.title' }),
- ).toBeInTheDocument();
+ expect(ui.importButton.get()).toBeInTheDocument();
+ await user.click(ui.importButton.get());
- await user.click(screen.getByRole('radio', { name: 'new_code_definition.global_setting' }));
- await user.click(
- screen.getByRole('button', {
- name: 'onboarding.create_project.new_code_definition.create_project',
- }),
- );
+ expect(await ui.newCodeTitle.find()).toBeInTheDocument();
+
+ await user.click(ui.globalSettingRadio.get());
+
+ expect(ui.createProjectButton.get()).toBeEnabled();
+ await user.click(ui.createProjectButton.get());
expect(await screen.findByText('/dashboard?id=key')).toBeInTheDocument();
});
+it('should import several projects', async () => {
+ const user = userEvent.setup();
+
+ almIntegrationHandler.setGithubRepositories([
+ mockGitHubRepository({ name: 'Github repo 1', key: 'key1' }),
+ mockGitHubRepository({ name: 'Github repo 2', key: 'key2' }),
+ mockGitHubRepository({ name: 'Github repo 3', key: 'key3' }),
+ ]);
+
+ renderCreateProject('project/create?mode=github&almInstance=conf-github-2&code=213321213');
+
+ expect(await ui.instanceSelector.find()).toBeInTheDocument();
+
+ await selectEvent.select(ui.organizationSelector.get(), [/org-1/]);
+
+ expect(await ui.project1.find()).toBeInTheDocument();
+ expect(ui.project1Checkbox.get()).not.toBeChecked();
+ expect(ui.project2Checkbox.get()).not.toBeChecked();
+ expect(ui.project3Checkbox.get()).not.toBeChecked();
+ expect(ui.checkAll.get()).not.toBeChecked();
+ expect(ui.importButton.query()).not.toBeInTheDocument();
+
+ await user.click(ui.project1Checkbox.get());
+
+ expect(ui.project1Checkbox.get()).toBeChecked();
+ expect(ui.project2Checkbox.get()).not.toBeChecked();
+ expect(ui.project3Checkbox.get()).not.toBeChecked();
+ expect(ui.checkAll.get()).not.toBeChecked();
+ expect(ui.importButton.get()).toBeInTheDocument();
+
+ await user.click(ui.checkAll.get());
+
+ expect(ui.project1Checkbox.get()).toBeChecked();
+ expect(ui.project2Checkbox.get()).toBeChecked();
+ expect(ui.project3Checkbox.get()).toBeChecked();
+ expect(ui.checkAll.get()).toBeChecked();
+ expect(ui.importButton.get()).toBeInTheDocument();
+
+ await user.click(ui.checkAll.get());
+
+ expect(ui.project1Checkbox.get()).not.toBeChecked();
+ expect(ui.project2Checkbox.get()).not.toBeChecked();
+ expect(ui.project3Checkbox.get()).not.toBeChecked();
+ expect(ui.checkAll.get()).not.toBeChecked();
+ expect(ui.importButton.query()).not.toBeInTheDocument();
+
+ await user.click(ui.project1Checkbox.get());
+ await user.click(ui.project2Checkbox.get());
+
+ expect(ui.importButton.get()).toBeInTheDocument();
+ await user.click(ui.importButton.get());
+
+ expect(await ui.newCodeTitle.find()).toBeInTheDocument();
+
+ // TBD
+});
+
it('should show search filter when the authentication is successful', async () => {
const user = userEvent.setup();
renderCreateProject('project/create?mode=github&almInstance=conf-github-2&code=213321213');
expect(screen.getByText('Gitlab project 1')).toBeInTheDocument();
expect(screen.getByText('Gitlab project 2')).toBeInTheDocument();
- projectItem = screen.getByRole('row', { name: /Gitlab project 1/ });
+ projectItem = screen.getByRole('listitem', { name: /Gitlab project 1/ });
expect(
within(projectItem).getByText('onboarding.create_project.repository_imported'),
).toBeInTheDocument();
'/dashboard?id=key',
);
- projectItem = screen.getByRole('row', { name: /Gitlab project 2/ });
+ projectItem = screen.getByRole('listitem', { name: /Gitlab project 2/ });
const importButton = within(projectItem).getByRole('button', {
name: 'onboarding.create_project.import',
});
import classNames from 'classnames';
import {
ButtonSecondary,
- Card,
CheckIcon,
+ Checkbox,
DiscreetLink,
LightLabel,
LightPrimary,
Link,
- themeColor,
+ themeBorder,
} from 'design-system';
import React from 'react';
import { translate } from '../../../../helpers/l10n';
import { getProjectUrl } from '../../../../helpers/urls';
-interface AlmRepoItemProps {
+type AlmRepoItemProps = {
primaryTextNode: React.ReactNode;
secondaryTextNode?: React.ReactNode;
sqProjectKey?: string;
almKey: string;
almUrl?: string;
almUrlText?: string;
- onImport: (key: string) => void;
almIconSrc: string;
-}
+} & (
+ | {
+ multiple: true;
+ onCheck: (key: string) => void;
+ selected: boolean;
+ onImport?: never;
+ }
+ | {
+ multiple?: false;
+ onCheck?: never;
+ selected?: never;
+ onImport: (key: string) => void;
+ }
+);
export default function AlmRepoItem({
almKey,
almUrl,
almUrlText,
almIconSrc,
+ multiple,
+ selected,
+ onCheck,
onImport,
}: AlmRepoItemProps) {
+ const labelId = `${almKey.replace(/\s/g, '_')}-label`;
return (
- <StyledCard
- key={almKey}
- role="row"
- className={classNames('sw-flex sw-px-4', {
- 'sw-py-4': sqProjectKey !== undefined,
- 'sw-py-2': sqProjectKey === undefined,
+ <RepositoryItem
+ selected={selected}
+ imported={sqProjectKey !== undefined}
+ aria-labelledby={labelId}
+ onClick={() => multiple && sqProjectKey === undefined && onCheck(almKey)}
+ className={classNames('sw-flex sw-items-center sw-w-full sw-p-4 sw-rounded-1', {
+ 'sw-py-4': multiple || sqProjectKey !== undefined,
+ 'sw-py-2': !multiple && sqProjectKey === undefined,
})}
>
+ {multiple && (
+ <Checkbox
+ checked={selected || sqProjectKey !== undefined}
+ className="sw-p-1 sw-mr-2"
+ disabled={sqProjectKey !== undefined}
+ onCheck={() => onCheck(almKey)}
+ onClick={(e: React.MouseEvent<HTMLLabelElement>) => e.stopPropagation()}
+ />
+ )}
<div className="sw-w-[70%] sw-min-w-0 sw-flex sw-mr-1">
- <div className="sw-max-w-[50%] sw-flex sw-items-center">
+ <div id={labelId} className="sw-max-w-[50%] sw-flex sw-items-center">
<img
alt="" // Should be ignored by screen readers
className="sw-h-4 sw-w-4 sw-mr-2"
</LightPrimary>
</div>
) : (
- <ButtonSecondary
- onClick={() => {
- onImport(almKey);
- }}
- >
- {translate('onboarding.create_project.import')}
- </ButtonSecondary>
+ <>
+ {!multiple && (
+ <ButtonSecondary
+ onClick={() => {
+ onImport(almKey);
+ }}
+ >
+ {translate('onboarding.create_project.import')}
+ </ButtonSecondary>
+ )}
+ </>
)}
</div>
- </StyledCard>
+ </RepositoryItem>
);
}
-const StyledCard = styled(Card)`
- border-color: ${themeColor('almCardBorder')};
+const RepositoryItem = styled.li<{ selected?: boolean; imported?: boolean }>`
+ box-sizing: border-box;
+ border: ${({ selected }) =>
+ selected ? themeBorder('default', 'primary') : themeBorder('default')};
+ cursor: ${({ imported }) => imported && 'default'};
`;
expect(
screen.getByRole('button', { name: 'onboarding.create_project.import' }),
).toBeInTheDocument();
+ expect(screen.queryByRole('checkbox')).not.toBeInTheDocument();
});
it('render the component correctly when sqProjectKey is present', () => {
expect(
screen.queryByRole('button', { name: 'onboarding.create_project.import' }),
).not.toBeInTheDocument();
+ expect(screen.queryByRole('checkbox')).not.toBeInTheDocument();
});
-function renderAlmRepoItem(props?: Partial<FCProps<typeof AlmRepoItem>>) {
+it('render the component correctly with checkboxes when sqProjectKey is not present', () => {
+ renderAlmRepoItem(undefined, true);
+ expect(screen.getByText('test1')).toBeInTheDocument();
+ expect(screen.getByText('url text')).toHaveAttribute('href', '/url');
+ expect(
+ screen.queryByText('onboarding.create_project.repository_imported'),
+ ).not.toBeInTheDocument();
+ expect(
+ screen.queryByRole('button', { name: 'onboarding.create_project.import' }),
+ ).not.toBeInTheDocument();
+ expect(screen.getByRole('checkbox')).toBeInTheDocument();
+ expect(screen.getByRole('checkbox')).not.toBeChecked();
+ expect(screen.getByRole('checkbox')).toBeEnabled();
+});
+
+it('render the component correctly with checkboxes when sqProjectKey is present', () => {
+ renderAlmRepoItem({ sqProjectKey: 'sqkey' }, true);
+ expect(screen.getByText('test1')).toBeInTheDocument();
+ expect(screen.getByText('url text')).toHaveAttribute('href', '/url');
+ expect(screen.getByText('onboarding.create_project.repository_imported')).toBeInTheDocument();
+ expect(
+ screen.queryByRole('button', { name: 'onboarding.create_project.import' }),
+ ).not.toBeInTheDocument();
+ expect(screen.getByRole('checkbox')).toBeInTheDocument();
+ expect(screen.getByRole('checkbox')).toBeChecked();
+ expect(screen.getByRole('checkbox')).toBeDisabled();
+});
+
+function renderAlmRepoItem(
+ props?: Omit<
+ Partial<FCProps<typeof AlmRepoItem>>,
+ 'multiple' | 'onCheck' | 'onImport' | 'selected'
+ >,
+ multiple?: boolean,
+) {
+ const commonProps = {
+ primaryTextNode: 'test1',
+ almKey: 'key',
+ almUrl: 'url',
+ almUrlText: 'url text',
+ almIconSrc: 'src',
+ };
return renderComponent(
- <AlmRepoItem
- primaryTextNode="test1"
- almKey="key"
- almUrl="url"
- almUrlText="url text"
- almIconSrc="src"
- onImport={jest.fn()}
- {...props}
- />,
+ multiple ? (
+ <AlmRepoItem {...commonProps} multiple onCheck={jest.fn()} selected={false} {...props} />
+ ) : (
+ <AlmRepoItem {...commonProps} onImport={jest.fn()} {...props} />
+ ),
);
}
id: 'id1234',
key: 'key3456',
name: 'repository 1',
- sqProjectKey: '',
url: 'https://github.com/owner/repo1',
...overrides,
};
slug: 'awesome-project-exclamation',
pathName: 'Company / Best Projects',
pathSlug: 'company/best-projects',
- sqProjectKey: '',
url: 'https://gitlab.company.com/best-projects/awesome-project-exclamation',
...overrides,
};
key: string;
name: string;
url: string;
- sqProjectKey: string;
+ sqProjectKey?: string;
}
export interface GitlabProject {
onboarding.create_project.gitlab.link=See on GitLab
onboarding.create_project.bitbucket.title=Bitbucket Server project onboarding
onboarding.create_project.bitbucket.subtitle=Import projects from one of your Bitbucket server workspaces
+onboarding.create_project.x_repositories_selected={count} {count, plural, one {repository} other {repositories}} selected
+onboarding.create_project.x_repository_created={count} {count, plural, one {repository} other {repositories}} will be created as a project on SonarQube
onboarding.create_project.new_code_definition.title=Set up project for Clean as You Code
onboarding.create_project.new_code_definition.description=The new code definition sets which part of your code will be considered new code. This helps you focus attention on the most recent changes to your project, enabling you to follow the Clean as You Code methodology. Learn more: {link}