import GlobalNavUserContainer from './GlobalNavUserContainer';
import Search from '../../search/Search';
import GlobalHelp from '../../help/GlobalHelp';
+import Tooltip from '../../../../components/controls/Tooltip';
import HelpIcon from '../../../../components/icons-components/HelpIcon';
import OnboardingModal from '../../../../apps/tutorials/onboarding/OnboardingModal';
import { getCurrentUser, getAppState, getSettingValue } from '../../../../store/rootReducer';
+import { translate } from '../../../../helpers/l10n';
type Props = {
appState: { organizationsEnabled: boolean },
type State = {
helpOpen: boolean,
- onboardingTutorialOpen: boolean
+ onboardingTutorialOpen: boolean,
+ onboardingTutorialTooltip: boolean
};
class GlobalNav extends React.PureComponent {
+ interval: ?number;
props: Props;
- state: State = { helpOpen: false, onboardingTutorialOpen: false };
+ state: State = {
+ helpOpen: false,
+ onboardingTutorialOpen: false,
+ onboardingTutorialTooltip: false
+ };
componentDidMount() {
window.addEventListener('keypress', this.onKeyPress);
}
componentWillUnmount() {
+ if (this.interval) {
+ clearInterval(this.interval);
+ }
window.removeEventListener('keypress', this.onKeyPress);
}
openOnboardingTutorial = () => this.setState({ helpOpen: false, onboardingTutorialOpen: true });
- closeOnboardingTutorial = () => this.setState({ onboardingTutorialOpen: false });
+ finishOnboardingTutorial = () => this.setState({ onboardingTutorialOpen: false });
+
+ skipOnboardingTutorial = () => {
+ this.setState({ onboardingTutorialOpen: false, onboardingTutorialTooltip: true });
+ this.interval = setInterval(() => {
+ this.setState({ onboardingTutorialTooltip: false });
+ }, 3000);
+ };
render() {
return (
<Search appState={this.props.appState} currentUser={this.props.currentUser} />
<li>
<a className="navbar-help" onClick={this.handleHelpClick} href="#">
- <HelpIcon />
+ {this.state.onboardingTutorialTooltip
+ ? <Tooltip
+ defaultVisible={true}
+ overlay={translate('tutorials.follow_later')}
+ trigger="manual">
+ <HelpIcon />
+ </Tooltip>
+ : <HelpIcon />}
</a>
</li>
<GlobalNavUserContainer {...this.props} />
/>}
{this.state.onboardingTutorialOpen &&
- <OnboardingModal onClose={this.closeOnboardingTutorial} />}
+ <OnboardingModal
+ onFinish={this.finishOnboardingTutorial}
+ onSkip={this.skipOnboardingTutorial}
+ />}
</nav>
);
}
handleLanguageSelect = (result?: Result) => {
this.setState({ result });
- this.props.onFinish(result && result.projectKey);
+ const projectKey = result && result.language !== 'java' ? result.projectKey : undefined;
+ this.props.onFinish(projectKey);
};
handleLanguageReset = () => {
render() {
return (
<Step
+ finished={false}
+ onOpen={() => {}}
open={this.props.open}
renderForm={this.renderForm}
renderResult={this.renderResult}
// @flow
import React from 'react';
import { debounce } from 'lodash';
+import CloseIcon from '../../../components/icons-components/CloseIcon';
import {
createOrganization,
deleteOrganization,
? <form onSubmit={this.handleOrganizationDelete}>
<span className="spacer-right text-middle">{organization}</span>
{loading
- ? <i className="spinner" />
- : <button className="button-clean">
- <i className="icon-delete" />
+ ? <i className="spinner text-middle" />
+ : <button className="button-clean text-middle">
+ <CloseIcon className="icon-red" />
</button>}
</form>
: <form onSubmit={this.handleOrganizationCreate}>
value={organization}
/>
{loading
- ? <i className="spinner" />
+ ? <i className="spinner text-middle" />
: <button className="text-middle" disabled={!valid}>{translate('create')}</button>}
{!unique &&
<span className="big-spacer-left text-danger text-middle">
*/
// @flow
import React from 'react';
+import CloseIcon from '../../../components/icons-components/CloseIcon';
import { createProject, deleteProject } from '../../../api/components';
import { translate } from '../../../helpers/l10n';
? <form onSubmit={this.handleProjectDelete}>
<span className="spacer-right text-middle">{projectKey}</span>
{loading
- ? <i className="spinner" />
- : <button className="button-clean">
- <i className="icon-delete" />
+ ? <i className="spinner text-middle" />
+ : <button className="button-clean text-middle">
+ <CloseIcon className="icon-red" />
</button>}
</form>
: <form onSubmit={this.handleProjectCreate}>
value={projectKey}
/>
{loading
- ? <i className="spinner" />
+ ? <i className="spinner text-middle" />
: <button className="text-middle" disabled={!valid}>{translate('Done')}</button>}
<div className="note spacer-top abs-width-300">
{translate('onboarding.project_key_requirement')}
*/
// @flow
import React from 'react';
+import Helmet from 'react-helmet';
import TokenStep from './TokenStep';
import OrganizationStep from './OrganizationStep';
import AnalysisStep from './AnalysisStep';
import handleRequiredAuthentication from '../../../app/utils/handleRequiredAuthentication';
import './styles.css';
-type Props = {
+type Props = {|
currentUser: { login: string, isLoggedIn: boolean },
+ onFinish: () => void,
onSkip: () => void,
organizationsEnabled: boolean,
sonarCloud: boolean
-};
+|};
type State = {
finished: boolean,
this.mounted = false;
}
- finishOnboarding = () => {
+ finishOnboarding = (skipped: boolean = false) => {
this.setState({ skipping: true });
skipOnboarding().then(
() => {
if (this.mounted) {
- this.props.onSkip();
+ if (skipped) {
+ this.props.onSkip();
+ } else {
+ this.props.onFinish();
+ }
if (this.state.projectKey) {
this.context.router.push(getProjectUrl(this.state.projectKey));
this.setState({ organization, step: 'token' });
};
+ handleTokenOpen = () => this.setState({ step: 'token' });
+
+ handleOrganizationOpen = () => this.setState({ step: 'organization' });
+
handleSkipClick = (event: Event) => {
event.preventDefault();
- this.finishOnboarding();
+ this.finishOnboarding(true);
};
handleFinish = (projectKey?: string) => this.setState({ finished: true, projectKey });
let stepNumber = 1;
+ const header = translate(sonarCloud ? 'onboarding.header.sonarcloud' : 'onboarding.header');
+
return (
- <div className="page page-limited">
- <header className="page-header">
- <h1 className="page-title">
- {translate(sonarCloud ? 'onboarding.header.sonarcloud' : 'onboarding.header')}
- </h1>
- <div className="page-actions">
- {this.state.skipping
- ? <i className="spinner" />
- : <a className="js-skip text-muted" href="#" onClick={this.handleSkipClick}>
- {translate('tutorials.skip')}
- </a>}
- </div>
- <div className="page-description">
- {translate('onboarding.header.description')}
- </div>
- </header>
-
- {organizationsEnabled &&
- <OrganizationStep
- currentUser={this.props.currentUser}
- onContinue={this.handleOrganizationDone}
- open={step === 'organization'}
+ <div className="modal-container">
+ <Helmet title={header} titleTemplate="%s" />
+
+ <div className="page page-limited onboarding">
+ <header className="page-header">
+ <h1 className="page-title">{header}</h1>
+ <div className="page-actions">
+ {this.state.skipping
+ ? <i className="spinner" />
+ : <a className="js-skip text-muted" href="#" onClick={this.handleSkipClick}>
+ {translate('tutorials.skip')}
+ </a>}
+ </div>
+ <div className="page-description">
+ {translate('onboarding.header.description')}
+ </div>
+ </header>
+
+ {organizationsEnabled &&
+ <OrganizationStep
+ currentUser={this.props.currentUser}
+ finished={this.state.organization != null}
+ onContinue={this.handleOrganizationDone}
+ onOpen={this.handleOrganizationOpen}
+ open={step === 'organization'}
+ stepNumber={stepNumber++}
+ />}
+
+ <TokenStep
+ finished={this.state.token != null}
+ onContinue={this.handleTokenDone}
+ onOpen={this.handleTokenOpen}
+ open={step === 'token'}
stepNumber={stepNumber++}
- />}
-
- <TokenStep
- onContinue={this.handleTokenDone}
- open={step === 'token'}
- stepNumber={stepNumber++}
- />
-
- <AnalysisStep
- onFinish={this.handleFinish}
- onReset={this.handleReset}
- organization={this.state.organization}
- open={step === 'analysis'}
- sonarCloud={sonarCloud}
- stepNumber={stepNumber}
- token={token}
- />
-
- {this.state.finished &&
- !this.state.skipping &&
- (this.state.projectKey
- ? <ProjectWatcher
- onFinish={this.finishOnboarding}
- onTimeout={this.handleTimeout}
- projectKey={this.state.projectKey}
- />
- : <footer className="text-right">
- <a className="button" href="#" onClick={this.handleSkipClick}>
- {translate('tutorials.finish')}
- </a>
- </footer>)}
+ />
+
+ <AnalysisStep
+ onFinish={this.handleFinish}
+ onReset={this.handleReset}
+ organization={this.state.organization}
+ open={step === 'analysis'}
+ sonarCloud={sonarCloud}
+ stepNumber={stepNumber}
+ token={token}
+ />
+
+ {this.state.finished &&
+ !this.state.skipping &&
+ (this.state.projectKey
+ ? <ProjectWatcher
+ onFinish={this.finishOnboarding}
+ onTimeout={this.handleTimeout}
+ projectKey={this.state.projectKey}
+ />
+ : <footer className="text-right">
+ <a className="button" href="#" onClick={this.handleSkipClick}>
+ {translate('tutorials.finish')}
+ </a>
+ </footer>)}
+ </div>
</div>
);
}
import Modal from 'react-modal';
import { translate } from '../../../helpers/l10n';
-type Props = {
- onClose: () => void
-};
+type Props = {|
+ onFinish: () => void,
+ onSkip: () => void
+|};
type State = {
OnboardingContainer?: Object
<Modal
isOpen={true}
contentLabel={translate('tutorials.onboarding')}
- className="modal modal-full-screen"
+ className="modal modal-large"
overlayClassName="modal-overlay">
- {OnboardingContainer != null && <OnboardingContainer onSkip={this.props.onClose} />}
+ {OnboardingContainer != null &&
+ <OnboardingContainer onFinish={this.props.onFinish} onSkip={this.props.onSkip} />}
</Modal>
);
}
import { getMyOrganizations } from '../../../api/organizations';
import { translate } from '../../../helpers/l10n';
-type Props = {
+type Props = {|
currentUser: { login: string, isLoggedIn: boolean },
+ finished: boolean,
+ onOpen: () => void,
+ onContinue: (organization: string) => void,
open: boolean,
- onContinue: (organization: string) => void
-};
+ stepNumber: number
+|};
type State = {
loading: boolean,
render() {
return (
<Step
+ finished={this.props.finished}
+ onOpen={this.props.onOpen}
open={this.props.open}
renderForm={this.renderForm}
renderResult={this.renderResult}
- stepNumber={1}
+ stepNumber={this.props.stepNumber}
stepTitle={translate('onboarding.organization.header')}
/>
);
import React from 'react';
import classNames from 'classnames';
-type Props = {
+type Props = {|
+ finished: boolean,
+ onOpen: () => void,
open: boolean,
renderForm: () => React.Element<*>,
renderResult: () => ?React.Element<*>,
stepNumber: number,
stepTitle: string
-};
+|};
export default function Step(props: Props) {
const className = classNames('boxed-group', 'onboarding-step', {
- 'onboarding-step-open': props.open
+ 'is-open': props.open,
+ 'is-finished': props.finished
});
+ const clickable = !props.open && props.finished;
+
+ const handleClick = (event: Event) => {
+ event.preventDefault;
+ props.onOpen();
+ };
+
return (
- <div className={className}>
+ <div
+ className={className}
+ onClick={clickable ? handleClick : undefined}
+ role={clickable ? 'button' : undefined}
+ tabIndex={clickable ? 0 : undefined}>
<div className="onboarding-step-number">{props.stepNumber}</div>
{!props.open && props.renderResult()}
<div className="boxed-group-header">
// @flow
import React from 'react';
import Step from './Step';
+import CloseIcon from '../../../components/icons-components/CloseIcon';
import { generateToken, revokeToken } from '../../../api/user-tokens';
import { translate } from '../../../helpers/l10n';
-type Props = {
+type Props = {|
+ finished: boolean,
open: boolean,
onContinue: (token: string) => void,
+ onOpen: () => void,
stepNumber: number
-};
+|};
type State = {
loading: boolean,
export default class TokenStep extends React.PureComponent {
mounted: boolean;
props: Props;
-
- static defaultProps = {
- stepNumber: 1
- };
-
state: State = {
loading: false
};
return (
<div className="boxed-group-inner">
- <div className="big-spacer-bottom width-50">
- {translate('onboarding.token.text')}
- </div>
-
{token != null
? <form onSubmit={this.handleTokenRevoke}>
- {tokenName}{': '}
- <span className="monospaced spacer-right">{token}</span>
+ <span className="text-middle">{tokenName}{': '}</span>
+ <strong className="spacer-right text-middle">{token}</strong>
{loading
- ? <i className="spinner" />
- : <button className="button-clean" onClick={this.handleTokenRevoke}>
- <i className="icon-delete" />
+ ? <i className="spinner text-middle" />
+ : <button className="button-clean text-middle" onClick={this.handleTokenRevoke}>
+ <CloseIcon className="icon-red" />
</button>}
</form>
: <form onSubmit={this.handleTokenGenerate}>
<input
autoFocus={true}
- className="input-large spacer-right"
+ className="input-large spacer-right text-middle"
onChange={this.handleTokenNameChange}
placeholder={translate('onboarding.token.placeholder')}
required={true}
value={tokenName || ''}
/>
{loading
- ? <i className="spinner" />
- : <button>{translate('onboarding.token.generate')}</button>}
+ ? <i className="spinner text-middle" />
+ : <button className="text-middle">{translate('onboarding.token.generate')}</button>}
</form>}
+ <div className="note big-spacer-top width-50">
+ {translate('onboarding.token.text')}
+ </div>
+
{token != null &&
<div className="big-spacer-top">
<button className="js-continue" onClick={this.handleContinueClick}>
<div className="boxed-group-actions">
<i className="icon-check spacer-right" />
{tokenName}{': '}
- <strong className="monospaced">{token}</strong>
+ <strong>{token}</strong>
</div>
);
};
render() {
return (
<Step
+ finished={this.props.finished}
+ onOpen={this.props.onOpen}
open={this.props.open}
renderForm={this.renderForm}
renderResult={this.renderResult}
const wrapper = shallow(
<Onboarding
currentUser={currentUser}
+ onFinish={jest.fn()}
onSkip={jest.fn()}
organizationsEnabled={false}
sonarCloud={false}
const wrapper = shallow(
<Onboarding
currentUser={currentUser}
+ onFinish={jest.fn()}
onSkip={jest.fn()}
organizationsEnabled={true}
sonarCloud={true}
const wrapper = mount(
<Onboarding
currentUser={currentUser}
+ onFinish={jest.fn()}
onSkip={onSkip}
organizationsEnabled={false}
sonarCloud={false}
it('works with personal organization', () => {
const onContinue = jest.fn();
const wrapper = mount(
- <OrganizationStep currentUser={currentUser} onContinue={onContinue} open={true} />
+ <OrganizationStep
+ currentUser={currentUser}
+ finished={false}
+ onContinue={onContinue}
+ onOpen={jest.fn()}
+ open={true}
+ stepNumber={1}
+ />
);
click(wrapper.find('.js-continue'));
expect(onContinue).toBeCalledWith('user');
it('works with existing organization', () => {
const onContinue = jest.fn();
const wrapper = mount(
- <OrganizationStep currentUser={currentUser} onContinue={onContinue} open={true} />
+ <OrganizationStep
+ currentUser={currentUser}
+ finished={false}
+ onContinue={onContinue}
+ onOpen={jest.fn()}
+ open={true}
+ stepNumber={1}
+ />
);
return doAsync(() => {
click(wrapper.find('.js-existing'));
it('works with new organization', () => {
const onContinue = jest.fn();
const wrapper = mount(
- <OrganizationStep currentUser={currentUser} onContinue={onContinue} open={true} />
+ <OrganizationStep
+ currentUser={currentUser}
+ finished={false}
+ onContinue={onContinue}
+ onOpen={jest.fn()}
+ open={true}
+ stepNumber={1}
+ />
);
click(wrapper.find('.js-new'));
wrapper.find('NewOrganizationForm').prop('onDone')('new');
import React from 'react';
import { shallow } from 'enzyme';
import Step from '../Step';
+import { click } from '../../../../helpers/testUtils';
it('renders', () => {
const wrapper = shallow(
<Step
+ finished={true}
+ onOpen={jest.fn()}
open={true}
renderForm={() => <div>form</div>}
renderResult={() => <div>result</div>}
wrapper.setProps({ open: false });
expect(wrapper).toMatchSnapshot();
});
+
+it('re-opens', () => {
+ const onOpen = jest.fn();
+ const wrapper = shallow(
+ <Step
+ finished={true}
+ onOpen={onOpen}
+ open={false}
+ renderForm={() => <div>form</div>}
+ renderResult={() => <div>result</div>}
+ stepNumber={1}
+ stepTitle="First Step"
+ />
+ );
+ click(wrapper);
+ expect(onOpen).toBeCalled();
+});
}));
it('generates token', () => {
- const wrapper = mount(<TokenStep open={true} onContinue={jest.fn()} />);
+ const wrapper = mount(
+ <TokenStep
+ finished={false}
+ open={true}
+ onContinue={jest.fn()}
+ onOpen={jest.fn()}
+ stepNumber={1}
+ />
+ );
expect(wrapper).toMatchSnapshot();
change(wrapper.find('input'), 'my token');
submit(wrapper.find('form'));
});
it('revokes token', () => {
- const wrapper = mount(<TokenStep open={true} onContinue={jest.fn()} />);
+ const wrapper = mount(
+ <TokenStep
+ finished={false}
+ open={true}
+ onContinue={jest.fn()}
+ onOpen={jest.fn()}
+ stepNumber={1}
+ />
+ );
wrapper.setState({ token: 'abcd1234', tokenName: 'my token' });
expect(wrapper).toMatchSnapshot();
submit(wrapper.find('form'));
it('continues', () => {
const onContinue = jest.fn();
- const wrapper = mount(<TokenStep open={true} onContinue={onContinue} />);
+ const wrapper = mount(
+ <TokenStep
+ finished={false}
+ open={true}
+ onContinue={onContinue}
+ onOpen={jest.fn()}
+ stepNumber={1}
+ />
+ );
wrapper.setState({ token: 'abcd1234', tokenName: 'my token' });
click(wrapper.find('.js-continue'));
expect(onContinue).toBeCalledWith('abcd1234');
value="foo"
/>
<i
- className="spinner"
+ className="spinner text-middle"
/>
<div
className="note spacer-top abs-width-300"
foo
</span>
<button
- className="button-clean"
+ className="button-clean text-middle"
>
- <i
- className="icon-delete"
- />
+ <CloseIcon
+ className="icon-red"
+ >
+ <svg
+ className="icon-red"
+ height={16}
+ viewBox="0 0 16 16"
+ width={16}
+ xmlns="http://www.w3.org/2000/svg"
+ >
+ <path
+ d="M12.843 11.232q0 0.357-0.25 0.607l-1.214 1.214q-0.25 0.25-0.607 0.25t-0.607-0.25l-2.625-2.625-2.625 2.625q-0.25 0.25-0.607 0.25t-0.607-0.25l-1.214-1.214q-0.25-0.25-0.25-0.607t0.25-0.607l2.625-2.625-2.625-2.625q-0.25-0.25-0.25-0.607t0.25-0.607l1.214-1.214q0.25-0.25 0.607-0.25t0.607 0.25l2.625 2.625 2.625-2.625q0.25-0.25 0.607-0.25t0.607 0.25l1.214 1.214q0.25 0.25 0.25 0.607t-0.25 0.607l-2.625 2.625 2.625 2.625q0.25 0.25 0.25 0.607z"
+ fill="currentColor"
+ />
+ </svg>
+ </CloseIcon>
</button>
</form>
</NewOrganizationForm>
foo
</span>
<button
- className="button-clean"
+ className="button-clean text-middle"
>
- <i
- className="icon-delete"
- />
+ <CloseIcon
+ className="icon-red"
+ >
+ <svg
+ className="icon-red"
+ height={16}
+ viewBox="0 0 16 16"
+ width={16}
+ xmlns="http://www.w3.org/2000/svg"
+ >
+ <path
+ d="M12.843 11.232q0 0.357-0.25 0.607l-1.214 1.214q-0.25 0.25-0.607 0.25t-0.607-0.25l-2.625-2.625-2.625 2.625q-0.25 0.25-0.607 0.25t-0.607-0.25l-1.214-1.214q-0.25-0.25-0.25-0.607t0.25-0.607l2.625-2.625-2.625-2.625q-0.25-0.25-0.25-0.607t0.25-0.607l1.214-1.214q0.25-0.25 0.607-0.25t0.607 0.25l2.625 2.625 2.625-2.625q0.25-0.25 0.607-0.25t0.607 0.25l1.214 1.214q0.25 0.25 0.25 0.607t-0.25 0.607l-2.625 2.625 2.625 2.625q0.25 0.25 0.25 0.607z"
+ fill="currentColor"
+ />
+ </svg>
+ </CloseIcon>
</button>
</form>
</NewOrganizationForm>
foo
</span>
<i
- className="spinner"
+ className="spinner text-middle"
/>
</form>
</NewOrganizationForm>
value="foo"
/>
<i
- className="spinner"
+ className="spinner text-middle"
/>
<div
className="note spacer-top abs-width-300"
foo
</span>
<button
- className="button-clean"
+ className="button-clean text-middle"
>
- <i
- className="icon-delete"
- />
+ <CloseIcon
+ className="icon-red"
+ >
+ <svg
+ className="icon-red"
+ height={16}
+ viewBox="0 0 16 16"
+ width={16}
+ xmlns="http://www.w3.org/2000/svg"
+ >
+ <path
+ d="M12.843 11.232q0 0.357-0.25 0.607l-1.214 1.214q-0.25 0.25-0.607 0.25t-0.607-0.25l-2.625-2.625-2.625 2.625q-0.25 0.25-0.607 0.25t-0.607-0.25l-1.214-1.214q-0.25-0.25-0.25-0.607t0.25-0.607l2.625-2.625-2.625-2.625q-0.25-0.25-0.25-0.607t0.25-0.607l1.214-1.214q0.25-0.25 0.607-0.25t0.607 0.25l2.625 2.625 2.625-2.625q0.25-0.25 0.607-0.25t0.607 0.25l1.214 1.214q0.25 0.25 0.25 0.607t-0.25 0.607l-2.625 2.625 2.625 2.625q0.25 0.25 0.25 0.607z"
+ fill="currentColor"
+ />
+ </svg>
+ </CloseIcon>
</button>
</form>
</div>
foo
</span>
<button
- className="button-clean"
+ className="button-clean text-middle"
>
- <i
- className="icon-delete"
- />
+ <CloseIcon
+ className="icon-red"
+ >
+ <svg
+ className="icon-red"
+ height={16}
+ viewBox="0 0 16 16"
+ width={16}
+ xmlns="http://www.w3.org/2000/svg"
+ >
+ <path
+ d="M12.843 11.232q0 0.357-0.25 0.607l-1.214 1.214q-0.25 0.25-0.607 0.25t-0.607-0.25l-2.625-2.625-2.625 2.625q-0.25 0.25-0.607 0.25t-0.607-0.25l-1.214-1.214q-0.25-0.25-0.25-0.607t0.25-0.607l2.625-2.625-2.625-2.625q-0.25-0.25-0.25-0.607t0.25-0.607l1.214-1.214q0.25-0.25 0.607-0.25t0.607 0.25l2.625 2.625 2.625-2.625q0.25-0.25 0.607-0.25t0.607 0.25l1.214 1.214q0.25 0.25 0.25 0.607t-0.25 0.607l-2.625 2.625 2.625 2.625q0.25 0.25 0.25 0.607z"
+ fill="currentColor"
+ />
+ </svg>
+ </CloseIcon>
</button>
</form>
</div>
foo
</span>
<i
- className="spinner"
+ className="spinner text-middle"
/>
</form>
</div>
exports[`guides for on-premise 1`] = `
<div
- className="page page-limited"
+ className="modal-container"
>
- <header
- className="page-header"
+ <HelmetWrapper
+ title="onboarding.header"
+ titleTemplate="%s"
+ />
+ <div
+ className="page page-limited onboarding"
>
- <h1
- className="page-title"
- >
- onboarding.header
- </h1>
- <div
- className="page-actions"
+ <header
+ className="page-header"
>
- <a
- className="js-skip text-muted"
- href="#"
- onClick={[Function]}
+ <h1
+ className="page-title"
>
- tutorials.skip
- </a>
- </div>
- <div
- className="page-description"
- >
- onboarding.header.description
- </div>
- </header>
- <TokenStep
- onContinue={[Function]}
- open={true}
- stepNumber={1}
- />
- <AnalysisStep
- onFinish={[Function]}
- onReset={[Function]}
- open={false}
- sonarCloud={false}
- stepNumber={2}
- />
+ onboarding.header
+ </h1>
+ <div
+ className="page-actions"
+ >
+ <a
+ className="js-skip text-muted"
+ href="#"
+ onClick={[Function]}
+ >
+ tutorials.skip
+ </a>
+ </div>
+ <div
+ className="page-description"
+ >
+ onboarding.header.description
+ </div>
+ </header>
+ <TokenStep
+ finished={false}
+ onContinue={[Function]}
+ onOpen={[Function]}
+ open={true}
+ stepNumber={1}
+ />
+ <AnalysisStep
+ onFinish={[Function]}
+ onReset={[Function]}
+ open={false}
+ sonarCloud={false}
+ stepNumber={2}
+ />
+ </div>
</div>
`;
exports[`guides for on-premise 2`] = `
<div
- className="page page-limited"
+ className="modal-container"
>
- <header
- className="page-header"
+ <HelmetWrapper
+ title="onboarding.header"
+ titleTemplate="%s"
+ />
+ <div
+ className="page page-limited onboarding"
>
- <h1
- className="page-title"
+ <header
+ className="page-header"
>
- onboarding.header
- </h1>
- <div
- className="page-actions"
- >
- <a
- className="js-skip text-muted"
- href="#"
- onClick={[Function]}
+ <h1
+ className="page-title"
>
- tutorials.skip
- </a>
- </div>
- <div
- className="page-description"
- >
- onboarding.header.description
- </div>
- </header>
- <TokenStep
- onContinue={[Function]}
- open={false}
- stepNumber={1}
- />
- <AnalysisStep
- onFinish={[Function]}
- onReset={[Function]}
- open={true}
- sonarCloud={false}
- stepNumber={2}
- token="abcd1234"
- />
+ onboarding.header
+ </h1>
+ <div
+ className="page-actions"
+ >
+ <a
+ className="js-skip text-muted"
+ href="#"
+ onClick={[Function]}
+ >
+ tutorials.skip
+ </a>
+ </div>
+ <div
+ className="page-description"
+ >
+ onboarding.header.description
+ </div>
+ </header>
+ <TokenStep
+ finished={true}
+ onContinue={[Function]}
+ onOpen={[Function]}
+ open={false}
+ stepNumber={1}
+ />
+ <AnalysisStep
+ onFinish={[Function]}
+ onReset={[Function]}
+ open={true}
+ sonarCloud={false}
+ stepNumber={2}
+ token="abcd1234"
+ />
+ </div>
</div>
`;
exports[`guides for sonarcloud 1`] = `
<div
- className="page page-limited"
+ className="modal-container"
>
- <header
- className="page-header"
+ <HelmetWrapper
+ title="onboarding.header.sonarcloud"
+ titleTemplate="%s"
+ />
+ <div
+ className="page page-limited onboarding"
>
- <h1
- className="page-title"
+ <header
+ className="page-header"
>
- onboarding.header.sonarcloud
- </h1>
- <div
- className="page-actions"
- >
- <a
- className="js-skip text-muted"
- href="#"
- onClick={[Function]}
+ <h1
+ className="page-title"
>
- tutorials.skip
- </a>
- </div>
- <div
- className="page-description"
- >
- onboarding.header.description
- </div>
- </header>
- <OrganizationStep
- currentUser={
- Object {
- "isLoggedIn": true,
- "login": "admin",
+ onboarding.header.sonarcloud
+ </h1>
+ <div
+ className="page-actions"
+ >
+ <a
+ className="js-skip text-muted"
+ href="#"
+ onClick={[Function]}
+ >
+ tutorials.skip
+ </a>
+ </div>
+ <div
+ className="page-description"
+ >
+ onboarding.header.description
+ </div>
+ </header>
+ <OrganizationStep
+ currentUser={
+ Object {
+ "isLoggedIn": true,
+ "login": "admin",
+ }
}
- }
- onContinue={[Function]}
- open={true}
- stepNumber={1}
- />
- <TokenStep
- onContinue={[Function]}
- open={false}
- stepNumber={2}
- />
- <AnalysisStep
- onFinish={[Function]}
- onReset={[Function]}
- open={false}
- sonarCloud={true}
- stepNumber={3}
- />
+ finished={false}
+ onContinue={[Function]}
+ onOpen={[Function]}
+ open={true}
+ stepNumber={1}
+ />
+ <TokenStep
+ finished={false}
+ onContinue={[Function]}
+ onOpen={[Function]}
+ open={false}
+ stepNumber={2}
+ />
+ <AnalysisStep
+ onFinish={[Function]}
+ onReset={[Function]}
+ open={false}
+ sonarCloud={true}
+ stepNumber={3}
+ />
+ </div>
</div>
`;
exports[`guides for sonarcloud 2`] = `
<div
- className="page page-limited"
+ className="modal-container"
>
- <header
- className="page-header"
+ <HelmetWrapper
+ title="onboarding.header.sonarcloud"
+ titleTemplate="%s"
+ />
+ <div
+ className="page page-limited onboarding"
>
- <h1
- className="page-title"
- >
- onboarding.header.sonarcloud
- </h1>
- <div
- className="page-actions"
+ <header
+ className="page-header"
>
- <a
- className="js-skip text-muted"
- href="#"
- onClick={[Function]}
+ <h1
+ className="page-title"
>
- tutorials.skip
- </a>
- </div>
- <div
- className="page-description"
- >
- onboarding.header.description
- </div>
- </header>
- <OrganizationStep
- currentUser={
- Object {
- "isLoggedIn": true,
- "login": "admin",
+ onboarding.header.sonarcloud
+ </h1>
+ <div
+ className="page-actions"
+ >
+ <a
+ className="js-skip text-muted"
+ href="#"
+ onClick={[Function]}
+ >
+ tutorials.skip
+ </a>
+ </div>
+ <div
+ className="page-description"
+ >
+ onboarding.header.description
+ </div>
+ </header>
+ <OrganizationStep
+ currentUser={
+ Object {
+ "isLoggedIn": true,
+ "login": "admin",
+ }
}
- }
- onContinue={[Function]}
- open={false}
- stepNumber={1}
- />
- <TokenStep
- onContinue={[Function]}
- open={true}
- stepNumber={2}
- />
- <AnalysisStep
- onFinish={[Function]}
- onReset={[Function]}
- open={false}
- organization="my-org"
- sonarCloud={true}
- stepNumber={3}
- />
+ finished={true}
+ onContinue={[Function]}
+ onOpen={[Function]}
+ open={false}
+ stepNumber={1}
+ />
+ <TokenStep
+ finished={false}
+ onContinue={[Function]}
+ onOpen={[Function]}
+ open={true}
+ stepNumber={2}
+ />
+ <AnalysisStep
+ onFinish={[Function]}
+ onReset={[Function]}
+ open={false}
+ organization="my-org"
+ sonarCloud={true}
+ stepNumber={3}
+ />
+ </div>
</div>
`;
exports[`guides for sonarcloud 3`] = `
<div
- className="page page-limited"
+ className="modal-container"
>
- <header
- className="page-header"
+ <HelmetWrapper
+ title="onboarding.header.sonarcloud"
+ titleTemplate="%s"
+ />
+ <div
+ className="page page-limited onboarding"
>
- <h1
- className="page-title"
- >
- onboarding.header.sonarcloud
- </h1>
- <div
- className="page-actions"
+ <header
+ className="page-header"
>
- <a
- className="js-skip text-muted"
- href="#"
- onClick={[Function]}
+ <h1
+ className="page-title"
>
- tutorials.skip
- </a>
- </div>
- <div
- className="page-description"
- >
- onboarding.header.description
- </div>
- </header>
- <OrganizationStep
- currentUser={
- Object {
- "isLoggedIn": true,
- "login": "admin",
+ onboarding.header.sonarcloud
+ </h1>
+ <div
+ className="page-actions"
+ >
+ <a
+ className="js-skip text-muted"
+ href="#"
+ onClick={[Function]}
+ >
+ tutorials.skip
+ </a>
+ </div>
+ <div
+ className="page-description"
+ >
+ onboarding.header.description
+ </div>
+ </header>
+ <OrganizationStep
+ currentUser={
+ Object {
+ "isLoggedIn": true,
+ "login": "admin",
+ }
}
- }
- onContinue={[Function]}
- open={false}
- stepNumber={1}
- />
- <TokenStep
- onContinue={[Function]}
- open={false}
- stepNumber={2}
- />
- <AnalysisStep
- onFinish={[Function]}
- onReset={[Function]}
- open={true}
- organization="my-org"
- sonarCloud={true}
- stepNumber={3}
- token="abcd1234"
- />
+ finished={true}
+ onContinue={[Function]}
+ onOpen={[Function]}
+ open={false}
+ stepNumber={1}
+ />
+ <TokenStep
+ finished={true}
+ onContinue={[Function]}
+ onOpen={[Function]}
+ open={false}
+ stepNumber={2}
+ />
+ <AnalysisStep
+ onFinish={[Function]}
+ onReset={[Function]}
+ open={true}
+ organization="my-org"
+ sonarCloud={true}
+ stepNumber={3}
+ token="abcd1234"
+ />
+ </div>
</div>
`;
exports[`renders 1`] = `
<div
- className="boxed-group onboarding-step onboarding-step-open"
+ className="boxed-group onboarding-step is-open is-finished"
>
<div
className="onboarding-step-number"
exports[`renders 2`] = `
<div
- className="boxed-group onboarding-step"
+ className="boxed-group onboarding-step is-finished"
+ onClick={[Function]}
+ role="button"
+ tabIndex={0}
>
<div
className="onboarding-step-number"
exports[`generates token 1`] = `
<TokenStep
+ finished={false}
onContinue={[Function]}
+ onOpen={[Function]}
open={true}
stepNumber={1}
>
<Step
+ finished={false}
+ onOpen={[Function]}
open={true}
renderForm={[Function]}
renderResult={[Function]}
stepTitle="onboarding.token.header"
>
<div
- className="boxed-group onboarding-step onboarding-step-open"
+ className="boxed-group onboarding-step is-open"
>
<div
className="onboarding-step-number"
<div
className="boxed-group-inner"
>
- <div
- className="big-spacer-bottom width-50"
- >
- onboarding.token.text
- </div>
<form
onSubmit={[Function]}
>
<input
autoFocus={true}
- className="input-large spacer-right"
+ className="input-large spacer-right text-middle"
onChange={[Function]}
placeholder="onboarding.token.placeholder"
required={true}
type="text"
value=""
/>
- <button>
+ <button
+ className="text-middle"
+ >
onboarding.token.generate
</button>
</form>
+ <div
+ className="note big-spacer-top width-50"
+ >
+ onboarding.token.text
+ </div>
</div>
</div>
</Step>
exports[`generates token 2`] = `
<TokenStep
+ finished={false}
onContinue={[Function]}
+ onOpen={[Function]}
open={true}
stepNumber={1}
>
<Step
+ finished={false}
+ onOpen={[Function]}
open={true}
renderForm={[Function]}
renderResult={[Function]}
stepTitle="onboarding.token.header"
>
<div
- className="boxed-group onboarding-step onboarding-step-open"
+ className="boxed-group onboarding-step is-open"
>
<div
className="onboarding-step-number"
<div
className="boxed-group-inner"
>
- <div
- className="big-spacer-bottom width-50"
- >
- onboarding.token.text
- </div>
<form
onSubmit={[Function]}
>
<input
autoFocus={true}
- className="input-large spacer-right"
+ className="input-large spacer-right text-middle"
onChange={[Function]}
placeholder="onboarding.token.placeholder"
required={true}
value="my token"
/>
<i
- className="spinner"
+ className="spinner text-middle"
/>
</form>
+ <div
+ className="note big-spacer-top width-50"
+ >
+ onboarding.token.text
+ </div>
</div>
</div>
</Step>
exports[`generates token 3`] = `
<TokenStep
+ finished={false}
onContinue={[Function]}
+ onOpen={[Function]}
open={true}
stepNumber={1}
>
<Step
+ finished={false}
+ onOpen={[Function]}
open={true}
renderForm={[Function]}
renderResult={[Function]}
stepTitle="onboarding.token.header"
>
<div
- className="boxed-group onboarding-step onboarding-step-open"
+ className="boxed-group onboarding-step is-open"
>
<div
className="onboarding-step-number"
<div
className="boxed-group-inner"
>
- <div
- className="big-spacer-bottom width-50"
- >
- onboarding.token.text
- </div>
<form
onSubmit={[Function]}
>
- my token
- :
<span
- className="monospaced spacer-right"
+ className="text-middle"
>
- abcd1234
+ my token
+ :
</span>
+ <strong
+ className="spacer-right text-middle"
+ >
+ abcd1234
+ </strong>
<button
- className="button-clean"
+ className="button-clean text-middle"
onClick={[Function]}
>
- <i
- className="icon-delete"
- />
+ <CloseIcon
+ className="icon-red"
+ >
+ <svg
+ className="icon-red"
+ height={16}
+ viewBox="0 0 16 16"
+ width={16}
+ xmlns="http://www.w3.org/2000/svg"
+ >
+ <path
+ d="M12.843 11.232q0 0.357-0.25 0.607l-1.214 1.214q-0.25 0.25-0.607 0.25t-0.607-0.25l-2.625-2.625-2.625 2.625q-0.25 0.25-0.607 0.25t-0.607-0.25l-1.214-1.214q-0.25-0.25-0.25-0.607t0.25-0.607l2.625-2.625-2.625-2.625q-0.25-0.25-0.25-0.607t0.25-0.607l1.214-1.214q0.25-0.25 0.607-0.25t0.607 0.25l2.625 2.625 2.625-2.625q0.25-0.25 0.607-0.25t0.607 0.25l1.214 1.214q0.25 0.25 0.25 0.607t-0.25 0.607l-2.625 2.625 2.625 2.625q0.25 0.25 0.25 0.607z"
+ fill="currentColor"
+ />
+ </svg>
+ </CloseIcon>
</button>
</form>
+ <div
+ className="note big-spacer-top width-50"
+ >
+ onboarding.token.text
+ </div>
<div
className="big-spacer-top"
>
exports[`revokes token 1`] = `
<TokenStep
+ finished={false}
onContinue={[Function]}
+ onOpen={[Function]}
open={true}
stepNumber={1}
>
<Step
+ finished={false}
+ onOpen={[Function]}
open={true}
renderForm={[Function]}
renderResult={[Function]}
stepTitle="onboarding.token.header"
>
<div
- className="boxed-group onboarding-step onboarding-step-open"
+ className="boxed-group onboarding-step is-open"
>
<div
className="onboarding-step-number"
<div
className="boxed-group-inner"
>
- <div
- className="big-spacer-bottom width-50"
- >
- onboarding.token.text
- </div>
<form
onSubmit={[Function]}
>
- my token
- :
<span
- className="monospaced spacer-right"
+ className="text-middle"
>
- abcd1234
+ my token
+ :
</span>
+ <strong
+ className="spacer-right text-middle"
+ >
+ abcd1234
+ </strong>
<button
- className="button-clean"
+ className="button-clean text-middle"
onClick={[Function]}
>
- <i
- className="icon-delete"
- />
+ <CloseIcon
+ className="icon-red"
+ >
+ <svg
+ className="icon-red"
+ height={16}
+ viewBox="0 0 16 16"
+ width={16}
+ xmlns="http://www.w3.org/2000/svg"
+ >
+ <path
+ d="M12.843 11.232q0 0.357-0.25 0.607l-1.214 1.214q-0.25 0.25-0.607 0.25t-0.607-0.25l-2.625-2.625-2.625 2.625q-0.25 0.25-0.607 0.25t-0.607-0.25l-1.214-1.214q-0.25-0.25-0.25-0.607t0.25-0.607l2.625-2.625-2.625-2.625q-0.25-0.25-0.25-0.607t0.25-0.607l1.214-1.214q0.25-0.25 0.607-0.25t0.607 0.25l2.625 2.625 2.625-2.625q0.25-0.25 0.607-0.25t0.607 0.25l1.214 1.214q0.25 0.25 0.25 0.607t-0.25 0.607l-2.625 2.625 2.625 2.625q0.25 0.25 0.25 0.607z"
+ fill="currentColor"
+ />
+ </svg>
+ </CloseIcon>
</button>
</form>
+ <div
+ className="note big-spacer-top width-50"
+ >
+ onboarding.token.text
+ </div>
<div
className="big-spacer-top"
>
exports[`revokes token 2`] = `
<TokenStep
+ finished={false}
onContinue={[Function]}
+ onOpen={[Function]}
open={true}
stepNumber={1}
>
<Step
+ finished={false}
+ onOpen={[Function]}
open={true}
renderForm={[Function]}
renderResult={[Function]}
stepTitle="onboarding.token.header"
>
<div
- className="boxed-group onboarding-step onboarding-step-open"
+ className="boxed-group onboarding-step is-open"
>
<div
className="onboarding-step-number"
<div
className="boxed-group-inner"
>
- <div
- className="big-spacer-bottom width-50"
- >
- onboarding.token.text
- </div>
<form
onSubmit={[Function]}
>
- my token
- :
<span
- className="monospaced spacer-right"
+ className="text-middle"
>
- abcd1234
+ my token
+ :
</span>
+ <strong
+ className="spacer-right text-middle"
+ >
+ abcd1234
+ </strong>
<i
- className="spinner"
+ className="spinner text-middle"
/>
</form>
+ <div
+ className="note big-spacer-top width-50"
+ >
+ onboarding.token.text
+ </div>
<div
className="big-spacer-top"
>
exports[`revokes token 3`] = `
<TokenStep
+ finished={false}
onContinue={[Function]}
+ onOpen={[Function]}
open={true}
stepNumber={1}
>
<Step
+ finished={false}
+ onOpen={[Function]}
open={true}
renderForm={[Function]}
renderResult={[Function]}
stepTitle="onboarding.token.header"
>
<div
- className="boxed-group onboarding-step onboarding-step-open"
+ className="boxed-group onboarding-step is-open"
>
<div
className="onboarding-step-number"
<div
className="boxed-group-inner"
>
- <div
- className="big-spacer-bottom width-50"
- >
- onboarding.token.text
- </div>
<form
onSubmit={[Function]}
>
<input
autoFocus={true}
- className="input-large spacer-right"
+ className="input-large spacer-right text-middle"
onChange={[Function]}
placeholder="onboarding.token.placeholder"
required={true}
type="text"
value=""
/>
- <button>
+ <button
+ className="text-middle"
+ >
onboarding.token.generate
</button>
</form>
+ <div
+ className="note big-spacer-top width-50"
+ >
+ onboarding.token.text
+ </div>
</div>
</div>
</Step>
+ .onboarding {
+ min-height: calc(70vh - 60px);
+ }
+
.onboarding-step {
position: relative;
padding-left: 34px;
}
+.onboarding-step:not(.is-open):not(.is-finished) {
+ opacity: 0.4;
+}
+
.onboarding-step .boxed-group-actions {
height: 24px;
line-height: 24px;
height: 24px;
line-height: 24px;
border-radius: 24px;
- background-color: #cdcdcd;
+ background-color: #b9b9b9;
color: #fff;
font-size: 14px;
text-align: center;
}
-.onboarding-step-open .onboarding-step-number {
+.onboarding-step.is-open .onboarding-step-number {
background-color: #236a97;
}
+.onboarding-step.is-finished {
+ cursor: pointer;
+ outline: none;
+}
+
.onboarding-command {
position: relative;
margin: 8px 0;
}
.modal-large {
- width: 90vw;
- margin-left: -45vw;
-}
-
-.modal-full-screen {
- top: 30%;
- width: 90vw;
- height: 90vh;
- margin-left: -45vw;
- margin-top: -45vh;
- border-radius: 2px;
-
- &.ReactModal__Content--after-open {
- top: 50%;
- }
+ width: ~"calc(100% - 40px)";
+ max-width: 1280px;
+ min-width: 1040px;
+ margin-left: 0;
+ transform: translateX(-50%);
}
.modal-overlay,
shortcuts.section.code=Code Page
shortcuts.section.code.search=search components in the project scope
-tutorials.onboarding=Onboarding Tutorial
+tutorials.onboarding=Analyze a new project
tutorials.skip=Skip this tutorial
tutorials.finish=Finish this tutorial
+tutorials.follow_later=Follow the tutorial later in the Help section
#------------------------------------------------------------------------------
#------------------------------------------------------------------------------
onboarding.header=Welcome to SonarQube!
onboarding.header.sonarcloud=Welcome to SonarCloud!
-onboarding.header.description=Let's learn how to analyze your first public project.
+onboarding.header.description=Let's analyze a new project.
onboarding.token.header=Generate a token
onboarding.token.text=We'll use it as a replacement of the user login. This will increase the security of your installation by not letting your analysis user's password going through your network.