diff options
author | Stas Vilchik <stas.vilchik@sonarsource.com> | 2017-10-04 17:34:27 +0200 |
---|---|---|
committer | Stas Vilchik <stas.vilchik@sonarsource.com> | 2017-10-05 12:12:43 +0200 |
commit | 76d113a7b4dae274132b2fe29f79c063d9a39588 (patch) | |
tree | 591132247a5e3774f969caefc64fcc1b6f34ed73 /server/sonar-web/src/main/js | |
parent | af039ace6676a976e367a6b9a2a52463028fc5a3 (diff) | |
download | sonarqube-76d113a7b4dae274132b2fe29f79c063d9a39588.tar.gz sonarqube-76d113a7b4dae274132b2fe29f79c063d9a39588.zip |
SONAR-9707 Add possibility to reuse an existing token in the onboarding wizard
Diffstat (limited to 'server/sonar-web/src/main/js')
5 files changed, 273 insertions, 80 deletions
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 index a9065a16b0a..d0e4b41b234 100644 --- a/server/sonar-web/src/main/js/apps/tutorials/onboarding/Onboarding.js +++ b/server/sonar-web/src/main/js/apps/tutorials/onboarding/Onboarding.js @@ -172,7 +172,7 @@ export default class Onboarding extends React.PureComponent { </div> <div className="page-description"> {translateWithParameters( - 'onboarding.header.description_x', + 'onboarding.header.description', organizationsEnabled ? 3 : 2 )} </div> diff --git a/server/sonar-web/src/main/js/apps/tutorials/onboarding/TokenStep.js b/server/sonar-web/src/main/js/apps/tutorials/onboarding/TokenStep.js index 80ed22c805d..abcdb98b84e 100644 --- a/server/sonar-web/src/main/js/apps/tutorials/onboarding/TokenStep.js +++ b/server/sonar-web/src/main/js/apps/tutorials/onboarding/TokenStep.js @@ -19,6 +19,7 @@ */ // @flow import React from 'react'; +import classNames from 'classnames'; import Step from './Step'; import CloseIcon from '../../../components/icons-components/CloseIcon'; import { generateToken, revokeToken } from '../../../api/user-tokens'; @@ -36,7 +37,9 @@ type Props = {| /*:: type State = { + existingToken:string, loading: boolean, + selection: string, tokenName?: string, token?: string }; @@ -46,7 +49,9 @@ export default class TokenStep extends React.PureComponent { /*:: mounted: boolean; */ /*:: props: Props; */ state /*: State */ = { - loading: false + existingToken: '', + loading: false, + selection: 'generate' }; componentDidMount() { @@ -57,6 +62,9 @@ export default class TokenStep extends React.PureComponent { this.mounted = false; } + getToken = () => + this.state.selection === 'generate' ? this.state.token : this.state.existingToken; + handleTokenNameChange = (event /*: { target: HTMLInputElement } */) => { this.setState({ tokenName: event.target.value }); }; @@ -103,13 +111,95 @@ export default class TokenStep extends React.PureComponent { handleContinueClick = (event /*: Event */) => { event.preventDefault(); - if (this.state.token) { - this.props.onContinue(this.state.token); + const token = this.getToken(); + if (token) { + this.props.onContinue(token); } }; + handleGenerateClick = (event /*: Event */) => { + event.preventDefault(); + this.setState({ selection: 'generate' }); + }; + + handleUseExistingClick = (event /*: Event */) => { + event.preventDefault(); + this.setState({ selection: 'use-existing' }); + }; + + handleExisingTokenChange = (event /*: { currentTarget: HTMLInputElement } */) => { + this.setState({ existingToken: event.currentTarget.value }); + }; + + renderGenerateOption = () => ( + <div> + <a + className="js-new link-base-color link-no-underline" + href="#" + onClick={this.handleGenerateClick}> + <i + className={classNames('icon-radio', 'spacer-right', { + 'is-checked': this.state.selection === 'generate' + })} + /> + {translate('onboading.token.generate_token')} + </a> + {this.state.selection === 'generate' && ( + <div className="big-spacer-top"> + <form onSubmit={this.handleTokenGenerate}> + <input + autoFocus={true} + className="input-large spacer-right text-middle" + onChange={this.handleTokenNameChange} + placeholder={translate('onboading.token.generate_token.placeholder')} + required={true} + type="text" + value={this.state.tokenName || ''} + /> + {this.state.loading ? ( + <i className="spinner text-middle" /> + ) : ( + <button className="text-middle" disabled={!this.state.tokenName}> + {translate('onboarding.token.generate')} + </button> + )} + </form> + </div> + )} + </div> + ); + + renderUseExistingOption = () => ( + <div className="big-spacer-top"> + <a + className="js-new link-base-color link-no-underline" + href="#" + onClick={this.handleUseExistingClick}> + <i + className={classNames('icon-radio', 'spacer-right', { + 'is-checked': this.state.selection === 'use-existing' + })} + /> + {translate('onboarding.token.use_existing_token')} + </a> + {this.state.selection === 'use-existing' && ( + <div className="big-spacer-top"> + <input + autoFocus={true} + className="input-large spacer-right text-middle" + onChange={this.handleExisingTokenChange} + placeholder={translate('onboarding.token.use_existing_token.placeholder')} + required={true} + type="text" + value={this.state.existingToken} + /> + </div> + )} + </div> + ); + renderForm = () => { - const { loading, token, tokenName } = this.state; + const { existingToken, loading, selection, token, tokenName } = this.state; return ( <div className="boxed-group-inner"> @@ -129,27 +219,16 @@ export default class TokenStep extends React.PureComponent { )} </form> ) : ( - <form onSubmit={this.handleTokenGenerate}> - <input - autoFocus={true} - className="input-large spacer-right text-middle" - onChange={this.handleTokenNameChange} - placeholder={translate('onboarding.token.placeholder')} - required={true} - type="text" - value={tokenName || ''} - /> - {loading ? ( - <i className="spinner text-middle" /> - ) : ( - <button className="text-middle">{translate('onboarding.token.generate')}</button> - )} - </form> + <div> + {this.renderGenerateOption()} + {this.renderUseExistingOption()} + </div> )} <div className="note big-spacer-top width-50">{translate('onboarding.token.text')}</div> - {token != null && ( + {((selection === 'generate' && token != null) || + (selection === 'use-existing' && existingToken)) && ( <div className="big-spacer-top"> <button className="js-continue" onClick={this.handleContinueClick}> {translate('continue')} @@ -161,7 +240,8 @@ export default class TokenStep extends React.PureComponent { }; renderResult = () => { - const { token, tokenName } = this.state; + const { selection, tokenName } = this.state; + const token = this.getToken(); if (!token) { return null; @@ -170,8 +250,7 @@ export default class TokenStep extends React.PureComponent { return ( <div className="boxed-group-actions"> <i className="icon-check spacer-right" /> - {tokenName} - {': '} + {selection === 'generate' && tokenName && `${tokenName}: `} <strong>{token}</strong> </div> ); 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/onboarding/__tests__/TokenStep-test.js index 8a4608bb7ab..2c2f07d5b75 100644 --- a/server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/TokenStep-test.js +++ b/server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/TokenStep-test.js @@ -77,3 +77,19 @@ it('continues', () => { click(wrapper.find('.js-continue')); expect(onContinue).toBeCalledWith('abcd1234'); }); + +it('uses existing token', () => { + const onContinue = jest.fn(); + const wrapper = mount( + <TokenStep + finished={false} + open={true} + onContinue={onContinue} + onOpen={jest.fn()} + stepNumber={1} + /> + ); + wrapper.setState({ existingToken: 'abcd1234', selection: 'use-existing' }); + click(wrapper.find('.js-continue')); + expect(onContinue).toBeCalledWith('abcd1234'); +}); 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 index f8234801320..3fed8ff9708 100644 --- 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 @@ -39,7 +39,7 @@ exports[`guides for on-premise 1`] = ` <div className="page-description" > - onboarding.header.description_x.2 + onboarding.header.description.2 </div> </header> <TokenStep @@ -99,7 +99,7 @@ exports[`guides for on-premise 2`] = ` <div className="page-description" > - onboarding.header.description_x.2 + onboarding.header.description.2 </div> </header> <TokenStep @@ -160,7 +160,7 @@ exports[`guides for sonarcloud 1`] = ` <div className="page-description" > - onboarding.header.description_x.3 + onboarding.header.description.3 </div> </header> <OrganizationStep @@ -233,7 +233,7 @@ exports[`guides for sonarcloud 2`] = ` <div className="page-description" > - onboarding.header.description_x.3 + onboarding.header.description.3 </div> </header> <OrganizationStep @@ -307,7 +307,7 @@ exports[`guides for sonarcloud 3`] = ` <div className="page-description" > - onboarding.header.description_x.3 + onboarding.header.description.3 </div> </header> <OrganizationStep 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/onboarding/__tests__/__snapshots__/TokenStep-test.js.snap index 65138e0b498..38b83ffa20a 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/onboarding/__tests__/__snapshots__/TokenStep-test.js.snap @@ -35,24 +35,57 @@ exports[`generates token 1`] = ` <div className="boxed-group-inner" > - <form - onSubmit={[Function]} - > - <input - autoFocus={true} - className="input-large spacer-right text-middle" - onChange={[Function]} - placeholder="onboarding.token.placeholder" - required={true} - type="text" - value="" - /> - <button - className="text-middle" + <div> + <div> + <a + className="js-new link-base-color link-no-underline" + href="#" + onClick={[Function]} + > + <i + className="icon-radio spacer-right is-checked" + /> + onboading.token.generate_token + </a> + <div + className="big-spacer-top" + > + <form + onSubmit={[Function]} + > + <input + autoFocus={true} + className="input-large spacer-right text-middle" + onChange={[Function]} + placeholder="onboading.token.generate_token.placeholder" + required={true} + type="text" + value="" + /> + <button + className="text-middle" + disabled={true} + > + onboarding.token.generate + </button> + </form> + </div> + </div> + <div + className="big-spacer-top" > - onboarding.token.generate - </button> - </form> + <a + className="js-new link-base-color link-no-underline" + href="#" + onClick={[Function]} + > + <i + className="icon-radio spacer-right" + /> + onboarding.token.use_existing_token + </a> + </div> + </div> <div className="note big-spacer-top width-50" > @@ -99,22 +132,54 @@ exports[`generates token 2`] = ` <div className="boxed-group-inner" > - <form - onSubmit={[Function]} - > - <input - autoFocus={true} - className="input-large spacer-right text-middle" - onChange={[Function]} - placeholder="onboarding.token.placeholder" - required={true} - type="text" - value="my token" - /> - <i - className="spinner text-middle" - /> - </form> + <div> + <div> + <a + className="js-new link-base-color link-no-underline" + href="#" + onClick={[Function]} + > + <i + className="icon-radio spacer-right is-checked" + /> + onboading.token.generate_token + </a> + <div + className="big-spacer-top" + > + <form + onSubmit={[Function]} + > + <input + autoFocus={true} + className="input-large spacer-right text-middle" + onChange={[Function]} + placeholder="onboading.token.generate_token.placeholder" + required={true} + type="text" + value="my token" + /> + <i + className="spinner text-middle" + /> + </form> + </div> + </div> + <div + className="big-spacer-top" + > + <a + className="js-new link-base-color link-no-underline" + href="#" + onClick={[Function]} + > + <i + className="icon-radio spacer-right" + /> + onboarding.token.use_existing_token + </a> + </div> + </div> <div className="note big-spacer-top width-50" > @@ -419,24 +484,57 @@ exports[`revokes token 3`] = ` <div className="boxed-group-inner" > - <form - onSubmit={[Function]} - > - <input - autoFocus={true} - className="input-large spacer-right text-middle" - onChange={[Function]} - placeholder="onboarding.token.placeholder" - required={true} - type="text" - value="" - /> - <button - className="text-middle" + <div> + <div> + <a + className="js-new link-base-color link-no-underline" + href="#" + onClick={[Function]} + > + <i + className="icon-radio spacer-right is-checked" + /> + onboading.token.generate_token + </a> + <div + className="big-spacer-top" + > + <form + onSubmit={[Function]} + > + <input + autoFocus={true} + className="input-large spacer-right text-middle" + onChange={[Function]} + placeholder="onboading.token.generate_token.placeholder" + required={true} + type="text" + value="" + /> + <button + className="text-middle" + disabled={true} + > + onboarding.token.generate + </button> + </form> + </div> + </div> + <div + className="big-spacer-top" > - onboarding.token.generate - </button> - </form> + <a + className="js-new link-base-color link-no-underline" + href="#" + onClick={[Function]} + > + <i + className="icon-radio spacer-right" + /> + onboarding.token.use_existing_token + </a> + </div> + </div> <div className="note big-spacer-top width-50" > |