aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-web/src/main/js
diff options
context:
space:
mode:
authorGrégoire Aubert <gregoire.aubert@sonarsource.com>2019-02-15 17:52:52 +0100
committersonartech <sonartech@sonarsource.com>2019-03-06 11:30:42 +0100
commit79a32b89b9d41df6cb8f1f96980d2591a5ecb192 (patch)
tree3b800b0d0f827ab655dbc3cf356b5058b490bdf8 /server/sonar-web/src/main/js
parent769d66fa99847e63cf5536f128b53a37e7a5a430 (diff)
downloadsonarqube-79a32b89b9d41df6cb8f1f96980d2591a5ecb192.tar.gz
sonarqube-79a32b89b9d41df6cb8f1f96980d2591a5ecb192.zip
SONARCLOUD-380 Rework modal styling of SC and SQ
* Update modal-fields * Update form-fields styling * Update modal-fields usage in extensions * Clean css
Diffstat (limited to 'server/sonar-web/src/main/js')
-rw-r--r--server/sonar-web/src/main/js/app/components/StartupModal.tsx1
-rw-r--r--server/sonar-web/src/main/js/app/styles/components/modals.css163
-rw-r--r--server/sonar-web/src/main/js/app/styles/init/base.css4
-rw-r--r--server/sonar-web/src/main/js/app/styles/init/forms.css29
-rw-r--r--server/sonar-web/src/main/js/app/styles/init/type.css6
-rw-r--r--server/sonar-web/src/main/js/app/types.d.ts5
-rw-r--r--server/sonar-web/src/main/js/apps/account/components/Password.tsx8
-rw-r--r--server/sonar-web/src/main/js/apps/account/components/__tests__/Password-test.tsx (renamed from server/sonar-web/src/main/js/components/ui/NewInfoBox.css)19
-rw-r--r--server/sonar-web/src/main/js/apps/account/components/__tests__/__snapshots__/Password-test.tsx.snap90
-rw-r--r--server/sonar-web/src/main/js/apps/background-tasks/components/ScannerContext.tsx2
-rw-r--r--server/sonar-web/src/main/js/apps/background-tasks/components/Stacktrace.tsx2
-rw-r--r--server/sonar-web/src/main/js/apps/background-tasks/components/__tests__/__snapshots__/ScannerContext-test.tsx.snap2
-rw-r--r--server/sonar-web/src/main/js/apps/background-tasks/components/__tests__/__snapshots__/Stacktrace-test.tsx.snap2
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/components/ActivationFormModal.tsx8
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/components/BulkChangeModal.tsx12
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/components/CustomRuleFormModal.tsx303
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetailsCustomRules.tsx4
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetailsDescription.tsx6
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetailsProfiles.tsx2
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/ActivationFormModal-test.tsx38
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/BulkChangeModal-test.tsx41
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/CustomRuleFormModal-test.tsx39
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/RuleListItem-test.tsx18
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/ActivationFormModal-test.tsx.snap101
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/BulkChangeModal-test.tsx.snap56
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/CustomRuleFormModal-test.tsx.snap237
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/RuleListItem-test.tsx.snap8
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/styles.css9
-rw-r--r--server/sonar-web/src/main/js/apps/create/components/UpgradeOrganizationModal.tsx4
-rw-r--r--server/sonar-web/src/main/js/apps/create/components/__tests__/__snapshots__/UpgradeOrganizationModal-test.tsx.snap2
-rw-r--r--server/sonar-web/src/main/js/apps/create/organization/AutoOrganizationCreate.tsx14
-rw-r--r--server/sonar-web/src/main/js/apps/create/organization/RemoteOrganizationChoose.tsx2
-rw-r--r--server/sonar-web/src/main/js/apps/create/organization/__tests__/AutoOrganizationCreate-test.tsx27
-rw-r--r--server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/AutoOrganizationCreate-test.tsx.snap14
-rw-r--r--server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/RemoteOrganizationChoose-test.tsx.snap1
-rw-r--r--server/sonar-web/src/main/js/apps/create/organization/__tests__/actions-test.ts8
-rw-r--r--server/sonar-web/src/main/js/apps/create/organization/actions.ts16
-rw-r--r--server/sonar-web/src/main/js/apps/custom-measures/components/Form.tsx7
-rw-r--r--server/sonar-web/src/main/js/apps/custom-measures/components/__tests__/__snapshots__/Form-test.tsx.snap2
-rw-r--r--server/sonar-web/src/main/js/apps/custom-metrics/components/Form.tsx5
-rw-r--r--server/sonar-web/src/main/js/apps/custom-metrics/components/__tests__/__snapshots__/Form-test.tsx.snap1
-rw-r--r--server/sonar-web/src/main/js/apps/groups/components/Form.tsx3
-rw-r--r--server/sonar-web/src/main/js/apps/groups/components/__tests__/__snapshots__/Form-test.tsx.snap1
-rw-r--r--server/sonar-web/src/main/js/apps/issues/components/BulkChangeModal.tsx95
-rw-r--r--server/sonar-web/src/main/js/apps/issues/components/__tests__/__snapshots__/BulkChangeModal-test.tsx.snap25
-rw-r--r--server/sonar-web/src/main/js/apps/issues/styles.css9
-rw-r--r--server/sonar-web/src/main/js/apps/organizationMembers/AddMemberForm.tsx2
-rw-r--r--server/sonar-web/src/main/js/apps/organizationMembers/ManageMemberGroupsForm.tsx106
-rw-r--r--server/sonar-web/src/main/js/apps/organizationMembers/MembersList.tsx11
-rw-r--r--server/sonar-web/src/main/js/apps/organizationMembers/MembersListHeader.tsx2
-rw-r--r--server/sonar-web/src/main/js/apps/organizationMembers/MembersListItem.tsx43
-rw-r--r--server/sonar-web/src/main/js/apps/organizationMembers/MembersPageHeader.tsx157
-rw-r--r--server/sonar-web/src/main/js/apps/organizationMembers/OrganizationMembers.tsx27
-rw-r--r--server/sonar-web/src/main/js/apps/organizationMembers/SyncMemberForm.tsx121
-rw-r--r--server/sonar-web/src/main/js/apps/organizationMembers/__tests__/ManageMemberGroupsForm-test.tsx36
-rw-r--r--server/sonar-web/src/main/js/apps/organizationMembers/__tests__/MembersList-test.tsx28
-rw-r--r--server/sonar-web/src/main/js/apps/organizationMembers/__tests__/MembersListItem-test.tsx81
-rw-r--r--server/sonar-web/src/main/js/apps/organizationMembers/__tests__/MembersPageHeader-test.tsx12
-rw-r--r--server/sonar-web/src/main/js/apps/organizationMembers/__tests__/OrganizationMembers-test.tsx13
-rw-r--r--server/sonar-web/src/main/js/apps/organizationMembers/__tests__/SyncMemberForm-test.tsx9
-rw-r--r--server/sonar-web/src/main/js/apps/organizationMembers/__tests__/__snapshots__/AddMemberForm-test.tsx.snap2
-rw-r--r--server/sonar-web/src/main/js/apps/organizationMembers/__tests__/__snapshots__/ManageMemberGroupsForm-test.tsx.snap66
-rw-r--r--server/sonar-web/src/main/js/apps/organizationMembers/__tests__/__snapshots__/MembersList-test.tsx.snap1
-rw-r--r--server/sonar-web/src/main/js/apps/organizationMembers/__tests__/__snapshots__/MembersListHeader-test.tsx.snap6
-rw-r--r--server/sonar-web/src/main/js/apps/organizationMembers/__tests__/__snapshots__/MembersListItem-test.tsx.snap80
-rw-r--r--server/sonar-web/src/main/js/apps/organizationMembers/__tests__/__snapshots__/MembersPageHeader-test.tsx.snap55
-rw-r--r--server/sonar-web/src/main/js/apps/organizationMembers/__tests__/__snapshots__/OrganizationMembers-test.tsx.snap13
-rw-r--r--server/sonar-web/src/main/js/apps/organizationMembers/__tests__/__snapshots__/SyncMemberForm-test.tsx.snap308
-rw-r--r--server/sonar-web/src/main/js/apps/organizations/components/OrganizationEdit.tsx28
-rw-r--r--server/sonar-web/src/main/js/apps/organizations/components/OrganizationEmpty.css2
-rw-r--r--server/sonar-web/src/main/js/apps/organizations/components/OrganizationEmpty.tsx14
-rw-r--r--server/sonar-web/src/main/js/apps/organizations/components/__tests__/__snapshots__/OrganizationEdit-test.tsx.snap108
-rw-r--r--server/sonar-web/src/main/js/apps/organizations/components/__tests__/__snapshots__/OrganizationEmpty-test.tsx.snap28
-rw-r--r--server/sonar-web/src/main/js/apps/permission-templates/components/Form.tsx7
-rw-r--r--server/sonar-web/src/main/js/apps/permission-templates/components/__tests__/Form-test.tsx30
-rw-r--r--server/sonar-web/src/main/js/apps/permission-templates/components/__tests__/__snapshots__/Form-test.tsx.snap12
-rw-r--r--server/sonar-web/src/main/js/apps/permissions/project/components/ApplyTemplate.tsx6
-rw-r--r--server/sonar-web/src/main/js/apps/permissions/project/components/__tests__/ApplyTemplate-test.tsx53
-rw-r--r--server/sonar-web/src/main/js/apps/permissions/project/components/__tests__/__snapshots__/ApplyTemplate-test.tsx.snap83
-rw-r--r--server/sonar-web/src/main/js/apps/projectActivity/components/forms/AddEventForm.tsx3
-rw-r--r--server/sonar-web/src/main/js/apps/projectActivity/components/forms/ChangeEventForm.tsx5
-rw-r--r--server/sonar-web/src/main/js/apps/projectActivity/components/forms/__tests__/AddEventForm-test.tsx41
-rw-r--r--server/sonar-web/src/main/js/apps/projectActivity/components/forms/__tests__/ChangeEventForm-test.tsx35
-rw-r--r--server/sonar-web/src/main/js/apps/projectActivity/components/forms/__tests__/__snapshots__/AddEventForm-test.tsx.snap26
-rw-r--r--server/sonar-web/src/main/js/apps/projectActivity/components/forms/__tests__/__snapshots__/ChangeEventForm-test.tsx.snap26
-rw-r--r--server/sonar-web/src/main/js/apps/projectBranches/components/RenameBranchModal.tsx2
-rw-r--r--server/sonar-web/src/main/js/apps/projectBranches/components/SettingForm.tsx6
-rw-r--r--server/sonar-web/src/main/js/apps/projectBranches/components/__tests__/__snapshots__/RenameBranchModal-test.tsx.snap3
-rw-r--r--server/sonar-web/src/main/js/apps/projectBranches/components/__tests__/__snapshots__/SettingForm-test.tsx.snap8
-rw-r--r--server/sonar-web/src/main/js/apps/projectLinks/CreationModal.tsx6
-rw-r--r--server/sonar-web/src/main/js/apps/projectLinks/__tests__/__snapshots__/CreationModal-test.tsx.snap1
-rw-r--r--server/sonar-web/src/main/js/apps/projectsManagement/BulkApplyTemplateModal.tsx2
-rw-r--r--server/sonar-web/src/main/js/apps/projectsManagement/CreateProjectForm.tsx3
-rw-r--r--server/sonar-web/src/main/js/apps/projectsManagement/__tests__/__snapshots__/BulkApplyTemplateModal-test.tsx.snap8
-rw-r--r--server/sonar-web/src/main/js/apps/projectsManagement/__tests__/__snapshots__/CreateProjectForm-test.tsx.snap9
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/components/ConditionModal.tsx7
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/components/ConditionOperator.tsx8
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/components/CopyQualityGateForm.tsx3
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/components/CreateQualityGateForm.tsx3
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/components/MetricSelect.tsx5
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/components/RenameQualityGateForm.tsx3
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/ConditionModal-test.tsx43
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/ConditionOperator-test.tsx37
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/CopyQualityGateForm-test.tsx (renamed from server/sonar-web/src/main/js/components/ui/__tests__/NewInfoBox-test.tsx)19
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/CreateQualityGateForm-test.tsx28
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/MetricSelect-test.tsx40
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/RenameQualityGateForm-test.tsx35
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/ConditionModal-test.tsx.snap85
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/ConditionOperator-test.tsx.snap26
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/CopyQualityGateForm-test.tsx.snap14
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/CreateQualityGateForm-test.tsx.snap8
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/MetricSelect-test.tsx.snap20
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/RenameQualityGateForm-test.tsx.snap37
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/components/CopyProfileForm.tsx2
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/components/ExtendProfileForm.tsx2
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/components/RenameProfileForm.tsx2
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/components/__tests__/__snapshots__/ExtendProfileForm-test.tsx.snap1
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/details/ChangeParentForm.tsx3
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/details/ProfilePermissionsForm.tsx2
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/details/ProfilePermissionsFormSelect.tsx8
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfilePermissionsForm-test.tsx.snap12
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/home/CreateProfileForm.tsx4
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/home/RestoreProfileForm.tsx42
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/home/__tests__/CreateProfileForm-test.tsx43
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/home/__tests__/RestoreProfileForm-test.tsx28
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/home/__tests__/__snapshots__/CreateProfileForm-test.tsx.snap39
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/home/__tests__/__snapshots__/RestoreProfileForm-test.tsx.snap62
-rw-r--r--server/sonar-web/src/main/js/apps/settings/components/EmailForm.tsx43
-rw-r--r--server/sonar-web/src/main/js/apps/settings/components/__tests__/EmailForm-test.tsx27
-rw-r--r--server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/EmailForm-test.tsx.snap90
-rw-r--r--server/sonar-web/src/main/js/apps/settings/encryption/EncryptionForm.tsx4
-rw-r--r--server/sonar-web/src/main/js/apps/tutorials/onboarding/OnboardingModal.tsx26
-rw-r--r--server/sonar-web/src/main/js/apps/tutorials/onboarding/OnboardingPage.tsx1
-rw-r--r--server/sonar-web/src/main/js/apps/tutorials/onboarding/OrganizationsShortList.tsx31
-rw-r--r--server/sonar-web/src/main/js/apps/tutorials/onboarding/OrganizationsShortListItem.tsx6
-rw-r--r--server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/OnboardingModal-test.tsx1
-rw-r--r--server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/OrganizationsShortList-test.tsx4
-rw-r--r--server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/OrganizationsShortListItem-test.tsx10
-rw-r--r--server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/__snapshots__/OnboardingModal-test.tsx.snap24
-rw-r--r--server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/__snapshots__/OrganizationsShortList-test.tsx.snap27
-rw-r--r--server/sonar-web/src/main/js/apps/tutorials/styles.css44
-rw-r--r--server/sonar-web/src/main/js/apps/users/components/PasswordForm.tsx2
-rw-r--r--server/sonar-web/src/main/js/apps/users/components/TokensForm.tsx2
-rw-r--r--server/sonar-web/src/main/js/apps/users/components/UserForm.tsx2
-rw-r--r--server/sonar-web/src/main/js/apps/users/components/UserScmAccountInput.tsx2
-rw-r--r--server/sonar-web/src/main/js/apps/users/components/__tests__/__snapshots__/TokensForm-test.tsx.snap4
-rw-r--r--server/sonar-web/src/main/js/apps/webhooks/components/CreateWebhookForm.tsx1
-rw-r--r--server/sonar-web/src/main/js/apps/webhooks/components/__tests__/__snapshots__/CreateWebhookForm-test.tsx.snap2
-rw-r--r--server/sonar-web/src/main/js/components/SourceViewer/components/MeasuresOverlay.tsx2
-rw-r--r--server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/__snapshots__/MeasuresOverlay-test.tsx.snap6
-rw-r--r--server/sonar-web/src/main/js/components/common/MarkdownTips.tsx9
-rw-r--r--server/sonar-web/src/main/js/components/controls/Checkbox.tsx17
-rw-r--r--server/sonar-web/src/main/js/components/controls/ConfirmModal.tsx4
-rw-r--r--server/sonar-web/src/main/js/components/controls/Modal.tsx19
-rw-r--r--server/sonar-web/src/main/js/components/controls/Radio.tsx5
-rw-r--r--server/sonar-web/src/main/js/components/controls/SearchSelect.tsx3
-rw-r--r--server/sonar-web/src/main/js/components/controls/ValidationModal.tsx10
-rw-r--r--server/sonar-web/src/main/js/components/controls/__tests__/Checkbox-test.tsx4
-rw-r--r--server/sonar-web/src/main/js/components/controls/__tests__/__snapshots__/Checkbox-test.tsx.snap9
-rw-r--r--server/sonar-web/src/main/js/components/controls/__tests__/__snapshots__/Radio-test.tsx.snap4
-rw-r--r--server/sonar-web/src/main/js/components/controls/__tests__/__snapshots__/SearchSelect-test.tsx.snap1
-rw-r--r--server/sonar-web/src/main/js/components/icons-components/OnboardingAddMembersIcon.tsx3
-rw-r--r--server/sonar-web/src/main/js/components/ui/NewInfoBox.tsx54
-rw-r--r--server/sonar-web/src/main/js/components/ui/__tests__/__snapshots__/NewInfoBox-test.tsx.snap44
-rw-r--r--server/sonar-web/src/main/js/components/ui/buttons.css26
-rw-r--r--server/sonar-web/src/main/js/components/ui/buttons.tsx2
-rw-r--r--server/sonar-web/src/main/js/helpers/testMocks.ts118
167 files changed, 2971 insertions, 1578 deletions
diff --git a/server/sonar-web/src/main/js/app/components/StartupModal.tsx b/server/sonar-web/src/main/js/app/components/StartupModal.tsx
index cbc48881b23..1162ec0ad5a 100644
--- a/server/sonar-web/src/main/js/app/components/StartupModal.tsx
+++ b/server/sonar-web/src/main/js/app/components/StartupModal.tsx
@@ -153,7 +153,6 @@ export class StartupModal extends React.PureComponent<Props, State> {
<OnboardingModal
onClose={this.closeOnboarding}
onOpenProjectOnboarding={this.openProjectOnboarding}
- skipOnboarding={this.props.skipOnboarding}
/>
)}
</OnboardingContext.Provider>
diff --git a/server/sonar-web/src/main/js/app/styles/components/modals.css b/server/sonar-web/src/main/js/app/styles/components/modals.css
index 370030f2a61..0ded3991928 100644
--- a/server/sonar-web/src/main/js/app/styles/components/modals.css
+++ b/server/sonar-web/src/main/js/app/styles/components/modals.css
@@ -28,9 +28,6 @@
background-color: #fff;
opacity: 0;
transition: all 0.2s ease;
-}
-
-.modal.sonarcloud {
border-radius: 3px;
}
@@ -45,6 +42,11 @@
opacity: 1;
}
+.modal-small {
+ width: 450px;
+ margin-left: -225px;
+}
+
.modal-medium {
width: 830px;
margin-left: -415px;
@@ -86,116 +88,57 @@
margin-right: var(--sbw);
}
-.modal-container {
- max-height: 60vh;
- padding: 10px;
- box-sizing: border-box;
- overflow: auto;
-}
-
-.modal.sonarcloud .modal-container {
- border-top: 1px solid var(--barBorderColor);
- margin-top: var(--pagePadding);
-}
-
-.modal.sonarcloud .modal-container > :last-child {
- margin-bottom: var(--pagePadding);
-}
-
.modal-head {
- padding: 0 10px;
- background-color: var(--gray94);
- border-bottom: 1px solid var(--disableGrayBorder);
-}
-
-.modal.sonarcloud .modal-head {
- background-color: transparent;
- border-bottom: none;
- padding: var(--pagePadding) calc(2 * var(--pagePadding)) 0;
+ padding: calc(4 * var(--gridSize));
+ padding-bottom: 0;
}
.modal-head h1,
.modal-head h2 {
- line-height: 30px;
- min-height: 30px;
-}
-
-.modal.sonarcloud .modal-head h1,
-.modal.sonarcloud .modal-head h2 {
- margin-top: var(--gridSize);
+ margin: 0;
font-size: var(--bigFontSize);
font-weight: bold;
- line-height: 30px;
+ line-height: normal;
}
.modal-body {
- padding: 10px;
+ padding: var(--pagePadding) calc(4 * var(--gridSize));
}
-.modal.sonarcloud .modal-body {
- padding: var(--pagePadding) calc(2 * var(--pagePadding));
+.modal-container {
+ max-height: 60vh;
+ box-sizing: border-box;
+ overflow-y: scroll;
+ border-top: 1px solid var(--barBorderColor);
+ margin-top: var(--pagePadding);
+ padding-right: calc(4 * var(--gridSize) - var(--sbw));
+}
+
+.modal-container > :last-child {
+ margin-bottom: var(--pagePadding);
}
.modal-field,
-.modal-large-field,
.modal-validation-field {
clear: both;
display: block;
- padding: 5px 0 5px 130px;
-}
-
-.modal-large-field {
- padding: 20px 40px;
-}
-
-.modal-validation-field {
- padding: 3px 0 3px 130px;
+ padding: 0;
+ margin-bottom: calc(var(--gridSize) * 2);
}
.modal-field label,
.modal-validation-field label {
- position: relative;
- left: -140px;
display: block;
- float: left;
- width: 120px;
- margin-right: -130px;
- padding-top: 5px;
- padding-bottom: 2px;
- padding-left: 10px;
- line-height: 1;
- text-align: right;
- overflow: hidden;
- text-overflow: ellipsis;
-}
-
-.modal-large-field label {
- display: inline-block;
- padding-bottom: 15px;
font-weight: bold;
-}
-
-.modal-field .note {
- line-height: var(--controlHeight);
-}
-
-.readonly-field {
- padding-top: 5px;
- margin-left: -5px;
- line-height: 1;
+ padding-bottom: calc(var(--gridSize) / 2);
}
.modal-field a.icon-checkbox,
.modal-field input,
-.modal-large-field input,
.modal-field select,
-.modal-large-field select,
.modal-field textarea,
-.modal-large-field textarea,
-.modal-field .Select,
-.modal-large-field .Select {
+.modal-field .Select {
margin-right: 5px;
- margin-bottom: 10px;
}
.modal-field a.icon-checkbox {
@@ -203,15 +146,12 @@
}
.modal-field input[type='radio'],
-.modal-large-field input[type='radio'],
-.modal-field input[type='checkbox'],
-.modal-large-field input[type='checkbox'] {
+.modal-field input[type='checkbox'] {
margin-top: 5px;
margin-bottom: 4px;
}
-.modal-field > .icon-checkbox,
-.modal-large-field > .icon-checkbox {
+.modal-field > .icon-checkbox {
padding-top: 6px;
padding-right: 8px;
}
@@ -222,77 +162,44 @@
.modal-field textarea,
.modal-field select,
.modal-field .Select {
- width: 250px;
-}
-
-.modal-field textarea {
- max-width: 250px;
- min-width: 250px;
- max-height: 50vh;
- min-height: var(--controlHeight);
-}
-
-.modal-large-field input[type='text'],
-.modal-large-field input[type='email'],
-.modal-large-field input[type='password'],
-.modal-large-field textarea,
-.modal-large-field select,
-.modal-large-field .Select {
width: 100%;
}
-.modal-large-field textarea {
- max-width: 100%;
- min-width: 100%;
- max-height: 50vh;
- min-height: var(--controlHeight);
-}
-
.modal-validation-field input,
.modal-validation-field textarea,
.modal-validation-field .Select {
- margin-right: 5px;
+ margin-right: var(--gridSize);
margin-bottom: 2px;
- width: 250px;
+ width: calc(100% - 3 * var(--gridSize));
}
+.modal-field textarea,
.modal-validation-field textarea {
- max-width: 250px;
- min-width: 250px;
+ max-width: 100%;
+ min-width: 100%;
max-height: 50vh;
min-height: var(--controlHeight);
}
-
.modal-validation-field input:not(.is-invalid),
.modal-validation-field .Select:not(.is-invalid) {
- margin-bottom: 18px;
+ margin-bottom: calc(var(--tinyControlHeight) + 2px);
}
.modal-field-description {
- padding-bottom: 4px;
line-height: 1.4;
color: var(--secondFontColor);
font-size: var(--smallFontSize);
overflow: hidden;
text-overflow: ellipsis;
-}
-
-.modal-validation-field .modal-field-description {
margin-top: 2px;
}
.modal-foot {
- padding: 10px;
- border-top: 1px solid var(--disableGrayBorder);
- background-color: var(--gray94);
- text-align: right;
-}
-
-.modal.sonarcloud .modal-foot {
- padding: var(--pagePadding);
+ padding: var(--pagePadding) calc(4 * var(--gridSize));
border-top: 1px solid var(--barBorderColor);
background-color: var(--barBackgroundColor);
border-radius: 3px;
+ text-align: right;
}
.modal-foot button,
diff --git a/server/sonar-web/src/main/js/app/styles/init/base.css b/server/sonar-web/src/main/js/app/styles/init/base.css
index 67eefb1de61..af8916a1839 100644
--- a/server/sonar-web/src/main/js/app/styles/init/base.css
+++ b/server/sonar-web/src/main/js/app/styles/init/base.css
@@ -81,6 +81,10 @@ textarea {
font-family: inherit;
}
+textarea {
+ min-height: 40px;
+}
+
/*Remove button padding in FF*/
select::-moz-focus-inner,
input::-moz-focus-inner,
diff --git a/server/sonar-web/src/main/js/app/styles/init/forms.css b/server/sonar-web/src/main/js/app/styles/init/forms.css
index e270290c3a5..3a820fe2894 100644
--- a/server/sonar-web/src/main/js/app/styles/init/forms.css
+++ b/server/sonar-web/src/main/js/app/styles/init/forms.css
@@ -160,16 +160,11 @@ select {
}
.input-super-large {
- width: 100%;
+ width: 100% !important;
max-width: 300px;
min-width: 200px;
}
-textarea.input-super-large {
- max-width: 600px;
- min-width: 300px;
-}
-
.input-ghost {
padding: 0 !important;
border: none !important;
@@ -193,13 +188,31 @@ em.mandatory {
.form-field {
clear: both;
display: block;
- padding-top: var(--gridSize);
padding-bottom: calc(2 * var(--gridSize));
}
.form-field label {
display: block;
- padding-bottom: var(--gridSize);
+ font-weight: bold;
+ padding-bottom: calc(var(--gridSize) / 2);
+}
+
+.form-field-description {
+ line-height: 1.4;
+ color: var(--secondFontColor);
+ font-size: var(--smallFontSize);
+ overflow: hidden;
+ text-overflow: ellipsis;
+ margin-top: 2px;
+}
+
+.form-field input[type='text'],
+.form-field input[type='email'],
+.form-field input[type='password'],
+.form-field textarea,
+.form-field select,
+.form-field .Select {
+ width: 250px;
}
.radio-toggle {
diff --git a/server/sonar-web/src/main/js/app/styles/init/type.css b/server/sonar-web/src/main/js/app/styles/init/type.css
index 2b056a346b4..36046303c59 100644
--- a/server/sonar-web/src/main/js/app/styles/init/type.css
+++ b/server/sonar-web/src/main/js/app/styles/init/type.css
@@ -168,6 +168,10 @@ small,
font-size: var(--smallFontSize);
}
+.medium {
+ font-size: var(--mediumFontSize);
+}
+
.big {
font-size: var(--bigFontSize);
}
@@ -255,7 +259,7 @@ small,
}
.text-normal {
- font-weight: normal;
+ font-weight: normal !important;
}
.text-muted {
diff --git a/server/sonar-web/src/main/js/app/types.d.ts b/server/sonar-web/src/main/js/app/types.d.ts
index 45d8d3e0dc2..f412c82962d 100644
--- a/server/sonar-web/src/main/js/app/types.d.ts
+++ b/server/sonar-web/src/main/js/app/types.d.ts
@@ -216,10 +216,7 @@ declare namespace T {
value: string;
}
- type CurrentUserSettingNames =
- | 'notifications.optOut'
- | 'notifications.readDate'
- | 'organizations.members.dismissSyncNotif';
+ type CurrentUserSettingNames = 'notifications.optOut' | 'notifications.readDate';
export interface CustomMeasure {
createdAt?: string;
diff --git a/server/sonar-web/src/main/js/apps/account/components/Password.tsx b/server/sonar-web/src/main/js/apps/account/components/Password.tsx
index fa681a26ec8..66e2aa43986 100644
--- a/server/sonar-web/src/main/js/apps/account/components/Password.tsx
+++ b/server/sonar-web/src/main/js/apps/account/components/Password.tsx
@@ -87,7 +87,7 @@ export default class Password extends React.Component<Props, State> {
</Alert>
))}
- <div className="modal-field">
+ <div className="form-field">
<label htmlFor="old_password">
{translate('my_profile.password.old')}
<em className="mandatory">*</em>
@@ -101,7 +101,7 @@ export default class Password extends React.Component<Props, State> {
type="password"
/>
</div>
- <div className="modal-field">
+ <div className="form-field">
<label htmlFor="password">
{translate('my_profile.password.new')}
<em className="mandatory">*</em>
@@ -115,7 +115,7 @@ export default class Password extends React.Component<Props, State> {
type="password"
/>
</div>
- <div className="modal-field">
+ <div className="form-field">
<label htmlFor="password_confirmation">
{translate('my_profile.password.confirm')}
<em className="mandatory">*</em>
@@ -129,7 +129,7 @@ export default class Password extends React.Component<Props, State> {
type="password"
/>
</div>
- <div className="modal-field">
+ <div className="form-field">
<SubmitButton id="change-password">
{translate('my_profile.password.submit')}
</SubmitButton>
diff --git a/server/sonar-web/src/main/js/components/ui/NewInfoBox.css b/server/sonar-web/src/main/js/apps/account/components/__tests__/Password-test.tsx
index 0c8b0ca2a20..2b5900be381 100644
--- a/server/sonar-web/src/main/js/components/ui/NewInfoBox.css
+++ b/server/sonar-web/src/main/js/apps/account/components/__tests__/Password-test.tsx
@@ -17,16 +17,11 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-.new-info-box {
- display: flex;
- padding: var(--gridSize);
- background-color: var(--veryLightBlue);
- border: 1px solid var(--alertBorderInfo);
- border-radius: 2px;
-}
+import * as React from 'react';
+import { shallow } from 'enzyme';
+import Password from '../Password';
+import { mockCurrentUser } from '../../../../helpers/testMocks';
-.new-info-box-header {
- display: flex;
- justify-content: space-between;
- align-items: center;
-}
+it('renders correctly', () => {
+ expect(shallow(<Password user={mockCurrentUser()} />)).toMatchSnapshot();
+});
diff --git a/server/sonar-web/src/main/js/apps/account/components/__tests__/__snapshots__/Password-test.tsx.snap b/server/sonar-web/src/main/js/apps/account/components/__tests__/__snapshots__/Password-test.tsx.snap
new file mode 100644
index 00000000000..e16485c7246
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/account/components/__tests__/__snapshots__/Password-test.tsx.snap
@@ -0,0 +1,90 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`renders correctly 1`] = `
+<section
+ className="boxed-group"
+>
+ <h2
+ className="spacer-bottom"
+ >
+ my_profile.password.title
+ </h2>
+ <form
+ className="boxed-group-inner"
+ onSubmit={[Function]}
+ >
+ <div
+ className="form-field"
+ >
+ <label
+ htmlFor="old_password"
+ >
+ my_profile.password.old
+ <em
+ className="mandatory"
+ >
+ *
+ </em>
+ </label>
+ <input
+ autoComplete="off"
+ id="old_password"
+ name="old_password"
+ required={true}
+ type="password"
+ />
+ </div>
+ <div
+ className="form-field"
+ >
+ <label
+ htmlFor="password"
+ >
+ my_profile.password.new
+ <em
+ className="mandatory"
+ >
+ *
+ </em>
+ </label>
+ <input
+ autoComplete="off"
+ id="password"
+ name="password"
+ required={true}
+ type="password"
+ />
+ </div>
+ <div
+ className="form-field"
+ >
+ <label
+ htmlFor="password_confirmation"
+ >
+ my_profile.password.confirm
+ <em
+ className="mandatory"
+ >
+ *
+ </em>
+ </label>
+ <input
+ autoComplete="off"
+ id="password_confirmation"
+ name="password_confirmation"
+ required={true}
+ type="password"
+ />
+ </div>
+ <div
+ className="form-field"
+ >
+ <SubmitButton
+ id="change-password"
+ >
+ my_profile.password.submit
+ </SubmitButton>
+ </div>
+ </form>
+</section>
+`;
diff --git a/server/sonar-web/src/main/js/apps/background-tasks/components/ScannerContext.tsx b/server/sonar-web/src/main/js/apps/background-tasks/components/ScannerContext.tsx
index a4fe42e1914..72c73262ce1 100644
--- a/server/sonar-web/src/main/js/apps/background-tasks/components/ScannerContext.tsx
+++ b/server/sonar-web/src/main/js/apps/background-tasks/components/ScannerContext.tsx
@@ -62,7 +62,7 @@ export default class ScannerContext extends React.PureComponent<Props, State> {
const { scannerContext } = this.state;
return (
- <Modal contentLabel="scanner context" large={true} onRequestClose={this.props.onClose}>
+ <Modal contentLabel="scanner context" onRequestClose={this.props.onClose} size={'large'}>
<div className="modal-head">
<h2>
{translate('background_tasks.scanner_context')}
diff --git a/server/sonar-web/src/main/js/apps/background-tasks/components/Stacktrace.tsx b/server/sonar-web/src/main/js/apps/background-tasks/components/Stacktrace.tsx
index 1d8e2735f25..9ac1066d96f 100644
--- a/server/sonar-web/src/main/js/apps/background-tasks/components/Stacktrace.tsx
+++ b/server/sonar-web/src/main/js/apps/background-tasks/components/Stacktrace.tsx
@@ -70,7 +70,7 @@ export default class Stacktrace extends React.PureComponent<Props, State> {
const { loading, stacktrace } = this.state;
return (
- <Modal contentLabel="stacktrace" large={true} onRequestClose={this.props.onClose}>
+ <Modal contentLabel="stacktrace" onRequestClose={this.props.onClose} size={'large'}>
<div className="modal-head">
<h2>
{translate('background_tasks.error_stacktrace')}
diff --git a/server/sonar-web/src/main/js/apps/background-tasks/components/__tests__/__snapshots__/ScannerContext-test.tsx.snap b/server/sonar-web/src/main/js/apps/background-tasks/components/__tests__/__snapshots__/ScannerContext-test.tsx.snap
index dc47f7e97c5..4515f08bff8 100644
--- a/server/sonar-web/src/main/js/apps/background-tasks/components/__tests__/__snapshots__/ScannerContext-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/background-tasks/components/__tests__/__snapshots__/ScannerContext-test.tsx.snap
@@ -3,8 +3,8 @@
exports[`renders 1`] = `
<Modal
contentLabel="scanner context"
- large={true}
onRequestClose={[MockFunction]}
+ size="large"
>
<div
className="modal-head"
diff --git a/server/sonar-web/src/main/js/apps/background-tasks/components/__tests__/__snapshots__/Stacktrace-test.tsx.snap b/server/sonar-web/src/main/js/apps/background-tasks/components/__tests__/__snapshots__/Stacktrace-test.tsx.snap
index 3c3c6519c93..aaf5962266d 100644
--- a/server/sonar-web/src/main/js/apps/background-tasks/components/__tests__/__snapshots__/Stacktrace-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/background-tasks/components/__tests__/__snapshots__/Stacktrace-test.tsx.snap
@@ -3,8 +3,8 @@
exports[`renders 1`] = `
<Modal
contentLabel="stacktrace"
- large={true}
onRequestClose={[MockFunction]}
+ size="large"
>
<div
className="modal-head"
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/ActivationFormModal.tsx b/server/sonar-web/src/main/js/apps/coding-rules/components/ActivationFormModal.tsx
index e9ffd00fa0d..50b5cce4633 100644
--- a/server/sonar-web/src/main/js/apps/coding-rules/components/ActivationFormModal.tsx
+++ b/server/sonar-web/src/main/js/apps/coding-rules/components/ActivationFormModal.tsx
@@ -21,7 +21,7 @@ import * as React from 'react';
import Modal from '../../../components/controls/Modal';
import Select from '../../../components/controls/Select';
import SeverityHelper from '../../../components/shared/SeverityHelper';
-import { activateRule, Profile as BaseProfile } from '../../../api/quality-profiles';
+import { activateRule, Profile } from '../../../api/quality-profiles';
import { SEVERITIES } from '../../../helpers/constants';
import { translate } from '../../../helpers/l10n';
import { sortProfiles } from '../../quality-profiles/utils';
@@ -34,7 +34,7 @@ interface Props {
onClose: () => void;
onDone: (severity: string) => Promise<void>;
organization: string | undefined;
- profiles: BaseProfile[];
+ profiles: Profile[];
rule: T.Rule | T.RuleDetails;
}
@@ -153,7 +153,7 @@ export default class ActivationFormModal extends React.PureComponent<Props, Stat
const isUpdateMode = !!activation;
return (
- <Modal contentLabel={this.props.modalHeader} onRequestClose={this.props.onClose}>
+ <Modal contentLabel={this.props.modalHeader} onRequestClose={this.props.onClose} size="small">
<form onSubmit={this.handleFormSubmit}>
<div className="modal-head">
<h2>{this.props.modalHeader}</h2>
@@ -206,7 +206,6 @@ export default class ActivationFormModal extends React.PureComponent<Props, Stat
<label title={param.key}>{param.key}</label>
{param.type === 'TEXT' ? (
<textarea
- className="width100"
disabled={submitting}
name={param.key}
onChange={this.handleParameterChange}
@@ -216,7 +215,6 @@ export default class ActivationFormModal extends React.PureComponent<Props, Stat
/>
) : (
<input
- className="input-super-large"
disabled={submitting}
name={param.key}
onChange={this.handleParameterChange}
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/BulkChangeModal.tsx b/server/sonar-web/src/main/js/apps/coding-rules/components/BulkChangeModal.tsx
index 0a8158a153c..b6bf6c9d4ec 100644
--- a/server/sonar-web/src/main/js/apps/coding-rules/components/BulkChangeModal.tsx
+++ b/server/sonar-web/src/main/js/apps/coding-rules/components/BulkChangeModal.tsx
@@ -19,13 +19,13 @@
*/
import * as React from 'react';
import { Query, serializeQuery } from '../query';
-import { Profile, bulkActivateRules, bulkDeactivateRules } from '../../../api/quality-profiles';
import Modal from '../../../components/controls/Modal';
import Select from '../../../components/controls/Select';
+import { Alert } from '../../../components/ui/Alert';
+import { SubmitButton, ResetButtonLink } from '../../../components/ui/buttons';
+import { Profile, bulkActivateRules, bulkDeactivateRules } from '../../../api/quality-profiles';
import { translate, translateWithParameters } from '../../../helpers/l10n';
import { formatMeasure } from '../../../helpers/measures';
-import { SubmitButton, ResetButtonLink } from '../../../components/ui/buttons';
-import { Alert } from '../../../components/ui/Alert';
interface Props {
action: string;
@@ -198,7 +198,7 @@ export default class BulkChangeModal extends React.PureComponent<Props, State> {
: `${translate('coding_rules.deactivate_in_quality_profile')} (${formatMeasure(total, 'INT')} ${translate('coding_rules._rules')})`;
return (
- <Modal contentLabel={header} onRequestClose={this.props.onClose}>
+ <Modal contentLabel={header} onRequestClose={this.props.onClose} size="small">
<form onSubmit={this.handleFormSubmit}>
<header className="modal-head">
<h2>{header}</h2>
@@ -218,11 +218,11 @@ export default class BulkChangeModal extends React.PureComponent<Props, State> {
</label>
</h3>
{profile ? (
- <h3 className="readonly-field">
+ <span>
{profile.name}
{' — '}
{translate('are_you_sure')}
- </h3>
+ </span>
) : (
this.renderProfileSelect()
)}
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/CustomRuleFormModal.tsx b/server/sonar-web/src/main/js/apps/coding-rules/components/CustomRuleFormModal.tsx
index a0d0385a8ec..c97036fd75a 100644
--- a/server/sonar-web/src/main/js/apps/coding-rules/components/CustomRuleFormModal.tsx
+++ b/server/sonar-web/src/main/js/apps/coding-rules/components/CustomRuleFormModal.tsx
@@ -153,76 +153,60 @@ export default class CustomRuleFormModal extends React.PureComponent<Props, Stat
};
renderNameField = () => (
- <tr className="property">
- <th className="nowrap">
- <h3>
- {translate('name')} <em className="mandatory">*</em>
- </h3>
- </th>
- <td>
+ <div className="modal-field">
+ <label htmlFor="coding-rules-custom-rule-creation-name">
+ {translate('name')} <em className="mandatory">*</em>
+ </label>
+ <input
+ autoFocus={true}
+ disabled={this.state.submitting}
+ id="coding-rules-custom-rule-creation-name"
+ onChange={this.handleNameChange}
+ required={true}
+ type="text"
+ value={this.state.name}
+ />
+ </div>
+ );
+
+ renderKeyField = () => (
+ <div className="modal-field">
+ <label htmlFor="coding-rules-custom-rule-creation-key">
+ {translate('key')} {!this.props.customRule && <em className="mandatory">*</em>}
+ </label>
+
+ {this.props.customRule ? (
+ <span className="coding-rules-detail-custom-rule-key" title={this.props.customRule.key}>
+ {this.props.customRule.key}
+ </span>
+ ) : (
<input
- autoFocus={true}
- className="coding-rules-name-key"
disabled={this.state.submitting}
- id="coding-rules-custom-rule-creation-name"
- onChange={this.handleNameChange}
+ id="coding-rules-custom-rule-creation-key"
+ onChange={this.handleKeyChange}
required={true}
type="text"
- value={this.state.name}
+ value={this.state.key}
/>
- </td>
- </tr>
- );
-
- renderKeyField = () => (
- <tr className="property">
- <th className="nowrap">
- <h3>
- {translate('key')} {!this.props.customRule && <em className="mandatory">*</em>}
- </h3>
- </th>
- <td>
- {this.props.customRule ? (
- <span className="coding-rules-detail-custom-rule-key" title={this.props.customRule.key}>
- {this.props.customRule.key}
- </span>
- ) : (
- <input
- className="coding-rules-name-key"
- disabled={this.state.submitting}
- id="coding-rules-custom-rule-creation-key"
- onChange={this.handleKeyChange}
- required={true}
- type="text"
- value={this.state.key}
- />
- )}
- </td>
- </tr>
+ )}
+ </div>
);
renderDescriptionField = () => (
- <tr className="property">
- <th className="nowrap">
- <h3>
- {translate('description')} <em className="mandatory">*</em>
- </h3>
- </th>
- <td>
- <textarea
- className="coding-rules-markdown-description"
- disabled={this.state.submitting}
- id="coding-rules-custom-rule-creation-html-description"
- onChange={this.handleDescriptionChange}
- required={true}
- rows={5}
- value={this.state.description}
- />
- <span className="text-right">
- <MarkdownTips />
- </span>
- </td>
- </tr>
+ <div className="modal-field">
+ <label htmlFor="coding-rules-custom-rule-creation-html-description">
+ {translate('description')} <em className="mandatory">*</em>
+ </label>
+ <textarea
+ disabled={this.state.submitting}
+ id="coding-rules-custom-rule-creation-html-description"
+ onChange={this.handleDescriptionChange}
+ required={true}
+ rows={5}
+ value={this.state.description}
+ />
+ <MarkdownTips className="modal-field-descriptor text-right" />
+ </div>
);
renderTypeOption = ({ value }: { value: T.RuleType }) => {
@@ -230,107 +214,99 @@ export default class CustomRuleFormModal extends React.PureComponent<Props, Stat
};
renderTypeField = () => (
- <tr className="property">
- <th className="nowrap">
- <h3>{translate('type')}</h3>
- </th>
- <td>
- <Select
- className="input-medium"
- clearable={false}
- disabled={this.state.submitting}
- onChange={this.handleTypeChange}
- optionRenderer={this.renderTypeOption}
- options={RULE_TYPES.map(type => ({
- label: translate('issue.type', type),
- value: type
- }))}
- searchable={false}
- value={this.state.type}
- valueRenderer={this.renderTypeOption}
- />
- </td>
- </tr>
+ <div className="modal-field flex-1 spacer-right">
+ <label htmlFor="coding-rules-custom-rule-type">{translate('type')}</label>
+ <Select
+ clearable={false}
+ disabled={this.state.submitting}
+ id="coding-rules-custom-rule-type"
+ onChange={this.handleTypeChange}
+ optionRenderer={this.renderTypeOption}
+ options={RULE_TYPES.map(type => ({
+ label: translate('issue.type', type),
+ value: type
+ }))}
+ searchable={false}
+ value={this.state.type}
+ valueRenderer={this.renderTypeOption}
+ />
+ </div>
);
renderSeverityOption = ({ value }: { value: string }) => <SeverityHelper severity={value} />;
renderSeverityField = () => (
- <tr className="property">
- <th className="nowrap">
- <h3>{translate('severity')}</h3>
- </th>
- <td>
- <Select
- className="input-medium"
- clearable={false}
- disabled={this.state.submitting}
- onChange={this.handleSeverityChange}
- optionRenderer={this.renderSeverityOption}
- options={SEVERITIES.map(severity => ({
- label: translate('severity', severity),
- value: severity
- }))}
- searchable={false}
- value={this.state.severity}
- valueRenderer={this.renderSeverityOption}
- />
- </td>
- </tr>
+ <div className="modal-field flex-1 spacer-right">
+ <label htmlFor="coding-rules-custom-rule-severity">{translate('severity')}</label>
+ <Select
+ clearable={false}
+ disabled={this.state.submitting}
+ id="coding-rules-custom-rule-severity"
+ onChange={this.handleSeverityChange}
+ optionRenderer={this.renderSeverityOption}
+ options={SEVERITIES.map(severity => ({
+ label: translate('severity', severity),
+ value: severity
+ }))}
+ searchable={false}
+ value={this.state.severity}
+ valueRenderer={this.renderSeverityOption}
+ />
+ </div>
);
renderStatusField = () => (
- <tr className="property">
- <th className="nowrap">
- <h3>{translate('coding_rules.filters.status')}</h3>
- </th>
- <td>
- <Select
- className="input-medium"
- clearable={false}
- disabled={this.state.submitting}
- onChange={this.handleStatusChange}
- options={RULE_STATUSES.map(status => ({
- label: translate('rules.status', status),
- value: status
- }))}
- searchable={false}
- value={this.state.status}
- />
- </td>
- </tr>
+ <div className="modal-field flex-1">
+ <label htmlFor="coding-rules-custom-rule-status">
+ {translate('coding_rules.filters.status')}
+ </label>
+ <Select
+ clearable={false}
+ disabled={this.state.submitting}
+ id="coding-rules-custom-rule-status"
+ onChange={this.handleStatusChange}
+ options={RULE_STATUSES.map(status => ({
+ label: translate('rules.status', status),
+ value: status
+ }))}
+ searchable={false}
+ value={this.state.status}
+ />
+ </div>
);
renderParameterField = (param: T.RuleParameter) => (
- <tr className="property" key={param.key}>
- <th className="nowrap">
- <h3>{param.key}</h3>
- </th>
- <td>
- {param.type === 'TEXT' ? (
- <textarea
- className="width100"
- disabled={this.state.submitting}
- name={param.key}
- onChange={this.handleParameterChange}
- placeholder={param.defaultValue}
- rows={3}
- value={this.state.params[param.key] || ''}
- />
- ) : (
- <input
- className="input-super-large"
- disabled={this.state.submitting}
- name={param.key}
- onChange={this.handleParameterChange}
- placeholder={param.defaultValue}
- type="text"
- value={this.state.params[param.key] || ''}
- />
- )}
- <div className="note" dangerouslySetInnerHTML={{ __html: param.htmlDesc || '' }} />
- </td>
- </tr>
+ <div className="modal-field" key={param.key}>
+ <label className="capitalize" htmlFor={param.key}>
+ {param.key}
+ </label>
+
+ {param.type === 'TEXT' ? (
+ <textarea
+ disabled={this.state.submitting}
+ id={param.key}
+ name={param.key}
+ onChange={this.handleParameterChange}
+ placeholder={param.defaultValue}
+ rows={3}
+ value={this.state.params[param.key] || ''}
+ />
+ ) : (
+ <input
+ disabled={this.state.submitting}
+ id={param.key}
+ name={param.key}
+ onChange={this.handleParameterChange}
+ placeholder={param.defaultValue}
+ type="text"
+ value={this.state.params[param.key] || ''}
+ />
+ )}
+ <div
+ className="modal-field-description"
+ dangerouslySetInnerHTML={{ __html: param.htmlDesc || '' }}
+ />
+ </div>
);
renderSubmitButton = () => {
@@ -371,18 +347,17 @@ export default class CustomRuleFormModal extends React.PureComponent<Props, Stat
{reactivating && (
<Alert variant="warning">{translate('coding_rules.reactivate.help')}</Alert>
)}
- <table>
- <tbody>
- {this.renderNameField()}
- {this.renderKeyField()}
- {this.renderDescriptionField()}
- {/* do not allow to change the type of existing rule */}
- {!customRule && this.renderTypeField()}
- {this.renderSeverityField()}
- {this.renderStatusField()}
- {params.map(this.renderParameterField)}
- </tbody>
- </table>
+
+ {this.renderNameField()}
+ {this.renderKeyField()}
+ <div className="display-flex-space-between">
+ {/* do not allow to change the type of existing rule */}
+ {!customRule && this.renderTypeField()}
+ {this.renderSeverityField()}
+ {this.renderStatusField()}
+ </div>
+ {this.renderDescriptionField()}
+ {params.map(this.renderParameterField)}
</div>
<div className="modal-foot">
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetailsCustomRules.tsx b/server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetailsCustomRules.tsx
index 39c11d5468a..966a96ca51c 100644
--- a/server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetailsCustomRules.tsx
+++ b/server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetailsCustomRules.tsx
@@ -104,7 +104,7 @@ export default class RuleDetailsCustomRules extends React.PureComponent<Props, S
</td>
<td className="coding-rules-detail-list-severity">
- <SeverityHelper severity={rule.severity} />
+ <SeverityHelper className="display-flex-center" severity={rule.severity} />
</td>
<td className="coding-rules-detail-list-parameters">
@@ -163,7 +163,7 @@ export default class RuleDetailsCustomRules extends React.PureComponent<Props, S
</CustomRuleButton>
)}
- <DeferredSpinner loading={loading}>
+ <DeferredSpinner className="spacer-left" loading={loading}>
{rules.length > 0 && (
<table className="coding-rules-detail-list" id="coding-rules-detail-custom-rules">
<tbody>{sortBy(rules, rule => rule.name).map(this.renderRule)}</tbody>
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetailsDescription.tsx b/server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetailsDescription.tsx
index c7bc85d7817..0f22be47412 100644
--- a/server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetailsDescription.tsx
+++ b/server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetailsDescription.tsx
@@ -127,16 +127,16 @@ export default class RuleDetailsDescription extends React.PureComponent<Props, S
renderForm = () => (
<div className="coding-rules-detail-extend-description-form">
- <table className="width100">
+ <table className="width-100">
<tbody>
<tr>
- <td className="width100" colSpan={2}>
+ <td colSpan={2}>
<textarea
autoFocus={true}
+ className="width-100 little-spacer-bottom"
id="coding-rules-detail-extend-description-text"
onChange={this.handleDescriptionChange}
rows={4}
- style={{ width: '100%', marginBottom: 4 }}
value={this.state.description}
/>
</td>
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetailsProfiles.tsx b/server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetailsProfiles.tsx
index 74cfa236e86..29f4de18cba 100644
--- a/server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetailsProfiles.tsx
+++ b/server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetailsProfiles.tsx
@@ -280,7 +280,7 @@ export default class RuleDetailsProfiles extends React.PureComponent<Props, Stat
{activations.length > 0 && (
<table
- className="coding-rules-detail-quality-profiles width100"
+ className="coding-rules-detail-quality-profiles width-100"
id="coding-rules-detail-quality-profiles">
<tbody>{activations.map(this.renderActivation)}</tbody>
</table>
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/ActivationFormModal-test.tsx b/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/ActivationFormModal-test.tsx
new file mode 100644
index 00000000000..5b9cd54183d
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/ActivationFormModal-test.tsx
@@ -0,0 +1,38 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+import * as React from 'react';
+import { shallow } from 'enzyme';
+import ActivationFormModal from '../ActivationFormModal';
+import { mockQualityProfile, mockRule } from '../../../../helpers/testMocks';
+
+it('render correctly', () => {
+ expect(
+ shallow(
+ <ActivationFormModal
+ modalHeader="title"
+ onClose={jest.fn()}
+ onDone={jest.fn()}
+ organization="foo"
+ profiles={[mockQualityProfile()]}
+ rule={mockRule()}
+ />
+ )
+ ).toMatchSnapshot();
+});
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/BulkChangeModal-test.tsx b/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/BulkChangeModal-test.tsx
new file mode 100644
index 00000000000..6174a899e02
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/BulkChangeModal-test.tsx
@@ -0,0 +1,41 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+import * as React from 'react';
+import { shallow } from 'enzyme';
+import BulkChangeModal from '../BulkChangeModal';
+import { mockQualityProfile } from '../../../../helpers/testMocks';
+import { Query } from '../../query';
+
+it('render correctly', () => {
+ expect(
+ shallow(
+ <BulkChangeModal
+ action="activate"
+ languages={{ js: { key: 'js', name: 'JavaScript' } }}
+ onClose={jest.fn()}
+ organization="foo"
+ profile={mockQualityProfile()}
+ query={{ languages: ['js'] } as Query}
+ referencedProfiles={{ foo: mockQualityProfile() }}
+ total={42}
+ />
+ )
+ ).toMatchSnapshot();
+});
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/CustomRuleFormModal-test.tsx b/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/CustomRuleFormModal-test.tsx
new file mode 100644
index 00000000000..8677a879507
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/CustomRuleFormModal-test.tsx
@@ -0,0 +1,39 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+import * as React from 'react';
+import { shallow } from 'enzyme';
+import CustomRuleFormModal from '../CustomRuleFormModal';
+import { mockRule } from '../../../../helpers/testMocks';
+
+it('should render correctly', () => {
+ expect(shallowRender()).toMatchSnapshot();
+});
+
+function shallowRender(props: Partial<CustomRuleFormModal['props']> = {}) {
+ return shallow(
+ <CustomRuleFormModal
+ onClose={jest.fn()}
+ onDone={jest.fn()}
+ organization={undefined}
+ templateRule={{ ...mockRule(), createdAt: 'date', repo: 'squid' }}
+ {...props}
+ />
+ );
+}
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/RuleListItem-test.tsx b/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/RuleListItem-test.tsx
index 2b220a43753..3d7b44dcb09 100644
--- a/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/RuleListItem-test.tsx
+++ b/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/RuleListItem-test.tsx
@@ -20,19 +20,7 @@
import * as React from 'react';
import { shallow } from 'enzyme';
import RuleListItem from '../RuleListItem';
-import { mockEvent } from '../../../../helpers/testMocks';
-
-const rule: T.Rule = {
- key: 'foo',
- lang: 'js',
- langName: 'JavaScript',
- name: 'Use foo',
- severity: 'MAJOR',
- status: 'READY',
- sysTags: ['a', 'b'],
- tags: ['x'],
- type: 'CODE_SMELL'
-};
+import { mockEvent, mockRule } from '../../../../helpers/testMocks';
it('should render', () => {
expect(shallowRender()).toMatchSnapshot();
@@ -42,7 +30,7 @@ it('should open rule', () => {
const onOpen = jest.fn();
const wrapper = shallowRender({ onOpen });
wrapper.find('Link').prop<Function>('onClick')(mockEvent({ button: 0 }));
- expect(onOpen).toBeCalledWith('foo');
+ expect(onOpen).toBeCalledWith('javascript:S1067');
});
function shallowRender(props?: Partial<RuleListItem['props']>) {
@@ -53,7 +41,7 @@ function shallowRender(props?: Partial<RuleListItem['props']>) {
onFilterChange={jest.fn()}
onOpen={jest.fn()}
organization="org"
- rule={rule}
+ rule={mockRule()}
selected={false}
{...props}
/>
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/ActivationFormModal-test.tsx.snap b/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/ActivationFormModal-test.tsx.snap
new file mode 100644
index 00000000000..2225b883507
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/ActivationFormModal-test.tsx.snap
@@ -0,0 +1,101 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`render correctly 1`] = `
+<Modal
+ contentLabel="title"
+ onRequestClose={[MockFunction]}
+ size="small"
+>
+ <form
+ onSubmit={[Function]}
+ >
+ <div
+ className="modal-head"
+ >
+ <h2>
+ title
+ </h2>
+ </div>
+ <div
+ className="modal-body"
+ >
+ <Alert
+ variant="info"
+ >
+ coding_rules.active_in_all_profiles
+ </Alert>
+ <div
+ className="modal-field"
+ >
+ <label>
+ coding_rules.quality_profile
+ </label>
+ <Select
+ className="js-profile"
+ clearable={false}
+ disabled={false}
+ onChange={[Function]}
+ options={Array []}
+ value=""
+ />
+ </div>
+ <div
+ className="modal-field"
+ >
+ <label>
+ severity
+ </label>
+ <Select
+ className="js-severity"
+ clearable={false}
+ disabled={false}
+ onChange={[Function]}
+ optionRenderer={[Function]}
+ options={
+ Array [
+ Object {
+ "label": "severity.BLOCKER",
+ "value": "BLOCKER",
+ },
+ Object {
+ "label": "severity.CRITICAL",
+ "value": "CRITICAL",
+ },
+ Object {
+ "label": "severity.MAJOR",
+ "value": "MAJOR",
+ },
+ Object {
+ "label": "severity.MINOR",
+ "value": "MINOR",
+ },
+ Object {
+ "label": "severity.INFO",
+ "value": "INFO",
+ },
+ ]
+ }
+ searchable={false}
+ value="MAJOR"
+ valueRenderer={[Function]}
+ />
+ </div>
+ </div>
+ <footer
+ className="modal-foot"
+ >
+ <SubmitButton
+ disabled={true}
+ >
+ coding_rules.activate
+ </SubmitButton>
+ <ResetButtonLink
+ disabled={false}
+ onClick={[MockFunction]}
+ >
+ cancel
+ </ResetButtonLink>
+ </footer>
+ </form>
+</Modal>
+`;
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/BulkChangeModal-test.tsx.snap b/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/BulkChangeModal-test.tsx.snap
new file mode 100644
index 00000000000..fed86bf741a
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/BulkChangeModal-test.tsx.snap
@@ -0,0 +1,56 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`render correctly 1`] = `
+<Modal
+ contentLabel="coding_rules.activate_in_quality_profile (42 coding_rules._rules)"
+ onRequestClose={[MockFunction]}
+ size="small"
+>
+ <form
+ onSubmit={[Function]}
+ >
+ <header
+ className="modal-head"
+ >
+ <h2>
+ coding_rules.activate_in_quality_profile (42 coding_rules._rules)
+ </h2>
+ </header>
+ <div
+ className="modal-body"
+ >
+ <div
+ className="modal-field"
+ >
+ <h3>
+ <label
+ htmlFor="coding-rules-bulk-change-profile"
+ >
+ coding_rules.activate_in
+ </label>
+ </h3>
+ <span>
+ name
+ —
+ are_you_sure
+ </span>
+ </div>
+ </div>
+ <footer
+ className="modal-foot"
+ >
+ <SubmitButton
+ disabled={false}
+ id="coding-rules-submit-bulk-change"
+ >
+ apply
+ </SubmitButton>
+ <ResetButtonLink
+ onClick={[MockFunction]}
+ >
+ cancel
+ </ResetButtonLink>
+ </footer>
+ </form>
+</Modal>
+`;
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/CustomRuleFormModal-test.tsx.snap b/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/CustomRuleFormModal-test.tsx.snap
new file mode 100644
index 00000000000..71f42992ddd
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/CustomRuleFormModal-test.tsx.snap
@@ -0,0 +1,237 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render correctly 1`] = `
+<Modal
+ contentLabel="coding_rules.create_custom_rule"
+ onRequestClose={[MockFunction]}
+>
+ <form
+ onSubmit={[Function]}
+ >
+ <div
+ className="modal-head"
+ >
+ <h2>
+ coding_rules.create_custom_rule
+ </h2>
+ </div>
+ <div
+ className="modal-body modal-container"
+ >
+ <div
+ className="modal-field"
+ >
+ <label
+ htmlFor="coding-rules-custom-rule-creation-name"
+ >
+ name
+
+ <em
+ className="mandatory"
+ >
+ *
+ </em>
+ </label>
+ <input
+ autoFocus={true}
+ disabled={false}
+ id="coding-rules-custom-rule-creation-name"
+ onChange={[Function]}
+ required={true}
+ type="text"
+ value=""
+ />
+ </div>
+ <div
+ className="modal-field"
+ >
+ <label
+ htmlFor="coding-rules-custom-rule-creation-key"
+ >
+ key
+
+ <em
+ className="mandatory"
+ >
+ *
+ </em>
+ </label>
+ <input
+ disabled={false}
+ id="coding-rules-custom-rule-creation-key"
+ onChange={[Function]}
+ required={true}
+ type="text"
+ value=""
+ />
+ </div>
+ <div
+ className="display-flex-space-between"
+ >
+ <div
+ className="modal-field flex-1 spacer-right"
+ >
+ <label
+ htmlFor="coding-rules-custom-rule-type"
+ >
+ type
+ </label>
+ <Select
+ clearable={false}
+ disabled={false}
+ id="coding-rules-custom-rule-type"
+ onChange={[Function]}
+ optionRenderer={[Function]}
+ options={
+ Array [
+ Object {
+ "label": "issue.type.BUG",
+ "value": "BUG",
+ },
+ Object {
+ "label": "issue.type.VULNERABILITY",
+ "value": "VULNERABILITY",
+ },
+ Object {
+ "label": "issue.type.CODE_SMELL",
+ "value": "CODE_SMELL",
+ },
+ Object {
+ "label": "issue.type.SECURITY_HOTSPOT",
+ "value": "SECURITY_HOTSPOT",
+ },
+ Object {
+ "label": "issue.type.UNKNOWN",
+ "value": "UNKNOWN",
+ },
+ ]
+ }
+ searchable={false}
+ value="CODE_SMELL"
+ valueRenderer={[Function]}
+ />
+ </div>
+ <div
+ className="modal-field flex-1 spacer-right"
+ >
+ <label
+ htmlFor="coding-rules-custom-rule-severity"
+ >
+ severity
+ </label>
+ <Select
+ clearable={false}
+ disabled={false}
+ id="coding-rules-custom-rule-severity"
+ onChange={[Function]}
+ optionRenderer={[Function]}
+ options={
+ Array [
+ Object {
+ "label": "severity.BLOCKER",
+ "value": "BLOCKER",
+ },
+ Object {
+ "label": "severity.CRITICAL",
+ "value": "CRITICAL",
+ },
+ Object {
+ "label": "severity.MAJOR",
+ "value": "MAJOR",
+ },
+ Object {
+ "label": "severity.MINOR",
+ "value": "MINOR",
+ },
+ Object {
+ "label": "severity.INFO",
+ "value": "INFO",
+ },
+ ]
+ }
+ searchable={false}
+ value="MAJOR"
+ valueRenderer={[Function]}
+ />
+ </div>
+ <div
+ className="modal-field flex-1"
+ >
+ <label
+ htmlFor="coding-rules-custom-rule-status"
+ >
+ coding_rules.filters.status
+ </label>
+ <Select
+ clearable={false}
+ disabled={false}
+ id="coding-rules-custom-rule-status"
+ onChange={[Function]}
+ options={
+ Array [
+ Object {
+ "label": "rules.status.READY",
+ "value": "READY",
+ },
+ Object {
+ "label": "rules.status.BETA",
+ "value": "BETA",
+ },
+ Object {
+ "label": "rules.status.DEPRECATED",
+ "value": "DEPRECATED",
+ },
+ ]
+ }
+ searchable={false}
+ value="READY"
+ />
+ </div>
+ </div>
+ <div
+ className="modal-field"
+ >
+ <label
+ htmlFor="coding-rules-custom-rule-creation-html-description"
+ >
+ description
+
+ <em
+ className="mandatory"
+ >
+ *
+ </em>
+ </label>
+ <textarea
+ disabled={false}
+ id="coding-rules-custom-rule-creation-html-description"
+ onChange={[Function]}
+ required={true}
+ rows={5}
+ value=""
+ />
+ <MarkdownTips
+ className="modal-field-descriptor text-right"
+ />
+ </div>
+ </div>
+ <div
+ className="modal-foot"
+ >
+ <SubmitButton
+ disabled={false}
+ id="coding-rules-custom-rule-creation-create"
+ >
+ create
+ </SubmitButton>
+ <ResetButtonLink
+ disabled={false}
+ id="coding-rules-custom-rule-creation-cancel"
+ onClick={[MockFunction]}
+ >
+ cancel
+ </ResetButtonLink>
+ </div>
+ </form>
+</Modal>
+`;
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/RuleListItem-test.tsx.snap b/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/RuleListItem-test.tsx.snap
index 459a8165037..e1b94a8164a 100644
--- a/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/RuleListItem-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/RuleListItem-test.tsx.snap
@@ -3,7 +3,7 @@
exports[`should render 1`] = `
<div
className="coding-rule"
- data-rule="foo"
+ data-rule="javascript:S1067"
>
<table
className="coding-rule-table"
@@ -23,8 +23,8 @@ exports[`should render 1`] = `
Object {
"pathname": "/organizations/org/rules",
"query": Object {
- "open": "foo",
- "rule_key": "foo",
+ "open": "javascript:S1067",
+ "rule_key": "javascript:S1067",
},
}
}
@@ -72,7 +72,7 @@ exports[`should render 1`] = `
onFilterChange={[MockFunction]}
rule={
Object {
- "key": "foo",
+ "key": "javascript:S1067",
"lang": "js",
"langName": "JavaScript",
"name": "Use foo",
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/styles.css b/server/sonar-web/src/main/js/apps/coding-rules/styles.css
index ca7a4fdde40..8450e0dab94 100644
--- a/server/sonar-web/src/main/js/apps/coding-rules/styles.css
+++ b/server/sonar-web/src/main/js/apps/coding-rules/styles.css
@@ -201,15 +201,6 @@
margin-left: 10px;
}
-input.coding-rules-name-key {
- width: 100%;
-}
-
-textarea.coding-rules-markdown-description {
- width: 100%;
- margin-bottom: 4px;
-}
-
.coding-rules-most-violated-projects td {
border-top-color: transparent;
}
diff --git a/server/sonar-web/src/main/js/apps/create/components/UpgradeOrganizationModal.tsx b/server/sonar-web/src/main/js/apps/create/components/UpgradeOrganizationModal.tsx
index dfaca7bc0f0..27467f3420f 100644
--- a/server/sonar-web/src/main/js/apps/create/components/UpgradeOrganizationModal.tsx
+++ b/server/sonar-web/src/main/js/apps/create/components/UpgradeOrganizationModal.tsx
@@ -72,10 +72,10 @@ export default class UpgradeOrganizationModal extends React.PureComponent<Props,
return (
<Modal
contentLabel={header}
- medium={true}
noBackdrop={this.props.insideModal}
onRequestClose={this.props.onClose}
- shouldCloseOnOverlayClick={false}>
+ shouldCloseOnOverlayClick={false}
+ size={'medium'}>
<div className="modal-head">
<h2>{header}</h2>
</div>
diff --git a/server/sonar-web/src/main/js/apps/create/components/__tests__/__snapshots__/UpgradeOrganizationModal-test.tsx.snap b/server/sonar-web/src/main/js/apps/create/components/__tests__/__snapshots__/UpgradeOrganizationModal-test.tsx.snap
index 3043cc08b5f..4d294fc1497 100644
--- a/server/sonar-web/src/main/js/apps/create/components/__tests__/__snapshots__/UpgradeOrganizationModal-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/create/components/__tests__/__snapshots__/UpgradeOrganizationModal-test.tsx.snap
@@ -3,9 +3,9 @@
exports[`should render correctly 1`] = `
<Modal
contentLabel="billing.upgrade_box.upgrade_to_paid_plan"
- medium={true}
onRequestClose={[MockFunction]}
shouldCloseOnOverlayClick={false}
+ size="medium"
>
<div
className="modal-head"
diff --git a/server/sonar-web/src/main/js/apps/create/organization/AutoOrganizationCreate.tsx b/server/sonar-web/src/main/js/apps/create/organization/AutoOrganizationCreate.tsx
index bce0bcb2c21..3a40718de56 100644
--- a/server/sonar-web/src/main/js/apps/create/organization/AutoOrganizationCreate.tsx
+++ b/server/sonar-web/src/main/js/apps/create/organization/AutoOrganizationCreate.tsx
@@ -77,12 +77,17 @@ export default class AutoOrganizationCreate extends React.PureComponent<Props, S
};
handleCreateOrganization = () => {
- const { organization } = this.props;
+ const { almApplication, almOrganization, organization } = this.props;
if (!organization) {
return Promise.reject();
}
return this.props.createOrganization({
...organization,
+ alm: {
+ key: almApplication.key,
+ membersSync: true,
+ url: almOrganization.almUrl
+ },
installationId: this.props.almInstallId
});
};
@@ -169,10 +174,13 @@ export default class AutoOrganizationCreate extends React.PureComponent<Props, S
)}
</p>
<a
- href={getAlmMembersUrl(almOrganization.key, almOrganization.almUrl)}
+ href={getAlmMembersUrl(almApplication.key, almOrganization.almUrl)}
rel="noopener noreferrer"
target="_blank">
- {translate('onboarding.import_organization.see_who_has_access')}
+ {translateWithParameters(
+ 'organization.members.see_all_members_on_x',
+ translate(almKey)
+ )}
</a>
</Alert>
}
diff --git a/server/sonar-web/src/main/js/apps/create/organization/RemoteOrganizationChoose.tsx b/server/sonar-web/src/main/js/apps/create/organization/RemoteOrganizationChoose.tsx
index 4c086a58779..33d9fcc8d79 100644
--- a/server/sonar-web/src/main/js/apps/create/organization/RemoteOrganizationChoose.tsx
+++ b/server/sonar-web/src/main/js/apps/create/organization/RemoteOrganizationChoose.tsx
@@ -168,7 +168,7 @@ export class RemoteOrganizationChoose extends React.PureComponent<Props & WithRo
</div>
<form className="big-spacer-top big-spacer-bottom" onSubmit={this.handleSubmit}>
<div className="form-field abs-width-400">
- <label htmlFor="select-unbound-installation">
+ <label className="text-normal" htmlFor="select-unbound-installation">
{translateWithParameters(
'onboarding.import_organization.choose_unbound_installation_x',
translate(sanitizeAlmId(almApplication.key))
diff --git a/server/sonar-web/src/main/js/apps/create/organization/__tests__/AutoOrganizationCreate-test.tsx b/server/sonar-web/src/main/js/apps/create/organization/__tests__/AutoOrganizationCreate-test.tsx
index 8ac2a9e1a07..b3fe16d362c 100644
--- a/server/sonar-web/src/main/js/apps/create/organization/__tests__/AutoOrganizationCreate-test.tsx
+++ b/server/sonar-web/src/main/js/apps/create/organization/__tests__/AutoOrganizationCreate-test.tsx
@@ -22,7 +22,7 @@ import { shallow } from 'enzyme';
import AutoOrganizationCreate from '../AutoOrganizationCreate';
import { Step } from '../utils';
import { bindAlmOrganization } from '../../../../api/alm-integration';
-import { mockAlmOrganization } from '../../../../helpers/testMocks';
+import { mockAlmOrganization, mockAlmApplication } from '../../../../helpers/testMocks';
import { waitAndUpdate, click } from '../../../../helpers/testUtils';
jest.mock('../../../../api/alm-integration', () => ({
@@ -34,7 +34,14 @@ const organization = mockAlmOrganization();
it('should render prefilled and create org', async () => {
const createOrganization = jest.fn().mockResolvedValue({ key: 'foo' });
const handleOrgDetailsFinish = jest.fn();
- const wrapper = shallowRender({ createOrganization, handleOrgDetailsFinish });
+ const almApplication = mockAlmApplication({ key: 'github' });
+ const almOrganization = mockAlmOrganization({ almUrl: 'http://github.com/thing' });
+ const wrapper = shallowRender({
+ almApplication,
+ almOrganization,
+ createOrganization,
+ handleOrgDetailsFinish
+ });
expect(wrapper).toMatchSnapshot();
@@ -44,7 +51,13 @@ it('should render prefilled and create org', async () => {
wrapper.setProps({ organization });
wrapper.find('PlanStep').prop<Function>('createOrganization')();
- expect(createOrganization).toBeCalledWith({ ...organization, installationId: 'id-foo' });
+
+ const alm = {
+ key: 'github',
+ membersSync: true,
+ url: 'http://github.com/thing'
+ };
+ expect(createOrganization).toBeCalledWith({ ...organization, alm, installationId: 'id-foo' });
});
it('should allow to cancel org import', () => {
@@ -86,13 +99,7 @@ it('should bind existing organization', async () => {
function shallowRender(props: Partial<AutoOrganizationCreate['props']> = {}) {
return shallow(
<AutoOrganizationCreate
- almApplication={{
- backgroundColor: '#0052CC',
- iconPath: '"/static/authbitbucket/bitbucket.svg"',
- installationUrl: 'https://bitbucket.org/install/app',
- key: 'bitbucket',
- name: 'BitBucket'
- }}
+ almApplication={mockAlmApplication()}
almInstallId="id-foo"
almOrganization={{ ...organization, personal: false }}
createOrganization={jest.fn()}
diff --git a/server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/AutoOrganizationCreate-test.tsx.snap b/server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/AutoOrganizationCreate-test.tsx.snap
index d16fbc70263..ca0868ae105 100644
--- a/server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/AutoOrganizationCreate-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/AutoOrganizationCreate-test.tsx.snap
@@ -121,7 +121,7 @@ exports[`should render prefilled and create org 1`] = `
"avatar": <img
alt="BitBucket"
className="little-spacer-left"
- src="/images/sonarcloud/bitbucket.svg"
+ src="/images/sonarcloud/github.svg"
width={16}
/>,
"name": <strong>
@@ -144,21 +144,21 @@ exports[`should render prefilled and create org 1`] = `
variant="info"
>
<p>
- onboarding.import_organization.members_sync_info_x.organization.bitbucket.foo.bitbucket
+ onboarding.import_organization.members_sync_info_x.organization.github.foo.github
</p>
<a
- href="https://github.com/foo/profile/members"
+ href="http://github.com/orgs/thing/people"
rel="noopener noreferrer"
target="_blank"
>
- onboarding.import_organization.see_who_has_access
+ organization.members.see_all_members_on_x.github
</a>
</Alert>
}
onContinue={[MockFunction]}
organization={
Object {
- "almUrl": "https://github.com/foo",
+ "almUrl": "http://github.com/thing",
"avatar": "http://example.com/avatar",
"description": "description-foo",
"key": "foo",
@@ -178,13 +178,13 @@ exports[`should render prefilled and create org 1`] = `
"backgroundColor": "#0052CC",
"iconPath": "\\"/static/authbitbucket/bitbucket.svg\\"",
"installationUrl": "https://bitbucket.org/install/app",
- "key": "bitbucket",
+ "key": "github",
"name": "BitBucket",
}
}
almOrganization={
Object {
- "almUrl": "https://github.com/foo",
+ "almUrl": "http://github.com/thing",
"avatar": "http://example.com/avatar",
"description": "description-foo",
"key": "foo",
diff --git a/server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/RemoteOrganizationChoose-test.tsx.snap b/server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/RemoteOrganizationChoose-test.tsx.snap
index 8cd6afa0fd3..cae179cdd68 100644
--- a/server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/RemoteOrganizationChoose-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/RemoteOrganizationChoose-test.tsx.snap
@@ -124,6 +124,7 @@ exports[`should display unbound installations 1`] = `
className="form-field abs-width-400"
>
<label
+ className="text-normal"
htmlFor="select-unbound-installation"
>
onboarding.import_organization.choose_unbound_installation_x.github
diff --git a/server/sonar-web/src/main/js/apps/create/organization/__tests__/actions-test.ts b/server/sonar-web/src/main/js/apps/create/organization/__tests__/actions-test.ts
index 26800ae126a..f95556451f8 100644
--- a/server/sonar-web/src/main/js/apps/create/organization/__tests__/actions-test.ts
+++ b/server/sonar-web/src/main/js/apps/create/organization/__tests__/actions-test.ts
@@ -51,9 +51,13 @@ describe('#createOrganization', () => {
});
it('should create and sync members', async () => {
- const org = mockOrganizationWithAlm({}, { membersSync: true });
+ const { alm, ...org } = mockOrganizationWithAlm(
+ {},
+ { key: 'github', membersSync: true, url: 'https://github.com/foo' }
+ );
+
(createOrganization as jest.Mock).mockResolvedValueOnce(org);
- const promise = actions.createOrganization(org)(dispatch);
+ const promise = actions.createOrganization({ alm, ...org })(dispatch);
expect(createOrganization).toHaveBeenCalledWith(org);
await promise;
diff --git a/server/sonar-web/src/main/js/apps/create/organization/actions.ts b/server/sonar-web/src/main/js/apps/create/organization/actions.ts
index 400de6cdf61..38b90fff706 100644
--- a/server/sonar-web/src/main/js/apps/create/organization/actions.ts
+++ b/server/sonar-web/src/main/js/apps/create/organization/actions.ts
@@ -21,17 +21,21 @@ import { Dispatch } from 'redux';
import { bindAlmOrganization } from '../../../api/alm-integration';
import * as api from '../../../api/organizations';
import * as actions from '../../../store/organizations';
+import { isGithub } from '../../../helpers/almIntegrations';
-export function createOrganization(organization: T.Organization & { installationId?: string }) {
+export function createOrganization({
+ alm,
+ ...organization
+}: T.Organization & { installationId?: string }) {
return (dispatch: Dispatch) => {
return api
.createOrganization({ ...organization, name: organization.name || organization.key })
- .then((organization: T.Organization) => {
- dispatch(actions.createOrganization(organization));
- if (organization.alm && organization.alm.membersSync) {
- api.syncMembers(organization.key);
+ .then((newOrganization: T.Organization) => {
+ dispatch(actions.createOrganization({ ...newOrganization, alm }));
+ if (alm && alm.membersSync && isGithub(alm.key)) {
+ api.syncMembers(newOrganization.key);
}
- return organization.key;
+ return newOrganization.key;
});
};
}
diff --git a/server/sonar-web/src/main/js/apps/custom-measures/components/Form.tsx b/server/sonar-web/src/main/js/apps/custom-measures/components/Form.tsx
index 261872e75ae..053b0789166 100644
--- a/server/sonar-web/src/main/js/apps/custom-measures/components/Form.tsx
+++ b/server/sonar-web/src/main/js/apps/custom-measures/components/Form.tsx
@@ -18,13 +18,13 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
-import { getAllMetrics } from '../../../api/metrics';
import DeferredSpinner from '../../../components/common/DeferredSpinner';
import Select from '../../../components/controls/Select';
import SimpleModal from '../../../components/controls/SimpleModal';
+import { Alert } from '../../../components/ui/Alert';
import { SubmitButton, ResetButtonLink } from '../../../components/ui/buttons';
+import { getAllMetrics } from '../../../api/metrics';
import { translate } from '../../../helpers/l10n';
-import { Alert } from '../../../components/ui/Alert';
interface Props {
confirmButtonText: string;
@@ -145,7 +145,8 @@ export default class Form extends React.PureComponent<Props, State> {
<SimpleModal
header={this.props.header}
onClose={this.props.onClose}
- onSubmit={this.handleSubmit}>
+ onSubmit={this.handleSubmit}
+ size="small">
{({ onCloseClick, onFormSubmit, submitting }) => (
<form onSubmit={onFormSubmit}>
<header className="modal-head">
diff --git a/server/sonar-web/src/main/js/apps/custom-measures/components/__tests__/__snapshots__/Form-test.tsx.snap b/server/sonar-web/src/main/js/apps/custom-measures/components/__tests__/__snapshots__/Form-test.tsx.snap
index 91da25f4c4b..aa237802219 100644
--- a/server/sonar-web/src/main/js/apps/custom-measures/components/__tests__/__snapshots__/Form-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/custom-measures/components/__tests__/__snapshots__/Form-test.tsx.snap
@@ -4,6 +4,7 @@ exports[`should render form 1`] = `
<Modal
contentLabel="header"
onRequestClose={[MockFunction]}
+ size="small"
>
<form
onSubmit={[Function]}
@@ -105,6 +106,7 @@ exports[`should render form 2`] = `
<Modal
contentLabel="header"
onRequestClose={[MockFunction]}
+ size="small"
>
<form
onSubmit={[Function]}
diff --git a/server/sonar-web/src/main/js/apps/custom-metrics/components/Form.tsx b/server/sonar-web/src/main/js/apps/custom-metrics/components/Form.tsx
index ad67fa6bc80..ffc3d920734 100644
--- a/server/sonar-web/src/main/js/apps/custom-metrics/components/Form.tsx
+++ b/server/sonar-web/src/main/js/apps/custom-metrics/components/Form.tsx
@@ -20,9 +20,9 @@
import * as React from 'react';
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';
+import { translate } from '../../../helpers/l10n';
export interface MetricProps {
description: string;
@@ -98,7 +98,8 @@ export default class Form extends React.PureComponent<Props, State> {
<SimpleModal
header={this.props.header}
onClose={this.props.onClose}
- onSubmit={this.handleSubmit}>
+ onSubmit={this.handleSubmit}
+ size="small">
{({ onCloseClick, onFormSubmit, submitting }) => (
<form onSubmit={onFormSubmit}>
<header className="modal-head">
diff --git a/server/sonar-web/src/main/js/apps/custom-metrics/components/__tests__/__snapshots__/Form-test.tsx.snap b/server/sonar-web/src/main/js/apps/custom-metrics/components/__tests__/__snapshots__/Form-test.tsx.snap
index 69ff6188f6b..692174076ae 100644
--- a/server/sonar-web/src/main/js/apps/custom-metrics/components/__tests__/__snapshots__/Form-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/custom-metrics/components/__tests__/__snapshots__/Form-test.tsx.snap
@@ -4,6 +4,7 @@ exports[`should render form 1`] = `
<Modal
contentLabel="header"
onRequestClose={[MockFunction]}
+ size="small"
>
<form
onSubmit={[Function]}
diff --git a/server/sonar-web/src/main/js/apps/groups/components/Form.tsx b/server/sonar-web/src/main/js/apps/groups/components/Form.tsx
index 73e2c0ee5f7..3f5d72afe08 100644
--- a/server/sonar-web/src/main/js/apps/groups/components/Form.tsx
+++ b/server/sonar-web/src/main/js/apps/groups/components/Form.tsx
@@ -64,7 +64,8 @@ export default class Form extends React.PureComponent<Props, State> {
<SimpleModal
header={this.props.header}
onClose={this.props.onClose}
- onSubmit={this.handleSubmit}>
+ onSubmit={this.handleSubmit}
+ size="small">
{({ onCloseClick, onFormSubmit, submitting }) => (
<form onSubmit={onFormSubmit}>
<header className="modal-head">
diff --git a/server/sonar-web/src/main/js/apps/groups/components/__tests__/__snapshots__/Form-test.tsx.snap b/server/sonar-web/src/main/js/apps/groups/components/__tests__/__snapshots__/Form-test.tsx.snap
index 2b0954e09fb..58f9268cf6c 100644
--- a/server/sonar-web/src/main/js/apps/groups/components/__tests__/__snapshots__/Form-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/groups/components/__tests__/__snapshots__/Form-test.tsx.snap
@@ -4,6 +4,7 @@ exports[`should render form 1`] = `
<Modal
contentLabel="header"
onRequestClose={[MockFunction]}
+ size="small"
>
<form
onSubmit={[Function]}
diff --git a/server/sonar-web/src/main/js/apps/issues/components/BulkChangeModal.tsx b/server/sonar-web/src/main/js/apps/issues/components/BulkChangeModal.tsx
index 361fb238886..5082742f980 100644
--- a/server/sonar-web/src/main/js/apps/issues/components/BulkChangeModal.tsx
+++ b/server/sonar-web/src/main/js/apps/issues/components/BulkChangeModal.tsx
@@ -18,23 +18,25 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
+import * as classNames from 'classnames';
import { FormattedMessage } from 'react-intl';
import { pickBy, sortBy } from 'lodash';
import { searchAssignees } from '../utils';
-import { searchIssueTags, bulkChangeIssues } from '../../../api/issues';
-import throwGlobalError from '../../../app/utils/throwGlobalError';
-import MarkdownTips from '../../../components/common/MarkdownTips';
-import SearchSelect from '../../../components/controls/SearchSelect';
+import Avatar from '../../../components/ui/Avatar';
import Checkbox from '../../../components/controls/Checkbox';
+import HelpTooltip from '../../../components/controls/HelpTooltip';
+import IssueTypeIcon from '../../../components/ui/IssueTypeIcon';
+import MarkdownTips from '../../../components/common/MarkdownTips';
import Modal from '../../../components/controls/Modal';
+import Radio from '../../../components/controls/Radio';
+import SearchSelect from '../../../components/controls/SearchSelect';
import Select from '../../../components/controls/Select';
-import HelpTooltip from '../../../components/controls/HelpTooltip';
import SeverityHelper from '../../../components/shared/SeverityHelper';
-import Avatar from '../../../components/ui/Avatar';
+import throwGlobalError from '../../../app/utils/throwGlobalError';
+import { Alert } from '../../../components/ui/Alert';
+import { searchIssueTags, bulkChangeIssues } from '../../../api/issues';
import { SubmitButton, ResetButtonLink } from '../../../components/ui/buttons';
-import IssueTypeIcon from '../../../components/ui/IssueTypeIcon';
import { translate, translateWithParameters } from '../../../helpers/l10n';
-import { Alert } from '../../../components/ui/Alert';
import { isLoggedIn } from '../../../helpers/users';
interface AssigneeOption {
@@ -188,10 +190,12 @@ export default class BulkChangeModal extends React.PureComponent<Props, State> {
}
};
- handleFieldChange = (field: 'comment' | 'transition') => (
- event: React.SyntheticEvent<HTMLInputElement | HTMLTextAreaElement>
- ) => {
- this.setState<keyof FormFields>({ [field]: event.currentTarget.value });
+ handleRadioTransitionChange = (transition: string) => {
+ this.setState({ transition });
+ };
+
+ handleCommentChange = (event: React.SyntheticEvent<HTMLTextAreaElement>) => {
+ this.setState({ comment: event.currentTarget.value });
};
handleSelectFieldChange = (field: 'severity' | 'type') => (data: { value: string } | null) => {
@@ -269,14 +273,6 @@ export default class BulkChangeModal extends React.PureComponent<Props, State> {
</div>
);
- renderCheckbox = (field: keyof FormFields, id?: string) => (
- <Checkbox
- checked={this.state[field] !== undefined}
- id={id}
- onCheck={this.handleFieldCheck(field)}
- />
- );
-
renderAffected = (affected: number) => (
<div className="pull-right note">
({translateWithParameters('issue_bulk_change.x_issues', affected)})
@@ -316,6 +312,7 @@ export default class BulkChangeModal extends React.PureComponent<Props, State> {
const input = (
<AssigneeSelect
+ className="input-super-large"
clearable={true}
defaultOptions={this.getDefaultAssignee()}
onSearch={this.handleAssigneeSearch}
@@ -348,6 +345,7 @@ export default class BulkChangeModal extends React.PureComponent<Props, State> {
const input = (
<Select
+ className="input-super-large"
clearable={true}
onChange={this.handleSelectFieldChange('type')}
optionRenderer={optionRenderer}
@@ -376,6 +374,7 @@ export default class BulkChangeModal extends React.PureComponent<Props, State> {
const input = (
<Select
+ className="input-super-large"
clearable={true}
onChange={this.handleSelectFieldChange('severity')}
optionRenderer={(option: { value: string }) => <SeverityHelper severity={option.value} />}
@@ -404,6 +403,7 @@ export default class BulkChangeModal extends React.PureComponent<Props, State> {
const input = (
<TagSelect
canCreate={allowCreate}
+ className="input-super-large"
clearable={true}
defaultOptions={this.state.initialTags}
minimumQueryLength={0}
@@ -431,22 +431,16 @@ export default class BulkChangeModal extends React.PureComponent<Props, State> {
<div className="modal-field">
<label>{translate('issue.transition')}</label>
{transitions.map(transition => (
- <span className="clearfix" key={transition.transition}>
- <input
+ <span
+ className="bulk-change-radio-button display-flex-center display-flex-space-between"
+ key={transition.transition}>
+ <Radio
checked={this.state.transition === transition.transition}
- id={`transition-${transition.transition}`}
- name="do_transition.transition"
- onChange={this.handleFieldChange('transition')}
- type="radio"
- value={transition.transition}
- />
- <label
- htmlFor={`transition-${transition.transition}`}
- style={{ float: 'none', display: 'inline', left: 0, cursor: 'pointer' }}>
+ onCheck={this.handleRadioTransitionChange}
+ value={transition.transition}>
{translate('issue.transition', transition.transition)}
- </label>
+ </Radio>
{this.renderAffected(transition.count)}
- <br />
</span>
))}
</div>
@@ -469,27 +463,26 @@ export default class BulkChangeModal extends React.PureComponent<Props, State> {
overlay={translate('issue_bulk_change.comment.help')}
/>
</label>
- <div>
- <textarea
- id="comment"
- onChange={this.handleFieldChange('comment')}
- rows={4}
- style={{ width: '100%' }}
- value={this.state.comment || ''}
- />
- </div>
- <div className="pull-right">
- <MarkdownTips />
- </div>
+ <textarea
+ id="comment"
+ onChange={this.handleCommentChange}
+ rows={4}
+ value={this.state.comment || ''}
+ />
+ <MarkdownTips className="modal-field-descriptor text-right" />
</div>
);
};
renderNotificationsField = () => (
- <div className="modal-field">
- <label htmlFor="send-notifications">{translate('issue.send_notifications')}</label>
- {this.renderCheckbox('notifications', 'send-notifications')}
- </div>
+ <Checkbox
+ checked={this.state.notifications !== undefined}
+ className="display-inline-block spacer-top"
+ id="send-notifications"
+ onCheck={this.handleFieldCheck('notifications')}
+ right={true}>
+ <strong className="little-spacer-right">{translate('issue.send_notifications')}</strong>
+ </Checkbox>
);
renderForm = () => {
@@ -503,7 +496,7 @@ export default class BulkChangeModal extends React.PureComponent<Props, State> {
<h2>{translateWithParameters('issue_bulk_change.form.title', issues.length)}</h2>
</div>
- <div className="modal-body">
+ <div className={classNames('modal-body', { 'modal-container': limitReached })}>
{limitReached && (
<Alert variant="warning">
<FormattedMessage
@@ -540,7 +533,7 @@ export default class BulkChangeModal extends React.PureComponent<Props, State> {
render() {
return (
- <Modal contentLabel="modal" onRequestClose={this.props.onClose}>
+ <Modal contentLabel="modal" onRequestClose={this.props.onClose} size={'small'}>
{this.state.loading ? this.renderLoading() : this.renderForm()}
</Modal>
);
diff --git a/server/sonar-web/src/main/js/apps/issues/components/__tests__/__snapshots__/BulkChangeModal-test.tsx.snap b/server/sonar-web/src/main/js/apps/issues/components/__tests__/__snapshots__/BulkChangeModal-test.tsx.snap
index e45da7a29a6..a15fbc837cb 100644
--- a/server/sonar-web/src/main/js/apps/issues/components/__tests__/__snapshots__/BulkChangeModal-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/issues/components/__tests__/__snapshots__/BulkChangeModal-test.tsx.snap
@@ -4,6 +4,7 @@ exports[`should display error message when no issues available 1`] = `
<Modal
contentLabel="modal"
onRequestClose={[Function]}
+ size="small"
>
<form
id="bulk-change-form"
@@ -48,6 +49,7 @@ exports[`should display form when issues are present 1`] = `
<Modal
contentLabel="modal"
onRequestClose={[Function]}
+ size="small"
>
<form
id="bulk-change-form"
@@ -63,21 +65,20 @@ exports[`should display form when issues are present 1`] = `
<div
className="modal-body"
>
- <div
- className="modal-field"
+ <Checkbox
+ checked={false}
+ className="display-inline-block spacer-top"
+ id="send-notifications"
+ onCheck={[Function]}
+ right={true}
+ thirdState={false}
>
- <label
- htmlFor="send-notifications"
+ <strong
+ className="little-spacer-right"
>
issue.send_notifications
- </label>
- <Checkbox
- checked={false}
- id="send-notifications"
- onCheck={[Function]}
- thirdState={false}
- />
- </div>
+ </strong>
+ </Checkbox>
</div>
<div
className="modal-foot"
diff --git a/server/sonar-web/src/main/js/apps/issues/styles.css b/server/sonar-web/src/main/js/apps/issues/styles.css
index ff12f912ecc..9964171042b 100644
--- a/server/sonar-web/src/main/js/apps/issues/styles.css
+++ b/server/sonar-web/src/main/js/apps/issues/styles.css
@@ -270,3 +270,12 @@
width: auto;
margin-right: 4px;
}
+
+.bulk-change-radio-button {
+ margin: 0 calc(- var(--gridSize) / 2);
+ padding: 0 calc(var(--gridSize) / 2);
+}
+
+.bulk-change-radio-button:hover {
+ background-color: var(--barBackgroundColor);
+}
diff --git a/server/sonar-web/src/main/js/apps/organizationMembers/AddMemberForm.tsx b/server/sonar-web/src/main/js/apps/organizationMembers/AddMemberForm.tsx
index 8cbfe2f6f20..a60df045b64 100644
--- a/server/sonar-web/src/main/js/apps/organizationMembers/AddMemberForm.tsx
+++ b/server/sonar-web/src/main/js/apps/organizationMembers/AddMemberForm.tsx
@@ -77,7 +77,7 @@ export default class AddMemberForm extends React.PureComponent<Props, State> {
</header>
<form onSubmit={this.handleSubmit}>
<div className="modal-body">
- <div className="modal-large-field">
+ <div className="modal-field">
<label>{translate('users.search_description')}</label>
<UsersSelectSearch
autoFocus={true}
diff --git a/server/sonar-web/src/main/js/apps/organizationMembers/ManageMemberGroupsForm.tsx b/server/sonar-web/src/main/js/apps/organizationMembers/ManageMemberGroupsForm.tsx
index 26d23cbb965..1bc300691ad 100644
--- a/server/sonar-web/src/main/js/apps/organizationMembers/ManageMemberGroupsForm.tsx
+++ b/server/sonar-web/src/main/js/apps/organizationMembers/ManageMemberGroupsForm.tsx
@@ -18,19 +18,24 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
-import { keyBy, pickBy } from 'lodash';
-import { getUserGroups, UserGroup } from '../../api/users';
-import Modal from '../../components/controls/Modal';
-import { translate, translateWithParameters } from '../../helpers/l10n';
+import { keyBy, pickBy, some } from 'lodash';
import OrganizationGroupCheckbox from '../organizations/components/OrganizationGroupCheckbox';
+import SimpleModal from '../../components/controls/SimpleModal';
import { SubmitButton, ResetButtonLink } from '../../components/ui/buttons';
+import { getUserGroups, UserGroup } from '../../api/users';
+import { translate, translateWithParameters } from '../../helpers/l10n';
+import DeferredSpinner from '../../components/common/DeferredSpinner';
interface Props {
onClose: () => void;
member: T.OrganizationMember;
organization: T.Organization;
organizationGroups: T.Group[];
- updateMemberGroups: (member: T.OrganizationMember, add: string[], remove: string[]) => void;
+ updateMemberGroups: (
+ member: T.OrganizationMember,
+ add: string[],
+ remove: string[]
+ ) => Promise<void>;
}
interface State {
@@ -81,7 +86,7 @@ export default class ManageMemberGroupsForm extends React.PureComponent<Props, S
onCheck = (groupName: string, checked: boolean) => {
this.setState((prevState: State) => {
- const userGroups = prevState.userGroups || {};
+ const { userGroups = {} } = prevState;
const group = userGroups[groupName] || {};
let status = '';
if (group.selected && !checked) {
@@ -93,53 +98,62 @@ export default class ManageMemberGroupsForm extends React.PureComponent<Props, S
});
};
- handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
- event.preventDefault();
- this.props.updateMemberGroups(
- this.props.member,
- Object.keys(pickBy(this.state.userGroups, group => group.status === 'add')),
- Object.keys(pickBy(this.state.userGroups, group => group.status === 'remove'))
- );
- this.props.onClose();
+ handleSubmit = () => {
+ return this.props
+ .updateMemberGroups(
+ this.props.member,
+ Object.keys(pickBy(this.state.userGroups, group => group.status === 'add')),
+ Object.keys(pickBy(this.state.userGroups, group => group.status === 'remove'))
+ )
+ .then(this.props.onClose);
};
render() {
+ const { loading, userGroups = {} } = this.state;
const header = translate('organization.members.manage_groups');
+ const hasChanges = some(userGroups, group => group.status !== undefined);
return (
- <Modal contentLabel={header} onRequestClose={this.props.onClose}>
- <header className="modal-head">
- <h2>{header}</h2>
- </header>
- <form onSubmit={this.handleSubmit}>
- <div className="modal-body modal-container">
- <strong>
- {translateWithParameters(
- 'organization.members.members_groups',
- this.props.member.name
+ <SimpleModal header={header} onClose={this.props.onClose} onSubmit={this.handleSubmit}>
+ {({ onCloseClick, onFormSubmit, submitting }) => (
+ <form onSubmit={onFormSubmit}>
+ <header className="modal-head">
+ <h2>{header}</h2>
+ </header>
+ <div className="modal-body modal-container">
+ <p>
+ <strong>
+ {translateWithParameters(
+ 'organization.members.members_groups',
+ this.props.member.name
+ )}
+ </strong>
+ </p>
+ {loading ? (
+ <DeferredSpinner className="spacer-top" />
+ ) : (
+ <ul className="list-spaced">
+ {this.props.organizationGroups.map(group => (
+ <OrganizationGroupCheckbox
+ checked={this.isGroupSelected(group.name)}
+ group={group}
+ key={group.id}
+ onCheck={this.onCheck}
+ />
+ ))}
+ </ul>
)}
- </strong>{' '}
- {this.state.loading && <i className="spinner" />}
- {!this.state.loading && (
- <ul className="list-spaced">
- {this.props.organizationGroups.map(group => (
- <OrganizationGroupCheckbox
- checked={this.isGroupSelected(group.name)}
- group={group}
- key={group.id}
- onCheck={this.onCheck}
- />
- ))}
- </ul>
- )}
- </div>
- <footer className="modal-foot">
- <div>
- <SubmitButton>{translate('save')}</SubmitButton>
- <ResetButtonLink onClick={this.props.onClose}>{translate('cancel')}</ResetButtonLink>
</div>
- </footer>
- </form>
- </Modal>
+
+ <footer className="modal-foot">
+ <DeferredSpinner className="spacer-right" loading={submitting} />
+ <SubmitButton disabled={submitting || !hasChanges}>{translate('save')}</SubmitButton>
+ <ResetButtonLink disabled={submitting} onClick={onCloseClick}>
+ {translate('cancel')}
+ </ResetButtonLink>
+ </footer>
+ </form>
+ )}
+ </SimpleModal>
);
}
}
diff --git a/server/sonar-web/src/main/js/apps/organizationMembers/MembersList.tsx b/server/sonar-web/src/main/js/apps/organizationMembers/MembersList.tsx
index 437b0ed61b9..4cc845a58ce 100644
--- a/server/sonar-web/src/main/js/apps/organizationMembers/MembersList.tsx
+++ b/server/sonar-web/src/main/js/apps/organizationMembers/MembersList.tsx
@@ -23,20 +23,21 @@ import MembersListItem from './MembersListItem';
import { translate } from '../../helpers/l10n';
interface Props {
+ currentUser: T.LoggedInUser;
members: T.OrganizationMember[];
organizationGroups: T.Group[];
organization: T.Organization;
- removeMember: (member: T.OrganizationMember) => void;
+ removeMember?: (member: T.OrganizationMember) => void;
updateMemberGroups: (
member: T.OrganizationMember,
add: Array<string>,
remove: Array<string>
- ) => void;
+ ) => Promise<void>;
}
export default class MembersList extends React.PureComponent<Props> {
render() {
- const { members } = this.props;
+ const { currentUser, members } = this.props;
if (!members.length) {
return <div className="note">{translate('no_results')}</div>;
@@ -53,7 +54,9 @@ export default class MembersList extends React.PureComponent<Props> {
member={member}
organization={this.props.organization}
organizationGroups={this.props.organizationGroups}
- removeMember={this.props.removeMember}
+ removeMember={
+ currentUser.login !== member.login ? this.props.removeMember : undefined
+ }
updateMemberGroups={this.props.updateMemberGroups}
/>
))}
diff --git a/server/sonar-web/src/main/js/apps/organizationMembers/MembersListHeader.tsx b/server/sonar-web/src/main/js/apps/organizationMembers/MembersListHeader.tsx
index 11a052e4462..beb8fe92be5 100644
--- a/server/sonar-web/src/main/js/apps/organizationMembers/MembersListHeader.tsx
+++ b/server/sonar-web/src/main/js/apps/organizationMembers/MembersListHeader.tsx
@@ -52,7 +52,7 @@ export default function MembersListHeader({
<HelpTooltip
className="spacer-left"
overlay={
- <div className="abs-width-300 markdown cut-margins">
+ <div className="abs-width-300 markdown cut-margins">
<p>
{translate(
'organization.members.auto_sync_total_help',
diff --git a/server/sonar-web/src/main/js/apps/organizationMembers/MembersListItem.tsx b/server/sonar-web/src/main/js/apps/organizationMembers/MembersListItem.tsx
index 143b7a549b3..4859e2f2ee5 100644
--- a/server/sonar-web/src/main/js/apps/organizationMembers/MembersListItem.tsx
+++ b/server/sonar-web/src/main/js/apps/organizationMembers/MembersListItem.tsx
@@ -32,8 +32,12 @@ interface Props {
member: T.OrganizationMember;
organization: T.Organization;
organizationGroups: T.Group[];
- removeMember: (member: T.OrganizationMember) => void;
- updateMemberGroups: (member: T.OrganizationMember, add: string[], remove: string[]) => void;
+ removeMember?: (member: T.OrganizationMember) => void;
+ updateMemberGroups: (
+ member: T.OrganizationMember,
+ add: string[],
+ remove: string[]
+ ) => Promise<void>;
}
interface State {
@@ -76,7 +80,7 @@ export default class MembersListItem extends React.PureComponent<Props, State> {
};
render() {
- const { member, organization } = this.props;
+ const { member, organization, removeMember } = this.props;
const { actions = {} } = organization;
return (
<tr>
@@ -96,16 +100,20 @@ export default class MembersListItem extends React.PureComponent<Props, State> {
</td>
)}
{actions.admin && (
- <React.Fragment>
+ <>
<td className="nowrap text-middle text-right">
<ActionsDropdown>
<ActionsDropdownItem onClick={this.handleManageGroupsClick}>
{translate('organization.members.manage_groups')}
</ActionsDropdownItem>
- <ActionsDropdownDivider />
- <ActionsDropdownItem destructive={true} onClick={this.handleRemoveMemberClick}>
- {translate('organization.members.remove')}
- </ActionsDropdownItem>
+ {removeMember && (
+ <>
+ <ActionsDropdownDivider />
+ <ActionsDropdownItem destructive={true} onClick={this.handleRemoveMemberClick}>
+ {translate('organization.members.remove')}
+ </ActionsDropdownItem>
+ </>
+ )}
</ActionsDropdown>
</td>
@@ -119,15 +127,16 @@ export default class MembersListItem extends React.PureComponent<Props, State> {
/>
)}
- {this.state.removeMemberForm && (
- <RemoveMemberForm
- member={this.props.member}
- onClose={this.closeRemoveMemberForm}
- organization={this.props.organization}
- removeMember={this.props.removeMember}
- />
- )}
- </React.Fragment>
+ {removeMember &&
+ this.state.removeMemberForm && (
+ <RemoveMemberForm
+ member={this.props.member}
+ onClose={this.closeRemoveMemberForm}
+ organization={this.props.organization}
+ removeMember={removeMember}
+ />
+ )}
+ </>
)}
</tr>
);
diff --git a/server/sonar-web/src/main/js/apps/organizationMembers/MembersPageHeader.tsx b/server/sonar-web/src/main/js/apps/organizationMembers/MembersPageHeader.tsx
index 9015c4ba8b2..5cf908cce7c 100644
--- a/server/sonar-web/src/main/js/apps/organizationMembers/MembersPageHeader.tsx
+++ b/server/sonar-web/src/main/js/apps/organizationMembers/MembersPageHeader.tsx
@@ -18,121 +18,82 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
-import { connect } from 'react-redux';
import { FormattedMessage } from 'react-intl';
import { Link } from 'react-router';
import AddMemberForm from './AddMemberForm';
import SyncMemberForm from './SyncMemberForm';
import DeferredSpinner from '../../components/common/DeferredSpinner';
import DocTooltip from '../../components/docs/DocTooltip';
-import NewInfoBox from '../../components/ui/NewInfoBox';
import { sanitizeAlmId } from '../../helpers/almIntegrations';
import { translate, translateWithParameters } from '../../helpers/l10n';
-import { getCurrentUserSetting, Store } from '../../store/rootReducer';
-import { setCurrentUserSetting } from '../../store/users';
+import { Alert } from '../../components/ui/Alert';
-interface Props {
- dismissSyncNotifOrg: string[];
+export interface Props {
handleAddMember: (member: T.OrganizationMember) => void;
loading: boolean;
members?: T.OrganizationMember[];
organization: T.Organization;
refreshMembers: () => Promise<void>;
- setCurrentUserSetting: (setting: T.CurrentUserSetting) => void;
}
-export class MembersPageHeader extends React.PureComponent<Props> {
- handleDismissSyncNotif = () => {
- const { dismissSyncNotifOrg, organization } = this.props;
- this.props.setCurrentUserSetting({
- key: 'organizations.members.dismissSyncNotif',
- value: [...dismissSyncNotifOrg, organization.key].join(',')
- });
- };
+export default function MembersPageHeader(props: Props) {
+ const { members, organization, refreshMembers } = props;
+ const memberLogins = members ? members.map(member => member.login) : [];
+ const isAdmin = organization.actions && organization.actions.admin;
+ const almKey = organization.alm && sanitizeAlmId(organization.alm.key);
+ const hasMemberSync = organization.alm && organization.alm.membersSync;
+ const showSyncNotif = isAdmin && organization.alm && !hasMemberSync;
- render() {
- const { dismissSyncNotifOrg, members, organization, refreshMembers } = this.props;
- const memberLogins = members ? members.map(member => member.login) : [];
- const isAdmin = organization.actions && organization.actions.admin;
- const almKey = organization.alm && sanitizeAlmId(organization.alm.key);
- const hasMemberSync = organization.alm && organization.alm.membersSync;
- const showSyncNotif =
- isAdmin &&
- organization.alm &&
- !hasMemberSync &&
- !dismissSyncNotifOrg.some(orgKey => orgKey === organization.key);
-
- return (
- <header className="page-header">
- <h1 className="page-title">{translate('organization.members.page')}</h1>
- <DeferredSpinner loading={this.props.loading} />
- {isAdmin && (
- <div className="page-actions text-right">
- {almKey &&
- !showSyncNotif && (
- <SyncMemberForm organization={organization} refreshMembers={refreshMembers} />
- )}
- {!hasMemberSync && (
- <div className="display-inline-block spacer-left spacer-bottom">
- <AddMemberForm
- addMember={this.props.handleAddMember}
- memberLogins={memberLogins}
- organization={organization}
- />
- <DocTooltip
- className="spacer-left"
- doc={import(/* webpackMode: "eager" */ 'Docs/tooltips/organizations/add-organization-member.md')}
- />
- </div>
+ return (
+ <header className="page-header">
+ <h1 className="page-title">{translate('organization.members.page')}</h1>
+ <DeferredSpinner loading={props.loading} />
+ {isAdmin && (
+ <div className="page-actions text-right">
+ {almKey &&
+ !showSyncNotif && (
+ <SyncMemberForm organization={organization} refreshMembers={refreshMembers} />
)}
- {almKey &&
- showSyncNotif && (
- <NewInfoBox
- description={translateWithParameters(
- 'organization.members.auto_sync_members_from_org_x',
- translate(almKey)
- )}
- onClose={this.handleDismissSyncNotif}
- title={translateWithParameters(
- 'organization.members.auto_sync_with_x',
- translate(almKey)
- )}>
- <SyncMemberForm
- dismissSyncNotif={this.handleDismissSyncNotif}
- organization={organization}
- refreshMembers={refreshMembers}
- />
- </NewInfoBox>
- )}
- </div>
- )}
- <div className="page-description">
- <FormattedMessage
- defaultMessage={translate('organization.members.page.description')}
- id="organization.members.page.description"
- values={{
- link: (
- <Link to="/documentation/organizations/manage-team/">
- {translate('organization.members.manage_a_team')}
- </Link>
- )
- }}
- />
+ {!hasMemberSync && (
+ <div className="display-inline-block spacer-left spacer-bottom">
+ <AddMemberForm
+ addMember={props.handleAddMember}
+ memberLogins={memberLogins}
+ organization={organization}
+ />
+ <DocTooltip
+ className="spacer-left"
+ doc={import(/* webpackMode: "eager" */ 'Docs/tooltips/organizations/add-organization-member.md')}
+ />
+ </div>
+ )}
</div>
- </header>
- );
- }
+ )}
+ <div className="page-description">
+ <FormattedMessage
+ defaultMessage={translate('organization.members.page.description')}
+ id="organization.members.page.description"
+ values={{
+ link: (
+ <Link target="_blank" to="/documentation/organizations/manage-team/">
+ {translate('organization.members.manage_a_team')}
+ </Link>
+ )
+ }}
+ />
+ {almKey &&
+ showSyncNotif && (
+ <Alert className="spacer-top" display="inline" variant="info">
+ {translateWithParameters(
+ 'organization.members.auto_sync_members_from_org_x',
+ translate('organization', almKey)
+ )}
+ <span className="spacer-left">
+ <SyncMemberForm organization={organization} refreshMembers={refreshMembers} />
+ </span>
+ </Alert>
+ )}
+ </div>
+ </header>
+ );
}
-
-const mapStateToProps = (state: Store) => ({
- dismissSyncNotifOrg: (
- getCurrentUserSetting(state, 'organizations.members.dismissSyncNotif') || ''
- ).split(',')
-});
-
-const mapDispatchToProps = { setCurrentUserSetting };
-
-export default connect(
- mapStateToProps,
- mapDispatchToProps
-)(MembersPageHeader);
diff --git a/server/sonar-web/src/main/js/apps/organizationMembers/OrganizationMembers.tsx b/server/sonar-web/src/main/js/apps/organizationMembers/OrganizationMembers.tsx
index da2ea484d97..bd141ec20e3 100644
--- a/server/sonar-web/src/main/js/apps/organizationMembers/OrganizationMembers.tsx
+++ b/server/sonar-web/src/main/js/apps/organizationMembers/OrganizationMembers.tsx
@@ -182,22 +182,20 @@ export default class OrganizationMembers extends React.PureComponent<Props, Stat
removeUserFromGroup({ name, login, organization: this.props.organization.key })
)
];
- Promise.all(promises).then(
- () => {
- if (this.mounted) {
- this.updateGroup(login, member => ({
- ...member,
- groupCount: (member.groupCount || 0) + add.length - remove.length
- }));
- }
- },
- () => {}
- );
+ return Promise.all(promises).then(() => {
+ if (this.mounted) {
+ this.updateGroup(login, member => ({
+ ...member,
+ groupCount: (member.groupCount || 0) + add.length - remove.length
+ }));
+ }
+ });
};
render() {
- const { organization } = this.props;
+ const { currentUser, organization } = this.props;
const { groups, loading, members, paging } = this.state;
+ const hasMemberSync = organization.alm && organization.alm.membersSync;
return (
<div className="page page-limited">
<Helmet title={translate('organization.members.page')} />
@@ -213,16 +211,17 @@ export default class OrganizationMembers extends React.PureComponent<Props, Stat
paging !== undefined && (
<>
<MembersListHeader
- currentUser={this.props.currentUser}
+ currentUser={currentUser}
handleSearch={this.handleSearchMembers}
organization={organization}
total={paging.total}
/>
<MembersList
+ currentUser={currentUser}
members={members}
organization={organization}
organizationGroups={groups}
- removeMember={this.handleRemoveMember}
+ removeMember={hasMemberSync ? undefined : this.handleRemoveMember}
updateMemberGroups={this.updateMemberGroups}
/>
{paging.total !== 0 && (
diff --git a/server/sonar-web/src/main/js/apps/organizationMembers/SyncMemberForm.tsx b/server/sonar-web/src/main/js/apps/organizationMembers/SyncMemberForm.tsx
index 9ebb9f57e21..9276aa1df56 100644
--- a/server/sonar-web/src/main/js/apps/organizationMembers/SyncMemberForm.tsx
+++ b/server/sonar-web/src/main/js/apps/organizationMembers/SyncMemberForm.tsx
@@ -30,7 +30,6 @@ import { translate, translateWithParameters } from '../../helpers/l10n';
import { fetchOrganization } from '../../store/rootActions';
interface Props {
- dismissSyncNotif?: () => void;
fetchOrganization: (key: string) => void;
organization: T.Organization;
refreshMembers: () => Promise<void>;
@@ -49,13 +48,9 @@ export class SyncMemberForm extends React.PureComponent<Props, State> {
}
handleConfirm = () => {
- const { dismissSyncNotif, organization } = this.props;
+ const { organization } = this.props;
const { membersSync } = this.state;
- if (dismissSyncNotif) {
- dismissSyncNotif();
- }
-
return setOrganizationMemberSync({
organization: organization.key,
enabled: membersSync
@@ -99,63 +94,55 @@ export class SyncMemberForm extends React.PureComponent<Props, State> {
const { organization } = this.props;
const almKey = organization.alm && sanitizeAlmId(organization.alm.key);
return (
- <>
- <div className="display-flex-stretch big-spacer-top">
- <RadioCard
- onClick={this.handleManualClick}
- selected={!membersSync}
- title={translate('organization.members.management.manual')}>
- <div className="spacer-left">
- <ul className="big-spacer-left note">
- <li className="spacer-bottom">
- {translate('organization.members.management.manual.add_members_manually')}
- </li>
- <li>
- {translate('organization.members.management.manual.choose_members_permissions')}
- </li>
- </ul>
- </div>
- </RadioCard>
- <RadioCard
- onClick={this.handleAutoClick}
- selected={membersSync}
- title={translateWithParameters(
- 'organization.members.management.automatic',
- translate(almKey || '')
- )}>
- <div className="spacer-left">
- <ul className="big-spacer-left note">
- {almKey && (
- <>
- <li className="spacer-bottom">
- {translateWithParameters(
- 'organization.members.management.automatic.synchronized_from_x',
- translate(almKey)
- )}
- </li>
- <li className="spacer-bottom">
- {translate(
- 'organization.members.management.automatic.members_changes_reflected',
- almKey
- )}
- </li>
- </>
- )}
- <li>
- {translate(
- 'organization.members.management.automatic.still_choose_members_permissions'
- )}
- </li>
- </ul>
- </div>
- {(!organization.alm || !organization.alm.membersSync) && (
- <Alert className="big-spacer-top" variant="warning">
- {translate('organization.members.management.automatic.warning')}
- </Alert>
- )}
- </RadioCard>
- </div>
- </>
+ <div className="display-flex-stretch big-spacer-top">
+ <RadioCard
+ onClick={this.handleManualClick}
+ selected={!membersSync}
+ title={translate('organization.members.management.manual')}>
+ <div className="spacer-left">
+ <ul className="big-spacer-left note">
+ <li className="spacer-bottom">
+ {translate('organization.members.management.manual.add_members_manually')}
+ </li>
+ <li>{translate('organization.members.management.choose_members_permissions')}</li>
+ </ul>
+ </div>
+ </RadioCard>
+ <RadioCard
+ onClick={this.handleAutoClick}
+ selected={membersSync}
+ title={translateWithParameters(
+ 'organization.members.management.automatic',
+ translate(almKey || '')
+ )}>
+ <div className="spacer-left">
+ <ul className="big-spacer-left note">
+ {almKey && (
+ <>
+ <li className="spacer-bottom">
+ {translateWithParameters(
+ 'organization.members.management.automatic.synchronized_from_x',
+ translate('organization', almKey)
+ )}
+ </li>
+ <li className="spacer-bottom">
+ {translate(
+ 'organization.members.management.automatic.members_changes_reflected',
+ almKey
+ )}
+ </li>
+ </>
+ )}
+ <li>{translate('organization.members.management.choose_members_permissions')}</li>
+ </ul>
+ </div>
+ {(!organization.alm || !organization.alm.membersSync) && (
+ <Alert className="big-spacer-top" variant="warning">
+ {translate('organization.members.management.automatic.warning')}
+ </Alert>
+ )}
+ </RadioCard>
+ </div>
);
};
@@ -167,14 +154,12 @@ export class SyncMemberForm extends React.PureComponent<Props, State> {
cancelButtonText={translate('close')}
confirmButtonText={translate('save')}
confirmDisable={this.state.membersSync === orgMemberSync}
- medium={true}
modalBody={this.renderModalBody()}
modalHeader={translate('organization.members.management.title')}
modalHeaderDescription={this.renderModalDescription()}
- onConfirm={this.handleConfirm}>
- {({ onClick }) => (
- <Button onClick={onClick}>{translate('organization.members.config_synchro')}</Button>
- )}
+ onConfirm={this.handleConfirm}
+ size={'medium'}>
+ {({ onClick }) => <Button onClick={onClick}>{translate('configure')}</Button>}
</ConfirmButton>
);
}
diff --git a/server/sonar-web/src/main/js/apps/organizationMembers/__tests__/ManageMemberGroupsForm-test.tsx b/server/sonar-web/src/main/js/apps/organizationMembers/__tests__/ManageMemberGroupsForm-test.tsx
index a412a1b679f..30b2f906974 100644
--- a/server/sonar-web/src/main/js/apps/organizationMembers/__tests__/ManageMemberGroupsForm-test.tsx
+++ b/server/sonar-web/src/main/js/apps/organizationMembers/__tests__/ManageMemberGroupsForm-test.tsx
@@ -20,7 +20,6 @@
import * as React from 'react';
import { shallow } from 'enzyme';
import ManageMemberGroupsForm from '../ManageMemberGroupsForm';
-import { mockEvent } from '../../../helpers/testMocks';
const member = { login: 'admin', name: 'Admin Istrator', avatar: '', groupCount: 3 };
const organization = { name: 'MyOrg', key: 'myorg' };
@@ -45,11 +44,17 @@ const organizationGroups = [
}
];
const userGroups = {
- 11: { id: 11, name: 'pull-request-analysers', description: 'Technical accounts', selected: true }
+ '11': {
+ default: false,
+ id: 11,
+ name: 'pull-request-analysers',
+ description: 'Technical accounts',
+ selected: true
+ }
};
-function getMountedForm(updateFunc = jest.fn()) {
- const wrapper = shallow(
+function getMountedForm(updateFunc = jest.fn().mockResolvedValue({})) {
+ const wrapper = shallow<ManageMemberGroupsForm>(
<ManageMemberGroupsForm
member={member}
onClose={jest.fn()}
@@ -75,27 +80,26 @@ it('should render', () => {
/>
);
expect(wrapper).toMatchSnapshot();
+ expect(wrapper.dive()).toMatchSnapshot();
});
it('should correctly select the groups', () => {
const form = getMountedForm();
- const instance = form.instance as ManageMemberGroupsForm;
- expect(instance.isGroupSelected('11')).toBeTruthy();
- expect(instance.isGroupSelected('7')).toBeFalsy();
- instance.onCheck('11', false);
- instance.onCheck('7', true);
+ expect(form.instance.isGroupSelected('11')).toBeTruthy();
+ expect(form.instance.isGroupSelected('7')).toBeFalsy();
+ form.instance.onCheck('11', false);
+ form.instance.onCheck('7', true);
expect(form.wrapper.state('userGroups')).toMatchSnapshot();
- expect(instance.isGroupSelected('11')).toBeFalsy();
- expect(instance.isGroupSelected('7')).toBeTruthy();
+ expect(form.instance.isGroupSelected('11')).toBeFalsy();
+ expect(form.instance.isGroupSelected('7')).toBeTruthy();
});
it('should correctly handle the submit event and close the modal', () => {
- const updateMemberGroups = jest.fn();
+ const updateMemberGroups = jest.fn().mockResolvedValue({});
const form = getMountedForm(updateMemberGroups);
- const instance = form.instance as ManageMemberGroupsForm;
- instance.onCheck('11', false);
- instance.onCheck('7', true);
- instance.handleSubmit(mockEvent());
+ form.instance.onCheck('11', false);
+ form.instance.onCheck('7', true);
+ form.instance.handleSubmit();
expect(updateMemberGroups.mock.calls).toMatchSnapshot();
expect(form.wrapper.state()).toMatchSnapshot();
});
diff --git a/server/sonar-web/src/main/js/apps/organizationMembers/__tests__/MembersList-test.tsx b/server/sonar-web/src/main/js/apps/organizationMembers/__tests__/MembersList-test.tsx
index 2157332788c..f3746540b7a 100644
--- a/server/sonar-web/src/main/js/apps/organizationMembers/__tests__/MembersList-test.tsx
+++ b/server/sonar-web/src/main/js/apps/organizationMembers/__tests__/MembersList-test.tsx
@@ -20,35 +20,31 @@
import * as React from 'react';
import { shallow } from 'enzyme';
import MembersList from '../MembersList';
+import { mockOrganization, mockCurrentUser } from '../../../helpers/testMocks';
-const organization = { key: 'foo', name: 'Foo' };
const members = [
{ login: 'admin', name: 'Admin Istrator', avatar: '', groupCount: 3 },
{ login: 'john', name: 'John Doe', avatar: '7daf6c79d4802916d83f6266e24850af', groupCount: 1 }
];
it('should render a list of members of an organization', () => {
- const wrapper = shallow(
- <MembersList
- members={members}
- organization={organization}
- organizationGroups={[]}
- removeMember={jest.fn()}
- updateMemberGroups={jest.fn()}
- />
- );
- expect(wrapper).toMatchSnapshot();
+ expect(shallowRender()).toMatchSnapshot();
});
it('should render "no results"', () => {
- const wrapper = shallow(
+ expect(shallowRender({ members: [] })).toMatchSnapshot();
+});
+
+function shallowRender(props: Partial<MembersList['props']> = {}) {
+ return shallow(
<MembersList
- members={[]}
- organization={organization}
+ currentUser={mockCurrentUser({ login: 'admin' })}
+ members={members}
+ organization={mockOrganization()}
organizationGroups={[]}
removeMember={jest.fn()}
updateMemberGroups={jest.fn()}
+ {...props}
/>
);
- expect(wrapper).toMatchSnapshot();
-});
+}
diff --git a/server/sonar-web/src/main/js/apps/organizationMembers/__tests__/MembersListItem-test.tsx b/server/sonar-web/src/main/js/apps/organizationMembers/__tests__/MembersListItem-test.tsx
index 13a4ebfae5e..58d4daaf89c 100644
--- a/server/sonar-web/src/main/js/apps/organizationMembers/__tests__/MembersListItem-test.tsx
+++ b/server/sonar-web/src/main/js/apps/organizationMembers/__tests__/MembersListItem-test.tsx
@@ -21,60 +21,30 @@ import * as React from 'react';
import { shallow } from 'enzyme';
import MembersListItem from '../MembersListItem';
import { click } from '../../../helpers/testUtils';
-
-const organization = { key: 'foo', name: 'Foo' };
-const admin = { login: 'admin', name: 'Admin Istrator', avatar: '', groupCount: 3 };
-const john = { login: 'john', name: 'John Doe', avatar: '7daf6c79d4802916d83f6266e24850af' };
+import { mockOrganizationWithAdminActions, mockOrganization } from '../../../helpers/testMocks';
it('should not render actions and groups for non admin', () => {
- const wrapper = shallow(
- <MembersListItem
- member={admin}
- organization={organization}
- organizationGroups={[]}
- removeMember={jest.fn()}
- updateMemberGroups={jest.fn()}
- />
- );
- expect(wrapper).toMatchSnapshot();
+ expect(shallowRender({ organization: mockOrganization() })).toMatchSnapshot();
});
it('should render actions and groups for admin', () => {
- const wrapper = shallow(
- <MembersListItem
- member={admin}
- organization={{ ...organization, actions: { admin: true } }}
- organizationGroups={[]}
- removeMember={jest.fn()}
- updateMemberGroups={jest.fn()}
- />
- );
- expect(wrapper).toMatchSnapshot();
+ expect(shallowRender()).toMatchSnapshot();
});
-it('should groups at 0 if the groupCount field is not defined (just added user)', () => {
- const wrapper = shallow(
- <MembersListItem
- member={john}
- organization={{ ...organization, actions: { admin: true } }}
- organizationGroups={[]}
- removeMember={jest.fn()}
- updateMemberGroups={jest.fn()}
- />
- );
- expect(wrapper).toMatchSnapshot();
+it('should show groups at 0 if the groupCount field is not defined (just added user)', () => {
+ expect(
+ shallowRender({
+ member: { login: 'john', name: 'John Doe', avatar: '7daf6c79d4802916d83f6266e24850af' }
+ })
+ ).toMatchSnapshot();
+});
+
+it('should not display the remove member action', () => {
+ expect(shallowRender({ removeMember: undefined }).find('ActionsDropdown')).toMatchSnapshot();
});
it('should open groups form', () => {
- const wrapper = shallow(
- <MembersListItem
- member={admin}
- organization={{ ...organization, actions: { admin: true } }}
- organizationGroups={[]}
- removeMember={jest.fn()}
- updateMemberGroups={jest.fn()}
- />
- );
+ const wrapper = shallowRender();
click(wrapper.find('ActionsDropdownItem').first());
expect(wrapper.find('ManageMemberGroupsForm').exists()).toBe(true);
@@ -85,15 +55,7 @@ it('should open groups form', () => {
});
it('should open remove member form', () => {
- const wrapper = shallow(
- <MembersListItem
- member={admin}
- organization={{ ...organization, actions: { admin: true } }}
- organizationGroups={[]}
- removeMember={jest.fn()}
- updateMemberGroups={jest.fn()}
- />
- );
+ const wrapper = shallowRender();
click(wrapper.find('ActionsDropdownItem').last());
expect(wrapper.find('RemoveMemberForm').exists()).toBe(true);
@@ -102,3 +64,16 @@ it('should open remove member form', () => {
wrapper.update();
expect(wrapper.find('RemoveMemberForm').exists()).toBe(false);
});
+
+function shallowRender(props: Partial<MembersListItem['props']> = {}) {
+ return shallow(
+ <MembersListItem
+ member={{ login: 'admin', name: 'Admin Istrator', avatar: '', groupCount: 3 }}
+ organization={mockOrganizationWithAdminActions()}
+ organizationGroups={[]}
+ removeMember={jest.fn()}
+ updateMemberGroups={jest.fn()}
+ {...props}
+ />
+ );
+}
diff --git a/server/sonar-web/src/main/js/apps/organizationMembers/__tests__/MembersPageHeader-test.tsx b/server/sonar-web/src/main/js/apps/organizationMembers/__tests__/MembersPageHeader-test.tsx
index bb2a10ad805..bc81f0149f4 100644
--- a/server/sonar-web/src/main/js/apps/organizationMembers/__tests__/MembersPageHeader-test.tsx
+++ b/server/sonar-web/src/main/js/apps/organizationMembers/__tests__/MembersPageHeader-test.tsx
@@ -19,7 +19,7 @@
*/
import * as React from 'react';
import { shallow } from 'enzyme';
-import { MembersPageHeader } from '../MembersPageHeader';
+import MembersPageHeader, { Props } from '../MembersPageHeader';
import {
mockOrganization,
mockOrganizationWithAlm,
@@ -39,10 +39,6 @@ it('should render for admin', () => {
it('should render for bound organization without sync', () => {
const organization = mockOrganizationWithAlm(mockOrganizationWithAdminActions());
expect(shallowRender({ organization })).toMatchSnapshot();
-
- const wrapper = shallowRender({ organization, dismissSyncNotifOrg: [organization.key] });
- expect(wrapper.find('Connect(SyncMemberForm)').exists()).toBe(true);
- expect(wrapper.find('NewInfoBox').exists()).toBe(false);
});
it('should render for bound organization with sync', () => {
@@ -52,19 +48,17 @@ it('should render for bound organization with sync', () => {
const wrapper = shallowRender({ organization });
expect(wrapper.find('Connect(SyncMemberForm)').exists()).toBe(true);
expect(wrapper.find('AddMemberForm').exists()).toBe(false);
- expect(wrapper.find('NewInfoBox').exists()).toBe(false);
+ expect(wrapper.find('Alert').exists()).toBe(false);
});
-function shallowRender(props: Partial<MembersPageHeader['props']> = {}) {
+function shallowRender(props: Partial<Props> = {}) {
return shallow(
<MembersPageHeader
- dismissSyncNotifOrg={[]}
handleAddMember={jest.fn()}
loading={false}
members={[]}
organization={mockOrganization()}
refreshMembers={jest.fn()}
- setCurrentUserSetting={jest.fn()}
{...props}
/>
);
diff --git a/server/sonar-web/src/main/js/apps/organizationMembers/__tests__/OrganizationMembers-test.tsx b/server/sonar-web/src/main/js/apps/organizationMembers/__tests__/OrganizationMembers-test.tsx
index eac606f79c0..1b119209666 100644
--- a/server/sonar-web/src/main/js/apps/organizationMembers/__tests__/OrganizationMembers-test.tsx
+++ b/server/sonar-web/src/main/js/apps/organizationMembers/__tests__/OrganizationMembers-test.tsx
@@ -25,7 +25,8 @@ import { searchUsersGroups, addUserToGroup, removeUserFromGroup } from '../../..
import {
mockOrganization,
mockCurrentUser,
- mockOrganizationWithAdminActions
+ mockOrganizationWithAdminActions,
+ mockOrganizationWithAlm
} from '../../../helpers/testMocks';
import { waitAndUpdate } from '../../../helpers/testUtils';
@@ -112,7 +113,7 @@ it('should refresh members', async () => {
it('should add new member', async () => {
const wrapper = shallowRender();
await waitAndUpdate(wrapper);
- wrapper.find('Connect(MembersPageHeader)').prop<Function>('handleAddMember')({ login: 'bar' });
+ wrapper.find('MembersPageHeader').prop<Function>('handleAddMember')({ login: 'bar' });
await waitAndUpdate(wrapper);
expect(
wrapper
@@ -160,6 +161,14 @@ it('should update groups', async () => {
expect(removeUserFromGroup).toBeCalledWith({ login: 'john', name: 'birds', organization: 'foo' });
});
+it('should not allow to remove members when in sync mode', async () => {
+ const wrapper = shallowRender({
+ organization: mockOrganizationWithAlm(mockOrganizationWithAdminActions(), { membersSync: true })
+ });
+ await waitAndUpdate(wrapper);
+ expect(wrapper.find('MembersList').prop('removeMember')).toBeUndefined();
+});
+
function shallowRender(props: Partial<OrganizationMembers['props']> = {}) {
return shallow<OrganizationMembers>(
<OrganizationMembers
diff --git a/server/sonar-web/src/main/js/apps/organizationMembers/__tests__/SyncMemberForm-test.tsx b/server/sonar-web/src/main/js/apps/organizationMembers/__tests__/SyncMemberForm-test.tsx
index 78dd230299a..1218009c845 100644
--- a/server/sonar-web/src/main/js/apps/organizationMembers/__tests__/SyncMemberForm-test.tsx
+++ b/server/sonar-web/src/main/js/apps/organizationMembers/__tests__/SyncMemberForm-test.tsx
@@ -30,15 +30,13 @@ jest.mock('../../../api/organizations', () => ({
}));
beforeEach(() => {
- (setOrganizationMemberSync as jest.Mock).mockClear();
- (syncMembers as jest.Mock).mockClear();
+ jest.clearAllMocks();
});
it('should allow to switch to automatic mode with github', async () => {
- const dismissSyncNotif = jest.fn();
const fetchOrganization = jest.fn();
const refreshMembers = jest.fn().mockResolvedValue({});
- const wrapper = shallowRender({ dismissSyncNotif, fetchOrganization, refreshMembers });
+ const wrapper = shallowRender({ fetchOrganization, refreshMembers });
expect(wrapper).toMatchSnapshot();
wrapper.setState({ membersSync: true });
@@ -48,8 +46,7 @@ it('should allow to switch to automatic mode with github', async () => {
await waitAndUpdate(wrapper);
expect(fetchOrganization).toHaveBeenCalledWith('foo');
expect(syncMembers).toHaveBeenCalledWith('foo');
- expect(refreshMembers).toBeCalledTimes(1);
- expect(dismissSyncNotif).toBeCalledTimes(1);
+ expect(refreshMembers).toBeCalled();
});
it('should allow to switch to automatic mode with bitbucket', async () => {
diff --git a/server/sonar-web/src/main/js/apps/organizationMembers/__tests__/__snapshots__/AddMemberForm-test.tsx.snap b/server/sonar-web/src/main/js/apps/organizationMembers/__tests__/__snapshots__/AddMemberForm-test.tsx.snap
index 895c48986d7..5eafb110cf2 100644
--- a/server/sonar-web/src/main/js/apps/organizationMembers/__tests__/__snapshots__/AddMemberForm-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/organizationMembers/__tests__/__snapshots__/AddMemberForm-test.tsx.snap
@@ -38,7 +38,7 @@ exports[`should render and open the modal 2`] = `
className="modal-body"
>
<div
- className="modal-large-field"
+ className="modal-field"
>
<label>
users.search_description
diff --git a/server/sonar-web/src/main/js/apps/organizationMembers/__tests__/__snapshots__/ManageMemberGroupsForm-test.tsx.snap b/server/sonar-web/src/main/js/apps/organizationMembers/__tests__/__snapshots__/ManageMemberGroupsForm-test.tsx.snap
index 660a194e06c..8a87fc3aa2b 100644
--- a/server/sonar-web/src/main/js/apps/organizationMembers/__tests__/__snapshots__/ManageMemberGroupsForm-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/organizationMembers/__tests__/__snapshots__/ManageMemberGroupsForm-test.tsx.snap
@@ -24,6 +24,7 @@ Object {
"loading": false,
"userGroups": Object {
"11": Object {
+ "default": false,
"description": "Technical accounts",
"id": 11,
"name": "pull-request-analysers",
@@ -40,6 +41,7 @@ Object {
exports[`should correctly select the groups 1`] = `
Object {
"11": Object {
+ "default": false,
"description": "Technical accounts",
"id": 11,
"name": "pull-request-analysers",
@@ -53,44 +55,62 @@ Object {
`;
exports[`should render 1`] = `
+<SimpleModal
+ header="organization.members.manage_groups"
+ onClose={[MockFunction]}
+ onSubmit={[Function]}
+>
+ <Component />
+</SimpleModal>
+`;
+
+exports[`should render 2`] = `
<Modal
contentLabel="organization.members.manage_groups"
onRequestClose={[MockFunction]}
>
- <header
- className="modal-head"
- >
- <h2>
- organization.members.manage_groups
- </h2>
- </header>
<form
onSubmit={[Function]}
>
+ <header
+ className="modal-head"
+ >
+ <h2>
+ organization.members.manage_groups
+ </h2>
+ </header>
<div
className="modal-body modal-container"
>
- <strong>
- organization.members.members_groups.Admin Istrator
- </strong>
-
- <i
- className="spinner"
+ <p>
+ <strong>
+ organization.members.members_groups.Admin Istrator
+ </strong>
+ </p>
+ <DeferredSpinner
+ className="spacer-top"
+ timeout={100}
/>
</div>
<footer
className="modal-foot"
>
- <div>
- <SubmitButton>
- save
- </SubmitButton>
- <ResetButtonLink
- onClick={[MockFunction]}
- >
- cancel
- </ResetButtonLink>
- </div>
+ <DeferredSpinner
+ className="spacer-right"
+ loading={false}
+ timeout={100}
+ />
+ <SubmitButton
+ disabled={true}
+ >
+ save
+ </SubmitButton>
+ <ResetButtonLink
+ disabled={false}
+ onClick={[Function]}
+ >
+ cancel
+ </ResetButtonLink>
</footer>
</form>
</Modal>
diff --git a/server/sonar-web/src/main/js/apps/organizationMembers/__tests__/__snapshots__/MembersList-test.tsx.snap b/server/sonar-web/src/main/js/apps/organizationMembers/__tests__/__snapshots__/MembersList-test.tsx.snap
index 9635b1e2685..a99cabd208d 100644
--- a/server/sonar-web/src/main/js/apps/organizationMembers/__tests__/__snapshots__/MembersList-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/organizationMembers/__tests__/__snapshots__/MembersList-test.tsx.snap
@@ -33,7 +33,6 @@ exports[`should render a list of members of an organization 1`] = `
}
}
organizationGroups={Array []}
- removeMember={[MockFunction]}
updateMemberGroups={[MockFunction]}
/>
<MembersListItem
diff --git a/server/sonar-web/src/main/js/apps/organizationMembers/__tests__/__snapshots__/MembersListHeader-test.tsx.snap b/server/sonar-web/src/main/js/apps/organizationMembers/__tests__/__snapshots__/MembersListHeader-test.tsx.snap
index 1d8ecfb92df..e9852b6c3c3 100644
--- a/server/sonar-web/src/main/js/apps/organizationMembers/__tests__/__snapshots__/MembersListHeader-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/organizationMembers/__tests__/__snapshots__/MembersListHeader-test.tsx.snap
@@ -5,7 +5,7 @@ exports[`should not render link in help tooltip 1`] = `
className="spacer-left"
overlay={
<div
- className="abs-width-300 markdown cut-margins"
+ className="abs-width-300 markdown cut-margins"
>
<p>
organization.members.auto_sync_total_help.github
@@ -20,7 +20,7 @@ exports[`should render a help tooltip 1`] = `
className="spacer-left"
overlay={
<div
- className="abs-width-300 markdown cut-margins"
+ className="abs-width-300 markdown cut-margins"
>
<p>
organization.members.auto_sync_total_help.github
@@ -47,7 +47,7 @@ exports[`should render a help tooltip 2`] = `
className="spacer-left"
overlay={
<div
- className="abs-width-300 markdown cut-margins"
+ className="abs-width-300 markdown cut-margins"
>
<p>
organization.members.auto_sync_total_help.bitbucket
diff --git a/server/sonar-web/src/main/js/apps/organizationMembers/__tests__/__snapshots__/MembersListItem-test.tsx.snap b/server/sonar-web/src/main/js/apps/organizationMembers/__tests__/__snapshots__/MembersListItem-test.tsx.snap
index b97c6a82959..414c9f4f30e 100644
--- a/server/sonar-web/src/main/js/apps/organizationMembers/__tests__/__snapshots__/MembersListItem-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/organizationMembers/__tests__/__snapshots__/MembersListItem-test.tsx.snap
@@ -1,13 +1,23 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
-exports[`should groups at 0 if the groupCount field is not defined (just added user) 1`] = `
+exports[`should not display the remove member action 1`] = `
+<ActionsDropdown>
+ <ActionsDropdownItem
+ onClick={[Function]}
+ >
+ organization.members.manage_groups
+ </ActionsDropdownItem>
+</ActionsDropdown>
+`;
+
+exports[`should not render actions and groups for non admin 1`] = `
<tr>
<td
className="thin nowrap"
>
<Connect(Avatar)
- hash="7daf6c79d4802916d83f6266e24850af"
- name="John Doe"
+ hash=""
+ name="Admin Istrator"
size={36}
/>
</td>
@@ -15,41 +25,18 @@ exports[`should groups at 0 if the groupCount field is not defined (just added u
className="nowrap text-middle"
>
<strong>
- John Doe
+ Admin Istrator
</strong>
<span
className="note little-spacer-left"
>
- john
+ admin
</span>
</td>
- <td
- className="text-right text-middle"
- >
- organization.members.x_groups.0
- </td>
- <td
- className="nowrap text-middle text-right"
- >
- <ActionsDropdown>
- <ActionsDropdownItem
- onClick={[Function]}
- >
- organization.members.manage_groups
- </ActionsDropdownItem>
- <ActionsDropdownDivider />
- <ActionsDropdownItem
- destructive={true}
- onClick={[Function]}
- >
- organization.members.remove
- </ActionsDropdownItem>
- </ActionsDropdown>
- </td>
</tr>
`;
-exports[`should not render actions and groups for non admin 1`] = `
+exports[`should render actions and groups for admin 1`] = `
<tr>
<td
className="thin nowrap"
@@ -72,17 +59,40 @@ exports[`should not render actions and groups for non admin 1`] = `
admin
</span>
</td>
+ <td
+ className="text-right text-middle"
+ >
+ organization.members.x_groups.3
+ </td>
+ <td
+ className="nowrap text-middle text-right"
+ >
+ <ActionsDropdown>
+ <ActionsDropdownItem
+ onClick={[Function]}
+ >
+ organization.members.manage_groups
+ </ActionsDropdownItem>
+ <ActionsDropdownDivider />
+ <ActionsDropdownItem
+ destructive={true}
+ onClick={[Function]}
+ >
+ organization.members.remove
+ </ActionsDropdownItem>
+ </ActionsDropdown>
+ </td>
</tr>
`;
-exports[`should render actions and groups for admin 1`] = `
+exports[`should show groups at 0 if the groupCount field is not defined (just added user) 1`] = `
<tr>
<td
className="thin nowrap"
>
<Connect(Avatar)
- hash=""
- name="Admin Istrator"
+ hash="7daf6c79d4802916d83f6266e24850af"
+ name="John Doe"
size={36}
/>
</td>
@@ -90,18 +100,18 @@ exports[`should render actions and groups for admin 1`] = `
className="nowrap text-middle"
>
<strong>
- Admin Istrator
+ John Doe
</strong>
<span
className="note little-spacer-left"
>
- admin
+ john
</span>
</td>
<td
className="text-right text-middle"
>
- organization.members.x_groups.3
+ organization.members.x_groups.0
</td>
<td
className="nowrap text-middle text-right"
diff --git a/server/sonar-web/src/main/js/apps/organizationMembers/__tests__/__snapshots__/MembersPageHeader-test.tsx.snap b/server/sonar-web/src/main/js/apps/organizationMembers/__tests__/__snapshots__/MembersPageHeader-test.tsx.snap
index 0225976a5a6..c21bb177f1c 100644
--- a/server/sonar-web/src/main/js/apps/organizationMembers/__tests__/__snapshots__/MembersPageHeader-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/organizationMembers/__tests__/__snapshots__/MembersPageHeader-test.tsx.snap
@@ -24,6 +24,7 @@ exports[`should render correctly 1`] = `
"link": <Link
onlyActiveOnIndex={false}
style={Object {}}
+ target="_blank"
to="/documentation/organizations/manage-team/"
>
organization.members.manage_a_team
@@ -84,6 +85,7 @@ exports[`should render for admin 1`] = `
"link": <Link
onlyActiveOnIndex={false}
style={Object {}}
+ target="_blank"
to="/documentation/organizations/manage-team/"
>
organization.members.manage_a_team
@@ -137,30 +139,6 @@ exports[`should render for bound organization without sync 1`] = `
doc={Promise {}}
/>
</div>
- <NewInfoBox
- description="organization.members.auto_sync_members_from_org_x.github"
- onClose={[Function]}
- title="organization.members.auto_sync_with_x.github"
- >
- <Connect(SyncMemberForm)
- dismissSyncNotif={[Function]}
- organization={
- Object {
- "actions": Object {
- "admin": true,
- },
- "alm": Object {
- "key": "github",
- "membersSync": false,
- "url": "https://github.com/foo",
- },
- "key": "foo",
- "name": "Foo",
- }
- }
- refreshMembers={[MockFunction]}
- />
- </NewInfoBox>
</div>
<div
className="page-description"
@@ -173,6 +151,7 @@ exports[`should render for bound organization without sync 1`] = `
"link": <Link
onlyActiveOnIndex={false}
style={Object {}}
+ target="_blank"
to="/documentation/organizations/manage-team/"
>
organization.members.manage_a_team
@@ -180,6 +159,34 @@ exports[`should render for bound organization without sync 1`] = `
}
}
/>
+ <Alert
+ className="spacer-top"
+ display="inline"
+ variant="info"
+ >
+ organization.members.auto_sync_members_from_org_x.organization.github
+ <span
+ className="spacer-left"
+ >
+ <Connect(SyncMemberForm)
+ organization={
+ Object {
+ "actions": Object {
+ "admin": true,
+ },
+ "alm": Object {
+ "key": "github",
+ "membersSync": false,
+ "url": "https://github.com/foo",
+ },
+ "key": "foo",
+ "name": "Foo",
+ }
+ }
+ refreshMembers={[MockFunction]}
+ />
+ </span>
+ </Alert>
</div>
</header>
`;
diff --git a/server/sonar-web/src/main/js/apps/organizationMembers/__tests__/__snapshots__/OrganizationMembers-test.tsx.snap b/server/sonar-web/src/main/js/apps/organizationMembers/__tests__/__snapshots__/OrganizationMembers-test.tsx.snap
index 2d451e1086c..9f3f3c11a3c 100644
--- a/server/sonar-web/src/main/js/apps/organizationMembers/__tests__/__snapshots__/OrganizationMembers-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/organizationMembers/__tests__/__snapshots__/OrganizationMembers-test.tsx.snap
@@ -12,7 +12,7 @@ exports[`should fetch members and render for non-admin 1`] = `
<Suggestions
suggestions="organization_members"
/>
- <Connect(MembersPageHeader)
+ <MembersPageHeader
handleAddMember={[Function]}
loading={true}
organization={
@@ -38,7 +38,7 @@ exports[`should fetch members and render for non-admin 2`] = `
<Suggestions
suggestions="organization_members"
/>
- <Connect(MembersPageHeader)
+ <MembersPageHeader
handleAddMember={[Function]}
loading={false}
members={
@@ -85,6 +85,15 @@ exports[`should fetch members and render for non-admin 2`] = `
total={3}
/>
<MembersList
+ currentUser={
+ Object {
+ "groups": Array [],
+ "isLoggedIn": true,
+ "login": "luke",
+ "name": "Skywalker",
+ "scmAccounts": Array [],
+ }
+ }
members={
Array [
Object {
diff --git a/server/sonar-web/src/main/js/apps/organizationMembers/__tests__/__snapshots__/SyncMemberForm-test.tsx.snap b/server/sonar-web/src/main/js/apps/organizationMembers/__tests__/__snapshots__/SyncMemberForm-test.tsx.snap
index c7c59107ab3..1be6de36a4c 100644
--- a/server/sonar-web/src/main/js/apps/organizationMembers/__tests__/__snapshots__/SyncMemberForm-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/organizationMembers/__tests__/__snapshots__/SyncMemberForm-test.tsx.snap
@@ -5,71 +5,68 @@ exports[`should allow to switch to automatic mode with bitbucket 1`] = `
cancelButtonText="close"
confirmButtonText="save"
confirmDisable={true}
- medium={true}
modalBody={
- <React.Fragment>
- <div
- className="display-flex-stretch big-spacer-top"
+ <div
+ className="display-flex-stretch big-spacer-top"
+ >
+ <RadioCard
+ onClick={[Function]}
+ selected={true}
+ title="organization.members.management.manual"
>
- <RadioCard
- onClick={[Function]}
- selected={true}
- title="organization.members.management.manual"
+ <div
+ className="spacer-left"
>
- <div
- className="spacer-left"
+ <ul
+ className="big-spacer-left note"
>
- <ul
- className="big-spacer-left note"
+ <li
+ className="spacer-bottom"
>
+ organization.members.management.manual.add_members_manually
+ </li>
+ <li>
+ organization.members.management.choose_members_permissions
+ </li>
+ </ul>
+ </div>
+ </RadioCard>
+ <RadioCard
+ onClick={[Function]}
+ selected={false}
+ title="organization.members.management.automatic.bitbucket"
+ >
+ <div
+ className="spacer-left"
+ >
+ <ul
+ className="big-spacer-left note"
+ >
+ <React.Fragment>
<li
className="spacer-bottom"
>
- organization.members.management.manual.add_members_manually
+ organization.members.management.automatic.synchronized_from_x.organization.bitbucket
</li>
- <li>
- organization.members.management.manual.choose_members_permissions
+ <li
+ className="spacer-bottom"
+ >
+ organization.members.management.automatic.members_changes_reflected.bitbucket
</li>
- </ul>
- </div>
- </RadioCard>
- <RadioCard
- onClick={[Function]}
- selected={false}
- title="organization.members.management.automatic.bitbucket"
+ </React.Fragment>
+ <li>
+ organization.members.management.choose_members_permissions
+ </li>
+ </ul>
+ </div>
+ <Alert
+ className="big-spacer-top"
+ variant="warning"
>
- <div
- className="spacer-left"
- >
- <ul
- className="big-spacer-left note"
- >
- <React.Fragment>
- <li
- className="spacer-bottom"
- >
- organization.members.management.automatic.synchronized_from_x.bitbucket
- </li>
- <li
- className="spacer-bottom"
- >
- organization.members.management.automatic.members_changes_reflected.bitbucket
- </li>
- </React.Fragment>
- <li>
- organization.members.management.automatic.still_choose_members_permissions
- </li>
- </ul>
- </div>
- <Alert
- className="big-spacer-top"
- variant="warning"
- >
- organization.members.management.automatic.warning
- </Alert>
- </RadioCard>
- </div>
- </React.Fragment>
+ organization.members.management.automatic.warning
+ </Alert>
+ </RadioCard>
+ </div>
}
modalHeader="organization.members.management.title"
modalHeaderDescription={
@@ -93,6 +90,7 @@ exports[`should allow to switch to automatic mode with bitbucket 1`] = `
</p>
}
onConfirm={[Function]}
+ size="medium"
>
<Component />
</ConfirmButton>
@@ -103,71 +101,68 @@ exports[`should allow to switch to automatic mode with github 1`] = `
cancelButtonText="close"
confirmButtonText="save"
confirmDisable={true}
- medium={true}
modalBody={
- <React.Fragment>
- <div
- className="display-flex-stretch big-spacer-top"
+ <div
+ className="display-flex-stretch big-spacer-top"
+ >
+ <RadioCard
+ onClick={[Function]}
+ selected={true}
+ title="organization.members.management.manual"
>
- <RadioCard
- onClick={[Function]}
- selected={true}
- title="organization.members.management.manual"
+ <div
+ className="spacer-left"
>
- <div
- className="spacer-left"
+ <ul
+ className="big-spacer-left note"
>
- <ul
- className="big-spacer-left note"
+ <li
+ className="spacer-bottom"
>
+ organization.members.management.manual.add_members_manually
+ </li>
+ <li>
+ organization.members.management.choose_members_permissions
+ </li>
+ </ul>
+ </div>
+ </RadioCard>
+ <RadioCard
+ onClick={[Function]}
+ selected={false}
+ title="organization.members.management.automatic.github"
+ >
+ <div
+ className="spacer-left"
+ >
+ <ul
+ className="big-spacer-left note"
+ >
+ <React.Fragment>
<li
className="spacer-bottom"
>
- organization.members.management.manual.add_members_manually
+ organization.members.management.automatic.synchronized_from_x.organization.github
</li>
- <li>
- organization.members.management.manual.choose_members_permissions
+ <li
+ className="spacer-bottom"
+ >
+ organization.members.management.automatic.members_changes_reflected.github
</li>
- </ul>
- </div>
- </RadioCard>
- <RadioCard
- onClick={[Function]}
- selected={false}
- title="organization.members.management.automatic.github"
+ </React.Fragment>
+ <li>
+ organization.members.management.choose_members_permissions
+ </li>
+ </ul>
+ </div>
+ <Alert
+ className="big-spacer-top"
+ variant="warning"
>
- <div
- className="spacer-left"
- >
- <ul
- className="big-spacer-left note"
- >
- <React.Fragment>
- <li
- className="spacer-bottom"
- >
- organization.members.management.automatic.synchronized_from_x.github
- </li>
- <li
- className="spacer-bottom"
- >
- organization.members.management.automatic.members_changes_reflected.github
- </li>
- </React.Fragment>
- <li>
- organization.members.management.automatic.still_choose_members_permissions
- </li>
- </ul>
- </div>
- <Alert
- className="big-spacer-top"
- variant="warning"
- >
- organization.members.management.automatic.warning
- </Alert>
- </RadioCard>
- </div>
- </React.Fragment>
+ organization.members.management.automatic.warning
+ </Alert>
+ </RadioCard>
+ </div>
}
modalHeader="organization.members.management.title"
modalHeaderDescription={
@@ -191,6 +186,7 @@ exports[`should allow to switch to automatic mode with github 1`] = `
</p>
}
onConfirm={[Function]}
+ size="medium"
>
<Component />
</ConfirmButton>
@@ -201,65 +197,62 @@ exports[`should allow to switch to manual mode 1`] = `
cancelButtonText="close"
confirmButtonText="save"
confirmDisable={true}
- medium={true}
modalBody={
- <React.Fragment>
- <div
- className="display-flex-stretch big-spacer-top"
+ <div
+ className="display-flex-stretch big-spacer-top"
+ >
+ <RadioCard
+ onClick={[Function]}
+ selected={false}
+ title="organization.members.management.manual"
>
- <RadioCard
- onClick={[Function]}
- selected={false}
- title="organization.members.management.manual"
+ <div
+ className="spacer-left"
>
- <div
- className="spacer-left"
+ <ul
+ className="big-spacer-left note"
>
- <ul
- className="big-spacer-left note"
+ <li
+ className="spacer-bottom"
>
+ organization.members.management.manual.add_members_manually
+ </li>
+ <li>
+ organization.members.management.choose_members_permissions
+ </li>
+ </ul>
+ </div>
+ </RadioCard>
+ <RadioCard
+ onClick={[Function]}
+ selected={true}
+ title="organization.members.management.automatic.github"
+ >
+ <div
+ className="spacer-left"
+ >
+ <ul
+ className="big-spacer-left note"
+ >
+ <React.Fragment>
<li
className="spacer-bottom"
>
- organization.members.management.manual.add_members_manually
- </li>
- <li>
- organization.members.management.manual.choose_members_permissions
+ organization.members.management.automatic.synchronized_from_x.organization.github
</li>
- </ul>
- </div>
- </RadioCard>
- <RadioCard
- onClick={[Function]}
- selected={true}
- title="organization.members.management.automatic.github"
- >
- <div
- className="spacer-left"
- >
- <ul
- className="big-spacer-left note"
- >
- <React.Fragment>
- <li
- className="spacer-bottom"
- >
- organization.members.management.automatic.synchronized_from_x.github
- </li>
- <li
- className="spacer-bottom"
- >
- organization.members.management.automatic.members_changes_reflected.github
- </li>
- </React.Fragment>
- <li>
- organization.members.management.automatic.still_choose_members_permissions
+ <li
+ className="spacer-bottom"
+ >
+ organization.members.management.automatic.members_changes_reflected.github
</li>
- </ul>
- </div>
- </RadioCard>
- </div>
- </React.Fragment>
+ </React.Fragment>
+ <li>
+ organization.members.management.choose_members_permissions
+ </li>
+ </ul>
+ </div>
+ </RadioCard>
+ </div>
}
modalHeader="organization.members.management.title"
modalHeaderDescription={
@@ -283,6 +276,7 @@ exports[`should allow to switch to manual mode 1`] = `
</p>
}
onConfirm={[Function]}
+ size="medium"
>
<Component />
</ConfirmButton>
diff --git a/server/sonar-web/src/main/js/apps/organizations/components/OrganizationEdit.tsx b/server/sonar-web/src/main/js/apps/organizations/components/OrganizationEdit.tsx
index 452950f680d..4d151adf2ab 100644
--- a/server/sonar-web/src/main/js/apps/organizations/components/OrganizationEdit.tsx
+++ b/server/sonar-web/src/main/js/apps/organizations/components/OrganizationEdit.tsx
@@ -111,12 +111,13 @@ export class OrganizationEdit extends React.PureComponent<Props, State> {
<div className="boxed-group boxed-group-inner">
<form onSubmit={this.handleSubmit}>
- <div className="modal-field">
+ <div className="form-field">
<label htmlFor="organization-name">
{translate('organization.name')}
<em className="mandatory">*</em>
</label>
<input
+ className="input-super-large"
disabled={this.state.loading}
id="organization-name"
maxLength={255}
@@ -126,13 +127,14 @@ export class OrganizationEdit extends React.PureComponent<Props, State> {
type="text"
value={this.state.name}
/>
- <div className="modal-field-description">
+ <div className="form-field-description">
{translate('organization.name.description')}
</div>
</div>
- <div className="modal-field">
+ <div className="form-field">
<label htmlFor="organization-avatar">{translate('organization.avatar')}</label>
<input
+ className="input-super-large"
disabled={this.state.loading}
id="organization-avatar"
maxLength={256}
@@ -142,11 +144,11 @@ export class OrganizationEdit extends React.PureComponent<Props, State> {
type="text"
value={this.state.avatar}
/>
- <div className="modal-field-description">
+ <div className="form-field-description">
{translate('organization.avatar.description')}
</div>
{(this.state.avatarImage || this.state.name) && (
- <div className="spacer-top spacer-bottom">
+ <div className="spacer-top">
<div className="little-spacer-bottom">
{translate('organization.avatar.preview')}
{':'}
@@ -160,9 +162,10 @@ export class OrganizationEdit extends React.PureComponent<Props, State> {
</div>
)}
</div>
- <div className="modal-field">
+ <div className="form-field">
<label htmlFor="organization-description">{translate('description')}</label>
<textarea
+ className="input-super-large"
disabled={this.state.loading}
id="organization-description"
maxLength={256}
@@ -171,13 +174,14 @@ export class OrganizationEdit extends React.PureComponent<Props, State> {
rows={3}
value={this.state.description}
/>
- <div className="modal-field-description">
+ <div className="form-field-description">
{translate('organization.description.description')}
</div>
</div>
- <div className="modal-field">
+ <div className="form-field">
<label htmlFor="organization-url">{translate('organization.url')}</label>
<input
+ className="input-super-large"
disabled={this.state.loading}
id="organization-url"
maxLength={256}
@@ -186,14 +190,12 @@ export class OrganizationEdit extends React.PureComponent<Props, State> {
type="text"
value={this.state.url}
/>
- <div className="modal-field-description">
+ <div className="form-field-description">
{translate('organization.url.description')}
</div>
</div>
- <div className="modal-field">
- <SubmitButton disabled={this.state.loading}>{translate('save')}</SubmitButton>
- {this.state.loading && <i className="spinner spacer-left" />}
- </div>
+ <SubmitButton disabled={this.state.loading}>{translate('save')}</SubmitButton>
+ {this.state.loading && <i className="spinner spacer-left" />}
</form>
</div>
</div>
diff --git a/server/sonar-web/src/main/js/apps/organizations/components/OrganizationEmpty.css b/server/sonar-web/src/main/js/apps/organizations/components/OrganizationEmpty.css
index 80a428e111c..19e438405fe 100644
--- a/server/sonar-web/src/main/js/apps/organizations/components/OrganizationEmpty.css
+++ b/server/sonar-web/src/main/js/apps/organizations/components/OrganizationEmpty.css
@@ -19,5 +19,5 @@
*/
.organization-empty {
margin: 100px auto 0;
- width: 800px;
+ width: 472px;
}
diff --git a/server/sonar-web/src/main/js/apps/organizations/components/OrganizationEmpty.tsx b/server/sonar-web/src/main/js/apps/organizations/components/OrganizationEmpty.tsx
index 4310bb215a9..078ba67c231 100644
--- a/server/sonar-web/src/main/js/apps/organizations/components/OrganizationEmpty.tsx
+++ b/server/sonar-web/src/main/js/apps/organizations/components/OrganizationEmpty.tsx
@@ -50,19 +50,15 @@ export class OrganizationEmpty extends React.PureComponent<Props> {
return (
<div className="organization-empty">
<h3 className="text-center">{translate('onboarding.create_organization.ready')}</h3>
- <div className="onboarding-choices">
- <Button className="onboarding-choice" onClick={this.handleNewProjectClick}>
+ <div className="display-flex-space-around huge-spacer-top">
+ <Button className="button-huge" onClick={this.handleNewProjectClick}>
<OnboardingProjectIcon className="big-spacer-bottom" />
- <h6 className="onboarding-choice-name">
- {translate('provisioning.analyze_new_project')}
- </h6>
+ <p className="medium spacer-top">{translate('provisioning.analyze_new_project')}</p>
</Button>
{!memberSyncActivated && (
- <Button className="onboarding-choice" onClick={this.handleAddMembersClick}>
+ <Button className="button-huge" onClick={this.handleAddMembersClick}>
<OnboardingAddMembersIcon />
- <h6 className="onboarding-choice-name">
- {translate('organization.members.add.multiple')}
- </h6>
+ <p className="medium spacer-top">{translate('organization.members.add.multiple')}</p>
</Button>
)}
</div>
diff --git a/server/sonar-web/src/main/js/apps/organizations/components/__tests__/__snapshots__/OrganizationEdit-test.tsx.snap b/server/sonar-web/src/main/js/apps/organizations/components/__tests__/__snapshots__/OrganizationEdit-test.tsx.snap
index 22b55f2ca3e..e948f1810c5 100644
--- a/server/sonar-web/src/main/js/apps/organizations/components/__tests__/__snapshots__/OrganizationEdit-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/organizations/components/__tests__/__snapshots__/OrganizationEdit-test.tsx.snap
@@ -25,7 +25,7 @@ exports[`smoke test 1`] = `
onSubmit={[Function]}
>
<div
- className="modal-field"
+ className="form-field"
>
<label
htmlFor="organization-name"
@@ -38,6 +38,7 @@ exports[`smoke test 1`] = `
</em>
</label>
<input
+ className="input-super-large"
disabled={false}
id="organization-name"
maxLength={255}
@@ -48,13 +49,13 @@ exports[`smoke test 1`] = `
value="Foo"
/>
<div
- className="modal-field-description"
+ className="form-field-description"
>
organization.name.description
</div>
</div>
<div
- className="modal-field"
+ className="form-field"
>
<label
htmlFor="organization-avatar"
@@ -62,6 +63,7 @@ exports[`smoke test 1`] = `
organization.avatar
</label>
<input
+ className="input-super-large"
disabled={false}
id="organization-avatar"
maxLength={256}
@@ -72,12 +74,12 @@ exports[`smoke test 1`] = `
value=""
/>
<div
- className="modal-field-description"
+ className="form-field-description"
>
organization.avatar.description
</div>
<div
- className="spacer-top spacer-bottom"
+ className="spacer-top"
>
<div
className="little-spacer-bottom"
@@ -96,7 +98,7 @@ exports[`smoke test 1`] = `
</div>
</div>
<div
- className="modal-field"
+ className="form-field"
>
<label
htmlFor="organization-description"
@@ -104,6 +106,7 @@ exports[`smoke test 1`] = `
description
</label>
<textarea
+ className="input-super-large"
disabled={false}
id="organization-description"
maxLength={256}
@@ -113,13 +116,13 @@ exports[`smoke test 1`] = `
value=""
/>
<div
- className="modal-field-description"
+ className="form-field-description"
>
organization.description.description
</div>
</div>
<div
- className="modal-field"
+ className="form-field"
>
<label
htmlFor="organization-url"
@@ -127,6 +130,7 @@ exports[`smoke test 1`] = `
organization.url
</label>
<input
+ className="input-super-large"
disabled={false}
id="organization-url"
maxLength={256}
@@ -136,20 +140,16 @@ exports[`smoke test 1`] = `
value=""
/>
<div
- className="modal-field-description"
+ className="form-field-description"
>
organization.url.description
</div>
</div>
- <div
- className="modal-field"
+ <SubmitButton
+ disabled={false}
>
- <SubmitButton
- disabled={false}
- >
- save
- </SubmitButton>
- </div>
+ save
+ </SubmitButton>
</form>
</div>
</div>
@@ -180,7 +180,7 @@ exports[`smoke test 2`] = `
onSubmit={[Function]}
>
<div
- className="modal-field"
+ className="form-field"
>
<label
htmlFor="organization-name"
@@ -193,6 +193,7 @@ exports[`smoke test 2`] = `
</em>
</label>
<input
+ className="input-super-large"
disabled={false}
id="organization-name"
maxLength={255}
@@ -203,13 +204,13 @@ exports[`smoke test 2`] = `
value="New Foo"
/>
<div
- className="modal-field-description"
+ className="form-field-description"
>
organization.name.description
</div>
</div>
<div
- className="modal-field"
+ className="form-field"
>
<label
htmlFor="organization-avatar"
@@ -217,6 +218,7 @@ exports[`smoke test 2`] = `
organization.avatar
</label>
<input
+ className="input-super-large"
disabled={false}
id="organization-avatar"
maxLength={256}
@@ -227,12 +229,12 @@ exports[`smoke test 2`] = `
value="foo-avatar"
/>
<div
- className="modal-field-description"
+ className="form-field-description"
>
organization.avatar.description
</div>
<div
- className="spacer-top spacer-bottom"
+ className="spacer-top"
>
<div
className="little-spacer-bottom"
@@ -251,7 +253,7 @@ exports[`smoke test 2`] = `
</div>
</div>
<div
- className="modal-field"
+ className="form-field"
>
<label
htmlFor="organization-description"
@@ -259,6 +261,7 @@ exports[`smoke test 2`] = `
description
</label>
<textarea
+ className="input-super-large"
disabled={false}
id="organization-description"
maxLength={256}
@@ -268,13 +271,13 @@ exports[`smoke test 2`] = `
value="foo-description"
/>
<div
- className="modal-field-description"
+ className="form-field-description"
>
organization.description.description
</div>
</div>
<div
- className="modal-field"
+ className="form-field"
>
<label
htmlFor="organization-url"
@@ -282,6 +285,7 @@ exports[`smoke test 2`] = `
organization.url
</label>
<input
+ className="input-super-large"
disabled={false}
id="organization-url"
maxLength={256}
@@ -291,20 +295,16 @@ exports[`smoke test 2`] = `
value="foo-url"
/>
<div
- className="modal-field-description"
+ className="form-field-description"
>
organization.url.description
</div>
</div>
- <div
- className="modal-field"
+ <SubmitButton
+ disabled={false}
>
- <SubmitButton
- disabled={false}
- >
- save
- </SubmitButton>
- </div>
+ save
+ </SubmitButton>
</form>
</div>
</div>
@@ -335,7 +335,7 @@ exports[`smoke test 3`] = `
onSubmit={[Function]}
>
<div
- className="modal-field"
+ className="form-field"
>
<label
htmlFor="organization-name"
@@ -348,6 +348,7 @@ exports[`smoke test 3`] = `
</em>
</label>
<input
+ className="input-super-large"
disabled={true}
id="organization-name"
maxLength={255}
@@ -358,13 +359,13 @@ exports[`smoke test 3`] = `
value="New Foo"
/>
<div
- className="modal-field-description"
+ className="form-field-description"
>
organization.name.description
</div>
</div>
<div
- className="modal-field"
+ className="form-field"
>
<label
htmlFor="organization-avatar"
@@ -372,6 +373,7 @@ exports[`smoke test 3`] = `
organization.avatar
</label>
<input
+ className="input-super-large"
disabled={true}
id="organization-avatar"
maxLength={256}
@@ -382,12 +384,12 @@ exports[`smoke test 3`] = `
value="foo-avatar"
/>
<div
- className="modal-field-description"
+ className="form-field-description"
>
organization.avatar.description
</div>
<div
- className="spacer-top spacer-bottom"
+ className="spacer-top"
>
<div
className="little-spacer-bottom"
@@ -406,7 +408,7 @@ exports[`smoke test 3`] = `
</div>
</div>
<div
- className="modal-field"
+ className="form-field"
>
<label
htmlFor="organization-description"
@@ -414,6 +416,7 @@ exports[`smoke test 3`] = `
description
</label>
<textarea
+ className="input-super-large"
disabled={true}
id="organization-description"
maxLength={256}
@@ -423,13 +426,13 @@ exports[`smoke test 3`] = `
value="foo-description"
/>
<div
- className="modal-field-description"
+ className="form-field-description"
>
organization.description.description
</div>
</div>
<div
- className="modal-field"
+ className="form-field"
>
<label
htmlFor="organization-url"
@@ -437,6 +440,7 @@ exports[`smoke test 3`] = `
organization.url
</label>
<input
+ className="input-super-large"
disabled={true}
id="organization-url"
maxLength={256}
@@ -446,23 +450,19 @@ exports[`smoke test 3`] = `
value="foo-url"
/>
<div
- className="modal-field-description"
+ className="form-field-description"
>
organization.url.description
</div>
</div>
- <div
- className="modal-field"
+ <SubmitButton
+ disabled={true}
>
- <SubmitButton
- disabled={true}
- >
- save
- </SubmitButton>
- <i
- className="spinner spacer-left"
- />
- </div>
+ save
+ </SubmitButton>
+ <i
+ className="spinner spacer-left"
+ />
</form>
</div>
</div>
diff --git a/server/sonar-web/src/main/js/apps/organizations/components/__tests__/__snapshots__/OrganizationEmpty-test.tsx.snap b/server/sonar-web/src/main/js/apps/organizations/components/__tests__/__snapshots__/OrganizationEmpty-test.tsx.snap
index d58fec02a6c..82a33f255a1 100644
--- a/server/sonar-web/src/main/js/apps/organizations/components/__tests__/__snapshots__/OrganizationEmpty-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/organizations/components/__tests__/__snapshots__/OrganizationEmpty-test.tsx.snap
@@ -10,20 +10,20 @@ exports[`should hide add members button when member sync activated 1`] = `
onboarding.create_organization.ready
</h3>
<div
- className="onboarding-choices"
+ className="display-flex-space-around huge-spacer-top"
>
<Button
- className="onboarding-choice"
+ className="button-huge"
onClick={[Function]}
>
<OnboardingProjectIcon
className="big-spacer-bottom"
/>
- <h6
- className="onboarding-choice-name"
+ <p
+ className="medium spacer-top"
>
provisioning.analyze_new_project
- </h6>
+ </p>
</Button>
</div>
</div>
@@ -39,31 +39,31 @@ exports[`should render 1`] = `
onboarding.create_organization.ready
</h3>
<div
- className="onboarding-choices"
+ className="display-flex-space-around huge-spacer-top"
>
<Button
- className="onboarding-choice"
+ className="button-huge"
onClick={[Function]}
>
<OnboardingProjectIcon
className="big-spacer-bottom"
/>
- <h6
- className="onboarding-choice-name"
+ <p
+ className="medium spacer-top"
>
provisioning.analyze_new_project
- </h6>
+ </p>
</Button>
<Button
- className="onboarding-choice"
+ className="button-huge"
onClick={[Function]}
>
<OnboardingAddMembersIcon />
- <h6
- className="onboarding-choice-name"
+ <p
+ className="medium spacer-top"
>
organization.members.add.multiple
- </h6>
+ </p>
</Button>
</div>
</div>
diff --git a/server/sonar-web/src/main/js/apps/permission-templates/components/Form.tsx b/server/sonar-web/src/main/js/apps/permission-templates/components/Form.tsx
index 9b040c23812..f3eb1a147d9 100644
--- a/server/sonar-web/src/main/js/apps/permission-templates/components/Form.tsx
+++ b/server/sonar-web/src/main/js/apps/permission-templates/components/Form.tsx
@@ -20,17 +20,17 @@
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';
+import { translate } from '../../../helpers/l10n';
interface Props {
confirmButtonText: string;
header: string;
- permissionTemplate?: { description?: string; name: string; projectKeyPattern?: string };
onClose: () => void;
onSubmit: (
data: { description: string; name: string; projectKeyPattern: string }
) => Promise<void>;
+ permissionTemplate?: { description?: string; name: string; projectKeyPattern?: string };
}
interface State {
@@ -79,7 +79,8 @@ export default class Form extends React.PureComponent<Props, State> {
<SimpleModal
header={this.props.header}
onClose={this.props.onClose}
- onSubmit={this.handleSubmit}>
+ onSubmit={this.handleSubmit}
+ size="small">
{({ onCloseClick, onFormSubmit, submitting }) => (
<form id="permission-template-form" onSubmit={onFormSubmit}>
<header className="modal-head">
diff --git a/server/sonar-web/src/main/js/apps/permission-templates/components/__tests__/Form-test.tsx b/server/sonar-web/src/main/js/apps/permission-templates/components/__tests__/Form-test.tsx
new file mode 100644
index 00000000000..1d8dfd32c52
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/permission-templates/components/__tests__/Form-test.tsx
@@ -0,0 +1,30 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+import * as React from 'react';
+import { shallow } from 'enzyme';
+import Form from '../Form';
+
+it('render correctly', () => {
+ expect(
+ shallow(
+ <Form confirmButtonText="confirm" header="title" onClose={jest.fn()} onSubmit={jest.fn()} />
+ )
+ ).toMatchSnapshot();
+});
diff --git a/server/sonar-web/src/main/js/apps/permission-templates/components/__tests__/__snapshots__/Form-test.tsx.snap b/server/sonar-web/src/main/js/apps/permission-templates/components/__tests__/__snapshots__/Form-test.tsx.snap
new file mode 100644
index 00000000000..81c1df3dd78
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/permission-templates/components/__tests__/__snapshots__/Form-test.tsx.snap
@@ -0,0 +1,12 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`render correctly 1`] = `
+<SimpleModal
+ header="title"
+ onClose={[MockFunction]}
+ onSubmit={[Function]}
+ size="small"
+>
+ <Component />
+</SimpleModal>
+`;
diff --git a/server/sonar-web/src/main/js/apps/permissions/project/components/ApplyTemplate.tsx b/server/sonar-web/src/main/js/apps/permissions/project/components/ApplyTemplate.tsx
index bd5538c91b0..d27ac5ad9f8 100644
--- a/server/sonar-web/src/main/js/apps/permissions/project/components/ApplyTemplate.tsx
+++ b/server/sonar-web/src/main/js/apps/permissions/project/components/ApplyTemplate.tsx
@@ -98,7 +98,11 @@ export default class ApplyTemplate extends React.PureComponent<Props, State> {
);
return (
- <SimpleModal header={header} onClose={this.props.onClose} onSubmit={this.handleSubmit}>
+ <SimpleModal
+ header={header}
+ onClose={this.props.onClose}
+ onSubmit={this.handleSubmit}
+ size="small">
{({ onCloseClick, onFormSubmit, submitting }) => (
<form id="project-permissions-apply-template-form" onSubmit={onFormSubmit}>
<header className="modal-head">
diff --git a/server/sonar-web/src/main/js/apps/permissions/project/components/__tests__/ApplyTemplate-test.tsx b/server/sonar-web/src/main/js/apps/permissions/project/components/__tests__/ApplyTemplate-test.tsx
new file mode 100644
index 00000000000..221b9e9e504
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/permissions/project/components/__tests__/ApplyTemplate-test.tsx
@@ -0,0 +1,53 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+import * as React from 'react';
+import { shallow } from 'enzyme';
+import ApplyTemplate from '../ApplyTemplate';
+import { waitAndUpdate } from '../../../../../helpers/testUtils';
+
+jest.mock('../../../../../api/permissions', () => ({
+ getPermissionTemplates: jest.fn().mockResolvedValue({
+ permissionTemplates: [
+ {
+ id: 'tmp1',
+ name: 'SonarSource projects',
+ createdAt: '2015-11-27T15:20:32+0100',
+ permissions: [
+ { key: 'admin', usersCount: 0, groupsCount: 3 },
+ { key: 'codeviewer', usersCount: 0, groupsCount: 6 }
+ ]
+ }
+ ],
+ defaultTemplates: [{ templateId: 'tmp1', qualifier: 'TRK' }],
+ permissions: [
+ { key: 'admin', name: 'Administer', description: 'Administer access' },
+ { key: 'codeviewer', name: 'See Source Code', description: 'View code' }
+ ]
+ })
+}));
+
+it('render correctly', async () => {
+ const wrapper = shallow(
+ <ApplyTemplate onClose={jest.fn()} organization="foo" project={{ key: 'foo', name: 'Foo' }} />
+ );
+ expect(wrapper).toMatchSnapshot();
+ await waitAndUpdate(wrapper);
+ expect(wrapper.dive()).toMatchSnapshot();
+});
diff --git a/server/sonar-web/src/main/js/apps/permissions/project/components/__tests__/__snapshots__/ApplyTemplate-test.tsx.snap b/server/sonar-web/src/main/js/apps/permissions/project/components/__tests__/__snapshots__/ApplyTemplate-test.tsx.snap
new file mode 100644
index 00000000000..84bd72b0667
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/permissions/project/components/__tests__/__snapshots__/ApplyTemplate-test.tsx.snap
@@ -0,0 +1,83 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`render correctly 1`] = `
+<SimpleModal
+ header="projects_role.apply_template_to_xxx.Foo"
+ onClose={[MockFunction]}
+ onSubmit={[Function]}
+ size="small"
+>
+ <Component />
+</SimpleModal>
+`;
+
+exports[`render correctly 2`] = `
+<Modal
+ contentLabel="projects_role.apply_template_to_xxx.Foo"
+ onRequestClose={[MockFunction]}
+ size="small"
+>
+ <form
+ id="project-permissions-apply-template-form"
+ onSubmit={[Function]}
+ >
+ <header
+ className="modal-head"
+ >
+ <h2>
+ projects_role.apply_template_to_xxx.Foo
+ </h2>
+ </header>
+ <div
+ className="modal-body"
+ >
+ <div
+ className="modal-field"
+ >
+ <label
+ htmlFor="project-permissions-template"
+ >
+ template
+ <em
+ className="mandatory"
+ >
+ *
+ </em>
+ </label>
+ <Select
+ clearable={false}
+ id="project-permissions-template"
+ onChange={[Function]}
+ options={
+ Array [
+ Object {
+ "label": "SonarSource projects",
+ "value": "tmp1",
+ },
+ ]
+ }
+ />
+ </div>
+ </div>
+ <footer
+ className="modal-foot"
+ >
+ <DeferredSpinner
+ className="spacer-right"
+ loading={false}
+ timeout={100}
+ />
+ <SubmitButton
+ disabled={true}
+ >
+ apply
+ </SubmitButton>
+ <ResetButtonLink
+ onClick={[Function]}
+ >
+ cancel
+ </ResetButtonLink>
+ </footer>
+ </form>
+</Modal>
+`;
diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/forms/AddEventForm.tsx b/server/sonar-web/src/main/js/apps/projectActivity/components/forms/AddEventForm.tsx
index 98bf37efb85..1f5bb6c51bf 100644
--- a/server/sonar-web/src/main/js/apps/projectActivity/components/forms/AddEventForm.tsx
+++ b/server/sonar-web/src/main/js/apps/projectActivity/components/forms/AddEventForm.tsx
@@ -51,7 +51,8 @@ export default class AddEventForm extends React.PureComponent<Props, State> {
confirmDisable={!this.state.name}
header={translate(this.props.addEventButtonText)}
onClose={this.props.onClose}
- onConfirm={this.handleSubmit}>
+ onConfirm={this.handleSubmit}
+ size="small">
<div className="modal-field">
<label>{translate('name')}</label>
<input
diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/forms/ChangeEventForm.tsx b/server/sonar-web/src/main/js/apps/projectActivity/components/forms/ChangeEventForm.tsx
index c566bbfc450..22680c097ad 100644
--- a/server/sonar-web/src/main/js/apps/projectActivity/components/forms/ChangeEventForm.tsx
+++ b/server/sonar-web/src/main/js/apps/projectActivity/components/forms/ChangeEventForm.tsx
@@ -23,8 +23,8 @@ import ConfirmModal from '../../../../components/controls/ConfirmModal';
interface Props {
changeEvent: (event: string, name: string) => Promise<void>;
- header: string;
event: T.AnalysisEvent;
+ header: string;
onClose: () => void;
}
@@ -54,7 +54,8 @@ export default class ChangeEventForm extends React.PureComponent<Props, State> {
confirmDisable={!name || name === this.props.event.name}
header={this.props.header}
onClose={this.props.onClose}
- onConfirm={this.handleSubmit}>
+ onConfirm={this.handleSubmit}
+ size="small">
<div className="modal-field">
<label>{translate('name')}</label>
<input autoFocus={true} onChange={this.changeInput} type="text" value={name} />
diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/forms/__tests__/AddEventForm-test.tsx b/server/sonar-web/src/main/js/apps/projectActivity/components/forms/__tests__/AddEventForm-test.tsx
new file mode 100644
index 00000000000..68791270850
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/projectActivity/components/forms/__tests__/AddEventForm-test.tsx
@@ -0,0 +1,41 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+import * as React from 'react';
+import { shallow } from 'enzyme';
+import AddEventForm from '../AddEventForm';
+
+it('should render correctly', () => {
+ expect(
+ shallow(
+ <AddEventForm
+ addEvent={jest.fn()}
+ addEventButtonText="add"
+ analysis={{
+ key: '1',
+ date: new Date('2019-01-14T15:44:51.000Z'),
+ events: [{ key: '2', category: 'VERSION', name: '1.0' }],
+ codePeriodVersion: '1.0',
+ manualNewCodePeriodBaseline: false
+ }}
+ onClose={jest.fn()}
+ />
+ )
+ ).toMatchSnapshot();
+});
diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/forms/__tests__/ChangeEventForm-test.tsx b/server/sonar-web/src/main/js/apps/projectActivity/components/forms/__tests__/ChangeEventForm-test.tsx
new file mode 100644
index 00000000000..4c739f499f7
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/projectActivity/components/forms/__tests__/ChangeEventForm-test.tsx
@@ -0,0 +1,35 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+import * as React from 'react';
+import { shallow } from 'enzyme';
+import ChangeEventForm from '../ChangeEventForm';
+
+it('should render correctly', () => {
+ expect(
+ shallow(
+ <ChangeEventForm
+ changeEvent={jest.fn()}
+ event={{ category: 'VERSION', key: '1', name: '1.0' }}
+ header="change"
+ onClose={jest.fn()}
+ />
+ )
+ ).toMatchSnapshot();
+});
diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/forms/__tests__/__snapshots__/AddEventForm-test.tsx.snap b/server/sonar-web/src/main/js/apps/projectActivity/components/forms/__tests__/__snapshots__/AddEventForm-test.tsx.snap
new file mode 100644
index 00000000000..2331d990381
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/projectActivity/components/forms/__tests__/__snapshots__/AddEventForm-test.tsx.snap
@@ -0,0 +1,26 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render correctly 1`] = `
+<ConfirmModal
+ confirmButtonText="save"
+ confirmDisable={true}
+ header="add"
+ onClose={[MockFunction]}
+ onConfirm={[Function]}
+ size="small"
+>
+ <div
+ className="modal-field"
+ >
+ <label>
+ name
+ </label>
+ <input
+ autoFocus={true}
+ onChange={[Function]}
+ type="text"
+ value=""
+ />
+ </div>
+</ConfirmModal>
+`;
diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/forms/__tests__/__snapshots__/ChangeEventForm-test.tsx.snap b/server/sonar-web/src/main/js/apps/projectActivity/components/forms/__tests__/__snapshots__/ChangeEventForm-test.tsx.snap
new file mode 100644
index 00000000000..70debbd5a51
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/projectActivity/components/forms/__tests__/__snapshots__/ChangeEventForm-test.tsx.snap
@@ -0,0 +1,26 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render correctly 1`] = `
+<ConfirmModal
+ confirmButtonText="change_verb"
+ confirmDisable={true}
+ header="change"
+ onClose={[MockFunction]}
+ onConfirm={[Function]}
+ size="small"
+>
+ <div
+ className="modal-field"
+ >
+ <label>
+ name
+ </label>
+ <input
+ autoFocus={true}
+ onChange={[Function]}
+ type="text"
+ value="1.0"
+ />
+ </div>
+</ConfirmModal>
+`;
diff --git a/server/sonar-web/src/main/js/apps/projectBranches/components/RenameBranchModal.tsx b/server/sonar-web/src/main/js/apps/projectBranches/components/RenameBranchModal.tsx
index 3ce5526a0fb..0a5ae1e36f0 100644
--- a/server/sonar-web/src/main/js/apps/projectBranches/components/RenameBranchModal.tsx
+++ b/server/sonar-web/src/main/js/apps/projectBranches/components/RenameBranchModal.tsx
@@ -79,7 +79,7 @@ export default class RenameBranchModal extends React.PureComponent<Props, State>
this.state.loading || !this.state.name || this.state.name === branch.name;
return (
- <Modal contentLabel={header} onRequestClose={this.props.onClose}>
+ <Modal contentLabel={header} onRequestClose={this.props.onClose} size="small">
<header className="modal-head">
<h2>{header}</h2>
</header>
diff --git a/server/sonar-web/src/main/js/apps/projectBranches/components/SettingForm.tsx b/server/sonar-web/src/main/js/apps/projectBranches/components/SettingForm.tsx
index 6a486e563eb..6cfbcee0d44 100644
--- a/server/sonar-web/src/main/js/apps/projectBranches/components/SettingForm.tsx
+++ b/server/sonar-web/src/main/js/apps/projectBranches/components/SettingForm.tsx
@@ -98,7 +98,7 @@ export default class SettingForm extends React.PureComponent<Props, State> {
className="big-spacer-bottom markdown"
dangerouslySetInnerHTML={{ __html: translate(`property.${setting.key}.description`) }}
/>
- <div className="big-spacer-bottom">
+ <div className="modal-field">
<input
autoFocus={true}
className="input-super-large"
@@ -108,11 +108,11 @@ export default class SettingForm extends React.PureComponent<Props, State> {
value={this.state.value}
/>
{setting.inherited && (
- <div className="note spacer-top">{translate('settings._default')}</div>
+ <div className="modal-field-description">{translate('settings._default')}</div>
)}
{!setting.inherited &&
setting.parentValue && (
- <div className="note spacer-top">
+ <div className="modal-field-description">
{translateWithParameters('settings.default_x', setting.parentValue)}
</div>
)}
diff --git a/server/sonar-web/src/main/js/apps/projectBranches/components/__tests__/__snapshots__/RenameBranchModal-test.tsx.snap b/server/sonar-web/src/main/js/apps/projectBranches/components/__tests__/__snapshots__/RenameBranchModal-test.tsx.snap
index 77c4b64b7f1..fc11d119916 100644
--- a/server/sonar-web/src/main/js/apps/projectBranches/components/__tests__/__snapshots__/RenameBranchModal-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/projectBranches/components/__tests__/__snapshots__/RenameBranchModal-test.tsx.snap
@@ -4,6 +4,7 @@ exports[`renders 1`] = `
<Modal
contentLabel="branches.rename"
onRequestClose={[MockFunction]}
+ size="small"
>
<header
className="modal-head"
@@ -66,6 +67,7 @@ exports[`renders 2`] = `
<Modal
contentLabel="branches.rename"
onRequestClose={[MockFunction]}
+ size="small"
>
<header
className="modal-head"
@@ -128,6 +130,7 @@ exports[`renders 3`] = `
<Modal
contentLabel="branches.rename"
onRequestClose={[MockFunction]}
+ size="small"
>
<header
className="modal-head"
diff --git a/server/sonar-web/src/main/js/apps/projectBranches/components/__tests__/__snapshots__/SettingForm-test.tsx.snap b/server/sonar-web/src/main/js/apps/projectBranches/components/__tests__/__snapshots__/SettingForm-test.tsx.snap
index 1c038c720ff..45cb96b4022 100644
--- a/server/sonar-web/src/main/js/apps/projectBranches/components/__tests__/__snapshots__/SettingForm-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/projectBranches/components/__tests__/__snapshots__/SettingForm-test.tsx.snap
@@ -16,7 +16,7 @@ exports[`changes value 1`] = `
}
/>
<div
- className="big-spacer-bottom"
+ className="modal-field"
>
<input
autoFocus={true}
@@ -27,7 +27,7 @@ exports[`changes value 1`] = `
value="release-.*"
/>
<div
- className="note spacer-top"
+ className="modal-field-description"
>
settings._default
</div>
@@ -66,7 +66,7 @@ exports[`resets value 1`] = `
}
/>
<div
- className="big-spacer-bottom"
+ className="modal-field"
>
<input
autoFocus={true}
@@ -77,7 +77,7 @@ exports[`resets value 1`] = `
value="release-.*"
/>
<div
- className="note spacer-top"
+ className="modal-field-description"
>
settings.default_x.branch-.*
</div>
diff --git a/server/sonar-web/src/main/js/apps/projectLinks/CreationModal.tsx b/server/sonar-web/src/main/js/apps/projectLinks/CreationModal.tsx
index 4b6285a29d2..5ad2acbd937 100644
--- a/server/sonar-web/src/main/js/apps/projectLinks/CreationModal.tsx
+++ b/server/sonar-web/src/main/js/apps/projectLinks/CreationModal.tsx
@@ -52,7 +52,11 @@ export default class CreationModal extends React.PureComponent<Props, State> {
const header = translate('project_links.create_new_project_link');
return (
- <SimpleModal header={header} onClose={this.props.onClose} onSubmit={this.handleSubmit}>
+ <SimpleModal
+ header={header}
+ onClose={this.props.onClose}
+ onSubmit={this.handleSubmit}
+ size="small">
{({ onCloseClick, onFormSubmit, submitting }) => (
<form onSubmit={onFormSubmit}>
<header className="modal-head">
diff --git a/server/sonar-web/src/main/js/apps/projectLinks/__tests__/__snapshots__/CreationModal-test.tsx.snap b/server/sonar-web/src/main/js/apps/projectLinks/__tests__/__snapshots__/CreationModal-test.tsx.snap
index b56b44cef77..b46aff716c6 100644
--- a/server/sonar-web/src/main/js/apps/projectLinks/__tests__/__snapshots__/CreationModal-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/projectLinks/__tests__/__snapshots__/CreationModal-test.tsx.snap
@@ -4,6 +4,7 @@ exports[`should create link 1`] = `
<Modal
contentLabel="project_links.create_new_project_link"
onRequestClose={[MockFunction]}
+ size="small"
>
<form
onSubmit={[Function]}
diff --git a/server/sonar-web/src/main/js/apps/projectsManagement/BulkApplyTemplateModal.tsx b/server/sonar-web/src/main/js/apps/projectsManagement/BulkApplyTemplateModal.tsx
index 63d8ae46b7f..86e8b4e1dce 100644
--- a/server/sonar-web/src/main/js/apps/projectsManagement/BulkApplyTemplateModal.tsx
+++ b/server/sonar-web/src/main/js/apps/projectsManagement/BulkApplyTemplateModal.tsx
@@ -152,7 +152,7 @@ export default class BulkApplyTemplateModal extends React.PureComponent<Props, S
const header = translate('permission_templates.bulk_apply_permission_template');
return (
- <Modal contentLabel={header} onRequestClose={this.props.onClose}>
+ <Modal contentLabel={header} onRequestClose={this.props.onClose} size="small">
<header className="modal-head">
<h2>{header}</h2>
</header>
diff --git a/server/sonar-web/src/main/js/apps/projectsManagement/CreateProjectForm.tsx b/server/sonar-web/src/main/js/apps/projectsManagement/CreateProjectForm.tsx
index dd78ed4eb3c..a916e81d16e 100644
--- a/server/sonar-web/src/main/js/apps/projectsManagement/CreateProjectForm.tsx
+++ b/server/sonar-web/src/main/js/apps/projectsManagement/CreateProjectForm.tsx
@@ -199,8 +199,9 @@ export default class CreateProjectForm extends React.PureComponent<Props, State>
{organization.actions &&
organization.actions.admin &&
!organization.canUpdateProjectsVisibilityToPrivate && (
- <div className="spacer-top display-flex-space-around">
+ <div className="spacer-top">
<UpgradeOrganizationBox
+ className="width-100"
insideModal={true}
onOrganizationUpgrade={this.props.onOrganizationUpgrade}
organization={organization}
diff --git a/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/__snapshots__/BulkApplyTemplateModal-test.tsx.snap b/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/__snapshots__/BulkApplyTemplateModal-test.tsx.snap
index 3582abeb9cf..9b7ddf1b306 100644
--- a/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/__snapshots__/BulkApplyTemplateModal-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/__snapshots__/BulkApplyTemplateModal-test.tsx.snap
@@ -4,6 +4,7 @@ exports[`bulk applies template to all results 1`] = `
<Modal
contentLabel="permission_templates.bulk_apply_permission_template"
onRequestClose={[MockFunction]}
+ size="small"
>
<header
className="modal-head"
@@ -36,6 +37,7 @@ exports[`bulk applies template to all results 2`] = `
<Modal
contentLabel="permission_templates.bulk_apply_permission_template"
onRequestClose={[MockFunction]}
+ size="small"
>
<header
className="modal-head"
@@ -106,6 +108,7 @@ exports[`bulk applies template to all results 3`] = `
<Modal
contentLabel="permission_templates.bulk_apply_permission_template"
onRequestClose={[MockFunction]}
+ size="small"
>
<header
className="modal-head"
@@ -179,6 +182,7 @@ exports[`bulk applies template to all results 4`] = `
<Modal
contentLabel="permission_templates.bulk_apply_permission_template"
onRequestClose={[MockFunction]}
+ size="small"
>
<header
className="modal-head"
@@ -213,6 +217,7 @@ exports[`bulk applies template to selected results 1`] = `
<Modal
contentLabel="permission_templates.bulk_apply_permission_template"
onRequestClose={[MockFunction]}
+ size="small"
>
<header
className="modal-head"
@@ -245,6 +250,7 @@ exports[`bulk applies template to selected results 2`] = `
<Modal
contentLabel="permission_templates.bulk_apply_permission_template"
onRequestClose={[MockFunction]}
+ size="small"
>
<header
className="modal-head"
@@ -315,6 +321,7 @@ exports[`bulk applies template to selected results 3`] = `
<Modal
contentLabel="permission_templates.bulk_apply_permission_template"
onRequestClose={[MockFunction]}
+ size="small"
>
<header
className="modal-head"
@@ -388,6 +395,7 @@ exports[`bulk applies template to selected results 4`] = `
<Modal
contentLabel="permission_templates.bulk_apply_permission_template"
onRequestClose={[MockFunction]}
+ size="small"
>
<header
className="modal-head"
diff --git a/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/__snapshots__/CreateProjectForm-test.tsx.snap b/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/__snapshots__/CreateProjectForm-test.tsx.snap
index 859052104d3..9879d2fd163 100644
--- a/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/__snapshots__/CreateProjectForm-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/__snapshots__/CreateProjectForm-test.tsx.snap
@@ -79,9 +79,10 @@ exports[`creates project 1`] = `
/>
</div>
<div
- className="spacer-top display-flex-space-around"
+ className="spacer-top"
>
<UpgradeOrganizationBox
+ className="width-100"
insideModal={true}
onOrganizationUpgrade={[MockFunction]}
organization={
@@ -196,9 +197,10 @@ exports[`creates project 2`] = `
/>
</div>
<div
- className="spacer-top display-flex-space-around"
+ className="spacer-top"
>
<UpgradeOrganizationBox
+ className="width-100"
insideModal={true}
onOrganizationUpgrade={[MockFunction]}
organization={
@@ -313,9 +315,10 @@ exports[`creates project 3`] = `
/>
</div>
<div
- className="spacer-top display-flex-space-around"
+ className="spacer-top"
>
<UpgradeOrganizationBox
+ className="width-100"
insideModal={true}
onOrganizationUpgrade={[MockFunction]}
organization={
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/ConditionModal.tsx b/server/sonar-web/src/main/js/apps/quality-gates/components/ConditionModal.tsx
index 7284f830995..92f9219f92c 100644
--- a/server/sonar-web/src/main/js/apps/quality-gates/components/ConditionModal.tsx
+++ b/server/sonar-web/src/main/js/apps/quality-gates/components/ConditionModal.tsx
@@ -107,7 +107,8 @@ export default class ConditionModal extends React.PureComponent<Props, State> {
confirmDisable={metric === undefined}
header={header}
onClose={onClose}
- onConfirm={this.handleFormSubmit}>
+ onConfirm={this.handleFormSubmit}
+ size="small">
{this.state.errorMessage && <Alert variant="error">{this.state.errorMessage}</Alert>}
<div className="modal-field">
<label htmlFor="condition-metric">{translate('quality_gates.conditions.metric')}</label>
@@ -118,7 +119,7 @@ export default class ConditionModal extends React.PureComponent<Props, State> {
</div>
{metric && (
<>
- <div className="modal-field">
+ <div className="modal-field display-inline-block">
<label htmlFor="condition-operator">
{translate('quality_gates.conditions.operator')}
</label>
@@ -128,7 +129,7 @@ export default class ConditionModal extends React.PureComponent<Props, State> {
op={op}
/>
</div>
- <div className="modal-field">
+ <div className="modal-field display-inline-block spacer-left">
<label htmlFor="condition-threshold">
{translate('quality_gates.conditions.error')}
</label>
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/ConditionOperator.tsx b/server/sonar-web/src/main/js/apps/quality-gates/components/ConditionOperator.tsx
index de16855c471..b2cb17a398e 100644
--- a/server/sonar-web/src/main/js/apps/quality-gates/components/ConditionOperator.tsx
+++ b/server/sonar-web/src/main/js/apps/quality-gates/components/ConditionOperator.tsx
@@ -23,9 +23,9 @@ import { translate } from '../../../helpers/l10n';
import { getPossibleOperators } from '../utils';
interface Props {
- op?: string;
metric: T.Metric;
onOperatorChange: (op: string) => void;
+ op?: string;
}
export default class ConditionOperator extends React.PureComponent<Props> {
@@ -62,7 +62,11 @@ export default class ConditionOperator extends React.PureComponent<Props> {
/>
);
} else {
- return <span className="note">{this.getLabel(operators, this.props.metric)}</span>;
+ return (
+ <span className="display-inline-block note abs-width-150">
+ {this.getLabel(operators, this.props.metric)}
+ </span>
+ );
}
}
}
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/CopyQualityGateForm.tsx b/server/sonar-web/src/main/js/apps/quality-gates/components/CopyQualityGateForm.tsx
index e6fc44670df..a631c273c05 100644
--- a/server/sonar-web/src/main/js/apps/quality-gates/components/CopyQualityGateForm.tsx
+++ b/server/sonar-web/src/main/js/apps/quality-gates/components/CopyQualityGateForm.tsx
@@ -71,7 +71,8 @@ class CopyQualityGateForm extends React.PureComponent<Props, State> {
confirmDisable={confirmDisable}
header={translate('quality_gates.copy')}
onClose={this.props.onClose}
- onConfirm={this.handleCopy}>
+ onConfirm={this.handleCopy}
+ size="small">
<div className="modal-field">
<label htmlFor="quality-gate-form-name">
{translate('name')}
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/CreateQualityGateForm.tsx b/server/sonar-web/src/main/js/apps/quality-gates/components/CreateQualityGateForm.tsx
index a2ffb8d04b3..c310bb32ab7 100644
--- a/server/sonar-web/src/main/js/apps/quality-gates/components/CreateQualityGateForm.tsx
+++ b/server/sonar-web/src/main/js/apps/quality-gates/components/CreateQualityGateForm.tsx
@@ -67,7 +67,8 @@ class CreateQualityGateForm extends React.PureComponent<Props, State> {
confirmDisable={!name}
header={translate('quality_gates.create')}
onClose={this.props.onClose}
- onConfirm={this.handleCreate}>
+ onConfirm={this.handleCreate}
+ size="small">
<div className="modal-field">
<label htmlFor="quality-gate-form-name">
{translate('name')}
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/MetricSelect.tsx b/server/sonar-web/src/main/js/apps/quality-gates/components/MetricSelect.tsx
index 17c3da58fcb..86bba914825 100644
--- a/server/sonar-web/src/main/js/apps/quality-gates/components/MetricSelect.tsx
+++ b/server/sonar-web/src/main/js/apps/quality-gates/components/MetricSelect.tsx
@@ -41,7 +41,8 @@ interface Option {
export default class MetricSelect extends React.PureComponent<Props, State> {
state = { value: -1 };
- handleChange = ({ value }: Option) => {
+ handleChange = (option: Option | null) => {
+ const value = option ? option.value : -1;
this.setState({ value });
this.props.onMetricChange(this.props.metrics[value]);
};
@@ -74,7 +75,7 @@ export default class MetricSelect extends React.PureComponent<Props, State> {
return (
<Select
- className="text-middle input-large"
+ className="text-middle"
id="condition-metric"
onChange={this.handleChange}
options={optionsWithDomains}
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/RenameQualityGateForm.tsx b/server/sonar-web/src/main/js/apps/quality-gates/components/RenameQualityGateForm.tsx
index b468b58d614..30bc667f7b7 100644
--- a/server/sonar-web/src/main/js/apps/quality-gates/components/RenameQualityGateForm.tsx
+++ b/server/sonar-web/src/main/js/apps/quality-gates/components/RenameQualityGateForm.tsx
@@ -67,7 +67,8 @@ export default class RenameQualityGateForm extends React.PureComponent<Props, St
confirmDisable={confirmDisable}
header={translate('quality_gates.rename')}
onClose={this.props.onClose}
- onConfirm={this.handleRename}>
+ onConfirm={this.handleRename}
+ size="small">
<div className="modal-field">
<label htmlFor="quality-gate-form-name">
{translate('name')}
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/ConditionModal-test.tsx b/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/ConditionModal-test.tsx
new file mode 100644
index 00000000000..b1769f7a610
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/ConditionModal-test.tsx
@@ -0,0 +1,43 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+import * as React from 'react';
+import { shallow } from 'enzyme';
+import ConditionModal from '../ConditionModal';
+import { mockQualityGate } from '../../../../helpers/testMocks';
+
+it('should render correctly', () => {
+ const wrapper = shallowRender();
+ expect(wrapper).toMatchSnapshot();
+
+ wrapper.instance().handleMetricChange({ id: '1', key: 'foo', name: 'Foo', type: 'PERCENT' });
+ expect(wrapper).toMatchSnapshot();
+});
+
+function shallowRender(props: Partial<ConditionModal['props']> = {}) {
+ return shallow<ConditionModal>(
+ <ConditionModal
+ header="a"
+ onAddCondition={jest.fn()}
+ onClose={jest.fn()}
+ qualityGate={mockQualityGate()}
+ {...props}
+ />
+ );
+}
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/ConditionOperator-test.tsx b/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/ConditionOperator-test.tsx
new file mode 100644
index 00000000000..ef6c9fd2776
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/ConditionOperator-test.tsx
@@ -0,0 +1,37 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+import * as React from 'react';
+import { shallow } from 'enzyme';
+import ConditionOperator from '../ConditionOperator';
+
+it('should render correctly', () => {
+ expect(shallowRender()).toMatchSnapshot();
+});
+
+function shallowRender(props: Partial<ConditionOperator['props']> = {}) {
+ return shallow(
+ <ConditionOperator
+ metric={{ id: '1', key: 'foo', name: 'Foo', type: 'PERCENT' }}
+ onOperatorChange={jest.fn()}
+ op="LT"
+ {...props}
+ />
+ );
+}
diff --git a/server/sonar-web/src/main/js/components/ui/__tests__/NewInfoBox-test.tsx b/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/CopyQualityGateForm-test.tsx
index d7fc93c6a11..85310abd443 100644
--- a/server/sonar-web/src/main/js/components/ui/__tests__/NewInfoBox-test.tsx
+++ b/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/CopyQualityGateForm-test.tsx
@@ -19,26 +19,13 @@
*/
import * as React from 'react';
import { shallow } from 'enzyme';
-import NewInfoBox from '../NewInfoBox';
-import { click } from '../../../helpers/testUtils';
+import CopyQualityGateForm from '../CopyQualityGateForm';
+import { mockQualityGate } from '../../../../helpers/testMocks';
it('should render correctly', () => {
expect(
shallow(
- <NewInfoBox description="My description" onClose={jest.fn()} title="My title">
- <div />
- </NewInfoBox>
+ <CopyQualityGateForm onClose={jest.fn()} onCopy={jest.fn()} qualityGate={mockQualityGate()} />
)
).toMatchSnapshot();
});
-
-it('should allow to opt out', () => {
- const onClose = jest.fn();
- const wrapper = shallow(
- <NewInfoBox description="" onClose={onClose} title="">
- <div />
- </NewInfoBox>
- );
- click(wrapper.find('ButtonIcon'));
- expect(onClose).toHaveBeenCalled();
-});
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/CreateQualityGateForm-test.tsx b/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/CreateQualityGateForm-test.tsx
new file mode 100644
index 00000000000..dde2fafa955
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/CreateQualityGateForm-test.tsx
@@ -0,0 +1,28 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+import * as React from 'react';
+import { shallow } from 'enzyme';
+import CreateQualityGateForm from '../CreateQualityGateForm';
+
+it('should render correctly', () => {
+ expect(
+ shallow(<CreateQualityGateForm onClose={jest.fn()} onCreate={jest.fn()} />)
+ ).toMatchSnapshot();
+});
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/MetricSelect-test.tsx b/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/MetricSelect-test.tsx
new file mode 100644
index 00000000000..ad2d9fea3f3
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/MetricSelect-test.tsx
@@ -0,0 +1,40 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+import * as React from 'react';
+import { shallow } from 'enzyme';
+import MetricSelect from '../MetricSelect';
+
+it('should render correctly', () => {
+ expect(
+ shallow(
+ <MetricSelect
+ metrics={[
+ {
+ id: '1',
+ key: '1',
+ name: 'metric 1',
+ type: 'test'
+ }
+ ]}
+ onMetricChange={jest.fn()}
+ />
+ )
+ ).toMatchSnapshot();
+});
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/RenameQualityGateForm-test.tsx b/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/RenameQualityGateForm-test.tsx
new file mode 100644
index 00000000000..1ed1d7d18eb
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/RenameQualityGateForm-test.tsx
@@ -0,0 +1,35 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+import * as React from 'react';
+import { shallow } from 'enzyme';
+import RenameQualityGateForm from '../RenameQualityGateForm';
+import { mockQualityGate } from '../../../../helpers/testMocks';
+
+it('should render correctly', () => {
+ expect(
+ shallow(
+ <RenameQualityGateForm
+ onClose={jest.fn()}
+ onRename={jest.fn()}
+ qualityGate={mockQualityGate()}
+ />
+ )
+ ).toMatchSnapshot();
+});
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/ConditionModal-test.tsx.snap b/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/ConditionModal-test.tsx.snap
new file mode 100644
index 00000000000..ab32f89c02b
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/ConditionModal-test.tsx.snap
@@ -0,0 +1,85 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render correctly 1`] = `
+<ConfirmModal
+ confirmButtonText="a"
+ confirmDisable={true}
+ header="a"
+ onClose={[MockFunction]}
+ onConfirm={[Function]}
+ size="small"
+>
+ <div
+ className="modal-field"
+ >
+ <label
+ htmlFor="condition-metric"
+ >
+ quality_gates.conditions.metric
+ </label>
+ </div>
+</ConfirmModal>
+`;
+
+exports[`should render correctly 2`] = `
+<ConfirmModal
+ confirmButtonText="a"
+ confirmDisable={false}
+ header="a"
+ onClose={[MockFunction]}
+ onConfirm={[Function]}
+ size="small"
+>
+ <div
+ className="modal-field"
+ >
+ <label
+ htmlFor="condition-metric"
+ >
+ quality_gates.conditions.metric
+ </label>
+ </div>
+ <div
+ className="modal-field display-inline-block"
+ >
+ <label
+ htmlFor="condition-operator"
+ >
+ quality_gates.conditions.operator
+ </label>
+ <ConditionOperator
+ metric={
+ Object {
+ "id": "1",
+ "key": "foo",
+ "name": "Foo",
+ "type": "PERCENT",
+ }
+ }
+ onOperatorChange={[Function]}
+ />
+ </div>
+ <div
+ className="modal-field display-inline-block spacer-left"
+ >
+ <label
+ htmlFor="condition-threshold"
+ >
+ quality_gates.conditions.error
+ </label>
+ <ThresholdInput
+ metric={
+ Object {
+ "id": "1",
+ "key": "foo",
+ "name": "Foo",
+ "type": "PERCENT",
+ }
+ }
+ name="error"
+ onChange={[Function]}
+ value=""
+ />
+ </div>
+</ConfirmModal>
+`;
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/ConditionOperator-test.tsx.snap b/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/ConditionOperator-test.tsx.snap
new file mode 100644
index 00000000000..a229502920f
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/ConditionOperator-test.tsx.snap
@@ -0,0 +1,26 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render correctly 1`] = `
+<Select
+ autoFocus={true}
+ className="input-medium"
+ clearable={false}
+ id="condition-operator"
+ name="operator"
+ onChange={[Function]}
+ options={
+ Array [
+ Object {
+ "label": "quality_gates.operator.LT",
+ "value": "LT",
+ },
+ Object {
+ "label": "quality_gates.operator.GT",
+ "value": "GT",
+ },
+ ]
+ }
+ searchable={false}
+ value="LT"
+/>
+`;
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/CopyQualityGateForm-test.tsx.snap b/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/CopyQualityGateForm-test.tsx.snap
new file mode 100644
index 00000000000..dae3160544a
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/CopyQualityGateForm-test.tsx.snap
@@ -0,0 +1,14 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render correctly 1`] = `
+<CopyQualityGateForm
+ onClose={[MockFunction]}
+ onCopy={[MockFunction]}
+ qualityGate={
+ Object {
+ "id": 1,
+ "name": "qualitygate",
+ }
+ }
+/>
+`;
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/CreateQualityGateForm-test.tsx.snap b/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/CreateQualityGateForm-test.tsx.snap
new file mode 100644
index 00000000000..d7702455fbe
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/CreateQualityGateForm-test.tsx.snap
@@ -0,0 +1,8 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render correctly 1`] = `
+<CreateQualityGateForm
+ onClose={[MockFunction]}
+ onCreate={[MockFunction]}
+/>
+`;
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/MetricSelect-test.tsx.snap b/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/MetricSelect-test.tsx.snap
new file mode 100644
index 00000000000..b03dceb54a2
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/MetricSelect-test.tsx.snap
@@ -0,0 +1,20 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render correctly 1`] = `
+<Select
+ className="text-middle"
+ id="condition-metric"
+ onChange={[Function]}
+ options={
+ Array [
+ Object {
+ "domain": undefined,
+ "label": "metric 1",
+ "value": 0,
+ },
+ ]
+ }
+ placeholder="search.search_for_metrics"
+ value={-1}
+/>
+`;
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/RenameQualityGateForm-test.tsx.snap b/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/RenameQualityGateForm-test.tsx.snap
new file mode 100644
index 00000000000..3a7393891bf
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/RenameQualityGateForm-test.tsx.snap
@@ -0,0 +1,37 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render correctly 1`] = `
+<ConfirmModal
+ confirmButtonText="rename"
+ confirmDisable={true}
+ header="quality_gates.rename"
+ onClose={[MockFunction]}
+ onConfirm={[Function]}
+ size="small"
+>
+ <div
+ className="modal-field"
+ >
+ <label
+ htmlFor="quality-gate-form-name"
+ >
+ name
+ <em
+ className="mandatory"
+ >
+ *
+ </em>
+ </label>
+ <input
+ autoFocus={true}
+ id="quality-gate-form-name"
+ maxLength={100}
+ onChange={[Function]}
+ required={true}
+ size={50}
+ type="text"
+ value="qualitygate"
+ />
+ </div>
+</ConfirmModal>
+`;
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/components/CopyProfileForm.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/components/CopyProfileForm.tsx
index 64ac78e752c..bcd4405a9c2 100644
--- a/server/sonar-web/src/main/js/apps/quality-profiles/components/CopyProfileForm.tsx
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/components/CopyProfileForm.tsx
@@ -80,7 +80,7 @@ export default class CopyProfileForm extends React.PureComponent<Props, State> {
this.state.loading || !this.state.name || this.state.name === profile.name;
return (
- <Modal contentLabel={header} onRequestClose={this.props.onClose}>
+ <Modal contentLabel={header} onRequestClose={this.props.onClose} size="small">
<form id="copy-profile-form" onSubmit={this.handleFormSubmit}>
<div className="modal-head">
<h2>{header}</h2>
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/components/ExtendProfileForm.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/components/ExtendProfileForm.tsx
index 6b8fd8db20d..a686ceb5e18 100644
--- a/server/sonar-web/src/main/js/apps/quality-profiles/components/ExtendProfileForm.tsx
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/components/ExtendProfileForm.tsx
@@ -96,7 +96,7 @@ export default class ExtendProfileForm extends React.PureComponent<Props, State>
);
return (
- <Modal contentLabel={header} onRequestClose={this.props.onClose}>
+ <Modal contentLabel={header} onRequestClose={this.props.onClose} size="small">
<form>
<div className="modal-head">
<h2>{header}</h2>
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/components/RenameProfileForm.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/components/RenameProfileForm.tsx
index 1f3ebc9114b..714dda6cce8 100644
--- a/server/sonar-web/src/main/js/apps/quality-profiles/components/RenameProfileForm.tsx
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/components/RenameProfileForm.tsx
@@ -80,7 +80,7 @@ export default class RenameProfileForm extends React.PureComponent<Props, State>
this.state.loading || !this.state.name || this.state.name === profile.name;
return (
- <Modal contentLabel={header} onRequestClose={this.props.onClose}>
+ <Modal contentLabel={header} onRequestClose={this.props.onClose} size="small">
<form id="rename-profile-form" onSubmit={this.handleFormSubmit}>
<div className="modal-head">
<h2>{header}</h2>
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/components/__tests__/__snapshots__/ExtendProfileForm-test.tsx.snap b/server/sonar-web/src/main/js/apps/quality-profiles/components/__tests__/__snapshots__/ExtendProfileForm-test.tsx.snap
index f0dbf25d18c..d055f1c8968 100644
--- a/server/sonar-web/src/main/js/apps/quality-profiles/components/__tests__/__snapshots__/ExtendProfileForm-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/components/__tests__/__snapshots__/ExtendProfileForm-test.tsx.snap
@@ -4,6 +4,7 @@ exports[`should render correctly 1`] = `
<Modal
contentLabel="quality_profiles.extend_x_title.name.JavaScript"
onRequestClose={[MockFunction]}
+ size="small"
>
<form>
<div
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/details/ChangeParentForm.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/details/ChangeParentForm.tsx
index e44df26c5b5..1795c8e7045 100644
--- a/server/sonar-web/src/main/js/apps/quality-profiles/details/ChangeParentForm.tsx
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/details/ChangeParentForm.tsx
@@ -95,7 +95,8 @@ export default class ChangeParentForm extends React.PureComponent<Props, State>
return (
<Modal
contentLabel={translate('quality_profiles.change_parent')}
- onRequestClose={this.props.onClose}>
+ onRequestClose={this.props.onClose}
+ size="small">
<form id="change-profile-parent-form" onSubmit={this.handleFormSubmit}>
<div className="modal-head">
<h2>{translate('quality_profiles.change_parent')}</h2>
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfilePermissionsForm.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfilePermissionsForm.tsx
index 35539ffc368..2cb0872c786 100644
--- a/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfilePermissionsForm.tsx
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfilePermissionsForm.tsx
@@ -119,7 +119,7 @@ export default class ProfilePermissionsForm extends React.PureComponent<Props, S
</header>
<form onSubmit={this.handleFormSubmit}>
<div className="modal-body">
- <div className="modal-large-field">
+ <div className="modal-field">
<label>{translate('quality_profiles.search_description')}</label>
<ProfilePermissionsFormSelect
onChange={this.handleValueChange}
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfilePermissionsFormSelect.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfilePermissionsFormSelect.tsx
index acf1d98288d..9cf5a9df4b2 100644
--- a/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfilePermissionsFormSelect.tsx
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfilePermissionsFormSelect.tsx
@@ -122,15 +122,15 @@ function getStringValue(option: Option) {
function optionRenderer(option: OptionWithValue) {
return isUser(option) ? (
- <div>
+ <>
<Avatar hash={option.avatar} name={option.name} size={16} />
<strong className="spacer-left">{option.name}</strong>
<span className="note little-spacer-left">{option.login}</span>
- </div>
+ </>
) : (
- <div>
+ <>
<GroupIcon size={16} />
<strong className="spacer-left">{option.name}</strong>
- </div>
+ </>
);
}
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfilePermissionsForm-test.tsx.snap b/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfilePermissionsForm-test.tsx.snap
index 4caa41a48c1..8d1e901d79a 100644
--- a/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfilePermissionsForm-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfilePermissionsForm-test.tsx.snap
@@ -19,7 +19,7 @@ exports[`adds group 1`] = `
className="modal-body"
>
<div
- className="modal-large-field"
+ className="modal-field"
>
<label>
quality_profiles.search_description
@@ -67,7 +67,7 @@ exports[`adds group 2`] = `
className="modal-body"
>
<div
- className="modal-large-field"
+ className="modal-field"
>
<label>
quality_profiles.search_description
@@ -120,7 +120,7 @@ exports[`adds group 3`] = `
className="modal-body"
>
<div
- className="modal-large-field"
+ className="modal-field"
>
<label>
quality_profiles.search_description
@@ -176,7 +176,7 @@ exports[`adds user 1`] = `
className="modal-body"
>
<div
- className="modal-large-field"
+ className="modal-field"
>
<label>
quality_profiles.search_description
@@ -224,7 +224,7 @@ exports[`adds user 2`] = `
className="modal-body"
>
<div
- className="modal-large-field"
+ className="modal-field"
>
<label>
quality_profiles.search_description
@@ -277,7 +277,7 @@ exports[`adds user 3`] = `
className="modal-body"
>
<div
- className="modal-large-field"
+ className="modal-field"
>
<label>
quality_profiles.search_description
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/home/CreateProfileForm.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/home/CreateProfileForm.tsx
index 876f85a88a5..7b19310ca2f 100644
--- a/server/sonar-web/src/main/js/apps/quality-profiles/home/CreateProfileForm.tsx
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/home/CreateProfileForm.tsx
@@ -134,7 +134,7 @@ export default class CreateProfileForm extends React.PureComponent<Props, State>
}
return (
- <Modal contentLabel={header} onRequestClose={this.props.onClose}>
+ <Modal contentLabel={header} onRequestClose={this.props.onClose} size="small">
<form id="create-profile-form" onSubmit={this.handleFormSubmit}>
<div className="modal-head">
<h2>{header}</h2>
@@ -163,7 +163,7 @@ export default class CreateProfileForm extends React.PureComponent<Props, State>
value={this.state.name}
/>
</div>
- <div className="modal-field spacer-bottom">
+ <div className="modal-field">
<label htmlFor="create-profile-language">
{translate('language')}
<em className="mandatory">*</em>
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/home/RestoreProfileForm.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/home/RestoreProfileForm.tsx
index 51dcf1783f7..d8462260ce2 100644
--- a/server/sonar-web/src/main/js/apps/quality-profiles/home/RestoreProfileForm.tsx
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/home/RestoreProfileForm.tsx
@@ -79,13 +79,34 @@ export default class RestoreProfileForm extends React.PureComponent<Props, State
);
};
+ renderAlert(profile: { name: string }, ruleFailures = 0, ruleSuccesses: number): React.ReactNode {
+ return ruleFailures ? (
+ <Alert variant="warning">
+ {translateWithParameters(
+ 'quality_profiles.restore_profile.warning',
+ profile.name,
+ ruleSuccesses,
+ ruleFailures
+ )}
+ </Alert>
+ ) : (
+ <Alert variant="success">
+ {translateWithParameters(
+ 'quality_profiles.restore_profile.success',
+ profile.name,
+ ruleSuccesses
+ )}
+ </Alert>
+ );
+ }
+
render() {
const header = translate('quality_profiles.restore_profile');
const { loading, profile, ruleFailures, ruleSuccesses } = this.state;
return (
- <Modal contentLabel={header} onRequestClose={this.props.onClose}>
+ <Modal contentLabel={header} onRequestClose={this.props.onClose} size="small">
<form id="restore-profile-form" onSubmit={this.handleFormSubmit}>
<div className="modal-head">
<h2>{header}</h2>
@@ -93,24 +114,7 @@ export default class RestoreProfileForm extends React.PureComponent<Props, State
<div className="modal-body">
{profile != null && ruleSuccesses != null ? (
- ruleFailures ? (
- <Alert variant="warning">
- {translateWithParameters(
- 'quality_profiles.restore_profile.warning',
- profile.name,
- ruleSuccesses,
- ruleFailures
- )}
- </Alert>
- ) : (
- <Alert variant="success">
- {translateWithParameters(
- 'quality_profiles.restore_profile.success',
- profile.name,
- ruleSuccesses
- )}
- </Alert>
- )
+ this.renderAlert(profile, ruleFailures, ruleSuccesses)
) : (
<div className="modal-field">
<label htmlFor="restore-profile-backup">
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/home/__tests__/CreateProfileForm-test.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/home/__tests__/CreateProfileForm-test.tsx
new file mode 100644
index 00000000000..6d5c388e38b
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/home/__tests__/CreateProfileForm-test.tsx
@@ -0,0 +1,43 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+import * as React from 'react';
+import { shallow } from 'enzyme';
+import CreateProfileForm from '../CreateProfileForm';
+import { mockQualityProfile } from '../../../../helpers/testMocks';
+
+jest.mock('../../../../api/quality-profiles', () => ({
+ changeProfileParent: jest.fn(),
+ createQualityProfile: jest.fn(),
+ getImporters: jest.fn().mockResolvedValue([])
+}));
+
+it('should render correctly', () => {
+ expect(
+ shallow(
+ <CreateProfileForm
+ languages={[{ key: 'kr', name: 'Hangeul' }]}
+ onClose={jest.fn()}
+ onCreate={jest.fn()}
+ organization="org"
+ profiles={[mockQualityProfile()]}
+ />
+ )
+ ).toMatchSnapshot();
+});
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/home/__tests__/RestoreProfileForm-test.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/home/__tests__/RestoreProfileForm-test.tsx
new file mode 100644
index 00000000000..f5c082b3d3b
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/home/__tests__/RestoreProfileForm-test.tsx
@@ -0,0 +1,28 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+import * as React from 'react';
+import { shallow } from 'enzyme';
+import RestoreProfileForm from '../RestoreProfileForm';
+
+it('should render correctly', () => {
+ expect(
+ shallow(<RestoreProfileForm onClose={jest.fn()} onRestore={jest.fn()} organization="org" />)
+ ).toMatchSnapshot();
+});
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/home/__tests__/__snapshots__/CreateProfileForm-test.tsx.snap b/server/sonar-web/src/main/js/apps/quality-profiles/home/__tests__/__snapshots__/CreateProfileForm-test.tsx.snap
new file mode 100644
index 00000000000..e4c75d36c7e
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/home/__tests__/__snapshots__/CreateProfileForm-test.tsx.snap
@@ -0,0 +1,39 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render correctly 1`] = `
+<Modal
+ contentLabel="quality_profiles.new_profile"
+ onRequestClose={[MockFunction]}
+ size="small"
+>
+ <form
+ id="create-profile-form"
+ onSubmit={[Function]}
+ >
+ <div
+ className="modal-head"
+ >
+ <h2>
+ quality_profiles.new_profile
+ </h2>
+ </div>
+ <div
+ className="modal-body"
+ >
+ <i
+ className="spinner"
+ />
+ </div>
+ <div
+ className="modal-foot"
+ >
+ <ResetButtonLink
+ id="create-profile-cancel"
+ onClick={[MockFunction]}
+ >
+ cancel
+ </ResetButtonLink>
+ </div>
+ </form>
+</Modal>
+`;
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/home/__tests__/__snapshots__/RestoreProfileForm-test.tsx.snap b/server/sonar-web/src/main/js/apps/quality-profiles/home/__tests__/__snapshots__/RestoreProfileForm-test.tsx.snap
new file mode 100644
index 00000000000..565666717c6
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/home/__tests__/__snapshots__/RestoreProfileForm-test.tsx.snap
@@ -0,0 +1,62 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render correctly 1`] = `
+<Modal
+ contentLabel="quality_profiles.restore_profile"
+ onRequestClose={[MockFunction]}
+ size="small"
+>
+ <form
+ id="restore-profile-form"
+ onSubmit={[Function]}
+ >
+ <div
+ className="modal-head"
+ >
+ <h2>
+ quality_profiles.restore_profile
+ </h2>
+ </div>
+ <div
+ className="modal-body"
+ >
+ <div
+ className="modal-field"
+ >
+ <label
+ htmlFor="restore-profile-backup"
+ >
+ backup
+ <em
+ className="mandatory"
+ >
+ *
+ </em>
+ </label>
+ <input
+ id="restore-profile-backup"
+ name="backup"
+ required={true}
+ type="file"
+ />
+ </div>
+ </div>
+ <div
+ className="modal-foot"
+ >
+ <SubmitButton
+ disabled={false}
+ id="restore-profile-submit"
+ >
+ restore
+ </SubmitButton>
+ <ResetButtonLink
+ id="restore-profile-cancel"
+ onClick={[MockFunction]}
+ >
+ cancel
+ </ResetButtonLink>
+ </div>
+ </form>
+</Modal>
+`;
diff --git a/server/sonar-web/src/main/js/apps/settings/components/EmailForm.tsx b/server/sonar-web/src/main/js/apps/settings/components/EmailForm.tsx
index 19ad53f17b2..278937d62d4 100644
--- a/server/sonar-web/src/main/js/apps/settings/components/EmailForm.tsx
+++ b/server/sonar-web/src/main/js/apps/settings/components/EmailForm.tsx
@@ -18,12 +18,13 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
-import { translate, translateWithParameters } from '../../../helpers/l10n';
-import { sendTestEmail } from '../../../api/settings';
-import { parseError } from '../../../helpers/request';
-import { SubmitButton } from '../../../components/ui/buttons';
+import DeferredSpinner from '../../../components/common/DeferredSpinner';
import { Alert } from '../../../components/ui/Alert';
+import { SubmitButton } from '../../../components/ui/buttons';
import { withCurrentUser } from '../../../components/hoc/withCurrentUser';
+import { sendTestEmail } from '../../../api/settings';
+import { translate, translateWithParameters } from '../../../helpers/l10n';
+import { parseError } from '../../../helpers/request';
interface Props {
currentUser: T.LoggedInUser;
@@ -38,7 +39,7 @@ interface State {
error?: string;
}
-class EmailForm extends React.PureComponent<Props, State> {
+export class EmailForm extends React.PureComponent<Props, State> {
mounted = false;
constructor(props: Props) {
@@ -93,12 +94,16 @@ class EmailForm extends React.PureComponent<Props, State> {
render() {
return (
- <div className="huge-spacer-top">
- <h3 className="spacer-bottom">{translate('email_configuration.test.title')}</h3>
-
- <form onSubmit={this.handleFormSubmit} style={{ marginLeft: 201 }}>
+ <div className="settings-definition">
+ <div className="settings-definition-left">
+ <h3 className="settings-definition-name">
+ {translate('email_configuration.test.title')}
+ </h3>
+ </div>
+
+ <form className="settings-definition-right" onSubmit={this.handleFormSubmit}>
{this.state.success && (
- <div className="modal-field">
+ <div className="form-field">
<Alert variant="success">
{translateWithParameters(
'email_configuration.test.email_was_sent_to_x',
@@ -109,12 +114,12 @@ class EmailForm extends React.PureComponent<Props, State> {
)}
{this.state.error != null && (
- <div className="modal-field">
+ <div className="form-field">
<Alert variant="error">{this.state.error}</Alert>
</div>
)}
- <div className="modal-field">
+ <div className="form-field">
<label htmlFor="test-email-to">
{translate('email_configuration.test.to_address')}
<em className="mandatory">*</em>
@@ -129,7 +134,7 @@ class EmailForm extends React.PureComponent<Props, State> {
value={this.state.recipient}
/>
</div>
- <div className="modal-field">
+ <div className="form-field">
<label htmlFor="test-email-subject">
{translate('email_configuration.test.subject')}
</label>
@@ -142,7 +147,7 @@ class EmailForm extends React.PureComponent<Props, State> {
value={this.state.subject}
/>
</div>
- <div className="modal-field">
+ <div className="form-field">
<label htmlFor="test-email-message">
{translate('email_configuration.test.message')}
<em className="mandatory">*</em>
@@ -158,12 +163,10 @@ class EmailForm extends React.PureComponent<Props, State> {
/>
</div>
- <div className="modal-field">
- {this.state.loading && <i className="spacer-right spinner" />}
- <SubmitButton disabled={this.state.loading}>
- {translate('email_configuration.test.send')}
- </SubmitButton>
- </div>
+ <SubmitButton disabled={this.state.loading}>
+ {translate('email_configuration.test.send')}
+ </SubmitButton>
+ {this.state.loading && <DeferredSpinner className="spacer-left" />}
</form>
</div>
);
diff --git a/server/sonar-web/src/main/js/apps/settings/components/__tests__/EmailForm-test.tsx b/server/sonar-web/src/main/js/apps/settings/components/__tests__/EmailForm-test.tsx
new file mode 100644
index 00000000000..6c714a8d248
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/settings/components/__tests__/EmailForm-test.tsx
@@ -0,0 +1,27 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+import * as React from 'react';
+import { shallow } from 'enzyme';
+import { EmailForm } from '../EmailForm';
+import { mockCurrentUser } from '../../../../helpers/testMocks';
+
+it('should render correctly', () => {
+ expect(shallow(<EmailForm currentUser={mockCurrentUser()} />)).toMatchSnapshot();
+});
diff --git a/server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/EmailForm-test.tsx.snap b/server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/EmailForm-test.tsx.snap
new file mode 100644
index 00000000000..cc15cb3104d
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/EmailForm-test.tsx.snap
@@ -0,0 +1,90 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render correctly 1`] = `
+<div
+ className="settings-definition"
+>
+ <div
+ className="settings-definition-left"
+ >
+ <h3
+ className="settings-definition-name"
+ >
+ email_configuration.test.title
+ </h3>
+ </div>
+ <form
+ className="settings-definition-right"
+ onSubmit={[Function]}
+ >
+ <div
+ className="form-field"
+ >
+ <label
+ htmlFor="test-email-to"
+ >
+ email_configuration.test.to_address
+ <em
+ className="mandatory"
+ >
+ *
+ </em>
+ </label>
+ <input
+ className="settings-large-input"
+ disabled={false}
+ id="test-email-to"
+ onChange={[Function]}
+ required={true}
+ type="email"
+ value=""
+ />
+ </div>
+ <div
+ className="form-field"
+ >
+ <label
+ htmlFor="test-email-subject"
+ >
+ email_configuration.test.subject
+ </label>
+ <input
+ className="settings-large-input"
+ disabled={false}
+ id="test-email-subject"
+ onChange={[Function]}
+ type="text"
+ value="email_configuration.test.subject"
+ />
+ </div>
+ <div
+ className="form-field"
+ >
+ <label
+ htmlFor="test-email-message"
+ >
+ email_configuration.test.message
+ <em
+ className="mandatory"
+ >
+ *
+ </em>
+ </label>
+ <textarea
+ className="settings-large-input"
+ disabled={false}
+ id="test-email-message"
+ onChange={[Function]}
+ required={true}
+ rows={5}
+ value="email_configuration.test.message_text"
+ />
+ </div>
+ <SubmitButton
+ disabled={false}
+ >
+ email_configuration.test.send
+ </SubmitButton>
+ </form>
+</div>
+`;
diff --git a/server/sonar-web/src/main/js/apps/settings/encryption/EncryptionForm.tsx b/server/sonar-web/src/main/js/apps/settings/encryption/EncryptionForm.tsx
index 57254c2fefa..8cfd77b3938 100644
--- a/server/sonar-web/src/main/js/apps/settings/encryption/EncryptionForm.tsx
+++ b/server/sonar-web/src/main/js/apps/settings/encryption/EncryptionForm.tsx
@@ -89,11 +89,11 @@ export default class EncryptionForm extends React.PureComponent<Props, State> {
<form className="big-spacer-bottom" id="encryption-form" onSubmit={this.handleEncrypt}>
<textarea
autoFocus={true}
- className="input-super-large"
+ className="abs-width-600"
id="encryption-form-value"
onChange={this.handleChange}
required={true}
- rows={3}
+ rows={5}
value={this.state.value}
/>
<div className="spacer-top">
diff --git a/server/sonar-web/src/main/js/apps/tutorials/onboarding/OnboardingModal.tsx b/server/sonar-web/src/main/js/apps/tutorials/onboarding/OnboardingModal.tsx
index 1163399e78b..3051b630d83 100644
--- a/server/sonar-web/src/main/js/apps/tutorials/onboarding/OnboardingModal.tsx
+++ b/server/sonar-web/src/main/js/apps/tutorials/onboarding/OnboardingModal.tsx
@@ -32,18 +32,11 @@ export interface Props {
currentUser: T.LoggedInUser;
onClose: () => void;
onOpenProjectOnboarding: () => void;
- skipOnboarding: () => void;
userOrganizations: T.Organization[];
}
export function OnboardingModal(props: Props) {
- const {
- currentUser,
- onClose,
- onOpenProjectOnboarding,
- skipOnboarding,
- userOrganizations
- } = props;
+ const { currentUser, onClose, onOpenProjectOnboarding, userOrganizations } = props;
const organizations = userOrganizations.filter(o => o.key !== currentUser.personalOrganization);
@@ -51,9 +44,9 @@ export function OnboardingModal(props: Props) {
return (
<Modal
contentLabel={header}
- medium={true}
onRequestClose={onClose}
- shouldCloseOnOverlayClick={false}>
+ shouldCloseOnOverlayClick={false}
+ size={organizations.length > 0 ? 'medium' : 'small'}>
<div className="modal-head">
<h2>{translate('onboarding.header')}</h2>
<p className="spacer-top">{translate('onboarding.header.description')}</p>
@@ -61,9 +54,7 @@ export function OnboardingModal(props: Props) {
<div className="modal-body text-center display-flex-row huge-spacer-top huge-spacer-bottom">
<div className="flex-1">
<OnboardingProjectIcon className="big-spacer-bottom" />
- <h6 className="onboarding-choice-name big-spacer-bottom">
- {translate('onboarding.analyze_your_code')}
- </h6>
+ <h3 className="big-spacer-bottom">{translate('onboarding.analyze_your_code')}</h3>
<Button onClick={onOpenProjectOnboarding}>
{translate('onboarding.project.create')}
</Button>
@@ -75,13 +66,10 @@ export function OnboardingModal(props: Props) {
</div>
<div className="flex-1">
<OnboardingTeamIcon className="big-spacer-bottom" />
- <h6 className="onboarding-choice-name big-spacer-bottom">
+ <h3 className="big-spacer-bottom">
{translate('onboarding.browse_your_organizations')}
- </h6>
- <OrganizationsShortList
- organizations={organizations}
- skipOnboarding={skipOnboarding}
- />
+ </h3>
+ <OrganizationsShortList onClick={onClose} organizations={organizations} />
</div>
</>
)}
diff --git a/server/sonar-web/src/main/js/apps/tutorials/onboarding/OnboardingPage.tsx b/server/sonar-web/src/main/js/apps/tutorials/onboarding/OnboardingPage.tsx
index 5bda42d861b..52644d6b02e 100644
--- a/server/sonar-web/src/main/js/apps/tutorials/onboarding/OnboardingPage.tsx
+++ b/server/sonar-web/src/main/js/apps/tutorials/onboarding/OnboardingPage.tsx
@@ -42,7 +42,6 @@ export class OnboardingPage extends React.PureComponent<Props> {
<OnboardingModal
onClose={this.closeOnboarding}
onOpenProjectOnboarding={openProjectOnboarding}
- skipOnboarding={this.props.skipOnboarding}
/>
)}
</OnboardingContext.Consumer>
diff --git a/server/sonar-web/src/main/js/apps/tutorials/onboarding/OrganizationsShortList.tsx b/server/sonar-web/src/main/js/apps/tutorials/onboarding/OrganizationsShortList.tsx
index a7eecc85f6f..d7685c0cffb 100644
--- a/server/sonar-web/src/main/js/apps/tutorials/onboarding/OrganizationsShortList.tsx
+++ b/server/sonar-web/src/main/js/apps/tutorials/onboarding/OrganizationsShortList.tsx
@@ -25,10 +25,10 @@ import { translate, translateWithParameters } from '../../../helpers/l10n';
export interface Props {
organizations: T.Organization[];
- skipOnboarding: () => void;
+ onClick: () => void;
}
-export default function OrganizationsShortList({ organizations, skipOnboarding }: Props) {
+export default function OrganizationsShortList({ onClick, organizations }: Props) {
if (organizations.length === 0) {
return null;
}
@@ -39,26 +39,27 @@ export default function OrganizationsShortList({ organizations, skipOnboarding }
return (
<div>
- <ul className="account-projects-list">
+ <ul>
{organizationsShown.map(organization => (
<li key={organization.key}>
- <OrganizationsShortListItem
- organization={organization}
- skipOnboarding={skipOnboarding}
- />
+ <OrganizationsShortListItem onClick={onClick} organization={organization} />
</li>
))}
</ul>
- <div className="big-spacer-top">
- <span className="big-spacer-right">
- {translateWithParameters('x_of_y_shown', organizationsShown.length, organizations.length)}
- </span>
- {organizations.length > 3 && (
- <Link className="small" onClick={skipOnboarding} to="/account/organizations">
+ {organizations.length > 3 && (
+ <div className="big-spacer-top">
+ <span className="big-spacer-right">
+ {translateWithParameters(
+ 'x_of_y_shown',
+ organizationsShown.length,
+ organizations.length
+ )}
+ </span>
+ <Link className="small" onClick={onClick} to="/account/organizations">
{translate('see_all')}
</Link>
- )}
- </div>
+ </div>
+ )}
</div>
);
}
diff --git a/server/sonar-web/src/main/js/apps/tutorials/onboarding/OrganizationsShortListItem.tsx b/server/sonar-web/src/main/js/apps/tutorials/onboarding/OrganizationsShortListItem.tsx
index 49be2296cb9..c9740ebd5e8 100644
--- a/server/sonar-web/src/main/js/apps/tutorials/onboarding/OrganizationsShortListItem.tsx
+++ b/server/sonar-web/src/main/js/apps/tutorials/onboarding/OrganizationsShortListItem.tsx
@@ -24,15 +24,15 @@ import { withRouter, Router } from '../../../components/hoc/withRouter';
import { getOrganizationUrl } from '../../../helpers/urls';
interface Props {
+ onClick: () => void;
organization: T.Organization;
router: Router;
- skipOnboarding: () => void;
}
export class OrganizationsShortListItem extends React.PureComponent<Props> {
handleClick = () => {
- const { organization, router, skipOnboarding } = this.props;
- skipOnboarding();
+ const { onClick, organization, router } = this.props;
+ onClick();
router.push(getOrganizationUrl(organization.key));
};
diff --git a/server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/OnboardingModal-test.tsx b/server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/OnboardingModal-test.tsx
index 2521fc0cba8..ab03de8c5d3 100644
--- a/server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/OnboardingModal-test.tsx
+++ b/server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/OnboardingModal-test.tsx
@@ -59,7 +59,6 @@ function shallowRender(props: Partial<Props> = {}) {
currentUser={mockCurrentUser()}
onClose={jest.fn()}
onOpenProjectOnboarding={jest.fn()}
- skipOnboarding={jest.fn()}
userOrganizations={[]}
{...props}
/>
diff --git a/server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/OrganizationsShortList-test.tsx b/server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/OrganizationsShortList-test.tsx
index f0e9b24e4d3..f5f46c188c7 100644
--- a/server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/OrganizationsShortList-test.tsx
+++ b/server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/OrganizationsShortList-test.tsx
@@ -50,7 +50,5 @@ it('should limit displayed orgs to the first three', () => {
});
function shallowRender(props: Partial<Props> = {}) {
- return shallow(
- <OrganizationsShortList organizations={[]} skipOnboarding={jest.fn()} {...props} />
- );
+ return shallow(<OrganizationsShortList onClick={jest.fn()} organizations={[]} {...props} />);
}
diff --git a/server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/OrganizationsShortListItem-test.tsx b/server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/OrganizationsShortListItem-test.tsx
index bf84c7895ba..acc2ba83e8f 100644
--- a/server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/OrganizationsShortListItem-test.tsx
+++ b/server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/OrganizationsShortListItem-test.tsx
@@ -27,23 +27,23 @@ it('renders correctly', () => {
expect(shallowRender()).toMatchSnapshot();
});
-it('calls skiponboarding and redirects to org page', () => {
- const skipOnboarding = jest.fn();
+it('calls onClick and redirects to org page', () => {
+ const onClick = jest.fn();
const push = jest.fn();
- const wrapper = shallowRender({ skipOnboarding, router: mockRouter({ push }) });
+ const wrapper = shallowRender({ onClick, router: mockRouter({ push }) });
click(wrapper);
- expect(skipOnboarding).toHaveBeenCalledTimes(1);
+ expect(onClick).toHaveBeenCalledTimes(1);
expect(push).toHaveBeenCalledWith('/organizations/foo');
});
function shallowRender(props: Partial<OrganizationsShortListItem['props']> = {}) {
return shallow(
<OrganizationsShortListItem
+ onClick={jest.fn()}
organization={mockOrganization()}
router={mockRouter()}
- skipOnboarding={jest.fn()}
{...props}
/>
);
diff --git a/server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/__snapshots__/OnboardingModal-test.tsx.snap b/server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/__snapshots__/OnboardingModal-test.tsx.snap
index 1c9eb96f30e..bd1ed0ad54f 100644
--- a/server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/__snapshots__/OnboardingModal-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/__snapshots__/OnboardingModal-test.tsx.snap
@@ -3,9 +3,9 @@
exports[`renders correctly 1`] = `
<Modal
contentLabel="onboarding.header"
- medium={true}
onRequestClose={[MockFunction]}
shouldCloseOnOverlayClick={false}
+ size="small"
>
<div
className="modal-head"
@@ -28,11 +28,11 @@ exports[`renders correctly 1`] = `
<OnboardingProjectIcon
className="big-spacer-bottom"
/>
- <h6
- className="onboarding-choice-name big-spacer-bottom"
+ <h3
+ className="big-spacer-bottom"
>
onboarding.analyze_your_code
- </h6>
+ </h3>
<Button
onClick={[MockFunction]}
>
@@ -55,9 +55,9 @@ exports[`renders correctly 1`] = `
exports[`should display organization list if any 1`] = `
<Modal
contentLabel="onboarding.header"
- medium={true}
onRequestClose={[MockFunction]}
shouldCloseOnOverlayClick={false}
+ size="medium"
>
<div
className="modal-head"
@@ -80,11 +80,11 @@ exports[`should display organization list if any 1`] = `
<OnboardingProjectIcon
className="big-spacer-bottom"
/>
- <h6
- className="onboarding-choice-name big-spacer-bottom"
+ <h3
+ className="big-spacer-bottom"
>
onboarding.analyze_your_code
- </h6>
+ </h3>
<Button
onClick={[MockFunction]}
>
@@ -104,12 +104,13 @@ exports[`should display organization list if any 1`] = `
<OnboardingTeamIcon
className="big-spacer-bottom"
/>
- <h6
- className="onboarding-choice-name big-spacer-bottom"
+ <h3
+ className="big-spacer-bottom"
>
onboarding.browse_your_organizations
- </h6>
+ </h3>
<OrganizationsShortList
+ onClick={[MockFunction]}
organizations={
Array [
Object {
@@ -122,7 +123,6 @@ exports[`should display organization list if any 1`] = `
},
]
}
- skipOnboarding={[MockFunction]}
/>
</div>
</div>
diff --git a/server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/__snapshots__/OrganizationsShortList-test.tsx.snap b/server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/__snapshots__/OrganizationsShortList-test.tsx.snap
index 5410482f395..fb1121921b8 100644
--- a/server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/__snapshots__/OrganizationsShortList-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/__snapshots__/OrganizationsShortList-test.tsx.snap
@@ -2,46 +2,44 @@
exports[`should limit displayed orgs to the first three 1`] = `
<div>
- <ul
- className="account-projects-list"
- >
+ <ul>
<li
key="bar"
>
<withRouter(OrganizationsShortListItem)
+ onClick={[MockFunction]}
organization={
Object {
"key": "bar",
"name": "Bar",
}
}
- skipOnboarding={[MockFunction]}
/>
</li>
<li
key="foo"
>
<withRouter(OrganizationsShortListItem)
+ onClick={[MockFunction]}
organization={
Object {
"key": "foo",
"name": "Foo",
}
}
- skipOnboarding={[MockFunction]}
/>
</li>
<li
key="kor"
>
<withRouter(OrganizationsShortListItem)
+ onClick={[MockFunction]}
organization={
Object {
"key": "kor",
"name": "Kor",
}
}
- skipOnboarding={[MockFunction]}
/>
</li>
</ul>
@@ -68,44 +66,33 @@ exports[`should limit displayed orgs to the first three 1`] = `
exports[`should render correctly 1`] = `
<div>
- <ul
- className="account-projects-list"
- >
+ <ul>
<li
key="bar"
>
<withRouter(OrganizationsShortListItem)
+ onClick={[MockFunction]}
organization={
Object {
"key": "bar",
"name": "Bar",
}
}
- skipOnboarding={[MockFunction]}
/>
</li>
<li
key="foo"
>
<withRouter(OrganizationsShortListItem)
+ onClick={[MockFunction]}
organization={
Object {
"key": "foo",
"name": "Foo",
}
}
- skipOnboarding={[MockFunction]}
/>
</li>
</ul>
- <div
- className="big-spacer-top"
- >
- <span
- className="big-spacer-right"
- >
- x_of_y_shown.2.2
- </span>
- </div>
</div>
`;
diff --git a/server/sonar-web/src/main/js/apps/tutorials/styles.css b/server/sonar-web/src/main/js/apps/tutorials/styles.css
index 41cf98f083d..2a7946f0f1c 100644
--- a/server/sonar-web/src/main/js/apps/tutorials/styles.css
+++ b/server/sonar-web/src/main/js/apps/tutorials/styles.css
@@ -57,47 +57,3 @@
cursor: pointer;
outline: none;
}
-
-.onboarding-choices {
- display: flex;
- justify-content: space-around;
- padding: 44px 170px;
- background-color: var(--barBackgroundColor);
- margin-top: var(--pagePadding);
-}
-
-.onboarding-choice {
- display: flex;
- flex-direction: column;
- justify-content: center;
- padding: calc(2 * var(--gridSize));
- width: 190px;
- height: 190px;
- background-color: #fff;
- border: solid 1px #fff;
- border-radius: 3px;
- transition: all 0.2s ease;
- box-shadow: 0 1px 1px 1px var(--barBorderColor);
-}
-
-.onboarding-choice svg {
- color: var(--gray40);
-}
-
-.onboarding-choice-name {
- color: inherit;
- font-size: var(--mediumFontSize);
-}
-
-.onboarding-choice .note {
- font-weight: 400;
-}
-
-.onboarding-choice:hover,
-.onboarding-choice:focus,
-.onboarding-choice:active {
- background-color: #fff;
- color: var(--darkBlue);
- box-shadow: var(--defaultShadow);
- transform: translateY(-2px);
-}
diff --git a/server/sonar-web/src/main/js/apps/users/components/PasswordForm.tsx b/server/sonar-web/src/main/js/apps/users/components/PasswordForm.tsx
index 3bad28086b9..1e7b4cbe738 100644
--- a/server/sonar-web/src/main/js/apps/users/components/PasswordForm.tsx
+++ b/server/sonar-web/src/main/js/apps/users/components/PasswordForm.tsx
@@ -101,7 +101,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}>
+ <Modal contentLabel={header} onRequestClose={this.props.onClose} size="small">
<form autoComplete="off" id="user-password-form" onSubmit={this.handleChangePassword}>
<header className="modal-head">
<h2>{header}</h2>
diff --git a/server/sonar-web/src/main/js/apps/users/components/TokensForm.tsx b/server/sonar-web/src/main/js/apps/users/components/TokensForm.tsx
index c62c2f253de..ef7b4103bf1 100644
--- a/server/sonar-web/src/main/js/apps/users/components/TokensForm.tsx
+++ b/server/sonar-web/src/main/js/apps/users/components/TokensForm.tsx
@@ -154,7 +154,7 @@ export default class TokensForm extends React.PureComponent<Props, State> {
id="generate-token-form"
onSubmit={this.handleGenerateToken}>
<input
- className="spacer-right"
+ className="input-large spacer-right"
maxLength={100}
onChange={this.handleNewTokenChange}
placeholder={translate('users.enter_token_name')}
diff --git a/server/sonar-web/src/main/js/apps/users/components/UserForm.tsx b/server/sonar-web/src/main/js/apps/users/components/UserForm.tsx
index a96a27ea7c0..e5ec88fab7c 100644
--- a/server/sonar-web/src/main/js/apps/users/components/UserForm.tsx
+++ b/server/sonar-web/src/main/js/apps/users/components/UserForm.tsx
@@ -153,7 +153,7 @@ export default class UserForm extends React.PureComponent<Props, State> {
const header = user ? translate('users.update_user') : translate('users.create_user');
return (
- <Modal contentLabel={header} onRequestClose={this.props.onClose}>
+ <Modal contentLabel={header} onRequestClose={this.props.onClose} size="small">
<form
autoComplete="off"
id="user-form"
diff --git a/server/sonar-web/src/main/js/apps/users/components/UserScmAccountInput.tsx b/server/sonar-web/src/main/js/apps/users/components/UserScmAccountInput.tsx
index 331e642bd8e..a39e72ec8e3 100644
--- a/server/sonar-web/src/main/js/apps/users/components/UserScmAccountInput.tsx
+++ b/server/sonar-web/src/main/js/apps/users/components/UserScmAccountInput.tsx
@@ -35,7 +35,7 @@ export default class UserScmAccountInput extends React.PureComponent<Props> {
render() {
return (
- <div className="js-scm-account">
+ <div className="js-scm-account display-flex-row spacer-bottom">
<input
maxLength={255}
onChange={this.handleChange}
diff --git a/server/sonar-web/src/main/js/apps/users/components/__tests__/__snapshots__/TokensForm-test.tsx.snap b/server/sonar-web/src/main/js/apps/users/components/__tests__/__snapshots__/TokensForm-test.tsx.snap
index 2a6ae4b6be1..401bae31b0a 100644
--- a/server/sonar-web/src/main/js/apps/users/components/__tests__/__snapshots__/TokensForm-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/users/components/__tests__/__snapshots__/TokensForm-test.tsx.snap
@@ -14,7 +14,7 @@ exports[`should render correctly 1`] = `
onSubmit={[Function]}
>
<input
- className="spacer-right"
+ className="input-large spacer-right"
maxLength={100}
onChange={[Function]}
placeholder="users.enter_token_name"
@@ -90,7 +90,7 @@ exports[`should render correctly 2`] = `
onSubmit={[Function]}
>
<input
- className="spacer-right"
+ className="input-large spacer-right"
maxLength={100}
onChange={[Function]}
placeholder="users.enter_token_name"
diff --git a/server/sonar-web/src/main/js/apps/webhooks/components/CreateWebhookForm.tsx b/server/sonar-web/src/main/js/apps/webhooks/components/CreateWebhookForm.tsx
index 0d43e06afd0..d8a830062c9 100644
--- a/server/sonar-web/src/main/js/apps/webhooks/components/CreateWebhookForm.tsx
+++ b/server/sonar-web/src/main/js/apps/webhooks/components/CreateWebhookForm.tsx
@@ -74,6 +74,7 @@ export default class CreateWebhookForm extends React.PureComponent<Props> {
isInitialValid={isUpdate}
onClose={this.props.onClose}
onSubmit={this.props.onDone}
+ size="small"
validate={this.handleValidate}>
{({
dirty,
diff --git a/server/sonar-web/src/main/js/apps/webhooks/components/__tests__/__snapshots__/CreateWebhookForm-test.tsx.snap b/server/sonar-web/src/main/js/apps/webhooks/components/__tests__/__snapshots__/CreateWebhookForm-test.tsx.snap
index 489d1aecb6f..b9b3eda1ca3 100644
--- a/server/sonar-web/src/main/js/apps/webhooks/components/__tests__/__snapshots__/CreateWebhookForm-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/webhooks/components/__tests__/__snapshots__/CreateWebhookForm-test.tsx.snap
@@ -13,6 +13,7 @@ exports[`should render correctly when creating a new webhook 1`] = `
isInitialValid={false}
onClose={[MockFunction]}
onSubmit={[MockFunction]}
+ size="small"
validate={[Function]}
>
<Component />
@@ -32,6 +33,7 @@ exports[`should render correctly when updating a webhook 1`] = `
isInitialValid={true}
onClose={[MockFunction]}
onSubmit={[MockFunction]}
+ size="small"
validate={[Function]}
>
<Component />
diff --git a/server/sonar-web/src/main/js/components/SourceViewer/components/MeasuresOverlay.tsx b/server/sonar-web/src/main/js/components/SourceViewer/components/MeasuresOverlay.tsx
index e34b22faf4d..9f6834cfa65 100644
--- a/server/sonar-web/src/main/js/components/SourceViewer/components/MeasuresOverlay.tsx
+++ b/server/sonar-web/src/main/js/components/SourceViewer/components/MeasuresOverlay.tsx
@@ -388,7 +388,7 @@ export default class MeasuresOverlay extends React.PureComponent<Props, State> {
const { loading } = this.state;
return (
- <Modal contentLabel="" large={true} onRequestClose={this.props.onClose}>
+ <Modal contentLabel="" onRequestClose={this.props.onClose} size={'large'}>
<div className="modal-container source-viewer-measures-modal">
<div className="source-viewer-header-component source-viewer-measures-component">
<div className="source-viewer-header-component-project">
diff --git a/server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/__snapshots__/MeasuresOverlay-test.tsx.snap b/server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/__snapshots__/MeasuresOverlay-test.tsx.snap
index 71583aa014b..fb658fc95a2 100644
--- a/server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/__snapshots__/MeasuresOverlay-test.tsx.snap
+++ b/server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/__snapshots__/MeasuresOverlay-test.tsx.snap
@@ -3,8 +3,8 @@
exports[`should render source file 1`] = `
<Modal
contentLabel=""
- large={true}
onRequestClose={[MockFunction]}
+ size="large"
>
<div
className="modal-container source-viewer-measures-modal"
@@ -362,8 +362,8 @@ exports[`should render source file 1`] = `
exports[`should render source file 2`] = `
<Modal
contentLabel=""
- large={true}
onRequestClose={[MockFunction]}
+ size="large"
>
<div
className="modal-container source-viewer-measures-modal"
@@ -1327,8 +1327,8 @@ exports[`should render source file 2`] = `
exports[`should render test file 1`] = `
<Modal
contentLabel=""
- large={true}
onRequestClose={[MockFunction]}
+ size="large"
>
<div
className="modal-container source-viewer-measures-modal"
diff --git a/server/sonar-web/src/main/js/components/common/MarkdownTips.tsx b/server/sonar-web/src/main/js/components/common/MarkdownTips.tsx
index 1007498f19b..2c99b2f74a4 100644
--- a/server/sonar-web/src/main/js/components/common/MarkdownTips.tsx
+++ b/server/sonar-web/src/main/js/components/common/MarkdownTips.tsx
@@ -18,10 +18,15 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
+import * as classNames from 'classnames';
import { getMarkdownHelpUrl } from '../../helpers/urls';
import { translate } from '../../helpers/l10n';
-export default class MarkdownTips extends React.PureComponent {
+interface Props {
+ className?: string;
+}
+
+export default class MarkdownTips extends React.PureComponent<Props> {
handleClick(evt: React.SyntheticEvent<HTMLAnchorElement>) {
evt.preventDefault();
window.open(getMarkdownHelpUrl(), 'Markdown', 'height=300,width=600,scrollbars=1,resizable=1');
@@ -29,7 +34,7 @@ export default class MarkdownTips extends React.PureComponent {
render() {
return (
- <div className="markdown-tips">
+ <div className={classNames('markdown-tips', this.props.className)}>
<a className="little-spacer-right" href="#" onClick={this.handleClick}>
{translate('markdown.helplink')}
</a>
diff --git a/server/sonar-web/src/main/js/components/controls/Checkbox.tsx b/server/sonar-web/src/main/js/components/controls/Checkbox.tsx
index e9e6a94f4b4..a1df816ed01 100644
--- a/server/sonar-web/src/main/js/components/controls/Checkbox.tsx
+++ b/server/sonar-web/src/main/js/components/controls/Checkbox.tsx
@@ -29,6 +29,7 @@ interface Props {
id?: string;
loading?: boolean;
onCheck: (checked: boolean, id?: string) => void;
+ right?: boolean;
thirdState?: boolean;
}
@@ -46,31 +47,33 @@ export default class Checkbox extends React.PureComponent<Props> {
};
render() {
+ const { children, disabled, loading, right } = this.props;
const className = classNames('icon-checkbox', {
'icon-checkbox-checked': this.props.checked,
'icon-checkbox-single': this.props.thirdState,
- 'icon-checkbox-disabled': this.props.disabled
+ 'icon-checkbox-disabled': disabled
});
- if (this.props.children) {
+ if (children) {
return (
<a
className={classNames('link-checkbox', this.props.className, {
- note: this.props.disabled,
- disabled: this.props.disabled
+ note: disabled,
+ disabled
})}
href="#"
id={this.props.id}
onClick={this.handleClick}>
- <DeferredSpinner loading={Boolean(this.props.loading)}>
+ {right && children}
+ <DeferredSpinner loading={Boolean(loading)}>
<i className={className} />
</DeferredSpinner>
- {this.props.children}
+ {!right && children}
</a>
);
}
- if (this.props.loading) {
+ if (loading) {
return <DeferredSpinner />;
}
diff --git a/server/sonar-web/src/main/js/components/controls/ConfirmModal.tsx b/server/sonar-web/src/main/js/components/controls/ConfirmModal.tsx
index a86ac592a73..2b018709630 100644
--- a/server/sonar-web/src/main/js/components/controls/ConfirmModal.tsx
+++ b/server/sonar-web/src/main/js/components/controls/ConfirmModal.tsx
@@ -94,8 +94,8 @@ export default class ConfirmModal<T = string> extends React.PureComponent<Props<
};
render() {
- const { header, onClose, medium, noBackdrop, large } = this.props;
- const modalProps = { header, onClose, medium, noBackdrop, large };
+ const { header, onClose, noBackdrop, size } = this.props;
+ const modalProps = { header, onClose, noBackdrop, size };
return (
<SimpleModal onSubmit={this.handleSubmit} {...modalProps}>
{this.renderModalContent}
diff --git a/server/sonar-web/src/main/js/components/controls/Modal.tsx b/server/sonar-web/src/main/js/components/controls/Modal.tsx
index e3beac1109b..c250470f9af 100644
--- a/server/sonar-web/src/main/js/components/controls/Modal.tsx
+++ b/server/sonar-web/src/main/js/components/controls/Modal.tsx
@@ -20,15 +20,13 @@
import * as React from 'react';
import * as ReactModal from 'react-modal';
import * as classNames from 'classnames';
-import { isSonarCloud } from '../../helpers/system';
ReactModal.setAppElement('#content');
export interface ModalProps {
children: React.ReactNode;
- medium?: boolean;
+ size?: 'small' | 'medium' | 'large';
noBackdrop?: boolean;
- large?: boolean;
}
type MandatoryProps = Pick<ReactModal.Props, 'contentLabel'>;
@@ -38,16 +36,11 @@ type Props = Partial<ReactModal.Props> & MandatoryProps & ModalProps;
export default function Modal(props: Props) {
return (
<ReactModal
- className={classNames(
- 'modal',
- {
- sonarcloud: isSonarCloud()
- },
- {
- 'modal-medium': props.medium,
- 'modal-large': props.large
- }
- )}
+ className={classNames('modal', {
+ 'modal-small': props.size === 'small',
+ 'modal-medium': props.size === 'medium',
+ 'modal-large': props.size === 'large'
+ })}
isOpen={true}
overlayClassName={classNames('modal-overlay', { 'modal-no-backdrop': props.noBackdrop })}
{...props}
diff --git a/server/sonar-web/src/main/js/components/controls/Radio.tsx b/server/sonar-web/src/main/js/components/controls/Radio.tsx
index e389c71d664..ba030d96b61 100644
--- a/server/sonar-web/src/main/js/components/controls/Radio.tsx
+++ b/server/sonar-web/src/main/js/components/controls/Radio.tsx
@@ -38,10 +38,7 @@ export default class Radio extends React.PureComponent<Props> {
return (
<a
aria-checked={this.props.checked}
- className={classNames(
- 'display-inline-flex-center link-base-color link-no-underline',
- this.props.className
- )}
+ className={classNames('display-inline-flex-center link-checkbox', this.props.className)}
href="#"
onClick={this.handleClick}
role="radio">
diff --git a/server/sonar-web/src/main/js/components/controls/SearchSelect.tsx b/server/sonar-web/src/main/js/components/controls/SearchSelect.tsx
index 3a78bd4ea42..43558ccdf90 100644
--- a/server/sonar-web/src/main/js/components/controls/SearchSelect.tsx
+++ b/server/sonar-web/src/main/js/components/controls/SearchSelect.tsx
@@ -25,6 +25,7 @@ import { translate, translateWithParameters } from '../../helpers/l10n';
interface Props<T> {
autofocus?: boolean;
canCreate?: boolean;
+ className?: string;
clearable?: boolean;
defaultOptions?: T[];
minimumQueryLength?: number;
@@ -126,7 +127,7 @@ export default class SearchSelect<T extends { value: string }> extends React.Pur
return (
<Component
autoFocus={this.autofocus}
- className="input-super-large"
+ className={this.props.className}
clearable={this.props.clearable}
escapeClearsValue={false}
filterOption={this.handleFilterOption}
diff --git a/server/sonar-web/src/main/js/components/controls/ValidationModal.tsx b/server/sonar-web/src/main/js/components/controls/ValidationModal.tsx
index d4138956752..d359d8daebf 100644
--- a/server/sonar-web/src/main/js/components/controls/ValidationModal.tsx
+++ b/server/sonar-web/src/main/js/components/controls/ValidationModal.tsx
@@ -18,13 +18,13 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
-import Modal from './Modal';
+import Modal, { ModalProps } from './Modal';
import ValidationForm, { ChildrenProps } from './ValidationForm';
import DeferredSpinner from '../common/DeferredSpinner';
import { SubmitButton, ResetButtonLink } from '../ui/buttons';
import { translate } from '../../helpers/l10n';
-interface Props<V> {
+interface Props<V> extends ModalProps {
children: (props: ChildrenProps<V>) => React.ReactNode;
confirmButtonText: string;
header: string;
@@ -44,7 +44,11 @@ export default class ValidationModal<V> extends React.PureComponent<Props<V>> {
render() {
return (
- <Modal contentLabel={this.props.header} onRequestClose={this.props.onClose}>
+ <Modal
+ contentLabel={this.props.header}
+ noBackdrop={this.props.noBackdrop}
+ onRequestClose={this.props.onClose}
+ size={this.props.size}>
<ValidationForm
initialValues={this.props.initialValues}
isInitialValid={this.props.isInitialValid}
diff --git a/server/sonar-web/src/main/js/components/controls/__tests__/Checkbox-test.tsx b/server/sonar-web/src/main/js/components/controls/__tests__/Checkbox-test.tsx
index a0985d44a84..f089b43dfa5 100644
--- a/server/sonar-web/src/main/js/components/controls/__tests__/Checkbox-test.tsx
+++ b/server/sonar-web/src/main/js/components/controls/__tests__/Checkbox-test.tsx
@@ -102,3 +102,7 @@ it('should apply custom class', () => {
);
expect(checkbox.is('.customclass')).toBeTruthy();
});
+
+it('should render the checkbox on the right', () => {
+ expect(shallow(<Checkbox checked={true} onCheck={() => true} right={true} />)).toMatchSnapshot();
+});
diff --git a/server/sonar-web/src/main/js/components/controls/__tests__/__snapshots__/Checkbox-test.tsx.snap b/server/sonar-web/src/main/js/components/controls/__tests__/__snapshots__/Checkbox-test.tsx.snap
new file mode 100644
index 00000000000..f733f88a916
--- /dev/null
+++ b/server/sonar-web/src/main/js/components/controls/__tests__/__snapshots__/Checkbox-test.tsx.snap
@@ -0,0 +1,9 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render the checkbox on the right 1`] = `
+<a
+ className="icon-checkbox icon-checkbox-checked"
+ href="#"
+ onClick={[Function]}
+/>
+`;
diff --git a/server/sonar-web/src/main/js/components/controls/__tests__/__snapshots__/Radio-test.tsx.snap b/server/sonar-web/src/main/js/components/controls/__tests__/__snapshots__/Radio-test.tsx.snap
index 92e8076ce5e..cd6bc65b080 100644
--- a/server/sonar-web/src/main/js/components/controls/__tests__/__snapshots__/Radio-test.tsx.snap
+++ b/server/sonar-web/src/main/js/components/controls/__tests__/__snapshots__/Radio-test.tsx.snap
@@ -3,7 +3,7 @@
exports[`should render and check 1`] = `
<a
aria-checked={false}
- className="display-inline-flex-center link-base-color link-no-underline"
+ className="display-inline-flex-center link-checkbox"
href="#"
onClick={[Function]}
role="radio"
@@ -17,7 +17,7 @@ exports[`should render and check 1`] = `
exports[`should render and check 2`] = `
<a
aria-checked={true}
- className="display-inline-flex-center link-base-color link-no-underline"
+ className="display-inline-flex-center link-checkbox"
href="#"
onClick={[Function]}
role="radio"
diff --git a/server/sonar-web/src/main/js/components/controls/__tests__/__snapshots__/SearchSelect-test.tsx.snap b/server/sonar-web/src/main/js/components/controls/__tests__/__snapshots__/SearchSelect-test.tsx.snap
index 9f99087e010..792343f1a73 100644
--- a/server/sonar-web/src/main/js/components/controls/__tests__/__snapshots__/SearchSelect-test.tsx.snap
+++ b/server/sonar-web/src/main/js/components/controls/__tests__/__snapshots__/SearchSelect-test.tsx.snap
@@ -3,7 +3,6 @@
exports[`should render Select 1`] = `
<Select
autoFocus={true}
- className="input-super-large"
escapeClearsValue={false}
filterOption={[Function]}
isLoading={false}
diff --git a/server/sonar-web/src/main/js/components/icons-components/OnboardingAddMembersIcon.tsx b/server/sonar-web/src/main/js/components/icons-components/OnboardingAddMembersIcon.tsx
index 0d6371cfaef..f0e0f2db583 100644
--- a/server/sonar-web/src/main/js/components/icons-components/OnboardingAddMembersIcon.tsx
+++ b/server/sonar-web/src/main/js/components/icons-components/OnboardingAddMembersIcon.tsx
@@ -19,10 +19,11 @@
*/
import * as React from 'react';
import Icon, { IconProps } from './Icon';
+import * as theme from '../../app/theme';
export default function OnboardingAddMembersIcon({
className,
- fill = 'currentColor',
+ fill = theme.darkBlue,
size = 64
}: IconProps) {
return (
diff --git a/server/sonar-web/src/main/js/components/ui/NewInfoBox.tsx b/server/sonar-web/src/main/js/components/ui/NewInfoBox.tsx
deleted file mode 100644
index c9a2da3e401..00000000000
--- a/server/sonar-web/src/main/js/components/ui/NewInfoBox.tsx
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2019 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-import * as React from 'react';
-import * as classNames from 'classnames';
-import { ButtonIcon } from './buttons';
-import ClearIcon from '../icons-components/ClearIcon';
-import { sonarcloudBlack500 } from '../../app/theme';
-import { translate } from '../../helpers/l10n';
-import './NewInfoBox.css';
-
-export interface Props {
- children: React.ReactNode;
- className?: string;
- description: React.ReactNode;
- onClose?: () => void;
- title: string;
-}
-
-export default function NewInfoBox({ className, children, description, onClose, title }: Props) {
- return (
- <div className={classNames('new-info-box', className)} role="alert">
- <div className="new-info-box-inner text-left">
- <div className="new-info-box-header spacer-bottom">
- <span className="display-inline-flex-center">
- <span className="badge badge-new spacer-right">{translate('new')}</span>
- <strong>{title}</strong>
- </span>
- </div>
- <p className="note spacer-bottom">{description}</p>
- {children}
- </div>
- <ButtonIcon className="button-small spacer-left" color={sonarcloudBlack500} onClick={onClose}>
- <ClearIcon size={12} />
- </ButtonIcon>
- </div>
- );
-}
diff --git a/server/sonar-web/src/main/js/components/ui/__tests__/__snapshots__/NewInfoBox-test.tsx.snap b/server/sonar-web/src/main/js/components/ui/__tests__/__snapshots__/NewInfoBox-test.tsx.snap
deleted file mode 100644
index fca669ba00a..00000000000
--- a/server/sonar-web/src/main/js/components/ui/__tests__/__snapshots__/NewInfoBox-test.tsx.snap
+++ /dev/null
@@ -1,44 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render correctly 1`] = `
-<div
- className="new-info-box"
- role="alert"
->
- <div
- className="new-info-box-inner text-left"
- >
- <div
- className="new-info-box-header spacer-bottom"
- >
- <span
- className="display-inline-flex-center"
- >
- <span
- className="badge badge-new spacer-right"
- >
- new
- </span>
- <strong>
- My title
- </strong>
- </span>
- </div>
- <p
- className="note spacer-bottom"
- >
- My description
- </p>
- <div />
- </div>
- <ButtonIcon
- className="button-small spacer-left"
- color="#8a8c8f"
- onClick={[MockFunction]}
- >
- <ClearIcon
- size={12}
- />
- </ButtonIcon>
-</div>
-`;
diff --git a/server/sonar-web/src/main/js/components/ui/buttons.css b/server/sonar-web/src/main/js/components/ui/buttons.css
index cede4ec4d10..93b755a2230 100644
--- a/server/sonar-web/src/main/js/components/ui/buttons.css
+++ b/server/sonar-web/src/main/js/components/ui/buttons.css
@@ -174,6 +174,27 @@
font-size: var(--mediumFontSize);
}
+.button-huge {
+ flex-direction: column;
+ padding: calc(2 * var(--gridSize));
+ width: 200px;
+ height: 200px;
+ background-color: #fff;
+ border: solid 1px #fff;
+ border-radius: 3px;
+ transition: all 0.2s ease;
+ box-shadow: 0 1px 1px 1px var(--barBorderColor);
+}
+
+.button-huge:hover,
+.button-huge:focus,
+.button-huge:active {
+ background-color: #fff;
+ color: var(--darkBlue);
+ box-shadow: var(--defaultShadow);
+ transform: translateY(-2px);
+}
+
/* #region .button-group */
.button-group {
display: inline-block;
@@ -271,12 +292,12 @@
/* #endregion */
.button-list {
+ display: inline-flex;
+ justify-content: space-between;
height: auto;
border: 1px solid var(--barBorderColor);
padding: var(--gridSize);
margin: calc(var(--gridSize) / 2);
- text-align: left;
- justify-content: space-between;
color: var(--secondFontColor);
font-weight: normal;
}
@@ -285,5 +306,4 @@
background-color: white;
border-color: var(--blue);
color: var(--darkBlue);
- box-shadow: 0 4px 6px rgba(0, 0, 0, 0.175);
}
diff --git a/server/sonar-web/src/main/js/components/ui/buttons.tsx b/server/sonar-web/src/main/js/components/ui/buttons.tsx
index 99e4c327a62..5ad0b9197ef 100644
--- a/server/sonar-web/src/main/js/components/ui/buttons.tsx
+++ b/server/sonar-web/src/main/js/components/ui/buttons.tsx
@@ -20,11 +20,11 @@
import * as React from 'react';
import * as classNames from 'classnames';
import * as theme from '../../app/theme';
+import ChevronRightIcon from '../icons-components/ChevronRightcon';
import ClearIcon from '../icons-components/ClearIcon';
import EditIcon from '../icons-components/EditIcon';
import Tooltip from '../controls/Tooltip';
import './buttons.css';
-import ChevronRightIcon from '../icons-components/ChevronRightcon';
type AllowedButtonAttributes = Pick<
React.ButtonHTMLAttributes<HTMLButtonElement>,
diff --git a/server/sonar-web/src/main/js/helpers/testMocks.ts b/server/sonar-web/src/main/js/helpers/testMocks.ts
index bd37528c205..a6e20a88178 100644
--- a/server/sonar-web/src/main/js/helpers/testMocks.ts
+++ b/server/sonar-web/src/main/js/helpers/testMocks.ts
@@ -21,6 +21,17 @@ import { InjectedRouter } from 'react-router';
import { Location } from 'history';
import { Profile } from '../apps/quality-profiles/types';
+export function mockAlmApplication(overrides: Partial<T.AlmApplication> = {}): T.AlmApplication {
+ return {
+ backgroundColor: '#0052CC',
+ iconPath: '"/static/authbitbucket/bitbucket.svg"',
+ installationUrl: 'https://bitbucket.org/install/app',
+ key: 'bitbucket',
+ name: 'BitBucket',
+ ...overrides
+ };
+}
+
export function mockAlmOrganization(overrides: Partial<T.AlmOrganization> = {}): T.AlmOrganization {
return {
avatar: 'http://example.com/avatar',
@@ -90,6 +101,52 @@ export function mockEvent(overrides = {}) {
} as any;
}
+export function mockIssue(withLocations = false, overrides: Partial<T.Issue> = {}) {
+ const issue: T.Issue = {
+ actions: [],
+ component: 'main.js',
+ componentLongName: 'main.js',
+ componentQualifier: 'FIL',
+ componentUuid: 'foo1234',
+ creationDate: '2017-03-01T09:36:01+0100',
+ flows: [],
+ fromHotspot: false,
+ key: 'AVsae-CQS-9G3txfbFN2',
+ line: 25,
+ message: 'Reduce the number of conditional operators (4) used in the expression',
+ organization: 'myorg',
+ project: 'myproject',
+ projectKey: 'foo',
+ projectName: 'Foo',
+ projectOrganization: 'org',
+ rule: 'javascript:S1067',
+ ruleName: 'foo',
+ secondaryLocations: [],
+ severity: 'MAJOR',
+ status: 'OPEN',
+ textRange: { startLine: 25, endLine: 26, startOffset: 0, endOffset: 15 },
+ transitions: [],
+ type: 'BUG'
+ };
+
+ function loc(): T.FlowLocation {
+ return {
+ component: 'main.js',
+ textRange: { startLine: 1, startOffset: 1, endLine: 2, endOffset: 2 }
+ };
+ }
+
+ if (withLocations) {
+ issue.flows = [[loc(), loc(), loc()], [loc(), loc()]];
+ issue.secondaryLocations = [loc(), loc()];
+ }
+
+ return {
+ ...issue,
+ ...overrides
+ };
+}
+
export function mockLocation(overrides: Partial<Location> = {}): Location {
return {
action: 'PUSH',
@@ -123,6 +180,14 @@ export function mockOrganizationWithAlm(
});
}
+export function mockQualityGate(overrides: Partial<T.QualityGate> = {}): T.QualityGate {
+ return {
+ id: 1,
+ name: 'qualitygate',
+ ...overrides
+ };
+}
+
export function mockQualityProfile(overrides: Partial<Profile> = {}): Profile {
return {
activeDeprecatedRuleCount: 2,
@@ -157,48 +222,17 @@ export function mockRouter(overrides: { push?: Function; replace?: Function } =
} as InjectedRouter;
}
-export function mockIssue(withLocations = false, overrides: Partial<T.Issue> = {}) {
- const issue: T.Issue = {
- actions: [],
- component: 'main.js',
- componentLongName: 'main.js',
- componentQualifier: 'FIL',
- componentUuid: 'foo1234',
- creationDate: '2017-03-01T09:36:01+0100',
- flows: [],
- fromHotspot: false,
- key: 'AVsae-CQS-9G3txfbFN2',
- line: 25,
- message: 'Reduce the number of conditional operators (4) used in the expression',
- organization: 'myorg',
- project: 'myproject',
- projectKey: 'foo',
- projectName: 'Foo',
- projectOrganization: 'org',
- rule: 'javascript:S1067',
- ruleName: 'foo',
- secondaryLocations: [],
- severity: 'MAJOR',
- status: 'OPEN',
- textRange: { startLine: 25, endLine: 26, startOffset: 0, endOffset: 15 },
- transitions: [],
- type: 'BUG'
- };
-
- function loc(): T.FlowLocation {
- return {
- component: 'main.js',
- textRange: { startLine: 1, startOffset: 1, endLine: 2, endOffset: 2 }
- };
- }
-
- if (withLocations) {
- issue.flows = [[loc(), loc(), loc()], [loc(), loc()]];
- issue.secondaryLocations = [loc(), loc()];
- }
-
+export function mockRule(overrides: Partial<T.Rule> = {}): T.Rule {
return {
- ...issue,
+ key: 'javascript:S1067',
+ lang: 'js',
+ langName: 'JavaScript',
+ name: 'Use foo',
+ severity: 'MAJOR',
+ status: 'READY',
+ sysTags: ['a', 'b'],
+ tags: ['x'],
+ type: 'CODE_SMELL',
...overrides
- };
+ } as T.Rule;
}