(window as any).React = React;
+const MockObserver = {
+ observe: jest.fn(),
+ unobserve: jest.fn(),
+ disconnect: jest.fn(),
+};
+
const content = document.createElement('div');
content.id = 'content';
document.documentElement.appendChild(content);
translateWithParameters: (messageKey: string, ...parameters: Array<string | number>) =>
[messageKey, ...parameters].join('.'),
}));
+
+const MockIntersectionObserverEntries = [{ isIntersecting: true }];
+
+(window as any).IntersectionObserver = jest.fn().mockImplementation((callback) => {
+ callback(MockIntersectionObserverEntries, MockObserver);
+ return MockObserver;
+});
"@emotion/react": "11.11.1",
"@emotion/styled": "11.11.0",
"@primer/octicons-react": "19.3.0",
+ "@react-spring/rafz": "9.7.3",
+ "@react-spring/web": "9.7.3",
"@tanstack/react-query": "4.29.14",
"classnames": "2.3.2",
"clipboard": "2.0.11",
function assertFinishStepIsCorrectlyRendered() {
expect(
screen.getByRole('heading', {
- name: 'onboarding.tutorial.ci_outro.all_set.title',
+ name: 'onboarding.tutorial.ci_outro.done',
})
).toBeInTheDocument();
}
} from '../../../../helpers/mocks/alm-settings';
import { mockComponent } from '../../../../helpers/mocks/component';
import { mockLanguage, mockLoggedInUser } from '../../../../helpers/testMocks';
-import { renderApp, RenderContext } from '../../../../helpers/testReactTestingUtils';
+import { RenderContext, renderApp } from '../../../../helpers/testReactTestingUtils';
import { AlmKeys } from '../../../../types/alm-settings';
import { Feature } from '../../../../types/features';
import {
// Create/update configuration file step
// Maven
await user.click(ui.mavenBuildButton.get());
- expect(getCopyToClipboardValue(1)).toMatchSnapshot('Maven: bitbucket-pipelines.yml');
+ expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot('Maven: bitbucket-pipelines.yml');
// Gradle
await user.click(ui.gradleBuildButton.get());
- expect(getCopyToClipboardValue(2)).toMatchSnapshot('Groovy: build.gradle');
+ expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot('Groovy: build.gradle');
await user.click(ui.gradleDSLButton(GradleBuildDSL.Kotlin).get());
- expect(getCopyToClipboardValue(2)).toMatchSnapshot('Kotlin: build.gradle.kts');
- expect(getCopyToClipboardValue(4)).toMatchSnapshot('Gradle: bitbucket-pipelines.yml');
+ expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot('Kotlin: build.gradle.kts');
+ expect(getCopyToClipboardValue(1, 'Copy')).toMatchSnapshot('Gradle: bitbucket-pipelines.yml');
// .NET
await user.click(ui.dotnetBuildButton.get());
- expect(getCopyToClipboardValue(1)).toMatchSnapshot('.NET: bitbucket-pipelines.yml');
+ expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot('.NET: bitbucket-pipelines.yml');
// CFamily
await user.click(ui.cFamilyBuildButton.get());
- expect(getCopyToClipboardValue()).toMatchSnapshot('CFamily: sonar-project.properties');
- expect(getCopyToClipboardValue(2)).toMatchSnapshot('CFamily: bitbucket-pipelines.yml');
+ expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot('CFamily: sonar-project.properties');
+ expect(getCopyToClipboardValue(1, 'Copy')).toMatchSnapshot('CFamily: bitbucket-pipelines.yml');
// Other
await user.click(ui.otherBuildButton.get());
- expect(getCopyToClipboardValue()).toMatchSnapshot('Other: sonar-project.properties');
- expect(getCopyToClipboardValue(2)).toMatchSnapshot('Other: .github/workflows/build.yml');
+ expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot('Other: sonar-project.properties');
+ expect(getCopyToClipboardValue(1, 'Copy')).toMatchSnapshot('Other: .github/workflows/build.yml');
await user.click(ui.finishTutorialButton.get());
expect(ui.allSetSentence.get()).toBeInTheDocument();
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+import styled from '@emotion/styled';
+import { animated, config, useSpring } from '@react-spring/web';
+import { CheckIcon, FlagVisual, SubTitle } from 'design-system';
import * as React from 'react';
import withAvailableFeatures, {
WithAvailableFeaturesProps,
} from '../../../app/components/available-features/withAvailableFeatures';
import { translate } from '../../../helpers/l10n';
-import { getBaseUrl } from '../../../helpers/system';
+import useIntersectionObserver from '../../../hooks/useIntersectionObserver';
import { AlmKeys } from '../../../types/alm-settings';
import { Feature } from '../../../types/features';
-import SentenceWithHighlights from './SentenceWithHighlights';
export interface AllSetProps extends WithAvailableFeaturesProps {
alm: AlmKeys;
}
export function AllSet(props: AllSetProps) {
+ const outroRef = React.useRef<HTMLDivElement>(null);
const { alm, willRefreshAutomatically } = props;
const branchSupportEnabled = props.hasFeature(Feature.BranchSupport);
+ const intersectionEntry = useIntersectionObserver(outroRef, { freezeOnceVisible: true });
+
+ const outroAnimation = useSpring({
+ from: { top: '200px' },
+ to: intersectionEntry?.isIntersecting ? { top: '0px' } : { top: '200px' },
+ config: config.wobbly,
+ });
+
return (
- <>
- <div className="abs-width-600">
- <p className="big-spacer-bottom">
- <SentenceWithHighlights
- highlightKeys={['all_set']}
- translationKey="onboarding.tutorial.ci_outro.all_set"
- />
+ <animated.div
+ className="sw-flex sw-flex-col sw-items-center sw-relative"
+ ref={outroRef}
+ style={outroAnimation}
+ >
+ <FlagVisual />
+ <SubTitle className="sw-mt-3 sw-mb-12">
+ {translate('onboarding.tutorial.ci_outro.done')}
+ </SubTitle>
+ <MessageContainer>
+ <p className="sw-body-sm sw-mb-4">
+ {translate('onboarding.tutorial.ci_outro.refresh_text')}
</p>
- <div className="display-flex-row big-spacer-bottom">
- <div>
- <img
- alt="" // Should be ignored by screen readers
- className="big-spacer-right"
- width={30}
- src={`${getBaseUrl()}/images/tutorials/commit.svg`}
- />
- </div>
- <div>
- <p className="little-spacer-bottom">
- <strong>{translate('onboarding.tutorial.ci_outro.commit')}</strong>
- </p>
- <p>
- {branchSupportEnabled
- ? translate('onboarding.tutorial.ci_outro.commit.why', alm)
- : translate('onboarding.tutorial.ci_outro.commit.why.no_branches')}
- </p>
- </div>
- </div>
- {willRefreshAutomatically && (
- <div className="display-flex-row">
- <div>
- <img
- alt="" // Should be ignored by screen readers
- className="big-spacer-right"
- width={30}
- src={`${getBaseUrl()}/images/tutorials/refresh.svg`}
- />
- </div>
- <div>
- <p className="little-spacer-bottom">
- <strong>{translate('onboarding.tutorial.ci_outro.refresh')}</strong>
- </p>
- <p>{translate('onboarding.tutorial.ci_outro.refresh.why')}</p>
- </div>
- </div>
- )}
- </div>
- {willRefreshAutomatically && (
- <div className="huge-spacer-bottom huge-spacer-top big-padded-top text-muted display-flex-center display-flex-justify-center">
- <i className="spinner spacer-right" />
- {translate('onboarding.tutorial.ci_outro.waiting_for_fist_analysis')}
- </div>
- )}
- </>
+ <ul className="sw-mb-6">
+ <li className="sw-mb-4 sw-flex sw-items-center">
+ <CheckIcon className="sw-mr-2" />
+ {branchSupportEnabled
+ ? translate('onboarding.tutorial.ci_outro.commit.why', alm)
+ : translate('onboarding.tutorial.ci_outro.commit.why.no_branches')}
+ </li>
+ {willRefreshAutomatically && (
+ <li className="sw-mb-4 sw-flex sw-items-center">
+ <CheckIcon className="sw-mr-2" />
+ {translate('onboarding.tutorial.ci_outro.refresh.why')}
+ </li>
+ )}
+ </ul>
+ </MessageContainer>
+ </animated.div>
);
}
+const MessageContainer = styled.div`
+ width: 840px;
+`;
+
export default withAvailableFeatures(AllSet);
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+import { ClipboardIconButton, CodeSnippet, NumberedListItem } from 'design-system';
import * as React from 'react';
import { FormattedMessage } from 'react-intl';
-import { ClipboardIconButton } from '../../../components/controls/clipboard';
import { translate } from '../../../helpers/l10n';
-import CodeSnippet from '../../common/CodeSnippet';
+import { InlineSnippet } from './InlineSnippet';
export interface CreateYmlFileProps {
yamlFileName: string;
export default function CreateYmlFile(props: CreateYmlFileProps) {
const { yamlTemplate, yamlFileName } = props;
return (
- <li className="abs-width-800">
+ <NumberedListItem>
<FormattedMessage
defaultMessage={translate('onboarding.tutorial.with.github_action.yaml.create_yml')}
id="onboarding.tutorial.with.github_action.yaml.create_yml"
values={{
file: (
<>
- <code className="rule">{yamlFileName}</code>
+ <InlineSnippet snippet={yamlFileName} />
<ClipboardIconButton copyValue={yamlFileName} />
</>
),
}}
/>
- <CodeSnippet snippet={yamlTemplate} />
- </li>
+ <CodeSnippet className="sw-p-6 sw-overflow-auto" snippet={yamlTemplate} language="yml" />
+ </NumberedListItem>
);
}
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+import { CodeSnippet, NumberedListItem } from 'design-system';
import * as React from 'react';
import { Component } from '../../../types/types';
-import CodeSnippet from '../../common/CodeSnippet';
import SentenceWithFilename from './SentenceWithFilename';
export interface DefaultProjectKeyProps {
export default function DefaultProjectKey(props: DefaultProjectKeyProps) {
const { component } = props;
return (
- <li className="abs-width-600">
+ <NumberedListItem>
<SentenceWithFilename
filename="sonar-project.properties"
translationKey="onboarding.tutorial.other.project_key"
/>
- <CodeSnippet snippet={sonarProjectSnippet(component.key)} />
- </li>
+ <CodeSnippet
+ snippet={sonarProjectSnippet(component.key)}
+ className="sw-p-8 sw-overflow-auto"
+ />
+ </NumberedListItem>
);
}
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import classNames from 'classnames';
+import { Card, LightLabel, StandoutLink } from 'design-system';
import React from 'react';
import { translate } from '../../../helpers/l10n';
import { getBaseUrl } from '../../../helpers/system';
-import Link from '../../common/Link';
import { OSs, TutorialModes } from '../types';
import './GithubCFamilyExampleRepositories.css';
const link = `https://github.com/orgs/sonarsource-cfamily-examples/repositories?q=${queryParams}`;
return (
- <div
- className={classNames(
- 'github-cfamily-example-repositories-box big-padded boxed-group',
- className
- )}
- >
- <div className="display-flex-center">
+ <Card className={classNames('sw-p-4', className)}>
+ <div>
<img
alt="" // Should be ignored by screen readers
className="spacer-right"
height={20}
src={`${getBaseUrl()}/images/alm/github.svg`}
/>
- <Link className="spacer-left big" target="_blank" to={link}>
+ <StandoutLink target="_blank" to={link}>
sonarsource-cfamily-examples
- </Link>
+ </StandoutLink>
</div>
- <p className="spacer-top">
+ <LightLabel as="p" className="sw-mt-4">
{translate('onboarding.tutorial.cfamily.examples_repositories_description')}
- </p>
- </div>
+ </LightLabel>
+ </Card>
);
}
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+import { ClipboardIconButton, CodeSnippet, NumberedListItem } from 'design-system/lib';
import React from 'react';
import { FormattedMessage } from 'react-intl';
import { translate } from '../../../helpers/l10n';
import { Component } from '../../../types/types';
-import CodeSnippet from '../../common/CodeSnippet';
-import { ClipboardIconButton } from '../../controls/clipboard';
import { GradleBuildDSL } from '../types';
import { buildGradleSnippet } from '../utils';
import GradleBuildSelection from './GradleBuildSelection';
+import { InlineSnippet } from './InlineSnippet';
interface Props {
component: Component;
export default function GradleBuild({ component }: Props) {
return (
- <li className="abs-width-600">
+ <NumberedListItem>
<FormattedMessage
defaultMessage={translate('onboarding.tutorial.with.yaml.gradle')}
id="onboarding.tutorial.with.yaml.gradle"
values={{
groovy: (
<>
- <code className="rule">{GradleBuildDSL.Groovy}</code>
+ <InlineSnippet snippet={GradleBuildDSL.Groovy} />
<ClipboardIconButton copyValue={GradleBuildDSL.Groovy} />
</>
),
kotlin: (
<>
- <code className="rule">{GradleBuildDSL.Kotlin}</code>
+ <InlineSnippet snippet={GradleBuildDSL.Kotlin} />
<ClipboardIconButton copyValue={GradleBuildDSL.Kotlin} />
</>
),
- sq: <code className="rule">org.sonarqube</code>,
+ sq: <InlineSnippet snippet="org.sonarqube" />,
}}
/>
- <GradleBuildSelection className="big-spacer-top big-spacer-bottom">
+ <GradleBuildSelection className="sw-my-4">
{(build) => (
- <CodeSnippet snippet={buildGradleSnippet(component.key, component.name, build)} />
+ <CodeSnippet
+ language="gradle"
+ className="sw-p-6"
+ snippet={buildGradleSnippet(component.key, component.name, build)}
+ />
)}
</GradleBuildSelection>
- </li>
+ </NumberedListItem>
);
}
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+import classNames from 'classnames';
import { CodeSnippet } from 'design-system';
import * as React from 'react';
import { FCProps } from '../../../types/misc';
-export function InlineSnippet({ snippet }: Pick<FCProps<typeof CodeSnippet>, 'snippet'>) {
+export function InlineSnippet({
+ snippet,
+ className,
+}: Pick<FCProps<typeof CodeSnippet>, 'snippet'> & { className?: string }) {
return (
- <CodeSnippet className="sw-code sw-inline-block sw-px-1" noCopy isOneLine snippet={snippet} />
+ <CodeSnippet
+ className={classNames('sw-code sw-inline-block sw-px-1', className)}
+ noCopy
+ isOneLine
+ snippet={snippet}
+ />
);
}
optionLabelKey: string;
options: string[];
titleLabelKey?: string;
+ setDone?: (doneStatus: boolean) => void;
}
export default function RenderOptions({
checked,
label,
onCheck,
+ setDone,
optionLabelKey,
options,
titleLabelKey,
}: RenderOptionsProps) {
+ const onChange = (checked: string) => {
+ if (setDone) {
+ setDone(true);
+ }
+ onCheck(checked);
+ };
+
return (
<div className="sw-mt-4">
{titleLabelKey && <label className="sw-block sw-mb-1">{translate(titleLabelKey)}</label>}
<ToggleButton
label={label}
- onChange={onCheck}
+ onChange={onChange}
options={options.map((build) => ({
label: translate(optionLabelKey, build),
value: build,
import * as React from 'react';
import { FormattedMessage } from 'react-intl';
import { translate } from '../../../helpers/l10n';
+import { InlineSnippet } from './InlineSnippet';
export interface SentenceWithFilenameProps {
filename: string;
defaultMessage={translate(translationKey, 'sentence')}
id={`${translationKey}.sentence`}
values={{
- file: <code>{filename}</code>,
+ file: <InlineSnippet snippet={filename} />,
}}
/>
</span>
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+import { ButtonSecondary, NumberedListItem } from 'design-system';
import * as React from 'react';
import { FormattedMessage } from 'react-intl';
-import { Button } from '../../../components/controls/buttons';
import { translate } from '../../../helpers/l10n';
import { Component } from '../../../types/types';
import { LoggedInUser } from '../../../types/users';
return (
<>
- <li className="big-spacer-bottom">
+ <NumberedListItem>
<FormattedMessage
defaultMessage={translate('onboarding.tutorial.env_variables')}
id="onboarding.tutorial.env_variables"
values={{
extra: (
- <Button className="spacer-left" onClick={toggleTokenModal}>
+ <ButtonSecondary className="sw-ml-2" onClick={toggleTokenModal}>
{translate('onboarding.token.generate.long')}
- </Button>
+ </ButtonSecondary>
+ ),
+ field: (
+ <span className="sw-body-sm-highlight">
+ {translate('onboarding.tutorial.env_variables.field')}
+ </span>
),
- field: <strong>{translate('onboarding.tutorial.env_variables.field')}</strong>,
value: translate('onboarding.tutorial.env_variables.token_generator.value'),
}}
/>
- </li>
+ </NumberedListItem>
{isModalVisible && (
<EditTokenModal component={component} currentUser={currentUser} onClose={closeTokenModal} />
)}
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+import { NumberedList, NumberedListItem } from 'design-system';
import * as React from 'react';
import { translate } from '../../../helpers/l10n';
import { withCLanguageFeature } from '../../hoc/withCLanguageFeature';
export interface YamlFileStepProps {
children?: (buildTool: BuildTools) => React.ReactElement<{}>;
hasCLanguageFeature: boolean;
+ setDone?: (doneStatus: boolean) => void;
}
export function YamlFileStep(props: YamlFileStepProps) {
const [buildToolSelected, setBuildToolSelected] = React.useState<BuildTools>();
return (
- <ol className="list-styled big-spacer-top big-spacer-bottom">
- <li className="abs-width-600">
+ <NumberedList>
+ <NumberedListItem>
{translate('onboarding.build')}
<RenderOptions
label={translate('onboarding.build')}
onCheck={(value) => setBuildToolSelected(value as BuildTools)}
options={buildTools}
optionLabelKey="onboarding.build"
+ setDone={props.setDone}
/>
- </li>
+ </NumberedListItem>
{children && buildToolSelected && children(buildToolSelected)}
- </ol>
+ </NumberedList>
);
}
buildTool: BuildTools;
mainBranchName: string;
component: Component;
- onDone: () => void;
}
export function AnalysisCommand(props: AnalysisCommandProps) {
branchesEnabled={branchSupportEnabled}
mainBranchName={mainBranchName}
component={component}
- onDone={props.onDone}
/>
);
case BuildTools.Gradle:
branchesEnabled={branchSupportEnabled}
mainBranchName={mainBranchName}
component={component}
- onDone={props.onDone}
/>
);
case BuildTools.DotNet:
branchesEnabled={branchSupportEnabled}
mainBranchName={mainBranchName}
component={component}
- onDone={props.onDone}
/>
);
case BuildTools.CFamily:
branchesEnabled={branchSupportEnabled}
mainBranchName={mainBranchName}
component={component}
- onDone={props.onDone}
/>
);
case BuildTools.Other:
branchesEnabled={branchSupportEnabled}
mainBranchName={mainBranchName}
component={component}
- onDone={props.onDone}
/>
);
}
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+import { BasicSeparator, TutorialStep, TutorialStepList } from 'design-system';
import * as React from 'react';
import { translate } from '../../../helpers/l10n';
import {
} from '../../../types/alm-settings';
import { Component } from '../../../types/types';
import { LoggedInUser } from '../../../types/users';
-import AllSetStep from '../components/AllSetStep';
-import Step from '../components/Step';
+import AllSet from '../components/AllSet';
import YamlFileStep from '../components/YamlFileStep';
import AnalysisCommand from './AnalysisCommand';
import SecretStep from './SecretStep';
-export enum Steps {
- CREATE_SECRET = 1,
- YAML = 2,
- ALL_SET = 3,
-}
-
export interface GitHubActionTutorialProps {
almBinding?: AlmSettingsInstance;
baseUrl: string;
}
export default function GitHubActionTutorial(props: GitHubActionTutorialProps) {
+ const [done, setDone] = React.useState<boolean>(false);
const {
almBinding,
baseUrl,
mainBranchName,
willRefreshAutomatically,
} = props;
-
- const [step, setStep] = React.useState<Steps>(Steps.CREATE_SECRET);
return (
- <>
- <Step
- finished={step > Steps.CREATE_SECRET}
- onOpen={() => setStep(Steps.CREATE_SECRET)}
- open={step === Steps.CREATE_SECRET}
- renderForm={() => (
- <SecretStep
- almBinding={almBinding}
- baseUrl={baseUrl}
- component={component}
- currentUser={currentUser}
- projectBinding={projectBinding}
- onDone={() => setStep(Steps.YAML)}
+ <TutorialStepList className="sw-mb-8">
+ <TutorialStep title={translate('onboarding.tutorial.with.github_action.create_secret.title')}>
+ <SecretStep
+ almBinding={almBinding}
+ baseUrl={baseUrl}
+ component={component}
+ currentUser={currentUser}
+ projectBinding={projectBinding}
+ />
+ </TutorialStep>
+ <TutorialStep title={translate('onboarding.tutorial.with.github_action.yaml.title')}>
+ <YamlFileStep setDone={setDone}>
+ {(buildTool) => (
+ <AnalysisCommand
+ buildTool={buildTool}
+ mainBranchName={mainBranchName}
+ component={component}
+ />
+ )}
+ </YamlFileStep>
+ </TutorialStep>
+ {done && (
+ <>
+ <BasicSeparator className="sw-my-10" />
+ <AllSet
+ alm={almBinding?.alm || AlmKeys.GitHub}
+ willRefreshAutomatically={willRefreshAutomatically}
/>
- )}
- stepNumber={Steps.CREATE_SECRET}
- stepTitle={translate('onboarding.tutorial.with.github_action.create_secret.title')}
- />
- <Step
- finished={step > Steps.YAML}
- onOpen={() => setStep(Steps.YAML)}
- open={step === Steps.YAML}
- renderForm={() => (
- <YamlFileStep>
- {(buildTool) => (
- <AnalysisCommand
- buildTool={buildTool}
- mainBranchName={mainBranchName}
- component={component}
- onDone={() => setStep(Steps.ALL_SET)}
- />
- )}
- </YamlFileStep>
- )}
- stepNumber={Steps.YAML}
- stepTitle={translate('onboarding.tutorial.with.github_action.yaml.title')}
- />
- <AllSetStep
- alm={almBinding?.alm || AlmKeys.GitHub}
- open={step === Steps.ALL_SET}
- stepNumber={Steps.ALL_SET}
- willRefreshAutomatically={willRefreshAutomatically}
- />
- </>
+ </>
+ )}
+ </TutorialStepList>
);
}
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+import {
+ BasicSeparator,
+ ClipboardIconButton,
+ NumberedList,
+ NumberedListItem,
+ StandoutLink,
+} from 'design-system';
import * as React from 'react';
import { FormattedMessage } from 'react-intl';
-import { Button } from '../../../components/controls/buttons';
-import { ClipboardIconButton } from '../../../components/controls/clipboard';
import { translate } from '../../../helpers/l10n';
import { AlmSettingsInstance, ProjectAlmBindingResponse } from '../../../types/alm-settings';
import { Component } from '../../../types/types';
import { LoggedInUser } from '../../../types/users';
+import { InlineSnippet } from '../components/InlineSnippet';
import SentenceWithHighlights from '../components/SentenceWithHighlights';
import TokenStepGenerator from '../components/TokenStepGenerator';
import { buildGithubLink } from '../utils';
component: Component;
currentUser: LoggedInUser;
projectBinding?: ProjectAlmBindingResponse;
- onDone: () => void;
}
export default function SecretStep(props: SecretStepProps) {
const { almBinding, baseUrl, component, currentUser, projectBinding } = props;
return (
- <div className="boxed-group-inner">
- <p className="big-spacer-bottom">
- <FormattedMessage
- defaultMessage={translate('onboarding.tutorial.with.github_action.secret.intro')}
- id="onboarding.tutorial.with.github_action.secret.intro"
- values={{
- settings_secret:
- almBinding && projectBinding ? (
- <a
- href={`${buildGithubLink(almBinding, projectBinding)}/settings/secrets`}
- target="_blank"
- rel="noopener noreferrer"
- >
- {translate('onboarding.tutorial.with.github_action.secret.intro.link')}
- </a>
- ) : (
- <strong>
- {translate('onboarding.tutorial.with.github_action.secret.intro.link')}
- </strong>
- ),
- }}
- />
- </p>
- <ol className="list-styled">
- <li>
+ <>
+ <FormattedMessage
+ defaultMessage={translate('onboarding.tutorial.with.github_action.secret.intro')}
+ id="onboarding.tutorial.with.github_action.secret.intro"
+ values={{
+ settings_secret:
+ almBinding && projectBinding ? (
+ <StandoutLink
+ to={`${buildGithubLink(almBinding, projectBinding)}/settings/secrets`}
+ target="_blank"
+ rel="noopener noreferrer"
+ >
+ {translate('onboarding.tutorial.with.github_action.secret.intro.link')}
+ </StandoutLink>
+ ) : (
+ <span className="sw-body-sm-highlight">
+ {translate('onboarding.tutorial.with.github_action.secret.intro.link')}
+ </span>
+ ),
+ }}
+ />
+ <NumberedList>
+ <NumberedListItem>
<SentenceWithHighlights
translationKey="onboarding.tutorial.with.github_action.secret.new"
highlightKeys={['new_secret']}
/>
- </li>
- <li>
+ </NumberedListItem>
+ <NumberedListItem>
<SentenceWithHighlights
translationKey="onboarding.tutorial.with.github_action.secret.name"
highlightKeys={['name']}
/>
- <code className="rule little-spacer-left">SONAR_TOKEN</code>
+ <InlineSnippet snippet="SONAR_TOKEN" className="sw-ml-1" />
<ClipboardIconButton copyValue="SONAR_TOKEN" />
- </li>
+ </NumberedListItem>
<TokenStepGenerator component={component} currentUser={currentUser} />
- <li>
+ <NumberedListItem>
<SentenceWithHighlights
translationKey="onboarding.tutorial.with.github_action.secret.add"
highlightKeys={['add_secret']}
/>
- </li>
- </ol>
-
- <hr className="no-horizontal-margins" />
-
- <ol className="list-styled big-spacer-top big-spacer-bottom">
- <li>
+ </NumberedListItem>
+ </NumberedList>
+ <BasicSeparator className="sw-my-6" />
+ <NumberedList>
+ <NumberedListItem>
<SentenceWithHighlights
translationKey="onboarding.tutorial.with.github_action.secret.new"
highlightKeys={['new_secret']}
/>
- </li>
- <li>
+ </NumberedListItem>
+ <NumberedListItem>
<SentenceWithHighlights
translationKey="onboarding.tutorial.with.github_action.secret.name"
highlightKeys={['name']}
/>
-
- <code className="rule little-spacer-left">SONAR_HOST_URL</code>
+ <InlineSnippet snippet="SONAR_HOST_URL" className="sw-ml-1" />
<ClipboardIconButton copyValue="SONAR_HOST_URL" />
- </li>
- <li className="big-spacer-bottom">
+ </NumberedListItem>
+ <NumberedListItem>
<FormattedMessage
defaultMessage={translate('onboarding.tutorial.env_variables')}
id="onboarding.tutorial.env_variables"
values={{
extra: <ClipboardIconButton copyValue={baseUrl} />,
- field: <strong>{translate('onboarding.tutorial.env_variables.field')}</strong>,
- value: <code className="rule">{baseUrl}</code>,
+ field: (
+ <span className="sw-body-sm-highlight">
+ {translate('onboarding.tutorial.env_variables.field')}
+ </span>
+ ),
+ value: <InlineSnippet snippet={baseUrl} className="sw-ml-1" />,
}}
/>
- </li>
- <li>
+ </NumberedListItem>
+ <NumberedListItem>
<SentenceWithHighlights
translationKey="onboarding.tutorial.with.github_action.secret.add"
highlightKeys={['add_secret']}
/>
- </li>
- </ol>
- <Button onClick={props.onDone}>{translate('continue')}</Button>
- </div>
+ </NumberedListItem>
+ </NumberedList>
+ </>
);
}
} from '../../../../helpers/mocks/alm-settings';
import { mockComponent } from '../../../../helpers/mocks/component';
import { mockLanguage, mockLoggedInUser } from '../../../../helpers/testMocks';
-import { renderApp, RenderContext } from '../../../../helpers/testReactTestingUtils';
+import { RenderContext, renderApp } from '../../../../helpers/testReactTestingUtils';
import { AlmKeys } from '../../../../types/alm-settings';
import { Feature } from '../../../../types/features';
import {
expect(await ui.secretsStepTitle.find()).toBeInTheDocument();
// Env variables step
- expect(getCopyToClipboardValue()).toMatchSnapshot('sonar token key');
- expect(getCopyToClipboardValue(1)).toMatchSnapshot('sonarqube host url key');
- expect(getCopyToClipboardValue(2)).toMatchSnapshot('sonarqube host url value');
- await user.click(ui.continueButton.get());
+ expect(getCopyToClipboardValue(0, 'Copy to clipboard')).toMatchSnapshot('sonar token key');
+ expect(getCopyToClipboardValue(1, 'Copy to clipboard')).toMatchSnapshot('sonarqube host url key');
+ expect(getCopyToClipboardValue(2, 'Copy to clipboard')).toMatchSnapshot(
+ 'sonarqube host url value'
+ );
// Create/update configuration file step
// Maven
await user.click(ui.mavenBuildButton.get());
- expect(getCopyToClipboardValue(1)).toMatchSnapshot('Maven: .github/workflows/build.yml');
+ expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot('Maven: .github/workflows/build.yml');
// Gradle
await user.click(ui.gradleBuildButton.get());
- expect(getCopyToClipboardValue(2)).toMatchSnapshot('Groovy: build.gradle');
+ expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot('Groovy: build.gradle');
await user.click(ui.gradleDSLButton(GradleBuildDSL.Kotlin).get());
- expect(getCopyToClipboardValue(2)).toMatchSnapshot('Kotlin: build.gradle.kts');
- expect(getCopyToClipboardValue(4)).toMatchSnapshot('Gradle: .github/workflows/build.yml');
+ expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot('Kotlin: build.gradle.kts');
+ expect(getCopyToClipboardValue(1, 'Copy')).toMatchSnapshot('Gradle: .github/workflows/build.yml');
// .NET
await user.click(ui.dotnetBuildButton.get());
- expect(getCopyToClipboardValue(1)).toMatchSnapshot('.NET: .github/workflows/build.yml');
+ expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot('.NET: .github/workflows/build.yml');
// CFamily
await user.click(ui.cFamilyBuildButton.get());
- expect(getCopyToClipboardValue()).toMatchSnapshot('CFamily: sonar-project.properties');
+ expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot('CFamily: sonar-project.properties');
await user.click(ui.linuxButton.get());
- expect(getCopyToClipboardValue(2)).toMatchSnapshot('CFamily Linux: .github/workflows/build.yml');
+ expect(getCopyToClipboardValue(1, 'Copy')).toMatchSnapshot(
+ 'CFamily Linux: .github/workflows/build.yml'
+ );
await user.click(ui.windowsButton.get());
- expect(getCopyToClipboardValue(2)).toMatchSnapshot(
+ expect(getCopyToClipboardValue(1, 'Copy')).toMatchSnapshot(
'CFamily Windows: .github/workflows/build.yml'
);
await user.click(ui.macosButton.get());
- expect(getCopyToClipboardValue(2)).toMatchSnapshot('CFamily MacOS: .github/workflows/build.yml');
+ expect(getCopyToClipboardValue(1, 'Copy')).toMatchSnapshot(
+ 'CFamily MacOS: .github/workflows/build.yml'
+ );
// Other
await user.click(ui.otherBuildButton.get());
- expect(getCopyToClipboardValue()).toMatchSnapshot('Other: sonar-project.properties');
- expect(getCopyToClipboardValue(2)).toMatchSnapshot('Other: .github/workflows/build.yml');
+ expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot('Other: sonar-project.properties');
+ expect(getCopyToClipboardValue(1, 'Copy')).toMatchSnapshot('Other: .github/workflows/build.yml');
- await user.click(ui.finishTutorialButton.get());
expect(ui.allSetSentence.get()).toBeInTheDocument();
});
// Generate token
await user.click(ui.genTokenDialogButton.get());
await user.click(ui.generateTokenButton.get());
- expect(getCopyToClipboardValue(3)).toEqual('generatedtoken2');
+ expect(getCopyToClipboardValue()).toEqual('generatedtoken2');
// Revoke current token and create new one
await user.click(ui.deleteTokenButton.get());
await selectEvent.select(ui.expiresInSelect.get(), 'users.tokens.expiration.365');
await user.click(ui.generateTokenButton.get());
expect(ui.tokenValue.get()).toBeInTheDocument();
- await user.click(ui.continueButton.getAll()[1]);
+ await user.click(ui.continueButton.getAll()[0]);
expect(ui.tokenValue.query()).not.toBeInTheDocument();
});
// If project is bound, link to repo is visible
expect(await ui.linkToRepo.find()).toBeInTheDocument();
- await user.click(await ui.continueButton.find());
await user.click(ui.mavenBuildButton.get());
- await user.click(ui.finishTutorialButton.get());
expect(ui.allSetSentence.get()).toBeInTheDocument();
await user.click(ui.ymlFileStepTitle.get());
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+import { NumberedListItem } from 'design-system';
import * as React from 'react';
import { translate } from '../../../../helpers/l10n';
import { Component } from '../../../../types/types';
import { CompilationInfo } from '../../components/CompilationInfo';
import CreateYmlFile from '../../components/CreateYmlFile';
import DefaultProjectKey from '../../components/DefaultProjectKey';
-import FinishButton from '../../components/FinishButton';
import GithubCFamilyExampleRepositories from '../../components/GithubCFamilyExampleRepositories';
import RenderOptions from '../../components/RenderOptions';
import { OSs, TutorialModes } from '../../types';
branchesEnabled?: boolean;
mainBranchName: string;
component: Component;
- onDone: () => void;
}
const STEPS = {
export default function CFamily(props: CFamilyProps) {
const { component, branchesEnabled, mainBranchName } = props;
- const [os, setOs] = React.useState<undefined | OSs>();
+ const [os, setOs] = React.useState<undefined | OSs>(OSs.Linux);
return (
<>
<DefaultProjectKey component={component} />
- <li className="abs-width-600">
+ <NumberedListItem>
<span>{translate('onboarding.build.other.os')}</span>
<RenderOptions
label={translate('onboarding.build.other.os')}
/>
{os && (
<GithubCFamilyExampleRepositories
- className="big-spacer-top"
+ className="sw-mt-4"
os={os}
ci={TutorialModes.GitHubActions}
/>
)}
- </li>
+ </NumberedListItem>
{os && (
<>
<CreateYmlFile
STEPS[os]
)}
/>
- <CompilationInfo className="abs-width-800" />
- <FinishButton onClick={props.onDone} />
+ <CompilationInfo />
</>
)}
</>
import * as React from 'react';
import { Component } from '../../../../types/types';
import CreateYmlFile from '../../components/CreateYmlFile';
-import FinishButton from '../../components/FinishButton';
import { GITHUB_ACTIONS_RUNS_ON_WINDOWS } from '../constants';
import { generateGitHubActionsYaml } from '../utils';
branchesEnabled?: boolean;
mainBranchName: string;
component: Component;
- onDone: () => void;
}
function dotnetYamlSteps(projectKey: string) {
export default function DotNet(props: DotNetProps) {
const { component, branchesEnabled, mainBranchName } = props;
return (
- <>
- <CreateYmlFile
- yamlFileName=".github/workflows/build.yml"
- yamlTemplate={generateGitHubActionsYaml(
- mainBranchName,
- !!branchesEnabled,
- GITHUB_ACTIONS_RUNS_ON_WINDOWS,
- dotnetYamlSteps(component.key)
- )}
- />
- <FinishButton onClick={props.onDone} />
- </>
+ <CreateYmlFile
+ yamlFileName=".github/workflows/build.yml"
+ yamlTemplate={generateGitHubActionsYaml(
+ mainBranchName,
+ !!branchesEnabled,
+ GITHUB_ACTIONS_RUNS_ON_WINDOWS,
+ dotnetYamlSteps(component.key)
+ )}
+ />
);
}
import * as React from 'react';
import { Component } from '../../../../types/types';
import CreateYmlFile from '../../components/CreateYmlFile';
-import FinishButton from '../../components/FinishButton';
import GradleBuild from '../../components/GradleBuild';
import { GITHUB_ACTIONS_RUNS_ON_LINUX } from '../constants';
import { generateGitHubActionsYaml } from '../utils';
branchesEnabled?: boolean;
mainBranchName: string;
component: Component;
- onDone: () => void;
}
const GRADLE_YAML_STEPS = `
GRADLE_YAML_STEPS
)}
/>
- <FinishButton onClick={props.onDone} />
</>
);
}
import * as React from 'react';
import { Component } from '../../../../types/types';
import CreateYmlFile from '../../components/CreateYmlFile';
-import FinishButton from '../../components/FinishButton';
import { GITHUB_ACTIONS_RUNS_ON_LINUX } from '../constants';
import { generateGitHubActionsYaml } from '../utils';
branchesEnabled?: boolean;
mainBranchName: string;
component: Component;
- onDone: () => void;
}
function mavenYamlSteps(projectKey: string, projectName: string) {
export default function JavaMaven(props: JavaMavenProps) {
const { component, branchesEnabled, mainBranchName } = props;
return (
- <>
- <CreateYmlFile
- yamlFileName=".github/workflows/build.yml"
- yamlTemplate={generateGitHubActionsYaml(
- mainBranchName,
- !!branchesEnabled,
- GITHUB_ACTIONS_RUNS_ON_LINUX,
- mavenYamlSteps(component.key, component.name)
- )}
- />
- <FinishButton onClick={props.onDone} />
- </>
+ <CreateYmlFile
+ yamlFileName=".github/workflows/build.yml"
+ yamlTemplate={generateGitHubActionsYaml(
+ mainBranchName,
+ !!branchesEnabled,
+ GITHUB_ACTIONS_RUNS_ON_LINUX,
+ mavenYamlSteps(component.key, component.name)
+ )}
+ />
);
}
import { Component } from '../../../../types/types';
import CreateYmlFile from '../../components/CreateYmlFile';
import DefaultProjectKey from '../../components/DefaultProjectKey';
-import FinishButton from '../../components/FinishButton';
import { GITHUB_ACTIONS_RUNS_ON_LINUX } from '../constants';
import { generateGitHubActionsYaml } from '../utils';
branchesEnabled?: boolean;
mainBranchName: string;
component: Component;
- onDone: () => void;
}
function otherYamlSteps(branchesEnabled: boolean) {
otherYamlSteps(!!branchesEnabled)
)}
/>
- <FinishButton onClick={props.onDone} />
</>
);
}
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-
import userEvent from '@testing-library/user-event';
import React from 'react';
import UserTokensMock from '../../../../api/mocks/UserTokensMock';
jenkinsStepTitle: byRole('heading', {
name: 'onboarding.tutorial.with.jenkins.jenkinsfile.title',
}),
- allSetSentence: byText('onboarding.tutorial.ci_outro.all_set.sentence'),
+ allSetSentence: byText('onboarding.tutorial.ci_outro.done'),
...getTutorialActionButtons(),
...getTutorialBuildButtons(),
};
// CFamilly
await user.click(ui.cFamilyBuildButton.get());
- expect(getCopyToClipboardValue()).toMatchSnapshot(`sonar-project.properties code`);
+ expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot(`sonar-project.properties code`);
await user.click(ui.linuxButton.get());
- expect(getCopyToClipboardValue(1)).toMatchSnapshot(`cfamily linux jenkinsfile`);
+ expect(getCopyToClipboardValue()).toMatchSnapshot(`cfamily linux jenkinsfile`);
await user.click(ui.windowsButton.get());
- expect(getCopyToClipboardValue(1)).toMatchSnapshot(`cfamily windows jenkinsfile`);
+ expect(getCopyToClipboardValue()).toMatchSnapshot(`cfamily windows jenkinsfile`);
await user.click(ui.macosButton.get());
- expect(getCopyToClipboardValue(1)).toMatchSnapshot(`cfamily macos jenkinsfile`);
+ expect(getCopyToClipboardValue()).toMatchSnapshot(`cfamily macos jenkinsfile`);
// Other
await user.click(ui.otherBuildButton.get());
- expect(getCopyToClipboardValue()).toMatchSnapshot(
+ expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot(
`other build tools sonar-project.properties code`
);
- expect(getCopyToClipboardValue(1)).toMatchSnapshot(`other build tools jenkinsfile`);
+ expect(getCopyToClipboardValue()).toMatchSnapshot(`other build tools jenkinsfile`);
await user.click(ui.finishTutorialButton.get());
expect(ui.allSetSentence.get()).toBeInTheDocument();
ci === TutorialModes.GitHubActions ? 'secret' : 'variables'
}.intro.link`,
}),
- allSetSentence: byText('onboarding.tutorial.ci_outro.all_set.sentence'),
+ allSetSentence: byText('onboarding.tutorial.ci_outro.done'),
};
}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 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 { RefObject, useEffect, useState } from 'react';
+import { isDefined } from '../helpers/types';
+
+interface Options extends IntersectionObserverInit {
+ freezeOnceVisible?: boolean;
+}
+
+export default function useIntersectionObserver<T extends Element>(
+ ref: RefObject<T>,
+ options: Options = {}
+) {
+ const { root = null, rootMargin = '0px', threshold = 0, freezeOnceVisible = false } = options;
+ const [entry, setEntry] = useState<IntersectionObserverEntry>();
+
+ const frozen = (entry?.isIntersecting || false) && freezeOnceVisible;
+
+ useEffect(() => {
+ if (!isDefined(IntersectionObserver) || !isDefined(ref.current) || frozen) {
+ return;
+ }
+
+ const observer = new IntersectionObserver(
+ ([entry]) => setEntry(entry),
+
+ { root, rootMargin, threshold }
+ );
+
+ observer.observe(ref.current);
+ return () => observer.disconnect();
+ }, [ref, frozen, root, rootMargin, threshold]);
+
+ return entry;
+}
languageName: node
linkType: hard
+"@react-spring/animated@npm:~9.7.3":
+ version: 9.7.3
+ resolution: "@react-spring/animated@npm:9.7.3"
+ dependencies:
+ "@react-spring/shared": ~9.7.3
+ "@react-spring/types": ~9.7.3
+ peerDependencies:
+ react: ^16.8.0 || ^17.0.0 || ^18.0.0
+ checksum: 468942ca3a11c02c3e56def26b2da9dd10ddbed548004245c4ac309cce00b58d971e781abed67db0d652f72737eaa73766ea9a43b8ef3b08a7ed2eddc04d4c39
+ languageName: node
+ linkType: hard
+
+"@react-spring/core@npm:~9.7.3":
+ version: 9.7.3
+ resolution: "@react-spring/core@npm:9.7.3"
+ dependencies:
+ "@react-spring/animated": ~9.7.3
+ "@react-spring/shared": ~9.7.3
+ "@react-spring/types": ~9.7.3
+ peerDependencies:
+ react: ^16.8.0 || ^17.0.0 || ^18.0.0
+ checksum: 8a80a07276458fd14099320eda824e58a11ce3a9b03a5c9cd3f4252adb4d26da04ee5caf5cbc961199f55c2d58a99638d5ea292cdb6aa029208dbab741b5c531
+ languageName: node
+ linkType: hard
+
+"@react-spring/rafz@npm:9.7.3":
+ version: 9.7.3
+ resolution: "@react-spring/rafz@npm:9.7.3"
+ checksum: c88b3c6306eab4a93a9f4ea38ad2e0ea3287a8b79a9a092cb1fd934a97ce092e41c32373b2b2e3de4b63027de7ae7414e0d442936dd8d94b51c2ff7d51e8f643
+ languageName: node
+ linkType: hard
+
+"@react-spring/shared@npm:~9.7.3":
+ version: 9.7.3
+ resolution: "@react-spring/shared@npm:9.7.3"
+ dependencies:
+ "@react-spring/types": ~9.7.3
+ peerDependencies:
+ react: ^16.8.0 || ^17.0.0 || ^18.0.0
+ checksum: 912b5e567eb5345c9a6c8e8c0c2d69b1f411af72a0685b95831809c267c89846a31341ca071f284ace98b3cb5de647054dc76f6ace81d6379513eaf96b52f195
+ languageName: node
+ linkType: hard
+
+"@react-spring/types@npm:~9.7.3":
+ version: 9.7.3
+ resolution: "@react-spring/types@npm:9.7.3"
+ checksum: f47b81fe556464aa54a78603311cb584d6a0f03088522229afb058265bbe2ade2095a55ec7f4e960c3b9cceaa5d47865bc41fc6643c0f5f4bd3d8650203d8389
+ languageName: node
+ linkType: hard
+
+"@react-spring/web@npm:9.7.3":
+ version: 9.7.3
+ resolution: "@react-spring/web@npm:9.7.3"
+ dependencies:
+ "@react-spring/animated": ~9.7.3
+ "@react-spring/core": ~9.7.3
+ "@react-spring/shared": ~9.7.3
+ "@react-spring/types": ~9.7.3
+ peerDependencies:
+ react: ^16.8.0 || ^17.0.0 || ^18.0.0
+ react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
+ checksum: 7f5cd05b2314b7f2f715e1926abcf9aa0a539399b222ab34e989144f48350adfcd2edab65d41425570f72c57f602fc6994d6730fbeed902171ac527b630a8a9b
+ languageName: node
+ linkType: hard
+
"@remix-run/router@npm:1.6.2":
version: 1.6.2
resolution: "@remix-run/router@npm:1.6.2"
"@emotion/react": 11.11.1
"@emotion/styled": 11.11.0
"@primer/octicons-react": 19.3.0
+ "@react-spring/rafz": 9.7.3
+ "@react-spring/web": 9.7.3
"@swc/core": 1.3.65
"@swc/jest": 0.2.26
"@tanstack/react-query": 4.29.14
onboarding.tutorial.return_to_list=Choose another option
onboarding.tutorial.ci_outro.all_set.title=You're all set!
-onboarding.tutorial.ci_outro.all_set.sentence={all_set} and ready to improve the quality and security of your code!
-onboarding.tutorial.ci_outro.all_set.sentence.all_set=You're all set
+onboarding.tutorial.ci_outro.done=And you are done!
+onboarding.tutorial.ci_outro.refresh_text=If everything is running successfully, once the analysis is complete you'll be redirected to the Overview page of your project where the new analysis results will be displayed. This can take a few minutes.
onboarding.tutorial.ci_outro.commit=Commit and push your code to start the analysis.
onboarding.tutorial.ci_outro.commit.why.gitlab=Each new push you make on your branches or merge requests will trigger a new analysis in SonarQube. We will decorate merge requests directly on GitLab for you.
onboarding.tutorial.ci_outro.commit.why.github=Each new push you make on your branches or pull requests will trigger a new analysis in SonarQube. We will decorate pull requests directly on GitHub for you.
onboarding.tutorial.ci_outro.commit.why.no_branches=Each new push you make on your main branch will trigger a new analysis in SonarQube.
onboarding.tutorial.ci_outro.refresh=This page will then refresh with your analysis results.
onboarding.tutorial.ci_outro.refresh.why=If the page doesn't refresh after a while, please double-check the analysis configuration, and check your logs.
-onboarding.tutorial.ci_outro.waiting_for_fist_analysis=Waiting for the first analysis to come in...
onboarding.tutorial.other.project_key.sentence=Create a {file} file in your repository and paste the following code:
onboarding.tutorial.cfamilly.compilation_database_info=If you have trouble using the build wrapper, you can try using a {link}.
onboarding.tutorial.cfamilly.compilation_database_info.link=compilation database