aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStas Vilchik <stas.vilchik@sonarsource.com>2018-05-14 17:22:53 +0200
committerSonarTech <sonartech@sonarsource.com>2018-05-14 20:20:49 +0200
commit2c91ba5af11b8e5955b65d4f0f76ea0e8917afbd (patch)
tree2bd822b92f062b3117df2f54339a47469517c23b
parent7fb43549fb85a0f79e61106ab770338d868b033d (diff)
downloadsonarqube-2c91ba5af11b8e5955b65d4f0f76ea0e8917afbd.tar.gz
sonarqube-2c91ba5af11b8e5955b65d4f0f76ea0e8917afbd.zip
replace native buttons with Button component (#235)
-rw-r--r--server/sonar-web/src/main/js/app/styles/init/forms.css258
-rw-r--r--server/sonar-web/src/main/js/app/utils/exposeLibraries.ts5
-rw-r--r--server/sonar-web/src/main/js/apps/account/components/Password.js13
-rw-r--r--server/sonar-web/src/main/js/apps/background-tasks/components/Search.js42
-rw-r--r--server/sonar-web/src/main/js/apps/organizations/components/OrganizationEdit.js31
-rw-r--r--server/sonar-web/src/main/js/apps/organizations/components/__tests__/__snapshots__/OrganizationEdit-test.js.snap15
-rw-r--r--server/sonar-web/src/main/js/apps/organizations/components/forms/AddMemberForm.js19
-rw-r--r--server/sonar-web/src/main/js/apps/organizations/components/forms/ManageMemberGroupsForm.js7
-rw-r--r--server/sonar-web/src/main/js/apps/organizations/components/forms/RemoveMemberForm.js9
-rw-r--r--server/sonar-web/src/main/js/apps/organizations/components/forms/__tests__/AddMemberForm-test.js6
-rw-r--r--server/sonar-web/src/main/js/apps/organizations/components/forms/__tests__/__snapshots__/AddMemberForm-test.js.snap19
-rw-r--r--server/sonar-web/src/main/js/apps/organizations/components/forms/__tests__/__snapshots__/ManageMemberGroupsForm-test.js.snap13
-rw-r--r--server/sonar-web/src/main/js/apps/organizations/components/forms/__tests__/__snapshots__/RemoveMemberForm-test.js.snap12
-rw-r--r--server/sonar-web/src/main/js/apps/permissions/project/components/PublicProjectDisclaimer.js17
-rw-r--r--server/sonar-web/src/main/js/apps/project-admin/deletion/Form.js25
-rw-r--r--server/sonar-web/src/main/js/apps/project-admin/key/BulkUpdateForm.js20
-rw-r--r--server/sonar-web/src/main/js/apps/project-admin/key/BulkUpdateResults.js21
-rw-r--r--server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityDateInput.js9
-rw-r--r--server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/__snapshots__/ProjectActivityDateInput-test.js.snap4
-rw-r--r--server/sonar-web/src/main/js/apps/projectActivity/components/forms/AddEventForm.js7
-rw-r--r--server/sonar-web/src/main/js/apps/projectActivity/components/forms/ChangeEventForm.js11
-rw-r--r--server/sonar-web/src/main/js/apps/projectActivity/components/forms/RemoveAnalysisForm.js9
-rw-r--r--server/sonar-web/src/main/js/apps/projectActivity/components/forms/RemoveEventForm.js9
-rw-r--r--server/sonar-web/src/main/js/apps/settings/components/DefinitionActions.tsx8
-rw-r--r--server/sonar-web/src/main/js/apps/settings/components/EmailForm.js27
-rw-r--r--server/sonar-web/src/main/js/apps/settings/components/inputs/InputForPassword.js9
-rw-r--r--server/sonar-web/src/main/js/apps/settings/components/inputs/__tests__/InputForPassword-test.js10
-rw-r--r--server/sonar-web/src/main/js/apps/settings/encryption/EncryptionForm.js11
-rw-r--r--server/sonar-web/src/main/js/apps/settings/encryption/GenerateSecretKeyForm.js13
-rw-r--r--server/sonar-web/src/main/js/apps/tutorials/onboarding/NewOrganizationForm.js6
-rw-r--r--server/sonar-web/src/main/js/apps/tutorials/onboarding/NewProjectForm.js8
-rw-r--r--server/sonar-web/src/main/js/apps/tutorials/onboarding/OrganizationStep.js8
-rw-r--r--server/sonar-web/src/main/js/apps/tutorials/onboarding/TokenStep.js13
-rw-r--r--server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/OrganizationStep-test.js4
-rw-r--r--server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/TokenStep-test.js12
-rw-r--r--server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/__snapshots__/NewOrganizationForm-test.js.snap42
-rw-r--r--server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/__snapshots__/NewProjectForm-test.js.snap40
-rw-r--r--server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/__snapshots__/TokenStep-test.js.snap81
-rw-r--r--server/sonar-web/src/main/js/components/controls/GlobalMessages.js8
-rw-r--r--server/sonar-web/src/main/js/components/controls/Toggle.js13
-rw-r--r--server/sonar-web/src/main/js/components/controls/__tests__/Toggle-test.js4
-rw-r--r--server/sonar-web/src/main/js/components/controls/styles.css14
-rw-r--r--server/sonar-web/src/main/js/components/issue/components/IssueMessage.js8
-rw-r--r--server/sonar-web/src/main/js/components/issue/components/__tests__/__snapshots__/IssueMessage-test.js.snap2
-rw-r--r--server/sonar-web/src/main/js/components/ui/buttons.css220
45 files changed, 568 insertions, 574 deletions
diff --git a/server/sonar-web/src/main/js/app/styles/init/forms.css b/server/sonar-web/src/main/js/app/styles/init/forms.css
index 98a484ef21e..696151b06bc 100644
--- a/server/sonar-web/src/main/js/app/styles/init/forms.css
+++ b/server/sonar-web/src/main/js/app/styles/init/forms.css
@@ -125,264 +125,6 @@ select {
line-height: var(--controlHeight);
}
-button,
-.button,
-input[type='submit'],
-input[type='button'] {
- display: inline-block;
- vertical-align: baseline;
- height: var(--controlHeight);
- line-height: calc(var(--controlHeight) - 2px);
- padding: 0 12px;
- border: 1px solid var(--darkBlue);
- border-radius: 2px;
- box-sizing: border-box;
- background: transparent;
- color: var(--darkBlue);
- font-weight: 600;
- font-size: var(--smallFontSize);
- text-align: center;
- text-decoration: none;
- cursor: pointer;
- outline: none;
- transition: border-color 0.2s ease;
-}
-
-button:hover,
-.button:hover,
-input[type='submit']:hover,
-input[type='button']:hover,
-button:focus,
-.button:focus,
-input[type='submit']:focus,
-input[type='button']:focus,
-button.button-active,
-.button.button-active,
-input[type='submit'].button-active,
-input[type='button'].button-active {
- background: var(--darkBlue);
- color: #fff;
-}
-
-button:active,
-.button:active,
-input[type='submit']:active,
-input[type='button']:active {
- box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
-}
-
-button.disabled,
-.button.disabled,
-input[type='submit'].disabled,
-input[type='button'].disabled,
-button:disabled,
-.button:disabled,
-input[type='submit']:disabled,
-input[type='button']:disabled,
-button:disabled:hover,
-.button:disabled:hover,
-input[type='submit']:disabled:hover,
-input[type='button']:disabled:hover,
-button:disabled:active,
-.button:disabled:active,
-input[type='submit']:disabled:active,
-input[type='button']:disabled:active,
-button:disabled:focus,
-.button:disabled:focus,
-input[type='submit']:disabled:focus,
-input[type='button']:disabled:focus {
- color: var(--disableGrayText) !important;
- border-color: var(--disableGrayBorder) !important;
- background: var(--disableGrayBg) !important;
- cursor: not-allowed !important;
- box-shadow: none !important;
-}
-
-.button svg {
- margin-top: calc((var(--controlHeight) - 16px - 2px) / 2);
-}
-
-.button-red,
-input[type='submit'].button-red {
- border-color: var(--red);
- color: var(--red);
-}
-
-.button-red:hover,
-input[type='submit'].button-red:hover,
-.button-red:focus,
-input[type='submit'].button-red:focus,
-.button-red.active,
-input[type='submit'].button-red.active {
- background: var(--red);
- color: #fff;
-}
-
-.button-success,
-input[type='submit'].button-success {
- border-color: var(--green);
- color: var(--green);
-}
-
-.button-success:hover,
-input[type='submit'].button-success:hover,
-.button-success:focus,
-input[type='submit'].button-success:focus,
-.button-success.active,
-input[type='submit'].button-success.active {
- background: var(--green);
- color: #fff;
-}
-
-.button-grey,
-input[type='submit'].button-grey {
- border-color: var(--gray71);
- color: var(--secondFontColor);
-}
-
-.button-grey:hover,
-input[type='submit'].button-grey:hover,
-.button-grey:focus,
-input[type='submit'].button-grey:focus,
-.button-grey.active,
-input[type='submit'].button-grey.active {
- background: var(--gray71);
- color: #ffffff;
-}
-
-.button-grey.button-active,
-input[type='submit'].button-grey.button-active {
- background: var(--secondFontColor);
- border-color: var(--secondFontColor);
- color: #ffffff;
-}
-
-.button-clean,
-.button-clean:hover,
-.button-clean:focus {
- padding: 0;
- line-height: 1;
- border: none;
- background: transparent;
- box-shadow: none;
- color: var(--baseFontColor);
-}
-
-.button-clean path {
- transition: opacity 0.3s ease;
-}
-
-.button-clean:hover path {
- opacity: 0.8;
-}
-
-.button-link {
- display: inline;
- height: auto; /* Keep this to not inherit the height from .button */
- margin: 0;
- padding: 0;
- border: none;
- background: transparent;
- color: var(--darkBlue);
- font-weight: 400;
- font-size: inherit;
- line-height: inherit;
- transition: all 0.2s ease;
-}
-
-.button-link svg {
- margin-top: 0;
-}
-
-.button-link:hover,
-.button-link:focus {
- background: transparent;
- color: var(--blue);
-}
-
-.button-link:active {
- box-shadow: none;
- outline: thin dotted #ccc;
-}
-
-.button-link:disabled,
-.button-link:disabled:hover,
-.button-link:disabled:active,
-.button-link:disabled:focus {
- color: var(--secondFontColor);
- background: transparent !important;
- cursor: default;
-}
-
-.button-small {
- height: var(--smallControlHeight);
- line-height: 18px;
- padding: 0 6px;
- font-size: 11px;
-}
-
-.button-small > svg {
- margin-top: 2px;
-}
-
-.button-group {
- display: inline-block;
- vertical-align: middle;
- font-size: 0;
- white-space: nowrap;
-}
-
-.button-group > button,
-.button-group > .button {
- position: relative;
- z-index: var(--normalZIndex);
- display: inline-block;
- vertical-align: middle;
- margin: 0;
- cursor: pointer;
-}
-
-.button-group > button:hover:not(:disabled),
-.button-group > .button:hover:not(:disabled),
-.button-group > button:focus:not(:disabled),
-.button-group > .button:focus:not(:disabled),
-.button-group > button:active:not(:disabled),
-.button-group > .button:active:not(:disabled),
-.button-group > button.active:not(:disabled),
-.button-group > .button.active:not(:disabled) {
- z-index: var(--aboveNormalZIndex);
-}
-
-.button-group > button:disabled,
-.button-group > .button:disabled {
- z-index: var(--belowNormalZIndex);
-}
-
-.button-group > button:not(:first-child),
-.button-group > .button:not(:first-child) {
- border-top-left-radius: 0;
- border-bottom-left-radius: 0;
-}
-
-.button-group > button:not(:last-child):not(.dropdown-toggle),
-.button-group > .button:not(:last-child):not(.dropdown-toggle) {
- border-top-right-radius: 0;
- border-bottom-right-radius: 0;
-}
-
-.button-group > button + button,
-.button-group > button + .button,
-.button-group > .button + button,
-.button-group > .button + .button {
- margin-left: -1px;
-}
-
-.button-group > a:not(.button) {
- vertical-align: middle;
- margin: 0 8px;
- font-size: var(--smallFontSize);
-}
-
.input-tiny {
width: 60px !important;
}
diff --git a/server/sonar-web/src/main/js/app/utils/exposeLibraries.ts b/server/sonar-web/src/main/js/app/utils/exposeLibraries.ts
index b07d8c1f53b..df9cd6a4b7f 100644
--- a/server/sonar-web/src/main/js/app/utils/exposeLibraries.ts
+++ b/server/sonar-web/src/main/js/app/utils/exposeLibraries.ts
@@ -40,7 +40,7 @@ import SelectList from '../../components/SelectList/SelectList';
import CoverageRating from '../../components/ui/CoverageRating';
import DuplicationsRating from '../../components/ui/DuplicationsRating';
import Level from '../../components/ui/Level';
-import { EditButton } from '../../components/ui/buttons';
+import { EditButton, Button, SubmitButton, ResetButtonLink } from '../../components/ui/buttons';
import DeferredSpinner from '../../components/common/DeferredSpinner';
import ReloadButton from '../../components/controls/ReloadButton';
@@ -53,6 +53,7 @@ const exposeLibraries = () => {
global.SonarMeasures = measures;
global.SonarRequest = { ...request, throwGlobalError, addGlobalSuccessMessage };
global.SonarComponents = {
+ Button,
CoverageRating,
DateFormatter,
DateFromNow,
@@ -68,9 +69,11 @@ const exposeLibraries = () => {
ListFooter,
Modal,
ReloadButton,
+ ResetButtonLink,
SearchBox,
Select,
SelectList,
+ SubmitButton,
Tooltip
};
};
diff --git a/server/sonar-web/src/main/js/apps/account/components/Password.js b/server/sonar-web/src/main/js/apps/account/components/Password.js
index 3c4b7ce7f1b..00c12a5e67a 100644
--- a/server/sonar-web/src/main/js/apps/account/components/Password.js
+++ b/server/sonar-web/src/main/js/apps/account/components/Password.js
@@ -19,6 +19,7 @@
*/
import React, { Component } from 'react';
import { changePassword } from '../../../api/users';
+import { SubmitButton } from '../../../components/ui/buttons';
import { translate } from '../../../helpers/l10n';
export default class Password extends Component {
@@ -80,7 +81,7 @@ export default class Password extends Component {
{errors &&
errors.map((e, i) => (
- <div key={i} className="alert alert-danger">
+ <div className="alert alert-danger" key={i}>
{e}
</div>
))}
@@ -91,10 +92,10 @@ export default class Password extends Component {
<em className="mandatory">*</em>
</label>
<input
- ref={elem => (this.oldPassword = elem)}
autoComplete="off"
id="old_password"
name="old_password"
+ ref={elem => (this.oldPassword = elem)}
required={true}
type="password"
/>
@@ -105,10 +106,10 @@ export default class Password extends Component {
<em className="mandatory">*</em>
</label>
<input
- ref={elem => (this.password = elem)}
autoComplete="off"
id="password"
name="password"
+ ref={elem => (this.password = elem)}
required={true}
type="password"
/>
@@ -119,18 +120,18 @@ export default class Password extends Component {
<em className="mandatory">*</em>
</label>
<input
- ref={elem => (this.passwordConfirmation = elem)}
autoComplete="off"
id="password_confirmation"
name="password_confirmation"
+ ref={elem => (this.passwordConfirmation = elem)}
required={true}
type="password"
/>
</div>
<div className="modal-field">
- <button id="change-password" type="submit">
+ <SubmitButton id="change-password">
{translate('my_profile.password.submit')}
- </button>
+ </SubmitButton>
</div>
</form>
</section>
diff --git a/server/sonar-web/src/main/js/apps/background-tasks/components/Search.js b/server/sonar-web/src/main/js/apps/background-tasks/components/Search.js
index 11f7de80c0a..e43c4adc54d 100644
--- a/server/sonar-web/src/main/js/apps/background-tasks/components/Search.js
+++ b/server/sonar-web/src/main/js/apps/background-tasks/components/Search.js
@@ -26,6 +26,7 @@ import CurrentsFilter from './CurrentsFilter';
import DateFilter from './DateFilter';
import { DEFAULT_FILTERS } from './../constants';
import SearchBox from '../../../components/controls/SearchBox';
+import { Button } from '../../../components/ui/buttons';
import { translate } from '../../../helpers/l10n';
export default class Search extends React.PureComponent {
@@ -39,17 +40,17 @@ export default class Search extends React.PureComponent {
onReload: PropTypes.func.isRequired
};
- handleStatusChange(status /*: string */) {
+ handleStatusChange = (status /*: string */) => {
this.props.onFilterUpdate({ status });
- }
+ };
- handleTypeChange(taskType /*: string */) {
+ handleTypeChange = (taskType /*: string */) => {
this.props.onFilterUpdate({ taskType });
- }
+ };
- handleCurrentsChange(currents /*: string */) {
+ handleCurrentsChange = (currents /*: string */) => {
this.props.onFilterUpdate({ currents });
- }
+ };
handleDateChange = (date /*: { maxExecutedAt?: Date; minSubmittedAt?: Date } */) => {
this.props.onFilterUpdate(date);
@@ -59,16 +60,9 @@ export default class Search extends React.PureComponent {
this.props.onFilterUpdate({ query });
};
- handleReload(e /*: Object */) {
- e.target.blur();
- this.props.onReload();
- }
-
- handleReset(e /*: Object */) {
- e.preventDefault();
- e.target.blur();
+ handleReset = () => {
this.props.onFilterUpdate(DEFAULT_FILTERS);
- }
+ };
renderSearchBox() {
const { component, query } = this.props;
@@ -106,16 +100,12 @@ export default class Search extends React.PureComponent {
<ul className="bt-search-form">
<li>
<h6 className="bt-search-form-label">{translate('status')}</h6>
- <StatusFilter value={status} onChange={this.handleStatusChange.bind(this)} />
+ <StatusFilter onChange={this.handleStatusChange} value={status} />
</li>
{types.length > 1 && (
<li>
<h6 className="bt-search-form-label">{translate('type')}</h6>
- <TypesFilter
- value={taskType}
- types={types}
- onChange={this.handleTypeChange.bind(this)}
- />
+ <TypesFilter onChange={this.handleTypeChange} types={types} value={taskType} />
</li>
)}
{!component && (
@@ -123,7 +113,7 @@ export default class Search extends React.PureComponent {
<h6 className="bt-search-form-label">
{translate('background_tasks.currents_filter.ONLY_CURRENTS')}
</h6>
- <CurrentsFilter value={currents} onChange={this.handleCurrentsChange.bind(this)} />
+ <CurrentsFilter onChange={this.handleCurrentsChange} value={currents} />
</li>
)}
<li>
@@ -138,12 +128,12 @@ export default class Search extends React.PureComponent {
{this.renderSearchBox()}
<li className="nowrap">
- <button className="js-reload" onClick={this.handleReload.bind(this)} disabled={loading}>
+ <Button className="js-reload" disabled={loading} onClick={this.props.onReload}>
{translate('reload')}
- </button>{' '}
- <button ref="resetButton" onClick={this.handleReset.bind(this)} disabled={loading}>
+ </Button>{' '}
+ <Button disabled={loading} onClick={this.handleReset}>
{translate('reset_verb')}
- </button>
+ </Button>
</li>
</ul>
</section>
diff --git a/server/sonar-web/src/main/js/apps/organizations/components/OrganizationEdit.js b/server/sonar-web/src/main/js/apps/organizations/components/OrganizationEdit.js
index b1dda0bb9fd..770c9587bac 100644
--- a/server/sonar-web/src/main/js/apps/organizations/components/OrganizationEdit.js
+++ b/server/sonar-web/src/main/js/apps/organizations/components/OrganizationEdit.js
@@ -26,6 +26,7 @@ import { translate } from '../../../helpers/l10n';
/*:: import type { Organization } from '../../../store/organizations/duck'; */
import { getOrganizationByKey } from '../../../store/rootReducer';
import { updateOrganization } from '../actions';
+import { SubmitButton } from '../../../components/ui/buttons';
/*::
type Props = {
@@ -115,14 +116,14 @@ class OrganizationEdit extends React.PureComponent {
<em className="mandatory">*</em>
</label>
<input
+ disabled={this.state.loading}
id="organization-name"
+ maxLength="64"
name="name"
+ onChange={e => this.setState({ name: e.target.value })}
required={true}
type="text"
- maxLength="64"
value={this.state.name}
- disabled={this.state.loading}
- onChange={e => this.setState({ name: e.target.value })}
/>
<div className="modal-field-description">
{translate('organization.name.description')}
@@ -131,13 +132,13 @@ class OrganizationEdit extends React.PureComponent {
<div className="modal-field">
<label htmlFor="organization-avatar">{translate('organization.avatar')}</label>
<input
+ disabled={this.state.loading}
id="organization-avatar"
+ maxLength="256"
name="avatar"
+ onChange={this.handleAvatarInputChange}
type="text"
- maxLength="256"
value={this.state.avatar}
- disabled={this.state.loading}
- onChange={this.handleAvatarInputChange}
/>
<div className="modal-field-description">
{translate('organization.avatar.description')}
@@ -148,20 +149,20 @@ class OrganizationEdit extends React.PureComponent {
{translate('organization.avatar.preview')}
{':'}
</div>
- <img src={this.state.avatarImage} alt="" height={30} />
+ <img alt="" height={30} src={this.state.avatarImage} />
</div>
)}
</div>
<div className="modal-field">
<label htmlFor="organization-description">{translate('description')}</label>
<textarea
+ disabled={this.state.loading}
id="organization-description"
+ maxLength="256"
name="description"
+ onChange={e => this.setState({ description: e.target.value })}
rows="3"
- maxLength="256"
value={this.state.description}
- disabled={this.state.loading}
- onChange={e => this.setState({ description: e.target.value })}
/>
<div className="modal-field-description">
{translate('organization.description.description')}
@@ -170,22 +171,20 @@ class OrganizationEdit extends React.PureComponent {
<div className="modal-field">
<label htmlFor="organization-url">{translate('organization.url')}</label>
<input
+ disabled={this.state.loading}
id="organization-url"
+ maxLength="256"
name="url"
+ onChange={e => this.setState({ url: e.target.value })}
type="text"
- maxLength="256"
value={this.state.url}
- disabled={this.state.loading}
- onChange={e => this.setState({ url: e.target.value })}
/>
<div className="modal-field-description">
{translate('organization.url.description')}
</div>
</div>
<div className="modal-field">
- <button type="submit" disabled={this.state.loading}>
- {translate('save')}
- </button>
+ <SubmitButton disabled={this.state.loading}>{translate('save')}</SubmitButton>
{this.state.loading && <i className="spinner spacer-left" />}
</div>
</form>
diff --git a/server/sonar-web/src/main/js/apps/organizations/components/__tests__/__snapshots__/OrganizationEdit-test.js.snap b/server/sonar-web/src/main/js/apps/organizations/components/__tests__/__snapshots__/OrganizationEdit-test.js.snap
index 9c18fd59ac8..0abba69f78e 100644
--- a/server/sonar-web/src/main/js/apps/organizations/components/__tests__/__snapshots__/OrganizationEdit-test.js.snap
+++ b/server/sonar-web/src/main/js/apps/organizations/components/__tests__/__snapshots__/OrganizationEdit-test.js.snap
@@ -125,12 +125,11 @@ exports[`smoke test 1`] = `
<div
className="modal-field"
>
- <button
+ <SubmitButton
disabled={false}
- type="submit"
>
save
- </button>
+ </SubmitButton>
</div>
</form>
</div>
@@ -277,12 +276,11 @@ exports[`smoke test 2`] = `
<div
className="modal-field"
>
- <button
+ <SubmitButton
disabled={false}
- type="submit"
>
save
- </button>
+ </SubmitButton>
</div>
</form>
</div>
@@ -429,12 +427,11 @@ exports[`smoke test 3`] = `
<div
className="modal-field"
>
- <button
+ <SubmitButton
disabled={true}
- type="submit"
>
save
- </button>
+ </SubmitButton>
<i
className="spinner spacer-left"
/>
diff --git a/server/sonar-web/src/main/js/apps/organizations/components/forms/AddMemberForm.js b/server/sonar-web/src/main/js/apps/organizations/components/forms/AddMemberForm.js
index e2659f5026f..90e04927efb 100644
--- a/server/sonar-web/src/main/js/apps/organizations/components/forms/AddMemberForm.js
+++ b/server/sonar-web/src/main/js/apps/organizations/components/forms/AddMemberForm.js
@@ -23,6 +23,7 @@ import UsersSelectSearch from '../../../users/components/UsersSelectSearch';
import { searchMembers } from '../../../../api/organizations';
import Modal from '../../../../components/controls/Modal';
import { translate } from '../../../../helpers/l10n';
+import { SubmitButton, ResetButtonLink, Button } from '../../../../components/ui/buttons';
/*:: import type { Organization } from '../../../../store/organizations/duck'; */
/*:: import type { Member } from '../../../../store/organizationsMembers/actions'; */
@@ -79,7 +80,7 @@ export default class AddMemberForm extends React.PureComponent {
renderModal() {
const header = translate('users.add');
return (
- <Modal key="add-member-modal" contentLabel={header} onRequestClose={this.closeForm}>
+ <Modal contentLabel={header} key="add-member-modal" onRequestClose={this.closeForm}>
<header className="modal-head">
<h2>{header}</h2>
</header>
@@ -89,21 +90,19 @@ export default class AddMemberForm extends React.PureComponent {
<label>{translate('users.search_description')}</label>
<UsersSelectSearch
autoFocus={true}
- selectedUser={this.state.selectedMember}
excludedUsers={this.props.memberLogins}
- searchUsers={this.handleSearch}
handleValueChange={this.selectedMemberChange}
+ searchUsers={this.handleSearch}
+ selectedUser={this.state.selectedMember}
/>
</div>
</div>
<footer className="modal-foot">
<div>
- <button type="submit" disabled={!this.state.selectedMember}>
+ <SubmitButton disabled={!this.state.selectedMember}>
{translate('organization.members.add_to_members')}
- </button>
- <button type="reset" className="button-link" onClick={this.closeForm}>
- {translate('cancel')}
- </button>
+ </SubmitButton>
+ <ResetButtonLink onClick={this.closeForm}>{translate('cancel')}</ResetButtonLink>
</div>
</footer>
</form>
@@ -113,9 +112,9 @@ export default class AddMemberForm extends React.PureComponent {
render() {
const buttonComponent = (
- <button key="add-member-button" onClick={this.openForm}>
+ <Button key="add-member-button" onClick={this.openForm}>
{translate('organization.members.add')}
- </button>
+ </Button>
);
if (this.state.open) {
return [buttonComponent, this.renderModal()];
diff --git a/server/sonar-web/src/main/js/apps/organizations/components/forms/ManageMemberGroupsForm.js b/server/sonar-web/src/main/js/apps/organizations/components/forms/ManageMemberGroupsForm.js
index 4339538beb1..bf15a60e97a 100644
--- a/server/sonar-web/src/main/js/apps/organizations/components/forms/ManageMemberGroupsForm.js
+++ b/server/sonar-web/src/main/js/apps/organizations/components/forms/ManageMemberGroupsForm.js
@@ -24,6 +24,7 @@ import { getUserGroups } from '../../../../api/users';
import Modal from '../../../../components/controls/Modal';
import { translate, translateWithParameters } from '../../../../helpers/l10n';
import OrganizationGroupCheckbox from '../OrganizationGroupCheckbox';
+import { SubmitButton, ResetButtonLink } from '../../../../components/ui/buttons';
/*:: import type { Member } from '../../../../store/organizationsMembers/actions'; */
/*:: import type { Organization, OrgGroup } from '../../../../store/organizations/duck'; */
@@ -141,10 +142,8 @@ export default class ManageMemberGroupsForm extends React.PureComponent {
</div>
<footer className="modal-foot">
<div>
- <button type="submit">{translate('save')}</button>
- <button className="button-link" onClick={this.props.onClose} type="reset">
- {translate('cancel')}
- </button>
+ <SubmitButton>{translate('save')}</SubmitButton>
+ <ResetButtonLink onClick={this.props.onClose}>{translate('cancel')}</ResetButtonLink>
</div>
</footer>
</form>
diff --git a/server/sonar-web/src/main/js/apps/organizations/components/forms/RemoveMemberForm.js b/server/sonar-web/src/main/js/apps/organizations/components/forms/RemoveMemberForm.js
index 343054e6be3..e1b48aa57c8 100644
--- a/server/sonar-web/src/main/js/apps/organizations/components/forms/RemoveMemberForm.js
+++ b/server/sonar-web/src/main/js/apps/organizations/components/forms/RemoveMemberForm.js
@@ -21,6 +21,7 @@
import React from 'react';
import Modal from '../../../../components/controls/Modal';
import { translate, translateWithParameters } from '../../../../helpers/l10n';
+import { SubmitButton, ResetButtonLink } from '../../../../components/ui/buttons';
/*:: import type { Member } from '../../../../store/organizationsMembers/actions'; */
/*:: import type { Organization } from '../../../../store/organizations/duck'; */
@@ -58,12 +59,10 @@ export default class RemoveMemberForm extends React.PureComponent {
</div>
<footer className="modal-foot">
<div>
- <button autoFocus={true} className="button-red" type="submit">
+ <SubmitButton autoFocus={true} className="button-red">
{translate('remove')}
- </button>
- <button className="button-link" onClick={this.props.onClose} type="reset">
- {translate('cancel')}
- </button>
+ </SubmitButton>
+ <ResetButtonLink onClick={this.props.onClose}>{translate('cancel')}</ResetButtonLink>
</div>
</footer>
</form>
diff --git a/server/sonar-web/src/main/js/apps/organizations/components/forms/__tests__/AddMemberForm-test.js b/server/sonar-web/src/main/js/apps/organizations/components/forms/__tests__/AddMemberForm-test.js
index 9068270a83c..b2a32edcbef 100644
--- a/server/sonar-web/src/main/js/apps/organizations/components/forms/__tests__/AddMemberForm-test.js
+++ b/server/sonar-web/src/main/js/apps/organizations/components/forms/__tests__/AddMemberForm-test.js
@@ -27,7 +27,7 @@ jest.mock('react-dom');
const memberLogins = ['admin'];
it('should render and open the modal', () => {
- const wrapper = shallow(<AddMemberForm memberLogins={memberLogins} addMember={jest.fn()} />);
+ const wrapper = shallow(<AddMemberForm addMember={jest.fn()} memberLogins={memberLogins} />);
expect(wrapper).toMatchSnapshot();
wrapper.setState({ open: true });
@@ -36,8 +36,8 @@ it('should render and open the modal', () => {
});
it('should correctly handle user interactions', () => {
- const wrapper = shallow(<AddMemberForm memberLogins={memberLogins} addMember={jest.fn()} />);
- click(wrapper.find('button'));
+ const wrapper = shallow(<AddMemberForm addMember={jest.fn()} memberLogins={memberLogins} />);
+ click(wrapper.find('Button'));
expect(wrapper.state('open')).toBeTruthy();
wrapper.instance().closeForm();
expect(wrapper.state('open')).toBeFalsy();
diff --git a/server/sonar-web/src/main/js/apps/organizations/components/forms/__tests__/__snapshots__/AddMemberForm-test.js.snap b/server/sonar-web/src/main/js/apps/organizations/components/forms/__tests__/__snapshots__/AddMemberForm-test.js.snap
index ac981164804..643417eae4a 100644
--- a/server/sonar-web/src/main/js/apps/organizations/components/forms/__tests__/__snapshots__/AddMemberForm-test.js.snap
+++ b/server/sonar-web/src/main/js/apps/organizations/components/forms/__tests__/__snapshots__/AddMemberForm-test.js.snap
@@ -1,21 +1,21 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`should render and open the modal 1`] = `
-<button
+<Button
key="add-member-button"
onClick={[Function]}
>
organization.members.add
-</button>
+</Button>
`;
exports[`should render and open the modal 2`] = `
Array [
- <button
+ <Button
onClick={[Function]}
>
organization.members.add
- </button>,
+ </Button>,
<Modal
contentLabel="users.add"
onRequestClose={[Function]}
@@ -56,19 +56,16 @@ Array [
className="modal-foot"
>
<div>
- <button
+ <SubmitButton
disabled={true}
- type="submit"
>
organization.members.add_to_members
- </button>
- <button
- className="button-link"
+ </SubmitButton>
+ <ResetButtonLink
onClick={[Function]}
- type="reset"
>
cancel
- </button>
+ </ResetButtonLink>
</div>
</footer>
</form>
diff --git a/server/sonar-web/src/main/js/apps/organizations/components/forms/__tests__/__snapshots__/ManageMemberGroupsForm-test.js.snap b/server/sonar-web/src/main/js/apps/organizations/components/forms/__tests__/__snapshots__/ManageMemberGroupsForm-test.js.snap
index 2eea5b66085..43f0bcecb5a 100644
--- a/server/sonar-web/src/main/js/apps/organizations/components/forms/__tests__/__snapshots__/ManageMemberGroupsForm-test.js.snap
+++ b/server/sonar-web/src/main/js/apps/organizations/components/forms/__tests__/__snapshots__/ManageMemberGroupsForm-test.js.snap
@@ -81,17 +81,12 @@ exports[`should render 1`] = `
className="modal-foot"
>
<div>
- <button
- type="submit"
- >
+ <SubmitButton>
save
- </button>
- <button
- className="button-link"
- type="reset"
- >
+ </SubmitButton>
+ <ResetButtonLink>
cancel
- </button>
+ </ResetButtonLink>
</div>
</footer>
</form>
diff --git a/server/sonar-web/src/main/js/apps/organizations/components/forms/__tests__/__snapshots__/RemoveMemberForm-test.js.snap b/server/sonar-web/src/main/js/apps/organizations/components/forms/__tests__/__snapshots__/RemoveMemberForm-test.js.snap
index eb235ae0e4a..ef1c3bfb9f1 100644
--- a/server/sonar-web/src/main/js/apps/organizations/components/forms/__tests__/__snapshots__/RemoveMemberForm-test.js.snap
+++ b/server/sonar-web/src/main/js/apps/organizations/components/forms/__tests__/__snapshots__/RemoveMemberForm-test.js.snap
@@ -24,19 +24,15 @@ exports[`should render 1`] = `
className="modal-foot"
>
<div>
- <button
+ <SubmitButton
autoFocus={true}
className="button-red"
- type="submit"
>
remove
- </button>
- <button
- className="button-link"
- type="reset"
- >
+ </SubmitButton>
+ <ResetButtonLink>
cancel
- </button>
+ </ResetButtonLink>
</div>
</footer>
</form>
diff --git a/server/sonar-web/src/main/js/apps/permissions/project/components/PublicProjectDisclaimer.js b/server/sonar-web/src/main/js/apps/permissions/project/components/PublicProjectDisclaimer.js
index 032d7435a0d..a62f07d30f5 100644
--- a/server/sonar-web/src/main/js/apps/permissions/project/components/PublicProjectDisclaimer.js
+++ b/server/sonar-web/src/main/js/apps/permissions/project/components/PublicProjectDisclaimer.js
@@ -21,6 +21,7 @@
import React from 'react';
import Modal from '../../../../components/controls/Modal';
import { translate, translateWithParameters } from '../../../../helpers/l10n';
+import { Button, ResetButtonLink } from '../../../../components/ui/buttons';
/*::
type Props = {
@@ -36,13 +37,7 @@ type Props = {
export default class PublicProjectDisclaimer extends React.PureComponent {
/*:: props: Props; */
- handleCancelClick = (event /*: Event */) => {
- event.preventDefault();
- this.props.onClose();
- };
-
- handleConfirmClick = (event /*: Event */) => {
- event.preventDefault();
+ handleConfirmClick = () => {
this.props.onConfirm();
this.props.onClose();
};
@@ -66,12 +61,10 @@ export default class PublicProjectDisclaimer extends React.PureComponent {
</div>
<footer className="modal-foot">
- <button id="confirm-turn-to-public" onClick={this.handleConfirmClick}>
+ <Button id="confirm-turn-to-public" onClick={this.handleConfirmClick}>
{translate('projects_role.turn_project_to_public', qualifier)}
- </button>
- <a href="#" onClick={this.handleCancelClick}>
- {translate('cancel')}
- </a>
+ </Button>
+ <ResetButtonLink onClick={this.props.onClose}>{translate('cancel')}</ResetButtonLink>
</footer>
</Modal>
);
diff --git a/server/sonar-web/src/main/js/apps/project-admin/deletion/Form.js b/server/sonar-web/src/main/js/apps/project-admin/deletion/Form.js
index d2ae453f4ed..5fe6e3a2d5a 100644
--- a/server/sonar-web/src/main/js/apps/project-admin/deletion/Form.js
+++ b/server/sonar-web/src/main/js/apps/project-admin/deletion/Form.js
@@ -22,6 +22,7 @@ import PropTypes from 'prop-types';
import { deleteProject, deletePortfolio } from '../../../api/components';
import Modal from '../../../components/controls/Modal';
import { translate, translateWithParameters } from '../../../helpers/l10n';
+import { Button, SubmitButton, ResetButtonLink } from '../../../components/ui/buttons';
export default class Form extends React.PureComponent {
static propTypes = {
@@ -42,8 +43,7 @@ export default class Form extends React.PureComponent {
this.mounted = false;
}
- handleDeleteClick = event => {
- event.preventDefault();
+ handleDeleteClick = () => {
this.setState({ modalOpen: true });
};
@@ -65,19 +65,14 @@ export default class Form extends React.PureComponent {
.catch(this.stopLoading);
};
- handleCloseClick = (event /*: Event */) => {
- event.preventDefault();
- this.closeModal();
- };
-
render() {
const { component } = this.props;
return (
<div>
- <button id="delete-project" className="button-red" onClick={this.handleDeleteClick}>
+ <Button className="button-red" id="delete-project" onClick={this.handleDeleteClick}>
{translate('delete')}
- </button>
+ </Button>
{this.state.modalOpen && (
<Modal contentLabel="project deletion" onRequestClose={this.closeModal}>
@@ -94,15 +89,15 @@ export default class Form extends React.PureComponent {
</div>
<div className="modal-foot">
{this.state.loading && <i className="js-modal-spinner spinner spacer-right" />}
- <button
- id="delete-project-confirm"
+ <SubmitButton
className="button-red"
- disabled={this.state.loading}>
+ disabled={this.state.loading}
+ id="delete-project-confirm">
{translate('delete')}
- </button>
- <a href="#" className="js-modal-close" onClick={this.handleCloseClick}>
+ </SubmitButton>
+ <ResetButtonLink className="js-modal-close" onClick={this.closeModal}>
{translate('cancel')}
- </a>
+ </ResetButtonLink>
</div>
</form>
</Modal>
diff --git a/server/sonar-web/src/main/js/apps/project-admin/key/BulkUpdateForm.js b/server/sonar-web/src/main/js/apps/project-admin/key/BulkUpdateForm.js
index 225772031b6..2bd1cc8960e 100644
--- a/server/sonar-web/src/main/js/apps/project-admin/key/BulkUpdateForm.js
+++ b/server/sonar-web/src/main/js/apps/project-admin/key/BulkUpdateForm.js
@@ -20,50 +20,50 @@
import React from 'react';
import PropTypes from 'prop-types';
import { translate } from '../../../helpers/l10n';
+import { SubmitButton } from '../../../components/ui/buttons';
export default class BulkUpdateForm extends React.PureComponent {
static propTypes = {
onSubmit: PropTypes.func.isRequired
};
- handleSubmit(e) {
+ handleSubmit = e => {
e.preventDefault();
- this.refs.submit.blur();
const replace = this.refs.replace.value;
const by = this.refs.by.value;
this.props.onSubmit(replace, by);
- }
+ };
render() {
return (
- <form onSubmit={this.handleSubmit.bind(this)}>
+ <form onSubmit={this.handleSubmit}>
<div className="modal-field">
<label htmlFor="bulk-update-replace">{translate('update_key.replace')}</label>
<input
- ref="replace"
id="bulk-update-replace"
name="replace"
- type="text"
placeholder={translate('update_key.replace_example')}
+ ref="replace"
required={true}
+ type="text"
/>
</div>
<div className="modal-field">
<label htmlFor="bulk-update-by">{translate('update_key.by')}</label>
<input
- ref="by"
id="bulk-update-by"
name="by"
- type="text"
placeholder={translate('update_key.by_example')}
+ ref="by"
required={true}
+ type="text"
/>
- <button ref="submit" id="bulk-update-see-results" className="big-spacer-left">
+ <SubmitButton className="big-spacer-left" id="bulk-update-see-results">
{translate('update_key.see_results')}
- </button>
+ </SubmitButton>
</div>
</form>
);
diff --git a/server/sonar-web/src/main/js/apps/project-admin/key/BulkUpdateResults.js b/server/sonar-web/src/main/js/apps/project-admin/key/BulkUpdateResults.js
index 368a6c4c91a..e5f49173d69 100644
--- a/server/sonar-web/src/main/js/apps/project-admin/key/BulkUpdateResults.js
+++ b/server/sonar-web/src/main/js/apps/project-admin/key/BulkUpdateResults.js
@@ -21,6 +21,7 @@ import React from 'react';
import PropTypes from 'prop-types';
import { some } from 'lodash';
import { translateWithParameters, translate } from '../../../helpers/l10n';
+import { Button } from '../../../components/ui/buttons';
export default class BulkUpdateResults extends React.PureComponent {
static propTypes = {
@@ -28,12 +29,6 @@ export default class BulkUpdateResults extends React.PureComponent {
onConfirm: PropTypes.func.isRequired
};
- handleConfirm(e) {
- e.preventDefault();
- e.target.blur();
- this.props.onConfirm();
- }
-
render() {
const { results, replace, by } = this.props;
const isEmpty = results.length === 0;
@@ -41,15 +36,15 @@ export default class BulkUpdateResults extends React.PureComponent {
const canUpdate = !isEmpty && !hasDuplications;
return (
- <div id="bulk-update-simulation" className="big-spacer-top">
+ <div className="big-spacer-top" id="bulk-update-simulation">
{isEmpty && (
- <div id="bulk-update-nothing" className="spacer-bottom">
+ <div className="spacer-bottom" id="bulk-update-nothing">
{translateWithParameters('update_key.no_key_to_update', replace)}
</div>
)}
{hasDuplications && (
- <div id="bulk-update-duplicate" className="spacer-bottom">
+ <div className="spacer-bottom" id="bulk-update-duplicate">
{translateWithParameters('update_key.cant_update_because_duplicate_keys', replace, by)}
</div>
)}
@@ -61,7 +56,7 @@ export default class BulkUpdateResults extends React.PureComponent {
)}
{!isEmpty && (
- <table id="bulk-update-results" className="data zebra zebra-hover">
+ <table className="data zebra zebra-hover" id="bulk-update-results">
<thead>
<tr>
<th>{translate('update_key.old_key')}</th>
@@ -70,7 +65,7 @@ export default class BulkUpdateResults extends React.PureComponent {
</thead>
<tbody>
{results.map(result => (
- <tr key={result.key} data-key={result.key}>
+ <tr data-key={result.key} key={result.key}>
<td className="js-old-key">{result.key}</td>
<td className="js-new-key">
{result.duplicate && (
@@ -88,9 +83,9 @@ export default class BulkUpdateResults extends React.PureComponent {
<div className="big-spacer-top">
{canUpdate && (
- <button id="bulk-update-confirm" onClick={this.handleConfirm.bind(this)}>
+ <Button id="bulk-update-confirm" onClick={this.props.onConfirm}>
{translate('update_verb')}
- </button>
+ </Button>
)}
</div>
</div>
diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityDateInput.js b/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityDateInput.js
index 1cb10269e5d..8c40fdb0a7e 100644
--- a/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityDateInput.js
+++ b/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityDateInput.js
@@ -21,6 +21,7 @@
import React from 'react';
import DateRangeInput from '../../../components/controls/DateRangeInput';
import { translate } from '../../../helpers/l10n';
+import { Button } from '../../../components/ui/buttons';
/*:: import type { RawQuery } from '../../../helpers/query'; */
/*::
@@ -49,12 +50,12 @@ export default class ProjectActivityDateInput extends React.PureComponent {
onChange={this.handleChange}
value={{ from: this.props.from, to: this.props.to }}
/>
- <button
+ <Button
className="spacer-left"
- onClick={this.handleResetClick}
- disabled={this.props.from == null && this.props.to == null}>
+ disabled={this.props.from == null && this.props.to == null}
+ onClick={this.handleResetClick}>
{translate('project_activity.reset_dates')}
- </button>
+ </Button>
</div>
);
}
diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/__snapshots__/ProjectActivityDateInput-test.js.snap b/server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/__snapshots__/ProjectActivityDateInput-test.js.snap
index 3406b3b0c76..d10a64ba95c 100644
--- a/server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/__snapshots__/ProjectActivityDateInput-test.js.snap
+++ b/server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/__snapshots__/ProjectActivityDateInput-test.js.snap
@@ -11,12 +11,12 @@ exports[`should render correctly the date inputs 1`] = `
}
}
/>
- <button
+ <Button
className="spacer-left"
disabled={false}
onClick={[Function]}
>
project_activity.reset_dates
- </button>
+ </Button>
</div>
`;
diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/forms/AddEventForm.js b/server/sonar-web/src/main/js/apps/projectActivity/components/forms/AddEventForm.js
index ded7c2b733c..e42126813e2 100644
--- a/server/sonar-web/src/main/js/apps/projectActivity/components/forms/AddEventForm.js
+++ b/server/sonar-web/src/main/js/apps/projectActivity/components/forms/AddEventForm.js
@@ -21,6 +21,7 @@
import React from 'react';
import Modal from '../../../../components/controls/Modal';
import { translate } from '../../../../helpers/l10n';
+import { SubmitButton, ResetButtonLink } from '../../../../components/ui/buttons';
/*:: import type { Analysis } from '../../types'; */
/*::
@@ -102,10 +103,10 @@ export default class AddEventForm extends React.PureComponent {
<i className="spinner" />
) : (
<div>
- <button type="submit">{translate('save')}</button>
- <button className="button-link" onClick={this.props.onClose} type="reset">
+ <SubmitButton>{translate('save')}</SubmitButton>
+ <ResetButtonLink onClick={this.props.onClose}>
{translate('cancel')}
- </button>
+ </ResetButtonLink>
</div>
)}
</footer>
diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/forms/ChangeEventForm.js b/server/sonar-web/src/main/js/apps/projectActivity/components/forms/ChangeEventForm.js
index 1de35bda7a6..25465a67e9b 100644
--- a/server/sonar-web/src/main/js/apps/projectActivity/components/forms/ChangeEventForm.js
+++ b/server/sonar-web/src/main/js/apps/projectActivity/components/forms/ChangeEventForm.js
@@ -21,6 +21,7 @@
import React from 'react';
import Modal from '../../../../components/controls/Modal';
import { translate } from '../../../../helpers/l10n';
+import { SubmitButton, ResetButtonLink } from '../../../../components/ui/buttons';
/*:: import type { Event } from '../../types'; */
/*::
@@ -107,11 +108,11 @@ export default class ChangeEventForm extends React.PureComponent {
<div className="modal-field">
<label>{translate('name')}</label>
<input
- value={this.state.name}
autoFocus={true}
disabled={this.state.processing}
- type="text"
onChange={this.changeInput}
+ type="text"
+ value={this.state.name}
/>
</div>
</div>
@@ -121,10 +122,8 @@ export default class ChangeEventForm extends React.PureComponent {
<i className="spinner" />
) : (
<div>
- <button type="submit">{translate('change_verb')}</button>
- <button type="reset" className="button-link" onClick={this.closeForm}>
- {translate('cancel')}
- </button>
+ <SubmitButton>{translate('change_verb')}</SubmitButton>
+ <ResetButtonLink onClick={this.closeForm}>{translate('cancel')}</ResetButtonLink>
</div>
)}
</footer>
diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/forms/RemoveAnalysisForm.js b/server/sonar-web/src/main/js/apps/projectActivity/components/forms/RemoveAnalysisForm.js
index e813985139a..e0e5dd70d48 100644
--- a/server/sonar-web/src/main/js/apps/projectActivity/components/forms/RemoveAnalysisForm.js
+++ b/server/sonar-web/src/main/js/apps/projectActivity/components/forms/RemoveAnalysisForm.js
@@ -21,6 +21,7 @@
import React from 'react';
import Modal from '../../../../components/controls/Modal';
import { translate } from '../../../../helpers/l10n';
+import { SubmitButton, ResetButtonLink } from '../../../../components/ui/buttons';
/*:: import type { Analysis } from '../../types'; */
/*::
@@ -82,12 +83,12 @@ export default class RemoveAnalysisForm extends React.PureComponent {
<i className="spinner" />
) : (
<div>
- <button autoFocus={true} className="button-red" type="submit">
+ <SubmitButton autoFocus={true} className="button-red">
{translate('delete')}
- </button>
- <button className="button-link" onClick={this.props.onClose} type="reset">
+ </SubmitButton>
+ <ResetButtonLink onClick={this.props.onClose}>
{translate('cancel')}
- </button>
+ </ResetButtonLink>
</div>
)}
</footer>
diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/forms/RemoveEventForm.js b/server/sonar-web/src/main/js/apps/projectActivity/components/forms/RemoveEventForm.js
index 346c50b9ee5..5b5db9eb199 100644
--- a/server/sonar-web/src/main/js/apps/projectActivity/components/forms/RemoveEventForm.js
+++ b/server/sonar-web/src/main/js/apps/projectActivity/components/forms/RemoveEventForm.js
@@ -21,6 +21,7 @@
import React from 'react';
import Modal from '../../../../components/controls/Modal';
import { translate } from '../../../../helpers/l10n';
+import { SubmitButton, ResetButtonLink } from '../../../../components/ui/buttons';
/*:: import type { Event } from '../../types'; */
/*::
@@ -96,12 +97,10 @@ export default class RemoveEventForm extends React.PureComponent {
<i className="spinner" />
) : (
<div>
- <button type="submit" className="button-red" autoFocus={true}>
+ <SubmitButton autoFocus={true} className="button-red">
{translate('delete')}
- </button>
- <button type="reset" className="button-link" onClick={this.closeForm}>
- {translate('cancel')}
- </button>
+ </SubmitButton>
+ <ResetButtonLink onClick={this.closeForm}>{translate('cancel')}</ResetButtonLink>
</div>
)}
</footer>
diff --git a/server/sonar-web/src/main/js/apps/settings/components/DefinitionActions.tsx b/server/sonar-web/src/main/js/apps/settings/components/DefinitionActions.tsx
index f45f0d984d9..2f2df148296 100644
--- a/server/sonar-web/src/main/js/apps/settings/components/DefinitionActions.tsx
+++ b/server/sonar-web/src/main/js/apps/settings/components/DefinitionActions.tsx
@@ -21,7 +21,7 @@ import * as React from 'react';
import Modal from '../../../components/controls/Modal';
import { isEmptyValue, getDefaultValue, getSettingValue } from '../utils';
import { translate } from '../../../helpers/l10n';
-import { Button } from '../../../components/ui/buttons';
+import { Button, ResetButtonLink, SubmitButton } from '../../../components/ui/buttons';
import { SettingValue, Definition } from '../../../api/settings';
type Props = {
@@ -65,10 +65,8 @@ export default class DefinitionActions extends React.PureComponent<Props, State>
<p>{translate('settings.reset_confirm.description')}</p>
</div>
<footer className="modal-foot">
- <button className="button-red">{translate('reset_verb')}</button>
- <button className="button-link" onClick={this.handleClose} type="reset">
- {translate('cancel')}
- </button>
+ <SubmitButton className="button-red">{translate('reset_verb')}</SubmitButton>
+ <ResetButtonLink onClick={this.handleClose}>{translate('cancel')}</ResetButtonLink>
</footer>
</form>
</Modal>
diff --git a/server/sonar-web/src/main/js/apps/settings/components/EmailForm.js b/server/sonar-web/src/main/js/apps/settings/components/EmailForm.js
index 0633d0cfbb9..83b624c8e5f 100644
--- a/server/sonar-web/src/main/js/apps/settings/components/EmailForm.js
+++ b/server/sonar-web/src/main/js/apps/settings/components/EmailForm.js
@@ -23,6 +23,7 @@ import { translate, translateWithParameters } from '../../../helpers/l10n';
import { sendTestEmail } from '../../../api/settings';
import { parseError } from '../../../helpers/request';
import { getCurrentUser } from '../../../store/rootReducer';
+import { SubmitButton } from '../../../components/ui/buttons';
class EmailForm extends React.PureComponent {
constructor(props) {
@@ -37,22 +38,22 @@ class EmailForm extends React.PureComponent {
};
}
- handleFormSubmit(e) {
- e.preventDefault();
+ handleFormSubmit = event => {
+ event.preventDefault();
this.setState({ success: false, error: null, loading: true });
const { recipient, subject, message } = this.state;
sendTestEmail(recipient, subject, message).then(
() => this.setState({ success: true, loading: false }),
error => parseError(error).then(message => this.setState({ error: message, loading: false }))
);
- }
+ };
render() {
return (
<div className="huge-spacer-top">
<h3 className="spacer-bottom">{translate('email_configuration.test.title')}</h3>
- <form style={{ marginLeft: 201 }} onSubmit={e => this.handleFormSubmit(e)}>
+ <form onSubmit={this.handleFormSubmit} style={{ marginLeft: 201 }}>
{this.state.success && (
<div className="modal-field">
<div className="alert alert-success">
@@ -77,12 +78,12 @@ class EmailForm extends React.PureComponent {
</label>
<input
className="settings-large-input"
+ disabled={this.state.loading}
id="test-email-to"
- type="email"
+ onChange={e => this.setState({ recipient: e.target.value })}
required={true}
+ type="email"
value={this.state.recipient}
- disabled={this.state.loading}
- onChange={e => this.setState({ recipient: e.target.value })}
/>
</div>
<div className="modal-field">
@@ -91,11 +92,11 @@ class EmailForm extends React.PureComponent {
</label>
<input
className="settings-large-input"
+ disabled={this.state.loading}
id="test-email-subject"
+ onChange={e => this.setState({ subject: e.target.value })}
type="text"
value={this.state.subject}
- disabled={this.state.loading}
- onChange={e => this.setState({ subject: e.target.value })}
/>
</div>
<div className="modal-field">
@@ -105,20 +106,20 @@ class EmailForm extends React.PureComponent {
</label>
<textarea
className="settings-large-input"
+ disabled={this.state.loading}
id="test-email-title"
+ onChange={e => this.setState({ message: e.target.value })}
required={true}
rows="5"
value={this.state.message}
- disabled={this.state.loading}
- onChange={e => this.setState({ message: e.target.value })}
/>
</div>
<div className="modal-field">
{this.state.loading && <i className="spacer-right spinner" />}
- <button disabled={this.state.loading}>
+ <SubmitButton disabled={this.state.loading}>
{translate('email_configuration.test.send')}
- </button>
+ </SubmitButton>
</div>
</form>
</div>
diff --git a/server/sonar-web/src/main/js/apps/settings/components/inputs/InputForPassword.js b/server/sonar-web/src/main/js/apps/settings/components/inputs/InputForPassword.js
index b2ed1582ada..e045d181f13 100644
--- a/server/sonar-web/src/main/js/apps/settings/components/inputs/InputForPassword.js
+++ b/server/sonar-web/src/main/js/apps/settings/components/inputs/InputForPassword.js
@@ -20,6 +20,7 @@
import React from 'react';
import { translate } from '../../../../helpers/l10n';
import { defaultInputPropTypes } from '../../propTypes';
+import { Button } from '../../../../components/ui/buttons';
export default class InputForPassword extends React.PureComponent {
static propTypes = defaultInputPropTypes;
@@ -40,11 +41,9 @@ export default class InputForPassword extends React.PureComponent {
this.setState({ changing: true, value: e.target.value });
}
- handleChangeClick(e) {
- e.preventDefault();
- e.target.blur();
+ handleChangeClick = () => {
this.setState({ changing: true });
- }
+ };
renderInput() {
return (
@@ -73,7 +72,7 @@ export default class InputForPassword extends React.PureComponent {
return (
<div>
<i className="big-spacer-right icon-lock icon-gray" />
- <button onClick={e => this.handleChangeClick(e)}>{translate('change_verb')}</button>
+ <Button onClick={this.handleChangeClick}>{translate('change_verb')}</Button>
</div>
);
}
diff --git a/server/sonar-web/src/main/js/apps/settings/components/inputs/__tests__/InputForPassword-test.js b/server/sonar-web/src/main/js/apps/settings/components/inputs/__tests__/InputForPassword-test.js
index b30d3db8cc9..935a500e979 100644
--- a/server/sonar-web/src/main/js/apps/settings/components/inputs/__tests__/InputForPassword-test.js
+++ b/server/sonar-web/src/main/js/apps/settings/components/inputs/__tests__/InputForPassword-test.js
@@ -25,7 +25,7 @@ import { click, change, submit } from '../../../../../helpers/testUtils';
it('should render lock icon, but no form', () => {
const onChange = jest.fn();
const input = shallow(
- <InputForPassword name="foo" value="bar" isDefault={false} onChange={onChange} />
+ <InputForPassword isDefault={false} name="foo" onChange={onChange} value="bar" />
);
expect(input.find('.icon-lock').length).toBe(1);
expect(input.find('form').length).toBe(0);
@@ -34,9 +34,9 @@ it('should render lock icon, but no form', () => {
it('should open form', () => {
const onChange = jest.fn();
const input = shallow(
- <InputForPassword name="foo" value="bar" isDefault={false} onChange={onChange} />
+ <InputForPassword isDefault={false} name="foo" onChange={onChange} value="bar" />
);
- const button = input.find('button');
+ const button = input.find('Button');
expect(button.length).toBe(1);
click(button);
@@ -46,9 +46,9 @@ it('should open form', () => {
it('should set value', () => {
const onChange = jest.fn(() => Promise.resolve());
const input = shallow(
- <InputForPassword name="foo" value="bar" isDefault={false} onChange={onChange} />
+ <InputForPassword isDefault={false} name="foo" onChange={onChange} value="bar" />
);
- click(input.find('button'));
+ click(input.find('Button'));
change(input.find('.js-password-input'), 'secret');
submit(input.find('form'));
expect(onChange).toBeCalledWith('secret');
diff --git a/server/sonar-web/src/main/js/apps/settings/encryption/EncryptionForm.js b/server/sonar-web/src/main/js/apps/settings/encryption/EncryptionForm.js
index 0a7b75c580a..fef8d687609 100644
--- a/server/sonar-web/src/main/js/apps/settings/encryption/EncryptionForm.js
+++ b/server/sonar-web/src/main/js/apps/settings/encryption/EncryptionForm.js
@@ -20,6 +20,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import { translate } from '../../../helpers/l10n';
+import { SubmitButton } from '../../../components/ui/buttons';
export default class EncryptionForm extends React.PureComponent {
static propTypes = {
@@ -46,8 +47,8 @@ export default class EncryptionForm extends React.PureComponent {
<div className="spacer-bottom">{translate('encryption.form_intro')}</div>
<form
- id="encryption-form"
className="big-spacer-bottom"
+ id="encryption-form"
onSubmit={e => this.handleEncrypt(e)}>
<textarea
autoFocus={true}
@@ -59,7 +60,7 @@ export default class EncryptionForm extends React.PureComponent {
value={this.state.value}
/>
<div className="spacer-top">
- <button>{translate('encryption.encrypt')}</button>
+ <SubmitButton>{translate('encryption.encrypt')}</SubmitButton>
</div>
</form>
@@ -68,10 +69,10 @@ export default class EncryptionForm extends React.PureComponent {
{translate('encryption.encrypted_value')}
{': '}
<input
- id="encrypted-value"
className="input-clear input-code input-super-large"
- type="text"
+ id="encrypted-value"
readOnly={true}
+ type="text"
value={this.props.encryptedValue}
/>
</div>
@@ -83,7 +84,7 @@ export default class EncryptionForm extends React.PureComponent {
dangerouslySetInnerHTML={{ __html: translate('encryption.form_note') }}
/>
<form id="encryption-new-key-form" onSubmit={e => this.handleGenerateNewKey(e)}>
- <button>{translate('encryption.generate_new_secret_key')}</button>
+ <SubmitButton>{translate('encryption.generate_new_secret_key')}</SubmitButton>
</form>
</div>
</div>
diff --git a/server/sonar-web/src/main/js/apps/settings/encryption/GenerateSecretKeyForm.js b/server/sonar-web/src/main/js/apps/settings/encryption/GenerateSecretKeyForm.js
index c818cecbcd6..bec7d661859 100644
--- a/server/sonar-web/src/main/js/apps/settings/encryption/GenerateSecretKeyForm.js
+++ b/server/sonar-web/src/main/js/apps/settings/encryption/GenerateSecretKeyForm.js
@@ -20,6 +20,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import { translate } from '../../../helpers/l10n';
+import { SubmitButton } from '../../../components/ui/buttons';
export default class GenerateSecretKeyForm extends React.PureComponent {
static propTypes = {
@@ -27,10 +28,10 @@ export default class GenerateSecretKeyForm extends React.PureComponent {
generateSecretKey: PropTypes.func.isRequired
};
- handleSubmit(e) {
+ handleSubmit = e => {
e.preventDefault();
this.props.generateSecretKey();
- }
+ };
render() {
return (
@@ -40,10 +41,10 @@ export default class GenerateSecretKeyForm extends React.PureComponent {
<div className="big-spacer-bottom">
<h3 className="spacer-bottom">{translate('encryption.secret_key')}</h3>
<input
- id="secret-key"
className="input-large"
- type="text"
+ id="secret-key"
readOnly={true}
+ type="text"
value={this.props.secretKey}
/>
</div>
@@ -62,8 +63,8 @@ export default class GenerateSecretKeyForm extends React.PureComponent {
dangerouslySetInnerHTML={{ __html: translate('ecryption.secret_key_description') }}
/>
- <form id="generate-secret-key-form" onSubmit={e => this.handleSubmit(e)}>
- <button>{translate('encryption.generate_secret_key')}s</button>
+ <form id="generate-secret-key-form" onSubmit={this.handleSubmit}>
+ <SubmitButton>{translate('encryption.generate_secret_key')}</SubmitButton>
</form>
</div>
)}
diff --git a/server/sonar-web/src/main/js/apps/tutorials/onboarding/NewOrganizationForm.js b/server/sonar-web/src/main/js/apps/tutorials/onboarding/NewOrganizationForm.js
index 618608b401e..11be750a41b 100644
--- a/server/sonar-web/src/main/js/apps/tutorials/onboarding/NewOrganizationForm.js
+++ b/server/sonar-web/src/main/js/apps/tutorials/onboarding/NewOrganizationForm.js
@@ -26,7 +26,7 @@ import {
getOrganization
} from '../../../api/organizations';
import AlertErrorIcon from '../../../components/icons-components/AlertErrorIcon';
-import { DeleteButton } from '../../../components/ui/buttons';
+import { DeleteButton, SubmitButton } from '../../../components/ui/buttons';
import { translate } from '../../../helpers/l10n';
/*::
@@ -153,9 +153,9 @@ export default class NewOrganizationForm extends React.PureComponent {
{loading ? (
<i className="spinner text-middle" />
) : (
- <button className="text-middle" disabled={!valid} type="submit">
+ <SubmitButton className="text-middle" disabled={!valid}>
{translate('create')}
- </button>
+ </SubmitButton>
)}
{!unique && (
<span className="big-spacer-left text-danger text-middle">
diff --git a/server/sonar-web/src/main/js/apps/tutorials/onboarding/NewProjectForm.js b/server/sonar-web/src/main/js/apps/tutorials/onboarding/NewProjectForm.js
index 0e7a96a5b46..cde4629a0a8 100644
--- a/server/sonar-web/src/main/js/apps/tutorials/onboarding/NewProjectForm.js
+++ b/server/sonar-web/src/main/js/apps/tutorials/onboarding/NewProjectForm.js
@@ -20,7 +20,7 @@
// @flow
import React from 'react';
import { createProject, deleteProject } from '../../../api/components';
-import { DeleteButton } from '../../../components/ui/buttons';
+import { DeleteButton, SubmitButton } from '../../../components/ui/buttons';
import { translate } from '../../../helpers/l10n';
/*::
@@ -123,8 +123,8 @@ export default class NewProjectForm extends React.PureComponent {
<input
autoFocus={true}
className="input-large spacer-right text-middle"
- minLength={1}
maxLength={400}
+ minLength={1}
onChange={this.handleProjectKeyChange}
required={true}
type="text"
@@ -133,9 +133,9 @@ export default class NewProjectForm extends React.PureComponent {
{loading ? (
<i className="spinner text-middle" />
) : (
- <button className="text-middle" disabled={!valid}>
+ <SubmitButton className="text-middle" disabled={!valid}>
{translate('Done')}
- </button>
+ </SubmitButton>
)}
<div className="note spacer-top abs-width-300">
{translate('onboarding.project_key_requirement')}
diff --git a/server/sonar-web/src/main/js/apps/tutorials/onboarding/OrganizationStep.js b/server/sonar-web/src/main/js/apps/tutorials/onboarding/OrganizationStep.js
index e7a5f9b938f..af4bc9d6547 100644
--- a/server/sonar-web/src/main/js/apps/tutorials/onboarding/OrganizationStep.js
+++ b/server/sonar-web/src/main/js/apps/tutorials/onboarding/OrganizationStep.js
@@ -27,6 +27,7 @@ import AlertSuccessIcon from '../../../components/icons-components/AlertSuccessI
import { getOrganizations } from '../../../api/organizations';
import Select from '../../../components/controls/Select';
import { translate } from '../../../helpers/l10n';
+import { Button } from '../../../components/ui/buttons';
/*::
type Props = {|
@@ -137,8 +138,7 @@ export default class OrganizationStep extends React.PureComponent {
this.setState({ existingOrganization: value });
};
- handleContinueClick = (event /*: Event */) => {
- event.preventDefault();
+ handleContinueClick = () => {
const organization = this.getSelectedOrganization();
if (organization) {
this.props.onContinue(organization);
@@ -234,9 +234,9 @@ export default class OrganizationStep extends React.PureComponent {
{this.getSelectedOrganization() != null &&
!this.state.loading && (
<div className="big-spacer-top">
- <button className="js-continue" onClick={this.handleContinueClick} type="button">
+ <Button className="js-continue" onClick={this.handleContinueClick}>
{translate('continue')}
- </button>
+ </Button>
</div>
)}
</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 d29500382e0..6ef86cd7b3a 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
@@ -24,7 +24,7 @@ import Step from './Step';
import { getTokens, generateToken, revokeToken } from '../../../api/user-tokens';
import AlertErrorIcon from '../../../components/icons-components/AlertErrorIcon';
import AlertSuccessIcon from '../../../components/icons-components/AlertSuccessIcon';
-import { DeleteButton } from '../../../components/ui/buttons';
+import { DeleteButton, SubmitButton, Button } from '../../../components/ui/buttons';
import { translate } from '../../../helpers/l10n';
/*::
@@ -130,8 +130,7 @@ export default class TokenStep extends React.PureComponent {
}
};
- handleContinueClick = (event /*: Event */) => {
- event.preventDefault();
+ handleContinueClick = () => {
const token = this.getToken();
if (token) {
this.props.onContinue(token);
@@ -184,9 +183,9 @@ export default class TokenStep extends React.PureComponent {
{this.state.loading ? (
<i className="spinner text-middle" />
) : (
- <button className="text-middle" disabled={!this.state.tokenName} type="submit">
+ <SubmitButton className="text-middle" disabled={!this.state.tokenName}>
{translate('onboarding.token.generate')}
- </button>
+ </SubmitButton>
)}
</form>
</div>
@@ -263,9 +262,9 @@ export default class TokenStep extends React.PureComponent {
{this.canContinue() && (
<div className="big-spacer-top">
- <button className="js-continue" onClick={this.handleContinueClick} type="button">
+ <Button className="js-continue" onClick={this.handleContinueClick}>
{translate('continue')}
- </button>
+ </Button>
</div>
)}
</div>
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/onboarding/__tests__/OrganizationStep-test.js
index 01add62b539..01a5b711a2a 100644
--- a/server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/OrganizationStep-test.js
+++ b/server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/OrganizationStep-test.js
@@ -78,7 +78,7 @@ it('works with existing organization', async () => {
.first()
.prop('onChange')({ value: 'another' });
wrapper.update();
- click(wrapper.find('.js-continue'));
+ click(wrapper.find('[className="js-continue"]'));
expect(onContinue).toBeCalledWith('another');
});
@@ -98,6 +98,6 @@ it('works with new organization', async () => {
click(wrapper.find('.js-new'));
wrapper.find('NewOrganizationForm').prop('onDone')('new');
wrapper.update();
- click(wrapper.find('.js-continue'));
+ click(wrapper.find('[className="js-continue"]'));
expect(onContinue).toBeCalledWith('new');
});
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 d336accbca7..4f439b440b5 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
@@ -38,9 +38,9 @@ it('generates token', async () => {
<TokenStep
currentUser={currentUser}
finished={false}
- open={true}
onContinue={jest.fn()}
onOpen={jest.fn()}
+ open={true}
stepNumber={1}
/>
);
@@ -58,9 +58,9 @@ it('revokes token', async () => {
<TokenStep
currentUser={currentUser}
finished={false}
- open={true}
onContinue={jest.fn()}
onOpen={jest.fn()}
+ open={true}
stepNumber={1}
/>
);
@@ -80,15 +80,15 @@ it('continues', async () => {
<TokenStep
currentUser={currentUser}
finished={false}
- open={true}
onContinue={onContinue}
onOpen={jest.fn()}
+ open={true}
stepNumber={1}
/>
);
await new Promise(setImmediate);
wrapper.setState({ token: 'abcd1234', tokenName: 'my token' });
- click(wrapper.find('.js-continue'));
+ click(wrapper.find('[className="js-continue"]'));
expect(onContinue).toBeCalledWith('abcd1234');
});
@@ -98,14 +98,14 @@ it('uses existing token', async () => {
<TokenStep
currentUser={currentUser}
finished={false}
- open={true}
onContinue={onContinue}
onOpen={jest.fn()}
+ open={true}
stepNumber={1}
/>
);
await new Promise(setImmediate);
wrapper.setState({ existingToken: 'abcd1234', selection: 'use-existing' });
- click(wrapper.find('.js-continue'));
+ click(wrapper.find('[className="js-continue"]'));
expect(onContinue).toBeCalledWith('abcd1234');
});
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/onboarding/__tests__/__snapshots__/NewOrganizationForm-test.js.snap
index 1e75378df8b..a7f34e63364 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/onboarding/__tests__/__snapshots__/NewOrganizationForm-test.js.snap
@@ -19,13 +19,26 @@ exports[`creates new organization 1`] = `
type="text"
value=""
/>
- <button
+ <SubmitButton
className="text-middle"
disabled={true}
- type="submit"
>
- create
- </button>
+ <Button
+ className="text-middle"
+ disabled={true}
+ preventDefault={false}
+ type="submit"
+ >
+ <button
+ className="button text-middle"
+ disabled={true}
+ onClick={[Function]}
+ type="submit"
+ >
+ create
+ </button>
+ </Button>
+ </SubmitButton>
<div
className="note spacer-top abs-width-300"
>
@@ -215,13 +228,26 @@ exports[`deletes organization 3`] = `
type="text"
value=""
/>
- <button
+ <SubmitButton
className="text-middle"
disabled={true}
- type="submit"
>
- create
- </button>
+ <Button
+ className="text-middle"
+ disabled={true}
+ preventDefault={false}
+ type="submit"
+ >
+ <button
+ className="button text-middle"
+ disabled={true}
+ onClick={[Function]}
+ type="submit"
+ >
+ create
+ </button>
+ </Button>
+ </SubmitButton>
<div
className="note spacer-top abs-width-300"
>
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/onboarding/__tests__/__snapshots__/NewProjectForm-test.js.snap
index a6682fb7870..f2c1036adeb 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/onboarding/__tests__/__snapshots__/NewProjectForm-test.js.snap
@@ -26,12 +26,26 @@ exports[`creates new project 1`] = `
type="text"
value=""
/>
- <button
+ <SubmitButton
className="text-middle"
disabled={true}
>
- Done
- </button>
+ <Button
+ className="text-middle"
+ disabled={true}
+ preventDefault={false}
+ type="submit"
+ >
+ <button
+ className="button text-middle"
+ disabled={true}
+ onClick={[Function]}
+ type="submit"
+ >
+ Done
+ </button>
+ </Button>
+ </SubmitButton>
<div
className="note spacer-top abs-width-300"
>
@@ -264,12 +278,26 @@ exports[`deletes project 3`] = `
type="text"
value=""
/>
- <button
+ <SubmitButton
className="text-middle"
disabled={true}
>
- Done
- </button>
+ <Button
+ className="text-middle"
+ disabled={true}
+ preventDefault={false}
+ type="submit"
+ >
+ <button
+ className="button text-middle"
+ disabled={true}
+ onClick={[Function]}
+ type="submit"
+ >
+ Done
+ </button>
+ </Button>
+ </SubmitButton>
<div
className="note spacer-top abs-width-300"
>
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 58074b468a3..9b991a92305 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
@@ -67,13 +67,26 @@ exports[`generates token 1`] = `
type="text"
value=""
/>
- <button
+ <SubmitButton
className="text-middle"
disabled={true}
- type="submit"
>
- onboarding.token.generate
- </button>
+ <Button
+ className="text-middle"
+ disabled={true}
+ preventDefault={false}
+ type="submit"
+ >
+ <button
+ className="button text-middle"
+ disabled={true}
+ onClick={[Function]}
+ type="submit"
+ >
+ onboarding.token.generate
+ </button>
+ </Button>
+ </SubmitButton>
</form>
</div>
</div>
@@ -299,13 +312,18 @@ exports[`generates token 3`] = `
<div
className="big-spacer-top"
>
- <button
+ <Button
className="js-continue"
onClick={[Function]}
- type="button"
>
- continue
- </button>
+ <button
+ className="button js-continue"
+ onClick={[Function]}
+ type="button"
+ >
+ continue
+ </button>
+ </Button>
</div>
</div>
</div>
@@ -410,13 +428,18 @@ exports[`revokes token 1`] = `
<div
className="big-spacer-top"
>
- <button
+ <Button
className="js-continue"
onClick={[Function]}
- type="button"
>
- continue
- </button>
+ <button
+ className="button js-continue"
+ onClick={[Function]}
+ type="button"
+ >
+ continue
+ </button>
+ </Button>
</div>
</div>
</div>
@@ -490,13 +513,18 @@ exports[`revokes token 2`] = `
<div
className="big-spacer-top"
>
- <button
+ <Button
className="js-continue"
onClick={[Function]}
- type="button"
>
- continue
- </button>
+ <button
+ className="button js-continue"
+ onClick={[Function]}
+ type="button"
+ >
+ continue
+ </button>
+ </Button>
</div>
</div>
</div>
@@ -571,13 +599,26 @@ exports[`revokes token 3`] = `
type="text"
value=""
/>
- <button
+ <SubmitButton
className="text-middle"
disabled={true}
- type="submit"
>
- onboarding.token.generate
- </button>
+ <Button
+ className="text-middle"
+ disabled={true}
+ preventDefault={false}
+ type="submit"
+ >
+ <button
+ className="button text-middle"
+ disabled={true}
+ onClick={[Function]}
+ type="submit"
+ >
+ onboarding.token.generate
+ </button>
+ </Button>
+ </SubmitButton>
</form>
</div>
</div>
diff --git a/server/sonar-web/src/main/js/components/controls/GlobalMessages.js b/server/sonar-web/src/main/js/components/controls/GlobalMessages.js
index 6e5df20efcb..0d0782009b7 100644
--- a/server/sonar-web/src/main/js/components/controls/GlobalMessages.js
+++ b/server/sonar-web/src/main/js/components/controls/GlobalMessages.js
@@ -21,6 +21,7 @@ import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import { ERROR, SUCCESS } from '../../store/globalMessages/duck';
+import { Button } from '../ui/buttons';
export default class GlobalMessages extends React.PureComponent {
static propTypes = {
@@ -40,14 +41,13 @@ export default class GlobalMessages extends React.PureComponent {
'process-spinner-success': message.level === SUCCESS
});
return (
- <div key={message.id} className={className}>
+ <div className={className} key={message.id}>
{message.message}
- <button
+ <Button
className="process-spinner-close"
- type="button"
onClick={() => this.props.closeGlobalMessage(message.id)}>
<i className="icon-close" />
- </button>
+ </Button>
</div>
);
};
diff --git a/server/sonar-web/src/main/js/components/controls/Toggle.js b/server/sonar-web/src/main/js/components/controls/Toggle.js
index b1b18b3a78b..4cbabfece09 100644
--- a/server/sonar-web/src/main/js/components/controls/Toggle.js
+++ b/server/sonar-web/src/main/js/components/controls/Toggle.js
@@ -20,6 +20,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
+import { Button } from '../ui/buttons';
import './styles.css';
export default class Toggle extends React.PureComponent {
@@ -29,13 +30,11 @@ export default class Toggle extends React.PureComponent {
onChange: PropTypes.func
};
- handleClick(e, value) {
- e.preventDefault();
- e.currentTarget.blur();
+ handleClick = value => {
if (this.props.onChange) {
this.props.onChange(!value);
}
- }
+ };
render() {
const { value } = this.props;
@@ -44,12 +43,12 @@ export default class Toggle extends React.PureComponent {
const className = classNames('boolean-toggle', { 'boolean-toggle-on': booleanValue });
return (
- <button
+ <Button
className={className}
name={this.props.name}
- onClick={e => this.handleClick(e, booleanValue)}>
+ onClick={() => this.handleClick(booleanValue)}>
<div className="boolean-toggle-handle" />
- </button>
+ </Button>
);
}
}
diff --git a/server/sonar-web/src/main/js/components/controls/__tests__/Toggle-test.js b/server/sonar-web/src/main/js/components/controls/__tests__/Toggle-test.js
index c371c10e730..3245074388a 100644
--- a/server/sonar-web/src/main/js/components/controls/__tests__/Toggle-test.js
+++ b/server/sonar-web/src/main/js/components/controls/__tests__/Toggle-test.js
@@ -23,12 +23,12 @@ import Toggle from '../Toggle';
import { click } from '../../../helpers/testUtils';
function getSample(props) {
- return <Toggle value={true} onChange={() => true} {...props} />;
+ return <Toggle onChange={() => true} value={true} {...props} />;
}
it('should render', () => {
const Toggle = shallow(getSample());
- expect(Toggle.is('button')).toBe(true);
+ expect(Toggle.is('Button')).toBe(true);
});
it('should call onChange', () => {
diff --git a/server/sonar-web/src/main/js/components/controls/styles.css b/server/sonar-web/src/main/js/components/controls/styles.css
index a68db8e3879..32fb0c19f36 100644
--- a/server/sonar-web/src/main/js/components/controls/styles.css
+++ b/server/sonar-web/src/main/js/components/controls/styles.css
@@ -81,7 +81,7 @@
width: 70px;
}
-.boolean-toggle {
+.button.boolean-toggle {
display: inline-block;
vertical-align: middle;
width: 48px;
@@ -95,11 +95,11 @@
transition: all 0.3s ease;
}
-.boolean-toggle:hover {
+.button.boolean-toggle:hover {
background-color: #fff;
}
-.boolean-toggle:focus {
+.button.boolean-toggle:focus {
border-color: var(--blue);
background-color: #f6f6f6;
}
@@ -114,20 +114,20 @@
transition: transform 0.3s cubic-bezier(0.87, -0.41, 0.19, 1.44), border 0.3s ease;
}
-.boolean-toggle-on {
+.button.boolean-toggle-on {
border-color: var(--darkBlue);
background-color: var(--darkBlue);
}
-.boolean-toggle-on:hover {
+.button.boolean-toggle-on:hover {
background-color: var(--darkBlue);
}
-.boolean-toggle-on:focus {
+.button.boolean-toggle-on:focus {
background-color: var(--darkBlue);
}
-.boolean-toggle-on .boolean-toggle-handle {
+.button.boolean-toggle-on .boolean-toggle-handle {
border-color: #f6f6f6;
transform: translateX(var(--controlHeight));
}
diff --git a/server/sonar-web/src/main/js/components/issue/components/IssueMessage.js b/server/sonar-web/src/main/js/components/issue/components/IssueMessage.js
index 1f8a9cb2ae5..74bf39609ab 100644
--- a/server/sonar-web/src/main/js/components/issue/components/IssueMessage.js
+++ b/server/sonar-web/src/main/js/components/issue/components/IssueMessage.js
@@ -22,6 +22,7 @@ import React from 'react';
import PropTypes from 'prop-types';
import Tooltip from '../../controls/Tooltip';
import { translate, translateWithParameters } from '../../../helpers/l10n';
+import { Button } from '../../ui/buttons';
export default class IssueMessage extends React.PureComponent {
/*:: props: {
@@ -36,8 +37,7 @@ export default class IssueMessage extends React.PureComponent {
workspace: PropTypes.object.isRequired
};
- handleClick = (e /*: MouseEvent */) => {
- e.preventDefault();
+ handleClick = () => {
this.context.workspace.openRule({
key: this.props.rule,
organization: this.props.organization
@@ -48,9 +48,9 @@ export default class IssueMessage extends React.PureComponent {
return (
<div className="issue-message">
{this.props.message}
- <button
- className="button-link issue-rule icon-ellipsis-h little-spacer-left"
+ <Button
aria-label={translate('issue.rule_details')}
+ className="button-link issue-rule icon-ellipsis-h little-spacer-left"
onClick={this.handleClick}
/>
{this.props.engine && (
diff --git a/server/sonar-web/src/main/js/components/issue/components/__tests__/__snapshots__/IssueMessage-test.js.snap b/server/sonar-web/src/main/js/components/issue/components/__tests__/__snapshots__/IssueMessage-test.js.snap
index 9bf92a16d15..91587572e88 100644
--- a/server/sonar-web/src/main/js/components/issue/components/__tests__/__snapshots__/IssueMessage-test.js.snap
+++ b/server/sonar-web/src/main/js/components/issue/components/__tests__/__snapshots__/IssueMessage-test.js.snap
@@ -5,7 +5,7 @@ exports[`should render with the message and a link to open the rule 1`] = `
className="issue-message"
>
Reduce the number of conditional operators (4) used in the expression
- <button
+ <Button
aria-label="issue.rule_details"
className="button-link issue-rule icon-ellipsis-h little-spacer-left"
onClick={[Function]}
diff --git a/server/sonar-web/src/main/js/components/ui/buttons.css b/server/sonar-web/src/main/js/components/ui/buttons.css
index 3df0420fc49..ea2ff4b6b13 100644
--- a/server/sonar-web/src/main/js/components/ui/buttons.css
+++ b/server/sonar-web/src/main/js/components/ui/buttons.css
@@ -17,10 +17,209 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+.button {
+ display: inline-block;
+ vertical-align: baseline;
+ height: var(--controlHeight);
+ line-height: calc(var(--controlHeight) - 2px);
+ padding: 0 12px;
+ border: 1px solid var(--darkBlue);
+ border-radius: 2px;
+ box-sizing: border-box;
+ background: transparent;
+ color: var(--darkBlue);
+ font-weight: 600;
+ font-size: var(--smallFontSize);
+ text-align: center;
+ text-decoration: none;
+ cursor: pointer;
+ outline: none;
+ transition: border-color 0.2s ease, box-shadow 0.2s ease;
+}
+
+.button:hover,
+.button.button-active {
+ background: var(--darkBlue);
+ color: #fff;
+}
+
+.button:active {
+ box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
+}
+
+.button:focus {
+ box-shadow: 0 0 0 3px rgba(35, 106, 151, 0.25);
+}
+
+.button.disabled,
+.button:disabled,
+.button:disabled:hover,
+.button:disabled:active,
+.button:disabled:focus {
+ color: var(--disableGrayText) !important;
+ border-color: var(--disableGrayBorder) !important;
+ background: var(--disableGrayBg) !important;
+ cursor: not-allowed !important;
+ box-shadow: none !important;
+}
+
+.button svg {
+ margin-top: calc((var(--controlHeight) - 16px - 2px) / 2);
+}
+
+/* #region .button-red */
+.button-red {
+ border-color: var(--red);
+ color: var(--red);
+}
+
+.button-red:hover,
+.button-red.active {
+ background: var(--red);
+ color: #fff;
+}
-/* use double selector .button-icon.button-icon to increase the specificity */
+.button-red:focus {
+ box-shadow: 0 0 0 3px rgba(212, 51, 63, 0.25);
+}
+/* #endregion */
+
+/* #region .button-success */
+.button-success {
+ border-color: var(--green);
+ color: var(--green);
+}
+
+.button-success:hover,
+.button-success.active {
+ background: var(--green);
+ color: #fff;
+}
+
+.button-success:focus {
+ box-shadow: 0 0 0 3px rgba(0, 170, 0, 0.25);
+}
+/* #endregion */
+
+/* #region .button-grey */
+.button-grey {
+ border-color: var(--gray71);
+ color: var(--secondFontColor);
+}
+
+.button-grey:hover,
+.button-grey.active {
+ background: var(--gray71);
+ color: #ffffff;
+}
+
+.button-grey:focus {
+ box-shadow: 0 0 0 3px rgba(180, 180, 180, 0.25);
+}
+/* #endregion */
+
+/* #region .button-link */
+.button-link {
+ display: inline;
+ height: auto; /* Keep this to not inherit the height from .button */
+ margin: 0;
+ padding: 0;
+ border: none;
+ background: transparent;
+ color: var(--darkBlue);
+ font-weight: 400;
+ font-size: inherit;
+ line-height: inherit;
+ transition: all 0.2s ease;
+}
+
+.button-link svg {
+ margin-top: 0;
+}
+
+.button-link:hover {
+ background: transparent;
+ color: var(--blue);
+}
+
+.button-link:active {
+ box-shadow: none;
+ outline: thin dotted #ccc;
+}
+
+.button-link:disabled,
+.button-link:disabled:hover,
+.button-link:disabled:active,
+.button-link:disabled:focus {
+ color: var(--secondFontColor);
+ background: transparent !important;
+ cursor: default;
+}
+/* #endregion */
+
+.button-small {
+ height: var(--smallControlHeight);
+ line-height: 18px;
+ padding: 0 6px;
+ font-size: 11px;
+}
+
+.button-small > svg {
+ margin-top: 2px;
+}
+
+/* #region .button-group */
+.button-group {
+ display: inline-block;
+ vertical-align: middle;
+ font-size: 0;
+ white-space: nowrap;
+}
+
+.button-group > button,
+.button-group > .button {
+ position: relative;
+ z-index: var(--normalZIndex);
+ display: inline-block;
+ vertical-align: middle;
+ margin: 0;
+ cursor: pointer;
+}
+
+.button-group > .button:hover:not(:disabled),
+.button-group > .button:focus:not(:disabled),
+.button-group > .button:active:not(:disabled),
+.button-group > .button.active:not(:disabled) {
+ z-index: var(--aboveNormalZIndex);
+}
+
+.button-group > .button:disabled {
+ z-index: var(--belowNormalZIndex);
+}
+
+.button-group > .button:not(:first-child) {
+ border-top-left-radius: 0;
+ border-bottom-left-radius: 0;
+}
+
+.button-group > .button:not(:last-child):not(.dropdown-toggle) {
+ border-top-right-radius: 0;
+ border-bottom-right-radius: 0;
+}
+
+.button-group > .button + .button {
+ margin-left: -1px;
+}
+
+.button-group > a:not(.button) {
+ vertical-align: middle;
+ margin: 0 8px;
+ font-size: var(--smallFontSize);
+}
+/* #endregion */
-.button-icon.button-icon {
+/* #region .button-icon */
+.button-icon {
display: inline-flex;
justify-content: center;
align-items: center;
@@ -32,32 +231,33 @@
color: inherit;
}
-.button-icon.button-icon.button-small {
+.button-icon.button-small {
width: var(--smallControlHeight);
height: var(--smallControlHeight);
padding: 0;
}
-.button-icon.button-icon.button-small svg {
+.button-icon.button-small svg {
margin-top: 0;
}
-.button-icon.button-icon.button-tiny {
+.button-icon.button-tiny {
width: var(--tinyControlHeight);
height: var(--tinyControlHeight);
padding: 0;
}
-.button-icon.button-icon.button-tiny svg {
+.button-icon.button-tiny svg {
margin-top: 0;
}
-.button-icon.button-icon:hover,
-.button-icon.button-icon:focus {
+.button-icon:hover,
+.button-icon:focus {
background-color: currentColor;
}
-.button-icon.button-icon:hover svg,
-.button-icon.button-icon:focus svg {
+.button-icon:hover svg,
+.button-icon:focus svg {
color: #fff;
}
+/* #endregion */