]> source.dussan.org Git - sonarqube.git/commitdiff
create and use Button component (#3087)
authorStas Vilchik <stas.vilchik@sonarsource.com>
Wed, 21 Feb 2018 15:36:42 +0000 (16:36 +0100)
committerGitHub <noreply@github.com>
Wed, 21 Feb 2018 15:36:42 +0000 (16:36 +0100)
201 files changed:
server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/LoginPage.java
server/sonar-web/src/main/js/api/quality-profiles.ts
server/sonar-web/src/main/js/app/components/nav/settings/SettingsEditionsNotif.tsx
server/sonar-web/src/main/js/app/components/nav/settings/__tests__/__snapshots__/SettingsEditionsNotif-test.tsx.snap
server/sonar-web/src/main/js/apps/account/organizations/CreateOrganizationForm.tsx
server/sonar-web/src/main/js/apps/account/organizations/UserOrganizations.tsx
server/sonar-web/src/main/js/apps/background-tasks/components/WorkersForm.tsx
server/sonar-web/src/main/js/apps/background-tasks/components/__tests__/__snapshots__/WorkersForm-test.tsx.snap
server/sonar-web/src/main/js/apps/coding-rules/components/ActivationButton.tsx
server/sonar-web/src/main/js/apps/coding-rules/components/ActivationFormModal.tsx
server/sonar-web/src/main/js/apps/coding-rules/components/BulkChange.tsx
server/sonar-web/src/main/js/apps/coding-rules/components/BulkChangeModal.tsx
server/sonar-web/src/main/js/apps/coding-rules/components/CustomRuleButton.tsx
server/sonar-web/src/main/js/apps/coding-rules/components/CustomRuleFormModal.tsx
server/sonar-web/src/main/js/apps/coding-rules/components/RemoveExtendedDescriptionModal.tsx
server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetails.tsx
server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetailsCustomRules.tsx
server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetailsDescription.tsx
server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetailsMeta.tsx
server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetailsProfiles.tsx
server/sonar-web/src/main/js/apps/coding-rules/components/RuleListItem.tsx
server/sonar-web/src/main/js/apps/custom-measures/components/CreateButton.tsx
server/sonar-web/src/main/js/apps/custom-measures/components/Form.tsx
server/sonar-web/src/main/js/apps/custom-measures/components/__tests__/Form-test.tsx
server/sonar-web/src/main/js/apps/custom-measures/components/__tests__/__snapshots__/CreateButton-test.tsx.snap
server/sonar-web/src/main/js/apps/custom-measures/components/__tests__/__snapshots__/Form-test.tsx.snap
server/sonar-web/src/main/js/apps/custom-metrics/components/CreateButton.tsx
server/sonar-web/src/main/js/apps/custom-metrics/components/Form.tsx
server/sonar-web/src/main/js/apps/custom-metrics/components/__tests__/Form-test.tsx
server/sonar-web/src/main/js/apps/custom-metrics/components/__tests__/__snapshots__/CreateButton-test.tsx.snap
server/sonar-web/src/main/js/apps/custom-metrics/components/__tests__/__snapshots__/Form-test.tsx.snap
server/sonar-web/src/main/js/apps/groups/components/EditMembers.tsx
server/sonar-web/src/main/js/apps/groups/components/Form.tsx
server/sonar-web/src/main/js/apps/groups/components/Header.tsx
server/sonar-web/src/main/js/apps/groups/components/__tests__/EditGroup-test.tsx
server/sonar-web/src/main/js/apps/groups/components/__tests__/EditMembers-test.tsx
server/sonar-web/src/main/js/apps/groups/components/__tests__/Form-test.tsx
server/sonar-web/src/main/js/apps/groups/components/__tests__/Header-test.tsx
server/sonar-web/src/main/js/apps/groups/components/__tests__/__snapshots__/EditGroup-test.tsx.snap
server/sonar-web/src/main/js/apps/groups/components/__tests__/__snapshots__/EditMembers-test.tsx.snap
server/sonar-web/src/main/js/apps/groups/components/__tests__/__snapshots__/Form-test.tsx.snap
server/sonar-web/src/main/js/apps/groups/components/__tests__/__snapshots__/Header-test.tsx.snap
server/sonar-web/src/main/js/apps/maintenance/components/App.tsx
server/sonar-web/src/main/js/apps/maintenance/components/__tests__/App-test.tsx
server/sonar-web/src/main/js/apps/maintenance/components/__tests__/__snapshots__/App-test.tsx.snap
server/sonar-web/src/main/js/apps/marketplace/PendingActions.tsx
server/sonar-web/src/main/js/apps/marketplace/__tests__/__snapshots__/PendingActions-test.tsx.snap
server/sonar-web/src/main/js/apps/marketplace/components/EditionBox.tsx
server/sonar-web/src/main/js/apps/marketplace/components/LicenseEditionForm.tsx
server/sonar-web/src/main/js/apps/marketplace/components/PluginActions.tsx
server/sonar-web/src/main/js/apps/marketplace/components/PluginChangeLogButton.tsx
server/sonar-web/src/main/js/apps/marketplace/components/PluginUpdateButton.tsx
server/sonar-web/src/main/js/apps/marketplace/components/UninstallEditionForm.tsx
server/sonar-web/src/main/js/apps/marketplace/components/__tests__/LicenseEditionForm-test.tsx
server/sonar-web/src/main/js/apps/marketplace/components/__tests__/UninstallEditionForm-test.tsx
server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/EditionBox-test.tsx.snap
server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/LicenseEditionForm-test.tsx.snap
server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/UninstallEditionForm-test.tsx.snap
server/sonar-web/src/main/js/apps/overview/badges/BadgeButton.tsx
server/sonar-web/src/main/js/apps/overview/badges/BadgesModal.tsx
server/sonar-web/src/main/js/apps/overview/badges/__tests__/BadgeButton-test.tsx
server/sonar-web/src/main/js/apps/overview/badges/__tests__/BadgesModal-test.tsx
server/sonar-web/src/main/js/apps/overview/badges/__tests__/__snapshots__/BadgeButton-test.tsx.snap
server/sonar-web/src/main/js/apps/overview/badges/__tests__/__snapshots__/BadgesModal-test.tsx.snap
server/sonar-web/src/main/js/apps/overview/meta/MetaTags.tsx
server/sonar-web/src/main/js/apps/overview/meta/__tests__/MetaTags-test.tsx
server/sonar-web/src/main/js/apps/overview/meta/__tests__/__snapshots__/MetaTags-test.tsx.snap
server/sonar-web/src/main/js/apps/permission-templates/components/Form.tsx
server/sonar-web/src/main/js/apps/permission-templates/components/Header.tsx
server/sonar-web/src/main/js/apps/permissions/project/components/ApplyTemplate.tsx
server/sonar-web/src/main/js/apps/permissions/project/components/PageHeader.tsx
server/sonar-web/src/main/js/apps/portfolio/components/Subscription.tsx
server/sonar-web/src/main/js/apps/portfolio/components/__tests__/__snapshots__/Subscription-test.tsx.snap
server/sonar-web/src/main/js/apps/projectBranches/components/DeleteBranchModal.tsx
server/sonar-web/src/main/js/apps/projectBranches/components/RenameBranchModal.tsx
server/sonar-web/src/main/js/apps/projectBranches/components/SettingForm.tsx
server/sonar-web/src/main/js/apps/projectBranches/components/__tests__/DeleteBranchModal-test.tsx
server/sonar-web/src/main/js/apps/projectBranches/components/__tests__/LongBranchesPattern-test.tsx
server/sonar-web/src/main/js/apps/projectBranches/components/__tests__/RenameBranchModal-test.tsx
server/sonar-web/src/main/js/apps/projectBranches/components/__tests__/SettingForm-test.tsx
server/sonar-web/src/main/js/apps/projectBranches/components/__tests__/__snapshots__/DeleteBranchModal-test.tsx.snap
server/sonar-web/src/main/js/apps/projectBranches/components/__tests__/__snapshots__/RenameBranchModal-test.tsx.snap
server/sonar-web/src/main/js/apps/projectBranches/components/__tests__/__snapshots__/SettingForm-test.tsx.snap
server/sonar-web/src/main/js/apps/projects/components/ClearAll.tsx
server/sonar-web/src/main/js/apps/projects/components/__tests__/ClearAll-test.tsx
server/sonar-web/src/main/js/apps/projects/components/__tests__/ProjectsSortingSelect-test.tsx
server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ClearAll-test.tsx.snap
server/sonar-web/src/main/js/apps/projectsManagement/BulkApplyTemplateModal.tsx
server/sonar-web/src/main/js/apps/projectsManagement/ChangeVisibilityForm.tsx
server/sonar-web/src/main/js/apps/projectsManagement/CreateProjectForm.tsx
server/sonar-web/src/main/js/apps/projectsManagement/DeleteModal.tsx
server/sonar-web/src/main/js/apps/projectsManagement/Header.tsx
server/sonar-web/src/main/js/apps/projectsManagement/RestoreAccessModal.tsx
server/sonar-web/src/main/js/apps/projectsManagement/Search.tsx
server/sonar-web/src/main/js/apps/projectsManagement/__tests__/BulkApplyTemplateModal-test.tsx
server/sonar-web/src/main/js/apps/projectsManagement/__tests__/DeleteModal-test.tsx
server/sonar-web/src/main/js/apps/projectsManagement/__tests__/__snapshots__/BulkApplyTemplateModal-test.tsx.snap
server/sonar-web/src/main/js/apps/projectsManagement/__tests__/__snapshots__/ChangeVisibilityForm-test.tsx.snap
server/sonar-web/src/main/js/apps/projectsManagement/__tests__/__snapshots__/CreateProjectForm-test.tsx.snap
server/sonar-web/src/main/js/apps/projectsManagement/__tests__/__snapshots__/DeleteModal-test.tsx.snap
server/sonar-web/src/main/js/apps/projectsManagement/__tests__/__snapshots__/Header-test.tsx.snap
server/sonar-web/src/main/js/apps/projectsManagement/__tests__/__snapshots__/Search-test.tsx.snap
server/sonar-web/src/main/js/apps/quality-gates/components/Condition.tsx
server/sonar-web/src/main/js/apps/quality-gates/components/CopyQualityGateForm.tsx
server/sonar-web/src/main/js/apps/quality-gates/components/CreateQualityGateForm.tsx
server/sonar-web/src/main/js/apps/quality-gates/components/DeleteConditionForm.tsx
server/sonar-web/src/main/js/apps/quality-gates/components/DeleteQualityGateForm.tsx
server/sonar-web/src/main/js/apps/quality-gates/components/DetailsHeader.tsx
server/sonar-web/src/main/js/apps/quality-gates/components/ListHeader.tsx
server/sonar-web/src/main/js/apps/quality-gates/components/RenameQualityGateForm.tsx
server/sonar-web/src/main/js/apps/quality-profiles/changelog/ChangelogSearch.tsx
server/sonar-web/src/main/js/apps/quality-profiles/changelog/__tests__/ChangelogSearch-test.tsx
server/sonar-web/src/main/js/apps/quality-profiles/components/CopyProfileForm.tsx
server/sonar-web/src/main/js/apps/quality-profiles/components/DeleteProfileForm.tsx
server/sonar-web/src/main/js/apps/quality-profiles/components/RenameProfileForm.tsx
server/sonar-web/src/main/js/apps/quality-profiles/details/ChangeParentForm.tsx
server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileInheritance.tsx
server/sonar-web/src/main/js/apps/quality-profiles/details/ProfilePermissions.tsx
server/sonar-web/src/main/js/apps/quality-profiles/details/ProfilePermissionsForm.tsx
server/sonar-web/src/main/js/apps/quality-profiles/details/ProfilePermissionsGroup.tsx
server/sonar-web/src/main/js/apps/quality-profiles/details/ProfilePermissionsUser.tsx
server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileProjects.tsx
server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfilePermissions-test.tsx
server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfilePermissionsGroup-test.tsx
server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfilePermissionsUser-test.tsx
server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfilePermissions-test.tsx.snap
server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfilePermissionsForm-test.tsx.snap
server/sonar-web/src/main/js/apps/quality-profiles/home/CreateProfileForm.tsx
server/sonar-web/src/main/js/apps/quality-profiles/home/PageHeader.tsx
server/sonar-web/src/main/js/apps/quality-profiles/home/RestoreProfileForm.tsx
server/sonar-web/src/main/js/apps/sessions/components/LoginForm.tsx
server/sonar-web/src/main/js/apps/sessions/components/__tests__/__snapshots__/LoginForm-test.tsx.snap
server/sonar-web/src/main/js/apps/system/components/ChangeLogLevelForm.tsx
server/sonar-web/src/main/js/apps/system/components/PageActions.tsx
server/sonar-web/src/main/js/apps/system/components/__tests__/__snapshots__/ChangeLogLevelForm-test.tsx.snap
server/sonar-web/src/main/js/apps/system/components/__tests__/__snapshots__/PageActions-test.tsx.snap
server/sonar-web/src/main/js/apps/system/components/system-upgrade/SystemUpgradeNotif.tsx
server/sonar-web/src/main/js/apps/system/components/system-upgrade/__tests__/SystemUpgradeNotif-test.tsx
server/sonar-web/src/main/js/apps/system/components/system-upgrade/__tests__/__snapshots__/SystemUpgradeNotif-test.tsx.snap
server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/__snapshots__/NewOrganizationForm-test.js.snap
server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/__snapshots__/NewProjectForm-test.js.snap
server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/__snapshots__/TokenStep-test.js.snap
server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/__tests__/__snapshots__/Command-test.js.snap
server/sonar-web/src/main/js/apps/users/Header.tsx
server/sonar-web/src/main/js/apps/users/__tests__/__snapshots__/Header-test.tsx.snap
server/sonar-web/src/main/js/apps/users/components/DeactivateForm.tsx
server/sonar-web/src/main/js/apps/users/components/PasswordForm.tsx
server/sonar-web/src/main/js/apps/users/components/TokensForm.tsx
server/sonar-web/src/main/js/apps/users/components/TokensFormItem.tsx
server/sonar-web/src/main/js/apps/users/components/UserForm.tsx
server/sonar-web/src/main/js/components/common/FiltersHeader.tsx
server/sonar-web/src/main/js/components/common/RestartForm.tsx
server/sonar-web/src/main/js/components/controls/ActionsDropdown.tsx
server/sonar-web/src/main/js/components/controls/ClipboardButton.tsx
server/sonar-web/src/main/js/components/controls/ConfirmButton.tsx
server/sonar-web/src/main/js/components/controls/Dropdown.tsx
server/sonar-web/src/main/js/components/controls/SimpleModal.tsx
server/sonar-web/src/main/js/components/controls/__tests__/Dropdown-test.tsx
server/sonar-web/src/main/js/components/controls/__tests__/SimpleModal-test.tsx
server/sonar-web/src/main/js/components/controls/__tests__/__snapshots__/ClipboardButton-test.tsx.snap
server/sonar-web/src/main/js/components/controls/__tests__/__snapshots__/SimpleModal-test.tsx.snap
server/sonar-web/src/main/js/components/facet/FacetHeader.tsx
server/sonar-web/src/main/js/components/facet/__tests__/__snapshots__/FacetHeader-test.tsx.snap
server/sonar-web/src/main/js/components/ui/buttons.css
server/sonar-web/src/main/js/components/ui/buttons.tsx
server/sonar-web/src/main/js/helpers/testUtils.ts
tests/src/test/resources/authorisation/ProvisioningPermissionTest/should-be-able-to-provision-project.html
tests/src/test/resources/authorisation/ProvisioningPermissionTest/should-not-be-able-to-provision-project.html
tests/src/test/resources/authorisation/QualityProfileAdminPermissionTest/normal-user.html
tests/src/test/resources/authorisation/QualityProfileAdminPermissionTest/profile-admin.html
tests/src/test/resources/organization/OrganizationQualityProfilesUiTest/should_copy.html
tests/src/test/resources/organization/OrganizationQualityProfilesUiTest/should_create.html
tests/src/test/resources/organization/OrganizationQualityProfilesUiTest/should_delete.html
tests/src/test/resources/organization/OrganizationQualityProfilesUiTest/should_rename.html
tests/src/test/resources/organization/OrganizationQualityProfilesUiTest/should_restore.html
tests/src/test/resources/organization/OrganizationQualityProfilesUiTest/should_set_default.html
tests/src/test/resources/projectAdministration/BackgroundTasksTest/should_not_display_failing_and_search_and_filter_elements_on_project_level_page.html
tests/src/test/resources/projectAdministration/ProjectAdministrationTest/project-deletion/project-deletion.html
tests/src/test/resources/projectAdministration/ProjectPermissionsTest/test_project_permissions_page_shows_only_single_project.html
tests/src/test/resources/qualityGate/QualityGateUiTest/should-display-alerts-correctly-history-page.html
tests/src/test/resources/qualityGate/notifications/activate_notification_channels.html
tests/src/test/resources/qualityGate/notifications/email_configuration.html
tests/src/test/resources/qualityProfile/QualityProfilesUiTest/should_copy.html
tests/src/test/resources/qualityProfile/QualityProfilesUiTest/should_create.html
tests/src/test/resources/qualityProfile/QualityProfilesUiTest/should_delete.html
tests/src/test/resources/qualityProfile/QualityProfilesUiTest/should_rename.html
tests/src/test/resources/qualityProfile/QualityProfilesUiTest/should_restore.html
tests/src/test/resources/qualityProfile/QualityProfilesUiTest/should_set_default.html
tests/src/test/resources/user/ExternalAuthenticationTest/external-user-details.html
tests/src/test/resources/user/ExternalAuthenticationTest/external-user-details2.html
tests/src/test/resources/user/LocalAuthenticationTest/allow_users_to_sign_up.html
tests/src/test/resources/user/LocalAuthenticationTest/force-authentication.html
tests/src/test/resources/user/LocalAuthenticationTest/login_successful.html
tests/src/test/resources/user/LocalAuthenticationTest/login_wrong_password.html
tests/src/test/resources/user/LocalAuthenticationTest/redirect_to_login_when_not_enough_privilege.html
tests/src/test/resources/user/LocalAuthenticationTest/redirect_to_original_url_after_direct_login.html
tests/src/test/resources/user/LocalAuthenticationTest/redirect_to_original_url_after_indirect_login.html
tests/src/test/resources/user/LocalAuthenticationTest/redirect_to_original_url_with_parameters_after_direct_login.html
tests/src/test/resources/user/LocalAuthenticationTest/should_not_be_unlogged_when_going_to_login_page.html
tests/src/test/resources/user/MyAccountPageTest/should_display_no_projects.html
tests/src/test/resources/user/MyAccountPageTest/should_display_projects.html

index a11730125aab1e8e583b7577d89d79fbe1924fe3..3f765429d47ac5d7cc783a493b3a4e47350b9914 100644 (file)
@@ -22,7 +22,6 @@ package org.sonarqube.qa.util.pageobjects;
 import com.codeborne.selenide.Condition;
 import com.codeborne.selenide.Selenide;
 import com.codeborne.selenide.SelenideElement;
-import org.openqa.selenium.By;
 
 public class LoginPage {
 
@@ -49,7 +48,7 @@ public class LoginPage {
   public LoginPage submitWrongCredentials(String login, String password) {
     Selenide.$("#login").val(login);
     Selenide.$("#password").val(password);
-    Selenide.$(By.name("commit")).click();
+    Selenide.$("[type=submit]").click();
     return Selenide.page(LoginPage.class);
   }
 
@@ -60,7 +59,7 @@ public class LoginPage {
   private static <T> T submitCredentials(String login, String password, Class<T> expectedResultPage) {
     Selenide.$("#login").val(login);
     Selenide.$("#password").val(password);
-    Selenide.$(By.name("commit")).click();
+    Selenide.$("[type=submit]").click();
     Selenide.$("#login").should(Condition.disappear);
     return Selenide.page(expectedResultPage);
   }
index 736de049edb8d0639b2de06bb1754c384d644a32..e589638ac763447f871aec9d41ada9f40fa02013 100644 (file)
@@ -112,7 +112,7 @@ export function getProfileProjects(data: RequestData): Promise<any> {
 }
 
 export function getProfileInheritance(profileKey: string): Promise<any> {
-  return getJSON('/api/qualityprofiles/inheritance', { profileKey });
+  return getJSON('/api/qualityprofiles/inheritance', { profileKey }).catch(throwGlobalError);
 }
 
 export function setDefaultProfile(profileKey: string): Promise<void> {
@@ -135,8 +135,10 @@ export function changeProfileParent(profileKey: string, parentKey: string): Prom
   return post('/api/qualityprofiles/change_parent', { profileKey, parentKey });
 }
 
-export function getImporters(): Promise<any> {
-  return getJSON('/api/qualityprofiles/importers').then(r => r.importers);
+export function getImporters(): Promise<
+  Array<{ key: string; languages: Array<string>; name: string }>
+> {
+  return getJSON('/api/qualityprofiles/importers').then(r => r.importers, throwGlobalError);
 }
 
 export function getExporters(): Promise<any> {
index e5a5d8dfef4f7316d370b48a1da0f8fb2ca7e1a5..3c8850a95efc02fc2fe65bd94f3550263a343387 100644 (file)
@@ -21,6 +21,7 @@ import * as React from 'react';
 import { FormattedMessage } from 'react-intl';
 import NavBarNotif from '../../../../components/nav/NavBarNotif';
 import RestartForm from '../../../../components/common/RestartForm';
+import { Button } from '../../../../components/ui/buttons';
 import { dismissErrorMessage, Edition, EditionStatus } from '../../../../api/marketplace';
 import { translate, translateWithParameters } from '../../../../helpers/l10n';
 
@@ -86,6 +87,7 @@ export default class SettingsEditionsNotif extends React.PureComponent<Props, St
                   url: (
                     <a
                       href="https://redirect.sonarsource.com/doc/data-center-edition.html"
+                      rel="noopener noreferrer"
                       target="_blank">
                       {edition.name}
                     </a>
@@ -95,9 +97,9 @@ export default class SettingsEditionsNotif extends React.PureComponent<Props, St
             </span>
           )}
         {!preventRestart && (
-          <button className="js-restart spacer-left" onClick={this.handleOpenRestart} type="button">
+          <Button className="js-restart spacer-left" onClick={this.handleOpenRestart}>
             {translate('marketplace.restart')}
-          </button>
+          </Button>
         )}
         {!preventRestart &&
           this.state.openRestart && <RestartForm onClose={this.hanleCloseRestart} />}
index 1b1380978bfa844591b774a6ccace29275244850..f843a2d29bab95a61778d40e0ec30ea71351de7f 100644 (file)
@@ -30,13 +30,12 @@ exports[`should display a ready notification 1`] = `
   <span>
     marketplace.edition_status.AUTOMATIC_READY
   </span>
-  <button
+  <Button
     className="js-restart spacer-left"
     onClick={[Function]}
-    type="button"
   >
     marketplace.restart
-  </button>
+  </Button>
 </NavBarNotif>
 `;
 
index 53117d71a120209d00052cd0bf0f57481b0ec453..04254a29e1aa981e1062c11e108358667224722b 100644 (file)
@@ -25,6 +25,7 @@ import { createOrganization } from '../../organizations/actions';
 import { Organization } from '../../../app/types';
 import Modal from '../../../components/controls/Modal';
 import { translate } from '../../../helpers/l10n';
+import { SubmitButton, ResetButtonLink } from '../../../components/ui/buttons';
 
 interface DispatchProps {
   createOrganization: (fields: Partial<Organization>) => Promise<{ key: string }>;
@@ -136,16 +137,16 @@ class CreateOrganizationForm extends React.PureComponent<Props, State> {
                 <em className="mandatory">*</em>
               </label>
               <input
-                id="organization-name"
                 autoFocus={true}
+                disabled={this.state.loading}
+                id="organization-name"
+                maxLength={64}
+                minLength={2}
                 name="name"
+                onChange={this.handleNameChange}
                 required={true}
                 type="text"
-                minLength={2}
-                maxLength={64}
                 value={this.state.name}
-                disabled={this.state.loading}
-                onChange={this.handleNameChange}
               />
               <div className="modal-field-description">
                 {translate('organization.name.description')}
@@ -154,14 +155,14 @@ class CreateOrganizationForm extends React.PureComponent<Props, State> {
             <div className="modal-field">
               <label htmlFor="organization-key">{translate('organization.key')}</label>
               <input
+                disabled={this.state.loading}
                 id="organization-key"
+                maxLength={64}
+                minLength={2}
                 name="key"
+                onChange={this.handleKeyChange}
                 type="text"
-                minLength={2}
-                maxLength={64}
                 value={this.state.key}
-                disabled={this.state.loading}
-                onChange={this.handleKeyChange}
               />
               <div className="modal-field-description">
                 {translate('organization.key.description')}
@@ -170,13 +171,13 @@ class CreateOrganizationForm extends React.PureComponent<Props, State> {
             <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')}
@@ -187,20 +188,20 @@ class CreateOrganizationForm extends React.PureComponent<Props, State> {
                     {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={this.handleDescriptionChange}
                 rows={3}
-                maxLength={256}
                 value={this.state.description}
-                disabled={this.state.loading}
-                onChange={this.handleDescriptionChange}
               />
               <div className="modal-field-description">
                 {translate('organization.description.description')}
@@ -209,13 +210,13 @@ class CreateOrganizationForm extends React.PureComponent<Props, State> {
             <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={this.handleUrlChange}
                 type="text"
-                maxLength={256}
                 value={this.state.url}
-                disabled={this.state.loading}
-                onChange={this.handleUrlChange}
               />
               <div className="modal-field-description">
                 {translate('organization.url.description')}
@@ -226,12 +227,8 @@ class CreateOrganizationForm extends React.PureComponent<Props, State> {
           <footer className="modal-foot">
             <div>
               {this.state.loading && <i className="spinner spacer-right" />}
-              <button disabled={this.state.loading} type="submit">
-                {translate('create')}
-              </button>
-              <button className="button-link" onClick={this.props.onClose} type="reset">
-                {translate('cancel')}
-              </button>
+              <SubmitButton disabled={this.state.loading}>{translate('create')}</SubmitButton>
+              <ResetButtonLink onClick={this.props.onClose}>{translate('cancel')}</ResetButtonLink>
             </div>
           </footer>
         </form>
index 6ec85e94b4308d0c1ea5ae960945adc53ffe2f41..9bf4071c8b06fe3c83bbfff61232ef011c717ad6 100644 (file)
@@ -26,6 +26,7 @@ import { fetchIfAnyoneCanCreateOrganizations } from './actions';
 import { translate } from '../../../helpers/l10n';
 import { getAppState, getMyOrganizations, getGlobalSettingValue } from '../../../store/rootReducer';
 import { Organization } from '../../../app/types';
+import { Button } from '../../../components/ui/buttons';
 
 interface StateProps {
   anyoneCanCreate?: { value: string };
@@ -63,18 +64,12 @@ class UserOrganizations extends React.PureComponent<Props, State> {
     }
   };
 
-  openCreateOrganizationForm = () => this.setState({ createOrganization: true });
-
-  closeCreateOrganizationForm = () => this.setState({ createOrganization: false });
-
-  handleCreateClick = (event: React.SyntheticEvent<HTMLButtonElement>) => {
-    event.preventDefault();
-    event.currentTarget.blur();
-    this.openCreateOrganizationForm();
+  openCreateOrganizationForm = () => {
+    this.setState({ createOrganization: true });
   };
 
-  handleCreate = () => {
-    this.closeCreateOrganizationForm();
+  closeCreateOrganizationForm = () => {
+    this.setState({ createOrganization: false });
   };
 
   render() {
@@ -91,7 +86,7 @@ class UserOrganizations extends React.PureComponent<Props, State> {
           {canCreateOrganizations && (
             <div className="clearfix">
               <div className="boxed-group-actions">
-                <button onClick={this.handleCreateClick}>{translate('create')}</button>
+                <Button onClick={this.openCreateOrganizationForm}>{translate('create')}</Button>
               </div>
             </div>
           )}
@@ -107,7 +102,7 @@ class UserOrganizations extends React.PureComponent<Props, State> {
         {this.state.createOrganization && (
           <CreateOrganizationForm
             onClose={this.closeCreateOrganizationForm}
-            onCreate={this.handleCreate}
+            onCreate={this.closeCreateOrganizationForm}
           />
         )}
       </div>
index 91c5b7d3747a8c341f30369fdcf47852066520f3..52d5eee1094d12e7ad27c5604caaeaa520c7ad53 100644 (file)
@@ -22,6 +22,7 @@ import { times } from 'lodash';
 import { setWorkerCount } from '../../../api/ce';
 import Modal from '../../../components/controls/Modal';
 import Select from '../../../components/controls/Select';
+import { SubmitButton, ResetButtonLink } from '../../../components/ui/buttons';
 import { translate } from '../../../helpers/l10n';
 
 const MAX_WORKERS = 10;
@@ -55,7 +56,9 @@ export default class WorkersForm extends React.PureComponent<Props, State> {
     this.mounted = false;
   }
 
-  handleClose = () => this.props.onClose();
+  handleClose = () => {
+    this.props.onClose();
+  };
 
   handleWorkerCountChange = (option: { value: number }) =>
     this.setState({ newWorkerCount: option.value });
@@ -105,12 +108,8 @@ export default class WorkersForm extends React.PureComponent<Props, State> {
           <footer className="modal-foot">
             <div>
               {this.state.submitting && <i className="spinner spacer-right" />}
-              <button disabled={this.state.submitting} type="submit">
-                {translate('save')}
-              </button>
-              <button type="reset" className="button-link" onClick={this.handleClose}>
-                {translate('cancel')}
-              </button>
+              <SubmitButton disabled={this.state.submitting}>{translate('save')}</SubmitButton>
+              <ResetButtonLink onClick={this.handleClose}>{translate('cancel')}</ResetButtonLink>
             </div>
           </footer>
         </form>
index 4b9dfd60686e0229a54e89166892c19b52607222..0b1320c6cb27c8e34ce66d4172d86b853702f8e9 100644 (file)
@@ -79,19 +79,16 @@ exports[`changes select 1`] = `
       className="modal-foot"
     >
       <div>
-        <button
+        <SubmitButton
           disabled={false}
-          type="submit"
         >
           save
-        </button>
-        <button
-          className="button-link"
+        </SubmitButton>
+        <ResetButtonLink
           onClick={[Function]}
-          type="reset"
         >
           cancel
-        </button>
+        </ResetButtonLink>
       </div>
     </footer>
   </form>
@@ -177,19 +174,16 @@ exports[`changes select 2`] = `
       className="modal-foot"
     >
       <div>
-        <button
+        <SubmitButton
           disabled={false}
-          type="submit"
         >
           save
-        </button>
-        <button
-          className="button-link"
+        </SubmitButton>
+        <ResetButtonLink
           onClick={[Function]}
-          type="reset"
         >
           cancel
-        </button>
+        </ResetButtonLink>
       </div>
     </footer>
   </form>
index 97ae4439f34c66d72debd05d23366d5157f61a63..043d71b134a6cbf456dab13da0bfb6b9d6611ce8 100644 (file)
@@ -21,6 +21,7 @@ import * as React from 'react';
 import ActivationFormModal from './ActivationFormModal';
 import { Profile as BaseProfile } from '../../../api/quality-profiles';
 import { Rule, RuleDetails, RuleActivation } from '../../../app/types';
+import { Button } from '../../../components/ui/buttons';
 
 interface Props {
   activation?: RuleActivation;
@@ -50,23 +51,23 @@ export default class ActivationButton extends React.PureComponent<Props, State>
     this.mounted = false;
   }
 
-  handleButtonClick = (event: React.SyntheticEvent<HTMLButtonElement>) => {
-    event.preventDefault();
-    event.currentTarget.blur();
+  handleButtonClick = () => {
     this.setState({ modal: true });
   };
 
-  handleCloseModal = () => this.setState({ modal: false });
+  handleCloseModal = () => {
+    this.setState({ modal: false });
+  };
 
   render() {
     return (
       <>
-        <button
+        <Button
           className={this.props.className}
           id="coding-rules-quality-profile-activate"
           onClick={this.handleButtonClick}>
           {this.props.buttonText}
-        </button>
+        </Button>
 
         {this.state.modal && (
           <ActivationFormModal
index c410a105cae8d95c837e41c0882a14bca4cf5c1d..97fa22a04028ffc79ce61300084350e32f2dfba1 100644 (file)
@@ -27,6 +27,7 @@ import { Rule, RuleDetails, RuleActivation } from '../../../app/types';
 import { SEVERITIES } from '../../../helpers/constants';
 import { translate } from '../../../helpers/l10n';
 import { sortProfiles } from '../../quality-profiles/utils';
+import { SubmitButton, ResetButtonLink } from '../../../components/ui/buttons';
 
 interface Props {
   activation?: RuleActivation;
@@ -84,8 +85,8 @@ export default class ActivationFormModal extends React.PureComponent<Props, Stat
   };
 
   // Choose QP which a user can administrate, which are the same language and which are not built-in
-  getQualityProfilesWithDepth = ({ profiles } = this.props) =>
-    sortProfiles(
+  getQualityProfilesWithDepth = ({ profiles } = this.props) => {
+    return sortProfiles(
       profiles.filter(
         profile =>
           !profile.isBuiltIn &&
@@ -98,11 +99,6 @@ export default class ActivationFormModal extends React.PureComponent<Props, Stat
       // Decrease depth by 1, so the top level starts at 0
       depth: profile.depth - 1
     }));
-
-  handleCancelClick = (event: React.SyntheticEvent<HTMLButtonElement>) => {
-    event.preventDefault();
-    event.currentTarget.blur();
-    this.props.onClose();
   };
 
   handleFormSubmit = (event: React.SyntheticEvent<HTMLFormElement>) => {
@@ -137,11 +133,17 @@ export default class ActivationFormModal extends React.PureComponent<Props, Stat
     this.setState((state: State) => ({ params: { ...state.params, [name]: value } }));
   };
 
-  handleProfileChange = ({ value }: { value: string }) => this.setState({ profile: value });
+  handleProfileChange = ({ value }: { value: string }) => {
+    this.setState({ profile: value });
+  };
 
-  handleSeverityChange = ({ value }: { value: string }) => this.setState({ severity: value });
+  handleSeverityChange = ({ value }: { value: string }) => {
+    this.setState({ severity: value });
+  };
 
-  renderSeverityOption = ({ value }: { value: string }) => <SeverityHelper severity={value} />;
+  renderSeverityOption = ({ value }: { value: string }) => {
+    return <SeverityHelper severity={value} />;
+  };
 
   render() {
     const { activation, rule } = this.props;
@@ -188,11 +190,11 @@ export default class ActivationFormModal extends React.PureComponent<Props, Stat
                 clearable={false}
                 disabled={submitting}
                 onChange={this.handleSeverityChange}
+                optionRenderer={this.renderSeverityOption}
                 options={SEVERITIES.map(severity => ({
                   label: translate('severity', severity),
                   value: severity
                 }))}
-                optionRenderer={this.renderSeverityOption}
                 searchable={false}
                 value={severity}
                 valueRenderer={this.renderSeverityOption}
@@ -241,16 +243,12 @@ export default class ActivationFormModal extends React.PureComponent<Props, Stat
 
           <footer className="modal-foot">
             {submitting && <i className="spinner spacer-right" />}
-            <button disabled={submitting || activeInAllProfiles} type="submit">
+            <SubmitButton disabled={submitting || activeInAllProfiles}>
               {isUpdateMode ? translate('save') : translate('coding_rules.activate')}
-            </button>
-            <button
-              className="button-link"
-              disabled={submitting}
-              onClick={this.handleCancelClick}
-              type="reset">
+            </SubmitButton>
+            <ResetButtonLink disabled={submitting} onClick={this.props.onClose}>
               {translate('cancel')}
-            </button>
+            </ResetButtonLink>
           </footer>
         </form>
       </Modal>
index 5b4d9e3c66531c70a9f5c0c0a19861cf7156194a..63d039166cca5474ff97edb58cc256808dbf1033 100644 (file)
@@ -23,6 +23,7 @@ import BulkChangeModal from './BulkChangeModal';
 import { Query } from '../query';
 import { Profile } from '../../../api/quality-profiles';
 import Dropdown from '../../../components/controls/Dropdown';
+import { Button } from '../../../components/ui/buttons';
 import { translate } from '../../../helpers/l10n';
 
 interface Props {
@@ -109,9 +110,9 @@ export default class BulkChange extends React.PureComponent<Props, State> {
             this.closeDropdown = closeDropdown;
             return (
               <div className={classNames('pull-left dropdown', { open })}>
-                <button className="js-bulk-change" onClick={onToggleClick}>
+                <Button className="js-bulk-change" onClick={onToggleClick}>
                   {translate('bulk_change')}
-                </button>
+                </Button>
                 <ul className="dropdown-menu">
                   <li>
                     <a href="#" onClick={this.handleActivateClick}>
index f798128569293ab448192f37c30664fc336ab75e..948d37436935f1f541644fee2fee1185fde727db 100644 (file)
@@ -25,6 +25,7 @@ import Modal from '../../../components/controls/Modal';
 import Select from '../../../components/controls/Select';
 import { translate, translateWithParameters } from '../../../helpers/l10n';
 import { formatMeasure } from '../../../helpers/measures';
+import { SubmitButton, ResetButtonLink } from '../../../components/ui/buttons';
 
 interface Props {
   action: string;
@@ -73,12 +74,6 @@ export default class BulkChangeModal extends React.PureComponent<Props, State> {
     this.mounted = false;
   }
 
-  handleCloseClick = (event: React.SyntheticEvent<HTMLButtonElement>) => {
-    event.preventDefault();
-    event.currentTarget.blur();
-    this.props.onClose();
-  };
-
   handleProfileSelect = (options: { value: string }[]) => {
     const selectedProfiles = options.map(option => option.value);
     this.setState({ selectedProfiles });
@@ -238,16 +233,13 @@ export default class BulkChangeModal extends React.PureComponent<Props, State> {
           <footer className="modal-foot">
             {this.state.submitting && <i className="spinner spacer-right" />}
             {!this.state.finished && (
-              <button
-                disabled={this.state.submitting}
-                id="coding-rules-submit-bulk-change"
-                type="submit">
+              <SubmitButton disabled={this.state.submitting} id="coding-rules-submit-bulk-change">
                 {translate('apply')}
-              </button>
+              </SubmitButton>
             )}
-            <button className="button-link" onClick={this.handleCloseClick} type="reset">
+            <ResetButtonLink onClick={this.props.onClose}>
               {this.state.finished ? translate('close') : translate('cancel')}
-            </button>
+            </ResetButtonLink>
           </footer>
         </form>
       </Modal>
index 5882d70b307a717f32e91f7ce1e9cde96415de59..6c8e466d20018d6c84fd2a0b1c30694bc47c5adf 100644 (file)
@@ -22,9 +22,7 @@ import CustomRuleFormModal from './CustomRuleFormModal';
 import { RuleDetails } from '../../../app/types';
 
 interface Props {
-  children: (
-    props: { onClick: (event: React.SyntheticEvent<HTMLButtonElement>) => void }
-  ) => React.ReactNode;
+  children: (props: { onClick: () => void }) => React.ReactNode;
   customRule?: RuleDetails;
   onDone: (newRuleDetails: RuleDetails) => void;
   organization: string | undefined;
@@ -47,9 +45,7 @@ export default class CustomRuleButton extends React.PureComponent<Props, State>
     this.mounted = false;
   }
 
-  handleClick = (event: React.SyntheticEvent<HTMLButtonElement>) => {
-    event.preventDefault();
-    event.currentTarget.blur();
+  handleClick = () => {
     this.setState({ modal: true });
   };
 
index 426c180a43a408ecc571eeea2571d000e3ec226c..e3076a11b3f3e18543722a7e1c47ba60779c7215 100644 (file)
@@ -29,6 +29,7 @@ import TypeHelper from '../../../components/shared/TypeHelper';
 import SeverityHelper from '../../../components/shared/SeverityHelper';
 import { createRule, updateRule } from '../../../api/rules';
 import { csvEscape } from '../../../helpers/csv';
+import { SubmitButton, ResetButtonLink } from '../../../components/ui/buttons';
 
 interface Props {
   customRule?: RuleDetails;
@@ -84,12 +85,6 @@ export default class CustomRuleFormModal extends React.PureComponent<Props, Stat
     this.mounted = false;
   }
 
-  handleCancelClick = (event: React.SyntheticEvent<HTMLButtonElement>) => {
-    event.preventDefault();
-    event.currentTarget.blur();
-    this.props.onClose();
-  };
-
   prepareRequest = () => {
     /* eslint-disable camelcase */
     const { customRule, organization, templateRule } = this.props;
@@ -248,11 +243,11 @@ export default class CustomRuleFormModal extends React.PureComponent<Props, Stat
           clearable={false}
           disabled={this.state.submitting}
           onChange={this.handleTypeChange}
+          optionRenderer={this.renderTypeOption}
           options={TYPES.map(type => ({
             label: translate('issue.type', type),
             value: type
           }))}
-          optionRenderer={this.renderTypeOption}
           searchable={false}
           value={this.state.type}
           valueRenderer={this.renderTypeOption}
@@ -274,11 +269,11 @@ export default class CustomRuleFormModal extends React.PureComponent<Props, Stat
           clearable={false}
           disabled={this.state.submitting}
           onChange={this.handleSeverityChange}
+          optionRenderer={this.renderSeverityOption}
           options={SEVERITIES.map(severity => ({
             label: translate('severity', severity),
             value: severity
           }))}
-          optionRenderer={this.renderSeverityOption}
           searchable={false}
           value={this.state.severity}
           valueRenderer={this.renderSeverityOption}
@@ -345,21 +340,19 @@ export default class CustomRuleFormModal extends React.PureComponent<Props, Stat
   renderSubmitButton = () => {
     if (this.state.reactivating) {
       return (
-        <button
+        <SubmitButton
           disabled={this.state.submitting}
-          id="coding-rules-custom-rule-creation-reactivate"
-          type="submit">
+          id="coding-rules-custom-rule-creation-reactivate">
           {translate('coding_rules.reactivate')}
-        </button>
+        </SubmitButton>
       );
     } else {
       return (
-        <button
+        <SubmitButton
           disabled={this.state.submitting}
-          id="coding-rules-custom-rule-creation-create"
-          type="submit">
+          id="coding-rules-custom-rule-creation-create">
           {translate(this.props.customRule ? 'save' : 'create')}
-        </button>
+        </SubmitButton>
       );
     }
   };
@@ -399,14 +392,12 @@ export default class CustomRuleFormModal extends React.PureComponent<Props, Stat
           <div className="modal-foot">
             {submitting && <i className="spinner spacer-right" />}
             {this.renderSubmitButton()}
-            <button
-              className="button-link"
+            <ResetButtonLink
               disabled={submitting}
               id="coding-rules-custom-rule-creation-cancel"
-              onClick={this.handleCancelClick}
-              type="reset">
+              onClick={this.props.onClose}>
               {translate('cancel')}
-            </button>
+            </ResetButtonLink>
           </div>
         </form>
       </Modal>
index 95a7ee29f44c10e0096ad02c50d14c316dbdf4e3..d3a1553985888f2c01b0dc834ddd0e17e96f90d0 100644 (file)
@@ -19,6 +19,7 @@
  */
 import * as React from 'react';
 import SimpleModal from '../../../components/controls/SimpleModal';
+import { SubmitButton, ResetButtonLink } from '../../../components/ui/buttons';
 import { translate } from '../../../helpers/l10n';
 
 interface Props {
@@ -30,8 +31,8 @@ export default function RemoveExtendedDescriptionModal({ onCancel, onSubmit }: P
   const header = translate('coding_rules.remove_extended_description');
   return (
     <SimpleModal header={header} onClose={onCancel} onSubmit={onSubmit}>
-      {({ onCloseClick, onSubmitClick, submitting }) => (
-        <>
+      {({ onCloseClick, onFormSubmit, submitting }) => (
+        <form onSubmit={onFormSubmit}>
           <header className="modal-head">
             <h2>{header}</h2>
           </header>
@@ -42,18 +43,15 @@ export default function RemoveExtendedDescriptionModal({ onCancel, onSubmit }: P
 
           <footer className="modal-foot">
             {submitting && <i className="spinner spacer-right" />}
-            <button
+            <SubmitButton
               className="button-red"
               disabled={submitting}
-              id="coding-rules-detail-extend-description-remove-submit"
-              onClick={onSubmitClick}>
+              id="coding-rules-detail-extend-description-remove-submit">
               {translate('remove')}
-            </button>
-            <a href="#" onClick={onCloseClick}>
-              {translate('cancel')}
-            </a>
+            </SubmitButton>
+            <ResetButtonLink onClick={onCloseClick}>{translate('cancel')}</ResetButtonLink>
           </footer>
-        </>
+        </form>
       )}
     </SimpleModal>
   );
index 331e1c908e2da3f42948d8e38569d2e9e10f60cd..e951e3b9d44e2180107c4fbea23bf0e26b36cbc7 100644 (file)
@@ -31,6 +31,7 @@ import { getRuleDetails, deleteRule, updateRule } from '../../../api/rules';
 import { RuleActivation, RuleDetails as IRuleDetails } from '../../../app/types';
 import DeferredSpinner from '../../../components/common/DeferredSpinner';
 import ConfirmButton from '../../../components/controls/ConfirmButton';
+import { Button } from '../../../components/ui/buttons';
 import { translate, translateWithParameters } from '../../../helpers/l10n';
 
 interface Props {
@@ -183,12 +184,12 @@ export default class RuleDetails extends React.PureComponent<Props, State> {
                 organization={organization}
                 templateRule={ruleDetails}>
                 {({ onClick }) => (
-                  <button
+                  <Button
                     className="js-edit-custom"
                     id="coding-rules-detail-custom-rule-change"
                     onClick={onClick}>
                     {translate('edit')}
-                  </button>
+                  </Button>
                 )}
               </CustomRuleButton>
               <ConfirmButton
@@ -201,12 +202,12 @@ export default class RuleDetails extends React.PureComponent<Props, State> {
                 modalHeader={translate('coding_rules.delete_rule')}
                 onConfirm={this.handleDelete}>
                 {({ onClick }) => (
-                  <button
+                  <Button
                     className="button-red spacer-left js-delete"
                     id="coding-rules-detail-rule-delete"
                     onClick={onClick}>
                     {translate('delete')}
-                  </button>
+                  </Button>
                 )}
               </ConfirmButton>
             </div>
index d080327593f7401dc9638e8a6be4af294de980bc..ac0e20cafed210674fb10df45f8467538b0415ea 100644 (file)
@@ -26,6 +26,7 @@ import { Rule, RuleDetails } from '../../../app/types';
 import DeferredSpinner from '../../../components/common/DeferredSpinner';
 import ConfirmButton from '../../../components/controls/ConfirmButton';
 import SeverityHelper from '../../../components/shared/SeverityHelper';
+import { Button } from '../../../components/ui/buttons';
 import { translate, translateWithParameters } from '../../../helpers/l10n';
 import { getRuleUrl } from '../../../helpers/urls';
 
@@ -100,7 +101,7 @@ export default class RuleDetailsCustomRules extends React.PureComponent<Props, S
   };
 
   renderRule = (rule: Rule) => (
-    <tr key={rule.key} data-rule={rule.key}>
+    <tr data-rule={rule.key} key={rule.key}>
       <td className="coding-rules-detail-list-name">
         <Link to={getRuleUrl(rule.key, this.props.organization)}>{rule.name}</Link>
       </td>
@@ -132,9 +133,9 @@ export default class RuleDetailsCustomRules extends React.PureComponent<Props, S
             modalHeader={translate('coding_rules.delete_rule')}
             onConfirm={this.handleRuleDelete}>
             {({ onClick }) => (
-              <button className="button-red js-delete-custom-rule" onClick={onClick}>
+              <Button className="button-red js-delete-custom-rule" onClick={onClick}>
                 {translate('delete')}
-              </button>
+              </Button>
             )}
           </ConfirmButton>
         </td>
@@ -158,16 +159,16 @@ export default class RuleDetailsCustomRules extends React.PureComponent<Props, S
               organization={this.props.organization}
               templateRule={this.props.ruleDetails}>
               {({ onClick }) => (
-                <button className="js-create-custom-rule spacer-left" onClick={onClick}>
+                <Button className="js-create-custom-rule spacer-left" onClick={onClick}>
                   {translate('coding_rules.create')}
-                </button>
+                </Button>
               )}
             </CustomRuleButton>
           )}
 
           <DeferredSpinner loading={loading}>
             {rules.length > 0 && (
-              <table id="coding-rules-detail-custom-rules" className="coding-rules-detail-list">
+              <table className="coding-rules-detail-list" id="coding-rules-detail-custom-rules">
                 <tbody>{sortBy(rules, rule => rule.name).map(this.renderRule)}</tbody>
               </table>
             )}
index b21425fa10a5fea71c1516d1c35cb2a03079515d..c353a8c5c77f9483b58a5bfa45fd0f1c19d5e777 100644 (file)
@@ -22,6 +22,7 @@ import RemoveExtendedDescriptionModal from './RemoveExtendedDescriptionModal';
 import { updateRule } from '../../../api/rules';
 import { RuleDetails } from '../../../app/types';
 import MarkdownTips from '../../../components/common/MarkdownTips';
+import { Button, ResetButtonLink } from '../../../components/ui/buttons';
 import { translate } from '../../../helpers/l10n';
 
 interface Props {
@@ -58,21 +59,15 @@ export default class RuleDetailsDescription extends React.PureComponent<Props, S
   handleDescriptionChange = (event: React.SyntheticEvent<HTMLTextAreaElement>) =>
     this.setState({ description: event.currentTarget.value });
 
-  handleCancelClick = (event: React.SyntheticEvent<HTMLButtonElement>) => {
-    event.preventDefault();
-    event.currentTarget.blur();
+  handleCancelClick = () => {
     this.setState({ descriptionForm: false });
   };
 
-  handleSaveClick = (event: React.SyntheticEvent<HTMLButtonElement>) => {
-    event.preventDefault();
-    event.currentTarget.blur();
+  handleSaveClick = () => {
     this.updateDescription(this.state.description);
   };
 
-  handleRemoveDescriptionClick = (event: React.SyntheticEvent<HTMLButtonElement>) => {
-    event.preventDefault();
-    event.currentTarget.blur();
+  handleRemoveDescriptionClick = () => {
     this.setState({ removeDescriptionModal: true });
   };
 
@@ -107,9 +102,7 @@ export default class RuleDetailsDescription extends React.PureComponent<Props, S
     );
   };
 
-  handleExtendDescriptionClick = (event: React.SyntheticEvent<HTMLButtonElement>) => {
-    event.preventDefault();
-    event.currentTarget.blur();
+  handleExtendDescriptionClick = () => {
     this.setState({
       // set description` to the current `mdNote` each time the form is open
       description: this.props.ruleDetails.mdNote || '',
@@ -126,11 +119,11 @@ export default class RuleDetailsDescription extends React.PureComponent<Props, S
         />
       )}
       {this.props.canWrite && (
-        <button
+        <Button
           id="coding-rules-detail-extend-description"
           onClick={this.handleExtendDescriptionClick}>
           {translate('coding_rules.extend_description')}
-        </button>
+        </Button>
       )}
     </div>
   );
@@ -153,21 +146,21 @@ export default class RuleDetailsDescription extends React.PureComponent<Props, S
           </tr>
           <tr>
             <td>
-              <button
+              <Button
                 disabled={this.state.submitting}
                 id="coding-rules-detail-extend-description-submit"
                 onClick={this.handleSaveClick}>
                 {translate('save')}
-              </button>
+              </Button>
               {this.props.ruleDetails.mdNote !== undefined && (
                 <>
-                  <button
+                  <Button
                     className="button-red spacer-left"
                     disabled={this.state.submitting}
                     id="coding-rules-detail-extend-description-remove"
                     onClick={this.handleRemoveDescriptionClick}>
                     {translate('remove')}
-                  </button>
+                  </Button>
                   {this.state.removeDescriptionModal && (
                     <RemoveExtendedDescriptionModal
                       onCancel={this.handleCancelRemoving}
@@ -176,13 +169,13 @@ export default class RuleDetailsDescription extends React.PureComponent<Props, S
                   )}
                 </>
               )}
-              <button
-                className="spacer-left button-link"
+              <ResetButtonLink
+                className="spacer-left"
                 disabled={this.state.submitting}
                 id="coding-rules-detail-extend-description-cancel"
                 onClick={this.handleCancelClick}>
                 {translate('cancel')}
-              </button>
+              </ResetButtonLink>
               {this.state.submitting && <i className="spinner spacer-left" />}
             </td>
             <td className="text-right">
index 310af7e4bee3cd0065cf4b63191e19b5d50805e4..fa43eebc44a2e15da4accc4a048713ce24d6700a 100644 (file)
@@ -33,6 +33,7 @@ import SeverityHelper from '../../../components/shared/SeverityHelper';
 import BubblePopupHelper from '../../../components/common/BubblePopupHelper';
 import TagsList from '../../../components/tags/TagsList';
 import DateFormatter from '../../../components/intl/DateFormatter';
+import { Button } from '../../../components/ui/buttons';
 
 interface Props {
   canWrite: boolean | undefined;
@@ -50,9 +51,7 @@ interface State {
 export default class RuleDetailsMeta extends React.PureComponent<Props, State> {
   state: State = { tagsPopup: false };
 
-  handleTagsClick = (event: React.SyntheticEvent<HTMLButtonElement>) => {
-    event.preventDefault();
-    event.currentTarget.blur();
+  handleTagsClick = () => {
     this.setState(state => ({ tagsPopup: !state.tagsPopup }));
   };
 
@@ -106,7 +105,6 @@ export default class RuleDetailsMeta extends React.PureComponent<Props, State> {
         {this.props.canWrite ? (
           <BubblePopupHelper
             isOpen={this.state.tagsPopup}
-            position="bottomleft"
             popup={
               <RuleDetailsTagsPopup
                 organization={this.props.organization}
@@ -115,13 +113,14 @@ export default class RuleDetailsMeta extends React.PureComponent<Props, State> {
                 tags={tags}
               />
             }
+            position="bottomleft"
             togglePopup={this.handleTagsPopupToggle}>
-            <button className="button-link" onClick={this.handleTagsClick}>
+            <Button className="button-link" onClick={this.handleTagsClick}>
               <TagsList
                 allowUpdate={canWrite}
                 tags={allTags.length > 0 ? allTags : [translate('coding_rules.no_tags')]}
               />
-            </button>
+            </Button>
           </BubblePopupHelper>
         ) : (
           <TagsList
index 551b6d1402a5b15635c4faab9e94e51c73b38b9b..bb80abd464fa282b2f1f79b8fd1fa065a6e82f00 100644 (file)
@@ -30,6 +30,7 @@ import { getQualityProfileUrl } from '../../../helpers/urls';
 import BuiltInQualityProfileBadge from '../../quality-profiles/components/BuiltInQualityProfileBadge';
 import Tooltip from '../../../components/controls/Tooltip';
 import SeverityHelper from '../../../components/shared/SeverityHelper';
+import { Button } from '../../../components/ui/buttons';
 
 interface Props {
   activations: RuleActivation[] | undefined;
@@ -193,11 +194,11 @@ export default class RuleDetailsProfiles extends React.PureComponent<Props, Stat
                   modalHeader={translate('coding_rules.revert_to_parent_definition')}
                   onConfirm={this.handleRevert}>
                   {({ onClick }) => (
-                    <button
+                    <Button
                       className="coding-rules-detail-quality-profile-revert button-red spacer-left"
                       onClick={onClick}>
                       {translate('coding_rules.revert_to_parent_definition')}
-                    </button>
+                    </Button>
                   )}
                 </ConfirmButton>
               )
@@ -209,11 +210,11 @@ export default class RuleDetailsProfiles extends React.PureComponent<Props, Stat
                 modalHeader={translate('coding_rules.deactivate')}
                 onConfirm={this.handleDeactivate}>
                 {({ onClick }) => (
-                  <button
+                  <Button
                     className="coding-rules-detail-quality-profile-deactivate button-red spacer-left"
                     onClick={onClick}>
                     {translate('coding_rules.deactivate')}
-                  </button>
+                  </Button>
                 )}
               </ConfirmButton>
             )}
@@ -233,7 +234,7 @@ export default class RuleDetailsProfiles extends React.PureComponent<Props, Stat
     const parentActivation = activations.find(x => x.qProfile === profile.parentKey);
 
     return (
-      <tr key={profile.key} data-profile={profile.key}>
+      <tr data-profile={profile.key} key={profile.key}>
         <td className="coding-rules-detail-quality-profile-name">
           <Link to={getQualityProfileUrl(profile.name, profile.language, this.props.organization)}>
             {profile.name}
@@ -281,8 +282,8 @@ export default class RuleDetailsProfiles extends React.PureComponent<Props, Stat
 
           {activations.length > 0 && (
             <table
-              id="coding-rules-detail-quality-profiles"
-              className="coding-rules-detail-quality-profiles width100">
+              className="coding-rules-detail-quality-profiles width100"
+              id="coding-rules-detail-quality-profiles">
               <tbody>{activations.map(this.renderActivation)}</tbody>
             </table>
           )}
index 3f9852c418d6e04504c13ea59fa60483159ba907..3b70690ca5fc637f915cce055cd6cad84444e330 100644 (file)
@@ -28,6 +28,7 @@ import { Rule, RuleInheritance } from '../../../app/types';
 import ConfirmButton from '../../../components/controls/ConfirmButton';
 import Tooltip from '../../../components/controls/Tooltip';
 import SeverityIcon from '../../../components/shared/SeverityIcon';
+import { Button } from '../../../components/ui/buttons';
 import IssueTypeIcon from '../../../components/ui/IssueTypeIcon';
 import { translate, translateWithParameters } from '../../../helpers/l10n';
 
@@ -141,18 +142,18 @@ export default class RuleListItem extends React.PureComponent<Props> {
         modalHeader={translate('coding_rules.deactivate')}
         onConfirm={this.handleDeactivate}>
         {({ onClick }) => (
-          <button
+          <Button
             className="coding-rules-detail-quality-profile-deactivate button-red"
             onClick={onClick}>
             {translate('coding_rules.deactivate')}
-          </button>
+          </Button>
         )}
       </ConfirmButton>
     ) : (
       <Tooltip overlay={translate('coding_rules.can_not_deactivate')} placement="left">
-        <button className="coding-rules-detail-quality-profile-deactivate button-red disabled">
+        <Button className="coding-rules-detail-quality-profile-deactivate button-red disabled">
           {translate('coding_rules.deactivate')}
-        </button>
+        </Button>
       </Tooltip>
     );
   };
index feea67a5ab4f87fc19bcbddd6c91c59e4c781f78..c8a7dd6049c8c5cfc73cecbf5f9db284c00c3d6d 100644 (file)
@@ -19,6 +19,7 @@
  */
 import * as React from 'react';
 import Form from './Form';
+import { Button } from '../../../components/ui/buttons';
 import { translate } from '../../../helpers/l10n';
 
 interface Props {
@@ -55,9 +56,9 @@ export default class CreateButton extends React.PureComponent<Props, State> {
   render() {
     return (
       <>
-        <button id="custom-measures-create" onClick={this.handleClick} type="button">
+        <Button id="custom-measures-create" onClick={this.handleClick}>
           {translate('create')}
-        </button>
+        </Button>
         {this.state.modal && (
           <Form
             confirmButtonText={translate('create')}
index 6b814dd735a8408c8e87b11961d92c0a71140d13..3f584725f8ae9709ce0b829c1e674c88455a1e90 100644 (file)
@@ -23,6 +23,7 @@ import { CustomMeasure, Metric } from '../../../app/types';
 import DeferredSpinner from '../../../components/common/DeferredSpinner';
 import Select from '../../../components/controls/Select';
 import SimpleModal from '../../../components/controls/SimpleModal';
+import { SubmitButton, ResetButtonLink } from '../../../components/ui/buttons';
 import { translate } from '../../../helpers/l10n';
 
 interface Props {
@@ -186,20 +187,17 @@ export default class Form extends React.PureComponent<Props, State> {
 
             <footer className="modal-foot">
               <DeferredSpinner className="spacer-right" loading={submitting} />
-              <button
+              <SubmitButton
                 disabled={forbidSubmitting || submitting}
-                id="create-custom-measure-submit"
-                type="submit">
+                id="create-custom-measure-submit">
                 {this.props.confirmButtonText}
-              </button>
-              <button
-                className="button-link"
+              </SubmitButton>
+              <ResetButtonLink
                 disabled={submitting}
                 id="create-custom-measure-cancel"
-                onClick={onCloseClick}
-                type="reset">
+                onClick={onCloseClick}>
                 {translate('cancel')}
-              </button>
+              </ResetButtonLink>
             </footer>
           </form>
         )}
index e6252db478f1c3e6f15fdc65b6ae458bad7a9256..1cfc6991abd8766d7d4331e7683d3dfcdfe11f75 100644 (file)
@@ -62,6 +62,6 @@ it('should render form', async () => {
   expect(onClose).toBeCalled();
 
   onClose.mockClear();
-  click(form.find('button[type="reset"]'));
+  click(form.find('ResetButtonLink'));
   expect(onClose).toBeCalled();
 });
index 78c55c67890c738242fc19b3e97282bfd7988780..8973f165faecc90b913aedef112015457ba1958d 100644 (file)
@@ -2,25 +2,23 @@
 
 exports[`should create new custom measure 1`] = `
 <React.Fragment>
-  <button
+  <Button
     id="custom-measures-create"
     onClick={[Function]}
-    type="button"
   >
     create
-  </button>
+  </Button>
 </React.Fragment>
 `;
 
 exports[`should create new custom measure 2`] = `
 <React.Fragment>
-  <button
+  <Button
     id="custom-measures-create"
     onClick={[Function]}
-    type="button"
   >
     create
-  </button>
+  </Button>
   <Form
     confirmButtonText="create"
     header="custom_measures.create_custom_measure"
index 17057ba370496e82d546e7d6c0a06a916b845f86..858183d2219ba3eb0b5670a3b2b76abdce524aed 100644 (file)
@@ -83,22 +83,19 @@ exports[`should render form 1`] = `
         loading={false}
         timeout={100}
       />
-      <button
+      <SubmitButton
         disabled={true}
         id="create-custom-measure-submit"
-        type="submit"
       >
         confirmButtonText
-      </button>
-      <button
-        className="button-link"
+      </SubmitButton>
+      <ResetButtonLink
         disabled={false}
         id="create-custom-measure-cancel"
         onClick={[Function]}
-        type="reset"
       >
         cancel
-      </button>
+      </ResetButtonLink>
     </footer>
   </form>
 </Modal>
@@ -197,22 +194,19 @@ exports[`should render form 2`] = `
         loading={false}
         timeout={100}
       />
-      <button
+      <SubmitButton
         disabled={false}
         id="create-custom-measure-submit"
-        type="submit"
       >
         confirmButtonText
-      </button>
-      <button
-        className="button-link"
+      </SubmitButton>
+      <ResetButtonLink
         disabled={false}
         id="create-custom-measure-cancel"
         onClick={[Function]}
-        type="reset"
       >
         cancel
-      </button>
+      </ResetButtonLink>
     </footer>
   </form>
 </Modal>
index e3b67e82d845dbc23394fbb64509ee7dfbaee07c..582f306f40fe35cda4b49064b68111d1ddab9550 100644 (file)
@@ -19,6 +19,7 @@
  */
 import * as React from 'react';
 import Form, { MetricProps } from './Form';
+import { Button } from '../../../components/ui/buttons';
 import { translate } from '../../../helpers/l10n';
 
 interface Props {
@@ -56,9 +57,9 @@ export default class CreateButton extends React.PureComponent<Props, State> {
   render() {
     return (
       <>
-        <button id="metrics-create" onClick={this.handleClick}>
+        <Button id="metrics-create" onClick={this.handleClick}>
           {translate('custom_metrics.create_metric')}
-        </button>
+        </Button>
         {this.state.modal && (
           <Form
             confirmButtonText={translate('create')}
index 0922d9a9b4088ec882a562a07e83bd16e5cb1c06..89c4fc9ba209d7b147fec96d7bee49b0ed042b47 100644 (file)
@@ -23,6 +23,7 @@ import DeferredSpinner from '../../../components/common/DeferredSpinner';
 import SimpleModal from '../../../components/controls/SimpleModal';
 import { translate } from '../../../helpers/l10n';
 import Select, { Creatable } from '../../../components/controls/Select';
+import { SubmitButton, ResetButtonLink } from '../../../components/ui/buttons';
 
 export interface MetricProps {
   description: string;
@@ -168,17 +169,15 @@ export default class Form extends React.PureComponent<Props, State> {
 
             <footer className="modal-foot">
               <DeferredSpinner className="spacer-right" loading={submitting} />
-              <button disabled={submitting} id="create-metric-submit" type="submit">
+              <SubmitButton disabled={submitting} id="create-metric-submit">
                 {this.props.confirmButtonText}
-              </button>
-              <button
-                className="button-link"
+              </SubmitButton>
+              <ResetButtonLink
                 disabled={submitting}
                 id="create-metric-cancel"
-                onClick={onCloseClick}
-                type="reset">
+                onClick={onCloseClick}>
                 {translate('cancel')}
-              </button>
+              </ResetButtonLink>
             </footer>
           </form>
         )}
index a2b633095d9a625ccc1fe669b978935407b4e30b..1e828bc139ead35bab2bdc7b1261578720a80bd7 100644 (file)
@@ -54,6 +54,6 @@ it('should render form', async () => {
   expect(onClose).toBeCalled();
 
   onClose.mockClear();
-  click(wrapper.find('button[type="reset"]'));
+  click(wrapper.find('ResetButtonLink'));
   expect(onClose).toBeCalled();
 });
index fcc3412a7a66de9efc250879cc971015e7383c63..46c2ffc4706c479341ac68f564d144411d3d23e6 100644 (file)
@@ -2,23 +2,23 @@
 
 exports[`should create new group 1`] = `
 <React.Fragment>
-  <button
+  <Button
     id="metrics-create"
     onClick={[Function]}
   >
     custom_metrics.create_metric
-  </button>
+  </Button>
 </React.Fragment>
 `;
 
 exports[`should create new group 2`] = `
 <React.Fragment>
-  <button
+  <Button
     id="metrics-create"
     onClick={[Function]}
   >
     custom_metrics.create_metric
-  </button>
+  </Button>
   <Form
     confirmButtonText="create"
     domains={
index 0eea472611e323e2f69cb50283e6825bb38ed404..f0baf33640e88c33fa3825d2505ab0f0f4a2c435 100644 (file)
@@ -144,22 +144,19 @@ exports[`should render form 1`] = `
         loading={false}
         timeout={100}
       />
-      <button
+      <SubmitButton
         disabled={false}
         id="create-metric-submit"
-        type="submit"
       >
         confirmButtonText
-      </button>
-      <button
-        className="button-link"
+      </SubmitButton>
+      <ResetButtonLink
         disabled={false}
         id="create-metric-cancel"
         onClick={[Function]}
-        type="reset"
       >
         cancel
-      </button>
+      </ResetButtonLink>
     </footer>
   </form>
 </Modal>
index a83ee573e559e6df76d3bb7c93c51b766a057613..9b62320e86b1e892a6d9080ef9a4a19d667d8eda 100644 (file)
@@ -23,7 +23,7 @@ import { Group } from '../../../app/types';
 import Modal from '../../../components/controls/Modal';
 import BulletListIcon from '../../../components/icons-components/BulletListIcon';
 import SelectList from '../../../components/SelectList';
-import { ButtonIcon } from '../../../components/ui/buttons';
+import { ButtonIcon, ResetButtonLink } from '../../../components/ui/buttons';
 import { translate } from '../../../helpers/l10n';
 import { getBaseUrl } from '../../../helpers/urls';
 
@@ -64,12 +64,6 @@ export default class EditMembers extends React.PureComponent<Props, State> {
     }
   };
 
-  handleCloseClick = (event: React.SyntheticEvent<HTMLElement>) => {
-    event.preventDefault();
-    event.currentTarget.blur();
-    this.handleModalClose();
-  };
-
   renderSelectList = () => {
     if (this.container) {
       const extra = { name: this.props.group.name, organization: this.props.organization };
@@ -114,9 +108,7 @@ export default class EditMembers extends React.PureComponent<Props, State> {
             </div>
 
             <footer className="modal-foot">
-              <button className="button-link" onClick={this.handleCloseClick} type="reset">
-                {translate('Done')}
-              </button>
+              <ResetButtonLink onClick={this.handleModalClose}>{translate('Done')}</ResetButtonLink>
             </footer>
           </Modal>
         )}
index 23452cef400a656e06f566b732cf2a0a12849b0e..8d1e4fc84340191165caddb44a657c8d7c76838f 100644 (file)
@@ -21,6 +21,7 @@ import * as React from 'react';
 import { Group } from '../../../app/types';
 import DeferredSpinner from '../../../components/common/DeferredSpinner';
 import SimpleModal from '../../../components/controls/SimpleModal';
+import { ResetButtonLink, SubmitButton } from '../../../components/ui/buttons';
 import { translate } from '../../../helpers/l10n';
 
 interface Props {
@@ -102,12 +103,8 @@ export default class Form extends React.PureComponent<Props, State> {
 
             <footer className="modal-foot">
               <DeferredSpinner className="spacer-right" loading={submitting} />
-              <button disabled={submitting} type="submit">
-                {this.props.confirmButtonText}
-              </button>
-              <button className="button-link" onClick={onCloseClick} type="reset">
-                {translate('cancel')}
-              </button>
+              <SubmitButton disabled={submitting}>{this.props.confirmButtonText}</SubmitButton>
+              <ResetButtonLink onClick={onCloseClick}>{translate('cancel')}</ResetButtonLink>
             </footer>
           </form>
         )}
index 36f13bad6471917c4998d0dc5ef7025eace4d267..cd2be39f67179e300ea2e2610041fe7633c63440 100644 (file)
@@ -20,6 +20,7 @@
 import * as React from 'react';
 import Form from './Form';
 import DeferredSpinner from '../../../components/common/DeferredSpinner';
+import { Button } from '../../../components/ui/buttons';
 import { translate } from '../../../helpers/l10n';
 
 interface Props {
@@ -43,9 +44,7 @@ export default class Header extends React.PureComponent<Props, State> {
     this.mounted = false;
   }
 
-  handleCreateClick = (event: React.SyntheticEvent<HTMLButtonElement>) => {
-    event.preventDefault();
-    event.currentTarget.blur();
+  handleCreateClick = () => {
     this.setState({ createModal: true });
   };
 
@@ -68,9 +67,9 @@ export default class Header extends React.PureComponent<Props, State> {
           <DeferredSpinner loading={this.props.loading} />
 
           <div className="page-actions">
-            <button id="groups-create" onClick={this.handleCreateClick}>
+            <Button id="groups-create" onClick={this.handleCreateClick}>
               {translate('groups.create_group')}
-            </button>
+            </Button>
           </div>
 
           <p className="page-description">{translate('user_groups.page.description')}</p>
index 578b79d356dc96558a97c4e169d91d0c37bf9d02..d650c321233df41986d95877a5dbb1f761429215 100644 (file)
@@ -31,7 +31,7 @@ it('should edit group', () => {
     <EditGroup group={group} onEdit={onEdit}>
       {props => {
         ({ onClick } = props);
-        return <button />;
+        return <div />;
       }}
     </EditGroup>
   );
index a4a9297d329324cae1f5d175db025c28ec952950..a480e466c8ab9dce3d4136e6435dd4891450fd76 100644 (file)
@@ -29,11 +29,10 @@ it('should edit members', () => {
   const wrapper = shallow(<EditMembers group={group} onEdit={onEdit} organization="org" />);
   expect(wrapper).toMatchSnapshot();
 
-  wrapper.find('ButtonIcon').prop<Function>('onClick')();
-  wrapper.update();
+  click(wrapper.find('ButtonIcon'));
   expect(wrapper).toMatchSnapshot();
 
-  click(wrapper.find('button[type="reset"]'));
+  click(wrapper.find('ResetButtonLink'));
   expect(onEdit).toBeCalled();
   expect(wrapper).toMatchSnapshot();
 });
index b89f7249231af3fb28af4ee1ea0444758c8b7df6..744b90c813d1598f553472290b3da03e421eef86 100644 (file)
@@ -44,6 +44,6 @@ it('should render form', async () => {
   expect(onClose).toBeCalled();
 
   onClose.mockClear();
-  click(wrapper.find('button[type="reset"]'));
+  click(wrapper.find('ResetButtonLink'));
   expect(onClose).toBeCalled();
 });
index a237e8bafec1a0e8301ac34848c0fafe20d1389a..416b1585e11b3f1d0d2905d3f7d94de5b16477ba 100644 (file)
@@ -27,7 +27,7 @@ it('should create new group', () => {
   const wrapper = shallow(<Header loading={false} onCreate={onCreate} />);
   expect(wrapper).toMatchSnapshot();
 
-  click(wrapper.find('#groups-create'));
+  click(wrapper.find('[id="groups-create"]'));
   expect(wrapper).toMatchSnapshot();
 
   wrapper.find('Form').prop<Function>('onSubmit')({ name: 'foo', description: 'bar' });
index 8b54df832b31d4bc00b9c2354afd1dea029eec49..43bc8b832cbb7a68fae50600009d37c8e93f3920 100644 (file)
@@ -2,13 +2,13 @@
 
 exports[`should edit group 1`] = `
 <React.Fragment>
-  <button />
+  <div />
 </React.Fragment>
 `;
 
 exports[`should edit group 2`] = `
 <React.Fragment>
-  <button />
+  <div />
   <Form
     confirmButtonText="update_verb"
     group={
@@ -27,6 +27,6 @@ exports[`should edit group 2`] = `
 
 exports[`should edit group 3`] = `
 <React.Fragment>
-  <button />
+  <div />
 </React.Fragment>
 `;
index 8c846140be828b0cc02ee375eaeadc2ad8619162..230836640f5143d32fcb3d4187343ae180357241 100644 (file)
@@ -40,13 +40,11 @@ exports[`should edit members 2`] = `
     <footer
       className="modal-foot"
     >
-      <button
-        className="button-link"
+      <ResetButtonLink
         onClick={[Function]}
-        type="reset"
       >
         Done
-      </button>
+      </ResetButtonLink>
     </footer>
   </Modal>
 </React.Fragment>
index e43777b754e93b3f83f0cb046e3f01c96945173b..2b0954e09fb59a9ca8d5c9fda188c4284b132f2e 100644 (file)
@@ -67,19 +67,16 @@ exports[`should render form 1`] = `
         loading={false}
         timeout={100}
       />
-      <button
+      <SubmitButton
         disabled={false}
-        type="submit"
       >
         confirmButtonText
-      </button>
-      <button
-        className="button-link"
+      </SubmitButton>
+      <ResetButtonLink
         onClick={[Function]}
-        type="reset"
       >
         cancel
-      </button>
+      </ResetButtonLink>
     </footer>
   </form>
 </Modal>
index 6578cf61fe88c7750436075a95944b7581f13441..546722468c81c864ba07db068e489b603b42aa22 100644 (file)
@@ -18,12 +18,12 @@ exports[`should create new group 1`] = `
     <div
       className="page-actions"
     >
-      <button
+      <Button
         id="groups-create"
         onClick={[Function]}
       >
         groups.create_group
-      </button>
+      </Button>
     </div>
     <p
       className="page-description"
@@ -52,12 +52,12 @@ exports[`should create new group 2`] = `
     <div
       className="page-actions"
     >
-      <button
+      <Button
         id="groups-create"
         onClick={[Function]}
       >
         groups.create_group
-      </button>
+      </Button>
     </div>
     <p
       className="page-description"
index 3bc5e06b616227386907452fac1761631d72986d..8f19a13856ec6b8d2da285b95f16b37db56ec121 100644 (file)
@@ -22,6 +22,7 @@ import * as classNames from 'classnames';
 import { getMigrationStatus, getSystemStatus, migrateDatabase } from '../../../api/system';
 import DateFromNow from '../../../components/intl/DateFromNow';
 import TimeFormatter from '../../../components/intl/TimeFormatter';
+import { Button } from '../../../components/ui/buttons';
 import { translate } from '../../../helpers/l10n';
 import { getBaseUrl } from '../../../helpers/urls';
 import '../styles.css';
@@ -113,9 +114,7 @@ export default class App extends React.PureComponent<Props, State> {
     }, 2500);
   };
 
-  handleMigrateClick = (event: React.SyntheticEvent<HTMLButtonElement>) => {
-    event.preventDefault();
-    event.currentTarget.blur();
+  handleMigrateClick = () => {
     migrateDatabase().then(
       ({ message, startedAt, state }) => {
         if (this.mounted) {
@@ -221,9 +220,9 @@ export default class App extends React.PureComponent<Props, State> {
               <p className="maintenance-text">{translate('maintenance.upgrade_database.2')}</p>
               <p className="maintenance-text">{translate('maintenance.upgrade_database.3')}</p>
               <div className="maintenance-spinner">
-                <button id="start-migration" onClick={this.handleMigrateClick} type="button">
+                <Button id="start-migration" onClick={this.handleMigrateClick}>
                   {translate('maintenance.upgrade')}
-                </button>
+                </Button>
               </div>
             </>
           )}
index 8d4511d09e7c2c66829529489893690c4583730d..93a6d4ab930af2f6c26e30c99125c3dd6839e5e0 100644 (file)
@@ -120,7 +120,7 @@ describe('Setup Page', () => {
     await waitAndUpdate(wrapper);
     expect(wrapper).toMatchSnapshot();
 
-    click(wrapper.find('button'));
+    click(wrapper.find('Button'));
     expect(migrateDatabase).toBeCalled();
     await waitAndUpdate(wrapper);
     expect(wrapper).toMatchSnapshot();
index 784a8eb19698d2f2ea054f825c1b262d27b3fff2..d054e906e8c8522a81079334727b4d01901a7d55 100644 (file)
@@ -340,13 +340,12 @@ exports[`Setup Page should start migration 1`] = `
       <div
         className="maintenance-spinner"
       >
-        <button
+        <Button
           id="start-migration"
           onClick={[Function]}
-          type="button"
         >
           maintenance.upgrade
-        </button>
+        </Button>
       </div>
     </React.Fragment>
   </div>
index bbe8e3c48c3f63dbe2da4275abb2ced3e0f969b4..d392d5efcc3c6aadee6666482523b760e7b019b4 100644 (file)
@@ -21,6 +21,7 @@ import * as React from 'react';
 import { FormattedMessage } from 'react-intl';
 import RestartForm from '../../components/common/RestartForm';
 import { cancelPendingPlugins, PluginPending } from '../../api/plugins';
+import { Button } from '../../components/ui/buttons';
 import { translate } from '../../helpers/l10n';
 
 interface Props {
@@ -39,8 +40,13 @@ interface State {
 export default class PendingActions extends React.PureComponent<Props, State> {
   state: State = { openRestart: false };
 
-  handleOpenRestart = () => this.setState({ openRestart: true });
-  hanleCloseRestart = () => this.setState({ openRestart: false });
+  handleOpenRestart = () => {
+    this.setState({ openRestart: true });
+  };
+
+  hanleCloseRestart = () => {
+    this.setState({ openRestart: false });
+  };
 
   handleRevert = () => {
     cancelPendingPlugins().then(this.props.refreshPending, () => {});
@@ -88,12 +94,12 @@ export default class PendingActions extends React.PureComponent<Props, State> {
           </ul>
         </div>
         <div className="pull-right">
-          <button className="js-restart little-spacer-right" onClick={this.handleOpenRestart}>
+          <Button className="js-restart little-spacer-right" onClick={this.handleOpenRestart}>
             {translate('marketplace.restart')}
-          </button>
-          <button className="js-cancel-all button-red" onClick={this.handleRevert}>
+          </Button>
+          <Button className="js-cancel-all button-red" onClick={this.handleRevert}>
             {translate('marketplace.revert')}
-          </button>
+          </Button>
         </div>
         {this.state.openRestart && <RestartForm onClose={this.hanleCloseRestart} />}
       </div>
index f68b3b68416a1068e55a62cb5aceff48f7a3d550..c8688596746a52b89f2e05b8fac896abb97b439d 100644 (file)
@@ -44,18 +44,18 @@ exports[`should display pending actions 1`] = `
   <div
     className="pull-right"
   >
-    <button
+    <Button
       className="js-restart little-spacer-right"
       onClick={[Function]}
     >
       marketplace.restart
-    </button>
-    <button
+    </Button>
+    <Button
       className="js-cancel-all button-red"
       onClick={[Function]}
     >
       marketplace.revert
-    </button>
+    </Button>
   </div>
 </div>
 `;
index 10965d760ced0c090c059c3b7f7bc452b4dfd1ea..0ad689bc93d85832cfc4ab69bf0b051eb1fa7fb6 100644 (file)
@@ -20,6 +20,7 @@
 import * as React from 'react';
 import EditionBoxBadge from './EditionBoxBadge';
 import { Edition, EditionStatus } from '../../../api/marketplace';
+import { Button } from '../../../components/ui/buttons';
 import { translate } from '../../../helpers/l10n';
 
 interface Props {
@@ -32,7 +33,9 @@ interface Props {
 }
 
 export default class EditionBox extends React.PureComponent<Props> {
-  handleAction = () => this.props.onAction(this.props.edition);
+  handleAction = () => {
+    this.props.onAction(this.props.edition);
+  };
 
   render() {
     const { disableAction, displayAction, edition, editionStatus } = this.props;
@@ -48,9 +51,9 @@ export default class EditionBox extends React.PureComponent<Props> {
             {translate('marketplace.learn_more')}
           </a>
           {displayAction && (
-            <button disabled={disableAction} onClick={this.handleAction}>
+            <Button disabled={disableAction} onClick={this.handleAction}>
               {this.props.actionLabel}
-            </button>
+            </Button>
           )}
         </div>
       </div>
index fb609f9e30a6ab0a1bf5c06d8a634a0c6dcb8b3b..a4c48287869130fa8e79b9cb6525130744f9e188 100644 (file)
@@ -21,6 +21,7 @@ import * as React from 'react';
 import LicenseEditionSet from './LicenseEditionSet';
 import { Edition, EditionStatus, applyLicense } from '../../../api/marketplace';
 import Modal from '../../../components/controls/Modal';
+import { Button, ResetButtonLink } from '../../../components/ui/buttons';
 import { translate, translateWithParameters } from '../../../helpers/l10n';
 
 export interface Props {
@@ -55,13 +56,7 @@ export default class LicenseEditionForm extends React.PureComponent<Props, State
     }
   };
 
-  handleCancelClick = (event: React.SyntheticEvent<HTMLAnchorElement>) => {
-    event.preventDefault();
-    this.props.onClose();
-  };
-
-  handleConfirmClick = (event: React.SyntheticEvent<HTMLButtonElement>) => {
-    event.preventDefault();
+  handleConfirmClick = () => {
     const { license, status } = this.state;
     if (license && status) {
       this.setState({ submitting: true });
@@ -102,18 +97,16 @@ export default class LicenseEditionForm extends React.PureComponent<Props, State
         <footer className="modal-foot">
           {submitting && <i className="spinner spacer-right" />}
           {status && (
-            <button
+            <Button
               className="js-confirm"
-              onClick={this.handleConfirmClick}
-              disabled={!license || submitting}>
+              disabled={!license || submitting}
+              onClick={this.handleConfirmClick}>
               {status === 'AUTOMATIC_INSTALL'
                 ? translate('marketplace.install')
                 : translate('save')}
-            </button>
+            </Button>
           )}
-          <a className="js-modal-close" href="#" onClick={this.handleCancelClick}>
-            {translate('cancel')}
-          </a>
+          <ResetButtonLink onClick={this.props.onClose}>{translate('cancel')}</ResetButtonLink>
         </footer>
       </Modal>
     );
index 8edda448a25f91fda1116c799e0d87f24da51bcf..45ca8e69773ac516cb434b036be3b9fdb50e4b0c 100644 (file)
  */
 import * as React from 'react';
 import PluginUpdateButton from './PluginUpdateButton';
+import { isPluginAvailable, isPluginInstalled } from '../utils';
+import { Plugin, installPlugin, updatePlugin, uninstallPlugin } from '../../../api/plugins';
 import Checkbox from '../../../components/controls/Checkbox';
 import CheckIcon from '../../../components/icons-components/CheckIcon';
-import { Plugin, installPlugin, updatePlugin, uninstallPlugin } from '../../../api/plugins';
-import { isPluginAvailable, isPluginInstalled } from '../utils';
+import { Button } from '../../../components/ui/buttons';
 import { translate } from '../../../helpers/l10n';
 
 interface Props {
@@ -150,23 +151,21 @@ export default class PluginActions extends React.PureComponent<Props, State> {
                   update={update}
                 />
               ))}
-            <button
+            <Button
               className="js-uninstall button-red little-spacer-left"
               disabled={loading}
-              onClick={this.handleUninstall}
-              type="button">
+              onClick={this.handleUninstall}>
               {translate('marketplace.uninstall')}
-            </button>
+            </Button>
           </div>
         )}
         {isPluginAvailable(plugin) && (
-          <button
+          <Button
             className="js-install"
             disabled={loading || (plugin.termsAndConditionsUrl != null && !this.state.acceptTerms)}
-            onClick={this.handleInstall}
-            type="button">
+            onClick={this.handleInstall}>
             {translate('marketplace.install')}
-          </button>
+          </Button>
         )}
       </div>
     );
index a1f17963a7e3645e546153a2d19a4ccfd490cd5a..b542908b5c28c93ea24ce0d5528eb7024af71d36 100644 (file)
@@ -19,8 +19,9 @@
  */
 import * as React from 'react';
 import PluginChangeLog from './PluginChangeLog';
-import BubblePopupHelper from '../../../components/common/BubblePopupHelper';
 import { Release, Update } from '../../../api/plugins';
+import BubblePopupHelper from '../../../components/common/BubblePopupHelper';
+import { Button } from '../../../components/ui/buttons';
 
 interface Props {
   release: Release;
@@ -34,12 +35,6 @@ interface State {
 export default class PluginChangeLogButton extends React.PureComponent<Props, State> {
   state: State = { changelogOpen: false };
 
-  handleChangelogClick = (event: React.SyntheticEvent<HTMLButtonElement>) => {
-    event.preventDefault();
-    event.stopPropagation();
-    this.toggleChangelog();
-  };
-
   toggleChangelog = (show?: boolean) => {
     if (show !== undefined) {
       this.setState({ changelogOpen: show });
@@ -48,17 +43,21 @@ export default class PluginChangeLogButton extends React.PureComponent<Props, St
     }
   };
 
+  handleClick = () => {
+    this.toggleChangelog();
+  };
+
   render() {
     return (
       <div className="display-inline-block little-spacer-left">
-        <button
+        <Button
           className="button-link js-changelog issue-rule icon-ellipsis-h"
-          onClick={this.handleChangelogClick}
+          onClick={this.handleClick}
         />
         <BubblePopupHelper
           isOpen={this.state.changelogOpen}
-          position="bottomright"
           popup={<PluginChangeLog release={this.props.release} update={this.props.update} />}
+          position="bottomright"
           togglePopup={this.toggleChangelog}
         />
       </div>
index b426f0482473f3cc19ee44c64d45d6d12a6ff619..8899613e0b9cc91139b95ddf80eb578c04d2f627 100644 (file)
@@ -19,6 +19,7 @@
  */
 import * as React from 'react';
 import { Update } from '../../../api/plugins';
+import { Button } from '../../../components/ui/buttons';
 import { translateWithParameters } from '../../../helpers/l10n';
 
 interface Props {
@@ -28,7 +29,9 @@ interface Props {
 }
 
 export default class PluginUpdateButton extends React.PureComponent<Props> {
-  handleClick = () => this.props.onClick(this.props.update);
+  handleClick = () => {
+    this.props.onClick(this.props.update);
+  };
 
   render() {
     const { disabled, update } = this.props;
@@ -36,13 +39,12 @@ export default class PluginUpdateButton extends React.PureComponent<Props> {
       return null;
     }
     return (
-      <button
+      <Button
         className="js-update little-spacer-bottom"
         disabled={disabled}
-        onClick={this.handleClick}
-        type="button">
+        onClick={this.handleClick}>
         {translateWithParameters('marketplace.update_to_x', update.release.version)}
-      </button>
+      </Button>
     );
   }
 }
index ad79e2a31d2fbb3babb00f11f5c19789d2d16588..1b9be29c7dfa541c25a263a6db84ec1ce630993e 100644 (file)
@@ -20,6 +20,7 @@
 import * as React from 'react';
 import { Edition, EditionStatus, uninstallEdition } from '../../../api/marketplace';
 import Modal from '../../../components/controls/Modal';
+import { Button, ResetButtonLink } from '../../../components/ui/buttons';
 import { translate, translateWithParameters } from '../../../helpers/l10n';
 
 export interface Props {
@@ -45,13 +46,7 @@ export default class UninstallEditionForm extends React.PureComponent<Props, Sta
     this.mounted = false;
   }
 
-  handleCancelClick = (event: React.SyntheticEvent<HTMLAnchorElement>) => {
-    event.preventDefault();
-    this.props.onClose();
-  };
-
-  handleConfirmClick = (event: React.SyntheticEvent<HTMLButtonElement>) => {
-    event.preventDefault();
+  handleConfirmClick = () => {
     this.setState({ loading: true });
     uninstallEdition()
       .then(() => {
@@ -86,12 +81,12 @@ export default class UninstallEditionForm extends React.PureComponent<Props, Sta
 
         <footer className="modal-foot">
           {loading && <i className="spinner spacer-right" />}
-          <button disabled={loading} onClick={this.handleConfirmClick}>
+          <Button disabled={loading} onClick={this.handleConfirmClick}>
             {translate('marketplace.downgrade')}
-          </button>
-          <a className="js-modal-close" href="#" onClick={this.handleCancelClick}>
+          </Button>
+          <ResetButtonLink className="js-modal-close" onClick={this.props.onClose}>
             {translate('cancel')}
-          </a>
+          </ResetButtonLink>
         </footer>
       </Modal>
     );
index 42db6f8c02b8c520648b0e4c5e9712655e9ccac4..9814570cbf8f02525199c036b40c24dfb97b36a5 100644 (file)
@@ -54,22 +54,19 @@ it('should correctly change the button based on the status and license', () => {
   (wrapper.instance() as LicenseEditionForm).mounted = true;
 
   wrapper.setState({ license: 'mylicense', status: 'NO_INSTALL' });
-  button = wrapper.find('button');
-  expect(button.text()).toBe('save');
-  expect(button.prop('disabled')).toBeFalsy();
+  button = wrapper.find('Button');
+  expect(button).toMatchSnapshot();
 
   wrapper.setState({ license: undefined, status: 'MANUAL_INSTALL' });
-  button = wrapper.find('button');
-  expect(button.text()).toBe('save');
-  expect(button.prop('disabled')).toBeTruthy();
+  button = wrapper.find('Button');
+  expect(button).toMatchSnapshot();
 
   wrapper.setState({ status: 'AUTOMATIC_INSTALL' });
-  button = wrapper.find('button');
-  expect(button.text()).toContain('install');
-  expect(button.prop('disabled')).toBeTruthy();
+  button = wrapper.find('Button');
+  expect(button).toMatchSnapshot();
 
   wrapper.setState({ license: 'mylicense' });
-  expect(wrapper.find('button').prop('disabled')).toBeFalsy();
+  expect(wrapper.find('Button').prop('disabled')).toBeFalsy();
 });
 
 it('should update the edition status after install', async () => {
@@ -78,7 +75,7 @@ it('should update the edition status after install', async () => {
   const form = wrapper.instance() as LicenseEditionForm;
   form.handleLicenseChange('mylicense', 'AUTOMATIC_INSTALL');
   wrapper.update();
-  click(wrapper.find('button'));
+  click(wrapper.find('Button'));
   expect(applyLicense).toHaveBeenCalledWith({ license: 'mylicense' });
   await new Promise(setImmediate);
   expect(updateEditionStatus).toHaveBeenCalledWith({
index 11256063574b122429d079a620a99c235607c471..f8dbede4cb21b5d68bd20a79994f9ae2e36f388d 100644 (file)
@@ -50,7 +50,7 @@ it('should update the edition status after uninstall', async () => {
   const updateEditionStatus = jest.fn();
   const wrapper = getWrapper({ updateEditionStatus });
   (wrapper.instance() as UninstallEditionForm).mounted = true;
-  click(wrapper.find('button'));
+  click(wrapper.find('Button'));
   expect(uninstallEdition).toHaveBeenCalled();
   await new Promise(setImmediate);
   expect(updateEditionStatus).toHaveBeenCalledWith({
index 9fa9b25310293a9083c881403981cfaeae6752ed..b9925cec2c1b50ab64df568ac88195e0231edd04 100644 (file)
@@ -70,12 +70,12 @@ exports[`should disable action button 1`] = `
     >
       marketplace.learn_more
     </a>
-    <button
+    <Button
       disabled={true}
       onClick={[Function]}
     >
       action
-    </button>
+    </Button>
   </div>
 </div>
 `;
@@ -113,12 +113,12 @@ exports[`should display the edition 1`] = `
     >
       marketplace.learn_more
     </a>
-    <button
+    <Button
       disabled={false}
       onClick={[Function]}
     >
       action
-    </button>
+    </Button>
   </div>
 </div>
 `;
index 9c3d5970d11c0f364fb5341ebae51b8d6b7ac9ff..c0dcce3c40a7698f071304d788f5a93c910990a0 100644 (file)
@@ -1,5 +1,35 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
+exports[`should correctly change the button based on the status and license 1`] = `
+<Button
+  className="js-confirm"
+  disabled={false}
+  onClick={[Function]}
+>
+  save
+</Button>
+`;
+
+exports[`should correctly change the button based on the status and license 2`] = `
+<Button
+  className="js-confirm"
+  disabled={true}
+  onClick={[Function]}
+>
+  save
+</Button>
+`;
+
+exports[`should correctly change the button based on the status and license 3`] = `
+<Button
+  className="js-confirm"
+  disabled={true}
+  onClick={[Function]}
+>
+  marketplace.install
+</Button>
+`;
+
 exports[`should display correctly 1`] = `
 <Modal
   contentLabel="marketplace.upgrade_to_x.Foo"
@@ -41,13 +71,11 @@ exports[`should display correctly 1`] = `
   <footer
     className="modal-foot"
   >
-    <a
-      className="js-modal-close"
-      href="#"
-      onClick={[Function]}
+    <ResetButtonLink
+      onClick={[MockFunction]}
     >
       cancel
-    </a>
+    </ResetButtonLink>
   </footer>
 </Modal>
 `;
index 06d57cb8f6fe1ee9931e17c0b43b2bd604d0b6e6..491d59eb8f545965600fd864e10e33b8eb482963 100644 (file)
@@ -22,19 +22,18 @@ exports[`should display correctly 1`] = `
   <footer
     className="modal-foot"
   >
-    <button
+    <Button
       disabled={false}
       onClick={[Function]}
     >
       marketplace.downgrade
-    </button>
-    <a
+    </Button>
+    <ResetButtonLink
       className="js-modal-close"
-      href="#"
-      onClick={[Function]}
+      onClick={[MockFunction]}
     >
       cancel
-    </a>
+    </ResetButtonLink>
   </footer>
 </Modal>
 `;
index a43aa00e0759e65b8fc1cd071f0931e41eda862c..832e0e43bc1e1574148d005cb5c75c360f7c35cd 100644 (file)
@@ -20,6 +20,7 @@
 import * as React from 'react';
 import * as classNames from 'classnames';
 import { BadgeType } from './utils';
+import { Button } from '../../../components/ui/buttons';
 import { translate } from '../../../helpers/l10n';
 
 interface Props {
@@ -30,15 +31,17 @@ interface Props {
 }
 
 export default class BadgeButton extends React.PureComponent<Props> {
-  handleClick = () => this.props.onClick(this.props.type);
+  handleClick = () => {
+    this.props.onClick(this.props.type);
+  };
 
   render() {
     const { selected, type, url } = this.props;
     const width = type !== BadgeType.measure ? '128px' : undefined;
     return (
-      <button className={classNames('badge-button', { selected })} onClick={this.handleClick}>
-        <img src={url} alt={translate('overview.badges', type, 'alt')} width={width} />
-      </button>
+      <Button className={classNames('badge-button', { selected })} onClick={this.handleClick}>
+        <img alt={translate('overview.badges', type, 'alt')} src={url} width={width} />
+      </Button>
     );
   }
 }
index 4a4e572f7c39120a2b58b79d523c8fdfdc75f897..fc98e4eb71ab7229ef309d89a664ddb2d729ec48 100644 (file)
@@ -26,6 +26,7 @@ import { Metric } from '../../../app/types';
 import Modal from '../../../components/controls/Modal';
 import { translate } from '../../../helpers/l10n';
 import './styles.css';
+import { Button, ResetButtonLink } from '../../../components/ui/buttons';
 
 interface Props {
   branch?: string;
@@ -46,18 +47,21 @@ export default class BadgesModal extends React.PureComponent<Props, State> {
     badgeOptions: { color: 'white', metric: 'alert_status' }
   };
 
-  handleClose = () => this.setState({ open: false });
-
-  handleOpen = () => this.setState({ open: true });
+  handleClose = () => {
+    this.setState({ open: false });
+  };
 
-  handleSelectBadge = (selectedType: BadgeType) => this.setState({ selectedType });
+  handleOpen = () => {
+    this.setState({ open: true });
+  };
 
-  handleUpdateOptions = (options: Partial<BadgeOptions>) =>
-    this.setState(state => ({
-      badgeOptions: { ...state.badgeOptions, ...options }
-    }));
+  handleSelectBadge = (selectedType: BadgeType) => {
+    this.setState({ selectedType });
+  };
 
-  handleCancelClick = () => this.handleClose();
+  handleUpdateOptions = (options: Partial<BadgeOptions>) => {
+    this.setState(state => ({ badgeOptions: { ...state.badgeOptions, ...options } }));
+  };
 
   render() {
     const { branch, project } = this.props;
@@ -66,9 +70,9 @@ export default class BadgesModal extends React.PureComponent<Props, State> {
     const fullBadgeOptions = { branch, project, ...badgeOptions };
     return (
       <div className="overview-meta-card">
-        <button className="js-project-badges" onClick={this.handleOpen}>
+        <Button className="js-project-badges" onClick={this.handleOpen}>
           {translate('overview.badges.get_badge')}
-        </button>
+        </Button>
         {this.state.open && (
           <Modal contentLabel={header} onRequestClose={this.handleClose}>
             <header className="modal-head">
@@ -100,9 +104,9 @@ export default class BadgesModal extends React.PureComponent<Props, State> {
               <BadgeSnippet snippet={getBadgeUrl(selectedType, fullBadgeOptions)} />
             </div>
             <footer className="modal-foot">
-              <button className="button-link js-modal-close" onClick={this.handleCancelClick}>
+              <ResetButtonLink className="js-modal-close" onClick={this.handleClose}>
                 {translate('close')}
-              </button>
+              </ResetButtonLink>
             </footer>
           </Modal>
         )}
index 3f958698649446affdfcd9ee1815d82854e891a8..4e45f44e94a7b0f9448f1a77685a20f9b4b8a674 100644 (file)
@@ -32,7 +32,7 @@ it('should display correctly', () => {
 it('should return the badge type on click', () => {
   const onClick = jest.fn();
   const wrapper = getWrapper({ onClick });
-  click(wrapper.find('button'));
+  click(wrapper.find('Button'));
   expect(onClick).toHaveBeenCalledWith(BadgeType.marketing);
 });
 
index fc403fc3f173caf3b0c4f8eb11e23986193c456a..cea857f2e90bd3c511f04b1705153d26fe472ca2 100644 (file)
@@ -29,6 +29,6 @@ jest.mock('../../../../helpers/urls', () => ({
 it('should display the modal after click', () => {
   const wrapper = shallow(<BadgesModal branch="branch-6.6" metrics={{}} project="foo" />);
   expect(wrapper).toMatchSnapshot();
-  click(wrapper.find('button'));
+  click(wrapper.find('Button'));
   expect(wrapper.find('Modal')).toMatchSnapshot();
 });
index 90b4d8e5e37926a4dc040f240e178e7edf8abf27..c1ea1a7eb507889ce8d9d2281da2a3c25d3cb79d 100644 (file)
@@ -1,7 +1,7 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
 exports[`should display correctly 1`] = `
-<button
+<Button
   className="badge-button"
   onClick={[Function]}
 >
@@ -10,11 +10,11 @@ exports[`should display correctly 1`] = `
     src="http://foo.bar"
     width="128px"
   />
-</button>
+</Button>
 `;
 
 exports[`should display correctly 2`] = `
-<button
+<Button
   className="badge-button selected"
   onClick={[Function]}
 >
@@ -23,11 +23,11 @@ exports[`should display correctly 2`] = `
     src="http://foo.bar"
     width="128px"
   />
-</button>
+</Button>
 `;
 
 exports[`should display correctly 3`] = `
-<button
+<Button
   className="badge-button"
   onClick={[Function]}
 >
@@ -35,5 +35,5 @@ exports[`should display correctly 3`] = `
     alt="overview.badges.measure.alt"
     src="http://foo.bar"
   />
-</button>
+</Button>
 `;
index e813fd772bda5de07bdc073a36f0fea32c450450..8366543e432fb0eb82f570f04c676c6610632612 100644 (file)
@@ -4,12 +4,12 @@ exports[`should display the modal after click 1`] = `
 <div
   className="overview-meta-card"
 >
-  <button
+  <Button
     className="js-project-badges"
     onClick={[Function]}
   >
     overview.badges.get_badge
-  </button>
+  </Button>
 </div>
 `;
 
@@ -82,12 +82,12 @@ exports[`should display the modal after click 2`] = `
   <footer
     className="modal-foot"
   >
-    <button
-      className="button-link js-modal-close"
+    <ResetButtonLink
+      className="js-modal-close"
       onClick={[Function]}
     >
       close
-    </button>
+    </ResetButtonLink>
   </footer>
 </Modal>
 `;
index 07365b2633069adb94b0de548c348c6c07eabfc9..4dde591feb7de37760ac164761e94c453048ba5b 100644 (file)
@@ -24,6 +24,7 @@ import { translate } from '../../../helpers/l10n';
 import TagsList from '../../../components/tags/TagsList';
 import { BubblePopupPosition } from '../../../components/common/BubblePopup';
 import { Component } from '../../../app/types';
+import { Button } from '../../../components/ui/buttons';
 
 interface Props {
   component: Component;
@@ -37,7 +38,7 @@ interface State {
 
 export default class MetaTags extends React.PureComponent<Props, State> {
   card?: HTMLDivElement | null;
-  tagsList?: HTMLButtonElement | null;
+  tagsList?: HTMLElement | null;
   tagsSelector?: HTMLDivElement | null;
   state: State = { popupOpen: false, popupPosition: { top: 0, right: 0 } };
 
@@ -70,8 +71,7 @@ export default class MetaTags extends React.PureComponent<Props, State> {
     }
   };
 
-  handleClick = (evt: React.SyntheticEvent<HTMLButtonElement>) => {
-    evt.stopPropagation();
+  handleClick = () => {
     this.setState(state => ({ popupOpen: !state.popupOpen }));
   };
 
@@ -100,12 +100,13 @@ export default class MetaTags extends React.PureComponent<Props, State> {
     if (this.canUpdateTags()) {
       return (
         <div className="big-spacer-top overview-meta-tags" ref={card => (this.card = card)}>
-          <button
+          <Button
             className="button-link"
+            innerRef={tagsList => (this.tagsList = tagsList)}
             onClick={this.handleClick}
-            ref={tagsList => (this.tagsList = tagsList)}>
+            stopPropagation={true}>
             <TagsList allowUpdate={true} tags={tags.length ? tags : [translate('no_tags')]} />
-          </button>
+          </Button>
           {popupOpen && (
             <div ref={tagsSelector => (this.tagsSelector = tagsSelector)}>
               <MetaTagsSelector
index 1479225a02241d4d895921b72f5d30e30cf94e1f..7fa2003a3317a9a9b54c2ae9e93c38f1ec63a5eb 100644 (file)
@@ -72,10 +72,10 @@ it('should open the tag selector on click', () => {
   expect(wrapper).toMatchSnapshot();
 
   // open
-  click(wrapper.find('button'));
+  click(wrapper.find('Button'));
   expect(wrapper).toMatchSnapshot();
 
   // close
-  click(wrapper.find('button'));
+  click(wrapper.find('Button'));
   expect(wrapper).toMatchSnapshot();
 });
index 1eea19831ab1704e9b27129d6f2a14ce935cf73b..d0b063accf7b2ec52f63152a96db55ce075b2870 100644 (file)
@@ -4,9 +4,11 @@ exports[`should open the tag selector on click 1`] = `
 <div
   className="big-spacer-top overview-meta-tags"
 >
-  <button
+  <Button
     className="button-link"
+    innerRef={[Function]}
     onClick={[Function]}
+    stopPropagation={true}
   >
     <TagsList
       allowUpdate={true}
@@ -17,7 +19,7 @@ exports[`should open the tag selector on click 1`] = `
         ]
       }
     />
-  </button>
+  </Button>
 </div>
 `;
 
@@ -25,9 +27,11 @@ exports[`should open the tag selector on click 2`] = `
 <div
   className="big-spacer-top overview-meta-tags"
 >
-  <button
+  <Button
     className="button-link"
+    innerRef={[Function]}
     onClick={[Function]}
+    stopPropagation={true}
   >
     <TagsList
       allowUpdate={true}
@@ -38,7 +42,7 @@ exports[`should open the tag selector on click 2`] = `
         ]
       }
     />
-  </button>
+  </Button>
   <div>
     <MetaTagsSelector
       position={
@@ -64,9 +68,11 @@ exports[`should open the tag selector on click 3`] = `
 <div
   className="big-spacer-top overview-meta-tags"
 >
-  <button
+  <Button
     className="button-link"
+    innerRef={[Function]}
     onClick={[Function]}
+    stopPropagation={true}
   >
     <TagsList
       allowUpdate={true}
@@ -77,7 +83,7 @@ exports[`should open the tag selector on click 3`] = `
         ]
       }
     />
-  </button>
+  </Button>
 </div>
 `;
 
@@ -85,9 +91,11 @@ exports[`should render with tags and admin rights 1`] = `
 <div
   className="big-spacer-top overview-meta-tags"
 >
-  <button
+  <Button
     className="button-link"
+    innerRef={[Function]}
     onClick={[Function]}
+    stopPropagation={true}
   >
     <TagsList
       allowUpdate={true}
@@ -98,7 +106,7 @@ exports[`should render with tags and admin rights 1`] = `
         ]
       }
     />
-  </button>
+  </Button>
 </div>
 `;
 
index 3bf36d12b1d01038fafe68289a70095407e26788..828fc380d78e77b3e1c56163905ba7cb0b2cd797 100644 (file)
@@ -21,6 +21,7 @@ import * as React from 'react';
 import DeferredSpinner from '../../../components/common/DeferredSpinner';
 import SimpleModal from '../../../components/controls/SimpleModal';
 import { translate } from '../../../helpers/l10n';
+import { SubmitButton, ResetButtonLink } from '../../../components/ui/buttons';
 
 interface Props {
   confirmButtonText: string;
@@ -134,17 +135,15 @@ export default class Form extends React.PureComponent<Props, State> {
 
             <footer className="modal-foot">
               <DeferredSpinner className="spacer-right" loading={submitting} />
-              <button disabled={submitting} id="permission-template-submit" type="submit">
+              <SubmitButton disabled={submitting} id="permission-template-submit">
                 {this.props.confirmButtonText}
-              </button>
-              <button
-                className="button-link"
+              </SubmitButton>
+              <ResetButtonLink
                 disabled={submitting}
                 id="permission-template-cancel"
-                onClick={onCloseClick}
-                type="reset">
+                onClick={onCloseClick}>
                 {translate('cancel')}
-              </button>
+              </ResetButtonLink>
             </footer>
           </form>
         )}
index 1db801795fef385e9ca7e42a9d9dbf1b5a631435..e6d9b84dc8855a07f6fcd0d0d8cb056c39cea27c 100644 (file)
@@ -21,6 +21,7 @@ import * as React from 'react';
 import * as PropTypes from 'prop-types';
 import Form from './Form';
 import { createPermissionTemplate } from '../../../api/permissions';
+import { Button } from '../../../components/ui/buttons';
 import { translate } from '../../../helpers/l10n';
 
 interface Props {
@@ -50,9 +51,7 @@ export default class Header extends React.PureComponent<Props, State> {
     this.mounted = false;
   }
 
-  handleCreateClick = (event: React.SyntheticEvent<HTMLButtonElement>) => {
-    event.preventDefault();
-    event.currentTarget.blur();
+  handleCreateClick = () => {
     this.setState({ createModal: true });
   };
 
@@ -86,9 +85,7 @@ export default class Header extends React.PureComponent<Props, State> {
         {!this.props.ready && <i className="spinner" />}
 
         <div className="page-actions">
-          <button onClick={this.handleCreateClick} type="button">
-            {translate('create')}
-          </button>
+          <Button onClick={this.handleCreateClick}>{translate('create')}</Button>
 
           {this.state.createModal && (
             <Form
index e8550cef79ac1b00d9e79383289472bcb31ada13..94fe7d09868a9c38cea4b2d89171807f837756ad 100644 (file)
@@ -23,6 +23,7 @@ import { PermissionTemplate } from '../../../../app/types';
 import DeferredSpinner from '../../../../components/common/DeferredSpinner';
 import SimpleModal from '../../../../components/controls/SimpleModal';
 import Select from '../../../../components/controls/Select';
+import { SubmitButton, ResetButtonLink } from '../../../../components/ui/buttons';
 import { translateWithParameters, translate } from '../../../../helpers/l10n';
 
 interface Props {
@@ -139,13 +140,13 @@ export default class ApplyTemplate extends React.PureComponent<Props, State> {
             <footer className="modal-foot">
               <DeferredSpinner className="spacer-right" loading={submitting} />
               {!this.state.done && (
-                <button disabled={submitting || !this.state.permissionTemplate} type="submit">
+                <SubmitButton disabled={submitting || !this.state.permissionTemplate}>
                   {translate('apply')}
-                </button>
+                </SubmitButton>
               )}
-              <button className="button-link" onClick={onCloseClick} type="reset">
+              <ResetButtonLink onClick={onCloseClick}>
                 {translate(this.state.done ? 'close' : 'cancel')}
-              </button>
+              </ResetButtonLink>
             </footer>
           </form>
         )}
index bab1ba6490d35c2e59bf2a571f1740e1093fdfd3..f9017efdefa0e4a9f1c5b979544bfed6d0a8b581 100644 (file)
@@ -20,6 +20,7 @@
 import * as React from 'react';
 import ApplyTemplate from './ApplyTemplate';
 import { Component } from '../../../../app/types';
+import { Button } from '../../../../components/ui/buttons';
 import { translate } from '../../../../helpers/l10n';
 
 interface Props {
@@ -44,9 +45,7 @@ export default class PageHeader extends React.PureComponent<Props, State> {
     this.mounted = false;
   }
 
-  handleApplyTemplate = (event: React.SyntheticEvent<HTMLButtonElement>) => {
-    event.preventDefault();
-    event.currentTarget.blur();
+  handleApplyTemplate = () => {
     this.setState({ applyTemplateModal: true });
   };
 
@@ -79,9 +78,9 @@ export default class PageHeader extends React.PureComponent<Props, State> {
 
         {canApplyPermissionTemplate && (
           <div className="page-actions">
-            <button className="js-apply-template" onClick={this.handleApplyTemplate} type="button">
+            <Button className="js-apply-template" onClick={this.handleApplyTemplate}>
               {translate('projects_role.apply_template')}
-            </button>
+            </Button>
 
             {this.state.applyTemplateModal && (
               <ApplyTemplate
index 98282a76287e0bdf0031db765c09c902d696920a..efd920612cd4c1f03f00b713fb1c0df0d9edfd72 100644 (file)
@@ -20,6 +20,7 @@
 import * as React from 'react';
 import { ReportStatus, subscribe, unsubscribe } from '../../../api/report';
 import { translate, translateWithParameters } from '../../../helpers/l10n';
+import { Button } from '../../../components/ui/buttons';
 
 interface Props {
   component: string;
@@ -66,18 +67,14 @@ export default class Subscription extends React.PureComponent<Props, State> {
     }
   };
 
-  handleSubscribe = (e: React.SyntheticEvent<HTMLButtonElement>) => {
-    e.preventDefault();
-    e.currentTarget.blur();
+  handleSubscribe = () => {
     this.setState({ loading: true });
     subscribe(this.props.component)
       .then(() => this.handleSubscription(true))
       .catch(this.stopLoading);
   };
 
-  handleUnsubscribe = (e: React.SyntheticEvent<HTMLButtonElement>) => {
-    e.preventDefault();
-    e.currentTarget.blur();
+  handleUnsubscribe = () => {
     this.setState({ loading: true });
     unsubscribe(this.props.component)
       .then(() => this.handleSubscription(false))
@@ -100,7 +97,7 @@ export default class Subscription extends React.PureComponent<Props, State> {
           {translateWithParameters('report.subscribed', this.getEffectiveFrequencyText())}
         </div>
       </div>
-      <button onClick={this.handleUnsubscribe}>{translate('report.unsubscribe')}</button>
+      <Button onClick={this.handleUnsubscribe}>{translate('report.unsubscribe')}</Button>
       {this.renderLoading()}
     </div>
   );
@@ -110,9 +107,9 @@ export default class Subscription extends React.PureComponent<Props, State> {
       <p className="spacer-bottom">
         {translateWithParameters('report.unsubscribed', this.getEffectiveFrequencyText())}
       </p>
-      <button className="js-report-subscribe" onClick={this.handleSubscribe}>
+      <Button className="js-report-subscribe" onClick={this.handleSubscribe}>
         {translate('report.subscribe')}
-      </button>
+      </Button>
       {this.renderLoading()}
     </div>
   );
index 03ba0e5f118c8950ec479721e131cc1835e67aaa..488d122219e935c31449c6e785139103fe39d7b3 100644 (file)
@@ -24,12 +24,12 @@ exports[`renders when not subscribed 1`] = `
     >
       report.unsubscribed.report.frequency.montly.effective
     </p>
-    <button
+    <Button
       className="js-report-subscribe"
       onClick={[Function]}
     >
       report.subscribe
-    </button>
+    </Button>
   </div>
 </div>
 `;
@@ -53,11 +53,11 @@ exports[`renders when subscribed 1`] = `
         report.subscribed.report.frequency.montly.effective
       </div>
     </div>
-    <button
+    <Button
       onClick={[Function]}
     >
       report.unsubscribe
-    </button>
+    </Button>
   </div>
 </div>
 `;
index 4760621d29cbd908820e35651adebb9b0a23c821..127b3781189348957d86ea7ae2dab1da520e2830 100644 (file)
@@ -21,6 +21,7 @@ import * as React from 'react';
 import { deleteBranch } from '../../../api/branches';
 import { Branch } from '../../../app/types';
 import Modal from '../../../components/controls/Modal';
+import { SubmitButton, ResetButtonLink } from '../../../components/ui/buttons';
 import { translate, translateWithParameters } from '../../../helpers/l10n';
 
 interface Props {
@@ -64,11 +65,6 @@ export default class DeleteBranchModal extends React.PureComponent<Props, State>
     );
   };
 
-  handleCancelClick = (event: React.SyntheticEvent<HTMLAnchorElement>) => {
-    event.preventDefault();
-    this.props.onClose();
-  };
-
   render() {
     const { branch } = this.props;
     const header = translate('branches.delete');
@@ -84,12 +80,10 @@ export default class DeleteBranchModal extends React.PureComponent<Props, State>
           </div>
           <footer className="modal-foot">
             {this.state.loading && <i className="spinner spacer-right" />}
-            <button className="button-red" disabled={this.state.loading} type="submit">
+            <SubmitButton className="button-red" disabled={this.state.loading}>
               {translate('delete')}
-            </button>
-            <a href="#" onClick={this.handleCancelClick}>
-              {translate('cancel')}
-            </a>
+            </SubmitButton>
+            <ResetButtonLink onClick={this.props.onClose}>{translate('cancel')}</ResetButtonLink>
           </footer>
         </form>
       </Modal>
index 67a7656fe0a98bcf94d5eae890a48f389cf9486e..bc78f0395cbc0b3f320990944ee6e5fa4301d0f0 100644 (file)
@@ -20,8 +20,9 @@
 import * as React from 'react';
 import { renameBranch } from '../../../api/branches';
 import { Branch } from '../../../app/types';
-import { translate } from '../../../helpers/l10n';
 import Modal from '../../../components/controls/Modal';
+import { SubmitButton, ResetButtonLink } from '../../../components/ui/buttons';
+import { translate } from '../../../helpers/l10n';
 
 interface Props {
   branch: Branch;
@@ -68,11 +69,6 @@ export default class RenameBranchModal extends React.PureComponent<Props, State>
     );
   };
 
-  handleCancelClick = (event: React.SyntheticEvent<HTMLAnchorElement>) => {
-    event.preventDefault();
-    this.props.onClose();
-  };
-
   handleNameChange = (event: React.SyntheticEvent<HTMLInputElement>) => {
     this.setState({ name: event.currentTarget.value });
   };
@@ -110,12 +106,8 @@ export default class RenameBranchModal extends React.PureComponent<Props, State>
           </div>
           <footer className="modal-foot">
             {this.state.loading && <i className="spinner spacer-right" />}
-            <button disabled={submitDisabled} type="submit">
-              {translate('rename')}
-            </button>
-            <a href="#" onClick={this.handleCancelClick}>
-              {translate('cancel')}
-            </a>
+            <SubmitButton disabled={submitDisabled}>{translate('rename')}</SubmitButton>
+            <ResetButtonLink onClick={this.props.onClose}>{translate('cancel')}</ResetButtonLink>
           </footer>
         </form>
       </Modal>
index e862b2b7f8c8d5d4115835cc18e44d09a6030d0c..6c3b737dd4cdfafb97a645fe70bb3dc7f0cb2907 100644 (file)
@@ -19,6 +19,7 @@
  */
 import * as React from 'react';
 import { SettingValue, setSimpleSettingValue, resetSettingValue } from '../../../api/settings';
+import { Button, SubmitButton, ResetButtonLink } from '../../../components/ui/buttons';
 import { translate, translateWithParameters } from '../../../helpers/l10n';
 
 interface Props {
@@ -75,9 +76,7 @@ export default class SettingForm extends React.PureComponent<Props, State> {
     this.setState({ value: event.currentTarget.value });
   };
 
-  handleResetClick = (event: React.SyntheticEvent<HTMLButtonElement>) => {
-    event.preventDefault();
-    event.currentTarget.blur();
+  handleResetClick = () => {
     this.setState({ submitting: true });
     resetSettingValue(this.props.setting.key, this.props.project, this.props.branch).then(
       this.props.onChange,
@@ -89,11 +88,6 @@ export default class SettingForm extends React.PureComponent<Props, State> {
     );
   };
 
-  handleCancelClick = (event: React.SyntheticEvent<HTMLAnchorElement>) => {
-    event.preventDefault();
-    this.props.onClose();
-  };
-
   render() {
     const { setting } = this.props;
     const submitDisabled = this.state.submitting || this.state.value === setting.value;
@@ -128,21 +122,17 @@ export default class SettingForm extends React.PureComponent<Props, State> {
         <footer className="modal-foot">
           {!setting.inherited &&
             setting.parentValue && (
-              <button
+              <Button
                 className="pull-left"
                 disabled={this.state.submitting}
                 onClick={this.handleResetClick}
                 type="reset">
                 {translate('reset_to_default')}
-              </button>
+              </Button>
             )}
           {this.state.submitting && <i className="spinner spacer-right" />}
-          <button disabled={submitDisabled} type="submit">
-            {translate('save')}
-          </button>
-          <a href="#" onClick={this.handleCancelClick}>
-            {translate('cancel')}
-          </a>
+          <SubmitButton disabled={submitDisabled}>{translate('save')}</SubmitButton>
+          <ResetButtonLink onClick={this.props.onClose}>{translate('cancel')}</ResetButtonLink>
         </footer>
       </form>
     );
index d23beef1c6e36624df35609f979272f4cded43a8..88fd8dffca7ce73ec21c2a35da750a1fcf846cde 100644 (file)
@@ -55,7 +55,7 @@ it('cancels', () => {
   const onClose = jest.fn();
   const wrapper = shallowRender(jest.fn(), onClose);
 
-  click(wrapper.find('a'));
+  click(wrapper.find('ResetButtonLink'));
 
   return doAsync().then(() => {
     expect(onClose).toBeCalled();
index 4a9221d80e92779bd7463dde7505e9dfc17c217d..0e2a0bf65a7e59648f4d20a28d9522a84f79e035 100644 (file)
@@ -21,6 +21,7 @@
 import * as React from 'react';
 import { shallow } from 'enzyme';
 import LongBranchesPattern from '../LongBranchesPattern';
+import { click } from '../../../../helpers/testUtils';
 
 jest.mock('../../../../api/settings', () => ({
   getValues: jest.fn(() => Promise.resolve([]))
@@ -42,8 +43,7 @@ it('opens form', () => {
   const wrapper = shallow(<LongBranchesPattern project="project" />);
   wrapper.setState({ loading: false, setting: { value: 'release-.*' } });
 
-  wrapper.find('EditButton').prop<Function>('onClick')();
-  wrapper.update();
+  click(wrapper.find('EditButton'));
   expect(wrapper.find('LongBranchesPatternForm').exists()).toBeTruthy();
 
   wrapper.find('LongBranchesPatternForm').prop<Function>('onClose')();
index 3d897c3a9530d6a12bd80b5479be99efa0c0e9fc..65bb3ca6f29a2941778fb3dde918af1e0a3b5a02 100644 (file)
@@ -57,7 +57,7 @@ it('cancels', () => {
   const onClose = jest.fn();
   const wrapper = shallowRender(jest.fn(), onClose);
 
-  click(wrapper.find('a'));
+  click(wrapper.find('ResetButtonLink'));
 
   return doAsync().then(() => {
     expect(onClose).toBeCalled();
index ed442a4bdd8d600c7633af759bef844d6fb388d1..6a107645fc0d4fc92ccf5dcf21bae72e0c1ca0a3 100644 (file)
@@ -75,7 +75,7 @@ it('resets value', async () => {
   );
   expect(wrapper).toMatchSnapshot();
 
-  click(wrapper.find('button[type="reset"]'));
+  click(wrapper.find('Button'));
   expect(resetSettingValue).toBeCalledWith('foo', 'project', undefined);
 
   await new Promise(setImmediate);
index 8571673e611f9140c4abfced4c80a9a684d3fb76..3864d0c38fd53328e04ee5ad8c4c3b884612ac9a 100644 (file)
@@ -23,19 +23,17 @@ exports[`renders 1`] = `
     <footer
       className="modal-foot"
     >
-      <button
+      <SubmitButton
         className="button-red"
         disabled={false}
-        type="submit"
       >
         delete
-      </button>
-      <a
-        href="#"
-        onClick={[Function]}
+      </SubmitButton>
+      <ResetButtonLink
+        onClick={[MockFunction]}
       >
         cancel
-      </a>
+      </ResetButtonLink>
     </footer>
   </form>
 </Modal>
@@ -67,19 +65,17 @@ exports[`renders 2`] = `
       <i
         className="spinner spacer-right"
       />
-      <button
+      <SubmitButton
         className="button-red"
         disabled={true}
-        type="submit"
       >
         delete
-      </button>
-      <a
-        href="#"
-        onClick={[Function]}
+      </SubmitButton>
+      <ResetButtonLink
+        onClick={[MockFunction]}
       >
         cancel
-      </a>
+      </ResetButtonLink>
     </footer>
   </form>
 </Modal>
index 2960f04d2d912986dccaabfa046ecf070da8eb02..77c4b64b7f165ba35487859a425cdf5e465b96bc 100644 (file)
@@ -47,18 +47,16 @@ exports[`renders 1`] = `
     <footer
       className="modal-foot"
     >
-      <button
+      <SubmitButton
         disabled={true}
-        type="submit"
       >
         rename
-      </button>
-      <a
-        href="#"
-        onClick={[Function]}
+      </SubmitButton>
+      <ResetButtonLink
+        onClick={[MockFunction]}
       >
         cancel
-      </a>
+      </ResetButtonLink>
     </footer>
   </form>
 </Modal>
@@ -111,18 +109,16 @@ exports[`renders 2`] = `
     <footer
       className="modal-foot"
     >
-      <button
+      <SubmitButton
         disabled={false}
-        type="submit"
       >
         rename
-      </button>
-      <a
-        href="#"
-        onClick={[Function]}
+      </SubmitButton>
+      <ResetButtonLink
+        onClick={[MockFunction]}
       >
         cancel
-      </a>
+      </ResetButtonLink>
     </footer>
   </form>
 </Modal>
@@ -178,18 +174,16 @@ exports[`renders 3`] = `
       <i
         className="spinner spacer-right"
       />
-      <button
+      <SubmitButton
         disabled={true}
-        type="submit"
       >
         rename
-      </button>
-      <a
-        href="#"
-        onClick={[Function]}
+      </SubmitButton>
+      <ResetButtonLink
+        onClick={[MockFunction]}
       >
         cancel
-      </a>
+      </ResetButtonLink>
     </footer>
   </form>
 </Modal>
index bec3f23435720aaef902e2ce616ef01de8b69d81..1c038c720ff209c3e2a4a5c30d5c9814a5ebb668 100644 (file)
@@ -36,18 +36,16 @@ exports[`changes value 1`] = `
   <footer
     className="modal-foot"
   >
-    <button
+    <SubmitButton
       disabled={true}
-      type="submit"
     >
       save
-    </button>
-    <a
-      href="#"
-      onClick={[Function]}
+    </SubmitButton>
+    <ResetButtonLink
+      onClick={[MockFunction]}
     >
       cancel
-    </a>
+    </ResetButtonLink>
   </footer>
 </form>
 `;
@@ -88,26 +86,24 @@ exports[`resets value 1`] = `
   <footer
     className="modal-foot"
   >
-    <button
+    <Button
       className="pull-left"
       disabled={false}
       onClick={[Function]}
       type="reset"
     >
       reset_to_default
-    </button>
-    <button
+    </Button>
+    <SubmitButton
       disabled={true}
-      type="submit"
     >
       save
-    </button>
-    <a
-      href="#"
-      onClick={[Function]}
+    </SubmitButton>
+    <ResetButtonLink
+      onClick={[MockFunction]}
     >
       cancel
-    </a>
+    </ResetButtonLink>
   </footer>
 </form>
 `;
index 0e2772309d46a4c54f6cc37c29c831df5db7f13b..ddf2825d01138bfde466dc81fd85212be3bc7fba 100644 (file)
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 import * as React from 'react';
+import { Button } from '../../../components/ui/buttons';
 import { translate } from '../../../helpers/l10n';
 
 interface Props {
   onClearAll: () => void;
 }
 
-export default class ClearAll extends React.PureComponent<Props> {
-  handleClick = (event: React.SyntheticEvent<HTMLButtonElement>) => {
-    event.preventDefault();
-    event.currentTarget.blur();
-    this.props.onClearAll();
-  };
-
-  render() {
-    return (
-      <div className="projects-facets-reset">
-        <button className="button-red" onClick={this.handleClick}>
-          {translate('clear_all_filters')}
-        </button>
-      </div>
-    );
-  }
+export default function ClearAll({ onClearAll }: Props) {
+  return (
+    <div className="projects-facets-reset">
+      <Button className="button-red" onClick={onClearAll}>
+        {translate('clear_all_filters')}
+      </Button>
+    </div>
+  );
 }
index 6978b60dcda445f0acdc74f622b81970a9b79e5b..8ff409b905a3784e8cb18583a7e63527be3ed72f 100644 (file)
@@ -29,6 +29,6 @@ it('renders', () => {
 it('clears all', () => {
   const onClearAll = jest.fn();
   const wrapper = shallow(<ClearAll onClearAll={onClearAll} />);
-  click(wrapper.find('button'));
+  click(wrapper.find('Button'));
   expect(onClearAll).toBeCalled();
 });
index f38303cf520ae01362d1aece78bd23b399b6a32a..19ca26cc2a67a1822a7e6381a3764f976c491569 100644 (file)
@@ -20,6 +20,7 @@
 import * as React from 'react';
 import { shallow } from 'enzyme';
 import ProjectsSortingSelect from '../ProjectsSortingSelect';
+import { click } from '../../../../helpers/testUtils';
 
 it('should render correctly for overall view', () => {
   expect(
@@ -84,6 +85,6 @@ it('reverses sorting', () => {
       view="overall"
     />
   );
-  wrapper.find('ButtonIcon').prop<Function>('onClick')();
+  click(wrapper.find('ButtonIcon'));
   expect(onChange).toBeCalledWith('size', false);
 });
index 5f61d820953d185618cb6a11926b79834c2e2a07..2340e0bbf0349ea011f8efff12f77efe134045ae 100644 (file)
@@ -4,11 +4,11 @@ exports[`renders 1`] = `
 <div
   className="projects-facets-reset"
 >
-  <button
+  <Button
     className="button-red"
-    onClick={[Function]}
+    onClick={[MockFunction]}
   >
     clear_all_filters
-  </button>
+  </Button>
 </div>
 `;
index 69bb55161c70d398a9f3005c8134c8478b8466b9..663c51055777d9fc644e23969ef5536fd3cbe7f8 100644 (file)
@@ -24,6 +24,7 @@ import { translate, translateWithParameters } from '../../helpers/l10n';
 import AlertWarnIcon from '../../components/icons-components/AlertWarnIcon';
 import Modal from '../../components/controls/Modal';
 import Select from '../../components/controls/Select';
+import { Button, ResetButtonLink } from '../../components/ui/buttons';
 
 export interface Props {
   analyzedBefore?: string;
@@ -78,11 +79,6 @@ export default class BulkApplyTemplateModal extends React.PureComponent<Props, S
     );
   }
 
-  handleCancelClick = (event: React.SyntheticEvent<HTMLAnchorElement>) => {
-    event.preventDefault();
-    this.props.onClose();
-  };
-
   handleConfirmClick = () => {
     const { permissionTemplate } = this.state;
     if (permissionTemplate) {
@@ -180,13 +176,13 @@ export default class BulkApplyTemplateModal extends React.PureComponent<Props, S
           {!loading &&
             !done &&
             permissionTemplates && (
-              <button disabled={submitting} onClick={this.handleConfirmClick}>
+              <Button disabled={submitting} onClick={this.handleConfirmClick}>
                 {translate('apply')}
-              </button>
+              </Button>
             )}
-          <a className="js-modal-close" href="#" onClick={this.handleCancelClick}>
+          <ResetButtonLink className="js-modal-close" onClick={this.props.onClose}>
             {done ? translate('close') : translate('cancel')}
-          </a>
+          </ResetButtonLink>
         </footer>
       </Modal>
     );
index 359fc4198c16c15760165ed287cee3a9d3dcc9b6..d603ab0a5d4511bb444b21ac80fad09c77581eec 100644 (file)
@@ -21,8 +21,9 @@ import * as React from 'react';
 import * as classNames from 'classnames';
 import { Organization, Visibility } from '../../app/types';
 import UpgradeOrganizationBox from '../../components/common/UpgradeOrganizationBox';
-import { translate } from '../../helpers/l10n';
 import Modal from '../../components/controls/Modal';
+import { Button, ResetButtonLink } from '../../components/ui/buttons';
+import { translate } from '../../helpers/l10n';
 
 export interface Props {
   onClose: () => void;
@@ -40,13 +41,7 @@ export default class ChangeVisibilityForm extends React.PureComponent<Props, Sta
     this.state = { visibility: props.organization.projectVisibility as Visibility };
   }
 
-  handleCancelClick = (event: React.SyntheticEvent<HTMLAnchorElement>) => {
-    event.preventDefault();
-    this.props.onClose();
-  };
-
-  handleConfirmClick = (event: React.SyntheticEvent<HTMLButtonElement>) => {
-    event.preventDefault();
+  handleConfirmClick = () => {
     this.props.onConfirm(this.state.visibility);
     this.props.onClose();
   };
@@ -111,12 +106,12 @@ export default class ChangeVisibilityForm extends React.PureComponent<Props, Sta
         </div>
 
         <footer className="modal-foot">
-          <button className="js-confirm" onClick={this.handleConfirmClick}>
+          <Button className="js-confirm" onClick={this.handleConfirmClick}>
             {translate('organization.change_visibility_form.submit')}
-          </button>
-          <a className="js-modal-close" href="#" onClick={this.handleCancelClick}>
+          </Button>
+          <ResetButtonLink className="js-modal-close" onClick={this.props.onClose}>
             {translate('cancel')}
-          </a>
+          </ResetButtonLink>
         </footer>
       </Modal>
     );
index b43aadab9c78a19f4334904c69e58ea7a18c1922..5a39b28c5f8857922fcc63d0b1b4a7d3b5f4b26c 100644 (file)
 import * as React from 'react';
 import { Link } from 'react-router';
 import { FormattedMessage } from 'react-intl';
+import { createProject } from '../../api/components';
 import { Organization } from '../../app/types';
 import UpgradeOrganizationBox from '../../components/common/UpgradeOrganizationBox';
 import VisibilitySelector from '../../components/common/VisibilitySelector';
-import { createProject } from '../../api/components';
+import Modal from '../../components/controls/Modal';
+import { SubmitButton, ResetButtonLink } from '../../components/ui/buttons';
 import { translate } from '../../helpers/l10n';
 import { getProjectUrl } from '../../helpers/urls';
-import Modal from '../../components/controls/Modal';
 
 interface Props {
   onClose: () => void;
@@ -79,11 +80,6 @@ export default class CreateProjectForm extends React.PureComponent<Props, State>
     this.mounted = false;
   }
 
-  handleCancelClick = (event: React.SyntheticEvent<HTMLAnchorElement>) => {
-    event.preventDefault();
-    this.props.onClose();
-  };
-
   handleAdvancedClick = (event: React.SyntheticEvent<HTMLAnchorElement>) => {
     event.preventDefault();
     event.currentTarget.blur();
@@ -155,13 +151,12 @@ export default class CreateProjectForm extends React.PureComponent<Props, State>
             </div>
 
             <footer className="modal-foot">
-              <a
-                href="#"
+              <ResetButtonLink
                 id="create-project-close"
-                onClick={this.handleCancelClick}
-                ref={node => (this.closeButton = node)}>
+                innerRef={node => (this.closeButton = node)}
+                onClick={this.props.onClose}>
                 {translate('close')}
-              </a>
+              </ResetButtonLink>
             </footer>
           </div>
         ) : (
@@ -243,12 +238,12 @@ export default class CreateProjectForm extends React.PureComponent<Props, State>
 
             <footer className="modal-foot">
               {this.state.loading && <i className="spinner spacer-right" />}
-              <button disabled={this.state.loading} id="create-project-submit" type="submit">
+              <SubmitButton disabled={this.state.loading} id="create-project-submit">
                 {translate('create')}
-              </button>
-              <a href="#" id="create-project-cancel" onClick={this.handleCancelClick}>
+              </SubmitButton>
+              <ResetButtonLink id="create-project-cancel" onClick={this.props.onClose}>
                 {translate('cancel')}
-              </a>
+              </ResetButtonLink>
             </footer>
           </form>
         )}
index a9b45bb2a52a8e32474a1af43d0b5de76f8eedde..9300d2bc96b83b9d302c08b86a447d25bb9a6a42 100644 (file)
  */
 import * as React from 'react';
 import { bulkDeleteProjects } from '../../api/components';
-import { translate, translateWithParameters } from '../../helpers/l10n';
-import AlertWarnIcon from '../../components/icons-components/AlertWarnIcon';
 import Modal from '../../components/controls/Modal';
+import AlertWarnIcon from '../../components/icons-components/AlertWarnIcon';
+import { Button, ResetButtonLink } from '../../components/ui/buttons';
+import { translate, translateWithParameters } from '../../helpers/l10n';
 
 export interface Props {
   analyzedBefore?: string;
@@ -51,11 +52,6 @@ export default class DeleteModal extends React.PureComponent<Props, State> {
     this.mounted = false;
   }
 
-  handleCancelClick = (event: React.SyntheticEvent<HTMLAnchorElement>) => {
-    event.preventDefault();
-    this.props.onClose();
-  };
-
   handleConfirmClick = () => {
     this.setState({ loading: true });
     const parameters = this.props.selection.length
@@ -112,15 +108,15 @@ export default class DeleteModal extends React.PureComponent<Props, State> {
 
         <footer className="modal-foot">
           {this.state.loading && <i className="spinner spacer-right" />}
-          <button
+          <Button
             className="button-red"
             disabled={this.state.loading}
             onClick={this.handleConfirmClick}>
             {translate('delete')}
-          </button>
-          <a className="js-modal-close" href="#" onClick={this.handleCancelClick}>
+          </Button>
+          <ResetButtonLink className="js-modal-close" onClick={this.props.onClose}>
             {translate('cancel')}
-          </a>
+          </ResetButtonLink>
         </footer>
       </Modal>
     );
index 3b2b14d39b176fefa5610a4668967a987fb8dfb4..90015c3cf5dfa2da54b004b6a9c954169013f70a 100644 (file)
@@ -20,8 +20,8 @@
 import * as React from 'react';
 import ChangeVisibilityForm from './ChangeVisibilityForm';
 import { Organization, Visibility } from '../../app/types';
+import { EditButton, Button } from '../../components/ui/buttons';
 import { translate } from '../../helpers/l10n';
-import { EditButton } from '../../components/ui/buttons';
 
 export interface Props {
   hasProvisionPermission?: boolean;
@@ -37,11 +37,6 @@ interface State {
 export default class Header extends React.PureComponent<Props, State> {
   state: State = { visibilityForm: false };
 
-  handleCreateProjectClick = (event: React.SyntheticEvent<HTMLButtonElement>) => {
-    event.preventDefault();
-    this.props.onProjectCreate();
-  };
-
   handleChangeVisibilityClick = () => {
     this.setState({ visibilityForm: true });
   };
@@ -69,9 +64,9 @@ export default class Header extends React.PureComponent<Props, State> {
             />
           </span>
           {this.props.hasProvisionPermission && (
-            <button id="create-project" onClick={this.handleCreateProjectClick}>
+            <Button id="create-project" onClick={this.props.onProjectCreate}>
               {translate('qualifiers.create.TRK')}
-            </button>
+            </Button>
           )}
         </div>
 
index d936fafa1e981e4383c9cf199bdc9f3ce09d6216..57183554451cb7e6ea408eb3f4a04339c466620f 100644 (file)
@@ -22,6 +22,7 @@ import { FormattedMessage } from 'react-intl';
 import { Project } from './utils';
 import { grantPermissionToUser } from '../../api/permissions';
 import Modal from '../../components/controls/Modal';
+import { SubmitButton, ResetButtonLink } from '../../components/ui/buttons';
 import { translate } from '../../helpers/l10n';
 
 interface Props {
@@ -47,11 +48,6 @@ export default class RestoreAccessModal extends React.PureComponent<Props, State
     this.mounted = false;
   }
 
-  handleCancelClick = (event: React.SyntheticEvent<HTMLAnchorElement>) => {
-    event.preventDefault();
-    this.props.onClose();
-  };
-
   handleFormSubmit = (event: React.SyntheticEvent<HTMLFormElement>) => {
     event.preventDefault();
     this.setState({ loading: true });
@@ -96,12 +92,8 @@ export default class RestoreAccessModal extends React.PureComponent<Props, State
 
           <footer className="modal-foot">
             {this.state.loading && <i className="spinner spacer-right" />}
-            <button disabled={this.state.loading} type="submit">
-              {translate('restore')}
-            </button>
-            <a className="js-modal-close" href="#" onClick={this.handleCancelClick}>
-              {translate('cancel')}
-            </a>
+            <SubmitButton disabled={this.state.loading}>{translate('restore')}</SubmitButton>
+            <ResetButtonLink onClick={this.props.onClose}>{translate('cancel')}</ResetButtonLink>
           </footer>
         </form>
       </Modal>
index 6e3fa3d02ac62e042ca2e09b0b3bfc709459e78a..c2df72d6befcda1878a1968f068685ccef97ee89 100644 (file)
@@ -30,6 +30,7 @@ import Tooltip from '../../components/controls/Tooltip';
 import DateInput from '../../components/controls/DateInput';
 import Select from '../../components/controls/Select';
 import SearchBox from '../../components/controls/SearchBox';
+import { Button } from '../../components/ui/buttons';
 
 export interface Props {
   analyzedBefore?: string;
@@ -76,9 +77,7 @@ export default class Search extends React.PureComponent<Props, State> {
     }
   };
 
-  handleDeleteClick = (event: React.SyntheticEvent<HTMLButtonElement>) => {
-    event.preventDefault();
-    event.currentTarget.blur();
+  handleDeleteClick = () => {
     this.setState({ deleteModal: true });
   };
 
@@ -91,9 +90,7 @@ export default class Search extends React.PureComponent<Props, State> {
     this.props.onDeleteProjects();
   };
 
-  handleBulkApplyTemplateClick = (event: React.SyntheticEvent<HTMLButtonElement>) => {
-    event.preventDefault();
-    event.currentTarget.blur();
+  handleBulkApplyTemplateClick = () => {
     this.setState({ bulkApplyTemplateModal: true });
   };
 
@@ -115,8 +112,8 @@ export default class Search extends React.PureComponent<Props, State> {
       <Checkbox
         checked={checked}
         id="projects-selection"
-        thirdState={thirdState}
         onCheck={this.onCheck}
+        thirdState={thirdState}
       />
     );
   };
@@ -139,13 +136,13 @@ export default class Search extends React.PureComponent<Props, State> {
           className="input-medium"
           clearable={false}
           disabled={!this.props.ready}
+          name="projects-qualifier"
+          onChange={this.handleQualifierChange}
           optionRenderer={this.renderQualifierOption}
           options={this.getQualifierOptions()}
+          searchable={false}
           value={this.props.qualifiers}
           valueRenderer={this.renderQualifierOption}
-          name="projects-qualifier"
-          onChange={this.handleQualifierChange}
-          searchable={false}
         />
       </td>
     );
@@ -155,8 +152,8 @@ export default class Search extends React.PureComponent<Props, State> {
     this.props.qualifiers === 'TRK' ? (
       <td className="thin nowrap text-middle">
         <Checkbox
-          className="link-checkbox-control"
           checked={this.props.provisioned}
+          className="link-checkbox-control"
           id="projects-provisioned"
           onCheck={this.props.onProvisionedChanged}>
           <span className="little-spacer-left">
@@ -204,19 +201,19 @@ export default class Search extends React.PureComponent<Props, State> {
                 />
               </td>
               <td className="thin nowrap text-middle">
-                <button
+                <Button
                   className="js-bulk-apply-permission-template"
                   disabled={this.props.total === 0}
                   onClick={this.handleBulkApplyTemplateClick}>
                   {translate('permission_templates.bulk_apply_permission_template')}
-                </button>
+                </Button>
                 {this.props.qualifiers === 'TRK' && (
-                  <button
+                  <Button
                     className="js-delete spacer-left button-red"
                     disabled={this.props.total === 0}
                     onClick={this.handleDeleteClick}>
                     {translate('delete')}
-                  </button>
+                  </Button>
                 )}
               </td>
             </tr>
index 09c56289581fcaf093f4fa6bd1e8d968997c24e4..2ce76067896885cdb00bd5ac1255dbb46c2352ff 100644 (file)
@@ -56,7 +56,7 @@ it('bulk applies template to all results', async () => {
   });
   expect(wrapper).toMatchSnapshot();
 
-  click(wrapper.find('button'));
+  click(wrapper.find('Button'));
   expect(bulkApplyTemplate).toBeCalledWith({
     analyzedBefore: '2017-04-08T00:00:00.000Z',
     onProvisionedOnly: true,
@@ -83,7 +83,7 @@ it('bulk applies template to selected results', async () => {
   });
   expect(wrapper).toMatchSnapshot();
 
-  click(wrapper.find('button'));
+  click(wrapper.find('Button'));
   expect(wrapper).toMatchSnapshot();
   await new Promise(setImmediate);
   expect(bulkApplyTemplate).toBeCalledWith({
index 2aaa50a288f6453a14ea112797223b9b7e687c78..853593d2058bcd700fb37b97cdab179574c08269 100644 (file)
@@ -39,7 +39,7 @@ it('deletes all projects', async () => {
   (wrapper.instance() as DeleteModal).mounted = true;
   expect(wrapper).toMatchSnapshot();
 
-  click(wrapper.find('button'));
+  click(wrapper.find('Button'));
   expect(wrapper).toMatchSnapshot();
   expect(bulkDeleteProjects).toBeCalledWith({
     analyzedBefore: '2017-04-08T00:00:00.000Z',
@@ -59,7 +59,7 @@ it('deletes selected projects', async () => {
   (wrapper.instance() as DeleteModal).mounted = true;
   expect(wrapper).toMatchSnapshot();
 
-  click(wrapper.find('button'));
+  click(wrapper.find('Button'));
   expect(wrapper).toMatchSnapshot();
   expect(bulkDeleteProjects).toBeCalledWith({ organization: 'org', projects: 'proj1,proj2' });
 
index fc6070810f93b0d499d08c1ed1a3f20e6f148b0a..2b7b17690f24bb1ec1676e999df023ce6443b725 100644 (file)
@@ -22,13 +22,12 @@ exports[`bulk applies template to all results 1`] = `
   <footer
     className="modal-foot"
   >
-    <a
+    <ResetButtonLink
       className="js-modal-close"
-      href="#"
-      onClick={[Function]}
+      onClick={[MockFunction]}
     >
       cancel
-    </a>
+    </ResetButtonLink>
   </footer>
 </Modal>
 `;
@@ -91,19 +90,18 @@ exports[`bulk applies template to all results 2`] = `
   <footer
     className="modal-foot"
   >
-    <button
+    <Button
       disabled={false}
       onClick={[Function]}
     >
       apply
-    </button>
-    <a
+    </Button>
+    <ResetButtonLink
       className="js-modal-close"
-      href="#"
-      onClick={[Function]}
+      onClick={[MockFunction]}
     >
       cancel
-    </a>
+    </ResetButtonLink>
   </footer>
 </Modal>
 `;
@@ -169,19 +167,18 @@ exports[`bulk applies template to all results 3`] = `
     <i
       className="spinner spacer-right"
     />
-    <button
+    <Button
       disabled={true}
       onClick={[Function]}
     >
       apply
-    </button>
-    <a
+    </Button>
+    <ResetButtonLink
       className="js-modal-close"
-      href="#"
-      onClick={[Function]}
+      onClick={[MockFunction]}
     >
       cancel
-    </a>
+    </ResetButtonLink>
   </footer>
 </Modal>
 `;
@@ -210,13 +207,12 @@ exports[`bulk applies template to all results 4`] = `
   <footer
     className="modal-foot"
   >
-    <a
+    <ResetButtonLink
       className="js-modal-close"
-      href="#"
-      onClick={[Function]}
+      onClick={[MockFunction]}
     >
       close
-    </a>
+    </ResetButtonLink>
   </footer>
 </Modal>
 `;
@@ -243,13 +239,12 @@ exports[`bulk applies template to selected results 1`] = `
   <footer
     className="modal-foot"
   >
-    <a
+    <ResetButtonLink
       className="js-modal-close"
-      href="#"
-      onClick={[Function]}
+      onClick={[MockFunction]}
     >
       cancel
-    </a>
+    </ResetButtonLink>
   </footer>
 </Modal>
 `;
@@ -312,19 +307,18 @@ exports[`bulk applies template to selected results 2`] = `
   <footer
     className="modal-foot"
   >
-    <button
+    <Button
       disabled={false}
       onClick={[Function]}
     >
       apply
-    </button>
-    <a
+    </Button>
+    <ResetButtonLink
       className="js-modal-close"
-      href="#"
-      onClick={[Function]}
+      onClick={[MockFunction]}
     >
       cancel
-    </a>
+    </ResetButtonLink>
   </footer>
 </Modal>
 `;
@@ -390,19 +384,18 @@ exports[`bulk applies template to selected results 3`] = `
     <i
       className="spinner spacer-right"
     />
-    <button
+    <Button
       disabled={true}
       onClick={[Function]}
     >
       apply
-    </button>
-    <a
+    </Button>
+    <ResetButtonLink
       className="js-modal-close"
-      href="#"
-      onClick={[Function]}
+      onClick={[MockFunction]}
     >
       cancel
-    </a>
+    </ResetButtonLink>
   </footer>
 </Modal>
 `;
@@ -431,13 +424,12 @@ exports[`bulk applies template to selected results 4`] = `
   <footer
     className="modal-foot"
   >
-    <a
+    <ResetButtonLink
       className="js-modal-close"
-      href="#"
-      onClick={[Function]}
+      onClick={[MockFunction]}
     >
       close
-    </a>
+    </ResetButtonLink>
   </footer>
 </Modal>
 `;
index c2471bfc944054b4b683c72ab1dda1cf7930c2c0..0375c6c07e833c53f0ec752ee66621ac374401d9 100644 (file)
@@ -80,19 +80,18 @@ exports[`changes visibility 1`] = `
   <footer
     className="modal-foot"
   >
-    <button
+    <Button
       className="js-confirm"
       onClick={[Function]}
     >
       organization.change_visibility_form.submit
-    </button>
-    <a
+    </Button>
+    <ResetButtonLink
       className="js-modal-close"
-      href="#"
-      onClick={[Function]}
+      onClick={[MockFunction]}
     >
       cancel
-    </a>
+    </ResetButtonLink>
   </footer>
 </Modal>
 `;
@@ -177,19 +176,18 @@ exports[`changes visibility 2`] = `
   <footer
     className="modal-foot"
   >
-    <button
+    <Button
       className="js-confirm"
       onClick={[Function]}
     >
       organization.change_visibility_form.submit
-    </button>
-    <a
+    </Button>
+    <ResetButtonLink
       className="js-modal-close"
-      href="#"
-      onClick={[Function]}
+      onClick={[MockFunction]}
     >
       cancel
-    </a>
+    </ResetButtonLink>
   </footer>
 </Modal>
 `;
@@ -269,19 +267,18 @@ exports[`renders disabled 1`] = `
   <footer
     className="modal-foot"
   >
-    <button
+    <Button
       className="js-confirm"
       onClick={[Function]}
     >
       organization.change_visibility_form.submit
-    </button>
-    <a
+    </Button>
+    <ResetButtonLink
       className="js-modal-close"
-      href="#"
-      onClick={[Function]}
+      onClick={[MockFunction]}
     >
       cancel
-    </a>
+    </ResetButtonLink>
   </footer>
 </Modal>
 `;
index 71af5dfa82dc314f375953cc0c92dfd21ab796cf..deec1e2e7fda72839b6585133813dc2a0aa54d80 100644 (file)
@@ -100,20 +100,18 @@ exports[`creates project 1`] = `
     <footer
       className="modal-foot"
     >
-      <button
+      <SubmitButton
         disabled={false}
         id="create-project-submit"
-        type="submit"
       >
         create
-      </button>
-      <a
-        href="#"
+      </SubmitButton>
+      <ResetButtonLink
         id="create-project-cancel"
-        onClick={[Function]}
+        onClick={[MockFunction]}
       >
         cancel
-      </a>
+      </ResetButtonLink>
     </footer>
   </form>
 </Modal>
@@ -219,20 +217,18 @@ exports[`creates project 2`] = `
     <footer
       className="modal-foot"
     >
-      <button
+      <SubmitButton
         disabled={false}
         id="create-project-submit"
-        type="submit"
       >
         create
-      </button>
-      <a
-        href="#"
+      </SubmitButton>
+      <ResetButtonLink
         id="create-project-cancel"
-        onClick={[Function]}
+        onClick={[MockFunction]}
       >
         cancel
-      </a>
+      </ResetButtonLink>
     </footer>
   </form>
 </Modal>
@@ -341,20 +337,18 @@ exports[`creates project 3`] = `
       <i
         className="spinner spacer-right"
       />
-      <button
+      <SubmitButton
         disabled={true}
         id="create-project-submit"
-        type="submit"
       >
         create
-      </button>
-      <a
-        href="#"
+      </SubmitButton>
+      <ResetButtonLink
         id="create-project-cancel"
-        onClick={[Function]}
+        onClick={[MockFunction]}
       >
         cancel
-      </a>
+      </ResetButtonLink>
     </footer>
   </form>
 </Modal>
@@ -407,13 +401,13 @@ exports[`creates project 4`] = `
     <footer
       className="modal-foot"
     >
-      <a
-        href="#"
+      <ResetButtonLink
         id="create-project-close"
-        onClick={[Function]}
+        innerRef={[Function]}
+        onClick={[MockFunction]}
       >
         close
-      </a>
+      </ResetButtonLink>
     </footer>
   </div>
 </Modal>
@@ -519,20 +513,18 @@ exports[`shows more 1`] = `
     <footer
       className="modal-foot"
     >
-      <button
+      <SubmitButton
         disabled={false}
         id="create-project-submit"
-        type="submit"
       >
         create
-      </button>
-      <a
-        href="#"
+      </SubmitButton>
+      <ResetButtonLink
         id="create-project-cancel"
-        onClick={[Function]}
+        onClick={[MockFunction]}
       >
         cancel
-      </a>
+      </ResetButtonLink>
     </footer>
   </form>
 </Modal>
@@ -645,20 +637,18 @@ exports[`shows more 2`] = `
     <footer
       className="modal-foot"
     >
-      <button
+      <SubmitButton
         disabled={false}
         id="create-project-submit"
-        type="submit"
       >
         create
-      </button>
-      <a
-        href="#"
+      </SubmitButton>
+      <ResetButtonLink
         id="create-project-cancel"
-        onClick={[Function]}
+        onClick={[MockFunction]}
       >
         cancel
-      </a>
+      </ResetButtonLink>
     </footer>
   </form>
 </Modal>
index 77ea7ac64fc097310c392eebfd1a7326b975880c..3fed0f28acdffc4565383f5eb5c086304bb998b7 100644 (file)
@@ -28,20 +28,19 @@ exports[`deletes all projects 1`] = `
   <footer
     className="modal-foot"
   >
-    <button
+    <Button
       className="button-red"
       disabled={false}
       onClick={[Function]}
     >
       delete
-    </button>
-    <a
+    </Button>
+    <ResetButtonLink
       className="js-modal-close"
-      href="#"
-      onClick={[Function]}
+      onClick={[MockFunction]}
     >
       cancel
-    </a>
+    </ResetButtonLink>
   </footer>
 </Modal>
 `;
@@ -77,20 +76,19 @@ exports[`deletes all projects 2`] = `
     <i
       className="spinner spacer-right"
     />
-    <button
+    <Button
       className="button-red"
       disabled={true}
       onClick={[Function]}
     >
       delete
-    </button>
-    <a
+    </Button>
+    <ResetButtonLink
       className="js-modal-close"
-      href="#"
-      onClick={[Function]}
+      onClick={[MockFunction]}
     >
       cancel
-    </a>
+    </ResetButtonLink>
   </footer>
 </Modal>
 `;
@@ -123,20 +121,19 @@ exports[`deletes selected projects 1`] = `
   <footer
     className="modal-foot"
   >
-    <button
+    <Button
       className="button-red"
       disabled={false}
       onClick={[Function]}
     >
       delete
-    </button>
-    <a
+    </Button>
+    <ResetButtonLink
       className="js-modal-close"
-      href="#"
-      onClick={[Function]}
+      onClick={[MockFunction]}
     >
       cancel
-    </a>
+    </ResetButtonLink>
   </footer>
 </Modal>
 `;
@@ -172,20 +169,19 @@ exports[`deletes selected projects 2`] = `
     <i
       className="spinner spacer-right"
     />
-    <button
+    <Button
       className="button-red"
       disabled={true}
       onClick={[Function]}
     >
       delete
-    </button>
-    <a
+    </Button>
+    <ResetButtonLink
       className="js-modal-close"
-      href="#"
-      onClick={[Function]}
+      onClick={[MockFunction]}
     >
       cancel
-    </a>
+    </ResetButtonLink>
   </footer>
 </Modal>
 `;
index aefc34315dd1b74d612ba75a2cdd765e82796692..456011bf7c63b5a5d39c12f9cf5d83fe5a124980 100644 (file)
@@ -43,12 +43,12 @@ exports[`renders 1`] = `
         onClick={[Function]}
       />
     </span>
-    <button
+    <Button
       id="create-project"
-      onClick={[Function]}
+      onClick={[MockFunction]}
     >
       qualifiers.create.TRK
-    </button>
+    </Button>
   </div>
   <p
     className="page-description"
index 15ccbea3e9b40726474412cef61c801d5d0faeed..31530ef6a9d55995b940de4ea7ad2278067eee55 100644 (file)
@@ -130,20 +130,20 @@ exports[`render qualifiers filter 1`] = `
         <td
           className="thin nowrap text-middle"
         >
-          <button
+          <Button
             className="js-bulk-apply-permission-template"
             disabled={false}
             onClick={[Function]}
           >
             permission_templates.bulk_apply_permission_template
-          </button>
-          <button
+          </Button>
+          <Button
             className="js-delete spacer-left button-red"
             disabled={false}
             onClick={[Function]}
           >
             delete
-          </button>
+          </Button>
         </td>
       </tr>
     </tbody>
@@ -220,20 +220,20 @@ exports[`renders 1`] = `
         <td
           className="thin nowrap text-middle"
         >
-          <button
+          <Button
             className="js-bulk-apply-permission-template"
             disabled={false}
             onClick={[Function]}
           >
             permission_templates.bulk_apply_permission_template
-          </button>
-          <button
+          </Button>
+          <Button
             className="js-delete spacer-left button-red"
             disabled={false}
             onClick={[Function]}
           >
             delete
-          </button>
+          </Button>
         </td>
       </tr>
     </tbody>
index c5e3c2be4717a0659b7c9a0665c49083c2ce88d8..2fd57fb40b896ff74e06fff19f813bf6f737cc61 100644 (file)
@@ -20,8 +20,6 @@
 import * as React from 'react';
 import DeleteConditionForm from './DeleteConditionForm';
 import ThresholdInput from './ThresholdInput';
-import Checkbox from '../../../components/controls/Checkbox';
-import Select from '../../../components/controls/Select';
 import {
   Condition as ICondition,
   ConditionBase,
@@ -30,6 +28,9 @@ import {
   updateCondition
 } from '../../../api/quality-gates';
 import { Metric } from '../../../app/types';
+import Checkbox from '../../../components/controls/Checkbox';
+import Select from '../../../components/controls/Select';
+import { Button, ResetButtonLink } from '../../../components/ui/buttons';
 import { translate, getLocalizedMetricName } from '../../../helpers/l10n';
 import { formatMeasure } from '../../../helpers/measures';
 
@@ -133,14 +134,17 @@ export default class Condition extends React.PureComponent<Props, State> {
     this.props.onResetError();
   };
 
-  handleCancelClick = (e: React.SyntheticEvent<HTMLAnchorElement>) => {
-    e.preventDefault();
-    e.stopPropagation();
+  handleCancelClick = () => {
     this.props.onDeleteCondition(this.props.condition);
   };
 
-  openDeleteConditionForm = () => this.setState({ openDeleteCondition: true });
-  closeDeleteConditionForm = () => this.setState({ openDeleteCondition: false });
+  openDeleteConditionForm = () => {
+    this.setState({ openDeleteCondition: true });
+  };
+
+  closeDeleteConditionForm = () => {
+    this.setState({ openDeleteCondition: false });
+  };
 
   renderPeriodValue() {
     const { condition, metric } = this.props;
@@ -228,10 +232,10 @@ export default class Condition extends React.PureComponent<Props, State> {
         <td className="thin text-middle nowrap">
           {edit ? (
             <ThresholdInput
-              name="warning"
-              value={this.state.warning}
               metric={metric}
+              name="warning"
               onChange={this.handleWarningChange}
+              value={this.state.warning}
             />
           ) : (
             formatMeasure(condition.warning, metric.type)
@@ -241,10 +245,10 @@ export default class Condition extends React.PureComponent<Props, State> {
         <td className="thin text-middle nowrap">
           {edit ? (
             <ThresholdInput
-              name="error"
-              value={this.state.error}
               metric={metric}
+              name="error"
               onChange={this.handleErrorChange}
+              value={this.state.error}
             />
           ) : (
             formatMeasure(condition.error, metric.type)
@@ -255,17 +259,17 @@ export default class Condition extends React.PureComponent<Props, State> {
           <td className="thin text-middle nowrap">
             {condition.id ? (
               <div>
-                <button
+                <Button
                   className="update-condition"
                   disabled={!this.state.changed}
                   onClick={this.handleUpdateClick}>
                   {translate('update_verb')}
-                </button>
-                <button
+                </Button>
+                <Button
                   className="button-red delete-condition little-spacer-left"
                   onClick={this.openDeleteConditionForm}>
                   {translate('delete')}
-                </button>
+                </Button>
                 {this.state.openDeleteCondition && (
                   <DeleteConditionForm
                     condition={condition}
@@ -278,15 +282,14 @@ export default class Condition extends React.PureComponent<Props, State> {
               </div>
             ) : (
               <div>
-                <button className="add-condition" onClick={this.handleSaveClick}>
+                <Button className="add-condition" onClick={this.handleSaveClick}>
                   {translate('add_verb')}
-                </button>
-                <a
+                </Button>
+                <ResetButtonLink
                   className="cancel-add-condition spacer-left"
-                  href="#"
                   onClick={this.handleCancelClick}>
                   {translate('cancel')}
-                </a>
+                </ResetButtonLink>
               </div>
             )}
           </td>
index 4c16c04184c61c62d7a9d9cbd4cc964e5defe10d..2c0609e5f660e14625373b44fb56c2e10e99a43a 100644 (file)
  */
 import * as React from 'react';
 import * as PropTypes from 'prop-types';
-import Modal from '../../../components/controls/Modal';
 import { copyQualityGate, QualityGate } from '../../../api/quality-gates';
-import { getQualityGateUrl } from '../../../helpers/urls';
+import Modal from '../../../components/controls/Modal';
+import { Button, ResetButtonLink } from '../../../components/ui/buttons';
 import { translate } from '../../../helpers/l10n';
+import { getQualityGateUrl } from '../../../helpers/urls';
 
 interface Props {
   qualityGate: QualityGate;
@@ -56,11 +57,6 @@ export default class CopyQualityGateForm extends React.PureComponent<Props, Stat
     this.mounted = false;
   }
 
-  handleCancelClick = (event: React.SyntheticEvent<HTMLElement>) => {
-    event.preventDefault();
-    this.props.onClose();
-  };
-
   handleNameChange = (event: React.SyntheticEvent<HTMLInputElement>) => {
     this.setState({ name: event.currentTarget.value });
   };
@@ -120,12 +116,12 @@ export default class CopyQualityGateForm extends React.PureComponent<Props, Stat
           </div>
           <div className="modal-foot">
             {loading && <i className="spinner spacer-right" />}
-            <button disabled={submitDisabled} className="js-confirm">
+            <Button className="js-confirm" disabled={submitDisabled}>
               {translate('copy')}
-            </button>
-            <a href="#" className="js-modal-close" onClick={this.handleCancelClick}>
+            </Button>
+            <ResetButtonLink className="js-modal-close" onClick={this.props.onClose}>
               {translate('cancel')}
-            </a>
+            </ResetButtonLink>
           </div>
         </form>
       </Modal>
index cb228fd06adbeadee502a6bb3ebe268c8ddf6645..f34aad8bfb92ede5d02e471446ad8d87738d820b 100644 (file)
@@ -19,8 +19,9 @@
  */
 import * as React from 'react';
 import * as PropTypes from 'prop-types';
-import Modal from '../../../components/controls/Modal';
 import { createQualityGate, QualityGate } from '../../../api/quality-gates';
+import Modal from '../../../components/controls/Modal';
+import { Button, ResetButtonLink } from '../../../components/ui/buttons';
 import { translate } from '../../../helpers/l10n';
 import { getQualityGateUrl } from '../../../helpers/urls';
 
@@ -52,11 +53,6 @@ export default class CreateQualityGateForm extends React.PureComponent<Props, St
     this.mounted = false;
   }
 
-  handleCancelClick = (event: React.SyntheticEvent<HTMLElement>) => {
-    event.preventDefault();
-    this.props.onClose();
-  };
-
   handleNameChange = (event: React.SyntheticEvent<HTMLInputElement>) => {
     this.setState({ name: event.currentTarget.value });
   };
@@ -113,12 +109,12 @@ export default class CreateQualityGateForm extends React.PureComponent<Props, St
           </div>
           <div className="modal-foot">
             {loading && <i className="spinner spacer-right" />}
-            <button disabled={submitDisabled} className="js-confirm">
+            <Button className="js-confirm" disabled={submitDisabled}>
               {translate('save')}
-            </button>
-            <a href="#" className="js-modal-close" onClick={this.handleCancelClick}>
+            </Button>
+            <ResetButtonLink className="js-modal-close" onClick={this.props.onClose}>
               {translate('cancel')}
-            </a>
+            </ResetButtonLink>
           </div>
         </form>
       </Modal>
index c8f041fef39f70a719db513b4050675807eb68a8..ce41277c1ad44ab523a69143dd3f8e7bfa023a8b 100644 (file)
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 import * as React from 'react';
-import Modal from '../../../components/controls/Modal';
-import { Metric } from '../../../app/types';
 import { Condition, deleteCondition } from '../../../api/quality-gates';
+import { Metric } from '../../../app/types';
+import Modal from '../../../components/controls/Modal';
+import { SubmitButton, ResetButtonLink } from '../../../components/ui/buttons';
 import { getLocalizedMetricName, translate, translateWithParameters } from '../../../helpers/l10n';
 
 interface Props {
@@ -47,11 +48,6 @@ export default class DeleteConditionForm extends React.PureComponent<Props, Stat
     this.mounted = false;
   }
 
-  handleCancelClick = (event: React.SyntheticEvent<HTMLElement>) => {
-    event.preventDefault();
-    this.props.onClose();
-  };
-
   handleFormSubmit = (event: React.SyntheticEvent<HTMLFormElement>) => {
     event.preventDefault();
     const { organization, condition } = this.props;
@@ -86,12 +82,12 @@ export default class DeleteConditionForm extends React.PureComponent<Props, Stat
           </div>
           <div className="modal-foot">
             {this.state.loading && <i className="spinner spacer-right" />}
-            <button className="js-delete button-red" disabled={this.state.loading}>
+            <SubmitButton className="js-delete button-red" disabled={this.state.loading}>
               {translate('delete')}
-            </button>
-            <a href="#" className="js-modal-close" onClick={this.handleCancelClick}>
+            </SubmitButton>
+            <ResetButtonLink className="js-modal-close" onClick={this.props.onClose}>
               {translate('cancel')}
-            </a>
+            </ResetButtonLink>
           </div>
         </form>
       </Modal>
index b37e21d0b5d97b9e0acc590eb9d05d838fb0e831..1d0edec416ab1b4194895a1c00d7c9aeaf90644b 100644 (file)
  */
 import * as React from 'react';
 import * as PropTypes from 'prop-types';
-import Modal from '../../../components/controls/Modal';
-import { getQualityGatesUrl } from '../../../helpers/urls';
 import { deleteQualityGate, QualityGate } from '../../../api/quality-gates';
+import Modal from '../../../components/controls/Modal';
+import { SubmitButton, ResetButtonLink } from '../../../components/ui/buttons';
 import { translate, translateWithParameters } from '../../../helpers/l10n';
+import { getQualityGatesUrl } from '../../../helpers/urls';
 
 interface Props {
   onClose: () => void;
@@ -52,11 +53,6 @@ export default class DeleteQualityGateForm extends React.PureComponent<Props, St
     this.mounted = false;
   }
 
-  handleCancelClick = (event: React.SyntheticEvent<HTMLElement>) => {
-    event.preventDefault();
-    this.props.onClose();
-  };
-
   handleFormSubmit = (event: React.SyntheticEvent<HTMLFormElement>) => {
     event.preventDefault();
     const { organization, qualityGate } = this.props;
@@ -91,12 +87,12 @@ export default class DeleteQualityGateForm extends React.PureComponent<Props, St
           </div>
           <div className="modal-foot">
             {this.state.loading && <i className="spinner spacer-right" />}
-            <button className="js-delete button-red" disabled={this.state.loading}>
+            <SubmitButton className="js-delete button-red" disabled={this.state.loading}>
               {translate('delete')}
-            </button>
-            <a href="#" className="js-modal-close" onClick={this.handleCancelClick}>
+            </SubmitButton>
+            <ResetButtonLink className="js-modal-close" onClick={this.props.onClose}>
               {translate('cancel')}
-            </a>
+            </ResetButtonLink>
           </div>
         </form>
       </Modal>
index 244b2a7090bbc8598528ae589f8ddf179e1378d1..a661becfdefa5a980910d0c1384aa129e1c6c54d 100644 (file)
@@ -23,6 +23,7 @@ import RenameQualityGateForm from './RenameQualityGateForm';
 import CopyQualityGateForm from './CopyQualityGateForm';
 import DeleteQualityGateForm from './DeleteQualityGateForm';
 import { fetchQualityGate, QualityGate, setQualityGateAsDefault } from '../../../api/quality-gates';
+import { Button } from '../../../components/ui/buttons';
 import { translate } from '../../../helpers/l10n';
 
 interface Props {
@@ -41,18 +42,15 @@ interface State {
 export default class DetailsHeader extends React.PureComponent<Props, State> {
   state = { openPopup: undefined };
 
-  handleRenameClick = (e: React.SyntheticEvent<HTMLButtonElement>) => {
-    e.preventDefault();
+  handleRenameClick = () => {
     this.setState({ openPopup: 'rename' });
   };
 
-  handleCopyClick = (e: React.SyntheticEvent<HTMLButtonElement>) => {
-    e.preventDefault();
+  handleCopyClick = () => {
     this.setState({ openPopup: 'copy' });
   };
 
-  handleSetAsDefaultClick = (e: React.SyntheticEvent<HTMLButtonElement>) => {
-    e.preventDefault();
+  handleSetAsDefaultClick = () => {
     const { qualityGate, onSetAsDefault, organization } = this.props;
     if (!qualityGate.isDefault) {
       setQualityGateAsDefault({ id: qualityGate.id, organization })
@@ -61,8 +59,7 @@ export default class DetailsHeader extends React.PureComponent<Props, State> {
     }
   };
 
-  handleDeleteClick = (e: React.SyntheticEvent<HTMLButtonElement>) => {
-    e.preventDefault();
+  handleDeleteClick = () => {
     this.setState({ openPopup: 'delete' });
   };
 
@@ -83,57 +80,57 @@ export default class DetailsHeader extends React.PureComponent<Props, State> {
 
             <div className="pull-right">
               {actions.rename && (
-                <button id="quality-gate-rename" onClick={this.handleRenameClick}>
+                <Button id="quality-gate-rename" onClick={this.handleRenameClick}>
                   {translate('rename')}
-                </button>
+                </Button>
               )}
               {actions.copy && (
-                <button
+                <Button
                   className="little-spacer-left"
                   id="quality-gate-copy"
                   onClick={this.handleCopyClick}>
                   {translate('copy')}
-                </button>
+                </Button>
               )}
               {actions.setAsDefault && (
-                <button
+                <Button
                   className="little-spacer-left"
                   id="quality-gate-toggle-default"
                   onClick={this.handleSetAsDefaultClick}>
                   {translate('set_as_default')}
-                </button>
+                </Button>
               )}
               {actions.delete && (
-                <button
-                  id="quality-gate-delete"
+                <Button
                   className="little-spacer-left button-red"
+                  id="quality-gate-delete"
                   onClick={this.handleDeleteClick}>
                   {translate('delete')}
-                </button>
+                </Button>
               )}
               {openPopup === 'rename' && (
                 <RenameQualityGateForm
+                  onClose={this.handleClosePopup}
                   onRename={this.props.onRename}
                   organization={organization}
-                  onClose={this.handleClosePopup}
                   qualityGate={qualityGate}
                 />
               )}
 
               {openPopup === 'copy' && (
                 <CopyQualityGateForm
+                  onClose={this.handleClosePopup}
                   onCopy={this.props.onCopy}
                   organization={organization}
-                  onClose={this.handleClosePopup}
                   qualityGate={qualityGate}
                 />
               )}
 
               {openPopup === 'delete' && (
                 <DeleteQualityGateForm
+                  onClose={this.handleClosePopup}
                   onDelete={this.props.onDelete}
                   organization={organization}
-                  onClose={this.handleClosePopup}
                   qualityGate={qualityGate}
                 />
               )}
index efef33d5335e8d1e3cdeef2ee21a729599cccb94..4a267e581fb4f61772fc5d7f664ee615e9984c3a 100644 (file)
@@ -20,8 +20,9 @@
 import * as React from 'react';
 import CreateQualityGateForm from '../components/CreateQualityGateForm';
 import { QualityGate } from '../../../api/quality-gates';
-import { translate } from '../../../helpers/l10n';
 import { Organization } from '../../../app/types';
+import { Button } from '../../../components/ui/buttons';
+import { translate } from '../../../helpers/l10n';
 
 interface Props {
   canCreate: boolean;
@@ -36,8 +37,13 @@ interface State {
 export default class ListHeader extends React.PureComponent<Props, State> {
   state = { createQualityGateOpen: false };
 
-  openCreateQualityGateForm = () => this.setState({ createQualityGateOpen: true });
-  closeCreateQualityGateForm = () => this.setState({ createQualityGateOpen: false });
+  openCreateQualityGateForm = () => {
+    this.setState({ createQualityGateOpen: true });
+  };
+
+  closeCreateQualityGateForm = () => {
+    this.setState({ createQualityGateOpen: false });
+  };
 
   render() {
     const { organization } = this.props;
@@ -47,9 +53,9 @@ export default class ListHeader extends React.PureComponent<Props, State> {
         <h1 className="page-title">{translate('quality_gates.page')}</h1>
         {this.props.canCreate && (
           <div className="page-actions">
-            <button id="quality-gate-add" onClick={this.openCreateQualityGateForm}>
+            <Button id="quality-gate-add" onClick={this.openCreateQualityGateForm}>
               {translate('create')}
-            </button>
+            </Button>
           </div>
         )}
         {this.state.createQualityGateOpen && (
index 964f160ad4023e2f160784f51dda6c4da87a1a2f..aebd61dda7337e660ee86d05607c335e30a25ba9 100644 (file)
@@ -18,8 +18,9 @@
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 import * as React from 'react';
-import Modal from '../../../components/controls/Modal';
 import { QualityGate, renameQualityGate } from '../../../api/quality-gates';
+import Modal from '../../../components/controls/Modal';
+import { SubmitButton, ResetButtonLink } from '../../../components/ui/buttons';
 import { translate } from '../../../helpers/l10n';
 
 interface Props {
@@ -50,11 +51,6 @@ export default class RenameQualityGateForm extends React.PureComponent<Props, St
     this.mounted = false;
   }
 
-  handleCancelClick = (event: React.SyntheticEvent<HTMLElement>) => {
-    event.preventDefault();
-    this.props.onClose();
-  };
-
   handleNameChange = (event: React.SyntheticEvent<HTMLInputElement>) => {
     this.setState({ name: event.currentTarget.value });
   };
@@ -111,12 +107,12 @@ export default class RenameQualityGateForm extends React.PureComponent<Props, St
           </div>
           <div className="modal-foot">
             {loading && <i className="spinner spacer-right" />}
-            <button disabled={submitDisabled} className="js-confirm">
+            <SubmitButton className="js-confirm" disabled={submitDisabled}>
               {translate('rename')}
-            </button>
-            <a href="#" className="js-modal-close" onClick={this.handleCancelClick}>
+            </SubmitButton>
+            <ResetButtonLink className="js-modal-close" onClick={this.props.onClose}>
               {translate('cancel')}
-            </a>
+            </ResetButtonLink>
           </div>
         </form>
       </Modal>
index e4e9220ba8d20a39d830dec72bf8acc9a5b85184..f298e8f40567d6488545da41e4ab7ce0f4d1976a 100644 (file)
@@ -19,6 +19,7 @@
  */
 import * as React from 'react';
 import DateInput from '../../../components/controls/DateInput';
+import { Button } from '../../../components/ui/buttons';
 import { toShortNotSoISOString } from '../../../helpers/dates';
 import { translate } from '../../../helpers/l10n';
 
@@ -31,12 +32,6 @@ interface Props {
 }
 
 export default class ChangelogSearch extends React.PureComponent<Props> {
-  handleResetClick = (event: React.SyntheticEvent<HTMLElement>) => {
-    event.preventDefault();
-    event.currentTarget.blur();
-    this.props.onReset();
-  };
-
   formatDate = (date?: string) => (date ? toShortNotSoISOString(date) : undefined);
 
   render() {
@@ -57,9 +52,9 @@ export default class ChangelogSearch extends React.PureComponent<Props> {
           placeholder={translate('to')}
           value={this.formatDate(this.props.toDate)}
         />
-        <button className="spacer-left" onClick={this.handleResetClick}>
+        <Button className="spacer-left" onClick={this.props.onReset}>
           {translate('reset_verb')}
-        </button>
+        </Button>
       </div>
     );
   }
index 454bc713ba69540efefd250422b6e17066d279d4..e0812fa388d2c0408c702a585f21181d776d3dfe 100644 (file)
@@ -29,10 +29,10 @@ it('should render DateInput', () => {
   const output = shallow(
     <ChangelogSearch
       fromDate="2016-01-01"
-      toDate="2016-05-05"
       onFromDateChange={onFromDateChange}
-      onToDateChange={onToDateChange}
       onReset={jest.fn()}
+      onToDateChange={onToDateChange}
+      toDate="2016-05-05"
     />
   );
   const dateInputs = output.find(DateInput);
@@ -48,13 +48,13 @@ it('should reset', () => {
   const output = shallow(
     <ChangelogSearch
       fromDate="2016-01-01"
-      toDate="2016-05-05"
       onFromDateChange={jest.fn()}
-      onToDateChange={jest.fn()}
       onReset={onReset}
+      onToDateChange={jest.fn()}
+      toDate="2016-05-05"
     />
   );
   expect(onReset).not.toBeCalled();
-  click(output.find('button'));
+  click(output.find('Button'));
   expect(onReset).toBeCalled();
 });
index 7d04a1f3809866287705bedf489c150ffda1b078..39cd96b4c881e69366a3c2a67a584b0163d72bcd 100644 (file)
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 import * as React from 'react';
+import { Profile } from '../types';
 import { copyProfile } from '../../../api/quality-profiles';
 import Modal from '../../../components/controls/Modal';
+import { SubmitButton, ResetButtonLink } from '../../../components/ui/buttons';
 import { translate, translateWithParameters } from '../../../helpers/l10n';
-import { Profile } from '../types';
 
 interface Props {
   onClose: () => void;
@@ -47,11 +48,6 @@ export default class CopyProfileForm extends React.PureComponent<Props, State> {
     this.mounted = false;
   }
 
-  handleCancelClick = (event: React.SyntheticEvent<HTMLElement>) => {
-    event.preventDefault();
-    this.props.onClose();
-  };
-
   handleNameChange = (event: React.SyntheticEvent<HTMLInputElement>) => {
     this.setState({ name: event.currentTarget.value });
   };
@@ -112,12 +108,12 @@ export default class CopyProfileForm extends React.PureComponent<Props, State> {
           </div>
           <div className="modal-foot">
             {this.state.loading && <i className="spinner spacer-right" />}
-            <button disabled={submitDisabled} id="copy-profile-submit">
+            <SubmitButton disabled={submitDisabled} id="copy-profile-submit">
               {translate('copy')}
-            </button>
-            <a href="#" id="copy-profile-cancel" onClick={this.handleCancelClick}>
+            </SubmitButton>
+            <ResetButtonLink id="copy-profile-cancel" onClick={this.props.onClose}>
               {translate('cancel')}
-            </a>
+            </ResetButtonLink>
           </div>
         </form>
       </Modal>
index 1b564ec9be0812d414a41c2e75b21185f5e8196d..39260d39eb73a33d4eb5f246eb5b637abd8217b8 100644 (file)
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 import * as React from 'react';
+import { Profile } from '../types';
 import { deleteProfile } from '../../../api/quality-profiles';
 import Modal from '../../../components/controls/Modal';
+import { SubmitButton, ResetButtonLink } from '../../../components/ui/buttons';
 import { translate, translateWithParameters } from '../../../helpers/l10n';
-import { Profile } from '../types';
 
 interface Props {
   onClose: () => void;
@@ -47,11 +48,6 @@ export default class DeleteProfileForm extends React.PureComponent<Props, State>
     this.mounted = false;
   }
 
-  handleCancelClick = (event: React.SyntheticEvent<HTMLElement>) => {
-    event.preventDefault();
-    this.props.onClose();
-  };
-
   handleFormSubmit = (event: React.SyntheticEvent<HTMLFormElement>) => {
     event.preventDefault();
     this.setState({ loading: true });
@@ -100,12 +96,15 @@ export default class DeleteProfileForm extends React.PureComponent<Props, State>
           </div>
           <div className="modal-foot">
             {this.state.loading && <i className="spinner spacer-right" />}
-            <button className="button-red" disabled={this.state.loading} id="delete-profile-submit">
+            <SubmitButton
+              className="button-red"
+              disabled={this.state.loading}
+              id="delete-profile-submit">
               {translate('delete')}
-            </button>
-            <a href="#" id="delete-profile-cancel" onClick={this.handleCancelClick}>
+            </SubmitButton>
+            <ResetButtonLink id="delete-profile-cancel" onClick={this.props.onClose}>
               {translate('cancel')}
-            </a>
+            </ResetButtonLink>
           </div>
         </form>
       </Modal>
index 34b610bc7d8e04416e228e927ca8369445a7923a..fd04227ede9b1201aefa07914f425fa5e6ec9db8 100644 (file)
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 import * as React from 'react';
+import { Profile } from '../types';
 import { renameProfile } from '../../../api/quality-profiles';
 import Modal from '../../../components/controls/Modal';
+import { SubmitButton, ResetButtonLink } from '../../../components/ui/buttons';
 import { translate, translateWithParameters } from '../../../helpers/l10n';
-import { Profile } from '../types';
 
 interface Props {
   onClose: () => void;
@@ -47,11 +48,6 @@ export default class RenameProfileForm extends React.PureComponent<Props, State>
     this.mounted = false;
   }
 
-  handleCancelClick = (event: React.SyntheticEvent<HTMLElement>) => {
-    event.preventDefault();
-    this.props.onClose();
-  };
-
   handleNameChange = (event: React.SyntheticEvent<HTMLInputElement>) => {
     this.setState({ name: event.currentTarget.value });
   };
@@ -112,12 +108,12 @@ export default class RenameProfileForm extends React.PureComponent<Props, State>
           </div>
           <div className="modal-foot">
             {this.state.loading && <i className="spinner spacer-right" />}
-            <button disabled={submitDisabled} id="rename-profile-submit">
+            <SubmitButton disabled={submitDisabled} id="rename-profile-submit">
               {translate('rename')}
-            </button>
-            <a href="#" id="rename-profile-cancel" onClick={this.handleCancelClick}>
+            </SubmitButton>
+            <ResetButtonLink id="rename-profile-cancel" onClick={this.props.onClose}>
               {translate('cancel')}
-            </a>
+            </ResetButtonLink>
           </div>
         </form>
       </Modal>
index b1978c6d310572706bb4fc045b9ea461cf977535..b783eb7e411039b01c82849af21961c319fa0b8f 100644 (file)
  */
 import * as React from 'react';
 import { sortBy } from 'lodash';
+import { Profile } from '../types';
 import { changeProfileParent } from '../../../api/quality-profiles';
 import Modal from '../../../components/controls/Modal';
 import Select from '../../../components/controls/Select';
+import { SubmitButton, ResetButtonLink } from '../../../components/ui/buttons';
 import { translate } from '../../../helpers/l10n';
-import { Profile } from '../types';
 
 interface Props {
   onChange: () => void;
@@ -53,11 +54,6 @@ export default class ChangeParentForm extends React.PureComponent<Props, State>
     this.mounted = false;
   }
 
-  handleCancelClick = (event: React.SyntheticEvent<HTMLElement>) => {
-    event.preventDefault();
-    this.props.onClose();
-  };
-
   handleSelectChange = (option: { value: string }) => {
     this.setState({ selected: option.value });
   };
@@ -126,12 +122,12 @@ export default class ChangeParentForm extends React.PureComponent<Props, State>
           </div>
           <div className="modal-foot">
             {this.state.loading && <i className="spinner spacer-right" />}
-            <button disabled={submitDisabled} id="change-profile-parent-submit">
+            <SubmitButton disabled={submitDisabled} id="change-profile-parent-submit">
               {translate('change_verb')}
-            </button>
-            <a href="#" id="change-profile-parent-cancel" onClick={this.handleCancelClick}>
+            </SubmitButton>
+            <ResetButtonLink id="change-profile-parent-cancel" onClick={this.props.onClose}>
               {translate('cancel')}
-            </a>
+            </ResetButtonLink>
           </div>
         </form>
       </Modal>
index 2786757c0f3e051f3b22a66261ed762e50416fe0..85deb056f70c02cb94f456e84d0ac37b78de8b86 100644 (file)
@@ -21,9 +21,10 @@ import * as React from 'react';
 import * as classNames from 'classnames';
 import ProfileInheritanceBox from './ProfileInheritanceBox';
 import ChangeParentForm from './ChangeParentForm';
-import { translate } from '../../../helpers/l10n';
-import { getProfileInheritance } from '../../../api/quality-profiles';
 import { Profile } from '../types';
+import { getProfileInheritance } from '../../../api/quality-profiles';
+import { Button } from '../../../components/ui/buttons';
+import { translate } from '../../../helpers/l10n';
 
 interface Props {
   onRequestFail: (reason: any) => void;
@@ -74,21 +75,27 @@ export default class ProfileInheritance extends React.PureComponent<Props, State
   }
 
   loadData() {
-    getProfileInheritance(this.props.profile.key).then((r: any) => {
-      if (this.mounted) {
-        const { ancestors, children } = r;
-        this.setState({
-          children,
-          ancestors: ancestors.reverse(),
-          profile: r.profile,
-          loading: false
-        });
+    getProfileInheritance(this.props.profile.key).then(
+      r => {
+        if (this.mounted) {
+          const { ancestors, children } = r;
+          this.setState({
+            children,
+            ancestors: ancestors.reverse(),
+            profile: r.profile,
+            loading: false
+          });
+        }
+      },
+      () => {
+        if (this.mounted) {
+          this.setState({ loading: false });
+        }
       }
-    });
+    );
   }
 
-  handleChangeParentClick = (event: React.SyntheticEvent<HTMLElement>) => {
-    event.preventDefault();
+  handleChangeParentClick = () => {
     this.setState({ formOpen: true });
   };
 
@@ -123,11 +130,11 @@ export default class ProfileInheritance extends React.PureComponent<Props, State
           profile.actions.edit &&
           !profile.isBuiltIn && (
             <div className="boxed-group-actions">
-              <button
+              <Button
                 className="pull-right js-change-parent"
                 onClick={this.handleChangeParentClick}>
                 {translate('quality_profiles.change_parent')}
-              </button>
+              </Button>
             </div>
           )}
 
index e77208909df9136144c0d7fa00ff58d29f66e86b..ad88c22f0a1df67fcaba10c165c99d8715d92c29 100644 (file)
@@ -27,6 +27,7 @@ import {
   searchGroups,
   SearchUsersGroupsParameters
 } from '../../../api/quality-profiles';
+import { Button } from '../../../components/ui/buttons';
 import { translate } from '../../../helpers/l10n';
 
 export interface User {
@@ -100,9 +101,7 @@ export default class ProfilePermissions extends React.PureComponent<Props, State
     );
   }
 
-  handleAddUserButtonClick = (event: React.SyntheticEvent<HTMLButtonElement>) => {
-    event.preventDefault();
-    event.currentTarget.blur();
+  handleAddUserButtonClick = () => {
     this.setState({ addUserForm: true });
   };
 
@@ -180,9 +179,9 @@ export default class ProfilePermissions extends React.PureComponent<Props, State
                   />
                 ))}
               <div className="text-right">
-                <button onClick={this.handleAddUserButtonClick}>
+                <Button onClick={this.handleAddUserButtonClick}>
                   {translate('quality_profiles.grant_permissions_to_more_users')}
-                </button>
+                </Button>
               </div>
             </div>
           )}
@@ -193,8 +192,8 @@ export default class ProfilePermissions extends React.PureComponent<Props, State
             onClose={this.handleAddUserFormClose}
             onGroupAdd={this.handleGroupAdd}
             onUserAdd={this.handleUserAdd}
-            profile={this.props.profile}
             organization={this.props.organization}
+            profile={this.props.profile}
           />
         )}
       </div>
index 06fc7c571da6b114aba4c10104451a146d8a9b86..cb5a652065aa6e80e8fd5cb7ae2c3eacacea837b 100644 (file)
@@ -28,6 +28,7 @@ import {
   SearchUsersGroupsParameters
 } from '../../../api/quality-profiles';
 import Modal from '../../../components/controls/Modal';
+import { SubmitButton, ResetButtonLink } from '../../../components/ui/buttons';
 import { translate } from '../../../helpers/l10n';
 
 interface Props {
@@ -121,20 +122,16 @@ export default class ProfilePermissionsForm extends React.PureComponent<Props, S
             <div className="modal-large-field">
               <label>{translate('quality_profiles.search_description')}</label>
               <ProfilePermissionsFormSelect
-                selected={this.state.selected}
                 onChange={this.handleValueChange}
                 onSearch={this.handleSearch}
+                selected={this.state.selected}
               />
             </div>
           </div>
           <footer className="modal-foot">
             {this.state.submitting && <i className="spinner spacer-right" />}
-            <button disabled={submitDisabled} type="submit">
-              {translate('add_verb')}
-            </button>
-            <button className="button-link" onClick={this.props.onClose} type="reset">
-              {translate('cancel')}
-            </button>
+            <SubmitButton disabled={submitDisabled}>{translate('add_verb')}</SubmitButton>
+            <ResetButtonLink onClick={this.props.onClose}>{translate('cancel')}</ResetButtonLink>
           </footer>
         </form>
       </Modal>
index 2dbb0885ee2ae7d4164202f4f48cd732c02c1631..7e859a8fe3810ab0d2c43ef06eb4625be4cf97d0 100644 (file)
@@ -23,7 +23,7 @@ import { Group } from './ProfilePermissions';
 import { removeGroup } from '../../../api/quality-profiles';
 import SimpleModal, { ChildrenProps } from '../../../components/controls/SimpleModal';
 import GroupIcon from '../../../components/icons-components/GroupIcon';
-import { DeleteButton } from '../../../components/ui/buttons';
+import { DeleteButton, Button, ResetButtonLink } from '../../../components/ui/buttons';
 import { translate } from '../../../helpers/l10n';
 
 interface Props {
@@ -91,12 +91,10 @@ export default class ProfilePermissionsGroup extends React.PureComponent<Props,
 
       <footer className="modal-foot">
         {props.submitting && <i className="spinner spacer-right" />}
-        <button className="button-red" disabled={props.submitting} onClick={props.onSubmitClick}>
+        <Button className="button-red" disabled={props.submitting} onClick={props.onSubmitClick}>
           {translate('remove')}
-        </button>
-        <a href="#" onClick={props.onCloseClick}>
-          {translate('cancel')}
-        </a>
+        </Button>
+        <ResetButtonLink onClick={props.onCloseClick}>{translate('cancel')}</ResetButtonLink>
       </footer>
     </div>
   );
index d2c2094cd6272d64240746e6d8220065a0589423..f35c21287e425a864a2d0703f223ff4dc504a14e 100644 (file)
@@ -22,7 +22,7 @@ import { FormattedMessage } from 'react-intl';
 import { User } from './ProfilePermissions';
 import { removeUser } from '../../../api/quality-profiles';
 import SimpleModal, { ChildrenProps } from '../../../components/controls/SimpleModal';
-import { DeleteButton } from '../../../components/ui/buttons';
+import { DeleteButton, SubmitButton, ResetButtonLink } from '../../../components/ui/buttons';
 import Avatar from '../../../components/ui/Avatar';
 import { translate } from '../../../helpers/l10n';
 
@@ -91,12 +91,13 @@ export default class ProfilePermissionsUser extends React.PureComponent<Props, S
 
       <footer className="modal-foot">
         {props.submitting && <i className="spinner spacer-right" />}
-        <button className="button-red" disabled={props.submitting} onClick={props.onSubmitClick}>
+        <SubmitButton
+          className="button-red"
+          disabled={props.submitting}
+          onClick={props.onSubmitClick}>
           {translate('remove')}
-        </button>
-        <a href="#" onClick={props.onCloseClick}>
-          {translate('cancel')}
-        </a>
+        </SubmitButton>
+        <ResetButtonLink onClick={props.onCloseClick}>{translate('cancel')}</ResetButtonLink>
       </footer>
     </div>
   );
index 84fd60c40fa09917a579ece8b8fa24bf96abbe61..7ac978ea69e46a380bedc8dd0a0c138c16bd91a5 100644 (file)
 import * as React from 'react';
 import { Link } from 'react-router';
 import ChangeProjectsForm from './ChangeProjectsForm';
-import QualifierIcon from '../../../components/shared/QualifierIcon';
+import { Profile } from '../types';
 import { getProfileProjects } from '../../../api/quality-profiles';
+import QualifierIcon from '../../../components/shared/QualifierIcon';
+import { Button } from '../../../components/ui/buttons';
 import { translate } from '../../../helpers/l10n';
-import { Profile } from '../types';
 
 interface Props {
   organization: string | null;
@@ -83,8 +84,7 @@ export default class ProfileProjects extends React.PureComponent<Props, State> {
     );
   }
 
-  handleChangeClick = (event: React.SyntheticEvent<HTMLElement>) => {
-    event.preventDefault();
+  handleChangeClick = () => {
     this.setState({ formOpen: true });
   };
 
@@ -124,10 +124,10 @@ export default class ProfileProjects extends React.PureComponent<Props, State> {
     return (
       <ul>
         {projects.map(project => (
-          <li key={project.uuid} className="spacer-top js-profile-project" data-key={project.key}>
+          <li className="spacer-top js-profile-project" data-key={project.key} key={project.uuid}>
             <Link
-              to={{ pathname: '/dashboard', query: { id: project.key } }}
-              className="link-with-icon">
+              className="link-with-icon"
+              to={{ pathname: '/dashboard', query: { id: project.key } }}>
               <QualifierIcon qualifier="TRK" /> <span>{project.name}</span>
             </Link>
           </li>
@@ -143,9 +143,9 @@ export default class ProfileProjects extends React.PureComponent<Props, State> {
         {profile.actions &&
           profile.actions.associateProjects && (
             <div className="boxed-group-actions">
-              <button className="js-change-projects" onClick={this.handleChangeClick}>
+              <Button className="js-change-projects" onClick={this.handleChangeClick}>
                 {translate('quality_profiles.change_projects')}
-              </button>
+              </Button>
             </div>
           )}
 
index 08452e22416f3c0c007d624331f8b5f602c7d997..ff5d9b2def5a4757465d25d804b868b0e95a8eec 100644 (file)
@@ -59,7 +59,7 @@ it('opens add users form', async () => {
   await waitAndUpdate(wrapper);
   expect(wrapper.find('ProfilePermissionsForm').exists()).toBeFalsy();
 
-  click(wrapper.find('button'));
+  click(wrapper.find('Button'));
   expect(wrapper.find('ProfilePermissionsForm').exists()).toBeTruthy();
 
   wrapper.find('ProfilePermissionsForm').prop<Function>('onClose')();
index e106e5a231cc7edad8b41f3ef8b9d0196dcea993..fa4d4662aa73400a832ea2e16ddc3994ec9e13a3 100644 (file)
@@ -25,6 +25,7 @@ jest.mock('../../../../api/quality-profiles', () => ({
 import * as React from 'react';
 import { shallow } from 'enzyme';
 import ProfilePermissionsGroup from '../ProfilePermissionsGroup';
+import { click } from '../../../../helpers/testUtils';
 
 const removeGroup = require('../../../../api/quality-profiles').removeGroup as jest.Mock<any>;
 
@@ -54,8 +55,7 @@ it('removes user', async () => {
   (wrapper.instance() as ProfilePermissionsGroup).mounted = true;
   expect(wrapper.find('SimpleModal').exists()).toBeFalsy();
 
-  wrapper.find('DeleteButton').prop<Function>('onClick')();
-  wrapper.update();
+  click(wrapper.find('DeleteButton'));
   expect(wrapper.find('SimpleModal').exists()).toBeTruthy();
 
   wrapper.find('SimpleModal').prop<Function>('onSubmit')();
index e26d1cf73220e9957379ccc48356d6b08892b8fc..7a9b63d9c4fb8f27daf505ad334d83e04c6b69e8 100644 (file)
@@ -25,6 +25,7 @@ jest.mock('../../../../api/quality-profiles', () => ({
 import * as React from 'react';
 import { shallow } from 'enzyme';
 import ProfilePermissionsUser from '../ProfilePermissionsUser';
+import { click } from '../../../../helpers/testUtils';
 
 const removeUser = require('../../../../api/quality-profiles').removeUser as jest.Mock<any>;
 
@@ -49,8 +50,7 @@ it('removes user', async () => {
   (wrapper.instance() as ProfilePermissionsUser).mounted = true;
   expect(wrapper.find('SimpleModal').exists()).toBeFalsy();
 
-  wrapper.find('DeleteButton').prop<Function>('onClick')();
-  wrapper.update();
+  click(wrapper.find('DeleteButton'));
   expect(wrapper.find('SimpleModal').exists()).toBeTruthy();
 
   wrapper.find('SimpleModal').prop<Function>('onSubmit')();
index 9dd2c1d6c4769c153dbcd845040affc6bca53e1c..29599454c98c09b103a1d2d6c11a51caf8a6c1a9 100644 (file)
@@ -78,11 +78,11 @@ exports[`renders 2`] = `
       <div
         className="text-right"
       >
-        <button
+        <Button
           onClick={[Function]}
         >
           quality_profiles.grant_permissions_to_more_users
-        </button>
+        </Button>
       </div>
     </div>
   </div>
index a4e51e5f160cdafa5608df0ab5ec0ab549503b70..4caa41a48c1a54fe6c48797a192d044d8bbd3a7a 100644 (file)
@@ -33,19 +33,16 @@ exports[`adds group 1`] = `
     <footer
       className="modal-foot"
     >
-      <button
+      <SubmitButton
         disabled={true}
-        type="submit"
       >
         add_verb
-      </button>
-      <button
-        className="button-link"
+      </SubmitButton>
+      <ResetButtonLink
         onClick={[MockFunction]}
-        type="reset"
       >
         cancel
-      </button>
+      </ResetButtonLink>
     </footer>
   </form>
 </Modal>
@@ -89,19 +86,16 @@ exports[`adds group 2`] = `
     <footer
       className="modal-foot"
     >
-      <button
+      <SubmitButton
         disabled={false}
-        type="submit"
       >
         add_verb
-      </button>
-      <button
-        className="button-link"
+      </SubmitButton>
+      <ResetButtonLink
         onClick={[MockFunction]}
-        type="reset"
       >
         cancel
-      </button>
+      </ResetButtonLink>
     </footer>
   </form>
 </Modal>
@@ -148,19 +142,16 @@ exports[`adds group 3`] = `
       <i
         className="spinner spacer-right"
       />
-      <button
+      <SubmitButton
         disabled={true}
-        type="submit"
       >
         add_verb
-      </button>
-      <button
-        className="button-link"
+      </SubmitButton>
+      <ResetButtonLink
         onClick={[MockFunction]}
-        type="reset"
       >
         cancel
-      </button>
+      </ResetButtonLink>
     </footer>
   </form>
 </Modal>
@@ -199,19 +190,16 @@ exports[`adds user 1`] = `
     <footer
       className="modal-foot"
     >
-      <button
+      <SubmitButton
         disabled={true}
-        type="submit"
       >
         add_verb
-      </button>
-      <button
-        className="button-link"
+      </SubmitButton>
+      <ResetButtonLink
         onClick={[MockFunction]}
-        type="reset"
       >
         cancel
-      </button>
+      </ResetButtonLink>
     </footer>
   </form>
 </Modal>
@@ -255,19 +243,16 @@ exports[`adds user 2`] = `
     <footer
       className="modal-foot"
     >
-      <button
+      <SubmitButton
         disabled={false}
-        type="submit"
       >
         add_verb
-      </button>
-      <button
-        className="button-link"
+      </SubmitButton>
+      <ResetButtonLink
         onClick={[MockFunction]}
-        type="reset"
       >
         cancel
-      </button>
+      </ResetButtonLink>
     </footer>
   </form>
 </Modal>
@@ -314,19 +299,16 @@ exports[`adds user 3`] = `
       <i
         className="spinner spacer-right"
       />
-      <button
+      <SubmitButton
         disabled={true}
-        type="submit"
       >
         add_verb
-      </button>
-      <button
-        className="button-link"
+      </SubmitButton>
+      <ResetButtonLink
         onClick={[MockFunction]}
-        type="reset"
       >
         cancel
-      </button>
+      </ResetButtonLink>
     </footer>
   </form>
 </Modal>
index d60ab75b1d229d4fb30cd5b8bd5484da93457636..e9fcc95b3ad9a22639bfee8d4a3ff6bd06113186 100644 (file)
@@ -22,6 +22,7 @@ import { sortBy } from 'lodash';
 import { getImporters, createQualityProfile } from '../../../api/quality-profiles';
 import Modal from '../../../components/controls/Modal';
 import Select from '../../../components/controls/Select';
+import { SubmitButton, ResetButtonLink } from '../../../components/ui/buttons';
 import { translate } from '../../../helpers/l10n';
 
 interface Props {
@@ -55,19 +56,19 @@ export default class CreateProfileForm extends React.PureComponent<Props, State>
 
   fetchImporters() {
     getImporters().then(
-      (importers: Array<{ key: string; languages: Array<string>; name: string }>) => {
+      importers => {
         if (this.mounted) {
           this.setState({ importers, preloading: false });
         }
+      },
+      () => {
+        if (this.mounted) {
+          this.setState({ preloading: false });
+        }
       }
     );
   }
 
-  handleCancelClick = (event: React.SyntheticEvent<HTMLElement>) => {
-    event.preventDefault();
-    this.props.onClose();
-  };
-
   handleNameChange = (event: React.SyntheticEvent<HTMLInputElement>) => {
     this.setState({ name: event.currentTarget.value });
   };
@@ -171,20 +172,20 @@ export default class CreateProfileForm extends React.PureComponent<Props, State>
                 </div>
               ))}
               {/* drop me when we stop supporting ie11 */}
-              <input type="hidden" name="hello-ie11" value="" />
+              <input name="hello-ie11" type="hidden" value="" />
             </div>
           )}
 
           <div className="modal-foot">
             {this.state.loading && <i className="spinner spacer-right" />}
             {!this.state.preloading && (
-              <button disabled={this.state.loading} id="create-profile-submit">
+              <SubmitButton disabled={this.state.loading} id="create-profile-submit">
                 {translate('create')}
-              </button>
+              </SubmitButton>
             )}
-            <a href="#" id="create-profile-cancel" onClick={this.handleCancelClick}>
+            <ResetButtonLink id="create-profile-cancel" onClick={this.props.onClose}>
               {translate('cancel')}
-            </a>
+            </ResetButtonLink>
           </div>
         </form>
       </Modal>
index 796daea5b2372f3e823d3acf309e2ea8cffc44ad..1373fa0f309960722a8ba3f6eb2b08b432f8696a 100644 (file)
@@ -21,10 +21,11 @@ import * as React from 'react';
 import * as PropTypes from 'prop-types';
 import CreateProfileForm from './CreateProfileForm';
 import RestoreProfileForm from './RestoreProfileForm';
-import { getProfilePath } from '../utils';
-import { translate } from '../../../helpers/l10n';
 import { Profile } from '../types';
+import { getProfilePath } from '../utils';
 import { Actions } from '../../../api/quality-profiles';
+import { Button } from '../../../components/ui/buttons';
+import { translate } from '../../../helpers/l10n';
 
 interface Props {
   actions: Actions;
@@ -49,26 +50,26 @@ export default class PageHeader extends React.PureComponent<Props, State> {
     restoreFormOpen: false
   };
 
-  handleCreateClick = (event: React.SyntheticEvent<HTMLElement>) => {
-    event.preventDefault();
-    event.currentTarget.blur();
+  handleCreateClick = () => {
     this.setState({ createFormOpen: true });
   };
 
   handleCreate = (profile: Profile) => {
-    this.props.updateProfiles().then(() => {
-      this.context.router.push(
-        getProfilePath(profile.name, profile.language, this.props.organization)
-      );
-    });
+    this.props.updateProfiles().then(
+      () => {
+        this.context.router.push(
+          getProfilePath(profile.name, profile.language, this.props.organization)
+        );
+      },
+      () => {}
+    );
   };
 
   closeCreateForm = () => {
     this.setState({ createFormOpen: false });
   };
 
-  handleRestoreClick = (event: React.SyntheticEvent<HTMLElement>) => {
-    event.preventDefault();
+  handleRestoreClick = () => {
     this.setState({ restoreFormOpen: true });
   };
 
@@ -83,15 +84,15 @@ export default class PageHeader extends React.PureComponent<Props, State> {
 
         {this.props.actions.create && (
           <div className="page-actions">
-            <button id="quality-profiles-create" onClick={this.handleCreateClick}>
+            <Button id="quality-profiles-create" onClick={this.handleCreateClick}>
               {translate('create')}
-            </button>
-            <button
+            </Button>
+            <Button
               className="little-spacer-left"
               id="quality-profiles-restore"
               onClick={this.handleRestoreClick}>
               {translate('restore')}
-            </button>
+            </Button>
           </div>
         )}
 
@@ -114,8 +115,8 @@ export default class PageHeader extends React.PureComponent<Props, State> {
           <CreateProfileForm
             languages={this.props.languages}
             onClose={this.closeCreateForm}
-            onRequestFail={this.props.onRequestFail}
             onCreate={this.handleCreate}
+            onRequestFail={this.props.onRequestFail}
             organization={this.props.organization}
           />
         )}
index d964ff019ee93b1836788314ca9b9348268d9338..a93f0e0a44ebc9487bb5a4cd493a9198852462a9 100644 (file)
@@ -21,6 +21,7 @@ import * as React from 'react';
 import { restoreQualityProfile } from '../../../api/quality-profiles';
 import Modal from '../../../components/controls/Modal';
 import { translate, translateWithParameters } from '../../../helpers/l10n';
+import { SubmitButton, ResetButtonLink } from '../../../components/ui/buttons';
 
 interface Props {
   onClose: () => void;
@@ -48,11 +49,6 @@ export default class RestoreProfileForm extends React.PureComponent<Props, State
     this.mounted = false;
   }
 
-  handleCancelClick = (event: React.SyntheticEvent<HTMLElement>) => {
-    event.preventDefault();
-    this.props.onClose();
-  };
-
   handleFormSubmit = (event: React.SyntheticEvent<HTMLFormElement>) => {
     event.preventDefault();
 
@@ -130,18 +126,18 @@ export default class RestoreProfileForm extends React.PureComponent<Props, State
           {ruleSuccesses == null ? (
             <div className="modal-foot">
               {loading && <i className="spinner spacer-right" />}
-              <button disabled={loading} id="restore-profile-submit">
+              <SubmitButton disabled={loading} id="restore-profile-submit">
                 {translate('restore')}
-              </button>
-              <a href="#" id="restore-profile-cancel" onClick={this.handleCancelClick}>
+              </SubmitButton>
+              <ResetButtonLink id="restore-profile-cancel" onClick={this.props.onClose}>
                 {translate('cancel')}
-              </a>
+              </ResetButtonLink>
             </div>
           ) : (
             <div className="modal-foot">
-              <a href="#" onClick={this.handleCancelClick}>
+              <ResetButtonLink id="restore-profile-cancel" onClick={this.props.onClose}>
                 {translate('close')}
-              </a>
+              </ResetButtonLink>
             </div>
           )}
         </form>
index 136f0b0052281aa129f69bf89b7a4e61c25309f6..b793c09a985de2aade4358dd995ff29b524825f6 100644 (file)
@@ -22,6 +22,7 @@ import { Link } from 'react-router';
 import OAuthProviders from './OAuthProviders';
 import GlobalMessagesContainer from '../../../app/components/GlobalMessagesContainer';
 import { IdentityProvider } from '../../../app/types';
+import { SubmitButton } from '../../../components/ui/buttons';
 import { translate } from '../../../helpers/l10n';
 import './LoginForm.css';
 
@@ -94,44 +95,42 @@ export default class LoginForm extends React.PureComponent<Props, State> {
             <GlobalMessagesContainer />
 
             <div className="big-spacer-bottom">
-              <label htmlFor="login" className="login-label">
+              <label className="login-label" htmlFor="login">
                 {translate('login')}
               </label>
               <input
-                type="text"
-                id="login"
-                name="login"
+                autoFocus={true}
                 className="login-input"
+                id="login"
                 maxLength={255}
-                required={true}
-                autoFocus={true}
+                name="login"
+                onChange={this.handleLoginChange}
                 placeholder={translate('login')}
+                required={true}
+                type="text"
                 value={this.state.login}
-                onChange={this.handleLoginChange}
               />
             </div>
 
             <div className="big-spacer-bottom">
-              <label htmlFor="password" className="login-label">
+              <label className="login-label" htmlFor="password">
                 {translate('password')}
               </label>
               <input
-                type="password"
+                className="login-input"
                 id="password"
                 name="password"
-                className="login-input"
-                required={true}
+                onChange={this.handlePwdChange}
                 placeholder={translate('password')}
+                required={true}
+                type="password"
                 value={this.state.password}
-                onChange={this.handlePwdChange}
               />
             </div>
 
             <div>
               <div className="text-right overflow-hidden">
-                <button name="commit" type="submit">
-                  {translate('sessions.log_in')}
-                </button>
+                <SubmitButton>{translate('sessions.log_in')}</SubmitButton>
                 <Link className="spacer-left" to="/">
                   {translate('cancel')}
                 </Link>
index fddd44f6bcd7f7a5073f5c778bf8b08b802143ea..744c0b93b0931076aa23f85602bc9f906c074056 100644 (file)
@@ -111,12 +111,9 @@ exports[`expands more options 2`] = `
       <div
         className="text-right overflow-hidden"
       >
-        <button
-          name="commit"
-          type="submit"
-        >
+        <SubmitButton>
           sessions.log_in
-        </button>
+        </SubmitButton>
         <Link
           className="spacer-left"
           onlyActiveOnIndex={false}
@@ -229,12 +226,9 @@ exports[`logs in with simple credentials 1`] = `
       <div
         className="text-right overflow-hidden"
       >
-        <button
-          name="commit"
-          type="submit"
-        >
+        <SubmitButton>
           sessions.log_in
-        </button>
+        </SubmitButton>
         <Link
           className="spacer-left"
           onlyActiveOnIndex={false}
index f3d70706104510aebba2cf92deb70d6e96d4399a..fd80e756d09f178a8e72b92c24eef862adf62bc5 100644 (file)
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 import * as React from 'react';
+import { LOGS_LEVELS } from '../utils';
 import { setLogLevel } from '../../../api/system';
 import Modal from '../../../components/controls/Modal';
+import { SubmitButton, ResetButtonLink } from '../../../components/ui/buttons';
 import { translate } from '../../../helpers/l10n';
-import { LOGS_LEVELS } from '../utils';
 
 interface Props {
   infoMsg: string;
@@ -41,11 +42,6 @@ export default class ChangeLogLevelForm extends React.PureComponent<Props, State
     this.state = { newLevel: props.logLevel, updating: false };
   }
 
-  handleCancelClick = (event: React.SyntheticEvent<HTMLElement>) => {
-    event.preventDefault();
-    this.props.onClose();
-  };
-
   handleFormSubmit = (event: React.SyntheticEvent<HTMLFormElement>) => {
     event.preventDefault();
     const { newLevel } = this.state;
@@ -72,15 +68,15 @@ export default class ChangeLogLevelForm extends React.PureComponent<Props, State
           </div>
           <div className="modal-body">
             {LOGS_LEVELS.map(level => (
-              <p key={level} className="spacer-bottom">
+              <p className="spacer-bottom" key={level}>
                 <input
-                  id={`loglevel-${level}`}
-                  type="radio"
+                  checked={level === newLevel}
                   className="spacer-right text-middle"
+                  id={`loglevel-${level}`}
                   name="system.log_levels"
-                  value={level}
-                  checked={level === newLevel}
                   onChange={this.handleLevelChange}
+                  type="radio"
+                  value={level}
                 />
                 <label className="text-middle" htmlFor={`loglevel-${level}`}>
                   {level}
@@ -96,12 +92,12 @@ export default class ChangeLogLevelForm extends React.PureComponent<Props, State
           </div>
           <div className="modal-foot">
             {updating && <i className="spinner spacer-right" />}
-            <button disabled={updating} id="set-log-level-submit">
+            <SubmitButton disabled={updating} id="set-log-level-submit">
               {translate('save')}
-            </button>
-            <a href="#" id="set-log-level-cancel" onClick={this.handleCancelClick}>
+            </SubmitButton>
+            <ResetButtonLink id="set-log-level-cancel" onClick={this.props.onClose}>
               {translate('cancel')}
-            </a>
+            </ResetButtonLink>
           </div>
         </form>
       </Modal>
index dfda4b9e904fbc0119da653c97b1c5e91d7c7890..7b718fc5ad20717a36b07f4387d1b1572d03d9de 100644 (file)
@@ -21,7 +21,7 @@ import * as React from 'react';
 import ChangeLogLevelForm from './ChangeLogLevelForm';
 import RestartForm from '../../../components/common/RestartForm';
 import { getFileNameSuffix } from '../utils';
-import { EditButton } from '../../../components/ui/buttons';
+import { EditButton, Button } from '../../../components/ui/buttons';
 import { getBaseUrl } from '../../../helpers/urls';
 import { translate } from '../../../helpers/l10n';
 
@@ -66,12 +66,21 @@ export default class PageActions extends React.PureComponent<Props, State> {
     this.handleLogsLevelClose();
   };
 
-  handleLogsLevelClose = () => this.setState({ openLogsLevelForm: false });
+  handleLogsLevelClose = () => {
+    this.setState({ openLogsLevelForm: false });
+  };
+
+  handleServerRestartOpen = () => {
+    this.setState({ openRestartForm: true });
+  };
 
-  handleServerRestartOpen = () => this.setState({ openRestartForm: true });
-  handleServerRestartClose = () => this.setState({ openRestartForm: false });
+  handleServerRestartClose = () => {
+    this.setState({ openRestartForm: false });
+  };
 
-  removeElementFocus = (event: React.SyntheticEvent<HTMLElement>) => event.currentTarget.blur();
+  removeElementFocus = (event: React.SyntheticEvent<HTMLElement>) => {
+    event.currentTarget.blur();
+  };
 
   render() {
     const infoUrl = getBaseUrl() + '/api/system/info';
@@ -85,50 +94,51 @@ export default class PageActions extends React.PureComponent<Props, State> {
             <strong className="little-spacer-left">{this.state.logLevel}</strong>
           </span>
           <EditButton
-            id="edit-logs-level-button"
             className="spacer-left button-small"
+            id="edit-logs-level-button"
             onClick={this.handleLogsLevelOpen}
           />
         </span>
         {this.props.canDownloadLogs && (
           <div className="display-inline-block dropdown spacer-left">
-            <button data-toggle="dropdown">
+            {/* TODO use Dropdown component */}
+            <Button data-toggle="dropdown">
               {translate('system.download_logs')}
               <i className="icon-dropdown little-spacer-left" />
-            </button>
+            </Button>
             <ul className="dropdown-menu">
               <li>
                 <a
+                  download="sonarqube_app.log"
                   href={logsUrl + '?process=app'}
                   id="logs-link"
-                  download="sonarqube_app.log"
                   target="_blank">
                   Main Process
                 </a>
               </li>
               <li>
                 <a
+                  download="sonarqube_ce.log"
                   href={logsUrl + '?process=ce'}
                   id="ce-logs-link"
-                  download="sonarqube_ce.log"
                   target="_blank">
                   Compute Engine
                 </a>
               </li>
               <li>
                 <a
+                  download="sonarqube_es.log"
                   href={logsUrl + '?process=es'}
                   id="es-logs-link"
-                  download="sonarqube_es.log"
                   target="_blank">
                   Search Engine
                 </a>
               </li>
               <li>
                 <a
+                  download="sonarqube_web.log"
                   href={logsUrl + '?process=web'}
                   id="web-logs-link"
-                  download="sonarqube_web.log"
                   target="_blank">
                   Web Server
                 </a>
@@ -137,21 +147,21 @@ export default class PageActions extends React.PureComponent<Props, State> {
           </div>
         )}
         <a
+          className="button spacer-left"
+          download={`sonarqube-support-info-${getFileNameSuffix(this.props.serverId)}.json`}
           href={infoUrl}
           id="download-link"
-          className="button spacer-left"
           onClick={this.removeElementFocus}
-          download={`sonarqube-support-info-${getFileNameSuffix(this.props.serverId)}.json`}
           target="_blank">
           {translate('system.download_system_info')}
         </a>
         {this.props.canRestart && (
-          <button
-            id="restart-server-button"
+          <Button
             className="spacer-left"
+            id="restart-server-button"
             onClick={this.handleServerRestartOpen}>
             {translate('system.restart_server')}
-          </button>
+          </Button>
         )}
         {this.props.canRestart &&
           this.state.openRestartForm && <RestartForm onClose={this.handleServerRestartClose} />}
index 0c857453ed933c64832677e63458913f43975177..d65fc8d487286990ed3ccf3d4631aa75f8fe8cb7 100644 (file)
@@ -93,19 +93,18 @@ exports[`should display some warning messages for non INFO levels 1`] = `
     <div
       className="modal-foot"
     >
-      <button
+      <SubmitButton
         disabled={false}
         id="set-log-level-submit"
       >
         save
-      </button>
-      <a
-        href="#"
+      </SubmitButton>
+      <ResetButtonLink
         id="set-log-level-cancel"
         onClick={[Function]}
       >
         cancel
-      </a>
+      </ResetButtonLink>
     </div>
   </form>
 </Modal>
@@ -199,19 +198,18 @@ exports[`should render correctly 1`] = `
     <div
       className="modal-foot"
     >
-      <button
+      <SubmitButton
         disabled={false}
         id="set-log-level-submit"
       >
         save
-      </button>
-      <a
-        href="#"
+      </SubmitButton>
+      <ResetButtonLink
         id="set-log-level-cancel"
         onClick={[Function]}
       >
         cancel
-      </a>
+      </ResetButtonLink>
     </div>
   </form>
 </Modal>
index dd7d6f39b3623958d4c9a83d34dfe915c63a2944..252b7a95398953f405684e6e5a434781c11bcdbb 100644 (file)
@@ -25,14 +25,14 @@ exports[`should render correctly 1`] = `
   <div
     className="display-inline-block dropdown spacer-left"
   >
-    <button
+    <Button
       data-toggle="dropdown"
     >
       system.download_logs
       <i
         className="icon-dropdown little-spacer-left"
       />
-    </button>
+    </Button>
     <ul
       className="dropdown-menu"
     >
@@ -88,13 +88,13 @@ exports[`should render correctly 1`] = `
   >
     system.download_system_info
   </a>
-  <button
+  <Button
     className="spacer-left"
     id="restart-server-button"
     onClick={[Function]}
   >
     system.restart_server
-  </button>
+  </Button>
 </div>
 `;
 
index a39c44965b341189bef6187952a7953877f7aa2b..15f4e8a3b10473d0cabd3617b5332651043c9a3a 100644 (file)
  */
 import * as React from 'react';
 import SystemUpgradeForm from './SystemUpgradeForm';
+import { sortUpgrades, groupUpgrades } from '../../utils';
 import { getSystemUpgrades, SystemUpgrade } from '../../../../api/system';
+import { Button } from '../../../../components/ui/buttons';
 import { translate } from '../../../../helpers/l10n';
-import { sortUpgrades, groupUpgrades } from '../../utils';
 
 interface State {
   systemUpgrades: SystemUpgrade[][];
@@ -51,8 +52,13 @@ export default class SystemUpgradeNotif extends React.PureComponent<{}, State> {
       () => {}
     );
 
-  handleOpenSystemUpgradeForm = () => this.setState({ openSystemUpgradeForm: true });
-  handleCloseSystemUpgradeForm = () => this.setState({ openSystemUpgradeForm: false });
+  handleOpenSystemUpgradeForm = () => {
+    this.setState({ openSystemUpgradeForm: true });
+  };
+
+  handleCloseSystemUpgradeForm = () => {
+    this.setState({ openSystemUpgradeForm: false });
+  };
 
   render() {
     const { systemUpgrades } = this.state;
@@ -65,14 +71,14 @@ export default class SystemUpgradeNotif extends React.PureComponent<{}, State> {
       <div className="page-notifs">
         <div className="alert alert-info">
           {translate('system.new_version_available')}
-          <button className="spacer-left" onClick={this.handleOpenSystemUpgradeForm}>
+          <Button className="spacer-left" onClick={this.handleOpenSystemUpgradeForm}>
             {translate('learn_more')}
-          </button>
+          </Button>
         </div>
         {this.state.openSystemUpgradeForm && (
           <SystemUpgradeForm
-            systemUpgrades={systemUpgrades}
             onClose={this.handleCloseSystemUpgradeForm}
+            systemUpgrades={systemUpgrades}
           />
         )}
       </div>
index 0ae8979a9d7d1bab72cd4c2de6a1afcad3903304..29d8533cd82069c8df15dc1ea71048b0fe2f9ee7 100644 (file)
@@ -103,6 +103,6 @@ it('should fetch upgrade when mounting', () => {
 it('should open the upgrade form', async () => {
   const wrapper = shallow(<SystemUpgradeNotif />);
   await waitAndUpdate(wrapper);
-  click(wrapper.find('button'));
+  click(wrapper.find('Button'));
   expect(wrapper.find('SystemUpgradeForm').exists()).toBeTruthy();
 });
index f1b0b8650204f046f1549c35b646812a6da45c80..50c3d8ee70ec8597fdc78636b15c10e9139bd0f6 100644 (file)
@@ -8,12 +8,12 @@ exports[`should display correctly 1`] = `
     className="alert alert-info"
   >
     system.new_version_available
-    <button
+    <Button
       className="spacer-left"
       onClick={[Function]}
     >
       learn_more
-    </button>
+    </Button>
   </div>
 </div>
 `;
index 40da82493a839638f45268bd626615ec5eb07332..7694dfc182d199a474342b426a8007d4b34e5177 100644 (file)
@@ -93,17 +93,29 @@ exports[`creates new organization 3`] = `
         color="#d4333f"
         onClick={[Function]}
       >
-        <button
+        <Button
           className="button-small button-icon"
           onClick={[Function]}
+          stopPropagation={true}
           style={
             Object {
               "color": "#d4333f",
             }
           }
         >
-          <ClearIcon />
-        </button>
+          <button
+            className="button button-small button-icon"
+            onClick={[Function]}
+            style={
+              Object {
+                "color": "#d4333f",
+              }
+            }
+            type="button"
+          >
+            <ClearIcon />
+          </button>
+        </Button>
       </ButtonIcon>
     </DeleteButton>
   </div>
@@ -130,17 +142,29 @@ exports[`deletes organization 1`] = `
         color="#d4333f"
         onClick={[Function]}
       >
-        <button
+        <Button
           className="button-small button-icon"
           onClick={[Function]}
+          stopPropagation={true}
           style={
             Object {
               "color": "#d4333f",
             }
           }
         >
-          <ClearIcon />
-        </button>
+          <button
+            className="button button-small button-icon"
+            onClick={[Function]}
+            style={
+              Object {
+                "color": "#d4333f",
+              }
+            }
+            type="button"
+          >
+            <ClearIcon />
+          </button>
+        </Button>
       </ButtonIcon>
     </DeleteButton>
   </div>
index 5f389a66391ca5d3c161623261432d0b7c483bef..a6682fb787017cacf6d00f6246c3a3b6b7f64186 100644 (file)
@@ -117,17 +117,29 @@ exports[`creates new project 3`] = `
           color="#d4333f"
           onClick={[Function]}
         >
-          <button
+          <Button
             className="button-small text-middle button-icon"
             onClick={[Function]}
+            stopPropagation={true}
             style={
               Object {
                 "color": "#d4333f",
               }
             }
           >
-            <ClearIcon />
-          </button>
+            <button
+              className="button button-small text-middle button-icon"
+              onClick={[Function]}
+              style={
+                Object {
+                  "color": "#d4333f",
+                }
+              }
+              type="button"
+            >
+              <ClearIcon />
+            </button>
+          </Button>
         </ButtonIcon>
       </DeleteButton>
     </div>
@@ -163,17 +175,29 @@ exports[`deletes project 1`] = `
           color="#d4333f"
           onClick={[Function]}
         >
-          <button
+          <Button
             className="button-small text-middle button-icon"
             onClick={[Function]}
+            stopPropagation={true}
             style={
               Object {
                 "color": "#d4333f",
               }
             }
           >
-            <ClearIcon />
-          </button>
+            <button
+              className="button button-small text-middle button-icon"
+              onClick={[Function]}
+              style={
+                Object {
+                  "color": "#d4333f",
+                }
+              }
+              type="button"
+            >
+              <ClearIcon />
+            </button>
+          </Button>
         </ButtonIcon>
       </DeleteButton>
     </div>
index 332780eddec98bc45e34cd7aa7efd152631eb16f..86d6f2a627cefe1c8868ce149b80f783da23e00d 100644 (file)
@@ -264,17 +264,29 @@ exports[`generates token 3`] = `
               color="#d4333f"
               onClick={[Function]}
             >
-              <button
+              <Button
                 className="button-small text-middle button-icon"
                 onClick={[Function]}
+                stopPropagation={true}
                 style={
                   Object {
                     "color": "#d4333f",
                   }
                 }
               >
-                <ClearIcon />
-              </button>
+                <button
+                  className="button button-small text-middle button-icon"
+                  onClick={[Function]}
+                  style={
+                    Object {
+                      "color": "#d4333f",
+                    }
+                  }
+                  type="button"
+                >
+                  <ClearIcon />
+                </button>
+              </Button>
             </ButtonIcon>
           </DeleteButton>
         </form>
@@ -362,17 +374,29 @@ exports[`revokes token 1`] = `
               color="#d4333f"
               onClick={[Function]}
             >
-              <button
+              <Button
                 className="button-small text-middle button-icon"
                 onClick={[Function]}
+                stopPropagation={true}
                 style={
                   Object {
                     "color": "#d4333f",
                   }
                 }
               >
-                <ClearIcon />
-              </button>
+                <button
+                  className="button button-small text-middle button-icon"
+                  onClick={[Function]}
+                  style={
+                    Object {
+                      "color": "#d4333f",
+                    }
+                  }
+                  type="button"
+                >
+                  <ClearIcon />
+                </button>
+              </Button>
             </ButtonIcon>
           </DeleteButton>
         </form>
index 2bd0aafd5b8972f87950ddf6692e7b7bc35cf976..a7a6a001caa03c7fd13c0c4c461a9aedb2ae1bbb 100644 (file)
@@ -17,13 +17,22 @@ bar
 bar"
       tooltipPlacement="top"
     >
-      <button
+      <Button
         className="js-copy-to-clipboard no-select"
         data-clipboard-text="foo
 bar"
+        innerRef={[Function]}
       >
-        copy
-      </button>
+        <button
+          className="button js-copy-to-clipboard no-select"
+          data-clipboard-text="foo
+bar"
+          onClick={[Function]}
+          type="button"
+        >
+          copy
+        </button>
+      </Button>
     </ClipboardButton>
   </div>
 </Command>
index 7131a7eb0f0ad729d4615737a7f1ffd6d90f2d37..658087041942238a07cb4760ed5851a8c796cff8 100644 (file)
@@ -20,6 +20,7 @@
 import * as React from 'react';
 import UserForm from './components/UserForm';
 import DeferredSpinner from '../../components/common/DeferredSpinner';
+import { Button } from '../../components/ui/buttons';
 import { translate } from '../../helpers/l10n';
 
 interface Props {
@@ -34,19 +35,24 @@ interface State {
 export default class Header extends React.PureComponent<Props, State> {
   state: State = { openUserForm: false };
 
-  handleOpenUserForm = () => this.setState({ openUserForm: true });
-  handleCloseUserForm = () => this.setState({ openUserForm: false });
+  handleOpenUserForm = () => {
+    this.setState({ openUserForm: true });
+  };
+
+  handleCloseUserForm = () => {
+    this.setState({ openUserForm: false });
+  };
 
   render() {
     return (
-      <header id="users-header" className="page-header">
+      <header className="page-header" id="users-header">
         <h1 className="page-title">{translate('users.page')}</h1>
         <DeferredSpinner loading={this.props.loading} />
 
         <div className="page-actions">
-          <button id="users-create" onClick={this.handleOpenUserForm}>
+          <Button id="users-create" onClick={this.handleOpenUserForm}>
             {translate('users.create_user')}
-          </button>
+          </Button>
         </div>
 
         <p className="page-description">{translate('users.page.description')}</p>
index 6e38124302c5ebe028fb7f90085ae441de516388..7acecae4f39e15b55bb6da32f41c41511e44e93c 100644 (file)
@@ -17,12 +17,12 @@ exports[`should render correctly 1`] = `
   <div
     className="page-actions"
   >
-    <button
+    <Button
       id="users-create"
       onClick={[Function]}
     >
       users.create_user
-    </button>
+    </Button>
   </div>
   <p
     className="page-description"
index 7b34c4c3fc89db0ae4b4b3a6bc3977b6efc4f220..062a463d444330f2129cf848458a337afa1e84fd 100644 (file)
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 import * as React from 'react';
-import Modal from '../../../components/controls/Modal';
 import { deactivateUser } from '../../../api/users';
 import { User } from '../../../app/types';
+import Modal from '../../../components/controls/Modal';
+import { SubmitButton, ResetButtonLink } from '../../../components/ui/buttons';
 import { translate, translateWithParameters } from '../../../helpers/l10n';
 
 export interface Props {
@@ -45,11 +46,6 @@ export default class DeactivateForm extends React.PureComponent<Props, State> {
     this.mounted = false;
   }
 
-  handleCancelClick = (event: React.SyntheticEvent<HTMLAnchorElement>) => {
-    event.preventDefault();
-    this.props.onClose();
-  };
-
   handleDeactivate = (event: React.SyntheticEvent<HTMLFormElement>) => {
     event.preventDefault();
     this.setState({ submitting: true });
@@ -73,7 +69,7 @@ export default class DeactivateForm extends React.PureComponent<Props, State> {
     const header = translate('users.deactivate_user');
     return (
       <Modal contentLabel={header} onRequestClose={this.props.onClose}>
-        <form id="deactivate-user-form" onSubmit={this.handleDeactivate} autoComplete="off">
+        <form autoComplete="off" id="deactivate-user-form" onSubmit={this.handleDeactivate}>
           <header className="modal-head">
             <h2>{header}</h2>
           </header>
@@ -82,12 +78,12 @@ export default class DeactivateForm extends React.PureComponent<Props, State> {
           </div>
           <footer className="modal-foot">
             {submitting && <i className="spinner spacer-right" />}
-            <button className="js-confirm button-red" disabled={submitting} type="submit">
+            <SubmitButton className="js-confirm button-red" disabled={submitting}>
               {translate('users.deactivate')}
-            </button>
-            <a className="js-modal-close" href="#" onClick={this.handleCancelClick}>
+            </SubmitButton>
+            <ResetButtonLink className="js-modal-close" onClick={this.props.onClose}>
               {translate('cancel')}
-            </a>
+            </ResetButtonLink>
           </footer>
         </form>
       </Modal>
index 675b42761ced337afaf21ccb84c751965e301844..00872246c35a08c26c25a102840a0bf211946d94 100644 (file)
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 import * as React from 'react';
-import Modal from '../../../components/controls/Modal';
+import { changePassword } from '../../../api/users';
+import { User } from '../../../app/types';
 import addGlobalSuccessMessage from '../../../app/utils/addGlobalSuccessMessage';
 import throwGlobalError from '../../../app/utils/throwGlobalError';
-import { User } from '../../../app/types';
-import { parseError } from '../../../helpers/request';
-import { changePassword } from '../../../api/users';
+import Modal from '../../../components/controls/Modal';
+import { SubmitButton, ResetButtonLink } from '../../../components/ui/buttons';
 import { translate } from '../../../helpers/l10n';
+import { parseError } from '../../../helpers/request';
 
 interface Props {
   isCurrentUser: boolean;
@@ -77,11 +78,6 @@ export default class PasswordForm extends React.PureComponent<Props, State> {
   handleOldPasswordChange = (event: React.SyntheticEvent<HTMLInputElement>) =>
     this.setState({ oldPassword: event.currentTarget.value });
 
-  handleCancelClick = (event: React.SyntheticEvent<HTMLAnchorElement>) => {
-    event.preventDefault();
-    this.props.onClose();
-  };
-
   handleChangePassword = (event: React.SyntheticEvent<HTMLFormElement>) => {
     event.preventDefault();
     if (
@@ -106,7 +102,7 @@ export default class PasswordForm extends React.PureComponent<Props, State> {
     const header = translate('my_profile.password.title');
     return (
       <Modal contentLabel={header} onRequestClose={this.props.onClose}>
-        <form id="user-password-form" onSubmit={this.handleChangePassword} autoComplete="off">
+        <form autoComplete="off" id="user-password-form" onSubmit={this.handleChangePassword}>
           <header className="modal-head">
             <h2>{header}</h2>
           </header>
@@ -119,14 +115,14 @@ export default class PasswordForm extends React.PureComponent<Props, State> {
                   <em className="mandatory">*</em>
                 </label>
                 {/* keep this fake field to hack browser autofill */}
-                <input name="old-password-fake" type="password" className="hidden" />
+                <input className="hidden" name="old-password-fake" type="password" />
                 <input
                   id="old-user-password"
-                  name="old-password"
-                  type="password"
                   maxLength={50}
+                  name="old-password"
                   onChange={this.handleOldPasswordChange}
                   required={true}
+                  type="password"
                   value={this.state.oldPassword}
                 />
               </div>
@@ -137,14 +133,14 @@ export default class PasswordForm extends React.PureComponent<Props, State> {
                 <em className="mandatory">*</em>
               </label>
               {/* keep this fake field to hack browser autofill */}
-              <input name="password-fake" type="password" className="hidden" />
+              <input className="hidden" name="password-fake" type="password" />
               <input
                 id="user-password"
-                name="password"
-                type="password"
                 maxLength={50}
+                name="password"
                 onChange={this.handleNewPasswordChange}
                 required={true}
+                type="password"
                 value={this.state.newPassword}
               />
             </div>
@@ -154,29 +150,28 @@ export default class PasswordForm extends React.PureComponent<Props, State> {
                 <em className="mandatory">*</em>
               </label>
               {/* keep this fake field to hack browser autofill */}
-              <input name="confirm-password-fake" type="password" className="hidden" />
+              <input className="hidden" name="confirm-password-fake" type="password" />
               <input
                 id="confirm-user-password"
-                name="confirm-password"
-                type="password"
                 maxLength={50}
+                name="confirm-password"
                 onChange={this.handleConfirmPasswordChange}
                 required={true}
+                type="password"
                 value={this.state.confirmPassword}
               />
             </div>
           </div>
           <footer className="modal-foot">
             {submitting && <i className="spinner spacer-right" />}
-            <button
+            <SubmitButton
               className="js-confirm"
-              disabled={submitting || !newPassword || newPassword !== confirmPassword}
-              type="submit">
+              disabled={submitting || !newPassword || newPassword !== confirmPassword}>
               {translate('change_verb')}
-            </button>
-            <a className="js-modal-close" href="#" onClick={this.handleCancelClick}>
+            </SubmitButton>
+            <ResetButtonLink className="js-modal-close" onClick={this.props.onClose}>
               {translate('cancel')}
-            </a>
+            </ResetButtonLink>
           </footer>
         </form>
       </Modal>
index 0869b13fbaf4c09262a669024bdf7e6b2350ced8..98d6c1e234ea537f8ec6544cef328d116803e06f 100644 (file)
@@ -20,8 +20,9 @@
 import * as React from 'react';
 import TokensFormItem from './TokensFormItem';
 import TokensFormNewToken from './TokensFormNewToken';
-import DeferredSpinner from '../../../components/common/DeferredSpinner';
 import { getTokens, generateToken, UserToken } from '../../../api/user-tokens';
+import DeferredSpinner from '../../../components/common/DeferredSpinner';
+import { SubmitButton } from '../../../components/ui/buttons';
 import { translate } from '../../../helpers/l10n';
 
 interface Props {
@@ -119,7 +120,7 @@ export default class TokensForm extends React.PureComponent<Props, State> {
     if (tokens.length <= 0) {
       return (
         <tr>
-          <td colSpan={3} className="note">
+          <td className="note" colSpan={3}>
             {translate('users.no_tokens')}
           </td>
         </tr>
@@ -129,8 +130,8 @@ export default class TokensForm extends React.PureComponent<Props, State> {
       <TokensFormItem
         key={token.name}
         login={this.props.login}
-        token={token}
         onRevokeToken={this.handleRevokeToken}
+        token={token}
       />
     ));
   }
@@ -147,22 +148,21 @@ export default class TokensForm extends React.PureComponent<Props, State> {
     return (
       <>
         <h3 className="spacer-bottom">{translate('users.generate_tokens')}</h3>
-        <form id="generate-token-form" onSubmit={this.handleGenerateToken} autoComplete="off">
+        <form autoComplete="off" id="generate-token-form" onSubmit={this.handleGenerateToken}>
           <input
             className="spacer-right"
-            type="text"
             maxLength={100}
             onChange={this.handleNewTokenChange}
             placeholder={translate('users.enter_token_name')}
             required={true}
+            type="text"
             value={newTokenName}
           />
-          <button
+          <SubmitButton
             className="js-generate-token"
-            disabled={generating || newTokenName.length <= 0}
-            type="submit">
+            disabled={generating || newTokenName.length <= 0}>
             {translate('users.generate')}
-          </button>
+          </SubmitButton>
         </form>
 
         {newToken && <TokensFormNewToken token={newToken} />}
index ab1a4902aa77b31ed6bc4b06fee9414294a33bde..fb48879e3b5a868fd476f47708893d33a60da415 100644 (file)
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 import * as React from 'react';
+import { revokeToken, UserToken } from '../../../api/user-tokens';
+import DeferredSpinner from '../../../components/common/DeferredSpinner';
 import Tooltip from '../../../components/controls/Tooltip';
 import DateFormatter from '../../../components/intl/DateFormatter';
-import DeferredSpinner from '../../../components/common/DeferredSpinner';
-import { revokeToken, UserToken } from '../../../api/user-tokens';
-import { limitComponentName } from '../../../helpers/path';
+import { Button } from '../../../components/ui/buttons';
 import { translate } from '../../../helpers/l10n';
+import { limitComponentName } from '../../../helpers/path';
 
 interface Props {
   login: string;
@@ -81,14 +82,14 @@ export default class TokensFormItem extends React.PureComponent<Props, State> {
           <DeferredSpinner loading={loading}>
             <i className="spinner-placeholder " />
           </DeferredSpinner>
-          <button
+          <Button
             className="button-red input-small spacer-left"
-            onClick={this.handleRevoke}
-            disabled={loading}>
+            disabled={loading}
+            onClick={this.handleRevoke}>
             {this.state.deleting
               ? translate('users.tokens.sure')
               : translate('users.tokens.revoke')}
-          </button>
+          </Button>
         </td>
       </tr>
     );
index 6aae213803869f4299d5f1f7b9e0cde785d481ce..95ccccce1260c01694071d0a204bdd2abd747f40 100644 (file)
 import * as React from 'react';
 import { uniq } from 'lodash';
 import UserScmAccountInput from './UserScmAccountInput';
-import Modal from '../../../components/controls/Modal';
-import throwGlobalError from '../../../app/utils/throwGlobalError';
-import { parseError } from '../../../helpers/request';
 import { createUser, updateUser } from '../../../api/users';
 import { User } from '../../../app/types';
+import throwGlobalError from '../../../app/utils/throwGlobalError';
+import Modal from '../../../components/controls/Modal';
+import { Button, SubmitButton, ResetButtonLink } from '../../../components/ui/buttons';
 import { translate, translateWithParameters } from '../../../helpers/l10n';
+import { parseError } from '../../../helpers/request';
 
 export interface Props {
   user?: User;
@@ -101,11 +102,6 @@ export default class UserForm extends React.PureComponent<Props, State> {
   handlePasswordChange = (event: React.SyntheticEvent<HTMLInputElement>) =>
     this.setState({ password: event.currentTarget.value });
 
-  handleCancelClick = (event: React.SyntheticEvent<HTMLAnchorElement>) => {
-    event.preventDefault();
-    this.props.onClose();
-  };
-
   handleCreateUser = (event: React.SyntheticEvent<HTMLFormElement>) => {
     event.preventDefault();
     this.setState({ submitting: true });
@@ -135,8 +131,7 @@ export default class UserForm extends React.PureComponent<Props, State> {
     }, this.handleError);
   };
 
-  handleAddScmAccount = (evt: React.SyntheticEvent<HTMLButtonElement>) => {
-    evt.preventDefault();
+  handleAddScmAccount = () => {
     this.setState(({ scmAccounts }) => ({ scmAccounts: scmAccounts.concat('') }));
   };
 
@@ -160,9 +155,9 @@ export default class UserForm extends React.PureComponent<Props, State> {
     return (
       <Modal contentLabel={header} onRequestClose={this.props.onClose}>
         <form
+          autoComplete="off"
           id="user-form"
-          onSubmit={this.props.user ? this.handleUpdateUser : this.handleCreateUser}
-          autoComplete="off">
+          onSubmit={this.props.user ? this.handleUpdateUser : this.handleCreateUser}>
           <header className="modal-head">
             <h2>{header}</h2>
           </header>
@@ -177,15 +172,15 @@ export default class UserForm extends React.PureComponent<Props, State> {
                   <em className="mandatory">*</em>
                 </label>
                 {/* keep this fake field to hack browser autofill */}
-                <input name="login-fake" type="text" className="hidden" />
+                <input className="hidden" name="login-fake" type="text" />
                 <input
                   id="create-user-login"
-                  name="login"
-                  type="text"
-                  minLength={3}
                   maxLength={255}
+                  minLength={3}
+                  name="login"
                   onChange={this.handleLoginChange}
                   required={true}
+                  type="text"
                   value={this.state.login}
                 />
                 <p className="note">{translateWithParameters('users.minimum_x_characters', 3)}</p>
@@ -197,27 +192,27 @@ export default class UserForm extends React.PureComponent<Props, State> {
                 <em className="mandatory">*</em>
               </label>
               {/* keep this fake field to hack browser autofill */}
-              <input name="name-fake" type="text" className="hidden" />
+              <input className="hidden" name="name-fake" type="text" />
               <input
                 id="create-user-name"
-                name="name"
-                type="text"
                 maxLength={200}
+                name="name"
                 onChange={this.handleNameChange}
                 required={true}
+                type="text"
                 value={this.state.name}
               />
             </div>
             <div className="modal-field">
               <label htmlFor="create-user-email">{translate('users.email')}</label>
               {/* keep this fake field to hack browser autofill */}
-              <input name="email-fake" type="email" className="hidden" />
+              <input className="hidden" name="email-fake" type="email" />
               <input
                 id="create-user-email"
-                name="email"
-                type="email"
                 maxLength={100}
+                name="email"
                 onChange={this.handleEmailChange}
+                type="email"
                 value={this.state.email}
               />
             </div>
@@ -228,14 +223,14 @@ export default class UserForm extends React.PureComponent<Props, State> {
                   <em className="mandatory">*</em>
                 </label>
                 {/* keep this fake field to hack browser autofill */}
-                <input name="password-fake" type="password" className="hidden" />
+                <input className="hidden" name="password-fake" type="password" />
                 <input
                   id="create-user-password"
-                  name="password"
-                  type="password"
                   maxLength={50}
+                  name="password"
                   onChange={this.handlePasswordChange}
                   required={true}
+                  type="password"
                   value={this.state.password}
                 />
               </div>
@@ -252,7 +247,9 @@ export default class UserForm extends React.PureComponent<Props, State> {
                 />
               ))}
               <div className="spacer-bottom">
-                <button onClick={this.handleAddScmAccount}>{translate('add_verb')}</button>
+                <Button onClick={this.handleAddScmAccount} type="reset">
+                  {translate('add_verb')}
+                </Button>
               </div>
               <p className="note">{translate('user.login_or_email_used_as_scm_account')}</p>
             </div>
@@ -260,12 +257,12 @@ export default class UserForm extends React.PureComponent<Props, State> {
 
           <footer className="modal-foot">
             {submitting && <i className="spinner spacer-right" />}
-            <button className="js-confirm" disabled={submitting} type="submit">
+            <SubmitButton className="js-confirm" disabled={submitting}>
               {user ? translate('update_verb') : translate('create')}
-            </button>
-            <a className="js-modal-close" href="#" onClick={this.handleCancelClick}>
+            </SubmitButton>
+            <ResetButtonLink className="js-modal-close" onClick={this.props.onClose}>
               {translate('cancel')}
-            </a>
+            </ResetButtonLink>
           </footer>
         </form>
       </Modal>
index 335674699283373b7aca537d90077717b494d993..6d643c45df77eed5fdc05e40b40ec28740b51c2c 100644 (file)
@@ -18,6 +18,7 @@
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 import * as React from 'react';
+import { Button } from '../ui/buttons';
 import { translate } from '../../helpers/l10n';
 
 interface Props {
@@ -25,29 +26,18 @@ interface Props {
   onReset: () => void;
 }
 
-export default class FiltersHeader extends React.PureComponent<Props> {
-  handleResetClick = (event: React.SyntheticEvent<HTMLButtonElement>) => {
-    event.preventDefault();
-    event.currentTarget.blur();
-    this.props.onReset();
-  };
+export default function FiltersHeader({ displayReset, onReset }: Props) {
+  return (
+    <div className="search-navigator-filters-header">
+      {displayReset && (
+        <div className="pull-right">
+          <Button className="button-red" id="coding-rules-clear-all-filters" onClick={onReset}>
+            {translate('clear_all_filters')}
+          </Button>
+        </div>
+      )}
 
-  render() {
-    return (
-      <div className="search-navigator-filters-header">
-        {this.props.displayReset && (
-          <div className="pull-right">
-            <button
-              className="button-red"
-              id="coding-rules-clear-all-filters"
-              onClick={this.handleResetClick}>
-              {translate('clear_all_filters')}
-            </button>
-          </div>
-        )}
-
-        <h3>{translate('filters')}</h3>
-      </div>
-    );
-  }
+      <h3>{translate('filters')}</h3>
+    </div>
+  );
 }
index 54b694ef5cb230f1f508ab816720cbfb38792bfa..eb4f31f7cfb89f10fca72bf429d6a13e1daaad14 100644 (file)
@@ -21,6 +21,7 @@ import * as React from 'react';
 import * as classNames from 'classnames';
 import { restartAndWait } from '../../api/system';
 import Modal from '../../components/controls/Modal';
+import { SubmitButton, ResetButtonLink } from '../ui/buttons';
 import { translate } from '../../helpers/l10n';
 
 interface Props {
@@ -34,11 +35,6 @@ interface State {
 export default class RestartForm extends React.PureComponent<Props, State> {
   state: State = { restarting: false };
 
-  handleCancelClick = (event: React.SyntheticEvent<HTMLElement>) => {
-    event.preventDefault();
-    this.props.onClose();
-  };
-
   handleFormSubmit = (event: React.SyntheticEvent<HTMLFormElement>) => {
     event.preventDefault();
     if (!this.state.restarting) {
@@ -71,14 +67,13 @@ export default class RestartForm extends React.PureComponent<Props, State> {
           </div>
           {!restarting && (
             <div className="modal-foot">
-              <button id="restart-server-submit">{translate('restart')}</button>
-              <a
-                href="#"
+              <SubmitButton id="restart-server-submit">{translate('restart')}</SubmitButton>
+              <ResetButtonLink
                 className="js-modal-close"
                 id="restart-server-cancel"
-                onClick={this.handleCancelClick}>
+                onClick={this.props.onClose}>
                 {translate('cancel')}
-              </a>
+              </ResetButtonLink>
             </div>
           )}
         </form>
index 39ad938beeedadf3dca35934c5d8732462b9b65b..a20842524a6133bf570ef573dae9f448d8a30522 100644 (file)
@@ -22,6 +22,7 @@ import * as classNames from 'classnames';
 import { Link } from 'react-router';
 import { LocationDescriptor } from 'history';
 import SettingsIcon from '../icons-components/SettingsIcon';
+import { Button } from '../ui/buttons';
 
 interface Props {
   className?: string;
@@ -37,7 +38,7 @@ interface Props {
 export default function ActionsDropdown({ menuPosition = 'right', ...props }: Props) {
   return (
     <div className={classNames('dropdown', props.className)}>
-      <button
+      <Button
         className={classNames('dropdown-toggle', props.toggleClassName, {
           'button-small': props.small
         })}
@@ -45,7 +46,7 @@ export default function ActionsDropdown({ menuPosition = 'right', ...props }: Pr
         onClick={props.onToggleClick}>
         <SettingsIcon className="text-text-bottom" />
         <i className="icon-dropdown little-spacer-left" />
-      </button>
+      </Button>
       <ul
         className={classNames('dropdown-menu', props.menuClassName, {
           'dropdown-menu-right': menuPosition === 'right'
index e7d0ed049f9110ea3b63aaddf7307f80c59d7a5b..6969fc0271fbdc84a39bfe2b60fa099d0db16d51 100644 (file)
@@ -21,6 +21,7 @@ import * as React from 'react';
 import * as classNames from 'classnames';
 import * as Clipboard from 'clipboard';
 import Tooltip from './Tooltip';
+import { Button } from '../ui/buttons';
 import { translate } from '../../helpers/l10n';
 
 interface Props {
@@ -35,7 +36,7 @@ interface State {
 
 export default class ClipboardButton extends React.PureComponent<Props, State> {
   clipboard?: Clipboard;
-  copyButton?: HTMLButtonElement | null;
+  copyButton?: HTMLElement | null;
   mounted = false;
   state: State = { tooltipShown: false };
 
@@ -77,19 +78,19 @@ export default class ClipboardButton extends React.PureComponent<Props, State> {
 
   render() {
     const button = (
-      <button
+      <Button
         className={classNames('js-copy-to-clipboard no-select', this.props.className)}
         data-clipboard-text={this.props.copyValue}
-        ref={node => (this.copyButton = node)}>
+        innerRef={node => (this.copyButton = node)}>
         {translate('copy')}
-      </button>
+      </Button>
     );
     if (this.state.tooltipShown) {
       return (
         <Tooltip
           defaultVisible={true}
-          placement={this.props.tooltipPlacement || 'bottom'}
           overlay={translate('copied_action')}
+          placement={this.props.tooltipPlacement || 'bottom'}
           trigger="manual">
           {button}
         </Tooltip>
index 54934d9e8ac5ff5e53daac7bc5cceb31b79359c9..0f05db2ac90a8ef937f2f9a1b7e9fbb2a29f4a67 100644 (file)
@@ -21,11 +21,10 @@ import * as React from 'react';
 import SimpleModal from './SimpleModal';
 import DeferredSpinner from '../common/DeferredSpinner';
 import { translate } from '../../helpers/l10n';
+import { SubmitButton, ResetButtonLink } from '../ui/buttons';
 
 interface Props {
-  children: (
-    props: { onClick: (event?: React.SyntheticEvent<HTMLButtonElement>) => void }
-  ) => React.ReactNode;
+  children: (props: { onClick: () => void }) => React.ReactNode;
   confirmButtonText: string;
   confirmData?: string;
   isDestructive?: boolean;
@@ -50,11 +49,7 @@ export default class ConfirmButton extends React.PureComponent<Props, State> {
     this.mounted = false;
   }
 
-  handleButtonClick = (event?: React.SyntheticEvent<HTMLButtonElement>) => {
-    if (event) {
-      event.preventDefault();
-      event.currentTarget.blur();
-    }
+  handleButtonClick = () => {
     this.setState({ modal: true });
   };
 
@@ -85,8 +80,8 @@ export default class ConfirmButton extends React.PureComponent<Props, State> {
             header={modalHeader}
             onClose={this.handleCloseModal}
             onSubmit={this.handleSubmit}>
-            {({ onCloseClick, onSubmitClick, submitting }) => (
-              <>
+            {({ onCloseClick, onFormSubmit, submitting }) => (
+              <form onSubmit={onFormSubmit}>
                 <header className="modal-head">
                   <h2>{modalHeader}</h2>
                 </header>
@@ -95,17 +90,14 @@ export default class ConfirmButton extends React.PureComponent<Props, State> {
 
                 <footer className="modal-foot">
                   <DeferredSpinner className="spacer-right" loading={submitting} />
-                  <button
+                  <SubmitButton
                     className={isDestructive ? 'button-red' : undefined}
-                    disabled={submitting}
-                    onClick={onSubmitClick}>
+                    disabled={submitting}>
                     {confirmButtonText}
-                  </button>
-                  <a href="#" onClick={onCloseClick}>
-                    {translate('cancel')}
-                  </a>
+                  </SubmitButton>
+                  <ResetButtonLink onClick={onCloseClick}>{translate('cancel')}</ResetButtonLink>
                 </footer>
-              </>
+              </form>
             )}
           </SimpleModal>
         )}
index 1022dc7fc02fab34df66f356e4edab9cf20f19c0..3b82fd1c408f3b442fa9958beb9befb4e703e654 100644 (file)
@@ -21,7 +21,7 @@ import * as React from 'react';
 
 interface RenderProps {
   closeDropdown: () => void;
-  onToggleClick: (event: React.SyntheticEvent<HTMLElement>) => void;
+  onToggleClick: (event?: React.SyntheticEvent<HTMLElement>) => void;
   open: boolean;
 }
 
@@ -75,10 +75,12 @@ export default class Dropdown extends React.PureComponent<Props, State> {
 
   closeDropdown = () => this.setState({ open: false });
 
-  handleToggleClick = (event: React.SyntheticEvent<HTMLElement>) => {
-    this.toggleNode = event.currentTarget;
-    event.preventDefault();
-    event.currentTarget.blur();
+  handleToggleClick = (event?: React.SyntheticEvent<HTMLElement>) => {
+    if (event) {
+      this.toggleNode = event.currentTarget;
+      event.preventDefault();
+      event.currentTarget.blur();
+    }
     this.setState(state => ({ open: !state.open }));
   };
 
index b277510245d4ef5c76ff20f6583a8ff0ed807109..e85d13031edadda27eda4dc12b5e2a593fa7587b 100644 (file)
@@ -21,9 +21,9 @@ import * as React from 'react';
 import Modal from '../../components/controls/Modal';
 
 export interface ChildrenProps {
-  onCloseClick: (event: React.SyntheticEvent<HTMLElement>) => void;
+  onCloseClick: (event?: React.SyntheticEvent<HTMLElement>) => void;
   onFormSubmit: (event: React.SyntheticEvent<HTMLFormElement>) => void;
-  onSubmitClick: (event: React.SyntheticEvent<HTMLElement>) => void;
+  onSubmitClick: (event?: React.SyntheticEvent<HTMLElement>) => void;
   submitting: boolean;
 }
 
@@ -56,9 +56,11 @@ export default class SimpleModal extends React.PureComponent<Props, State> {
     }
   };
 
-  handleCloseClick = (event: React.SyntheticEvent<HTMLElement>) => {
-    event.preventDefault();
-    event.currentTarget.blur();
+  handleCloseClick = (event?: React.SyntheticEvent<HTMLElement>) => {
+    if (event) {
+      event.preventDefault();
+      event.currentTarget.blur();
+    }
     this.props.onClose();
   };
 
@@ -67,9 +69,11 @@ export default class SimpleModal extends React.PureComponent<Props, State> {
     this.submit();
   };
 
-  handleSubmitClick = (event: React.SyntheticEvent<HTMLElement>) => {
-    event.preventDefault();
-    event.currentTarget.blur();
+  handleSubmitClick = (event?: React.SyntheticEvent<HTMLElement>) => {
+    if (event) {
+      event.preventDefault();
+      event.currentTarget.blur();
+    }
     this.submit();
   };
 
index 84653beb24ed386a9327ddfac04997c253ffd3ae..096dc8356ba667580a9828b2e987f3ca6aa2cca0 100644 (file)
@@ -20,6 +20,7 @@
 import * as React from 'react';
 import { shallow } from 'enzyme';
 import Dropdown from '../Dropdown';
+import { Button } from '../../ui/buttons';
 import { click } from '../../../helpers/testUtils';
 
 it('renders', () => {
@@ -32,13 +33,13 @@ it('renders', () => {
 
 it('toggles', () => {
   const wrapper = shallow(
-    <Dropdown>{({ onToggleClick }) => <button onClick={onToggleClick} />}</Dropdown>
+    <Dropdown>{({ onToggleClick }) => <Button onClick={onToggleClick} />}</Dropdown>
   );
   expect(wrapper.state()).toEqual({ open: false });
 
-  click(wrapper.find('button'));
+  click(wrapper.find('Button'));
   expect(wrapper.state()).toEqual({ open: true });
 
-  click(wrapper.find('button'));
+  click(wrapper.find('Button'));
   expect(wrapper.state()).toEqual({ open: false });
 });
index c4ea074996ca3e8dd1a1784e1a036fb413b0bf2c..dd07b347bb107ac36e56f44a58ec3f6196a3739b 100644 (file)
@@ -20,6 +20,7 @@
 import * as React from 'react';
 import { shallow } from 'enzyme';
 import SimpleModal, { ChildrenProps } from '../SimpleModal';
+import { Button } from '../../ui/buttons';
 import { click, waitAndUpdate } from '../../../helpers/testUtils';
 
 it('renders', () => {
@@ -35,22 +36,22 @@ it('renders', () => {
 
 it('closes', () => {
   const onClose = jest.fn();
-  const inner = ({ onCloseClick }: ChildrenProps) => <button onClick={onCloseClick}>close</button>;
+  const inner = ({ onCloseClick }: ChildrenProps) => <Button onClick={onCloseClick}>close</Button>;
   const wrapper = shallow(
     <SimpleModal header="" onClose={onClose} onSubmit={jest.fn()}>
       {inner}
     </SimpleModal>
   );
-  click(wrapper.find('button'));
+  click(wrapper.find('Button'));
   expect(onClose).toBeCalled();
 });
 
 it('submits', async () => {
   const onSubmit = jest.fn(() => Promise.resolve());
   const inner = ({ onSubmitClick, submitting }: ChildrenProps) => (
-    <button disabled={submitting} onClick={onSubmitClick}>
+    <Button disabled={submitting} onClick={onSubmitClick}>
       close
-    </button>
+    </Button>
   );
   const wrapper = shallow(
     <SimpleModal header="" onClose={jest.fn()} onSubmit={onSubmit}>
@@ -60,7 +61,7 @@ it('submits', async () => {
   (wrapper.instance() as SimpleModal).mounted = true;
   expect(wrapper).toMatchSnapshot();
 
-  click(wrapper.find('button'));
+  click(wrapper.find('Button'));
   expect(onSubmit).toBeCalled();
   expect(wrapper).toMatchSnapshot();
 
index 61870f7336518ef94d9bfa495a5d0184e06d00b4..4ad6724747bfe4fa1b15102804c21f821b5db3f7 100644 (file)
@@ -1,12 +1,13 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
 exports[`should display correctly 1`] = `
-<button
+<Button
   className="js-copy-to-clipboard no-select"
   data-clipboard-text="foo"
+  innerRef={[Function]}
 >
   copy
-</button>
+</Button>
 `;
 
 exports[`should display correctly 2`] = `
@@ -16,11 +17,12 @@ exports[`should display correctly 2`] = `
   placement="bottom"
   trigger="manual"
 >
-  <button
+  <Button
     className="js-copy-to-clipboard no-select"
     data-clipboard-text="foo"
+    innerRef={[Function]}
   >
     copy
-  </button>
+  </Button>
 </Tooltip>
 `;
index 359048021d7bfffe9b81a954f8114084d7914d77..49b14a9e20faf5450defe434e0a0e4d33e29cc0e 100644 (file)
@@ -14,12 +14,12 @@ exports[`submits 1`] = `
   contentLabel=""
   onRequestClose={[MockFunction]}
 >
-  <button
+  <Button
     disabled={false}
     onClick={[Function]}
   >
     close
-  </button>
+  </Button>
 </Modal>
 `;
 
@@ -28,12 +28,12 @@ exports[`submits 2`] = `
   contentLabel=""
   onRequestClose={[MockFunction]}
 >
-  <button
+  <Button
     disabled={true}
     onClick={[Function]}
   >
     close
-  </button>
+  </Button>
 </Modal>
 `;
 
@@ -42,11 +42,11 @@ exports[`submits 3`] = `
   contentLabel=""
   onRequestClose={[MockFunction]}
 >
-  <button
+  <Button
     disabled={false}
     onClick={[Function]}
   >
     close
-  </button>
+  </Button>
 </Modal>
 `;
index cfe3f068bc4bb7c6799082c00b59ec25659276e9..f1394dba8988a92952d6aea7aa9467766d9c4ace 100644 (file)
@@ -21,6 +21,7 @@ import * as React from 'react';
 import OpenCloseIcon from '../icons-components/OpenCloseIcon';
 import HelpIcon from '../icons-components/HelpIcon';
 import Tooltip from '../controls/Tooltip';
+import { Button } from '../ui/buttons';
 import { translate, translateWithParameters } from '../../helpers/l10n';
 
 interface Props {
@@ -33,14 +34,6 @@ interface Props {
 }
 
 export default class FacetHeader extends React.PureComponent<Props> {
-  handleClearClick = (event: React.SyntheticEvent<HTMLButtonElement>) => {
-    event.preventDefault();
-    event.currentTarget.blur();
-    if (this.props.onClear) {
-      this.props.onClear();
-    }
-  };
-
   handleClick = (event: React.SyntheticEvent<HTMLAnchorElement>) => {
     event.preventDefault();
     event.currentTarget.blur();
@@ -102,11 +95,11 @@ export default class FacetHeader extends React.PureComponent<Props> {
         </span>
 
         {showClearButton && (
-          <button
+          <Button
             className="search-navigator-facet-header-button button-small button-red"
-            onClick={this.handleClearClick}>
+            onClick={this.props.onClear}>
             {translate('clear')}
-          </button>
+          </Button>
         )}
       </div>
     );
index dad6166c9592c943e3429ba34ddad9fdf1c498a6..e68fe594ca17eb42e6dc2210e3fa725f638d9b48 100644 (file)
@@ -28,12 +28,12 @@ exports[`should clear 1`] = `
       x_selected.3
     </span>
   </span>
-  <button
+  <Button
     className="search-navigator-facet-header-button button-small button-red"
-    onClick={[Function]}
+    onClick={[MockFunction]}
   >
     clear
-  </button>
+  </Button>
 </div>
 `;
 
index 5d1d7b0410c4cbe624a805362ac2dc16c4c44d40..da474c888c4316d1c258a1d35efbfd52a0bed4d4 100644 (file)
  * along with this program; if not, write to the Free Software Foundation,
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
-.button-icon {
+
+/* use double selector .button-icon.button-icon to increase the specificity */
+
+.button-icon.button-icon {
   display: inline-flex;
   justify-content: center;
   align-items: center;
   color: inherit;
 }
 
-.button-icon.button-small {
+.button-icon.button-icon.button-small {
   width: var(--smallControlHeight);
   height: var(--smallControlHeight);
   padding: 0;
 }
 
-.button-icon.button-small svg {
+.button-icon.button-icon.button-small svg {
   margin-top: 0;
 }
 
-.button-icon.button-tiny {
+.button-icon.button-icon.button-tiny {
   width: var(--tinyControlHeight);
   height: var(--tinyControlHeight);
   padding: 0;
 }
 
-.button-icon:hover,
-.button-icon:focus {
+.button-icon.button-icon:hover,
+.button-icon.button-icon:focus {
   background-color: currentColor;
 }
 
-.button-icon:hover svg,
-.button-icon:focus svg {
+.button-icon.button-icon:hover svg,
+.button-icon.button-icon:focus svg {
   color: #fff;
 }
index c89c0cda24ca242f2c92d08519565f29eb01a466..82992ba0a6478ab2f517a5199c75e64993c84406 100644 (file)
 import * as React from 'react';
 import * as classNames from 'classnames';
 import * as theme from '../../app/theme';
+import { Omit } from '../../app/types';
 import ClearIcon from '../icons-components/ClearIcon';
 import EditIcon from '../icons-components/EditIcon';
 import Tooltip from '../controls/Tooltip';
 import './buttons.css';
 
-interface ButtonIconProps {
-  children: React.ReactNode;
+interface ButtonProps {
   className?: string;
-  color?: string;
-  onClick?: () => void;
-  tooltip?: string;
-  [x: string]: any;
+  children?: React.ReactNode;
+  disabled?: boolean;
+  id?: string;
+  innerRef?: (node: HTMLElement | null) => void;
+  onClick?: (event: React.MouseEvent<HTMLElement>) => void;
+  preventDefault?: boolean;
+  stopPropagation?: boolean;
+  style?: React.CSSProperties;
+  type?: string;
 }
 
-export class ButtonIcon extends React.PureComponent<ButtonIconProps> {
-  handleClick = (event: React.SyntheticEvent<HTMLButtonElement>) => {
-    event.preventDefault();
+export class Button extends React.PureComponent<ButtonProps> {
+  handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
+    const { onClick, preventDefault = true, stopPropagation = false } = this.props;
+
     event.currentTarget.blur();
-    event.stopPropagation();
-    if (this.props.onClick) {
-      this.props.onClick();
-    }
+    if (preventDefault) event.preventDefault();
+    if (stopPropagation) event.stopPropagation();
+    if (onClick) onClick(event);
   };
 
   render() {
-    const { children, className, color = theme.darkBlue, onClick, tooltip, ...props } = this.props;
-    const buttonComponent = (
+    const {
+      className,
+      innerRef,
+      onClick,
+      preventDefault,
+      stopPropagation,
+      type = 'button',
+      ...props
+    } = this.props;
+    return (
+      // eslint-disable-next-line react/button-has-type
       <button
-        className={classNames(className, 'button-icon')}
+        {...props}
+        className={classNames('button', className)}
+        disabled={this.props.disabled}
+        id={this.props.id}
         onClick={this.handleClick}
-        style={{ color }}
-        {...props}>
-        {children}
-      </button>
+        ref={this.props.innerRef}
+        type={type}
+      />
+    );
+  }
+}
+
+export function SubmitButton(props: Omit<ButtonProps, 'type'>) {
+  // do not prevent default to actually submit a form
+  return <Button {...props} preventDefault={false} type="submit" />;
+}
+
+export function ResetButtonLink({ className, ...props }: Omit<ButtonProps, 'type'>) {
+  return <Button {...props} className={classNames('button-link', className)} type="reset" />;
+}
+
+interface ButtonIconProps {
+  className?: string;
+  color?: string;
+  onClick?: () => void;
+  tooltip?: string;
+  [x: string]: any;
+}
+
+export function ButtonIcon(props: ButtonIconProps) {
+  const { className, color = theme.darkBlue, tooltip, ...other } = props;
+  const buttonComponent = (
+    <Button
+      className={classNames(className, 'button-icon')}
+      stopPropagation={true}
+      style={{ color }}
+      {...other}
+    />
+  );
+  if (tooltip) {
+    return (
+      <Tooltip mouseEnterDelay={0.4} overlay={tooltip}>
+        {buttonComponent}
+      </Tooltip>
     );
-    if (tooltip) {
-      return (
-        <Tooltip overlay={tooltip} mouseEnterDelay={0.4}>
-          {buttonComponent}
-        </Tooltip>
-      );
-    }
-    return buttonComponent;
   }
+  return buttonComponent;
 }
 
 interface ActionButtonProps {
index 5e7ad5f1d4cafcd5664c5be8bc74b87539f4b40e..bdd52ec8e1ea14e7e8fcd34186ec2f7f88a9c042 100644 (file)
@@ -28,7 +28,15 @@ export const mockEvent = {
 };
 
 export function click(element: ShallowWrapper | ReactWrapper, event = {}): void {
-  element.simulate('click', { ...mockEvent, ...event });
+  // `type()` returns a component constructor for a composite element and string for DOM nodes
+  if (typeof element.type() === 'function') {
+    element.prop<Function>('onClick')();
+    // TODO find out if `root` is a public api
+    // https://github.com/airbnb/enzyme/blob/master/packages/enzyme/src/ReactWrapper.js#L109
+    (element as any).root().update();
+  } else {
+    element.simulate('click', { ...mockEvent, ...event });
+  }
 }
 
 export function clickOutside(event = {}): void {
index c43f94871a0df3562ffa1c07f4412f0c98cc567d..75132ae116b94a8c82856c638f0aadcaf1eb4abc 100644 (file)
@@ -26,7 +26,7 @@
   </tr>
   <tr>
     <td>clickAndWait</td>
-    <td>commit</td>
+    <td>[type=submit]</td>
     <td></td>
   </tr>
   <tr>
index 1e4b156a020cf8d86bd81eb215edae16ae4aee70..e739382b53ba86e929c81a4d49260cd622324722 100644 (file)
@@ -25,7 +25,7 @@
   </tr>
   <tr>
     <td>clickAndWait</td>
-    <td>commit</td>
+    <td>[type=submit]</td>
     <td></td>
   </tr>
   <tr>
index 251518aac0c2f71b292989b91aa5e981400f1b93..70cd7c22a7e0ef8594d6b469b45f96d292c154af 100644 (file)
@@ -25,7 +25,7 @@
 </tr>
 <tr>
        <td>clickAndWait</td>
-       <td>commit</td>
+       <td>[type=submit]</td>
        <td></td>
 </tr>
 <tr>
index 1ed2b8a9018d4b5ba8e2fadcda5108029f6846f5..e95b3addfbe94a593a0dce00152d7452415aa4ab 100644 (file)
@@ -31,7 +31,7 @@
 </tr>
 <tr>
        <td>clickAndWait</td>
-       <td>commit</td>
+       <td>[type=submit]</td>
        <td></td>
 </tr>
 <tr>
index a6716b8c06acd39e2c75345e996cea9f06b04ebe..e4801f3345e3dae19f87410dcb171b39eca62b0b 100644 (file)
@@ -31,7 +31,7 @@
 </tr>
 <tr>
        <td>clickAndWait</td>
-       <td>commit</td>
+       <td>[type=submit]</td>
        <td></td>
 </tr>
 <tr>
index 9610b0db66c2184028340fcd57af0424302b9b41..aa4e3d2b479b373c8688626c041e859cafda9912 100644 (file)
@@ -31,7 +31,7 @@
 </tr>
 <tr>
        <td>clickAndWait</td>
-       <td>commit</td>
+       <td>[type=submit]</td>
        <td></td>
 </tr>
 <tr>
index 5da4d40ca0c2d580e54673bd97ee1bc0380abf63..7c38169c5f5fc88d0f2304fa68fdb26a36b21f34 100644 (file)
@@ -31,7 +31,7 @@
 </tr>
 <tr>
        <td>clickAndWait</td>
-       <td>commit</td>
+       <td>[type=submit]</td>
        <td></td>
 </tr>
 <tr>
index 3091176ebaeaa7ce9d464d61f595bbcde7b6a22f..127855daaaa309759687d67d0ed796d491ffd997 100644 (file)
@@ -31,7 +31,7 @@
 </tr>
 <tr>
        <td>clickAndWait</td>
-       <td>commit</td>
+       <td>[type=submit]</td>
        <td></td>
 </tr>
 <tr>
index c267bf702e3fcee6a4405c2f3680a1cd5cbada3c..e8ace96e337c7ef3aea80c30a4c44b8c41e7a92f 100644 (file)
@@ -31,7 +31,7 @@
 </tr>
 <tr>
        <td>clickAndWait</td>
-       <td>commit</td>
+       <td>[type=submit]</td>
        <td></td>
 </tr>
 <tr>
index 951ae58d38a0f6f3e665846184a4d212dcbfda8d..74d7de10eb44f664f6b214194cec571666683d52 100644 (file)
@@ -50,7 +50,7 @@
     </tr>
     <tr>
         <td>clickAndWait</td>
-        <td>commit</td>
+        <td>[type=submit]</td>
         <td></td>
     </tr>
     <tr>
index 40de82c178af488bd10929785206c125fd8a2ca3..5a96ced0c162ab444696da8456d1dcce447cc4ee 100644 (file)
@@ -30,7 +30,7 @@
 </tr>
 <tr>
        <td>clickAndWait</td>
-       <td>commit</td>
+       <td>[type=submit]</td>
        <td></td>
 </tr>
 <tr>
index 7b43cf8ee03f0266db295d2a6e5cc96af656ffb3..ac2f0410613df4905cd5cbcfee9e9d0160ebe688 100644 (file)
@@ -30,7 +30,7 @@
     </tr>
     <tr>
         <td>clickAndWait</td>
-        <td>commit</td>
+        <td>[type=submit]</td>
         <td></td>
     </tr>
     <tr>
index d2f0a12a8b3d327d026d4bb351fe998727973d30..29f595eacda4caaa7ffc42d4cdf569f2420bd74c 100644 (file)
@@ -31,7 +31,7 @@
 </tr>
 <tr>
        <td>clickAndWait</td>
-       <td>commit</td>
+       <td>[type=submit]</td>
        <td></td>
 </tr>
 <tr>
index 7fefabfbd4a3532607414aeae74677eae5f93cb2..c819a141a740eb65e0373b08534202af1b5304ea 100644 (file)
@@ -26,7 +26,7 @@
 </tr>
 <tr>
        <td>clickAndWait</td>
-       <td>commit</td>
+       <td>[type=submit]</td>
        <td></td>
 </tr>
 <tr>
index 85e85c0391cc9fc08b1d534119ebc92cff28751e..cc902557a8d471fc1cf0b0ea630759c34947cfbd 100644 (file)
@@ -26,7 +26,7 @@
 </tr>
 <tr>
        <td>clickAndWait</td>
-       <td>commit</td>
+       <td>[type=submit]</td>
        <td></td>
 </tr>
 <tr>
index dfa3138f8b1789b15b41bd81d1039d334391aef7..f1a507aa0aa83973511971f767f08ecf67a9ac4e 100644 (file)
@@ -31,7 +31,7 @@
 </tr>
 <tr>
        <td>clickAndWait</td>
-       <td>commit</td>
+       <td>[type=submit]</td>
        <td></td>
 </tr>
 <tr>
index 792e12c864c22137a8fe35a7ae8d5abdb4f2245b..4e725fa35f4081e6db7f4bd1e5bd8379e770f022 100644 (file)
@@ -26,7 +26,7 @@
 </tr>
 <tr>
        <td>clickAndWait</td>
-       <td>commit</td>
+       <td>[type=submit]</td>
        <td></td>
 </tr>
 <tr>
index 6306820ba6add19fec6a1e5f460429b1943b3e48..11b4d22e341dc8d5157e2602919befbf662550da 100644 (file)
@@ -26,7 +26,7 @@
 </tr>
 <tr>
        <td>clickAndWait</td>
-       <td>commit</td>
+       <td>[type=submit]</td>
        <td></td>
 </tr>
 <tr>
index b2d712f516cfc194c926cef4a35a6d7cf42f2d81..4b540f77943607e1d5a5a9452f468fa42c6c483d 100644 (file)
@@ -30,7 +30,7 @@
   </tr>
   <tr>
     <td>clickAndWait</td>
-    <td>commit</td>
+    <td>[type=submit]</td>
     <td></td>
   </tr>
   <tr>
index d18ae08afb957919fa70bf4a83435c1b69a73a06..b83aa73edfb18dd5dc891d4192169b590333299a 100644 (file)
@@ -30,7 +30,7 @@
   </tr>
   <tr>
     <td>clickAndWait</td>
-    <td>commit</td>
+    <td>[type=submit]</td>
     <td></td>
   </tr>
   <tr>
index dab0c303ea52dd819a5b58f3834a9b73d549935e..8293994ca3a9308a82089e77e635ec566987e030 100644 (file)
@@ -50,7 +50,7 @@
   </tr>
   <tr>
     <td>clickAndWait</td>
-    <td>commit</td>
+    <td>[type=submit]</td>
     <td></td>
   </tr>
   <tr>
@@ -80,7 +80,7 @@
   </tr>
   <tr>
     <td>clickAndWait</td>
-    <td>commit</td>
+    <td>[type=submit]</td>
     <td></td>
   </tr>
   <tr>
index 44778593de0cc3559c70c3a429b1457a89ac860a..9109a420fec05b87cd4e3ce15980af69799cb615 100644 (file)
@@ -36,7 +36,7 @@
   </tr>
   <tr>
     <td>clickAndWait</td>
-    <td>commit</td>
+    <td>[type=submit]</td>
     <td></td>
   </tr>
   <tr>
index 18da805f66b9caa160602de5ecd6e01fd2e937e3..5ecdb0b4921f40b05eb824fa09535d9f347395a1 100644 (file)
@@ -35,7 +35,7 @@
   </tr>
   <tr>
     <td>clickAndWait</td>
-    <td>commit</td>
+    <td>[type=submit]</td>
     <td></td>
   </tr>
   <tr>
index dc66db8ee9b1a0fa299bbb29f9a1cbe5a6caa9ea..c3fe96656505450013522471632251b92a747ecc 100644 (file)
@@ -30,7 +30,7 @@
   </tr>
   <tr>
     <td>clickAndWait</td>
-    <td>commit</td>
+    <td>[type=submit]</td>
     <td></td>
   </tr>
   <tr>
index 9b5a1645ace3db8a8f278b4c0a166602a27c358c..d461a89cb7ec33b9df628689828fa2525c6f19e8 100644 (file)
@@ -30,7 +30,7 @@
 </tr>
 <tr>
        <td>clickAndWait</td>
-       <td>commit</td>
+       <td>[type=submit]</td>
        <td></td>
 </tr>
 <tr>
index c6519726f27fd5e52011b3e0faaeab619e6469a6..4fb3a5df6ee0cde178ac81570905be984a5aaf79 100644 (file)
@@ -40,7 +40,7 @@
   </tr>
   <tr>
     <td>clickAndWait</td>
-    <td>commit</td>
+    <td>[type=submit]</td>
     <td></td>
   </tr>
   <tr>
index aecfd8af765983b5165b04e9d2d947c52bede9ce..ab1cd03aa55cbf84cd04f0f68c056dac8e921dae 100644 (file)
@@ -39,7 +39,7 @@
   </tr>
   <tr>
     <td>clickAndWait</td>
-    <td>commit</td>
+    <td>[type=submit]</td>
     <td></td>
   </tr>
   <tr>
index af5b4362f0edc0c1433c4489ae57dabd77115ae1..3f34c50c1de495be516995835d84e12c05bc26dd 100644 (file)
@@ -30,7 +30,7 @@
 </tr>
 <tr>
        <td>clickAndWait</td>
-       <td>commit</td>
+       <td>[type=submit]</td>
        <td></td>
 </tr>
 <tr>
index 2c2f0f026dbb5f51bcdbdf357a05def340758675..2e8f49c6d6b7982224f08e74ab60dcdafcc95887 100644 (file)
@@ -31,7 +31,7 @@
 </tr>
 <tr>
        <td>clickAndWait</td>
-       <td>commit</td>
+       <td>[type=submit]</td>
        <td></td>
 </tr>
 <tr>
index b199152803fb3a12f0f4bfbdd88216a5123096cb..768b94e612ee93e83cf75c78a669418848ebe60c 100644 (file)
@@ -31,7 +31,7 @@
 </tr>
 <tr>
        <td>clickAndWait</td>
-       <td>commit</td>
+       <td>[type=submit]</td>
        <td></td>
 </tr>
 <tr>