]> source.dussan.org Git - sonarqube.git/commitdiff
apply feedback on onboarding (#2175)
authorStas Vilchik <stas.vilchik@sonarsource.com>
Fri, 16 Jun 2017 12:56:47 +0000 (05:56 -0700)
committerStas Vilchik <stas.vilchik@sonarsource.com>
Tue, 20 Jun 2017 11:10:53 +0000 (04:10 -0700)
21 files changed:
server/sonar-web/src/main/js/app/components/nav/global/GlobalNav.js
server/sonar-web/src/main/js/apps/tutorials/onboarding/AnalysisStep.js
server/sonar-web/src/main/js/apps/tutorials/onboarding/NewOrganizationForm.js
server/sonar-web/src/main/js/apps/tutorials/onboarding/NewProjectForm.js
server/sonar-web/src/main/js/apps/tutorials/onboarding/Onboarding.js
server/sonar-web/src/main/js/apps/tutorials/onboarding/OnboardingModal.js
server/sonar-web/src/main/js/apps/tutorials/onboarding/OrganizationStep.js
server/sonar-web/src/main/js/apps/tutorials/onboarding/Step.js
server/sonar-web/src/main/js/apps/tutorials/onboarding/TokenStep.js
server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/Onboarding-test.js
server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/OrganizationStep-test.js
server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/Step-test.js
server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/TokenStep-test.js
server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/__snapshots__/NewOrganizationForm-test.js.snap
server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/__snapshots__/NewProjectForm-test.js.snap
server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/__snapshots__/Onboarding-test.js.snap
server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/__snapshots__/Step-test.js.snap
server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/__snapshots__/TokenStep-test.js.snap
server/sonar-web/src/main/js/apps/tutorials/onboarding/styles.css
server/sonar-web/src/main/less/components/modals.less
sonar-core/src/main/resources/org/sonar/l10n/core.properties

index dc2829912945845c95e8f7597c9ae9a43456479f..cb2d2eca4284e9a58cd5eb4d8ec7e6fe16a52bbc 100644 (file)
@@ -25,9 +25,11 @@ import GlobalNavMenu from './GlobalNavMenu';
 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 },
@@ -37,12 +39,18 @@ type Props = {
 
 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);
@@ -52,6 +60,9 @@ class GlobalNav extends React.PureComponent {
   }
 
   componentWillUnmount() {
+    if (this.interval) {
+      clearInterval(this.interval);
+    }
     window.removeEventListener('keypress', this.onKeyPress);
   }
 
@@ -76,7 +87,14 @@ class GlobalNav extends React.PureComponent {
 
   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 (
@@ -90,7 +108,14 @@ class GlobalNav extends React.PureComponent {
             <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} />
@@ -106,7 +131,10 @@ class GlobalNav extends React.PureComponent {
           />}
 
         {this.state.onboardingTutorialOpen &&
-          <OnboardingModal onClose={this.closeOnboardingTutorial} />}
+          <OnboardingModal
+            onFinish={this.finishOnboardingTutorial}
+            onSkip={this.skipOnboardingTutorial}
+          />}
       </nav>
     );
   }
index 83b7319a0132f3dc97f343ec8c291d89c2d85bcb..78733e66992c40886cde5c4aa0f2f1ce13ef8784 100644 (file)
@@ -50,7 +50,8 @@ export default class AnalysisStep extends React.PureComponent {
 
   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 = () => {
@@ -174,6 +175,8 @@ export default class AnalysisStep extends React.PureComponent {
   render() {
     return (
       <Step
+        finished={false}
+        onOpen={() => {}}
         open={this.props.open}
         renderForm={this.renderForm}
         renderResult={this.renderResult}
index 16a7c54d5265e8e7f92a91a96f28a1608d44a0d1..1f20fe29572ec8ae62153f098b08538660142e44 100644 (file)
@@ -20,6 +20,7 @@
 // @flow
 import React from 'react';
 import { debounce } from 'lodash';
+import CloseIcon from '../../../components/icons-components/CloseIcon';
 import {
   createOrganization,
   deleteOrganization,
@@ -124,9 +125,9 @@ export default class NewOrganizationForm extends React.PureComponent {
       ? <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}>
@@ -142,7 +143,7 @@ export default class NewOrganizationForm extends React.PureComponent {
             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">
index 2bf64f8fe5a68e9deeea92fd4bdda17d57b561fb..11f24180d82dd0ddc2e1ef6fe3ef2486a71ed11c 100644 (file)
@@ -19,6 +19,7 @@
  */
 // @flow
 import React from 'react';
+import CloseIcon from '../../../components/icons-components/CloseIcon';
 import { createProject, deleteProject } from '../../../api/components';
 import { translate } from '../../../helpers/l10n';
 
@@ -109,9 +110,9 @@ export default class NewProjectForm extends React.PureComponent {
       ? <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}>
@@ -126,7 +127,7 @@ export default class NewProjectForm extends React.PureComponent {
             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')}
index 06a5de99f38f21654d5468d81b61b06d73ffa388..2f79f7add07a3ab000c76996db67bd89a080a23d 100644 (file)
@@ -19,6 +19,7 @@
  */
 // @flow
 import React from 'react';
+import Helmet from 'react-helmet';
 import TokenStep from './TokenStep';
 import OrganizationStep from './OrganizationStep';
 import AnalysisStep from './AnalysisStep';
@@ -29,12 +30,13 @@ import { getProjectUrl } from '../../../helpers/urls';
 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,
@@ -74,12 +76,16 @@ export default class Onboarding extends React.PureComponent {
     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));
@@ -107,9 +113,13 @@ export default class Onboarding extends React.PureComponent {
     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 });
@@ -126,61 +136,69 @@ export default class Onboarding extends React.PureComponent {
 
     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>
     );
   }
index 13e1af8810717555218810789bc7be4b552f301a..3ec32829d17dac40ebe4e9ee20d984c63eeb7747 100644 (file)
@@ -22,9 +22,10 @@ import React from 'react';
 import Modal from 'react-modal';
 import { translate } from '../../../helpers/l10n';
 
-type Props = {
-  onClose: () => void
-};
+type Props = {|
+  onFinish: () => void,
+  onSkip: () => void
+|};
 
 type State = {
   OnboardingContainer?: Object
@@ -60,9 +61,10 @@ export default class OnboardingModal extends React.PureComponent {
       <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>
     );
   }
index 325e8e8ac5609e68da6f60aac83b1755be870e5b..9270d7c603351f968ebc2898ba8e069659663536 100644 (file)
@@ -27,11 +27,14 @@ import NewOrganizationForm from './NewOrganizationForm';
 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,
@@ -229,10 +232,12 @@ export default class OrganizationStep extends React.PureComponent {
   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')}
       />
     );
index 763cef635dfedd06707353caf10a6f78334085d5..fba30c8102b6de7c13eacfe579175e2a68a0e88a 100644 (file)
 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">
index 9a171f7bc779a610b538e0d51e250eef9dd7b048..443d4f5c2ecaac4e6fd8e0e7ba86ec9d9e19587a 100644 (file)
 // @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,
@@ -38,11 +41,6 @@ type State = {
 export default class TokenStep extends React.PureComponent {
   mounted: boolean;
   props: Props;
-
-  static defaultProps = {
-    stepNumber: 1
-  };
-
   state: State = {
     loading: false
   };
@@ -111,24 +109,20 @@ export default class TokenStep extends React.PureComponent {
 
     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}
@@ -136,10 +130,14 @@ export default class TokenStep extends React.PureComponent {
                 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}>
@@ -161,7 +159,7 @@ export default class TokenStep extends React.PureComponent {
       <div className="boxed-group-actions">
         <i className="icon-check spacer-right" />
         {tokenName}{': '}
-        <strong className="monospaced">{token}</strong>
+        <strong>{token}</strong>
       </div>
     );
   };
@@ -169,6 +167,8 @@ export default class TokenStep extends React.PureComponent {
   render() {
     return (
       <Step
+        finished={this.props.finished}
+        onOpen={this.props.onOpen}
         open={this.props.open}
         renderForm={this.renderForm}
         renderResult={this.renderResult}
index e489889728435a5f890ccb82f68c1fe8d54e51f4..f336d73a79b34c36187d7e621235a8d85cdd8701 100644 (file)
@@ -33,6 +33,7 @@ it('guides for on-premise', () => {
   const wrapper = shallow(
     <Onboarding
       currentUser={currentUser}
+      onFinish={jest.fn()}
       onSkip={jest.fn()}
       organizationsEnabled={false}
       sonarCloud={false}
@@ -50,6 +51,7 @@ it('guides for sonarcloud', () => {
   const wrapper = shallow(
     <Onboarding
       currentUser={currentUser}
+      onFinish={jest.fn()}
       onSkip={jest.fn()}
       organizationsEnabled={true}
       sonarCloud={true}
@@ -73,6 +75,7 @@ it('skips', () => {
   const wrapper = mount(
     <Onboarding
       currentUser={currentUser}
+      onFinish={jest.fn()}
       onSkip={onSkip}
       organizationsEnabled={false}
       sonarCloud={false}
index 9352556f5d1be99deafe06e2270df67f7160ba5d..b566bcd10b775da872111e237d70a86c792c1618 100644 (file)
@@ -32,7 +32,14 @@ const currentUser = { isLoggedIn: true, login: 'user' };
 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');
@@ -41,7 +48,14 @@ it('works with personal organization', () => {
 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'));
@@ -54,7 +68,14 @@ it('works with existing organization', () => {
 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');
index 89d7a3b7ba44e834782b14467898c43d7e612bec..daf5c8431de303d62b00cd21d5e91bb4dafad99e 100644 (file)
 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>}
@@ -36,3 +39,20 @@ it('renders', () => {
   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();
+});
index 9498fc27adaa137edd69cee1784e8a119e65714c..8a4608bb7abbfa1be6443dcfdbf24af59abcae5f 100644 (file)
@@ -29,7 +29,15 @@ jest.mock('../../../../api/user-tokens', () => ({
 }));
 
 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'));
@@ -38,7 +46,15 @@ it('generates token', () => {
 });
 
 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'));
@@ -48,7 +64,15 @@ it('revokes token', () => {
 
 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');
index c5f05454644ad567abe8751c31a48a109b107e9e..fde0778232ae014f7bf23c996c6585f55826a617 100644 (file)
@@ -54,7 +54,7 @@ exports[`creates new organization 2`] = `
       value="foo"
     />
     <i
-      className="spinner"
+      className="spinner text-middle"
     />
     <div
       className="note spacer-top abs-width-300"
@@ -79,11 +79,24 @@ exports[`creates new organization 3`] = `
       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>
@@ -103,11 +116,24 @@ exports[`deletes organization 1`] = `
       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>
@@ -127,7 +153,7 @@ exports[`deletes organization 2`] = `
       foo
     </span>
     <i
-      className="spinner"
+      className="spinner text-middle"
     />
   </form>
 </NewOrganizationForm>
index 50ef33521cc4423104d127cf1799f35c335e95e2..1943af530a5054df429aa1abc72e20a77c91ff23 100644 (file)
@@ -69,7 +69,7 @@ exports[`creates new project 2`] = `
         value="foo"
       />
       <i
-        className="spinner"
+        className="spinner text-middle"
       />
       <div
         className="note spacer-top abs-width-300"
@@ -103,11 +103,24 @@ exports[`creates new project 3`] = `
         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>
@@ -136,11 +149,24 @@ exports[`deletes project 1`] = `
         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>
@@ -169,7 +195,7 @@ exports[`deletes project 2`] = `
         foo
       </span>
       <i
-        className="spinner"
+        className="spinner text-middle"
       />
     </form>
   </div>
index df1611144a1b8775f0d3bb869d5fa249baaa3648..5e9291b6c85c5822f5ea72f7867bd59b7b2a6ff3 100644 (file)
 
 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>
 `;
index 1df068a503c64f33d30369dfd97e1e9638804cf7..4667ca7ae8abf906981d47c5e8f85187c7213a35 100644 (file)
@@ -2,7 +2,7 @@
 
 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"
@@ -24,7 +24,10 @@ exports[`renders 1`] = `
 
 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"
index 73ae3896df482e87a300d85daeb36900f2a81422..65138e0b498f2977deb29f47dab999298d923eb9 100644 (file)
@@ -2,11 +2,15 @@
 
 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]}
@@ -14,7 +18,7 @@ exports[`generates token 1`] = `
     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"
@@ -31,27 +35,29 @@ exports[`generates token 1`] = `
       <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>
@@ -60,11 +66,15 @@ exports[`generates token 1`] = `
 
 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]}
@@ -72,7 +82,7 @@ exports[`generates token 2`] = `
     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"
@@ -89,17 +99,12 @@ exports[`generates token 2`] = `
       <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}
@@ -107,9 +112,14 @@ exports[`generates token 2`] = `
             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>
@@ -118,11 +128,15 @@ exports[`generates token 2`] = `
 
 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]}
@@ -130,7 +144,7 @@ exports[`generates token 3`] = `
     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"
@@ -147,30 +161,47 @@ exports[`generates token 3`] = `
       <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"
         >
@@ -189,11 +220,15 @@ exports[`generates token 3`] = `
 
 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]}
@@ -201,7 +236,7 @@ exports[`revokes token 1`] = `
     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"
@@ -218,30 +253,47 @@ exports[`revokes token 1`] = `
       <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"
         >
@@ -260,11 +312,15 @@ exports[`revokes token 1`] = `
 
 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]}
@@ -272,7 +328,7 @@ exports[`revokes token 2`] = `
     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"
@@ -289,25 +345,29 @@ exports[`revokes token 2`] = `
       <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"
         >
@@ -326,11 +386,15 @@ exports[`revokes token 2`] = `
 
 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]}
@@ -338,7 +402,7 @@ exports[`revokes token 3`] = `
     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"
@@ -355,27 +419,29 @@ exports[`revokes token 3`] = `
       <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>
index 870da20acde147445c958088e474d5f7dc70ea65..ba588639db34dc91f806d17e4584c48e0887e623 100644 (file)
@@ -1,8 +1,16 @@
+ .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;
index aaae44af086c16167a05dce81ce00e9c7fa9db5b..4add5ce14c665fb6b4cfcd740a73f4627b359973 100644 (file)
 }
 
 .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,
index 7c830e984a6870086dac9671f2333a6720210a7f..6eb28fde7ae47d8db1a83919f32635fdaa10bf59 100644 (file)
@@ -1091,9 +1091,10 @@ shortcuts.section.rules.deactivate=deactivate selected rule
 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
 
 
 #------------------------------------------------------------------------------
@@ -2957,7 +2958,7 @@ footer.web_api=Web API
 #------------------------------------------------------------------------------
 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.