diff options
author | Grégoire Aubert <gregoire.aubert@sonarsource.com> | 2018-06-15 15:04:21 +0200 |
---|---|---|
committer | SonarTech <sonartech@sonarsource.com> | 2018-06-21 20:21:29 +0200 |
commit | 4e91bd432aeef4a2e7ff680910907e62007ac801 (patch) | |
tree | c1e8ff11fefd379dcd57ae152c766e99e6ec3e98 /server/sonar-web/src/main/js/apps | |
parent | 7a8b69cb8e9e6cd0d47b5dc3f5dc4ab9e2368661 (diff) | |
download | sonarqube-4e91bd432aeef4a2e7ff680910907e62007ac801.tar.gz sonarqube-4e91bd432aeef4a2e7ff680910907e62007ac801.zip |
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
Diffstat (limited to 'server/sonar-web/src/main/js/apps')
-rw-r--r-- | server/sonar-web/src/main/js/apps/projects/components/NoFavoriteProjects.tsx | 4 | ||||
-rw-r--r-- | server/sonar-web/src/main/js/apps/tutorials/Onboarding.tsx | 102 | ||||
-rw-r--r-- | server/sonar-web/src/main/js/apps/tutorials/__tests__/Onboarding-test.tsx | 61 | ||||
-rw-r--r-- | server/sonar-web/src/main/js/apps/tutorials/__tests__/__snapshots__/Onboarding-test.tsx.snap | 82 | ||||
-rw-r--r-- | server/sonar-web/src/main/js/apps/tutorials/onboarding/Onboarding.js | 236 | ||||
-rw-r--r-- | server/sonar-web/src/main/js/apps/tutorials/onboarding/OnboardingContainer.js | 33 | ||||
-rw-r--r-- | server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/__snapshots__/Onboarding-test.js.snap | 388 | ||||
-rw-r--r-- | server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/AnalysisStep.tsx (renamed from server/sonar-web/src/main/js/apps/tutorials/onboarding/AnalysisStep.js) | 149 | ||||
-rw-r--r-- | server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/LanguageStep.tsx (renamed from server/sonar-web/src/main/js/apps/tutorials/onboarding/LanguageStep.js) | 51 | ||||
-rw-r--r-- | server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/NewOrganizationForm.tsx (renamed from server/sonar-web/src/main/js/apps/tutorials/onboarding/NewOrganizationForm.js) | 45 | ||||
-rw-r--r-- | server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/NewProjectForm.tsx (renamed from server/sonar-web/src/main/js/apps/tutorials/onboarding/NewProjectForm.js) | 49 | ||||
-rw-r--r-- | server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/OrganizationStep.tsx (renamed from server/sonar-web/src/main/js/apps/tutorials/onboarding/OrganizationStep.js) | 58 | ||||
-rw-r--r-- | server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/ProjectOnboarding.tsx | 206 | ||||
-rw-r--r-- | server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/ProjectOnboardingModal.tsx (renamed from server/sonar-web/src/main/js/apps/tutorials/onboarding/OnboardingModal.tsx) | 6 | ||||
-rw-r--r-- | server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/ProjectOnboardingPage.tsx (renamed from server/sonar-web/src/main/js/apps/tutorials/onboarding/OnboardingPage.tsx) | 8 | ||||
-rw-r--r-- | server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/ProjectWatcher.tsx (renamed from server/sonar-web/src/main/js/apps/tutorials/onboarding/ProjectWatcher.js) | 58 | ||||
-rw-r--r-- | server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/Step.tsx (renamed from server/sonar-web/src/main/js/apps/tutorials/onboarding/Step.js) | 30 | ||||
-rw-r--r-- | server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/TokenStep.tsx (renamed from server/sonar-web/src/main/js/apps/tutorials/onboarding/TokenStep.js) | 102 | ||||
-rw-r--r-- | server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/__tests__/LanguageStep-test.tsx (renamed from server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/LanguageStep-test.js) | 47 | ||||
-rw-r--r-- | server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/__tests__/NewOrganizationForm-test.tsx (renamed from server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/NewOrganizationForm-test.js) | 5 | ||||
-rw-r--r-- | server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/__tests__/NewProjectForm-test.tsx (renamed from server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/NewProjectForm-test.js) | 5 | ||||
-rw-r--r-- | server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/__tests__/OrganizationStep-test.tsx (renamed from server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/OrganizationStep-test.js) | 11 | ||||
-rw-r--r-- | server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/__tests__/ProjectOnboarding-test.tsx (renamed from server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/Onboarding-test.js) | 35 | ||||
-rw-r--r-- | server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/__tests__/ProjectWatcher-test.tsx (renamed from server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/ProjectWatcher-test.js) | 3 | ||||
-rw-r--r-- | server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/__tests__/Step-test.tsx (renamed from server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/Step-test.js) | 3 | ||||
-rw-r--r-- | server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/__tests__/TokenStep-test.tsx (renamed from server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/TokenStep-test.js) | 5 | ||||
-rw-r--r-- | server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/__tests__/__snapshots__/LanguageStep-test.tsx.snap (renamed from server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/__snapshots__/LanguageStep-test.js.snap) | 0 | ||||
-rw-r--r-- | server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/__tests__/__snapshots__/NewOrganizationForm-test.tsx.snap (renamed from server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/__snapshots__/NewOrganizationForm-test.js.snap) | 0 | ||||
-rw-r--r-- | server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/__tests__/__snapshots__/NewProjectForm-test.tsx.snap (renamed from server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/__snapshots__/NewProjectForm-test.js.snap) | 0 | ||||
-rw-r--r-- | server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/__tests__/__snapshots__/OrganizationStep-test.tsx.snap (renamed from server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/__snapshots__/OrganizationStep-test.js.snap) | 0 | ||||
-rw-r--r-- | server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/__tests__/__snapshots__/ProjectOnboarding-test.tsx.snap | 349 | ||||
-rw-r--r-- | server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/__tests__/__snapshots__/ProjectWatcher-test.tsx.snap (renamed from server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/__snapshots__/ProjectWatcher-test.js.snap) | 8 | ||||
-rw-r--r-- | server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/__tests__/__snapshots__/Step-test.tsx.snap (renamed from server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/__snapshots__/Step-test.js.snap) | 0 | ||||
-rw-r--r-- | server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/__tests__/__snapshots__/TokenStep-test.tsx.snap (renamed from server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/__snapshots__/TokenStep-test.js.snap) | 12 | ||||
-rw-r--r-- | server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/commands/BuildWrapper.tsx (renamed from server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/BuildWrapper.js) | 20 | ||||
-rw-r--r-- | server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/commands/ClangGCC.tsx (renamed from server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/ClangGCC.js) | 23 | ||||
-rw-r--r-- | server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/commands/DotNet.tsx (renamed from server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/DotNet.js) | 19 | ||||
-rw-r--r-- | server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/commands/JavaGradle.tsx (renamed from server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/JavaGradle.js) | 17 | ||||
-rw-r--r-- | server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/commands/JavaMaven.tsx (renamed from server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/JavaMaven.js) | 17 | ||||
-rw-r--r-- | server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/commands/MSBuildScanner.tsx (renamed from server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/MSBuildScanner.js) | 15 | ||||
-rw-r--r-- | server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/commands/Msvc.tsx (renamed from server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/Msvc.js) | 19 | ||||
-rw-r--r-- | server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/commands/Other.tsx (renamed from server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/Other.js) | 21 | ||||
-rw-r--r-- | server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/commands/SQScanner.tsx (renamed from server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/SQScanner.js) | 17 | ||||
-rw-r--r-- | server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/commands/__tests__/BuildWrapper-test.tsx (renamed from server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/__tests__/BuildWrapper-test.js) | 3 | ||||
-rw-r--r-- | server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/commands/__tests__/ClangGCC-test.tsx (renamed from server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/__tests__/ClangGCC-test.js) | 6 | ||||
-rw-r--r-- | server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/commands/__tests__/DotNet-test.tsx (renamed from server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/__tests__/DotNet-test.js) | 4 | ||||
-rw-r--r-- | server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/commands/__tests__/JavaGradle-test.tsx (renamed from server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/__tests__/JavaGradle-test.js) | 4 | ||||
-rw-r--r-- | server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/commands/__tests__/JavaMaven-test.tsx (renamed from server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/__tests__/JavaMaven-test.js) | 4 | ||||
-rw-r--r-- | server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/commands/__tests__/MSBuildScanner-test.tsx (renamed from server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/__tests__/MSBuildScanner-test.js) | 4 | ||||
-rw-r--r-- | server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/commands/__tests__/Msvc-test.tsx (renamed from server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/__tests__/Msvc-test.js) | 4 | ||||
-rw-r--r-- | server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/commands/__tests__/Other-test.tsx (renamed from server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/__tests__/Other-test.js) | 6 | ||||
-rw-r--r-- | server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/commands/__tests__/SQScanner-test.tsx (renamed from server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/__tests__/SQScanner-test.js) | 4 | ||||
-rw-r--r-- | server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/commands/__tests__/__snapshots__/BuildWrapper-test.tsx.snap (renamed from server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/__tests__/__snapshots__/BuildWrapper-test.js.snap) | 0 | ||||
-rw-r--r-- | server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/commands/__tests__/__snapshots__/ClangGCC-test.tsx.snap (renamed from server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/__tests__/__snapshots__/ClangGCC-test.js.snap) | 0 | ||||
-rw-r--r-- | server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/commands/__tests__/__snapshots__/DotNet-test.tsx.snap (renamed from server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/__tests__/__snapshots__/DotNet-test.js.snap) | 0 | ||||
-rw-r--r-- | server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/commands/__tests__/__snapshots__/JavaGradle-test.tsx.snap (renamed from server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/__tests__/__snapshots__/JavaGradle-test.js.snap) | 0 | ||||
-rw-r--r-- | server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/commands/__tests__/__snapshots__/JavaMaven-test.tsx.snap (renamed from server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/__tests__/__snapshots__/JavaMaven-test.js.snap) | 0 | ||||
-rw-r--r-- | server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/commands/__tests__/__snapshots__/MSBuildScanner-test.tsx.snap (renamed from server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/__tests__/__snapshots__/MSBuildScanner-test.js.snap) | 1 | ||||
-rw-r--r-- | server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/commands/__tests__/__snapshots__/Msvc-test.tsx.snap (renamed from server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/__tests__/__snapshots__/Msvc-test.js.snap) | 0 | ||||
-rw-r--r-- | server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/commands/__tests__/__snapshots__/Other-test.tsx.snap (renamed from server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/__tests__/__snapshots__/Other-test.js.snap) | 0 | ||||
-rw-r--r-- | server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/commands/__tests__/__snapshots__/SQScanner-test.tsx.snap (renamed from server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/__tests__/__snapshots__/SQScanner-test.js.snap) | 3 | ||||
-rw-r--r-- | server/sonar-web/src/main/js/apps/tutorials/styles.css (renamed from server/sonar-web/src/main/js/apps/tutorials/onboarding/styles.css) | 15 | ||||
-rw-r--r-- | server/sonar-web/src/main/js/apps/tutorials/teamOnboarding/TeamOnboardingModal.tsx | 68 | ||||
-rw-r--r-- | server/sonar-web/src/main/js/apps/tutorials/teamOnboarding/__tests__/TeamOnboardingModal-test.tsx (renamed from server/sonar-web/src/main/js/apps/tutorials/onboarding/OnboardingModal.d.ts) | 10 | ||||
-rw-r--r-- | server/sonar-web/src/main/js/apps/tutorials/teamOnboarding/__tests__/__snapshots__/TeamOnboardingModal-test.tsx.snap | 61 |
65 files changed, 1357 insertions, 1159 deletions
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<StateProps> { static contextTypes = { - openOnboardingTutorial: PropTypes.func + openProjectOnboarding: PropTypes.func }; onAnalyzeProjectClick = (event: React.SyntheticEvent<HTMLAnchorElement>) => { 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<Props> { + componentDidMount() { + if (!isLoggedIn(this.props.currentUser)) { + handleRequiredAuthentication(); + } + } + + render() { + if (!isLoggedIn(this.props.currentUser)) { + return null; + } + + const header = translate('onboarding.header'); + return ( + <Modal + contentLabel={header} + medium={true} + onRequestClose={this.props.onFinish} + shouldCloseOnOverlayClick={false}> + <header className="modal-head"> + <h2>{header}</h2> + </header> + <div className="modal-body"> + <p className="spacer-top big-spacer-bottom"> + {translate('onboarding.header.description')} + </p> + <ul className="onboarding-choices"> + <li className="text-center"> + <p className="big-spacer-bottom">{translate('onboarding.analyze_public_code')}</p> + <Button onClick={this.props.onOpenProjectOnboarding}> + {translate('onboarding.analyze_public_code.button')} + </Button> + </li> + <li className="text-center"> + <p className="big-spacer-bottom">{translate('onboarding.analyze_private_code')}</p> + <Button onClick={this.props.onOpenOrganizationOnboarding}> + {translate('onboarding.analyze_private_code.button')} + </Button> + </li> + <li className="text-center"> + <p className="big-spacer-bottom"> + {translate('onboarding.contribute_existing_project')} + </p> + <Button onClick={this.props.onOpenTeamOnboarding}> + {translate('onboarding.contribute_existing_project.button')} + </Button> + </li> + </ul> + </div> + <footer className="modal-foot"> + <ResetButtonLink onClick={this.props.onFinish}>{translate('close')}</ResetButtonLink> + </footer> + </Modal> + ); + } +} + +const mapStateToProps = (state: any): StateProps => ({ currentUser: getCurrentUser(state) }); + +export default connect<StateProps, {}, OwnProps>(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( + <Onboarding + currentUser={{ isLoggedIn: true }} + onFinish={jest.fn()} + onOpenOrganizationOnboarding={jest.fn()} + onOpenProjectOnboarding={jest.fn()} + onOpenTeamOnboarding={jest.fn()} + /> + ) + ).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( + <Onboarding + currentUser={{ isLoggedIn: true }} + onFinish={onFinish} + onOpenOrganizationOnboarding={onOpenOrganizationOnboarding} + onOpenProjectOnboarding={onOpenProjectOnboarding} + onOpenTeamOnboarding={onOpenTeamOnboarding} + /> + ); + + 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`] = ` +<Modal + contentLabel="onboarding.header" + medium={true} + onRequestClose={[MockFunction]} + shouldCloseOnOverlayClick={false} +> + <header + className="modal-head" + > + <h2> + onboarding.header + </h2> + </header> + <div + className="modal-body" + > + <p + className="spacer-top big-spacer-bottom" + > + onboarding.header.description + </p> + <ul + className="onboarding-choices" + > + <li + className="text-center" + > + <p + className="big-spacer-bottom" + > + onboarding.analyze_public_code + </p> + <Button + onClick={[MockFunction]} + > + onboarding.analyze_public_code.button + </Button> + </li> + <li + className="text-center" + > + <p + className="big-spacer-bottom" + > + onboarding.analyze_private_code + </p> + <Button + onClick={[MockFunction]} + > + onboarding.analyze_private_code.button + </Button> + </li> + <li + className="text-center" + > + <p + className="big-spacer-bottom" + > + onboarding.contribute_existing_project + </p> + <Button + onClick={[MockFunction]} + > + onboarding.contribute_existing_project.button + </Button> + </li> + </ul> + </div> + <footer + className="modal-foot" + > + <ResetButtonLink + onClick={[MockFunction]} + > + close + </ResetButtonLink> + </footer> +</Modal> +`; 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 ( - <div className={this.props.className}> - <InstanceMessage message={translate('onboarding.header')}> - {transformedMessage => <Helmet title={transformedMessage} titleTemplate="%s" />} - </InstanceMessage> - - <div className="page page-limited onboarding"> - <header className="page-header"> - <h1 className="page-title"> - <InstanceMessage message={translate('onboarding.header')} /> - </h1> - <div className="page-actions"> - <DeferredSpinner loading={this.state.skipping}> - <a className="js-skip text-muted" href="#" onClick={this.handleSkipClick}> - {automatic ? translate('tutorials.skip') : translate('close')} - </a> - </DeferredSpinner> - - <p className="note"> - {translate( - isSonarCloud() - ? 'tutorials.find_it_back_in_plus' - : 'tutorials.find_it_back_in_help' - )} - </p> - </div> - <div className="page-description"> - {translateWithParameters( - 'onboarding.header.description', - organizationsEnabled ? 3 : 2 - )} - </div> - </header> - - {organizationsEnabled && ( - <OrganizationStep - currentUser={this.props.currentUser} - finished={this.state.organization != null} - onContinue={this.handleOrganizationDone} - onOpen={this.handleOrganizationOpen} - open={step === 'organization'} - stepNumber={stepNumber++} - /> - )} - - <TokenStep - currentUser={this.props.currentUser} - finished={this.state.token != null} - onContinue={this.handleTokenDone} - onOpen={this.handleTokenOpen} - open={step === 'token'} - stepNumber={stepNumber++} - /> - - <AnalysisStep - onFinish={this.handleFinish} - onReset={this.handleReset} - open={step === 'analysis'} - organization={this.state.organization} - stepNumber={stepNumber} - token={token} - /> - - {this.state.finished && - !this.state.skipping && - (this.state.projectKey ? ( - <ProjectWatcher - onFinish={this.finishOnboarding} - onTimeout={this.handleTimeout} - projectKey={this.state.projectKey} - /> - ) : ( - <footer className="text-right"> - <a className="button" href="#" onClick={this.handleSkipClick}> - {translate('tutorials.finish')} - </a> - </footer> - ))} - </div> - </div> - ); - } -} 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`] = ` -<div - className="modal-container" -> - <InstanceMessage - message="onboarding.header" - /> - <div - className="page page-limited onboarding" - > - <header - className="page-header" - > - <h1 - className="page-title" - > - <InstanceMessage - message="onboarding.header" - /> - </h1> - <div - className="page-actions" - > - <DeferredSpinner - loading={false} - timeout={100} - > - <a - className="js-skip text-muted" - href="#" - onClick={[Function]} - > - close - </a> - </DeferredSpinner> - <p - className="note" - > - tutorials.find_it_back_in_help - </p> - </div> - <div - className="page-description" - > - onboarding.header.description.2 - </div> - </header> - <TokenStep - currentUser={ - Object { - "isLoggedIn": true, - "login": "admin", - } - } - finished={false} - onContinue={[Function]} - onOpen={[Function]} - open={true} - stepNumber={1} - /> - <AnalysisStep - onFinish={[Function]} - onReset={[Function]} - open={false} - stepNumber={2} - /> - </div> -</div> -`; - -exports[`guides for on-premise 2`] = ` -<div - className="modal-container" -> - <InstanceMessage - message="onboarding.header" - /> - <div - className="page page-limited onboarding" - > - <header - className="page-header" - > - <h1 - className="page-title" - > - <InstanceMessage - message="onboarding.header" - /> - </h1> - <div - className="page-actions" - > - <DeferredSpinner - loading={false} - timeout={100} - > - <a - className="js-skip text-muted" - href="#" - onClick={[Function]} - > - close - </a> - </DeferredSpinner> - <p - className="note" - > - tutorials.find_it_back_in_help - </p> - </div> - <div - className="page-description" - > - onboarding.header.description.2 - </div> - </header> - <TokenStep - currentUser={ - Object { - "isLoggedIn": true, - "login": "admin", - } - } - finished={true} - onContinue={[Function]} - onOpen={[Function]} - open={false} - stepNumber={1} - /> - <AnalysisStep - onFinish={[Function]} - onReset={[Function]} - open={true} - stepNumber={2} - token="abcd1234" - /> - </div> -</div> -`; - -exports[`guides for sonarcloud 1`] = ` -<div> - <InstanceMessage - message="onboarding.header" - /> - <div - className="page page-limited onboarding" - > - <header - className="page-header" - > - <h1 - className="page-title" - > - <InstanceMessage - message="onboarding.header" - /> - </h1> - <div - className="page-actions" - > - <DeferredSpinner - loading={false} - timeout={100} - > - <a - className="js-skip text-muted" - href="#" - onClick={[Function]} - > - close - </a> - </DeferredSpinner> - <p - className="note" - > - tutorials.find_it_back_in_plus - </p> - </div> - <div - className="page-description" - > - onboarding.header.description.3 - </div> - </header> - <OrganizationStep - currentUser={ - Object { - "isLoggedIn": true, - "login": "admin", - } - } - finished={false} - onContinue={[Function]} - onOpen={[Function]} - open={true} - stepNumber={1} - /> - <TokenStep - currentUser={ - Object { - "isLoggedIn": true, - "login": "admin", - } - } - finished={false} - onContinue={[Function]} - onOpen={[Function]} - open={false} - stepNumber={2} - /> - <AnalysisStep - onFinish={[Function]} - onReset={[Function]} - open={false} - stepNumber={3} - /> - </div> -</div> -`; - -exports[`guides for sonarcloud 2`] = ` -<div> - <InstanceMessage - message="onboarding.header" - /> - <div - className="page page-limited onboarding" - > - <header - className="page-header" - > - <h1 - className="page-title" - > - <InstanceMessage - message="onboarding.header" - /> - </h1> - <div - className="page-actions" - > - <DeferredSpinner - loading={false} - timeout={100} - > - <a - className="js-skip text-muted" - href="#" - onClick={[Function]} - > - close - </a> - </DeferredSpinner> - <p - className="note" - > - tutorials.find_it_back_in_plus - </p> - </div> - <div - className="page-description" - > - onboarding.header.description.3 - </div> - </header> - <OrganizationStep - currentUser={ - Object { - "isLoggedIn": true, - "login": "admin", - } - } - finished={true} - onContinue={[Function]} - onOpen={[Function]} - open={false} - stepNumber={1} - /> - <TokenStep - currentUser={ - Object { - "isLoggedIn": true, - "login": "admin", - } - } - finished={false} - onContinue={[Function]} - onOpen={[Function]} - open={true} - stepNumber={2} - /> - <AnalysisStep - onFinish={[Function]} - onReset={[Function]} - open={false} - organization="my-org" - stepNumber={3} - /> - </div> -</div> -`; - -exports[`guides for sonarcloud 3`] = ` -<div> - <InstanceMessage - message="onboarding.header" - /> - <div - className="page page-limited onboarding" - > - <header - className="page-header" - > - <h1 - className="page-title" - > - <InstanceMessage - message="onboarding.header" - /> - </h1> - <div - className="page-actions" - > - <DeferredSpinner - loading={false} - timeout={100} - > - <a - className="js-skip text-muted" - href="#" - onClick={[Function]} - > - close - </a> - </DeferredSpinner> - <p - className="note" - > - tutorials.find_it_back_in_plus - </p> - </div> - <div - className="page-description" - > - onboarding.header.description.3 - </div> - </header> - <OrganizationStep - currentUser={ - Object { - "isLoggedIn": true, - "login": "admin", - } - } - finished={true} - onContinue={[Function]} - onOpen={[Function]} - open={false} - stepNumber={1} - /> - <TokenStep - currentUser={ - Object { - "isLoggedIn": true, - "login": "admin", - } - } - finished={true} - onContinue={[Function]} - onOpen={[Function]} - open={false} - stepNumber={2} - /> - <AnalysisStep - onFinish={[Function]} - onReset={[Function]} - open={true} - organization="my-org" - stepNumber={3} - token="abcd1234" - /> - </div> -</div> -`; 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 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<Props, State> { + 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<string> */) => ( + renderFormattedCommand = (...lines: Array<string>) => ( // keep this "useless" concatentation for the readability reason // eslint-disable-next-line no-useless-concat <pre>{lines.join(' ' + '\\' + '\n' + ' ')}</pre> @@ -108,69 +101,87 @@ export default class AnalysisStep extends React.PureComponent { } }; - renderCommandForMaven = () => ( - <JavaMaven - host={getHostUrl()} - organization={this.props.organization} - token={this.props.token} - /> - ); + renderCommandForMaven = () => { + const { token } = this.props; + if (!token) { + return null; + } + return <JavaMaven host={getHostUrl()} organization={this.props.organization} token={token} />; + }; - renderCommandForGradle = () => ( - <JavaGradle - host={getHostUrl()} - organization={this.props.organization} - token={this.props.token} - /> - ); + renderCommandForGradle = () => { + const { token } = this.props; + if (!token) { + return null; + } + return <JavaGradle host={getHostUrl()} organization={this.props.organization} token={token} />; + }; renderCommandForDotNet = () => { + const { token } = this.props; + const { result } = this.state; + if (!result || !result.projectKey || !token) { + return null; + } return ( <DotNet host={getHostUrl()} organization={this.props.organization} - // $FlowFixMe - projectKey={this.state.result.projectKey} - token={this.props.token} + projectKey={result.projectKey} + token={token} /> ); }; renderCommandForMSVC = () => { + const { token } = this.props; + const { result } = this.state; + if (!result || !result.projectKey || !token) { + return null; + } return ( <Msvc host={getHostUrl()} organization={this.props.organization} - // $FlowFixMe - projectKey={this.state.result.projectKey} - token={this.props.token} + projectKey={result.projectKey} + token={token} /> ); }; - renderCommandForClangGCC = () => ( - <ClangGCC - host={getHostUrl()} - organization={this.props.organization} - // $FlowFixMe - os={this.state.result.os} - // $FlowFixMe - projectKey={this.state.result.projectKey} - token={this.props.token} - /> - ); + renderCommandForClangGCC = () => { + const { token } = this.props; + const { result } = this.state; + if (!result || !result.projectKey || !result.os || !token) { + return null; + } + return ( + <ClangGCC + host={getHostUrl()} + organization={this.props.organization} + os={result.os} + projectKey={result.projectKey} + token={token} + /> + ); + }; - renderCommandForOther = () => ( - <Other - host={getHostUrl()} - organization={this.props.organization} - // $FlowFixMe - os={this.state.result.os} - // $FlowFixMe - projectKey={this.state.result.projectKey} - token={this.props.token} - /> - ); + renderCommandForOther = () => { + const { token } = this.props; + const { result } = this.state; + if (!result || !result.projectKey || !result.os || !token) { + return null; + } + return ( + <Other + host={getHostUrl()} + organization={this.props.organization} + os={result.os} + projectKey={result.projectKey} + token={token} + /> + ); + }; 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 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<Props, State> { + 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 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<Props, State> { + 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<HTMLInputElement>) => { const organization = this.sanitizeOrganization(event.target.value); this.setState({ organization }); this.validateOrganization(organization); }; - handleOrganizationCreate = (event /*: Event */) => { + handleOrganizationCreate = (event: React.FormEvent<HTMLFormElement>) => { 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 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<Props, State> { + 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<HTMLInputElement>) => { this.setState({ projectKey: this.sanitizeProjectKey(event.target.value) }); }; - handleProjectCreate = (event /*: Event */) => { + handleProjectCreate = (event: React.FormEvent<HTMLFormElement>) => { 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 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<string>, - personalOrganization?: string, - selection: 'personal' | 'existing' | 'new' -}; -*/ +interface State { + loading: boolean; + newOrganization?: string; + existingOrganization?: string; + existingOrganizations: Array<string>; + 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<Props, State> { + 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<HTMLAnchorElement>) => { event.preventDefault(); this.setState({ selection: 'personal' }); }; - handleExistingClick = (event /*: Event */) => { + handleExistingClick = (event: React.MouseEvent<HTMLAnchorElement>) => { event.preventDefault(); this.setState({ selection: 'existing' }); }; - handleNewClick = (event /*: Event */) => { + handleNewClick = (event: React.MouseEvent<HTMLAnchorElement>) => { 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<Props, State> { + 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 ( + <> + <Helmet title={header} titleTemplate="%s" /> + <header className="modal-head"> + <h2>{header}</h2> + </header> + <div className="modal-body modal-container"> + <p className="spacer-top big-spacer-bottom"> + {translateWithParameters( + 'onboarding.project.header.description', + organizationsEnabled ? 3 : 2 + )} + </p> + {organizationsEnabled && ( + <OrganizationStep + currentUser={currentUser} + finished={this.state.organization != null} + onContinue={this.handleOrganizationDone} + onOpen={this.handleOrganizationOpen} + open={step === 'organization'} + stepNumber={stepNumber++} + /> + )} + + <TokenStep + currentUser={currentUser} + finished={this.state.token != null} + onContinue={this.handleTokenDone} + onOpen={this.handleTokenOpen} + open={step === 'token'} + stepNumber={stepNumber++} + /> + + <AnalysisStep + onFinish={this.handleFinish} + onReset={this.handleReset} + open={step === 'analysis'} + organization={this.state.organization} + stepNumber={stepNumber} + token={token} + /> + </div> + <footer className="modal-foot"> + <ResetButtonLink className="js-skip" onClick={this.finishOnboarding}> + {(finished && translate('tutorials.finish')) || + (automatic ? translate('tutorials.skip') : translate('close'))} + </ResetButtonLink> + {finished && projectKey ? ( + <ProjectWatcher + onFinish={this.finishOnboarding} + onTimeout={this.handleTimeout} + projectKey={projectKey} + /> + ) : ( + <span className="pull-left note"> + {translate( + isSonarCloud() + ? 'tutorials.find_tutorial_back_in_plus' + : 'tutorials.find_tutorial_back_in_help' + )} + </span> + )} + </footer> + </> + ); + } +} + +const mapStateToProps = (state: any): StateProps => { + return { + currentUser: getCurrentUser(state), + organizationsEnabled: areThereCustomOrganizations(state) + }; +}; + +export default connect<StateProps, {}, OwnProps>(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 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 ( <Modal contentLabel={translate('tutorials.onboarding')} large={true}> - <OnboardingContainer {...props} /> + <ProjectOnboarding {...props} /> </Modal> ); } 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 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<DispatchProps> { +export class ProjectOnboardingPage extends React.PureComponent<DispatchProps> { static contextTypes = { router: PropTypes.object.isRequired }; @@ -38,10 +38,10 @@ export class OnboardingPage extends React.PureComponent<DispatchProps> { }; render() { - return <OnboardingModal onFinish={this.onSkipOnboardingTutorial} />; + return <ProjectOnboardingModal onFinish={this.onSkipOnboardingTutorial} />; } } 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 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<Props, State> { + 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 ( - <div className="big-spacer-top note text-center"> + <div className={classNames(className, 'display-inline-flex-center')}> <AlertSuccessIcon className="spacer-right" /> {translate('onboarding.project_watcher.finished')} </div> @@ -105,7 +99,7 @@ export default class ProjectWatcher extends React.PureComponent { if (inQueue || status === STATUSES.PENDING || status === STATUSES.IN_PROGRESS) { return ( - <div className="big-spacer-top note text-center"> + <div className={className}> <i className="spinner spacer-right" /> {translate('onboarding.project_watcher.in_progress')} </div> @@ -114,17 +108,13 @@ export default class ProjectWatcher extends React.PureComponent { if (status != null) { return ( - <div className="big-spacer-top note text-center"> + <div className={classNames(className, 'display-inline-flex-center')}> <AlertErrorIcon className="spacer-right" /> {translate('onboarding.project_watcher.failed')} </div> ); } - return ( - <div className="big-spacer-top note text-center"> - {translate('onboarding.project_watcher.not_started')} - </div> - ); + return <div className={className}>{translate('onboarding.project_watcher.not_started')}</div>; } } 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 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<HTMLDivElement>) => { 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 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<Props, State> { + 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<HTMLInputElement>) => { this.setState({ tokenName: event.target.value }); }; - handleTokenGenerate = (event /*: Event */) => { + handleTokenGenerate = (event: React.FormEvent<HTMLFormElement>) => { 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<HTMLAnchorElement>) => { event.preventDefault(); this.setState({ selection: 'generate' }); }; - handleUseExistingClick = (event /*: Event */) => { + handleUseExistingClick = (event: React.MouseEvent<HTMLAnchorElement>) => { event.preventDefault(); this.setState({ selection: 'use-existing' }); }; - handleExisingTokenChange = (event /*: { currentTarget: HTMLInputElement } */) => { + handleExisingTokenChange = (event: React.ChangeEvent<HTMLInputElement>) => { this.setState({ existingToken: event.currentTarget.value }); }; + stopLoading = () => { + if (this.mounted) { + this.setState({ loading: false }); + } + }; + renderGenerateOption = () => ( <div> {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')} </a> ) : ( - translate('onboading.token.generate_token') + translate('onboarding.token.generate_token') )} {this.state.selection === 'generate' && ( <div className="big-spacer-top"> @@ -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 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<any>).mockImplementation(() => false); }); it('selects java', () => { const onDone = jest.fn(); const wrapper = shallow(<LanguageStep onDone={onDone} onReset={jest.fn()} />); - 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(<LanguageStep onDone={onDone} onReset={jest.fn()} />); - 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<any>).mockImplementation(() => true); const onDone = jest.fn(); const wrapper = shallow(<LanguageStep onDone={onDone} onReset={jest.fn()} />); - 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(<LanguageStep onDone={onDone} onReset={jest.fn()} />); - 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 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(<NewOrganizationForm onDelete={onDelete} onDone={jest.fn()} />); 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 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(<NewProjectForm onDelete={onDelete} onDone={jest.fn()} />); 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 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<any>).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 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<any>).mockImplementation(() => 'SonarQube'); + (isSonarCloud as jest.Mock<any>).mockImplementation(() => false); const wrapper = shallow( - <Onboarding - className="modal-container" + <ProjectOnboarding currentUser={currentUser} onFinish={jest.fn()} organizationsEnabled={false} @@ -48,39 +46,36 @@ it('guides for on-premise', () => { ); 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<any>).mockImplementation(() => 'SonarCloud'); + (isSonarCloud as jest.Mock<any>).mockImplementation(() => true); const wrapper = shallow( - <Onboarding currentUser={currentUser} onFinish={jest.fn()} organizationsEnabled={true} /> + <ProjectOnboarding currentUser={currentUser} onFinish={jest.fn()} organizationsEnabled={true} /> ); 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<any>).mockImplementation(() => 'SonarQube'); + (isSonarCloud as jest.Mock<any>).mockImplementation(() => false); const onFinish = jest.fn(); const wrapper = mount( - <Onboarding currentUser={currentUser} onFinish={onFinish} organizationsEnabled={false} /> + <ProjectOnboarding currentUser={currentUser} onFinish={onFinish} organizationsEnabled={false} /> ); - 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 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 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 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 index 8e82d384b3f..8e82d384b3f 100644 --- 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 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 index eae8fcdb833..eae8fcdb833 100644 --- 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 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 index f6722a21bf6..f6722a21bf6 100644 --- 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 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 index 7e30dedc4a9..7e30dedc4a9 100644 --- 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 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`] = ` +<React.Fragment> + <HelmetWrapper + defer={true} + encodeSpecialCharacters={true} + title="onboarding.project.header" + titleTemplate="%s" + /> + <header + className="modal-head" + > + <h2> + onboarding.project.header + </h2> + </header> + <div + className="modal-body modal-container" + > + <p + className="spacer-top big-spacer-bottom" + > + onboarding.project.header.description.2 + </p> + <TokenStep + currentUser={ + Object { + "isLoggedIn": true, + "login": "admin", + } + } + finished={false} + onContinue={[Function]} + onOpen={[Function]} + open={true} + stepNumber={1} + /> + <AnalysisStep + onFinish={[Function]} + onReset={[Function]} + open={false} + stepNumber={2} + /> + </div> + <footer + className="modal-foot" + > + <ResetButtonLink + className="js-skip" + onClick={[Function]} + > + close + </ResetButtonLink> + <span + className="pull-left note" + > + tutorials.find_tutorial_back_in_help + </span> + </footer> +</React.Fragment> +`; + +exports[`guides for on-premise 2`] = ` +<React.Fragment> + <HelmetWrapper + defer={true} + encodeSpecialCharacters={true} + title="onboarding.project.header" + titleTemplate="%s" + /> + <header + className="modal-head" + > + <h2> + onboarding.project.header + </h2> + </header> + <div + className="modal-body modal-container" + > + <p + className="spacer-top big-spacer-bottom" + > + onboarding.project.header.description.2 + </p> + <TokenStep + currentUser={ + Object { + "isLoggedIn": true, + "login": "admin", + } + } + finished={true} + onContinue={[Function]} + onOpen={[Function]} + open={false} + stepNumber={1} + /> + <AnalysisStep + onFinish={[Function]} + onReset={[Function]} + open={true} + stepNumber={2} + token="abcd1234" + /> + </div> + <footer + className="modal-foot" + > + <ResetButtonLink + className="js-skip" + onClick={[Function]} + > + close + </ResetButtonLink> + <span + className="pull-left note" + > + tutorials.find_tutorial_back_in_help + </span> + </footer> +</React.Fragment> +`; + +exports[`guides for sonarcloud 1`] = ` +<React.Fragment> + <HelmetWrapper + defer={true} + encodeSpecialCharacters={true} + title="onboarding.project.header" + titleTemplate="%s" + /> + <header + className="modal-head" + > + <h2> + onboarding.project.header + </h2> + </header> + <div + className="modal-body modal-container" + > + <p + className="spacer-top big-spacer-bottom" + > + onboarding.project.header.description.3 + </p> + <OrganizationStep + currentUser={ + Object { + "isLoggedIn": true, + "login": "admin", + } + } + finished={false} + onContinue={[Function]} + onOpen={[Function]} + open={true} + stepNumber={1} + /> + <TokenStep + currentUser={ + Object { + "isLoggedIn": true, + "login": "admin", + } + } + finished={false} + onContinue={[Function]} + onOpen={[Function]} + open={false} + stepNumber={2} + /> + <AnalysisStep + onFinish={[Function]} + onReset={[Function]} + open={false} + stepNumber={3} + /> + </div> + <footer + className="modal-foot" + > + <ResetButtonLink + className="js-skip" + onClick={[Function]} + > + close + </ResetButtonLink> + <span + className="pull-left note" + > + tutorials.find_tutorial_back_in_plus + </span> + </footer> +</React.Fragment> +`; + +exports[`guides for sonarcloud 2`] = ` +<React.Fragment> + <HelmetWrapper + defer={true} + encodeSpecialCharacters={true} + title="onboarding.project.header" + titleTemplate="%s" + /> + <header + className="modal-head" + > + <h2> + onboarding.project.header + </h2> + </header> + <div + className="modal-body modal-container" + > + <p + className="spacer-top big-spacer-bottom" + > + onboarding.project.header.description.3 + </p> + <OrganizationStep + currentUser={ + Object { + "isLoggedIn": true, + "login": "admin", + } + } + finished={true} + onContinue={[Function]} + onOpen={[Function]} + open={false} + stepNumber={1} + /> + <TokenStep + currentUser={ + Object { + "isLoggedIn": true, + "login": "admin", + } + } + finished={false} + onContinue={[Function]} + onOpen={[Function]} + open={true} + stepNumber={2} + /> + <AnalysisStep + onFinish={[Function]} + onReset={[Function]} + open={false} + organization="my-org" + stepNumber={3} + /> + </div> + <footer + className="modal-foot" + > + <ResetButtonLink + className="js-skip" + onClick={[Function]} + > + close + </ResetButtonLink> + <span + className="pull-left note" + > + tutorials.find_tutorial_back_in_plus + </span> + </footer> +</React.Fragment> +`; + +exports[`guides for sonarcloud 3`] = ` +<React.Fragment> + <HelmetWrapper + defer={true} + encodeSpecialCharacters={true} + title="onboarding.project.header" + titleTemplate="%s" + /> + <header + className="modal-head" + > + <h2> + onboarding.project.header + </h2> + </header> + <div + className="modal-body modal-container" + > + <p + className="spacer-top big-spacer-bottom" + > + onboarding.project.header.description.3 + </p> + <OrganizationStep + currentUser={ + Object { + "isLoggedIn": true, + "login": "admin", + } + } + finished={true} + onContinue={[Function]} + onOpen={[Function]} + open={false} + stepNumber={1} + /> + <TokenStep + currentUser={ + Object { + "isLoggedIn": true, + "login": "admin", + } + } + finished={true} + onContinue={[Function]} + onOpen={[Function]} + open={false} + stepNumber={2} + /> + <AnalysisStep + onFinish={[Function]} + onReset={[Function]} + open={true} + organization="my-org" + stepNumber={3} + token="abcd1234" + /> + </div> + <footer + className="modal-foot" + > + <ResetButtonLink + className="js-skip" + onClick={[Function]} + > + close + </ResetButtonLink> + <span + className="pull-left note" + > + tutorials.find_tutorial_back_in_plus + </span> + </footer> +</React.Fragment> +`; 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 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`] = ` <div - className="big-spacer-top note text-center" + className="pull-left note" > onboarding.project_watcher.not_started </div> @@ -10,7 +10,7 @@ exports[`renders 1`] = ` exports[`renders 2`] = ` <div - className="big-spacer-top note text-center" + className="pull-left note" > <i className="spinner spacer-right" @@ -21,7 +21,7 @@ exports[`renders 2`] = ` exports[`renders 3`] = ` <div - className="big-spacer-top note text-center" + className="pull-left note display-inline-flex-center" > <AlertSuccessIcon className="spacer-right" @@ -32,7 +32,7 @@ exports[`renders 3`] = ` exports[`renders 4`] = ` <div - className="big-spacer-top note text-center" + className="pull-left note" > <i className="spinner spacer-right" diff --git a/server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/__snapshots__/Step-test.js.snap b/server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/__tests__/__snapshots__/Step-test.tsx.snap index 4667ca7ae8a..4667ca7ae8a 100644 --- a/server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/__snapshots__/Step-test.js.snap +++ b/server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/__tests__/__snapshots__/Step-test.tsx.snap diff --git a/server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/__snapshots__/TokenStep-test.js.snap b/server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/__tests__/__snapshots__/TokenStep-test.tsx.snap index 9b991a92305..54847972e8c 100644 --- a/server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/__snapshots__/TokenStep-test.js.snap +++ b/server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/__tests__/__snapshots__/TokenStep-test.tsx.snap @@ -50,7 +50,7 @@ exports[`generates token 1`] = ` <i className="icon-radio spacer-right is-checked" /> - onboading.token.generate_token + onboarding.token.generate_token </a> <div className="big-spacer-top" @@ -62,7 +62,7 @@ exports[`generates token 1`] = ` autoFocus={true} className="input-large spacer-right text-middle" onChange={[Function]} - placeholder="onboading.token.generate_token.placeholder" + placeholder="onboarding.token.generate_token.placeholder" required={true} type="text" value="" @@ -166,7 +166,7 @@ exports[`generates token 2`] = ` <i className="icon-radio spacer-right is-checked" /> - onboading.token.generate_token + onboarding.token.generate_token </a> <div className="big-spacer-top" @@ -178,7 +178,7 @@ exports[`generates token 2`] = ` autoFocus={true} className="input-large spacer-right text-middle" onChange={[Function]} - placeholder="onboading.token.generate_token.placeholder" + placeholder="onboarding.token.generate_token.placeholder" required={true} type="text" value="my token" @@ -582,7 +582,7 @@ exports[`revokes token 3`] = ` <i className="icon-radio spacer-right is-checked" /> - onboading.token.generate_token + onboarding.token.generate_token </a> <div className="big-spacer-top" @@ -594,7 +594,7 @@ exports[`revokes token 3`] = ` autoFocus={true} className="input-large spacer-right text-middle" onChange={[Function]} - placeholder="onboading.token.generate_token.placeholder" + placeholder="onboarding.token.generate_token.placeholder" required={true} type="text" value="" diff --git a/server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/BuildWrapper.js b/server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/commands/BuildWrapper.tsx index 47fe5202388..ed03adbf911 100644 --- a/server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/BuildWrapper.js +++ b/server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/commands/BuildWrapper.tsx @@ -17,24 +17,22 @@ * 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'; +import { getBaseUrl } from '../../../../helpers/urls'; -/*:: -type Props = { - className?: string, - os: string -}; -*/ +interface Props { + className?: string; + os: string; +} -const filenames = { +const filenames: { [key: string]: string } = { win: 'build-wrapper-win-x86.zip', linux: 'build-wrapper-linux-x86.zip', mac: 'build-wrapper-macosx-x86.zip' }; -export default function BuildWrapper(props /*: Props */) { +export default function BuildWrapper(props: Props) { return ( <div className={props.className}> <h4 className="spacer-bottom"> @@ -50,7 +48,7 @@ export default function BuildWrapper(props /*: Props */) { <a className="button" download={filenames[props.os]} - href={window.baseUrl + '/static/cpp/' + filenames[props.os]} + href={`${getBaseUrl()}/static/cpp/${filenames[props.os]}`} target="_blank"> {translate('download_verb')} </a> 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 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 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 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 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 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 ( <div className={props.className}> <h4 className="spacer-bottom">{translate('onboarding.analysis.msbuild.header')}</h4> @@ -39,6 +37,7 @@ export default function MSBuildScanner(props /*: Props */) { <a className="button" href="http://redirect.sonarsource.com/doc/install-configure-scanner-msbuild.html" + rel="noopener noreferrer" target="_blank"> {translate('download_verb')} </a> 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 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 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 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 ( <div className={props.className}> <h4 className="spacer-bottom"> @@ -44,6 +42,7 @@ export default function SQScanner(props /*: Props */) { <a className="button" href="http://redirect.sonarsource.com/doc/install-configure-scanner.html" + rel="noopener noreferrer" target="_blank"> {translate('download_verb')} </a> 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 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 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( <ClangGCC host="host" - os="linux" organization="organization" + os="linux" projectKey="projectKey" token="token" /> 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 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 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 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 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 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 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( <Other host="host" - os="linux" organization="organization" + os="linux" projectKey="projectKey" token="token" /> 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 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 index 07e8eb45f0a..07e8eb45f0a 100644 --- 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 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 index cdffd4c2061..cdffd4c2061 100644 --- 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 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 index 89b57fa28b4..89b57fa28b4 100644 --- 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 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 index 401137a9234..401137a9234 100644 --- 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 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 index d4462b957c6..d4462b957c6 100644 --- 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 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 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`] = ` <a className="button" href="http://redirect.sonarsource.com/doc/install-configure-scanner-msbuild.html" + rel="noopener noreferrer" target="_blank" > 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 index 2240a13995c..2240a13995c 100644 --- 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 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 index 73aba357dca..73aba357dca 100644 --- 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 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 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`] = ` <a className="button" href="http://redirect.sonarsource.com/doc/install-configure-scanner.html" + rel="noopener noreferrer" target="_blank" > download_verb @@ -46,6 +47,7 @@ exports[`renders correctly 2`] = ` <a className="button" href="http://redirect.sonarsource.com/doc/install-configure-scanner.html" + rel="noopener noreferrer" target="_blank" > download_verb @@ -73,6 +75,7 @@ exports[`renders correctly 3`] = ` <a className="button" href="http://redirect.sonarsource.com/doc/install-configure-scanner.html" + rel="noopener noreferrer" target="_blank" > 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 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<Props> { + render() { + const header = translate('onboarding.team.header'); + return ( + <Modal + contentLabel={header} + medium={true} + onRequestClose={this.props.onFinish} + shouldCloseOnOverlayClick={false}> + <header className="modal-head"> + <h2>{header}</h2> + </header> + <div className="modal-body"> + <div className="alert alert-info modal-alert"> + {translate('onboarding.team.work_in_progress')} + </div> + <p className="spacer-top big-spacer-bottom">{translate('onboarding.team.first_step')}</p> + <p className="spacer-top big-spacer-bottom"> + <FormattedMessage + defaultMessage={translate('onboarding.team.how_to_join')} + id="onboarding.team.how_to_join" + values={{ + link: ( + <Link onClick={this.props.onFinish} to="/documentation/organizations/manage-team"> + {translate('as_explained_here')} + </Link> + ) + }} + /> + </p> + </div> + <footer className="modal-foot"> + <ResetButtonLink onClick={this.props.onFinish}>{translate('close')}</ResetButtonLink> + </footer> + </Modal> + ); + } +} 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 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<Props> {} +it('renders correctly', () => { + expect(shallow(<TeamOnboardingModal onFinish={jest.fn()} />)).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`] = ` +<Modal + contentLabel="onboarding.team.header" + medium={true} + onRequestClose={[MockFunction]} + shouldCloseOnOverlayClick={false} +> + <header + className="modal-head" + > + <h2> + onboarding.team.header + </h2> + </header> + <div + className="modal-body" + > + <div + className="alert alert-info modal-alert" + > + onboarding.team.work_in_progress + </div> + <p + className="spacer-top big-spacer-bottom" + > + onboarding.team.first_step + </p> + <p + className="spacer-top big-spacer-bottom" + > + <FormattedMessage + defaultMessage="onboarding.team.how_to_join" + id="onboarding.team.how_to_join" + values={ + Object { + "link": <Link + onClick={[MockFunction]} + onlyActiveOnIndex={false} + style={Object {}} + to="/documentation/organizations/manage-team" + > + as_explained_here + </Link>, + } + } + /> + </p> + </div> + <footer + className="modal-foot" + > + <ResetButtonLink + onClick={[MockFunction]} + > + close + </ResetButtonLink> + </footer> +</Modal> +`; |