From 4e91bd432aeef4a2e7ff680910907e62007ac801 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Gr=C3=A9goire=20Aubert?= Date: Fri, 15 Jun 2018 15:04:21 +0200 Subject: [PATCH] SONARCLOUD-64 New onboarding with 3 cases for SonarCloud (#383) * SONARCLOUD-64 Move Onboarding to ProjectOnboarding * SONARCLOUD-64 Migrate project onboarding to TS * SONARCLOUD-64 Update ProjectOnboarding style * SONARCLOUD-64 Add main onboarding page --- .../main/js/app/components/StartupModal.tsx | 75 +++- .../__tests__/StartupModal-test.tsx | 9 +- .../embed-docs-modal/EmbedDocsPopup.tsx | 4 +- .../app/components/nav/global/GlobalNav.tsx | 4 +- .../components/nav/global/GlobalNavPlus.tsx | 4 +- .../global/__tests__/GlobalNavPlus-test.tsx | 8 +- .../src/main/js/app/utils/startReactApp.js | 2 +- .../components/NoFavoriteProjects.tsx | 4 +- .../src/main/js/apps/tutorials/Onboarding.tsx | 102 +++++ .../tutorials/__tests__/Onboarding-test.tsx | 61 +++ .../__snapshots__/Onboarding-test.tsx.snap | 82 ++++ .../apps/tutorials/onboarding/Onboarding.js | 236 ----------- .../onboarding/OnboardingContainer.js | 33 -- .../__snapshots__/Onboarding-test.js.snap | 388 ------------------ .../AnalysisStep.tsx} | 149 +++---- .../LanguageStep.tsx} | 51 +-- .../NewOrganizationForm.tsx} | 45 +- .../NewProjectForm.tsx} | 49 ++- .../OrganizationStep.tsx} | 58 ++- .../projectOnboarding/ProjectOnboarding.tsx | 206 ++++++++++ .../ProjectOnboardingModal.tsx} | 6 +- .../ProjectOnboardingPage.tsx} | 8 +- .../ProjectWatcher.tsx} | 58 ++- .../Step.js => projectOnboarding/Step.tsx} | 30 +- .../TokenStep.tsx} | 102 ++--- .../__tests__/LanguageStep-test.tsx} | 47 ++- .../__tests__/NewOrganizationForm-test.tsx} | 5 +- .../__tests__/NewProjectForm-test.tsx} | 5 +- .../__tests__/OrganizationStep-test.tsx} | 11 +- .../__tests__/ProjectOnboarding-test.tsx} | 35 +- .../__tests__/ProjectWatcher-test.tsx} | 3 +- .../__tests__/Step-test.tsx} | 3 +- .../__tests__/TokenStep-test.tsx} | 5 +- .../__snapshots__/LanguageStep-test.tsx.snap} | 0 .../NewOrganizationForm-test.tsx.snap} | 0 .../NewProjectForm-test.tsx.snap} | 0 .../OrganizationStep-test.tsx.snap} | 0 .../ProjectOnboarding-test.tsx.snap | 349 ++++++++++++++++ .../ProjectWatcher-test.tsx.snap} | 8 +- .../__snapshots__/Step-test.tsx.snap} | 0 .../__snapshots__/TokenStep-test.tsx.snap} | 12 +- .../commands/BuildWrapper.tsx} | 20 +- .../commands/ClangGCC.tsx} | 23 +- .../commands/DotNet.tsx} | 19 +- .../commands/JavaGradle.tsx} | 17 +- .../commands/JavaMaven.tsx} | 17 +- .../commands/MSBuildScanner.tsx} | 15 +- .../commands/Msvc.tsx} | 19 +- .../commands/Other.tsx} | 21 +- .../commands/SQScanner.tsx} | 17 +- .../commands/__tests__/BuildWrapper-test.tsx} | 3 +- .../commands/__tests__/ClangGCC-test.tsx} | 6 +- .../commands/__tests__/DotNet-test.tsx} | 4 +- .../commands/__tests__/JavaGradle-test.tsx} | 4 +- .../commands/__tests__/JavaMaven-test.tsx} | 4 +- .../__tests__/MSBuildScanner-test.tsx} | 4 +- .../commands/__tests__/Msvc-test.tsx} | 4 +- .../commands/__tests__/Other-test.tsx} | 6 +- .../commands/__tests__/SQScanner-test.tsx} | 4 +- .../__snapshots__/BuildWrapper-test.tsx.snap} | 0 .../__snapshots__/ClangGCC-test.tsx.snap} | 0 .../__snapshots__/DotNet-test.tsx.snap} | 0 .../__snapshots__/JavaGradle-test.tsx.snap} | 0 .../__snapshots__/JavaMaven-test.tsx.snap} | 0 .../MSBuildScanner-test.tsx.snap} | 1 + .../__snapshots__/Msvc-test.tsx.snap} | 0 .../__snapshots__/Other-test.tsx.snap} | 0 .../__snapshots__/SQScanner-test.tsx.snap} | 3 + .../tutorials/{onboarding => }/styles.css | 15 +- .../teamOnboarding/TeamOnboardingModal.tsx | 68 +++ .../__tests__/TeamOnboardingModal-test.tsx} | 10 +- .../TeamOnboardingModal-test.tsx.snap | 61 +++ .../src/main/js/helpers/testUtils.ts | 4 +- .../resources/org/sonar/l10n/core.properties | 28 +- 74 files changed, 1460 insertions(+), 1194 deletions(-) create mode 100644 server/sonar-web/src/main/js/apps/tutorials/Onboarding.tsx create mode 100644 server/sonar-web/src/main/js/apps/tutorials/__tests__/Onboarding-test.tsx create mode 100644 server/sonar-web/src/main/js/apps/tutorials/__tests__/__snapshots__/Onboarding-test.tsx.snap delete mode 100644 server/sonar-web/src/main/js/apps/tutorials/onboarding/Onboarding.js delete mode 100644 server/sonar-web/src/main/js/apps/tutorials/onboarding/OnboardingContainer.js delete mode 100644 server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/__snapshots__/Onboarding-test.js.snap rename server/sonar-web/src/main/js/apps/tutorials/{onboarding/AnalysisStep.js => projectOnboarding/AnalysisStep.tsx} (61%) rename server/sonar-web/src/main/js/apps/tutorials/{onboarding/LanguageStep.js => projectOnboarding/LanguageStep.tsx} (87%) rename server/sonar-web/src/main/js/apps/tutorials/{onboarding/NewOrganizationForm.js => projectOnboarding/NewOrganizationForm.tsx} (87%) rename server/sonar-web/src/main/js/apps/tutorials/{onboarding/NewProjectForm.js => projectOnboarding/NewProjectForm.tsx} (83%) rename server/sonar-web/src/main/js/apps/tutorials/{onboarding/OrganizationStep.js => projectOnboarding/OrganizationStep.tsx} (89%) create mode 100644 server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/ProjectOnboarding.tsx rename server/sonar-web/src/main/js/apps/tutorials/{onboarding/OnboardingModal.tsx => projectOnboarding/ProjectOnboardingModal.tsx} (87%) rename server/sonar-web/src/main/js/apps/tutorials/{onboarding/OnboardingPage.tsx => projectOnboarding/ProjectOnboardingPage.tsx} (81%) rename server/sonar-web/src/main/js/apps/tutorials/{onboarding/ProjectWatcher.js => projectOnboarding/ProjectWatcher.tsx} (75%) rename server/sonar-web/src/main/js/apps/tutorials/{onboarding/Step.js => projectOnboarding/Step.tsx} (76%) rename server/sonar-web/src/main/js/apps/tutorials/{onboarding/TokenStep.js => projectOnboarding/TokenStep.tsx} (81%) rename server/sonar-web/src/main/js/apps/tutorials/{onboarding/__tests__/LanguageStep-test.js => projectOnboarding/__tests__/LanguageStep-test.tsx} (73%) rename server/sonar-web/src/main/js/apps/tutorials/{onboarding/__tests__/NewOrganizationForm-test.js => projectOnboarding/__tests__/NewOrganizationForm-test.tsx} (95%) rename server/sonar-web/src/main/js/apps/tutorials/{onboarding/__tests__/NewProjectForm-test.js => projectOnboarding/__tests__/NewProjectForm-test.tsx} (95%) rename server/sonar-web/src/main/js/apps/tutorials/{onboarding/__tests__/OrganizationStep-test.js => projectOnboarding/__tests__/OrganizationStep-test.tsx} (92%) rename server/sonar-web/src/main/js/apps/tutorials/{onboarding/__tests__/Onboarding-test.js => projectOnboarding/__tests__/ProjectOnboarding-test.tsx} (65%) rename server/sonar-web/src/main/js/apps/tutorials/{onboarding/__tests__/ProjectWatcher-test.js => projectOnboarding/__tests__/ProjectWatcher-test.tsx} (98%) rename server/sonar-web/src/main/js/apps/tutorials/{onboarding/__tests__/Step-test.js => projectOnboarding/__tests__/Step-test.tsx} (97%) rename server/sonar-web/src/main/js/apps/tutorials/{onboarding/__tests__/TokenStep-test.js => projectOnboarding/__tests__/TokenStep-test.tsx} (97%) rename server/sonar-web/src/main/js/apps/tutorials/{onboarding/__tests__/__snapshots__/LanguageStep-test.js.snap => projectOnboarding/__tests__/__snapshots__/LanguageStep-test.tsx.snap} (100%) rename server/sonar-web/src/main/js/apps/tutorials/{onboarding/__tests__/__snapshots__/NewOrganizationForm-test.js.snap => projectOnboarding/__tests__/__snapshots__/NewOrganizationForm-test.tsx.snap} (100%) rename server/sonar-web/src/main/js/apps/tutorials/{onboarding/__tests__/__snapshots__/NewProjectForm-test.js.snap => projectOnboarding/__tests__/__snapshots__/NewProjectForm-test.tsx.snap} (100%) rename server/sonar-web/src/main/js/apps/tutorials/{onboarding/__tests__/__snapshots__/OrganizationStep-test.js.snap => projectOnboarding/__tests__/__snapshots__/OrganizationStep-test.tsx.snap} (100%) create mode 100644 server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/__tests__/__snapshots__/ProjectOnboarding-test.tsx.snap rename server/sonar-web/src/main/js/apps/tutorials/{onboarding/__tests__/__snapshots__/ProjectWatcher-test.js.snap => projectOnboarding/__tests__/__snapshots__/ProjectWatcher-test.tsx.snap} (74%) rename server/sonar-web/src/main/js/apps/tutorials/{onboarding/__tests__/__snapshots__/Step-test.js.snap => projectOnboarding/__tests__/__snapshots__/Step-test.tsx.snap} (100%) rename server/sonar-web/src/main/js/apps/tutorials/{onboarding/__tests__/__snapshots__/TokenStep-test.js.snap => projectOnboarding/__tests__/__snapshots__/TokenStep-test.tsx.snap} (97%) rename server/sonar-web/src/main/js/apps/tutorials/{onboarding/commands/BuildWrapper.js => projectOnboarding/commands/BuildWrapper.tsx} (83%) rename server/sonar-web/src/main/js/apps/tutorials/{onboarding/commands/ClangGCC.js => projectOnboarding/commands/ClangGCC.tsx} (91%) rename server/sonar-web/src/main/js/apps/tutorials/{onboarding/commands/DotNet.js => projectOnboarding/commands/DotNet.tsx} (92%) rename server/sonar-web/src/main/js/apps/tutorials/{onboarding/commands/JavaGradle.js => projectOnboarding/commands/JavaGradle.tsx} (92%) rename server/sonar-web/src/main/js/apps/tutorials/{onboarding/commands/JavaMaven.js => projectOnboarding/commands/JavaMaven.tsx} (91%) rename server/sonar-web/src/main/js/apps/tutorials/{onboarding/commands/MSBuildScanner.js => projectOnboarding/commands/MSBuildScanner.tsx} (89%) rename server/sonar-web/src/main/js/apps/tutorials/{onboarding/commands/Msvc.js => projectOnboarding/commands/Msvc.tsx} (92%) rename server/sonar-web/src/main/js/apps/tutorials/{onboarding/commands/Other.js => projectOnboarding/commands/Other.tsx} (91%) rename server/sonar-web/src/main/js/apps/tutorials/{onboarding/commands/SQScanner.js => projectOnboarding/commands/SQScanner.tsx} (89%) rename server/sonar-web/src/main/js/apps/tutorials/{onboarding/commands/__tests__/BuildWrapper-test.js => projectOnboarding/commands/__tests__/BuildWrapper-test.tsx} (96%) rename server/sonar-web/src/main/js/apps/tutorials/{onboarding/commands/__tests__/ClangGCC-test.js => projectOnboarding/commands/__tests__/ClangGCC-test.tsx} (97%) rename server/sonar-web/src/main/js/apps/tutorials/{onboarding/commands/__tests__/DotNet-test.js => projectOnboarding/commands/__tests__/DotNet-test.tsx} (97%) rename server/sonar-web/src/main/js/apps/tutorials/{onboarding/commands/__tests__/JavaGradle-test.js => projectOnboarding/commands/__tests__/JavaGradle-test.tsx} (96%) rename server/sonar-web/src/main/js/apps/tutorials/{onboarding/commands/__tests__/JavaMaven-test.js => projectOnboarding/commands/__tests__/JavaMaven-test.tsx} (96%) rename server/sonar-web/src/main/js/apps/tutorials/{onboarding/commands/__tests__/MSBuildScanner-test.js => projectOnboarding/commands/__tests__/MSBuildScanner-test.tsx} (96%) rename server/sonar-web/src/main/js/apps/tutorials/{onboarding/commands/__tests__/Msvc-test.js => projectOnboarding/commands/__tests__/Msvc-test.tsx} (97%) rename server/sonar-web/src/main/js/apps/tutorials/{onboarding/commands/__tests__/Other-test.js => projectOnboarding/commands/__tests__/Other-test.tsx} (97%) rename server/sonar-web/src/main/js/apps/tutorials/{onboarding/commands/__tests__/SQScanner-test.js => projectOnboarding/commands/__tests__/SQScanner-test.tsx} (96%) rename server/sonar-web/src/main/js/apps/tutorials/{onboarding/commands/__tests__/__snapshots__/BuildWrapper-test.js.snap => projectOnboarding/commands/__tests__/__snapshots__/BuildWrapper-test.tsx.snap} (100%) rename server/sonar-web/src/main/js/apps/tutorials/{onboarding/commands/__tests__/__snapshots__/ClangGCC-test.js.snap => projectOnboarding/commands/__tests__/__snapshots__/ClangGCC-test.tsx.snap} (100%) rename server/sonar-web/src/main/js/apps/tutorials/{onboarding/commands/__tests__/__snapshots__/DotNet-test.js.snap => projectOnboarding/commands/__tests__/__snapshots__/DotNet-test.tsx.snap} (100%) rename server/sonar-web/src/main/js/apps/tutorials/{onboarding/commands/__tests__/__snapshots__/JavaGradle-test.js.snap => projectOnboarding/commands/__tests__/__snapshots__/JavaGradle-test.tsx.snap} (100%) rename server/sonar-web/src/main/js/apps/tutorials/{onboarding/commands/__tests__/__snapshots__/JavaMaven-test.js.snap => projectOnboarding/commands/__tests__/__snapshots__/JavaMaven-test.tsx.snap} (100%) rename server/sonar-web/src/main/js/apps/tutorials/{onboarding/commands/__tests__/__snapshots__/MSBuildScanner-test.js.snap => projectOnboarding/commands/__tests__/__snapshots__/MSBuildScanner-test.tsx.snap} (94%) rename server/sonar-web/src/main/js/apps/tutorials/{onboarding/commands/__tests__/__snapshots__/Msvc-test.js.snap => projectOnboarding/commands/__tests__/__snapshots__/Msvc-test.tsx.snap} (100%) rename server/sonar-web/src/main/js/apps/tutorials/{onboarding/commands/__tests__/__snapshots__/Other-test.js.snap => projectOnboarding/commands/__tests__/__snapshots__/Other-test.tsx.snap} (100%) rename server/sonar-web/src/main/js/apps/tutorials/{onboarding/commands/__tests__/__snapshots__/SQScanner-test.js.snap => projectOnboarding/commands/__tests__/__snapshots__/SQScanner-test.tsx.snap} (94%) rename server/sonar-web/src/main/js/apps/tutorials/{onboarding => }/styles.css (88%) create mode 100644 server/sonar-web/src/main/js/apps/tutorials/teamOnboarding/TeamOnboardingModal.tsx rename server/sonar-web/src/main/js/apps/tutorials/{onboarding/OnboardingModal.d.ts => teamOnboarding/__tests__/TeamOnboardingModal-test.tsx} (80%) create mode 100644 server/sonar-web/src/main/js/apps/tutorials/teamOnboarding/__tests__/__snapshots__/TeamOnboardingModal-test.tsx.snap diff --git a/server/sonar-web/src/main/js/app/components/StartupModal.tsx b/server/sonar-web/src/main/js/app/components/StartupModal.tsx index 101279628d1..914adc7b3ea 100644 --- a/server/sonar-web/src/main/js/app/components/StartupModal.tsx +++ b/server/sonar-web/src/main/js/app/components/StartupModal.tsx @@ -20,16 +20,21 @@ import * as React from 'react'; import * as PropTypes from 'prop-types'; import { connect } from 'react-redux'; -import OnboardingModal from '../../apps/tutorials/onboarding/OnboardingModal'; +import Onboarding from '../../apps/tutorials/Onboarding'; +import CreateOrganizationForm from '../../apps/account/organizations/CreateOrganizationForm'; import LicensePromptModal from '../../apps/marketplace/components/LicensePromptModal'; -import { CurrentUser, isLoggedIn } from '../types'; +import ProjectOnboardingModal from '../../apps/tutorials/projectOnboarding/ProjectOnboardingModal'; +import TeamOnboardingModal from '../../apps/tutorials/teamOnboarding/TeamOnboardingModal'; +import { CurrentUser, isLoggedIn, Organization } from '../types'; import { differenceInDays, parseDate, toShortNotSoISOString } from '../../helpers/dates'; import { EditionKey } from '../../apps/marketplace/utils'; import { getCurrentUser, getAppState } from '../../store/rootReducer'; -import { skipOnboarding } from '../../store/users/actions'; +import { skipOnboarding as skipOnboardingAction } from '../../store/users/actions'; import { showLicense } from '../../api/marketplace'; import { hasMessage } from '../../helpers/l10n'; import { save, get } from '../../helpers/storage'; +import { isSonarCloud } from '../../helpers/system'; +import { skipOnboarding } from '../../api/users'; interface StateProps { canAdmin: boolean; @@ -38,7 +43,7 @@ interface StateProps { } interface DispatchProps { - skipOnboarding: () => void; + skipOnboardingAction: () => void; } interface OwnProps { @@ -50,7 +55,10 @@ type Props = StateProps & DispatchProps & OwnProps; enum ModalKey { license, - onboarding + onboarding, + organizationOnboarding, + projectOnboarding, + teamOnboarding } interface State { @@ -61,14 +69,18 @@ interface State { const LICENSE_PROMPT = 'sonarqube.license.prompt'; export class StartupModal extends React.PureComponent { + static contextTypes = { + router: PropTypes.object.isRequired + }; + static childContextTypes = { - openOnboardingTutorial: PropTypes.func + openProjectOnboarding: PropTypes.func }; state: State = { automatic: false }; getChildContext() { - return { openOnboardingTutorial: this.openOnboarding }; + return { openProjectOnboarding: this.openProjectOnboarding }; } componentDidMount() { @@ -77,8 +89,9 @@ export class StartupModal extends React.PureComponent { closeOnboarding = () => { this.setState(state => { - if (state.modal === ModalKey.onboarding) { - this.props.skipOnboarding(); + if (state.modal !== ModalKey.license) { + skipOnboarding(); + this.props.skipOnboardingAction(); return { automatic: false, modal: undefined }; } return undefined; @@ -94,10 +107,27 @@ export class StartupModal extends React.PureComponent { }); }; + closeOrganizationOnboarding = ({ key }: Pick) => { + this.closeOnboarding(); + this.context.router.push(`/organizations/${key}`); + }; + openOnboarding = () => { this.setState({ modal: ModalKey.onboarding }); }; + openOrganizationOnboarding = () => { + this.setState({ modal: ModalKey.organizationOnboarding }); + }; + + openProjectOnboarding = () => { + this.setState({ modal: ModalKey.projectOnboarding }); + }; + + openTeamOnboarding = () => { + this.setState({ modal: ModalKey.teamOnboarding }); + }; + tryAutoOpenLicense = () => { const { canAdmin, currentEdition, currentUser } = this.props; const hasLicenseManager = hasMessage('license.prompt.title'); @@ -124,7 +154,11 @@ export class StartupModal extends React.PureComponent { const { currentUser, location } = this.props; if (currentUser.showOnboardingTutorial && !location.pathname.startsWith('documentation')) { this.setState({ automatic: true }); - this.openOnboarding(); + if (isSonarCloud()) { + this.openOnboarding(); + } else { + this.openProjectOnboarding(); + } } }; @@ -135,7 +169,24 @@ export class StartupModal extends React.PureComponent { {this.props.children} {modal === ModalKey.license && } {modal === ModalKey.onboarding && ( - + + )} + {modal === ModalKey.projectOnboarding && ( + + )} + {modal === ModalKey.organizationOnboarding && ( + + )} + {modal === ModalKey.teamOnboarding && ( + )} ); @@ -148,7 +199,7 @@ const mapStateToProps = (state: any): StateProps => ({ currentUser: getCurrentUser(state) }); -const mapDispatchToProps: DispatchProps = { skipOnboarding }; +const mapDispatchToProps: DispatchProps = { skipOnboardingAction }; export default connect(mapStateToProps, mapDispatchToProps)( StartupModal diff --git a/server/sonar-web/src/main/js/app/components/__tests__/StartupModal-test.tsx b/server/sonar-web/src/main/js/app/components/__tests__/StartupModal-test.tsx index d2d48ee7d51..7a1e2bf2d3d 100644 --- a/server/sonar-web/src/main/js/app/components/__tests__/StartupModal-test.tsx +++ b/server/sonar-web/src/main/js/app/components/__tests__/StartupModal-test.tsx @@ -119,12 +119,12 @@ it('should render onboarding modal', async () => { async function shouldNotHaveModals(wrapper: ShallowWrapper) { await waitAndUpdate(wrapper); expect(wrapper.find('LicensePromptModal').exists()).toBeFalsy(); - expect(wrapper.find('OnboardingModal').exists()).toBeFalsy(); + expect(wrapper.find('ProjectOnboardingModal').exists()).toBeFalsy(); } async function shouldDisplayOnboarding(wrapper: ShallowWrapper) { await waitAndUpdate(wrapper); - expect(wrapper.find('OnboardingModal').exists()).toBeTruthy(); + expect(wrapper.find('ProjectOnboardingModal').exists()).toBeTruthy(); } async function shouldDisplayLicense(wrapper: ShallowWrapper) { @@ -139,9 +139,10 @@ function getWrapper(props = {}) { currentEdition={EditionKey.enterprise} currentUser={LOGGED_IN_USER} location={{ pathname: 'foo/bar' }} - skipOnboarding={jest.fn()} + skipOnboardingAction={jest.fn()} {...props}>
- + , + { context: { router: { push: jest.fn() } } } ); } diff --git a/server/sonar-web/src/main/js/app/components/embed-docs-modal/EmbedDocsPopup.tsx b/server/sonar-web/src/main/js/app/components/embed-docs-modal/EmbedDocsPopup.tsx index 061a3af556b..5cb06388326 100644 --- a/server/sonar-web/src/main/js/app/components/embed-docs-modal/EmbedDocsPopup.tsx +++ b/server/sonar-web/src/main/js/app/components/embed-docs-modal/EmbedDocsPopup.tsx @@ -36,13 +36,13 @@ interface Props { export default class EmbedDocsPopup extends React.PureComponent { static contextTypes = { - openOnboardingTutorial: PropTypes.func + openProjectOnboarding: PropTypes.func }; onAnalyzeProjectClick = (event: React.SyntheticEvent) => { event.preventDefault(); event.currentTarget.blur(); - this.context.openOnboardingTutorial(); + this.context.openProjectOnboarding(); }; renderTitle(text: string) { diff --git a/server/sonar-web/src/main/js/app/components/nav/global/GlobalNav.tsx b/server/sonar-web/src/main/js/app/components/nav/global/GlobalNav.tsx index 6d3cdcece68..0f596735299 100644 --- a/server/sonar-web/src/main/js/app/components/nav/global/GlobalNav.tsx +++ b/server/sonar-web/src/main/js/app/components/nav/global/GlobalNav.tsx @@ -50,7 +50,7 @@ interface OwnProps { type Props = StateProps & OwnProps; class GlobalNav extends React.PureComponent { - static contextTypes = { openOnboardingTutorial: PropTypes.func }; + static contextTypes = { openProjectOnboarding: PropTypes.func }; render() { return ( @@ -69,7 +69,7 @@ class GlobalNav extends React.PureComponent { {isLoggedIn(this.props.currentUser) && isSonarCloud() && ( - + )} diff --git a/server/sonar-web/src/main/js/app/components/nav/global/GlobalNavPlus.tsx b/server/sonar-web/src/main/js/app/components/nav/global/GlobalNavPlus.tsx index d58fb2379ac..412846db466 100644 --- a/server/sonar-web/src/main/js/app/components/nav/global/GlobalNavPlus.tsx +++ b/server/sonar-web/src/main/js/app/components/nav/global/GlobalNavPlus.tsx @@ -25,7 +25,7 @@ import Dropdown from '../../../../components/controls/Dropdown'; import { translate } from '../../../../helpers/l10n'; interface Props { - openOnboardingTutorial: () => void; + openProjectOnboarding: () => void; } interface State { @@ -44,7 +44,7 @@ export default class GlobalNavPlus extends React.PureComponent { handleNewProjectClick = (event: React.SyntheticEvent) => { event.preventDefault(); - this.props.openOnboardingTutorial(); + this.props.openProjectOnboarding(); }; openCreateOrganizationForm = () => this.setState({ createOrganization: true }); diff --git a/server/sonar-web/src/main/js/app/components/nav/global/__tests__/GlobalNavPlus-test.tsx b/server/sonar-web/src/main/js/app/components/nav/global/__tests__/GlobalNavPlus-test.tsx index 3e4110f5193..88aaf4f172d 100644 --- a/server/sonar-web/src/main/js/app/components/nav/global/__tests__/GlobalNavPlus-test.tsx +++ b/server/sonar-web/src/main/js/app/components/nav/global/__tests__/GlobalNavPlus-test.tsx @@ -23,18 +23,18 @@ import GlobalNavPlus from '../GlobalNavPlus'; import { click } from '../../../../../helpers/testUtils'; it('render', () => { - const wrapper = shallow(); + const wrapper = shallow(); expect(wrapper.is('Dropdown')).toBe(true); expect(wrapper.find('Dropdown')).toMatchSnapshot(); }); it('opens onboarding', () => { - const openOnboardingTutorial = jest.fn(); + const openProjectOnboarding = jest.fn(); const wrapper = shallow( - shallow() + shallow() .find('Dropdown') .prop('overlay') ); click(wrapper.find('.js-new-project')); - expect(openOnboardingTutorial).toBeCalled(); + expect(openProjectOnboarding).toBeCalled(); }); diff --git a/server/sonar-web/src/main/js/app/utils/startReactApp.js b/server/sonar-web/src/main/js/app/utils/startReactApp.js index b4b79feec06..e092a6c13eb 100644 --- a/server/sonar-web/src/main/js/app/utils/startReactApp.js +++ b/server/sonar-web/src/main/js/app/utils/startReactApp.js @@ -172,7 +172,7 @@ const startReactApp = () => { - import('../../apps/tutorials/onboarding/OnboardingPage') + import('../../apps/tutorials/projectOnboarding/ProjectOnboardingPage') )} /> diff --git a/server/sonar-web/src/main/js/apps/projects/components/NoFavoriteProjects.tsx b/server/sonar-web/src/main/js/apps/projects/components/NoFavoriteProjects.tsx index 6c52d002bb1..4cd7b7d2164 100644 --- a/server/sonar-web/src/main/js/apps/projects/components/NoFavoriteProjects.tsx +++ b/server/sonar-web/src/main/js/apps/projects/components/NoFavoriteProjects.tsx @@ -36,13 +36,13 @@ interface StateProps { export class NoFavoriteProjects extends React.PureComponent { static contextTypes = { - openOnboardingTutorial: PropTypes.func + openProjectOnboarding: PropTypes.func }; onAnalyzeProjectClick = (event: React.SyntheticEvent) => { event.preventDefault(); event.currentTarget.blur(); - this.context.openOnboardingTutorial(); + this.context.openProjectOnboarding(); }; render() { diff --git a/server/sonar-web/src/main/js/apps/tutorials/Onboarding.tsx b/server/sonar-web/src/main/js/apps/tutorials/Onboarding.tsx new file mode 100644 index 00000000000..e6a6d887bd3 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/tutorials/Onboarding.tsx @@ -0,0 +1,102 @@ +/* + * SonarQube + * Copyright (C) 2009-2018 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import * as React from 'react'; +import { connect } from 'react-redux'; +import handleRequiredAuthentication from '../../app/utils/handleRequiredAuthentication'; +import Modal from '../../components/controls/Modal'; +import { ResetButtonLink, Button } from '../../components/ui/buttons'; +import { translate } from '../../helpers/l10n'; +import { CurrentUser, isLoggedIn } from '../../app/types'; +import { getCurrentUser } from '../../store/rootReducer'; +import './styles.css'; + +interface OwnProps { + onFinish: () => void; + onOpenOrganizationOnboarding: () => void; + onOpenProjectOnboarding: () => void; + onOpenTeamOnboarding: () => void; +} + +interface StateProps { + currentUser: CurrentUser; +} + +type Props = OwnProps & StateProps; + +export class Onboarding extends React.PureComponent { + componentDidMount() { + if (!isLoggedIn(this.props.currentUser)) { + handleRequiredAuthentication(); + } + } + + render() { + if (!isLoggedIn(this.props.currentUser)) { + return null; + } + + const header = translate('onboarding.header'); + return ( + +
+

{header}

+
+
+

+ {translate('onboarding.header.description')} +

+
    +
  • +

    {translate('onboarding.analyze_public_code')}

    + +
  • +
  • +

    {translate('onboarding.analyze_private_code')}

    + +
  • +
  • +

    + {translate('onboarding.contribute_existing_project')} +

    + +
  • +
+
+
+ {translate('close')} +
+
+ ); + } +} + +const mapStateToProps = (state: any): StateProps => ({ currentUser: getCurrentUser(state) }); + +export default connect(mapStateToProps)(Onboarding); diff --git a/server/sonar-web/src/main/js/apps/tutorials/__tests__/Onboarding-test.tsx b/server/sonar-web/src/main/js/apps/tutorials/__tests__/Onboarding-test.tsx new file mode 100644 index 00000000000..c9b3e77500e --- /dev/null +++ b/server/sonar-web/src/main/js/apps/tutorials/__tests__/Onboarding-test.tsx @@ -0,0 +1,61 @@ +/* + * SonarQube + * Copyright (C) 2009-2018 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import * as React from 'react'; +import { shallow } from 'enzyme'; +import { Onboarding } from '../Onboarding'; +import { click } from '../../../helpers/testUtils'; + +it('renders correctly', () => { + expect( + shallow( + + ) + ).toMatchSnapshot(); +}); + +it('should correctly open the different tutorials', () => { + const onFinish = jest.fn(); + const onOpenOrganizationOnboarding = jest.fn(); + const onOpenProjectOnboarding = jest.fn(); + const onOpenTeamOnboarding = jest.fn(); + const wrapper = shallow( + + ); + + click(wrapper.find('ResetButtonLink')); + expect(onFinish).toHaveBeenCalled(); + + wrapper.find('Button').forEach(button => click(button)); + expect(onOpenOrganizationOnboarding).toHaveBeenCalled(); + expect(onOpenProjectOnboarding).toHaveBeenCalled(); + expect(onOpenTeamOnboarding).toHaveBeenCalled(); +}); diff --git a/server/sonar-web/src/main/js/apps/tutorials/__tests__/__snapshots__/Onboarding-test.tsx.snap b/server/sonar-web/src/main/js/apps/tutorials/__tests__/__snapshots__/Onboarding-test.tsx.snap new file mode 100644 index 00000000000..e017f778601 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/tutorials/__tests__/__snapshots__/Onboarding-test.tsx.snap @@ -0,0 +1,82 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`renders correctly 1`] = ` + +
+

+ onboarding.header +

+
+
+

+ onboarding.header.description +

+
    +
  • +

    + onboarding.analyze_public_code +

    + +
  • +
  • +

    + onboarding.analyze_private_code +

    + +
  • +
  • +

    + onboarding.contribute_existing_project +

    + +
  • +
+
+
+ + close + +
+
+`; diff --git a/server/sonar-web/src/main/js/apps/tutorials/onboarding/Onboarding.js b/server/sonar-web/src/main/js/apps/tutorials/onboarding/Onboarding.js deleted file mode 100644 index afcf049e397..00000000000 --- a/server/sonar-web/src/main/js/apps/tutorials/onboarding/Onboarding.js +++ /dev/null @@ -1,236 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2018 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -// @flow -import React from 'react'; -import PropTypes from 'prop-types'; -import Helmet from 'react-helmet'; -import TokenStep from './TokenStep'; -import OrganizationStep from './OrganizationStep'; -import AnalysisStep from './AnalysisStep'; -import ProjectWatcher from './ProjectWatcher'; -import DeferredSpinner from '../../../components/common/DeferredSpinner'; -import InstanceMessage from '../../../components/common/InstanceMessage'; -import handleRequiredAuthentication from '../../../app/utils/handleRequiredAuthentication'; -import { skipOnboarding } from '../../../api/users'; -import { translate, translateWithParameters } from '../../../helpers/l10n'; -import { getProjectUrl } from '../../../helpers/urls'; -import { isSonarCloud } from '../../../helpers/system'; -import './styles.css'; - -/*:: -type Props = {| - automatic?:boolean, - className?: string, - currentUser: { login: string, isLoggedIn: boolean }, - onFinish: () => void, - organizationsEnabled: boolean -|}; -*/ - -/*:: -type State = { - finished: boolean, - organization?: string, - projectKey?: string, - skipping: boolean, - step: string, - token?: string -}; -*/ - -export default class Onboarding extends React.PureComponent { - /*:: mounted: boolean; */ - /*:: props: Props; */ - /*:: state: State; */ - - static contextTypes = { - router: PropTypes.object - }; - - constructor(props /*: Props */) { - super(props); - this.state = { - finished: false, - skipping: false, - step: props.organizationsEnabled ? 'organization' : 'token' - }; - } - - componentDidMount() { - this.mounted = true; - - // useCapture = true to receive the event before inputs - window.addEventListener('keydown', this.onKeyDown, true); - - if (!this.props.currentUser.isLoggedIn) { - handleRequiredAuthentication(); - } - } - - componentWillUnmount() { - window.removeEventListener('keydown', this.onKeyDown, true); - this.mounted = false; - } - - onKeyDown = (event /*: KeyboardEvent */) => { - // ESC key - if (event.keyCode === 27) { - this.finishOnboarding(); - } - }; - - finishOnboarding = () => { - this.setState({ skipping: true }); - skipOnboarding().then( - () => { - if (this.mounted) { - this.props.onFinish(); - - if (this.state.projectKey) { - this.context.router.push(getProjectUrl(this.state.projectKey)); - } - } - }, - () => { - if (this.mounted) { - this.setState({ skipping: false }); - } - } - ); - }; - - handleTimeout = () => { - // unset `projectKey` to display a generic "Finish this tutorial" button - this.setState({ projectKey: undefined }); - }; - - handleTokenDone = (token /*: string */) => { - this.setState({ step: 'analysis', token }); - }; - - handleOrganizationDone = (organization /*: string */) => { - this.setState({ organization, step: 'token' }); - }; - - handleTokenOpen = () => this.setState({ step: 'token' }); - - handleOrganizationOpen = () => this.setState({ step: 'organization' }); - - handleSkipClick = (event /*: Event */) => { - event.preventDefault(); - this.finishOnboarding(); - }; - - handleFinish = (projectKey /*: string | void */) => this.setState({ finished: true, projectKey }); - - handleReset = () => this.setState({ finished: false, projectKey: undefined }); - - render() { - if (!this.props.currentUser.isLoggedIn) { - return null; - } - - const { automatic, organizationsEnabled } = this.props; - const { step, token } = this.state; - let stepNumber = 1; - - return ( -
- - {transformedMessage => } - - -
-
-

- -

-
- - - {automatic ? translate('tutorials.skip') : translate('close')} - - - -

- {translate( - isSonarCloud() - ? 'tutorials.find_it_back_in_plus' - : 'tutorials.find_it_back_in_help' - )} -

-
-
- {translateWithParameters( - 'onboarding.header.description', - organizationsEnabled ? 3 : 2 - )} -
-
- - {organizationsEnabled && ( - - )} - - - - - - {this.state.finished && - !this.state.skipping && - (this.state.projectKey ? ( - - ) : ( - - ))} -
-
- ); - } -} diff --git a/server/sonar-web/src/main/js/apps/tutorials/onboarding/OnboardingContainer.js b/server/sonar-web/src/main/js/apps/tutorials/onboarding/OnboardingContainer.js deleted file mode 100644 index cf27d5dd333..00000000000 --- a/server/sonar-web/src/main/js/apps/tutorials/onboarding/OnboardingContainer.js +++ /dev/null @@ -1,33 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2018 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -// @flow -import { connect } from 'react-redux'; -import Onboarding from './Onboarding'; -import { getCurrentUser, areThereCustomOrganizations } from '../../../store/rootReducer'; - -const mapStateToProps = state => { - return { - className: 'modal-container', - currentUser: getCurrentUser(state), - organizationsEnabled: areThereCustomOrganizations(state) - }; -}; - -export default connect(mapStateToProps)(Onboarding); diff --git a/server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/__snapshots__/Onboarding-test.js.snap b/server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/__snapshots__/Onboarding-test.js.snap deleted file mode 100644 index 1635dc70bd5..00000000000 --- a/server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/__snapshots__/Onboarding-test.js.snap +++ /dev/null @@ -1,388 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`guides for on-premise 1`] = ` -
- -
-
-

- -

-
- - - close - - -

- tutorials.find_it_back_in_help -

-
-
- onboarding.header.description.2 -
-
- - -
-
-`; - -exports[`guides for on-premise 2`] = ` -
- -
-
-

- -

-
- - - close - - -

- tutorials.find_it_back_in_help -

-
-
- onboarding.header.description.2 -
-
- - -
-
-`; - -exports[`guides for sonarcloud 1`] = ` -
- -
-
-

- -

-
- - - close - - -

- tutorials.find_it_back_in_plus -

-
-
- onboarding.header.description.3 -
-
- - - -
-
-`; - -exports[`guides for sonarcloud 2`] = ` -
- -
-
-

- -

-
- - - close - - -

- tutorials.find_it_back_in_plus -

-
-
- onboarding.header.description.3 -
-
- - - -
-
-`; - -exports[`guides for sonarcloud 3`] = ` -
- -
-
-

- -

-
- - - close - - -

- tutorials.find_it_back_in_plus -

-
-
- onboarding.header.description.3 -
-
- - - -
-
-`; diff --git a/server/sonar-web/src/main/js/apps/tutorials/onboarding/AnalysisStep.js b/server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/AnalysisStep.tsx similarity index 61% rename from server/sonar-web/src/main/js/apps/tutorials/onboarding/AnalysisStep.js rename to server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/AnalysisStep.tsx index afaa359bfb7..a91ff541119 100644 --- a/server/sonar-web/src/main/js/apps/tutorials/onboarding/AnalysisStep.js +++ b/server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/AnalysisStep.tsx @@ -17,11 +17,9 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -// @flow -import React from 'react'; +import * as React from 'react'; import Step from './Step'; -import LanguageStep from './LanguageStep'; -/*:: import type { Result } from './LanguageStep'; */ +import LanguageStep, { Result } from './LanguageStep'; import JavaMaven from './commands/JavaMaven'; import JavaGradle from './commands/JavaGradle'; import DotNet from './commands/DotNet'; @@ -31,28 +29,23 @@ import Other from './commands/Other'; import { translate } from '../../../helpers/l10n'; import { getHostUrl } from '../../../helpers/urls'; -/*:: -type Props = {| - onFinish: (projectKey?: string) => void, - onReset: () => void, - open: boolean, - organization?: string, - stepNumber: number, - token: string -|}; -*/ - -/*:: -type State = { - result?: Result -}; -*/ - -export default class AnalysisStep extends React.PureComponent { - /*:: props: Props; */ - state /*: State */ = {}; - - handleLanguageSelect = (result /*: Result | void */) => { +interface Props { + onFinish: (projectKey?: string) => void; + onReset: () => void; + open: boolean; + organization?: string; + stepNumber: number; + token?: string; +} + +interface State { + result?: Result; +} + +export default class AnalysisStep extends React.PureComponent { + state: State = {}; + + handleLanguageSelect = (result?: Result) => { this.setState({ result }); const projectKey = result && result.language !== 'java' ? result.projectKey : undefined; this.props.onFinish(projectKey); @@ -80,7 +73,7 @@ export default class AnalysisStep extends React.PureComponent { ); }; - renderFormattedCommand = (...lines /*: Array */) => ( + renderFormattedCommand = (...lines: Array) => ( // keep this "useless" concatentation for the readability reason // eslint-disable-next-line no-useless-concat
{lines.join(' ' + '\\' + '\n' + '  ')}
@@ -108,69 +101,87 @@ export default class AnalysisStep extends React.PureComponent { } }; - renderCommandForMaven = () => ( - - ); + renderCommandForMaven = () => { + const { token } = this.props; + if (!token) { + return null; + } + return ; + }; - renderCommandForGradle = () => ( - - ); + renderCommandForGradle = () => { + const { token } = this.props; + if (!token) { + return null; + } + return ; + }; renderCommandForDotNet = () => { + const { token } = this.props; + const { result } = this.state; + if (!result || !result.projectKey || !token) { + return null; + } return ( ); }; renderCommandForMSVC = () => { + const { token } = this.props; + const { result } = this.state; + if (!result || !result.projectKey || !token) { + return null; + } return ( ); }; - renderCommandForClangGCC = () => ( - - ); + renderCommandForClangGCC = () => { + const { token } = this.props; + const { result } = this.state; + if (!result || !result.projectKey || !result.os || !token) { + return null; + } + return ( + + ); + }; - renderCommandForOther = () => ( - - ); + renderCommandForOther = () => { + const { token } = this.props; + const { result } = this.state; + if (!result || !result.projectKey || !result.os || !token) { + return null; + } + return ( + + ); + }; renderResult = () => null; diff --git a/server/sonar-web/src/main/js/apps/tutorials/onboarding/LanguageStep.js b/server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/LanguageStep.tsx similarity index 87% rename from server/sonar-web/src/main/js/apps/tutorials/onboarding/LanguageStep.js rename to server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/LanguageStep.tsx index c9bd1c15e5a..25b7544ddcc 100644 --- a/server/sonar-web/src/main/js/apps/tutorials/onboarding/LanguageStep.js +++ b/server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/LanguageStep.tsx @@ -17,38 +17,31 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -// @flow -import React from 'react'; + +import * as React from 'react'; import NewProjectForm from './NewProjectForm'; import RadioToggle from '../../../components/controls/RadioToggle'; import { translate } from '../../../helpers/l10n'; import { isSonarCloud } from '../../../helpers/system'; -/*:: -type Props = {| - onDone: (result: Result) => void, - onReset: () => void, - organization?: string, -|}; -*/ - -/*:: -type State = { - language?: string, - javaBuild?: string, - cFamilyCompiler?: string, - os?: string, - projectKey?: string -}; -*/ +export interface Result { + language?: string; + javaBuild?: string; + cFamilyCompiler?: string; + os?: string; + projectKey?: string; +} -/*:: -export type Result = State; */ +interface Props { + onDone: (result: Result) => void; + onReset: () => void; + organization?: string; +} -export default class LanguageStep extends React.PureComponent { - /*:: props: Props; */ +type State = Result; - state /*: State */ = {}; +export default class LanguageStep extends React.PureComponent { + state: State = {}; isConfigured = () => { const { language, javaBuild, cFamilyCompiler, os, projectKey } = this.state; @@ -69,23 +62,23 @@ export default class LanguageStep extends React.PureComponent { } }; - handleLanguageChange = (language /*: string */) => { + handleLanguageChange = (language: string) => { this.setState({ language }, this.handleChange); }; - handleJavaBuildChange = (javaBuild /*: string */) => { + handleJavaBuildChange = (javaBuild: string) => { this.setState({ javaBuild }, this.handleChange); }; - handleCFamilyCompilerChange = (cFamilyCompiler /*: string */) => { + handleCFamilyCompilerChange = (cFamilyCompiler: string) => { this.setState({ cFamilyCompiler }, this.handleChange); }; - handleOSChange = (os /*: string */) => { + handleOSChange = (os: string) => { this.setState({ os }, this.handleChange); }; - handleProjectKeyDone = (projectKey /*: string */) => { + handleProjectKeyDone = (projectKey: string) => { this.setState({ projectKey }, this.handleChange); }; diff --git a/server/sonar-web/src/main/js/apps/tutorials/onboarding/NewOrganizationForm.js b/server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/NewOrganizationForm.tsx similarity index 87% rename from server/sonar-web/src/main/js/apps/tutorials/onboarding/NewOrganizationForm.js rename to server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/NewOrganizationForm.tsx index 11be750a41b..1a0e5500129 100644 --- a/server/sonar-web/src/main/js/apps/tutorials/onboarding/NewOrganizationForm.js +++ b/server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/NewOrganizationForm.tsx @@ -17,8 +17,7 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -// @flow -import React from 'react'; +import * as React from 'react'; import { debounce } from 'lodash'; import { createOrganization, @@ -29,29 +28,23 @@ import AlertErrorIcon from '../../../components/icons-components/AlertErrorIcon' import { DeleteButton, SubmitButton } from '../../../components/ui/buttons'; import { translate } from '../../../helpers/l10n'; -/*:: -type Props = {| - onDelete: () => void, - onDone: (organization: string) => void, - organization?: string -|}; -*/ +interface Props { + onDelete: () => void; + onDone: (organization: string) => void; + organization?: string; +} -/*:: -type State = { - done: boolean, - loading: boolean, - organization: string, - unique: boolean -}; -*/ +interface State { + done: boolean; + loading: boolean; + organization: string; + unique: boolean; +} -export default class NewOrganizationForm extends React.PureComponent { - /*:: mounted: boolean; */ - /*:: props: Props; */ - /*:: state: State; */ +export default class NewOrganizationForm extends React.PureComponent { + mounted = false; - constructor(props /*: Props */) { + constructor(props: Props) { super(props); this.state = { done: props.organization != null, @@ -76,7 +69,7 @@ export default class NewOrganizationForm extends React.PureComponent { } }; - validateOrganization = (organization /*: string */) => { + validateOrganization = (organization: string) => { getOrganization(organization).then(response => { if (this.mounted) { this.setState({ unique: response == null }); @@ -84,19 +77,19 @@ export default class NewOrganizationForm extends React.PureComponent { }); }; - sanitizeOrganization = (organization /*: string */) => + sanitizeOrganization = (organization: string) => organization .toLowerCase() .replace(/[^a-z0-9-]/, '') .replace(/^-/, ''); - handleOrganizationChange = (event /*: { target: HTMLInputElement } */) => { + handleOrganizationChange = (event: React.ChangeEvent) => { const organization = this.sanitizeOrganization(event.target.value); this.setState({ organization }); this.validateOrganization(organization); }; - handleOrganizationCreate = (event /*: Event */) => { + handleOrganizationCreate = (event: React.FormEvent) => { event.preventDefault(); const { organization } = this.state; if (organization) { diff --git a/server/sonar-web/src/main/js/apps/tutorials/onboarding/NewProjectForm.js b/server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/NewProjectForm.tsx similarity index 83% rename from server/sonar-web/src/main/js/apps/tutorials/onboarding/NewProjectForm.js rename to server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/NewProjectForm.tsx index cde4629a0a8..f09b4d152bf 100644 --- a/server/sonar-web/src/main/js/apps/tutorials/onboarding/NewProjectForm.js +++ b/server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/NewProjectForm.tsx @@ -17,35 +17,28 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -// @flow -import React from 'react'; +import * as React from 'react'; import { createProject, deleteProject } from '../../../api/components'; import { DeleteButton, SubmitButton } from '../../../components/ui/buttons'; import { translate } from '../../../helpers/l10n'; -/*:: -type Props = {| - onDelete: () => void, - onDone: (projectKey: string) => void, - organization?: string, - projectKey?: string -|}; -*/ +interface Props { + onDelete: () => void; + onDone: (projectKey: string) => void; + organization?: string; + projectKey?: string; +} -/*:: -type State = { - done: boolean, - loading: boolean, - projectKey: string -}; -*/ +interface State { + done: boolean; + loading: boolean; + projectKey: string; +} -export default class NewProjectForm extends React.PureComponent { - /*:: mounted: boolean; */ - /*:: props: Props; */ - /*:: state: State; */ +export default class NewProjectForm extends React.PureComponent { + mounted = false; - constructor(props /*: Props */) { + constructor(props: Props) { super(props); this.state = { done: props.projectKey != null, @@ -68,16 +61,20 @@ export default class NewProjectForm extends React.PureComponent { } }; - sanitizeProjectKey = (projectKey /*: string */) => projectKey.replace(/[^-_a-zA-Z0-9.:]/, ''); + sanitizeProjectKey = (projectKey: string) => projectKey.replace(/[^-_a-zA-Z0-9.:]/, ''); - handleProjectKeyChange = (event /*: { target: HTMLInputElement } */) => { + handleProjectKeyChange = (event: React.ChangeEvent) => { this.setState({ projectKey: this.sanitizeProjectKey(event.target.value) }); }; - handleProjectCreate = (event /*: Event */) => { + handleProjectCreate = (event: React.FormEvent) => { event.preventDefault(); const { projectKey } = this.state; - const data /*: { [string]: string } */ = { + const data: { + name: string; + project: string; + organization?: string; + } = { name: projectKey, project: projectKey }; diff --git a/server/sonar-web/src/main/js/apps/tutorials/onboarding/OrganizationStep.js b/server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/OrganizationStep.tsx similarity index 89% rename from server/sonar-web/src/main/js/apps/tutorials/onboarding/OrganizationStep.js rename to server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/OrganizationStep.tsx index e55c34a341a..0d155ef9eee 100644 --- a/server/sonar-web/src/main/js/apps/tutorials/onboarding/OrganizationStep.js +++ b/server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/OrganizationStep.tsx @@ -17,9 +17,8 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -// @flow -import React from 'react'; -import classNames from 'classnames'; +import * as React from 'react'; +import * as classNames from 'classnames'; import { sortBy } from 'lodash'; import Step from './Step'; import NewOrganizationForm from './NewOrganizationForm'; @@ -30,32 +29,27 @@ import Select from '../../../components/controls/Select'; import { translate } from '../../../helpers/l10n'; import { Button } from '../../../components/ui/buttons'; -/*:: -type Props = {| - currentUser: { login: string, isLoggedIn: boolean }, - finished: boolean, - onOpen: () => void, - onContinue: (organization: string) => void, - open: boolean, - stepNumber: number -|}; -*/ +interface Props { + currentUser: { login: string; isLoggedIn: boolean }; + finished: boolean; + onOpen: () => void; + onContinue: (organization: string) => void; + open: boolean; + stepNumber: number; +} -/*:: -type State = { - loading: boolean, - newOrganization?: string, - existingOrganization?: string, - existingOrganizations: Array, - personalOrganization?: string, - selection: 'personal' | 'existing' | 'new' -}; -*/ +interface State { + loading: boolean; + newOrganization?: string; + existingOrganization?: string; + existingOrganizations: Array; + personalOrganization?: string; + selection: 'personal' | 'existing' | 'new'; +} -export default class OrganizationStep extends React.PureComponent { - /*:: mounted: boolean; */ - /*:: props: Props; */ - state /*: State */ = { +export default class OrganizationStep extends React.PureComponent { + mounted = false; + state: State = { loading: true, existingOrganizations: [], selection: 'personal' @@ -112,22 +106,22 @@ export default class OrganizationStep extends React.PureComponent { } }; - handlePersonalClick = (event /*: Event */) => { + handlePersonalClick = (event: React.MouseEvent) => { event.preventDefault(); this.setState({ selection: 'personal' }); }; - handleExistingClick = (event /*: Event */) => { + handleExistingClick = (event: React.MouseEvent) => { event.preventDefault(); this.setState({ selection: 'existing' }); }; - handleNewClick = (event /*: Event */) => { + handleNewClick = (event: React.MouseEvent) => { event.preventDefault(); this.setState({ selection: 'new' }); }; - handleOrganizationCreate = (newOrganization /*: string */) => { + handleOrganizationCreate = (newOrganization: string) => { this.setState({ newOrganization }); }; @@ -135,7 +129,7 @@ export default class OrganizationStep extends React.PureComponent { this.setState({ newOrganization: undefined }); }; - handleExistingOrganizationSelect = ({ value } /*: { value: string } */) => { + handleExistingOrganizationSelect = ({ value }: { value: string }) => { this.setState({ existingOrganization: value }); }; diff --git a/server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/ProjectOnboarding.tsx b/server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/ProjectOnboarding.tsx new file mode 100644 index 00000000000..384e1726e39 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/ProjectOnboarding.tsx @@ -0,0 +1,206 @@ +/* + * SonarQube + * Copyright (C) 2009-2018 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import * as React from 'react'; +import * as PropTypes from 'prop-types'; +import Helmet from 'react-helmet'; +import { connect } from 'react-redux'; +import TokenStep from './TokenStep'; +import OrganizationStep from './OrganizationStep'; +import AnalysisStep from './AnalysisStep'; +import ProjectWatcher from './ProjectWatcher'; +import handleRequiredAuthentication from '../../../app/utils/handleRequiredAuthentication'; +import { getCurrentUser, areThereCustomOrganizations } from '../../../store/rootReducer'; +import { CurrentUser, isLoggedIn } from '../../../app/types'; +import { ResetButtonLink } from '../../../components/ui/buttons'; +import { getProjectUrl } from '../../../helpers/urls'; +import { translate, translateWithParameters } from '../../../helpers/l10n'; +import { isSonarCloud } from '../../../helpers/system'; +import '../styles.css'; + +interface OwnProps { + automatic?: boolean; + onFinish: () => void; +} + +interface StateProps { + currentUser: CurrentUser; + organizationsEnabled: boolean; +} + +type Props = OwnProps & StateProps; + +interface State { + finished: boolean; + organization?: string; + projectKey?: string; + step: string; + token?: string; +} + +export class ProjectOnboarding extends React.PureComponent { + mounted = false; + static contextTypes = { + router: PropTypes.object + }; + + constructor(props: Props) { + super(props); + this.state = { + finished: false, + step: props.organizationsEnabled ? 'organization' : 'token' + }; + } + + componentDidMount() { + this.mounted = true; + + // useCapture = true to receive the event before inputs + window.addEventListener('keydown', this.onKeyDown, true); + + if (!isLoggedIn(this.props.currentUser)) { + handleRequiredAuthentication(); + } + } + + componentWillUnmount() { + window.removeEventListener('keydown', this.onKeyDown, true); + this.mounted = false; + } + + onKeyDown = (event: KeyboardEvent) => { + if (event.key === 'Escape') { + this.finishOnboarding(); + } + }; + + finishOnboarding = () => { + this.props.onFinish(); + if (this.state.projectKey) { + this.context.router.push(getProjectUrl(this.state.projectKey)); + } + }; + + handleTimeout = () => { + // unset `projectKey` to display a generic "Finish this tutorial" button + this.setState({ projectKey: undefined }); + }; + + handleTokenDone = (token: string) => { + this.setState({ step: 'analysis', token }); + }; + + handleOrganizationDone = (organization: string) => { + this.setState({ organization, step: 'token' }); + }; + + handleTokenOpen = () => this.setState({ step: 'token' }); + + handleOrganizationOpen = () => this.setState({ step: 'organization' }); + + handleFinish = (projectKey?: string) => this.setState({ finished: true, projectKey }); + + handleReset = () => this.setState({ finished: false, projectKey: undefined }); + + render() { + const { automatic, currentUser, organizationsEnabled } = this.props; + if (!isLoggedIn(currentUser)) { + return null; + } + + const { finished, projectKey, step, token } = this.state; + const header = translate('onboarding.project.header'); + let stepNumber = 1; + + return ( + <> + +
+

{header}

+
+
+

+ {translateWithParameters( + 'onboarding.project.header.description', + organizationsEnabled ? 3 : 2 + )} +

+ {organizationsEnabled && ( + + )} + + + + +
+
+ + {(finished && translate('tutorials.finish')) || + (automatic ? translate('tutorials.skip') : translate('close'))} + + {finished && projectKey ? ( + + ) : ( + + {translate( + isSonarCloud() + ? 'tutorials.find_tutorial_back_in_plus' + : 'tutorials.find_tutorial_back_in_help' + )} + + )} +
+ + ); + } +} + +const mapStateToProps = (state: any): StateProps => { + return { + currentUser: getCurrentUser(state), + organizationsEnabled: areThereCustomOrganizations(state) + }; +}; + +export default connect(mapStateToProps)(ProjectOnboarding); diff --git a/server/sonar-web/src/main/js/apps/tutorials/onboarding/OnboardingModal.tsx b/server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/ProjectOnboardingModal.tsx similarity index 87% rename from server/sonar-web/src/main/js/apps/tutorials/onboarding/OnboardingModal.tsx rename to server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/ProjectOnboardingModal.tsx index 188e5cc7927..d91e78872f4 100644 --- a/server/sonar-web/src/main/js/apps/tutorials/onboarding/OnboardingModal.tsx +++ b/server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/ProjectOnboardingModal.tsx @@ -27,12 +27,12 @@ interface Props { onFinish: () => void; } -const OnboardingContainer = lazyLoad(() => import('./OnboardingContainer')); +const ProjectOnboarding = lazyLoad(() => import('./ProjectOnboarding')); -export default function OnboardingModal(props: Props) { +export default function ProjectOnboardingModal(props: Props) { return ( - + ); } diff --git a/server/sonar-web/src/main/js/apps/tutorials/onboarding/OnboardingPage.tsx b/server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/ProjectOnboardingPage.tsx similarity index 81% rename from server/sonar-web/src/main/js/apps/tutorials/onboarding/OnboardingPage.tsx rename to server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/ProjectOnboardingPage.tsx index 971ae8bddff..5d0978e41d9 100644 --- a/server/sonar-web/src/main/js/apps/tutorials/onboarding/OnboardingPage.tsx +++ b/server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/ProjectOnboardingPage.tsx @@ -20,14 +20,14 @@ import * as React from 'react'; import * as PropTypes from 'prop-types'; import { connect } from 'react-redux'; -import OnboardingModal from './OnboardingModal'; +import ProjectOnboardingModal from './ProjectOnboardingModal'; import { skipOnboarding } from '../../../store/users/actions'; interface DispatchProps { skipOnboarding: () => void; } -export class OnboardingPage extends React.PureComponent { +export class ProjectOnboardingPage extends React.PureComponent { static contextTypes = { router: PropTypes.object.isRequired }; @@ -38,10 +38,10 @@ export class OnboardingPage extends React.PureComponent { }; render() { - return ; + return ; } } const mapDispatchToProps: DispatchProps = { skipOnboarding }; -export default connect<{}, DispatchProps, {}>(null, mapDispatchToProps)(OnboardingPage); +export default connect<{}, DispatchProps>(null, mapDispatchToProps)(ProjectOnboardingPage); diff --git a/server/sonar-web/src/main/js/apps/tutorials/onboarding/ProjectWatcher.js b/server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/ProjectWatcher.tsx similarity index 75% rename from server/sonar-web/src/main/js/apps/tutorials/onboarding/ProjectWatcher.js rename to server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/ProjectWatcher.tsx index e836e988fbe..31683d2f4cd 100644 --- a/server/sonar-web/src/main/js/apps/tutorials/onboarding/ProjectWatcher.js +++ b/server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/ProjectWatcher.tsx @@ -17,8 +17,9 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -// @flow -import React from 'react'; + +import * as React from 'react'; +import * as classNames from 'classnames'; import AlertErrorIcon from '../../../components/icons-components/AlertErrorIcon'; import AlertSuccessIcon from '../../../components/icons-components/AlertSuccessIcon'; import { getTasksForComponent } from '../../../api/ce'; @@ -28,35 +29,27 @@ import { translate } from '../../../helpers/l10n'; const INTERVAL = 5000; const TIMEOUT = 10 * 60 * 1000; // 10 min -/*:: -type Props = { - onFinish: () => void, - onTimeout: () => void, - projectKey: string -}; -*/ +interface Props { + onFinish: () => void; + onTimeout: () => void; + projectKey: string; +} -/*:: -type State = { - inQueue: boolean, - status: ?string -}; -*/ +interface State { + inQueue: boolean; + status?: string; +} -export default class ProjectWatcher extends React.PureComponent { - /*:: interval: number; */ - /*:: mounted: boolean; */ - /*:: props: Props; */ - /*:: timeout: number; */ - state /*: State */ = { - inQueue: false, - status: null - }; +export default class ProjectWatcher extends React.PureComponent { + interval?: number; + timeout?: number; + mounted = false; + state: State = { inQueue: false }; componentDidMount() { this.mounted = true; this.watch(); - this.timeout = setTimeout(this.props.onTimeout, TIMEOUT); + this.timeout = window.setTimeout(this.props.onTimeout, TIMEOUT); } componentWillUnmount() { @@ -65,7 +58,7 @@ export default class ProjectWatcher extends React.PureComponent { this.mounted = false; } - watch = () => (this.interval = setTimeout(this.checkProject, INTERVAL)); + watch = () => (this.interval = window.setTimeout(this.checkProject, INTERVAL)); checkProject = () => { const { projectKey } = this.props; @@ -93,10 +86,11 @@ export default class ProjectWatcher extends React.PureComponent { render() { const { inQueue, status } = this.state; + const className = 'pull-left note'; if (status === STATUSES.SUCCESS) { return ( -
+
{translate('onboarding.project_watcher.finished')}
@@ -105,7 +99,7 @@ export default class ProjectWatcher extends React.PureComponent { if (inQueue || status === STATUSES.PENDING || status === STATUSES.IN_PROGRESS) { return ( -
+
{translate('onboarding.project_watcher.in_progress')}
@@ -114,17 +108,13 @@ export default class ProjectWatcher extends React.PureComponent { if (status != null) { return ( -
+
{translate('onboarding.project_watcher.failed')}
); } - return ( -
- {translate('onboarding.project_watcher.not_started')} -
- ); + return
{translate('onboarding.project_watcher.not_started')}
; } } diff --git a/server/sonar-web/src/main/js/apps/tutorials/onboarding/Step.js b/server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/Step.tsx similarity index 76% rename from server/sonar-web/src/main/js/apps/tutorials/onboarding/Step.js rename to server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/Step.tsx index d9f6394a75f..7fda3877528 100644 --- a/server/sonar-web/src/main/js/apps/tutorials/onboarding/Step.js +++ b/server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/Step.tsx @@ -17,23 +17,21 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -// @flow -import React from 'react'; -import classNames from 'classnames'; +/* eslint-disable jsx-a11y/no-static-element-interactions, jsx-a11y/no-noninteractive-tabindex */ +import * as React from 'react'; +import * as classNames from 'classnames'; -/*:: -type Props = {| - finished: boolean, - onOpen: () => void, - open: boolean, - renderForm: () => React.Element<*>, - renderResult: () => ?React.Element<*>, - stepNumber: number, - stepTitle: React.Element<*> | string -|}; -*/ +interface Props { + finished: boolean; + onOpen: () => void; + open: boolean; + renderForm: () => React.ReactNode; + renderResult: () => React.ReactNode; + stepNumber: number; + stepTitle: React.ReactNode; +} -export default function Step(props /*: Props */) { +export default function Step(props: Props) { const className = classNames('boxed-group', 'onboarding-step', { 'is-open': props.open, 'is-finished': props.finished @@ -41,7 +39,7 @@ export default function Step(props /*: Props */) { const clickable = !props.open && props.finished; - const handleClick = (event /*: Event */) => { + const handleClick = (event: React.MouseEvent) => { event.preventDefault(); props.onOpen(); }; diff --git a/server/sonar-web/src/main/js/apps/tutorials/onboarding/TokenStep.js b/server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/TokenStep.tsx similarity index 81% rename from server/sonar-web/src/main/js/apps/tutorials/onboarding/TokenStep.js rename to server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/TokenStep.tsx index 6ef86cd7b3a..e0cd2c5aacc 100644 --- a/server/sonar-web/src/main/js/apps/tutorials/onboarding/TokenStep.js +++ b/server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/TokenStep.tsx @@ -17,9 +17,9 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -// @flow -import React from 'react'; -import classNames from 'classnames'; + +import * as React from 'react'; +import * as classNames from 'classnames'; import Step from './Step'; import { getTokens, generateToken, revokeToken } from '../../../api/user-tokens'; import AlertErrorIcon from '../../../components/icons-components/AlertErrorIcon'; @@ -27,32 +27,28 @@ import AlertSuccessIcon from '../../../components/icons-components/AlertSuccessI import { DeleteButton, SubmitButton, Button } from '../../../components/ui/buttons'; import { translate } from '../../../helpers/l10n'; -/*:: -type Props = {| - currentUser: { login: string }, - finished: boolean, - open: boolean, - onContinue: (token: string) => void, - onOpen: () => void, - stepNumber: number -|}; -*/ +interface Props { + currentUser: { login: string }; + finished: boolean; + open: boolean; + onContinue: (token: string) => void; + onOpen: () => void; + stepNumber: number; +} -/*:: -type State = { - canUseExisting: boolean, - existingToken: string, - loading: boolean, - selection: string, - tokenName?: string, - token?: string -}; -*/ +interface State { + canUseExisting: boolean; + existingToken: string; + loading: boolean; + selection: string; + tokenName?: string; + token?: string; +} -export default class TokenStep extends React.PureComponent { - /*:: mounted: boolean; */ - /*:: props: Props; */ - state /*: State */ = { +export default class TokenStep extends React.PureComponent { + mounted = false; + + state: State = { canUseExisting: false, existingToken: '', loading: false, @@ -87,27 +83,20 @@ export default class TokenStep extends React.PureComponent { ); }; - handleTokenNameChange = (event /*: { target: HTMLInputElement } */) => { + handleTokenNameChange = (event: React.ChangeEvent) => { this.setState({ tokenName: event.target.value }); }; - handleTokenGenerate = (event /*: Event */) => { + handleTokenGenerate = (event: React.FormEvent) => { event.preventDefault(); const { tokenName } = this.state; if (tokenName) { this.setState({ loading: true }); - generateToken({ name: tokenName }).then( - ({ token }) => { - if (this.mounted) { - this.setState({ loading: false, token }); - } - }, - () => { - if (this.mounted) { - this.setState({ loading: false }); - } + generateToken({ name: tokenName }).then(({ token }) => { + if (this.mounted) { + this.setState({ loading: false, token }); } - ); + }, this.stopLoading); } }; @@ -115,18 +104,11 @@ export default class TokenStep extends React.PureComponent { const { tokenName } = this.state; if (tokenName) { this.setState({ loading: true }); - revokeToken({ name: tokenName }).then( - () => { - if (this.mounted) { - this.setState({ loading: false, token: undefined, tokenName: undefined }); - } - }, - () => { - if (this.mounted) { - this.setState({ loading: false }); - } + revokeToken({ name: tokenName }).then(() => { + if (this.mounted) { + this.setState({ loading: false, token: undefined, tokenName: undefined }); } - ); + }, this.stopLoading); } }; @@ -137,20 +119,26 @@ export default class TokenStep extends React.PureComponent { } }; - handleGenerateClick = (event /*: Event */) => { + handleGenerateClick = (event: React.MouseEvent) => { event.preventDefault(); this.setState({ selection: 'generate' }); }; - handleUseExistingClick = (event /*: Event */) => { + handleUseExistingClick = (event: React.MouseEvent) => { event.preventDefault(); this.setState({ selection: 'use-existing' }); }; - handleExisingTokenChange = (event /*: { currentTarget: HTMLInputElement } */) => { + handleExisingTokenChange = (event: React.ChangeEvent) => { this.setState({ existingToken: event.currentTarget.value }); }; + stopLoading = () => { + if (this.mounted) { + this.setState({ loading: false }); + } + }; + renderGenerateOption = () => (
{this.state.canUseExisting ? ( @@ -163,10 +151,10 @@ export default class TokenStep extends React.PureComponent { 'is-checked': this.state.selection === 'generate' })} /> - {translate('onboading.token.generate_token')} + {translate('onboarding.token.generate_token')} ) : ( - translate('onboading.token.generate_token') + translate('onboarding.token.generate_token') )} {this.state.selection === 'generate' && (
@@ -175,7 +163,7 @@ export default class TokenStep extends React.PureComponent { autoFocus={true} className="input-large spacer-right text-middle" onChange={this.handleTokenNameChange} - placeholder={translate('onboading.token.generate_token.placeholder')} + placeholder={translate('onboarding.token.generate_token.placeholder')} required={true} type="text" value={this.state.tokenName || ''} diff --git a/server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/LanguageStep-test.js b/server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/__tests__/LanguageStep-test.tsx similarity index 73% rename from server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/LanguageStep-test.js rename to server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/__tests__/LanguageStep-test.tsx index 2609b550dd5..629044f2c4c 100644 --- a/server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/LanguageStep-test.js +++ b/server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/__tests__/LanguageStep-test.tsx @@ -17,8 +17,7 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -// @flow -import React from 'react'; +import * as React from 'react'; import { shallow } from 'enzyme'; import LanguageStep from '../LanguageStep'; import { isSonarCloud } from '../../../../helpers/system'; @@ -26,29 +25,29 @@ import { isSonarCloud } from '../../../../helpers/system'; jest.mock('../../../../helpers/system', () => ({ isSonarCloud: jest.fn() })); beforeEach(() => { - isSonarCloud.mockImplementation(() => false); + (isSonarCloud as jest.Mock).mockImplementation(() => false); }); it('selects java', () => { const onDone = jest.fn(); const wrapper = shallow(); - wrapper.find('RadioToggle').prop('onCheck')('java'); + (wrapper.find('RadioToggle').prop('onCheck') as Function)('java'); wrapper.update(); expect(wrapper).toMatchSnapshot(); - wrapper + (wrapper .find('RadioToggle') .at(1) - .prop('onCheck')('maven'); + .prop('onCheck') as Function)('maven'); wrapper.update(); expect(wrapper).toMatchSnapshot(); expect(onDone).lastCalledWith({ language: 'java', javaBuild: 'maven' }); - wrapper + (wrapper .find('RadioToggle') .at(1) - .prop('onCheck')('gradle'); + .prop('onCheck') as Function)('gradle'); wrapper.update(); expect(wrapper).toMatchSnapshot(); expect(onDone).lastCalledWith({ language: 'java', javaBuild: 'gradle' }); @@ -58,52 +57,52 @@ it('selects c#', () => { const onDone = jest.fn(); const wrapper = shallow(); - wrapper.find('RadioToggle').prop('onCheck')('dotnet'); + (wrapper.find('RadioToggle').prop('onCheck') as Function)('dotnet'); wrapper.update(); expect(wrapper).toMatchSnapshot(); - wrapper.find('NewProjectForm').prop('onDone')('project-foo'); + (wrapper.find('NewProjectForm').prop('onDone') as Function)('project-foo'); expect(onDone).lastCalledWith({ language: 'dotnet', projectKey: 'project-foo' }); }); it('selects c-family', () => { - isSonarCloud.mockImplementation(() => true); + (isSonarCloud as jest.Mock).mockImplementation(() => true); const onDone = jest.fn(); const wrapper = shallow(); - wrapper.find('RadioToggle').prop('onCheck')('c-family'); + (wrapper.find('RadioToggle').prop('onCheck') as Function)('c-family'); wrapper.update(); expect(wrapper).toMatchSnapshot(); - wrapper + (wrapper .find('RadioToggle') .at(1) - .prop('onCheck')('msvc'); + .prop('onCheck') as Function)('msvc'); wrapper.update(); expect(wrapper).toMatchSnapshot(); - wrapper.find('NewProjectForm').prop('onDone')('project-foo'); + (wrapper.find('NewProjectForm').prop('onDone') as Function)('project-foo'); expect(onDone).lastCalledWith({ language: 'c-family', cFamilyCompiler: 'msvc', projectKey: 'project-foo' }); - wrapper + (wrapper .find('RadioToggle') .at(1) - .prop('onCheck')('clang-gcc'); + .prop('onCheck') as Function)('clang-gcc'); wrapper.update(); expect(wrapper).toMatchSnapshot(); - wrapper + (wrapper .find('RadioToggle') .at(2) - .prop('onCheck')('linux'); + .prop('onCheck') as Function)('linux'); wrapper.update(); expect(wrapper).toMatchSnapshot(); - wrapper.find('NewProjectForm').prop('onDone')('project-foo'); + (wrapper.find('NewProjectForm').prop('onDone') as Function)('project-foo'); expect(onDone).lastCalledWith({ language: 'c-family', cFamilyCompiler: 'clang-gcc', @@ -116,17 +115,17 @@ it('selects other', () => { const onDone = jest.fn(); const wrapper = shallow(); - wrapper.find('RadioToggle').prop('onCheck')('other'); + (wrapper.find('RadioToggle').prop('onCheck') as Function)('other'); wrapper.update(); expect(wrapper).toMatchSnapshot(); - wrapper + (wrapper .find('RadioToggle') .at(1) - .prop('onCheck')('mac'); + .prop('onCheck') as Function)('mac'); wrapper.update(); expect(wrapper).toMatchSnapshot(); - wrapper.find('NewProjectForm').prop('onDone')('project-foo'); + (wrapper.find('NewProjectForm').prop('onDone') as Function)('project-foo'); expect(onDone).lastCalledWith({ language: 'other', os: 'mac', projectKey: 'project-foo' }); }); diff --git a/server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/NewOrganizationForm-test.js b/server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/__tests__/NewOrganizationForm-test.tsx similarity index 95% rename from server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/NewOrganizationForm-test.js rename to server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/__tests__/NewOrganizationForm-test.tsx index fcfbf8d810c..bb16f901167 100644 --- a/server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/NewOrganizationForm-test.js +++ b/server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/__tests__/NewOrganizationForm-test.tsx @@ -17,8 +17,7 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -// @flow -import React from 'react'; +import * as React from 'react'; import { mount } from 'enzyme'; import NewOrganizationForm from '../NewOrganizationForm'; import { change, submit, waitAndUpdate } from '../../../../helpers/testUtils'; @@ -48,7 +47,7 @@ it('deletes organization', async () => { const wrapper = mount(); wrapper.setState({ done: true, loading: false, organization: 'foo' }); expect(wrapper).toMatchSnapshot(); - wrapper.find('DeleteButton').prop('onClick')(); + (wrapper.find('DeleteButton').prop('onClick') as Function)(); wrapper.update(); expect(wrapper).toMatchSnapshot(); // spinner await waitAndUpdate(wrapper); diff --git a/server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/NewProjectForm-test.js b/server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/__tests__/NewProjectForm-test.tsx similarity index 95% rename from server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/NewProjectForm-test.js rename to server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/__tests__/NewProjectForm-test.tsx index 2d290f10503..2c8c18cace4 100644 --- a/server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/NewProjectForm-test.js +++ b/server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/__tests__/NewProjectForm-test.tsx @@ -17,8 +17,7 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -// @flow -import React from 'react'; +import * as React from 'react'; import { mount } from 'enzyme'; import NewProjectForm from '../NewProjectForm'; import { change, submit, waitAndUpdate } from '../../../../helpers/testUtils'; @@ -47,7 +46,7 @@ it('deletes project', async () => { const wrapper = mount(); wrapper.setState({ done: true, loading: false, projectKey: 'foo' }); expect(wrapper).toMatchSnapshot(); - wrapper.find('DeleteButton').prop('onClick')(); + (wrapper.find('DeleteButton').prop('onClick') as Function)(); wrapper.update(); expect(wrapper).toMatchSnapshot(); // spinner await waitAndUpdate(wrapper); diff --git a/server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/OrganizationStep-test.js b/server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/__tests__/OrganizationStep-test.tsx similarity index 92% rename from server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/OrganizationStep-test.js rename to server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/__tests__/OrganizationStep-test.tsx index 01a5b711a2a..b2192a0bce7 100644 --- a/server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/OrganizationStep-test.js +++ b/server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/__tests__/OrganizationStep-test.tsx @@ -17,8 +17,7 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -// @flow -import React from 'react'; +import * as React from 'react'; import { mount } from 'enzyme'; import OrganizationStep from '../OrganizationStep'; import { click, waitAndUpdate } from '../../../../helpers/testUtils'; @@ -35,7 +34,7 @@ jest.mock('../../../../api/organizations', () => ({ const currentUser = { isLoggedIn: true, login: 'user' }; beforeEach(() => { - getOrganizations.mockClear(); + (getOrganizations as jest.Mock).mockClear(); }); // FIXME @@ -73,10 +72,10 @@ it('works with existing organization', async () => { await waitAndUpdate(wrapper); click(wrapper.find('.js-existing')); expect(wrapper).toMatchSnapshot(); - wrapper + (wrapper .find('Select') .first() - .prop('onChange')({ value: 'another' }); + .prop('onChange') as Function)({ value: 'another' }); wrapper.update(); click(wrapper.find('[className="js-continue"]')); expect(onContinue).toBeCalledWith('another'); @@ -96,7 +95,7 @@ it('works with new organization', async () => { ); await waitAndUpdate(wrapper); click(wrapper.find('.js-new')); - wrapper.find('NewOrganizationForm').prop('onDone')('new'); + (wrapper.find('NewOrganizationForm').prop('onDone') as Function)('new'); wrapper.update(); click(wrapper.find('[className="js-continue"]')); expect(onContinue).toBeCalledWith('new'); diff --git a/server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/Onboarding-test.js b/server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/__tests__/ProjectOnboarding-test.tsx similarity index 65% rename from server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/Onboarding-test.js rename to server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/__tests__/ProjectOnboarding-test.tsx index e3acb2a83f6..078d6702aa6 100644 --- a/server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/Onboarding-test.js +++ b/server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/__tests__/ProjectOnboarding-test.tsx @@ -17,10 +17,9 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -// @flow -import React from 'react'; +import * as React from 'react'; import { shallow, mount } from 'enzyme'; -import Onboarding from '../Onboarding'; +import { ProjectOnboarding } from '../ProjectOnboarding'; import { click, doAsync } from '../../../../helpers/testUtils'; import { getInstance, isSonarCloud } from '../../../../helpers/system'; @@ -36,11 +35,10 @@ jest.mock('../../../../helpers/system', () => ({ const currentUser = { login: 'admin', isLoggedIn: true }; it('guides for on-premise', () => { - getInstance.mockImplementation(() => 'SonarQube'); - isSonarCloud.mockImplementation(() => false); + (getInstance as jest.Mock).mockImplementation(() => 'SonarQube'); + (isSonarCloud as jest.Mock).mockImplementation(() => false); const wrapper = shallow( - { ); expect(wrapper).toMatchSnapshot(); - // $FlowFixMe - wrapper.instance().handleTokenDone('abcd1234'); + (wrapper.instance() as ProjectOnboarding).handleTokenDone('abcd1234'); wrapper.update(); expect(wrapper).toMatchSnapshot(); }); it('guides for sonarcloud', () => { - getInstance.mockImplementation(() => 'SonarCloud'); - isSonarCloud.mockImplementation(() => true); + (getInstance as jest.Mock).mockImplementation(() => 'SonarCloud'); + (isSonarCloud as jest.Mock).mockImplementation(() => true); const wrapper = shallow( - + ); expect(wrapper).toMatchSnapshot(); - // $FlowFixMe - wrapper.instance().handleOrganizationDone('my-org'); + (wrapper.instance() as ProjectOnboarding).handleOrganizationDone('my-org'); wrapper.update(); expect(wrapper).toMatchSnapshot(); - // $FlowFixMe - wrapper.instance().handleTokenDone('abcd1234'); + (wrapper.instance() as ProjectOnboarding).handleTokenDone('abcd1234'); wrapper.update(); expect(wrapper).toMatchSnapshot(); }); it('finishes', () => { - getInstance.mockImplementation(() => 'SonarQube'); - isSonarCloud.mockImplementation(() => false); + (getInstance as jest.Mock).mockImplementation(() => 'SonarQube'); + (isSonarCloud as jest.Mock).mockImplementation(() => false); const onFinish = jest.fn(); const wrapper = mount( - + ); - click(wrapper.find('.js-skip')); + click(wrapper.find('ResetButtonLink')); return doAsync(() => { expect(onFinish).toBeCalled(); }); diff --git a/server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/ProjectWatcher-test.js b/server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/__tests__/ProjectWatcher-test.tsx similarity index 98% rename from server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/ProjectWatcher-test.js rename to server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/__tests__/ProjectWatcher-test.tsx index cc6699fdefa..16215259325 100644 --- a/server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/ProjectWatcher-test.js +++ b/server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/__tests__/ProjectWatcher-test.tsx @@ -17,8 +17,7 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -// @flow -import React from 'react'; +import * as React from 'react'; import { shallow, mount } from 'enzyme'; import ProjectWatcher from '../ProjectWatcher'; diff --git a/server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/Step-test.js b/server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/__tests__/Step-test.tsx similarity index 97% rename from server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/Step-test.js rename to server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/__tests__/Step-test.tsx index 396c4fa769a..8a9664f71c8 100644 --- a/server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/Step-test.js +++ b/server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/__tests__/Step-test.tsx @@ -17,8 +17,7 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -// @flow -import React from 'react'; +import * as React from 'react'; import { shallow } from 'enzyme'; import Step from '../Step'; import { click } from '../../../../helpers/testUtils'; diff --git a/server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/TokenStep-test.js b/server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/__tests__/TokenStep-test.tsx similarity index 97% rename from server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/TokenStep-test.js rename to server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/__tests__/TokenStep-test.tsx index 4f439b440b5..25b5f0c7462 100644 --- a/server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/TokenStep-test.js +++ b/server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/__tests__/TokenStep-test.tsx @@ -17,8 +17,7 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -// @flow -import React from 'react'; +import * as React from 'react'; import { mount } from 'enzyme'; import TokenStep from '../TokenStep'; import { change, click, submit, waitAndUpdate } from '../../../../helpers/testUtils'; @@ -67,7 +66,7 @@ it('revokes token', async () => { await new Promise(setImmediate); wrapper.setState({ token: 'abcd1234', tokenName: 'my token' }); expect(wrapper).toMatchSnapshot(); - wrapper.find('DeleteButton').prop('onClick')(); + (wrapper.find('DeleteButton').prop('onClick') as Function)(); wrapper.update(); expect(wrapper).toMatchSnapshot(); // spinner await waitAndUpdate(wrapper); diff --git a/server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/__snapshots__/LanguageStep-test.js.snap b/server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/__tests__/__snapshots__/LanguageStep-test.tsx.snap similarity index 100% rename from server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/__snapshots__/LanguageStep-test.js.snap rename to server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/__tests__/__snapshots__/LanguageStep-test.tsx.snap diff --git a/server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/__snapshots__/NewOrganizationForm-test.js.snap b/server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/__tests__/__snapshots__/NewOrganizationForm-test.tsx.snap similarity index 100% rename from server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/__snapshots__/NewOrganizationForm-test.js.snap rename to server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/__tests__/__snapshots__/NewOrganizationForm-test.tsx.snap diff --git a/server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/__snapshots__/NewProjectForm-test.js.snap b/server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/__tests__/__snapshots__/NewProjectForm-test.tsx.snap similarity index 100% rename from server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/__snapshots__/NewProjectForm-test.js.snap rename to server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/__tests__/__snapshots__/NewProjectForm-test.tsx.snap diff --git a/server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/__snapshots__/OrganizationStep-test.js.snap b/server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/__tests__/__snapshots__/OrganizationStep-test.tsx.snap similarity index 100% rename from server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/__snapshots__/OrganizationStep-test.js.snap rename to server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/__tests__/__snapshots__/OrganizationStep-test.tsx.snap diff --git a/server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/__tests__/__snapshots__/ProjectOnboarding-test.tsx.snap b/server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/__tests__/__snapshots__/ProjectOnboarding-test.tsx.snap new file mode 100644 index 00000000000..0c5b93e108a --- /dev/null +++ b/server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/__tests__/__snapshots__/ProjectOnboarding-test.tsx.snap @@ -0,0 +1,349 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`guides for on-premise 1`] = ` + + +
+

+ onboarding.project.header +

+
+
+

+ onboarding.project.header.description.2 +

+ + +
+
+ + close + + + tutorials.find_tutorial_back_in_help + +
+
+`; + +exports[`guides for on-premise 2`] = ` + + +
+

+ onboarding.project.header +

+
+
+

+ onboarding.project.header.description.2 +

+ + +
+
+ + close + + + tutorials.find_tutorial_back_in_help + +
+
+`; + +exports[`guides for sonarcloud 1`] = ` + + +
+

+ onboarding.project.header +

+
+
+

+ onboarding.project.header.description.3 +

+ + + +
+
+ + close + + + tutorials.find_tutorial_back_in_plus + +
+
+`; + +exports[`guides for sonarcloud 2`] = ` + + +
+

+ onboarding.project.header +

+
+
+

+ onboarding.project.header.description.3 +

+ + + +
+
+ + close + + + tutorials.find_tutorial_back_in_plus + +
+
+`; + +exports[`guides for sonarcloud 3`] = ` + + +
+

+ onboarding.project.header +

+
+
+

+ onboarding.project.header.description.3 +

+ + + +
+
+ + close + + + tutorials.find_tutorial_back_in_plus + +
+
+`; diff --git a/server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/__snapshots__/ProjectWatcher-test.js.snap b/server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/__tests__/__snapshots__/ProjectWatcher-test.tsx.snap similarity index 74% rename from server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/__snapshots__/ProjectWatcher-test.js.snap rename to server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/__tests__/__snapshots__/ProjectWatcher-test.tsx.snap index 4ecd80c2ae3..1cc4d9991f6 100644 --- a/server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/__snapshots__/ProjectWatcher-test.js.snap +++ b/server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/__tests__/__snapshots__/ProjectWatcher-test.tsx.snap @@ -2,7 +2,7 @@ exports[`renders 1`] = `
onboarding.project_watcher.not_started
@@ -10,7 +10,7 @@ exports[`renders 1`] = ` exports[`renders 2`] = `
- onboading.token.generate_token + onboarding.token.generate_token
- onboading.token.generate_token + onboarding.token.generate_token
- onboading.token.generate_token + onboarding.token.generate_token

@@ -50,7 +48,7 @@ export default function BuildWrapper(props /*: Props */) { {translate('download_verb')} diff --git a/server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/ClangGCC.js b/server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/commands/ClangGCC.tsx similarity index 91% rename from server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/ClangGCC.js rename to server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/commands/ClangGCC.tsx index 0c1fdab16cf..6ebe42a3ca9 100644 --- a/server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/ClangGCC.js +++ b/server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/commands/ClangGCC.tsx @@ -17,31 +17,28 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -// @flow -import React from 'react'; +import * as React from 'react'; import SQScanner from './SQScanner'; import BuildWrapper from './BuildWrapper'; import CodeSnippet from '../../../../components/common/CodeSnippet'; import InstanceMessage from '../../../../components/common/InstanceMessage'; import { translate } from '../../../../helpers/l10n'; -/*:: -type Props = { - host: string, - os: string, - organization?: string, - projectKey: string, - token: string -}; -*/ +interface Props { + host: string; + os: string; + organization?: string; + projectKey: string; + token: string; +} -const executables = { +const executables: { [key: string]: string } = { linux: 'build-wrapper-linux-x86-64', win: 'build-wrapper-win-x86-64.exe', mac: 'build-wrapper-macosx-x86' }; -export default function ClangGCC(props /*: Props */) { +export default function ClangGCC(props: Props) { const command1 = `${executables[props.os]} --out-dir bw-output make clean all`; const command2 = [ diff --git a/server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/DotNet.js b/server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/commands/DotNet.tsx similarity index 92% rename from server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/DotNet.js rename to server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/commands/DotNet.tsx index a5bd3fd4285..347ea01b7ac 100644 --- a/server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/DotNet.js +++ b/server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/commands/DotNet.tsx @@ -17,23 +17,20 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -// @flow -import React from 'react'; +import * as React from 'react'; import MSBuildScanner from './MSBuildScanner'; import CodeSnippet from '../../../../components/common/CodeSnippet'; import InstanceMessage from '../../../../components/common/InstanceMessage'; import { translate } from '../../../../helpers/l10n'; -/*:: -type Props = {| - host: string, - organization?: string, - projectKey: string, - token: string -|}; -*/ +interface Props { + host: string; + organization?: string; + projectKey: string; + token: string; +} -export default function DotNet(props /*: Props */) { +export default function DotNet(props: Props) { const command1 = [ 'SonarQube.Scanner.MSBuild.exe begin', `/k:"${props.projectKey}"`, diff --git a/server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/JavaGradle.js b/server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/commands/JavaGradle.tsx similarity index 92% rename from server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/JavaGradle.js rename to server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/commands/JavaGradle.tsx index d3cc1c847da..02da9fd1a2a 100644 --- a/server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/JavaGradle.js +++ b/server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/commands/JavaGradle.tsx @@ -17,21 +17,18 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -// @flow -import React from 'react'; +import * as React from 'react'; import CodeSnippet from '../../../../components/common/CodeSnippet'; import InstanceMessage from '../../../../components/common/InstanceMessage'; import { translate } from '../../../../helpers/l10n'; -/*:: -type Props = {| - host: string, - organization?: string, - token: string -|}; -*/ +interface Props { + host: string; + organization?: string; + token: string; +} -export default function JavaGradle(props /*: Props */) { +export default function JavaGradle(props: Props) { const config = 'plugins {\n id "org.sonarqube" version "2.6"\n}'; const command = [ diff --git a/server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/JavaMaven.js b/server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/commands/JavaMaven.tsx similarity index 91% rename from server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/JavaMaven.js rename to server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/commands/JavaMaven.tsx index 8a02fb75630..5663b1f2911 100644 --- a/server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/JavaMaven.js +++ b/server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/commands/JavaMaven.tsx @@ -17,21 +17,18 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -// @flow -import React from 'react'; +import * as React from 'react'; import CodeSnippet from '../../../../components/common/CodeSnippet'; import InstanceMessage from '../../../../components/common/InstanceMessage'; import { translate } from '../../../../helpers/l10n'; -/*:: -type Props = {| - host: string, - organization?: string, - token: string -|}; -*/ +interface Props { + host: string; + organization?: string; + token: string; +} -export default function JavaMaven(props /*: Props */) { +export default function JavaMaven(props: Props) { const command = [ 'mvn sonar:sonar', props.organization && `-Dsonar.organization=${props.organization}`, diff --git a/server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/MSBuildScanner.js b/server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/commands/MSBuildScanner.tsx similarity index 89% rename from server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/MSBuildScanner.js rename to server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/commands/MSBuildScanner.tsx index af0ade047c3..65c4005af86 100644 --- a/server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/MSBuildScanner.js +++ b/server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/commands/MSBuildScanner.tsx @@ -17,17 +17,15 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -// @flow -import React from 'react'; + +import * as React from 'react'; import { translate } from '../../../../helpers/l10n'; -/*:: -type Props = { - className?: string -}; -*/ +interface Props { + className?: string; +} -export default function MSBuildScanner(props /*: Props */) { +export default function MSBuildScanner(props: Props) { return (

{translate('onboarding.analysis.msbuild.header')}

@@ -39,6 +37,7 @@ export default function MSBuildScanner(props /*: Props */) { {translate('download_verb')} diff --git a/server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/Msvc.js b/server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/commands/Msvc.tsx similarity index 92% rename from server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/Msvc.js rename to server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/commands/Msvc.tsx index 03f5fe3eb63..cf6120a3b31 100644 --- a/server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/Msvc.js +++ b/server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/commands/Msvc.tsx @@ -17,24 +17,21 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -// @flow -import React from 'react'; +import * as React from 'react'; import MSBuildScanner from './MSBuildScanner'; import BuildWrapper from './BuildWrapper'; import CodeSnippet from '../../../../components/common/CodeSnippet'; import InstanceMessage from '../../../../components/common/InstanceMessage'; import { translate } from '../../../../helpers/l10n'; -/*:: -type Props = {| - host: string, - organization?: string, - projectKey: string, - token: string -|}; -*/ +interface Props { + host: string; + organization?: string; + projectKey: string; + token: string; +} -export default function Msvc(props /*: Props */) { +export default function Msvc(props: Props) { const command1 = [ 'SonarQube.Scanner.MSBuild.exe begin', `/k:"${props.projectKey}"`, diff --git a/server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/Other.js b/server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/commands/Other.tsx similarity index 91% rename from server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/Other.js rename to server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/commands/Other.tsx index 5bb0d0dd649..3d0b3b2a718 100644 --- a/server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/Other.js +++ b/server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/commands/Other.tsx @@ -17,24 +17,21 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -// @flow -import React from 'react'; +import * as React from 'react'; import SQScanner from './SQScanner'; import CodeSnippet from '../../../../components/common/CodeSnippet'; import InstanceMessage from '../../../../components/common/InstanceMessage'; import { translate } from '../../../../helpers/l10n'; -/*:: -type Props = {| - host: string, - organization?: string, - os: string, - projectKey: string, - token: string -|}; -*/ +interface Props { + host: string; + organization?: string; + os: string; + projectKey: string; + token: string; +} -export default function Other(props /*: Props */) { +export default function Other(props: Props) { const command = [ props.os === 'win' ? 'sonar-scanner.bat' : 'sonar-scanner', `-Dsonar.projectKey=${props.projectKey}`, diff --git a/server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/SQScanner.js b/server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/commands/SQScanner.tsx similarity index 89% rename from server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/SQScanner.js rename to server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/commands/SQScanner.tsx index 3fd3e3e9711..78b90f851bc 100644 --- a/server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/SQScanner.js +++ b/server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/commands/SQScanner.tsx @@ -17,18 +17,16 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -// @flow -import React from 'react'; + +import * as React from 'react'; import { translate } from '../../../../helpers/l10n'; -/*:: -type Props = { - className?: string, - os: string -}; -*/ +interface Props { + className?: string; + os: string; +} -export default function SQScanner(props /*: Props */) { +export default function SQScanner(props: Props) { return (

@@ -44,6 +42,7 @@ export default function SQScanner(props /*: Props */) { {translate('download_verb')} diff --git a/server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/__tests__/BuildWrapper-test.js b/server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/commands/__tests__/BuildWrapper-test.tsx similarity index 96% rename from server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/__tests__/BuildWrapper-test.js rename to server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/commands/__tests__/BuildWrapper-test.tsx index 4d3f979b3c8..b19e334793b 100644 --- a/server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/__tests__/BuildWrapper-test.js +++ b/server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/commands/__tests__/BuildWrapper-test.tsx @@ -17,8 +17,7 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -// @flow -import React from 'react'; +import * as React from 'react'; import { shallow } from 'enzyme'; import BuildWrapper from '../BuildWrapper'; diff --git a/server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/__tests__/ClangGCC-test.js b/server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/commands/__tests__/ClangGCC-test.tsx similarity index 97% rename from server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/__tests__/ClangGCC-test.js rename to server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/commands/__tests__/ClangGCC-test.tsx index 9f8f9243da5..a6f7c4f550a 100644 --- a/server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/__tests__/ClangGCC-test.js +++ b/server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/commands/__tests__/ClangGCC-test.tsx @@ -17,8 +17,8 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -// @flow -import React from 'react'; + +import * as React from 'react'; import { shallow } from 'enzyme'; import ClangGCC from '../ClangGCC'; @@ -35,8 +35,8 @@ it('renders correctly', () => { shallow( diff --git a/server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/__tests__/DotNet-test.js b/server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/commands/__tests__/DotNet-test.tsx similarity index 97% rename from server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/__tests__/DotNet-test.js rename to server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/commands/__tests__/DotNet-test.tsx index b9dc347956a..91ecab828b2 100644 --- a/server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/__tests__/DotNet-test.js +++ b/server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/commands/__tests__/DotNet-test.tsx @@ -17,8 +17,8 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -// @flow -import React from 'react'; + +import * as React from 'react'; import { shallow } from 'enzyme'; import DotNet from '../DotNet'; diff --git a/server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/__tests__/JavaGradle-test.js b/server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/commands/__tests__/JavaGradle-test.tsx similarity index 96% rename from server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/__tests__/JavaGradle-test.js rename to server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/commands/__tests__/JavaGradle-test.tsx index bfddcc9c728..f0d130a248d 100644 --- a/server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/__tests__/JavaGradle-test.js +++ b/server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/commands/__tests__/JavaGradle-test.tsx @@ -17,8 +17,8 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -// @flow -import React from 'react'; + +import * as React from 'react'; import { shallow } from 'enzyme'; import JavaGradle from '../JavaGradle'; diff --git a/server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/__tests__/JavaMaven-test.js b/server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/commands/__tests__/JavaMaven-test.tsx similarity index 96% rename from server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/__tests__/JavaMaven-test.js rename to server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/commands/__tests__/JavaMaven-test.tsx index 592e578b3bc..dbda16ca13e 100644 --- a/server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/__tests__/JavaMaven-test.js +++ b/server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/commands/__tests__/JavaMaven-test.tsx @@ -17,8 +17,8 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -// @flow -import React from 'react'; + +import * as React from 'react'; import { shallow } from 'enzyme'; import JavaMaven from '../JavaMaven'; diff --git a/server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/__tests__/MSBuildScanner-test.js b/server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/commands/__tests__/MSBuildScanner-test.tsx similarity index 96% rename from server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/__tests__/MSBuildScanner-test.js rename to server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/commands/__tests__/MSBuildScanner-test.tsx index 5551fcead5c..e5411341d10 100644 --- a/server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/__tests__/MSBuildScanner-test.js +++ b/server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/commands/__tests__/MSBuildScanner-test.tsx @@ -17,8 +17,8 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -// @flow -import React from 'react'; + +import * as React from 'react'; import { shallow } from 'enzyme'; import MSBuildScanner from '../MSBuildScanner'; diff --git a/server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/__tests__/Msvc-test.js b/server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/commands/__tests__/Msvc-test.tsx similarity index 97% rename from server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/__tests__/Msvc-test.js rename to server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/commands/__tests__/Msvc-test.tsx index 62ccf801fb9..78470bbd2e6 100644 --- a/server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/__tests__/Msvc-test.js +++ b/server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/commands/__tests__/Msvc-test.tsx @@ -17,8 +17,8 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -// @flow -import React from 'react'; + +import * as React from 'react'; import { shallow } from 'enzyme'; import Msvc from '../Msvc'; diff --git a/server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/__tests__/Other-test.js b/server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/commands/__tests__/Other-test.tsx similarity index 97% rename from server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/__tests__/Other-test.js rename to server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/commands/__tests__/Other-test.tsx index b7175078c96..16cc44ae69f 100644 --- a/server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/__tests__/Other-test.js +++ b/server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/commands/__tests__/Other-test.tsx @@ -17,8 +17,8 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -// @flow -import React from 'react'; + +import * as React from 'react'; import { shallow } from 'enzyme'; import Other from '../Other'; @@ -35,8 +35,8 @@ it('renders correctly', () => { shallow( diff --git a/server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/__tests__/SQScanner-test.js b/server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/commands/__tests__/SQScanner-test.tsx similarity index 96% rename from server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/__tests__/SQScanner-test.js rename to server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/commands/__tests__/SQScanner-test.tsx index 11a0ccb58ad..2be4e546bb3 100644 --- a/server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/__tests__/SQScanner-test.js +++ b/server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/commands/__tests__/SQScanner-test.tsx @@ -17,8 +17,8 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -// @flow -import React from 'react'; + +import * as React from 'react'; import { shallow } from 'enzyme'; import SQScanner from '../SQScanner'; diff --git a/server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/__tests__/__snapshots__/BuildWrapper-test.js.snap b/server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/commands/__tests__/__snapshots__/BuildWrapper-test.tsx.snap similarity index 100% rename from server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/__tests__/__snapshots__/BuildWrapper-test.js.snap rename to server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/commands/__tests__/__snapshots__/BuildWrapper-test.tsx.snap diff --git a/server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/__tests__/__snapshots__/ClangGCC-test.js.snap b/server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/commands/__tests__/__snapshots__/ClangGCC-test.tsx.snap similarity index 100% rename from server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/__tests__/__snapshots__/ClangGCC-test.js.snap rename to server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/commands/__tests__/__snapshots__/ClangGCC-test.tsx.snap diff --git a/server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/__tests__/__snapshots__/DotNet-test.js.snap b/server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/commands/__tests__/__snapshots__/DotNet-test.tsx.snap similarity index 100% rename from server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/__tests__/__snapshots__/DotNet-test.js.snap rename to server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/commands/__tests__/__snapshots__/DotNet-test.tsx.snap diff --git a/server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/__tests__/__snapshots__/JavaGradle-test.js.snap b/server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/commands/__tests__/__snapshots__/JavaGradle-test.tsx.snap similarity index 100% rename from server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/__tests__/__snapshots__/JavaGradle-test.js.snap rename to server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/commands/__tests__/__snapshots__/JavaGradle-test.tsx.snap diff --git a/server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/__tests__/__snapshots__/JavaMaven-test.js.snap b/server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/commands/__tests__/__snapshots__/JavaMaven-test.tsx.snap similarity index 100% rename from server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/__tests__/__snapshots__/JavaMaven-test.js.snap rename to server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/commands/__tests__/__snapshots__/JavaMaven-test.tsx.snap diff --git a/server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/__tests__/__snapshots__/MSBuildScanner-test.js.snap b/server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/commands/__tests__/__snapshots__/MSBuildScanner-test.tsx.snap similarity index 94% rename from server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/__tests__/__snapshots__/MSBuildScanner-test.js.snap rename to server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/commands/__tests__/__snapshots__/MSBuildScanner-test.tsx.snap index c7f156bc53f..740e26ed12b 100644 --- a/server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/__tests__/__snapshots__/MSBuildScanner-test.js.snap +++ b/server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/commands/__tests__/__snapshots__/MSBuildScanner-test.tsx.snap @@ -19,6 +19,7 @@ exports[`renders correctly 1`] = ` download_verb diff --git a/server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/__tests__/__snapshots__/Msvc-test.js.snap b/server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/commands/__tests__/__snapshots__/Msvc-test.tsx.snap similarity index 100% rename from server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/__tests__/__snapshots__/Msvc-test.js.snap rename to server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/commands/__tests__/__snapshots__/Msvc-test.tsx.snap diff --git a/server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/__tests__/__snapshots__/Other-test.js.snap b/server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/commands/__tests__/__snapshots__/Other-test.tsx.snap similarity index 100% rename from server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/__tests__/__snapshots__/Other-test.js.snap rename to server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/commands/__tests__/__snapshots__/Other-test.tsx.snap diff --git a/server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/__tests__/__snapshots__/SQScanner-test.js.snap b/server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/commands/__tests__/__snapshots__/SQScanner-test.tsx.snap similarity index 94% rename from server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/__tests__/__snapshots__/SQScanner-test.js.snap rename to server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/commands/__tests__/__snapshots__/SQScanner-test.tsx.snap index bcdac075ce4..cf6a8de4ce3 100644 --- a/server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/__tests__/__snapshots__/SQScanner-test.js.snap +++ b/server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/commands/__tests__/__snapshots__/SQScanner-test.tsx.snap @@ -19,6 +19,7 @@ exports[`renders correctly 1`] = ` download_verb @@ -46,6 +47,7 @@ exports[`renders correctly 2`] = ` download_verb @@ -73,6 +75,7 @@ exports[`renders correctly 3`] = ` download_verb diff --git a/server/sonar-web/src/main/js/apps/tutorials/onboarding/styles.css b/server/sonar-web/src/main/js/apps/tutorials/styles.css similarity index 88% rename from server/sonar-web/src/main/js/apps/tutorials/onboarding/styles.css rename to server/sonar-web/src/main/js/apps/tutorials/styles.css index 9c8dfc093fc..f73428e0f40 100644 --- a/server/sonar-web/src/main/js/apps/tutorials/onboarding/styles.css +++ b/server/sonar-web/src/main/js/apps/tutorials/styles.css @@ -17,9 +17,6 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -.onboarding { - min-height: calc(70vh - 60px); -} .onboarding-step { position: relative; @@ -58,12 +55,8 @@ outline: none; } -.onboarding .page-actions { - text-align: right; - margin-bottom: 0; -} - -.onboarding .page-actions p { - line-height: 16px; - margin-top: 6px; +.onboarding-choices { + display: flex; + justify-content: space-around; + padding: 24px 0 44px; } diff --git a/server/sonar-web/src/main/js/apps/tutorials/teamOnboarding/TeamOnboardingModal.tsx b/server/sonar-web/src/main/js/apps/tutorials/teamOnboarding/TeamOnboardingModal.tsx new file mode 100644 index 00000000000..0763e82b9ab --- /dev/null +++ b/server/sonar-web/src/main/js/apps/tutorials/teamOnboarding/TeamOnboardingModal.tsx @@ -0,0 +1,68 @@ +/* + * SonarQube + * Copyright (C) 2009-2018 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import * as React from 'react'; +import { FormattedMessage } from 'react-intl'; +import { Link } from 'react-router'; +import Modal from '../../../components/controls/Modal'; +import { translate } from '../../../helpers/l10n'; +import { ResetButtonLink } from '../../../components/ui/buttons'; + +interface Props { + onFinish: () => void; +} + +export default class TeamOnboardingModal extends React.PureComponent { + render() { + const header = translate('onboarding.team.header'); + return ( + +
+

{header}

+
+
+
+ {translate('onboarding.team.work_in_progress')} +
+

{translate('onboarding.team.first_step')}

+

+ + {translate('as_explained_here')} + + ) + }} + /> +

+
+
+ {translate('close')} +
+
+ ); + } +} diff --git a/server/sonar-web/src/main/js/apps/tutorials/onboarding/OnboardingModal.d.ts b/server/sonar-web/src/main/js/apps/tutorials/teamOnboarding/__tests__/TeamOnboardingModal-test.tsx similarity index 80% rename from server/sonar-web/src/main/js/apps/tutorials/onboarding/OnboardingModal.d.ts rename to server/sonar-web/src/main/js/apps/tutorials/teamOnboarding/__tests__/TeamOnboardingModal-test.tsx index 4b70adcc600..0bd903f80f2 100644 --- a/server/sonar-web/src/main/js/apps/tutorials/onboarding/OnboardingModal.d.ts +++ b/server/sonar-web/src/main/js/apps/tutorials/teamOnboarding/__tests__/TeamOnboardingModal-test.tsx @@ -18,9 +18,9 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import * as React from 'react'; +import { shallow } from 'enzyme'; +import TeamOnboardingModal from '../TeamOnboardingModal'; -export interface Props { - onFinish: () => void; -} - -export default class OnboardingModal extends React.PureComponent {} +it('renders correctly', () => { + expect(shallow()).toMatchSnapshot(); +}); diff --git a/server/sonar-web/src/main/js/apps/tutorials/teamOnboarding/__tests__/__snapshots__/TeamOnboardingModal-test.tsx.snap b/server/sonar-web/src/main/js/apps/tutorials/teamOnboarding/__tests__/__snapshots__/TeamOnboardingModal-test.tsx.snap new file mode 100644 index 00000000000..a510b0e0eb9 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/tutorials/teamOnboarding/__tests__/__snapshots__/TeamOnboardingModal-test.tsx.snap @@ -0,0 +1,61 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`renders correctly 1`] = ` + +
+

+ onboarding.team.header +

+
+
+
+ onboarding.team.work_in_progress +
+

+ onboarding.team.first_step +

+

+ + as_explained_here + , + } + } + /> +

+
+ +
+`; diff --git a/server/sonar-web/src/main/js/helpers/testUtils.ts b/server/sonar-web/src/main/js/helpers/testUtils.ts index d650182b932..f3f96a9506a 100644 --- a/server/sonar-web/src/main/js/helpers/testUtils.ts +++ b/server/sonar-web/src/main/js/helpers/testUtils.ts @@ -44,13 +44,13 @@ export function clickOutside(event = {}): void { window.dispatchEvent(dispatchedEvent); } -export function submit(element: ShallowWrapper): void { +export function submit(element: ShallowWrapper | ReactWrapper): void { element.simulate('submit', { preventDefault() {} }); } -export function change(element: ShallowWrapper, value: string, event = {}): void { +export function change(element: ShallowWrapper | ReactWrapper, value: string, event = {}): void { element.simulate('change', { target: { value }, currentTarget: { value }, diff --git a/sonar-core/src/main/resources/org/sonar/l10n/core.properties b/sonar-core/src/main/resources/org/sonar/l10n/core.properties index ac5f5a477ce..0027ae95882 100644 --- a/sonar-core/src/main/resources/org/sonar/l10n/core.properties +++ b/sonar-core/src/main/resources/org/sonar/l10n/core.properties @@ -202,6 +202,7 @@ no=No and_worse=and worse are_you_sure=Are you sure? +as_explained_here=as explained here assigned_to=Assigned to bulk_change=Bulk Change bulleted_point=Bulleted point @@ -921,8 +922,8 @@ shortcuts.section.rules.deactivate=deactivate selected rule tutorials.onboarding=Analyze a new project tutorials.skip=Skip this tutorial tutorials.finish=Finish this tutorial -tutorials.find_it_back_in_help=Find it back anytime in the Help section -tutorials.find_it_back_in_plus=Find it back anytime in the "+" menu +tutorials.find_tutorial_back_in_help=Find this tutorial back anytime in the Help section +tutorials.find_tutorial_back_in_plus=Find this tutorial back anytime in the "+" menu #------------------------------------------------------------------------------ @@ -2589,15 +2590,30 @@ footer.web_api=Web API # ONBOARDING # #------------------------------------------------------------------------------ -onboarding.header=Welcome to {instance}! -onboarding.header.description=Want to quickly analyze a first project? Follow these {0} easy steps. +onboarding.header=Welcome to SonarCloud! +onboarding.header.description=Let us help you get started. What do you want to do? + +onboarding.project.header=Analyze a project +onboarding.project.header.description=Want to quickly analyze a first project? Follow these {0} easy steps. + +onboarding.team.header=Join a team +onboarding.team.first_step=Well congrats, the first step is done! +onboarding.team.how_to_join=To join a team, the only thing you need to do is to be a user registered on Sonarcloud. The administrator of the Sonarcloud organization you wish to join has to add you to his organization's members {link}. Ask him to do so! +onboarding.team.work_in_progress=We are currently working on a better way to join a team or invite people to yours. + +onboarding.analyze_public_code=I want to analyze public code +onboarding.analyze_public_code.button=Analyze a project +onboarding.analyze_private_code=I want to analyze private code +onboarding.analyze_private_code.button=Setup a new organization +onboarding.contribute_existing_project=I want to contribute to an existing project +onboarding.contribute_existing_project.button=Join a team onboarding.token.header=Provide a token onboarding.token.text=The token is used to identify you when an analysis is performed. If it has been compromised, you can revoke it at any point of time in your user account. onboarding.token.generate=Generate onboarding.token.placeholder=Enter a name for your token -onboading.token.generate_token=Generate a token -onboading.token.generate_token.placeholder=Enter a name for your token +onboarding.token.generate_token=Generate a token +onboarding.token.generate_token.placeholder=Enter a name for your token onboarding.token.use_existing_token=Use existing token onboarding.token.use_existing_token.placeholder=Enter your existing token onboarding.token.invalid_format=The token you have entered has invalid format. -- 2.39.5