aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--server/sonar-web/.eslintrc2
-rw-r--r--server/sonar-web/config/indexHtmlTemplate.js2
-rw-r--r--server/sonar-web/design-system/.eslintrc2
-rw-r--r--server/sonar-web/design-system/src/components/Banner.tsx9
-rw-r--r--server/sonar-web/design-system/src/components/Spinner.tsx15
-rw-r--r--server/sonar-web/design-system/src/components/__tests__/__snapshots__/CodeSnippet-test.tsx.snap18
-rw-r--r--server/sonar-web/design-system/src/components/buttons/ButtonSecondary.tsx6
-rw-r--r--server/sonar-web/design-system/src/components/buttons/DangerButtonSecondary.tsx6
-rw-r--r--server/sonar-web/design-system/src/components/input/DatePicker.tsx10
-rw-r--r--server/sonar-web/design-system/src/components/input/InputField.tsx11
-rw-r--r--server/sonar-web/design-system/src/components/input/RadioButton.tsx6
-rw-r--r--server/sonar-web/design-system/src/components/modal/Modal.tsx4
-rw-r--r--server/sonar-web/eslint-local-rules/__tests__/no-conditional-rendering-of-spinner-test.js (renamed from server/sonar-web/eslint-local-rules/__tests__/no-conditional-rendering-of-deferredspinner-test.js)48
-rw-r--r--server/sonar-web/eslint-local-rules/index.js2
-rw-r--r--server/sonar-web/eslint-local-rules/no-conditional-rendering-of-spinner.js (renamed from server/sonar-web/eslint-local-rules/no-conditional-rendering-of-deferredspinner.js)16
-rw-r--r--server/sonar-web/src/main/js/app/components/AlmSynchronisationWarning.tsx1
-rw-r--r--server/sonar-web/src/main/js/app/components/ComponentContainerNotFound.tsx24
-rw-r--r--server/sonar-web/src/main/js/app/components/DocumentationRedirect.tsx12
-rw-r--r--server/sonar-web/src/main/js/app/components/GitHubSynchronisationWarning.tsx1
-rw-r--r--server/sonar-web/src/main/js/app/components/GitLabSynchronisationWarning.tsx1
-rw-r--r--server/sonar-web/src/main/js/app/components/GlobalContainer.tsx52
-rw-r--r--server/sonar-web/src/main/js/app/components/LicensePromptModal.tsx22
-rw-r--r--server/sonar-web/src/main/js/app/components/NonAdminPagesContainer.tsx25
-rw-r--r--server/sonar-web/src/main/js/app/components/PluginRiskConsent.css35
-rw-r--r--server/sonar-web/src/main/js/app/components/PluginRiskConsent.tsx36
-rw-r--r--server/sonar-web/src/main/js/app/components/SonarLintConnection.css53
-rw-r--r--server/sonar-web/src/main/js/app/components/SonarLintConnection.tsx231
-rw-r--r--server/sonar-web/src/main/js/app/components/SystemAnnouncement.css29
-rw-r--r--server/sonar-web/src/main/js/app/components/SystemAnnouncement.tsx37
-rw-r--r--server/sonar-web/src/main/js/app/components/__tests__/NonAdminPagesContainer-test.tsx4
-rw-r--r--server/sonar-web/src/main/js/app/components/__tests__/SonarLintConnection-test.tsx2
-rw-r--r--server/sonar-web/src/main/js/app/components/extensions/Extension.tsx25
-rw-r--r--server/sonar-web/src/main/js/app/components/indexation/IndexationNotification.css28
-rw-r--r--server/sonar-web/src/main/js/app/components/indexation/IndexationNotification.tsx1
-rw-r--r--server/sonar-web/src/main/js/app/components/indexation/IndexationNotificationRenderer.tsx111
-rw-r--r--server/sonar-web/src/main/js/app/components/indexation/PageUnavailableDueToIndexation.tsx8
-rw-r--r--server/sonar-web/src/main/js/app/components/indexation/__tests__/IndexationNotification-test.tsx2
-rw-r--r--server/sonar-web/src/main/js/app/components/nav/component/ComponentNavProjectBindingErrorNotif.tsx26
-rw-r--r--server/sonar-web/src/main/js/app/components/promotion-notification/PromotionNotification.tsx14
-rw-r--r--server/sonar-web/src/main/js/app/components/update-notification/UpdateNotification.css28
-rw-r--r--server/sonar-web/src/main/js/app/components/update-notification/UpdateNotification.tsx10
-rw-r--r--server/sonar-web/src/main/js/app/styles/GlobalStyles.tsx (renamed from server/sonar-web/src/main/js/components/ui/__tests__/DismissableAlertComponent-test.tsx)53
-rw-r--r--server/sonar-web/src/main/js/app/styles/components/badges.css83
-rw-r--r--server/sonar-web/src/main/js/app/styles/components/boxed-group.css29
-rw-r--r--server/sonar-web/src/main/js/app/styles/components/columns.css66
-rw-r--r--server/sonar-web/src/main/js/app/styles/components/component-name.css57
-rw-r--r--server/sonar-web/src/main/js/app/styles/components/dropdowns.css34
-rw-r--r--server/sonar-web/src/main/js/app/styles/components/global-loading.css41
-rw-r--r--server/sonar-web/src/main/js/app/styles/components/issues.css37
-rw-r--r--server/sonar-web/src/main/js/app/styles/components/list-groups.css87
-rw-r--r--server/sonar-web/src/main/js/app/styles/components/menu.css188
-rw-r--r--server/sonar-web/src/main/js/app/styles/components/page.css24
-rw-r--r--server/sonar-web/src/main/js/app/styles/components/panels.css42
-rw-r--r--server/sonar-web/src/main/js/app/styles/components/spinner.css69
-rw-r--r--server/sonar-web/src/main/js/app/styles/components/ui.css85
-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.css220
-rw-r--r--server/sonar-web/src/main/js/app/styles/init/icons.css66
-rw-r--r--server/sonar-web/src/main/js/app/styles/init/links.css53
-rw-r--r--server/sonar-web/src/main/js/app/styles/init/lists.css108
-rw-r--r--server/sonar-web/src/main/js/app/styles/init/misc.css445
-rw-r--r--server/sonar-web/src/main/js/app/styles/init/tables.css301
-rw-r--r--server/sonar-web/src/main/js/app/styles/init/type.css304
-rw-r--r--server/sonar-web/src/main/js/app/styles/mixins.css29
-rw-r--r--server/sonar-web/src/main/js/app/styles/sonar.ts17
-rw-r--r--server/sonar-web/src/main/js/app/utils/startReactApp.tsx2
-rw-r--r--server/sonar-web/src/main/js/apps/account/projects/Projects.tsx1
-rw-r--r--server/sonar-web/src/main/js/apps/background-tasks/components/BackgroundTasksApp.tsx1
-rw-r--r--server/sonar-web/src/main/js/apps/change-admin-password/ChangeAdminPasswordAppRenderer.tsx182
-rw-r--r--server/sonar-web/src/main/js/apps/code/code.css143
-rw-r--r--server/sonar-web/src/main/js/apps/code/components/CodeApp.tsx1
-rw-r--r--server/sonar-web/src/main/js/apps/code/components/CodeAppRenderer.tsx1
-rw-r--r--server/sonar-web/src/main/js/apps/code/components/Components.tsx2
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/components/ActivationFormModal.tsx6
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/components/CodingRulesApp.tsx1
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/style.css28
-rw-r--r--server/sonar-web/src/main/js/apps/create/project/Azure/AzureProjectAccordion.tsx3
-rw-r--r--server/sonar-web/src/main/js/apps/create/project/Azure/AzureProjectsList.tsx1
-rw-r--r--server/sonar-web/src/main/js/apps/create/project/BitbucketCloud/BitbucketCloudSearchForm.tsx1
-rw-r--r--server/sonar-web/src/main/js/apps/create/project/Github/GitHubProjectCreateRenderer.tsx3
-rw-r--r--server/sonar-web/src/main/js/apps/create/project/Gitlab/GitlabProjectSelectionForm.tsx1
-rw-r--r--server/sonar-web/src/main/js/apps/groups/GroupsApp.tsx1
-rw-r--r--server/sonar-web/src/main/js/apps/groups/components/ViewMembersModal.tsx1
-rw-r--r--server/sonar-web/src/main/js/apps/issues/components/ComponentBreadcrumbs.tsx3
-rw-r--r--server/sonar-web/src/main/js/apps/issues/components/IssueGuide.tsx6
-rw-r--r--server/sonar-web/src/main/js/apps/issues/components/IssueReviewHistory.tsx4
-rw-r--r--server/sonar-web/src/main/js/apps/issues/components/IssuesApp.tsx17
-rw-r--r--server/sonar-web/src/main/js/apps/issues/components/IssuesList.tsx4
-rw-r--r--server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/CrossComponentSourceViewer.tsx7
-rw-r--r--server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/IssueSourceViewerHeader.tsx33
-rw-r--r--server/sonar-web/src/main/js/apps/issues/issues-subnavigation/SubnavigationIssuesList.tsx1
-rw-r--r--server/sonar-web/src/main/js/apps/issues/sidebar/ListStyleFacet.tsx1
-rw-r--r--server/sonar-web/src/main/js/apps/issues/styles.css8
-rw-r--r--server/sonar-web/src/main/js/apps/maintenance/components/App.tsx6
-rw-r--r--server/sonar-web/src/main/js/apps/marketplace/App.tsx6
-rw-r--r--server/sonar-web/src/main/js/apps/overview/branches/FirstAnalysisNextStepsNotif.tsx74
-rw-r--r--server/sonar-web/src/main/js/apps/overview/branches/MeasuresPanelNoNewCode.tsx19
-rw-r--r--server/sonar-web/src/main/js/apps/overview/branches/NewCodeMeasuresPanel.tsx2
-rw-r--r--server/sonar-web/src/main/js/apps/overview/branches/TabsPanel.tsx6
-rw-r--r--server/sonar-web/src/main/js/apps/overview/components/AnalysisErrorModal.tsx38
-rw-r--r--server/sonar-web/src/main/js/apps/overview/components/OverviewDisabledLinkTooltip.tsx6
-rw-r--r--server/sonar-web/src/main/js/apps/overview/components/__tests__/AnalysisErrorModal-test.tsx67
-rw-r--r--server/sonar-web/src/main/js/apps/projectBranches/components/BranchLikeTabs.tsx3
-rw-r--r--server/sonar-web/src/main/js/apps/projectInformation/about/AboutProject.tsx7
-rw-r--r--server/sonar-web/src/main/js/apps/projectInformation/about/components/MetaDescription.tsx4
-rw-r--r--server/sonar-web/src/main/js/apps/projectInformation/about/components/MetaKey.tsx6
-rw-r--r--server/sonar-web/src/main/js/apps/projectInformation/about/components/MetaLinks.tsx5
-rw-r--r--server/sonar-web/src/main/js/apps/projectInformation/about/components/MetaQualityGate.tsx14
-rw-r--r--server/sonar-web/src/main/js/apps/projectInformation/about/components/MetaQualityProfiles.tsx16
-rw-r--r--server/sonar-web/src/main/js/apps/projectInformation/about/components/MetaSize.tsx26
-rw-r--r--server/sonar-web/src/main/js/apps/projectInformation/about/components/MetaTags.tsx4
-rw-r--r--server/sonar-web/src/main/js/apps/projectInformation/about/components/MetaVisibility.tsx3
-rw-r--r--server/sonar-web/src/main/js/apps/projectInformation/badges/ProjectBadges.tsx2
-rw-r--r--server/sonar-web/src/main/js/apps/projectInformation/projectRegulatoryReport/RegulatoryReport.tsx8
-rw-r--r--server/sonar-web/src/main/js/apps/projectNewCode/components/BranchNewCodeDefinitionSettingModal.tsx2
-rw-r--r--server/sonar-web/src/main/js/apps/projectQualityGate/ProjectQualityGateAppRenderer.tsx4
-rw-r--r--server/sonar-web/src/main/js/apps/projectQualityProfiles/components/AddLanguageModal.tsx2
-rw-r--r--server/sonar-web/src/main/js/apps/projectQualityProfiles/components/LanguageProfileSelectOption.tsx2
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/ProjectsList.tsx1
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/project-card/ProjectCard.tsx4
-rw-r--r--server/sonar-web/src/main/js/apps/projects/styles.css4
-rw-r--r--server/sonar-web/src/main/js/apps/projectsManagement/ProjectManagementApp.tsx1
-rw-r--r--server/sonar-web/src/main/js/apps/projectsManagement/Search.tsx2
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/components/CaycBadgeTooltip.tsx6
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/components/Conditions.tsx2
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/components/ThresholdInput.tsx2
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileProjects.tsx1
-rw-r--r--server/sonar-web/src/main/js/apps/security-hotspots/components/EmptyHotspotsPage.tsx6
-rw-r--r--server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotDisabledFilterTooltip.tsx6
-rw-r--r--server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotList.tsx1
-rw-r--r--server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotReviewHistory.tsx4
-rw-r--r--server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotSnippetContainerRenderer.tsx2
-rw-r--r--server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/__snapshots__/HotspotDisabledFilterTooltip-test.tsx.snap68
-rw-r--r--server/sonar-web/src/main/js/apps/settings/components/NewCodeDefinition.tsx14
-rw-r--r--server/sonar-web/src/main/js/apps/settings/components/__tests__/NewCodeDefinition-it.tsx4
-rw-r--r--server/sonar-web/src/main/js/apps/settings/components/authentication/Authentication.tsx5
-rw-r--r--server/sonar-web/src/main/js/apps/settings/components/authentication/AuthenticationFormFieldWrapper.tsx45
-rw-r--r--server/sonar-web/src/main/js/apps/settings/components/authentication/AutoProvisionningConsent.tsx40
-rw-r--r--server/sonar-web/src/main/js/apps/settings/components/inputs/InputForBoolean.tsx5
-rw-r--r--server/sonar-web/src/main/js/apps/settings/encryption/GenerateSecretKeyForm.tsx2
-rw-r--r--server/sonar-web/src/main/js/apps/tutorials/components/__tests__/TutorialsApp-test.tsx4
-rw-r--r--server/sonar-web/src/main/js/apps/users/UsersApp.tsx3
-rw-r--r--server/sonar-web/src/main/js/apps/users/components/PasswordForm.tsx6
-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/constants.ts2
-rw-r--r--server/sonar-web/src/main/js/apps/web-api/__tests__/WebApi-it.tsx4
-rw-r--r--server/sonar-web/src/main/js/apps/web-api/components/Action.tsx236
-rw-r--r--server/sonar-web/src/main/js/apps/web-api/components/ActionChangelog.tsx13
-rw-r--r--server/sonar-web/src/main/js/apps/web-api/components/DeprecatedBadge.tsx5
-rw-r--r--server/sonar-web/src/main/js/apps/web-api/components/Domain.tsx28
-rw-r--r--server/sonar-web/src/main/js/apps/web-api/components/InternalBadge.tsx5
-rw-r--r--server/sonar-web/src/main/js/apps/web-api/components/Menu.tsx47
-rw-r--r--server/sonar-web/src/main/js/apps/web-api/components/Params.tsx123
-rw-r--r--server/sonar-web/src/main/js/apps/web-api/components/ResponseExample.tsx6
-rw-r--r--server/sonar-web/src/main/js/apps/web-api/components/Search.tsx34
-rw-r--r--server/sonar-web/src/main/js/apps/web-api/components/WebApiApp.tsx88
-rw-r--r--server/sonar-web/src/main/js/apps/web-api/components/__tests__/Actions-test.tsx8
-rw-r--r--server/sonar-web/src/main/js/apps/webhooks/components/DeliveriesForm.tsx1
-rw-r--r--server/sonar-web/src/main/js/components/SourceViewer/SourceViewer.tsx14
-rw-r--r--server/sonar-web/src/main/js/components/SourceViewer/styles.css4
-rw-r--r--server/sonar-web/src/main/js/components/activity-graph/GraphsHistory.tsx2
-rw-r--r--server/sonar-web/src/main/js/components/common/ActivityLink.css26
-rw-r--r--server/sonar-web/src/main/js/components/common/ActivityLink.tsx1
-rw-r--r--server/sonar-web/src/main/js/components/common/BranchStatus.tsx36
-rw-r--r--server/sonar-web/src/main/js/components/common/CodeSnippet.css45
-rw-r--r--server/sonar-web/src/main/js/components/common/CodeSnippet.tsx52
-rw-r--r--server/sonar-web/src/main/js/components/common/DocLink.tsx32
-rw-r--r--server/sonar-web/src/main/js/components/common/DocumentationTooltip.tsx13
-rw-r--r--server/sonar-web/src/main/js/components/common/FiltersHeader.tsx43
-rw-r--r--server/sonar-web/src/main/js/components/common/MetaLink.tsx2
-rw-r--r--server/sonar-web/src/main/js/components/common/MultiSelect.tsx234
-rw-r--r--server/sonar-web/src/main/js/components/common/MultiSelectOption.tsx74
-rw-r--r--server/sonar-web/src/main/js/components/common/StatusIndicator.css63
-rw-r--r--server/sonar-web/src/main/js/components/common/StatusIndicator.tsx1
-rw-r--r--server/sonar-web/src/main/js/components/common/VisibilitySelector.tsx2
-rw-r--r--server/sonar-web/src/main/js/components/common/__tests__/BranchStatus-test.tsx50
-rw-r--r--server/sonar-web/src/main/js/components/common/__tests__/DocumentationTooltip-test.tsx2
-rw-r--r--server/sonar-web/src/main/js/components/common/__tests__/MultiSelect-test.tsx121
-rw-r--r--server/sonar-web/src/main/js/components/controls/ActionsDropdown.tsx143
-rw-r--r--server/sonar-web/src/main/js/components/controls/BoxedGroupAccordion.tsx73
-rw-r--r--server/sonar-web/src/main/js/components/controls/BoxedTabs.tsx105
-rw-r--r--server/sonar-web/src/main/js/components/controls/ButtonToggle.css27
-rw-r--r--server/sonar-web/src/main/js/components/controls/ButtonToggle.tsx60
-rw-r--r--server/sonar-web/src/main/js/components/controls/Checkbox.css94
-rw-r--r--server/sonar-web/src/main/js/components/controls/Checkbox.tsx106
-rw-r--r--server/sonar-web/src/main/js/components/controls/Dropdown.css34
-rw-r--r--server/sonar-web/src/main/js/components/controls/Dropdown.tsx94
-rw-r--r--server/sonar-web/src/main/js/components/controls/HelpTooltip.css31
-rw-r--r--server/sonar-web/src/main/js/components/controls/HelpTooltip.tsx32
-rw-r--r--server/sonar-web/src/main/js/components/controls/IdentityProviderLink.css64
-rw-r--r--server/sonar-web/src/main/js/components/controls/IdentityProviderLink.tsx64
-rw-r--r--server/sonar-web/src/main/js/components/controls/ListFooter.tsx49
-rw-r--r--server/sonar-web/src/main/js/components/controls/Modal.css228
-rw-r--r--server/sonar-web/src/main/js/components/controls/Modal.tsx76
-rw-r--r--server/sonar-web/src/main/js/components/controls/Radio.css76
-rw-r--r--server/sonar-web/src/main/js/components/controls/Radio.tsx65
-rw-r--r--server/sonar-web/src/main/js/components/controls/RadioCard.css130
-rw-r--r--server/sonar-web/src/main/js/components/controls/RadioCard.tsx109
-rw-r--r--server/sonar-web/src/main/js/components/controls/SearchBox.css110
-rw-r--r--server/sonar-web/src/main/js/components/controls/SearchBox.tsx182
-rw-r--r--server/sonar-web/src/main/js/components/controls/Select.tsx369
-rw-r--r--server/sonar-web/src/main/js/components/controls/SelectList.tsx1
-rw-r--r--server/sonar-web/src/main/js/components/controls/SimpleModal.tsx101
-rw-r--r--server/sonar-web/src/main/js/components/controls/Toggle.css84
-rw-r--r--server/sonar-web/src/main/js/components/controls/Toggle.tsx74
-rw-r--r--server/sonar-web/src/main/js/components/controls/Tooltip.tsx2
-rw-r--r--server/sonar-web/src/main/js/components/controls/__tests__/BoxedGroupAccordion-test.tsx56
-rw-r--r--server/sonar-web/src/main/js/components/controls/__tests__/ButtonToggle-test.tsx65
-rw-r--r--server/sonar-web/src/main/js/components/controls/__tests__/Checkbox-test.tsx70
-rw-r--r--server/sonar-web/src/main/js/components/controls/__tests__/ListFooter-test.tsx61
-rw-r--r--server/sonar-web/src/main/js/components/controls/__tests__/RadioCard-test.tsx55
-rw-r--r--server/sonar-web/src/main/js/components/controls/__tests__/__snapshots__/Checkbox-test.tsx.snap55
-rw-r--r--server/sonar-web/src/main/js/components/controls/__tests__/clipboard-test.tsx108
-rw-r--r--server/sonar-web/src/main/js/components/controls/clipboard.tsx165
-rw-r--r--server/sonar-web/src/main/js/components/devops-platform/AlmSettingsInstanceSelector.tsx4
-rw-r--r--server/sonar-web/src/main/js/components/icons/BranchIcon.tsx33
-rw-r--r--server/sonar-web/src/main/js/components/icons/CheckIcon.tsx32
-rw-r--r--server/sonar-web/src/main/js/components/icons/CopyIcon.tsx33
-rw-r--r--server/sonar-web/src/main/js/components/icons/HelpIcon.tsx42
-rw-r--r--server/sonar-web/src/main/js/components/icons/ProjectLinkIcon.tsx25
-rw-r--r--server/sonar-web/src/main/js/components/issue/Issue.css4
-rw-r--r--server/sonar-web/src/main/js/components/new-code-definition/NCDAutoUpdateMessage.tsx40
-rw-r--r--server/sonar-web/src/main/js/components/new-code-definition/NewCodeDefinitionDaysOption.tsx20
-rw-r--r--server/sonar-web/src/main/js/components/new-code-definition/__tests__/NCDAutoUpdateMessage-test.tsx14
-rw-r--r--server/sonar-web/src/main/js/components/permissions/AllHoldersList.tsx2
-rw-r--r--server/sonar-web/src/main/js/components/rules/IssueTabViewer.tsx2
-rw-r--r--server/sonar-web/src/main/js/components/rules/RuleTabViewer.tsx3
-rw-r--r--server/sonar-web/src/main/js/components/tags/TagsSelector.tsx53
-rw-r--r--server/sonar-web/src/main/js/components/tutorials/TutorialSelectionRenderer.tsx3
-rw-r--r--server/sonar-web/src/main/js/components/tutorials/__tests__/TutorialSelection-it.tsx4
-rw-r--r--server/sonar-web/src/main/js/components/ui/Alert.tsx177
-rw-r--r--server/sonar-web/src/main/js/components/ui/CoverageRating.tsx68
-rw-r--r--server/sonar-web/src/main/js/components/ui/DismissableAlert.css27
-rw-r--r--server/sonar-web/src/main/js/components/ui/DismissableAlert.tsx17
-rw-r--r--server/sonar-web/src/main/js/components/ui/DismissableAlertComponent.tsx53
-rw-r--r--server/sonar-web/src/main/js/components/ui/DuplicationsRating.css171
-rw-r--r--server/sonar-web/src/main/js/components/ui/DuplicationsRating.tsx45
-rw-r--r--server/sonar-web/src/main/js/components/ui/GenericAvatar.tsx62
-rw-r--r--server/sonar-web/src/main/js/components/ui/LegacyAvatar.tsx74
-rw-r--r--server/sonar-web/src/main/js/components/ui/Level.css81
-rw-r--r--server/sonar-web/src/main/js/components/ui/Level.tsx50
-rw-r--r--server/sonar-web/src/main/js/components/ui/NavBarTabs.css51
-rw-r--r--server/sonar-web/src/main/js/components/ui/NavBarTabs.tsx35
-rw-r--r--server/sonar-web/src/main/js/components/ui/PageShortcutsTooltip.tsx113
-rw-r--r--server/sonar-web/src/main/js/components/ui/Spinner.css78
-rw-r--r--server/sonar-web/src/main/js/components/ui/Spinner.tsx63
-rw-r--r--server/sonar-web/src/main/js/components/ui/__tests__/PageShortcutsTooltip-test.tsx73
-rw-r--r--server/sonar-web/src/main/js/components/ui/__tests__/Spinner-test.tsx40
-rw-r--r--server/sonar-web/src/main/js/components/ui/popups.tsx213
-rw-r--r--server/sonar-web/src/main/js/components/upgrade/SystemUpgradeButton.tsx6
-rw-r--r--server/sonar-web/src/main/js/components/upgrade/SystemUpgradeForm.tsx63
-rw-r--r--server/sonar-web/src/main/js/components/upgrade/SystemUpgradeIntermediate.tsx41
-rw-r--r--server/sonar-web/src/main/js/components/upgrade/SystemUpgradeItem.tsx39
-rw-r--r--server/sonar-web/src/main/js/helpers/search.tsx5
-rw-r--r--server/sonar-web/src/main/js/types/extension.ts45
-rw-r--r--server/sonar-web/tailwind.base.config.js1
-rw-r--r--sonar-core/src/main/resources/org/sonar/l10n/core.properties3
257 files changed, 1397 insertions, 9698 deletions
diff --git a/server/sonar-web/.eslintrc b/server/sonar-web/.eslintrc
index 7a2728eb58a..3634bec594b 100644
--- a/server/sonar-web/.eslintrc
+++ b/server/sonar-web/.eslintrc
@@ -17,7 +17,7 @@
"local-rules/use-metrictype-enum": "warn",
"local-rules/use-visibility-enum": "warn",
"local-rules/convert-class-to-function-component": "warn",
- "local-rules/no-conditional-rendering-of-deferredspinner": "warn",
+ "local-rules/no-conditional-rendering-of-spinner": "warn",
"local-rules/use-jest-mocked": "warn",
"local-rules/use-await-expect-async-matcher": "warn",
"local-rules/no-implicit-coercion": "warn",
diff --git a/server/sonar-web/config/indexHtmlTemplate.js b/server/sonar-web/config/indexHtmlTemplate.js
index f1980952c1b..6d708a8c2e9 100644
--- a/server/sonar-web/config/indexHtmlTemplate.js
+++ b/server/sonar-web/config/indexHtmlTemplate.js
@@ -46,7 +46,7 @@ module.exports = (cssHash, jsHash) => `
<body>
<div id="content" data-base-url="%WEB_CONTEXT%" data-server-status="%SERVER_STATUS%" data-instance="%INSTANCE%" data-official="%OFFICIAL%">
<div class="global-loading">
- <i class="spinner global-loading-spinner"></i>
+ <i class="global-loading-spinner"></i>
<span aria-live="polite" class="global-loading-text">Loading...</span>
</div>
</div>
diff --git a/server/sonar-web/design-system/.eslintrc b/server/sonar-web/design-system/.eslintrc
index 07e825a3518..b6218abddcd 100644
--- a/server/sonar-web/design-system/.eslintrc
+++ b/server/sonar-web/design-system/.eslintrc
@@ -22,7 +22,7 @@
"local-rules/use-metrictype-enum": "warn",
"local-rules/use-visibility-enum": "warn",
"local-rules/convert-class-to-function-component": "warn",
- "local-rules/no-conditional-rendering-of-deferredspinner": "warn",
+ "local-rules/no-conditional-rendering-of-spinner": "warn",
"local-rules/use-jest-mocked": "warn",
// New rules added after updating eslint packages to more recent versions than eslint-config-sonarqube
diff --git a/server/sonar-web/design-system/src/components/Banner.tsx b/server/sonar-web/design-system/src/components/Banner.tsx
index 4da28d16d1e..f7053a03924 100644
--- a/server/sonar-web/design-system/src/components/Banner.tsx
+++ b/server/sonar-web/design-system/src/components/Banner.tsx
@@ -30,6 +30,7 @@ export type Variant = 'error' | 'warning' | 'success' | 'info';
interface Props {
children: ReactNode;
+ className?: string;
onDismiss?: VoidFunction;
variant: Variant;
}
@@ -61,19 +62,19 @@ function getVariantInfo(variant: Variant) {
return variantList[variant];
}
-export function Banner({ children, onDismiss, variant }: Props) {
+export function Banner({ children, className, onDismiss, variant }: Props) {
const variantInfo = getVariantInfo(variant);
const intl = useIntl();
return (
- <div role="alert" style={{ height: LAYOUT_BANNER_HEIGHT }}>
+ <div className={className} role="alert" style={{ height: LAYOUT_BANNER_HEIGHT }}>
<BannerWrapper
backGroundColor={variantInfo.backGroundColor}
fontColor={variantInfo.fontColor}
>
<BannerInner>
- <div className="sw-flex">
+ <div className="sw-flex sw-items-center">
<div className="sw-mr-3">{variantInfo.icon}</div>
{children}
</div>
@@ -103,7 +104,7 @@ const BannerWrapper = styled.div<{
height: inherit;
background-color: ${({ backGroundColor }) => themeColor(backGroundColor)};
color: ${({ fontColor }) => themeColor(fontColor)};
- ${tw`sw-z-global-navbar sw-fixed sw-w-full`}
+ ${tw`sw-z-popup sw-fixed sw-w-full`}
${tw`sw-sticky sw-top-0`}
`;
diff --git a/server/sonar-web/design-system/src/components/Spinner.tsx b/server/sonar-web/design-system/src/components/Spinner.tsx
index a0ecc957962..d76bbfef775 100644
--- a/server/sonar-web/design-system/src/components/Spinner.tsx
+++ b/server/sonar-web/design-system/src/components/Spinner.tsx
@@ -49,16 +49,25 @@ export function Spinner(props: React.PropsWithChildren<Props>) {
}
return (
- <>
+ // Below: using <></> won't work in extenstions ('React' is not defined). This is because the
+ // name 'React' would already have been minified to something else when <> is resolved to
+ // React.Fragment
+ // eslint-disable-next-line react/jsx-fragments
+ <React.Fragment>
<div className="sw-relative">
- <div className={classNames('sw-overflow-hidden', { 'sw-sr-only': !loading })}>
+ <div
+ className={classNames('sw-overflow-hidden', {
+ 'sw-sr-only': !loading,
+ it__loading: loading,
+ })}
+ >
<StyledSpinner aria-live="polite" className={className} role="status">
{loading && <span className="sw-sr-only">{ariaLabel}</span>}
</StyledSpinner>
</div>
</div>
{!loading && (children ?? (placeholder && <Placeholder className={className} />) ?? null)}
- </>
+ </React.Fragment>
);
}
diff --git a/server/sonar-web/design-system/src/components/__tests__/__snapshots__/CodeSnippet-test.tsx.snap b/server/sonar-web/design-system/src/components/__tests__/__snapshots__/CodeSnippet-test.tsx.snap
index 6d2f58112e9..b279c96b379 100644
--- a/server/sonar-web/design-system/src/components/__tests__/__snapshots__/CodeSnippet-test.tsx.snap
+++ b/server/sonar-web/design-system/src/components/__tests__/__snapshots__/CodeSnippet-test.tsx.snap
@@ -91,12 +91,6 @@ exports[`should highlight code content correctly 1`] = `
pointer-events: none;
}
-.emotion-4:hover,
-.emotion-4:active,
-.emotion-4:focus {
- border-color: rgb(197,205,223);
-}
-
.code-snippet-highlighted-oneline .emotion-4 {
bottom: 0.5rem;
}
@@ -303,12 +297,6 @@ exports[`should show full size when multiline with no editing 1`] = `
pointer-events: none;
}
-.emotion-4:hover,
-.emotion-4:active,
-.emotion-4:focus {
- border-color: rgb(197,205,223);
-}
-
.code-snippet-highlighted-oneline .emotion-4 {
bottom: 0.5rem;
}
@@ -519,12 +507,6 @@ exports[`should show reduced size when single line with no editing 1`] = `
pointer-events: none;
}
-.emotion-4:hover,
-.emotion-4:active,
-.emotion-4:focus {
- border-color: rgb(197,205,223);
-}
-
.code-snippet-highlighted-oneline .emotion-4 {
bottom: 0.5rem;
}
diff --git a/server/sonar-web/design-system/src/components/buttons/ButtonSecondary.tsx b/server/sonar-web/design-system/src/components/buttons/ButtonSecondary.tsx
index fafaa4259cc..bd898e1e4ed 100644
--- a/server/sonar-web/design-system/src/components/buttons/ButtonSecondary.tsx
+++ b/server/sonar-web/design-system/src/components/buttons/ButtonSecondary.tsx
@@ -27,10 +27,4 @@ export const ButtonSecondary: React.FC<React.PropsWithChildren<ButtonProps>> = s
--color: ${themeContrast('buttonSecondary')};
--focus: ${themeColor('buttonSecondaryBorder', OPACITY_20_PERCENT)};
--border: ${themeBorder('default', 'buttonSecondaryBorder')};
-
- &:hover,
- &:active,
- &:focus {
- border-color: ${themeColor('buttonSecondaryBorder')};
- }
`;
diff --git a/server/sonar-web/design-system/src/components/buttons/DangerButtonSecondary.tsx b/server/sonar-web/design-system/src/components/buttons/DangerButtonSecondary.tsx
index c3cb55e3e8d..94935bf0bf0 100644
--- a/server/sonar-web/design-system/src/components/buttons/DangerButtonSecondary.tsx
+++ b/server/sonar-web/design-system/src/components/buttons/DangerButtonSecondary.tsx
@@ -27,10 +27,4 @@ export const DangerButtonSecondary: React.FC<React.PropsWithChildren<ButtonProps
--color: ${themeContrast('dangerButtonSecondary')};
--focus: ${themeColor('dangerButtonSecondaryFocus', OPACITY_20_PERCENT)};
--border: ${themeBorder('default', 'dangerButtonSecondaryBorder')};
-
- &:hover,
- &:active,
- &:focus {
- border-color: ${themeColor('dangerButtonSecondaryBorder')};
- }
`;
diff --git a/server/sonar-web/design-system/src/components/input/DatePicker.tsx b/server/sonar-web/design-system/src/components/input/DatePicker.tsx
index e282d1e4eed..89d4c415549 100644
--- a/server/sonar-web/design-system/src/components/input/DatePicker.tsx
+++ b/server/sonar-web/design-system/src/components/input/DatePicker.tsx
@@ -236,13 +236,11 @@ const StyledInteractiveIcon = styled(InteractiveIcon)`
`;
const StyledInputField = styled(InputField)`
- input[type='text']& {
- ${tw`sw-pl-8`};
- ${tw`sw-cursor-pointer`};
+ ${tw`sw-pl-8`};
+ ${tw`sw-cursor-pointer`};
- &.is-filled {
- ${tw`sw-pr-8`};
- }
+ &.is-filled {
+ ${tw`sw-pr-8`};
}
`;
diff --git a/server/sonar-web/design-system/src/components/input/InputField.tsx b/server/sonar-web/design-system/src/components/input/InputField.tsx
index 2d37c9b095f..3690c438216 100644
--- a/server/sonar-web/design-system/src/components/input/InputField.tsx
+++ b/server/sonar-web/design-system/src/components/input/InputField.tsx
@@ -129,14 +129,9 @@ const baseStyle = (props: ThemedProps) => css`
`;
const StyledInput = styled.input`
- input[type='text']&,
- input[type='number']&,
- input[type='password']&,
- input[type='email']& {
- ${getInputVariant}
- ${baseStyle}
- ${tw`sw-h-control`}
- }
+ ${getInputVariant}
+ ${baseStyle}
+ ${tw`sw-h-control`}
`;
const StyledTextArea = styled.textarea`
diff --git a/server/sonar-web/design-system/src/components/input/RadioButton.tsx b/server/sonar-web/design-system/src/components/input/RadioButton.tsx
index 2797de1b7f5..7ee29e59f89 100644
--- a/server/sonar-web/design-system/src/components/input/RadioButton.tsx
+++ b/server/sonar-web/design-system/src/components/input/RadioButton.tsx
@@ -140,9 +140,9 @@ export const RadioButtonStyled = styled.input`
to right,
${themeColor('radioDisabledBackground')},
${themeColor('radioDisabledBackground')}
- ) !important;
- background-clip: content-box, padding-box !important;
- border: ${themeBorder('default', 'radioDisabledBorder')} !important;
+ );
+ background-clip: content-box, padding-box;
+ border: ${themeBorder('default', 'radioDisabledBorder')};
}
}
`;
diff --git a/server/sonar-web/design-system/src/components/modal/Modal.tsx b/server/sonar-web/design-system/src/components/modal/Modal.tsx
index 8ba03e93d02..d7654dbce00 100644
--- a/server/sonar-web/design-system/src/components/modal/Modal.tsx
+++ b/server/sonar-web/design-system/src/components/modal/Modal.tsx
@@ -92,7 +92,7 @@ export function Modal({
<ReactModal
aria={{ labelledby: 'modal_header_title' }}
- className={classNames('design-system-modal-contents modal', { large: isLarge })}
+ className={classNames('design-system-modal-contents', { large: isLarge })}
isOpen={isOpen}
onRequestClose={onClose}
overlayClassName="design-system-modal-overlay"
@@ -148,8 +148,6 @@ const globalStyles = ({ theme }: { theme: Theme }) => css`
&.large {
max-width: 1280px;
min-width: 1040px;
- transform: translateX(-50%);
- margin-left: 0px;
}
}
diff --git a/server/sonar-web/eslint-local-rules/__tests__/no-conditional-rendering-of-deferredspinner-test.js b/server/sonar-web/eslint-local-rules/__tests__/no-conditional-rendering-of-spinner-test.js
index 99a34407909..bcd456e9f98 100644
--- a/server/sonar-web/eslint-local-rules/__tests__/no-conditional-rendering-of-deferredspinner-test.js
+++ b/server/sonar-web/eslint-local-rules/__tests__/no-conditional-rendering-of-spinner-test.js
@@ -18,7 +18,7 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
const { RuleTester } = require('eslint');
-const noConditionalRenderingOfDeferredSpinner = require('../no-conditional-rendering-of-deferredspinner');
+const noConditionalRenderingOfSpinner = require('../no-conditional-rendering-of-spinner');
const ruleTester = new RuleTester({
parser: require.resolve('@typescript-eslint/parser'),
@@ -29,44 +29,40 @@ const ruleTester = new RuleTester({
},
});
-ruleTester.run(
- 'no-conditional-rendering-of-deferredspinner',
- noConditionalRenderingOfDeferredSpinner,
- {
- valid: [
- {
- code: `function MyCompontent({ loading }) {
+ruleTester.run('no-conditional-rendering-of-spinner', noConditionalRenderingOfSpinner, {
+ valid: [
+ {
+ code: `function MyCompontent({ loading }) {
return <>
<Spinner loading={loading} />
</>
}`,
- },
- ],
- invalid: [
- {
- code: `function MyCompontent({ loading }) {
+ },
+ ],
+ invalid: [
+ {
+ code: `function MyCompontent({ loading }) {
return <>
{loading && <Spinner />}
</>
}`,
- errors: [{ messageId: 'noConditionalRenderingOfDeferredSpinner' }],
- },
- {
- code: `function MyComponent({ loading }) {
+ errors: [{ messageId: 'noConditionalRenderingOfSpinner' }],
+ },
+ {
+ code: `function MyComponent({ loading }) {
return <>
{loading ? <Spinner /> : <div />}
</>
}`,
- errors: [{ messageId: 'noConditionalRenderingOfDeferredSpinner' }],
- },
- {
- code: `function MyCompontent({ loaded }) {
+ errors: [{ messageId: 'noConditionalRenderingOfSpinner' }],
+ },
+ {
+ code: `function MyCompontent({ loaded }) {
return <>
{loaded ? <div /> : <Spinner />}
</>
}`,
- errors: [{ messageId: 'noConditionalRenderingOfDeferredSpinner' }],
- },
- ],
- }
-);
+ errors: [{ messageId: 'noConditionalRenderingOfSpinner' }],
+ },
+ ],
+});
diff --git a/server/sonar-web/eslint-local-rules/index.js b/server/sonar-web/eslint-local-rules/index.js
index 7d9345d3021..e80671fb5a5 100644
--- a/server/sonar-web/eslint-local-rules/index.js
+++ b/server/sonar-web/eslint-local-rules/index.js
@@ -20,7 +20,7 @@
module.exports = {
'use-jest-mocked': require('./use-jest-mocked'),
'convert-class-to-function-component': require('./convert-class-to-function-component'),
- 'no-conditional-rendering-of-deferredspinner': require('./no-conditional-rendering-of-deferredspinner'),
+ 'no-conditional-rendering-of-spinner': require('./no-conditional-rendering-of-spinner'),
'use-visibility-enum': require('./use-visibility-enum'),
'use-componentqualifier-enum': require('./use-componentqualifier-enum'),
'use-metrickey-enum': require('./use-metrickey-enum'),
diff --git a/server/sonar-web/eslint-local-rules/no-conditional-rendering-of-deferredspinner.js b/server/sonar-web/eslint-local-rules/no-conditional-rendering-of-spinner.js
index 89540719d75..06ed0a9fa55 100644
--- a/server/sonar-web/eslint-local-rules/no-conditional-rendering-of-deferredspinner.js
+++ b/server/sonar-web/eslint-local-rules/no-conditional-rendering-of-spinner.js
@@ -20,7 +20,7 @@
module.exports = {
meta: {
messages: {
- noConditionalRenderingOfDeferredSpinner:
+ noConditionalRenderingOfSpinner:
'For accessibility reasons, you should not conditionally render a <Spinner />. Always render it, and pass a loading prop instead.',
},
},
@@ -30,18 +30,18 @@ module.exports = {
switch (node.expression.type) {
case 'LogicalExpression':
const { right } = node.expression;
- if (isDeferredSpinnerComponent(right)) {
- context.report({ node, messageId: 'noConditionalRenderingOfDeferredSpinner' });
+ if (isSpinnerComponent(right)) {
+ context.report({ node, messageId: 'noConditionalRenderingOfSpinner' });
}
break;
case 'ConditionalExpression':
const { consequent, alternate } = node.expression;
- if (isDeferredSpinnerComponent(consequent)) {
- context.report({ node, messageId: 'noConditionalRenderingOfDeferredSpinner' });
+ if (isSpinnerComponent(consequent)) {
+ context.report({ node, messageId: 'noConditionalRenderingOfSpinner' });
}
- if (isDeferredSpinnerComponent(alternate)) {
- context.report({ node, messageId: 'noConditionalRenderingOfDeferredSpinner' });
+ if (isSpinnerComponent(alternate)) {
+ context.report({ node, messageId: 'noConditionalRenderingOfSpinner' });
}
break;
}
@@ -50,7 +50,7 @@ module.exports = {
},
};
-function isDeferredSpinnerComponent(element) {
+function isSpinnerComponent(element) {
return (
element.type === 'JSXElement' &&
element.openingElement &&
diff --git a/server/sonar-web/src/main/js/app/components/AlmSynchronisationWarning.tsx b/server/sonar-web/src/main/js/app/components/AlmSynchronisationWarning.tsx
index 9f6259e4a32..ffc9f55bdaf 100644
--- a/server/sonar-web/src/main/js/app/components/AlmSynchronisationWarning.tsx
+++ b/server/sonar-web/src/main/js/app/components/AlmSynchronisationWarning.tsx
@@ -25,7 +25,6 @@ import { FormattedMessage } from 'react-intl';
import { translate, translateWithParameters } from '../../helpers/l10n';
import { AlmSyncStatus } from '../../types/provisioning';
import { TaskStatuses } from '../../types/tasks';
-import './SystemAnnouncement.css';
interface SynchronisationWarningProps {
short?: boolean;
diff --git a/server/sonar-web/src/main/js/app/components/ComponentContainerNotFound.tsx b/server/sonar-web/src/main/js/app/components/ComponentContainerNotFound.tsx
index aed1b98b8f6..b96e54a5d4d 100644
--- a/server/sonar-web/src/main/js/app/components/ComponentContainerNotFound.tsx
+++ b/server/sonar-web/src/main/js/app/components/ComponentContainerNotFound.tsx
@@ -17,9 +17,9 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+import { Card, CenteredLayout, Link, SubHeading } from 'design-system/lib';
import * as React from 'react';
import { Helmet } from 'react-helmet-async';
-import Link from '../../components/common/Link';
import { translate } from '../../helpers/l10n';
export interface ComponentContainerNotFoundProps {
@@ -32,19 +32,15 @@ export default function ComponentContainerNotFound({
const componentType = isPortfolioLike ? 'portfolio' : 'project';
return (
- <>
+ <CenteredLayout className="sw-flex sw-justify-around" id="bd">
<Helmet defaultTitle={translate('404_not_found')} defer={false} />
- <div className="page-wrapper-simple" id="bd">
- <div className="page-simple" id="nonav">
- <h2 className="big-spacer-bottom">
- {translate('dashboard', componentType, 'not_found')}
- </h2>
- <p className="spacer-bottom">{translate('dashboard', componentType, 'not_found.2')}</p>
- <p>
- <Link to="/">{translate('go_back_to_homepage')}</Link>
- </p>
- </div>
- </div>
- </>
+ <Card className="sw-mt-24" id="nonav">
+ <SubHeading>{translate('dashboard', componentType, 'not_found')}</SubHeading>
+ <p className="sw-mb-2">{translate('dashboard', componentType, 'not_found.2')}</p>
+ <p>
+ <Link to="/">{translate('go_back_to_homepage')}</Link>
+ </p>
+ </Card>
+ </CenteredLayout>
);
}
diff --git a/server/sonar-web/src/main/js/app/components/DocumentationRedirect.tsx b/server/sonar-web/src/main/js/app/components/DocumentationRedirect.tsx
index db9b1473502..ce07d94d59d 100644
--- a/server/sonar-web/src/main/js/app/components/DocumentationRedirect.tsx
+++ b/server/sonar-web/src/main/js/app/components/DocumentationRedirect.tsx
@@ -17,10 +17,10 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+import { Link } from 'design-system';
import * as React from 'react';
import { Helmet } from 'react-helmet-async';
import { useLocation } from 'react-router-dom';
-import Link from '../../components/common/Link';
import { useDocUrl } from '../../helpers/docs';
const PAUSE_REDIRECT = 1;
@@ -34,12 +34,12 @@ export default function DocumentationRedirect() {
<Helmet>
<meta httpEquiv="refresh" content={`${PAUSE_REDIRECT}; url='${url}'`} />
</Helmet>
- <div className="global-loading">
- <div className="display-flex-center">
- <i className="spinner global-loading-spinner" />
- <span className="spacer-left global-loading-text">Redirecting...</span>
+ <div className="sw-flex sw-flex-col sw-items-center sw-gap-4 sw-h-[100vh]">
+ <div className="global-loading">
+ <i className="global-loading-spinner" />
+ <span className="global-loading-text">Redirecting...</span>
</div>
- <div className="display-flex-justify-content spacer-top large">
+ <div>
<Link to={url}>Click here if you&apos;re not being redirected automatically</Link>
</div>
</div>
diff --git a/server/sonar-web/src/main/js/app/components/GitHubSynchronisationWarning.tsx b/server/sonar-web/src/main/js/app/components/GitHubSynchronisationWarning.tsx
index 5adbce4ac73..12c1592adb7 100644
--- a/server/sonar-web/src/main/js/app/components/GitHubSynchronisationWarning.tsx
+++ b/server/sonar-web/src/main/js/app/components/GitHubSynchronisationWarning.tsx
@@ -20,7 +20,6 @@
import * as React from 'react';
import { useGitHubSyncStatusQuery } from '../../queries/identity-provider/github';
import AlmSynchronisationWarning from './AlmSynchronisationWarning';
-import './SystemAnnouncement.css';
interface Props {
short?: boolean;
diff --git a/server/sonar-web/src/main/js/app/components/GitLabSynchronisationWarning.tsx b/server/sonar-web/src/main/js/app/components/GitLabSynchronisationWarning.tsx
index 39a18f809ad..806882fa76b 100644
--- a/server/sonar-web/src/main/js/app/components/GitLabSynchronisationWarning.tsx
+++ b/server/sonar-web/src/main/js/app/components/GitLabSynchronisationWarning.tsx
@@ -20,7 +20,6 @@
import * as React from 'react';
import { useGitLabSyncStatusQuery } from '../../queries/identity-provider/gitlab';
import AlmSynchronisationWarning from './AlmSynchronisationWarning';
-import './SystemAnnouncement.css';
interface Props {
short?: boolean;
diff --git a/server/sonar-web/src/main/js/app/components/GlobalContainer.tsx b/server/sonar-web/src/main/js/app/components/GlobalContainer.tsx
index 66951e5aae3..d8aa49d5ce2 100644
--- a/server/sonar-web/src/main/js/app/components/GlobalContainer.tsx
+++ b/server/sonar-web/src/main/js/app/components/GlobalContainer.tsx
@@ -18,8 +18,8 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import { ThemeProvider } from '@emotion/react';
-import classNames from 'classnames';
-import { lightTheme, ToastMessageContainer } from 'design-system';
+import styled from '@emotion/styled';
+import { lightTheme, themeColor, ToastMessageContainer } from 'design-system';
import * as React from 'react';
import { Outlet, useLocation } from 'react-router-dom';
import A11yProvider from '../../components/a11y/A11yProvider';
@@ -38,30 +38,11 @@ import StartupModal from './StartupModal';
import SystemAnnouncement from './SystemAnnouncement';
import UpdateNotification from './update-notification/UpdateNotification';
-const TEMP_PAGELIST_WITH_NEW_BACKGROUND = [
- '/admin/extension/governance/views_console',
- '/admin/extension/license',
- '/code',
- '/coding_rules',
- '/component_measures',
- '/dashboard',
- '/portfolios',
- '/profiles',
- '/project/activity',
- '/project/admin/extension/governance/console',
- '/project/admin/extension/governance/report',
- '/project/extension/securityreport/securityreport',
- '/project/information',
- '/project/issues',
- '/projects',
- '/quality_gates',
- '/security_hotspots',
- '/web_api_v2',
- '/portfolio',
- '/account',
-];
-
-const TEMP_PAGELIST_WITH_NEW_BACKGROUND_WHITE = [
+/*
+ * These pages need a white background (aka 'secondary', rather than the default 'primary')
+ * This should be revisited at some point (why the exception?)
+ */
+const PAGES_WITH_SECONDARY_BACKGROUND = [
'/tutorials',
'/projects/create',
'/project/baseline',
@@ -101,15 +82,9 @@ export default function GlobalContainer() {
<A11yProvider>
<A11ySkipLinks />
<div className="global-container">
- <div
- className={classNames('page-wrapper', {
- 'new-background': TEMP_PAGELIST_WITH_NEW_BACKGROUND.some((element) =>
- location.pathname.startsWith(element),
- ),
- 'white-background': TEMP_PAGELIST_WITH_NEW_BACKGROUND_WHITE.includes(
- location.pathname,
- ),
- })}
+ <GlobalBackground
+ secondary={PAGES_WITH_SECONDARY_BACKGROUND.includes(location.pathname)}
+ className="sw-box-border sw-flex-[1_0_auto]"
id="container"
>
<div className="page-container">
@@ -136,7 +111,7 @@ export default function GlobalContainer() {
</Workspace>
</div>
<PromotionNotification />
- </div>
+ </GlobalBackground>
<GlobalFooter />
</div>
<StartupModal />
@@ -145,3 +120,8 @@ export default function GlobalContainer() {
</ThemeProvider>
);
}
+
+const GlobalBackground = styled.div<{ secondary: boolean }>`
+ background-color: ${({ secondary }) =>
+ themeColor(secondary ? 'backgroundSecondary' : 'backgroundPrimary')};
+`;
diff --git a/server/sonar-web/src/main/js/app/components/LicensePromptModal.tsx b/server/sonar-web/src/main/js/app/components/LicensePromptModal.tsx
index 3ed3a5f191e..cee66aca180 100644
--- a/server/sonar-web/src/main/js/app/components/LicensePromptModal.tsx
+++ b/server/sonar-web/src/main/js/app/components/LicensePromptModal.tsx
@@ -17,11 +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.
*/
+
+import { Modal } from 'design-system';
import * as React from 'react';
import { FormattedMessage } from 'react-intl';
import Link from '../../components/common/Link';
-import Modal from '../../components/controls/Modal';
-import { ResetButtonLink } from '../../components/controls/buttons';
import { translate } from '../../helpers/l10n';
interface Props {
@@ -29,13 +29,9 @@ interface Props {
}
export default function LicensePromptModal({ onClose }: Readonly<Props>) {
- const header = translate('license.prompt.title');
return (
- <Modal contentLabel={header} onRequestClose={onClose}>
- <header className="modal-head">
- <h2>{header}</h2>
- </header>
- <div className="modal-body">
+ <Modal
+ body={
<FormattedMessage
defaultMessage={translate('license.prompt.description')}
id="license.prompt.description"
@@ -47,10 +43,10 @@ export default function LicensePromptModal({ onClose }: Readonly<Props>) {
),
}}
/>
- </div>
- <footer className="modal-foot">
- <ResetButtonLink onClick={onClose}>{translate('cancel')}</ResetButtonLink>
- </footer>
- </Modal>
+ }
+ headerTitle={translate('license.prompt.title')}
+ onClose={onClose}
+ secondaryButtonLabel={translate('cancel')}
+ />
);
}
diff --git a/server/sonar-web/src/main/js/app/components/NonAdminPagesContainer.tsx b/server/sonar-web/src/main/js/app/components/NonAdminPagesContainer.tsx
index e7dad14f203..6053ebd3de9 100644
--- a/server/sonar-web/src/main/js/app/components/NonAdminPagesContainer.tsx
+++ b/server/sonar-web/src/main/js/app/components/NonAdminPagesContainer.tsx
@@ -17,9 +17,9 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+import { CenteredLayout, FlagMessage } from 'design-system';
import * as React from 'react';
import { Outlet } from 'react-router-dom';
-import { Alert } from '../../components/ui/Alert';
import { translate } from '../../helpers/l10n';
import { isApplication } from '../../types/component';
import { ComponentContext } from './componentContext/ComponentContext';
@@ -34,17 +34,18 @@ export default function NonAdminPagesContainer() {
*/
if (component && isApplication(component.qualifier) && !component.canBrowseAllChildProjects) {
return (
- <div className="page page-limited display-flex-justify-center">
- <Alert
- className="it__alert-no-access-all-child-project max-width-60 huge-spacer-top"
- display="block"
- variant="error"
- >
- <p>{translate('application.cannot_access_all_child_projects1')}</p>
- <br />
- <p>{translate('application.cannot_access_all_child_projects2')}</p>
- </Alert>
- </div>
+ <CenteredLayout
+ className="sw-py-8 sw-body-md sw-flex sw-flex-col sw-items-center"
+ id="code-page"
+ >
+ <FlagMessage className="it__alert-no-access-all-child-project sw-mt-10" variant="error">
+ <p>
+ {translate('application.cannot_access_all_child_projects1')}
+ <br />
+ {translate('application.cannot_access_all_child_projects2')}
+ </p>
+ </FlagMessage>
+ </CenteredLayout>
);
}
diff --git a/server/sonar-web/src/main/js/app/components/PluginRiskConsent.css b/server/sonar-web/src/main/js/app/components/PluginRiskConsent.css
deleted file mode 100644
index 142d8bafe5f..00000000000
--- a/server/sonar-web/src/main/js/app/components/PluginRiskConsent.css
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2024 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.
- */
-.plugin-risk-consent-page {
- padding-top: 10vh;
-}
-
-.plugin-risk-consent-page h1 {
- line-height: 1.5;
- font-size: 24px;
- font-weight: 300;
- text-align: center;
-}
-
-.plugin-risk-consent-content {
- min-width: 500px;
- width: 40%;
- margin: 0 auto;
-}
diff --git a/server/sonar-web/src/main/js/app/components/PluginRiskConsent.tsx b/server/sonar-web/src/main/js/app/components/PluginRiskConsent.tsx
index a4f0e5a3a85..8ac5fff5109 100644
--- a/server/sonar-web/src/main/js/app/components/PluginRiskConsent.tsx
+++ b/server/sonar-web/src/main/js/app/components/PluginRiskConsent.tsx
@@ -17,10 +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.
*/
+
+import { ButtonPrimary, Card, Title } from 'design-system';
import * as React from 'react';
import { Helmet } from 'react-helmet-async';
import { setSimpleSettingValue } from '../../api/settings';
-import { Button } from '../../components/controls/buttons';
import { whenLoggedIn } from '../../components/hoc/whenLoggedIn';
import { Router, withRouter } from '../../components/hoc/withRouter';
import { translate } from '../../helpers/l10n';
@@ -30,15 +31,14 @@ import { Permissions } from '../../types/permissions';
import { RiskConsent } from '../../types/plugins';
import { SettingsKey } from '../../types/settings';
import { LoggedInUser } from '../../types/users';
-import './PluginRiskConsent.css';
export interface PluginRiskConsentProps {
currentUser: LoggedInUser;
router: Router;
}
-export function PluginRiskConsent(props: PluginRiskConsentProps) {
- const { router, currentUser } = props;
+export function PluginRiskConsent(props: Readonly<PluginRiskConsentProps>) {
+ const { currentUser, router } = props;
const isAdmin = hasGlobalPermission(currentUser, Permissions.Admin);
@@ -67,20 +67,24 @@ export function PluginRiskConsent(props: PluginRiskConsentProps) {
};
return (
- <div className="plugin-risk-consent-page">
+ <>
<Helmet defer={false} title={translate('plugin_risk_consent.page')} />
- <div className="plugin-risk-consent-content boxed-group">
- <div className="boxed-group-inner text-center">
- <h1 className="big-spacer-bottom">{translate('plugin_risk_consent.title')}</h1>
- <p className="big big-spacer-bottom">{translate('plugin_risk_consent.description')}</p>
- <p className="big huge-spacer-bottom">{translate('plugin_risk_consent.description2')}</p>
- <Button className="big-spacer-bottom" onClick={acknowledgeRisk}>
- {translate('plugin_risk_consent.action')}
- </Button>
- </div>
- </div>
- </div>
+ <Card
+ className="sw-body-md sw-min-w-[500px] sw-mx-auto sw-mt-[10vh] sw-w-[40%] sw-text-center"
+ data-testid="plugin-risk-consent-page"
+ >
+ <Title className="sw-mb-4">{translate('plugin_risk_consent.title')}</Title>
+
+ <p className="sw-mb-4">{translate('plugin_risk_consent.description')}</p>
+
+ <p className="sw-mb-6">{translate('plugin_risk_consent.description2')}</p>
+
+ <ButtonPrimary className="sw-my-4" onClick={acknowledgeRisk}>
+ {translate('plugin_risk_consent.action')}
+ </ButtonPrimary>
+ </Card>
+ </>
);
}
diff --git a/server/sonar-web/src/main/js/app/components/SonarLintConnection.css b/server/sonar-web/src/main/js/app/components/SonarLintConnection.css
deleted file mode 100644
index 26bdc7cde12..00000000000
--- a/server/sonar-web/src/main/js/app/components/SonarLintConnection.css
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2024 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.
- */
-.sonarlint-connection-page {
- padding-top: 10vh;
-}
-
-.sonarlint-connection-page h1 {
- line-height: 1.5;
- font-size: 24px;
- font-weight: 300;
- text-align: center;
-}
-
-.sonarlint-connection-content {
- min-width: 600px;
- width: 40%;
- margin: 0 auto;
-}
-
-.sonarlint-connection-page ol {
- list-style: inside decimal;
-}
-
-.sonarlint-connection-page .field-label {
- display: inline-block;
- width: 150px;
- color: var(--secondFontColor);
- text-align: start;
- flex-shrink: 0;
-}
-
-.sonarlint-connection-page .sonarlint-token-value {
- background-color: var(--codeBackground);
- border: 1px solid var(--barBorderColor);
- padding: calc(var(--gridSize) / 2) var(--gridSize);
-}
diff --git a/server/sonar-web/src/main/js/app/components/SonarLintConnection.tsx b/server/sonar-web/src/main/js/app/components/SonarLintConnection.tsx
index 6b8832dc414..4f8a19c4d40 100644
--- a/server/sonar-web/src/main/js/app/components/SonarLintConnection.tsx
+++ b/server/sonar-web/src/main/js/app/components/SonarLintConnection.tsx
@@ -17,19 +17,27 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+import {
+ ButtonPrimary,
+ Card,
+ CardSeparator,
+ CheckIcon,
+ ClipboardButton,
+ InputField,
+ Link,
+ ListItem,
+ Note,
+ OrderedList,
+ Title,
+} from 'design-system';
import * as React from 'react';
import { FormattedMessage } from 'react-intl';
import { useSearchParams } from 'react-router-dom';
-import Link from '../../components/common/Link';
-import { Button } from '../../components/controls/buttons';
-import { ClipboardButton } from '../../components/controls/clipboard';
import { whenLoggedIn } from '../../components/hoc/whenLoggedIn';
-import CheckIcon from '../../components/icons/CheckIcon';
import { translate, translateWithParameters } from '../../helpers/l10n';
import { generateSonarLintUserToken, portIsValid, sendUserToken } from '../../helpers/sonarlint';
import { NewUserToken } from '../../types/token';
import { LoggedInUser } from '../../types/users';
-import './SonarLintConnection.css';
enum Status {
request,
@@ -76,127 +84,98 @@ export function SonarLintConnection({ currentUser }: Readonly<Props>) {
}, [port, ideName, login]);
return (
- <div className="sonarlint-connection-page">
- <div className="sonarlint-connection-content boxed-group">
- <div className="boxed-group-inner text-center">
- {status === Status.request && (
- <>
- <h1 className="big-spacer-top big-spacer-bottom">
- {translate('sonarlint-connection.request.title')}
- </h1>
- <img
- alt=""
- aria-hidden
- className="big-spacer-top big-spacer-bottom"
- src="/images/SonarLint-connection-request.png"
- />
- <p className="big big-spacer-top big-spacer-bottom">
- {translateWithParameters('sonarlint-connection.request.description', ideName)}
- </p>
- <p className="big huge-spacer-bottom">
- {translate('sonarlint-connection.request.description2')}
- </p>
-
- <Button className="big-spacer-bottom" onClick={authorize}>
- <CheckIcon className="spacer-right" />
- {translate('sonarlint-connection.request.action')}
- </Button>
- </>
- )}
-
- {status === Status.tokenError && (
- <>
- <img
- alt=""
- aria-hidden
- className="big-spacer-top big-spacer-bottom padded-top"
- src="/images/cross.svg"
- />
- <h1 className="big-spacer-bottom">
- {translate('sonarlint-connection.token-error.title')}
- </h1>
- <p className="big big-spacer-top big-spacer-bottom">
- {translate('sonarlint-connection.token-error.description')}
- </p>
- <p className="big huge-spacer-bottom">
- <FormattedMessage
- id="sonarlint-connection.token-error.description2"
- defaultMessage={translate('sonarlint-connection.token-error.description2')}
- values={{
- link: (
- <Link to="/account/security">
- {translate('sonarlint-connection.token-error.description2.link')}
- </Link>
- ),
- }}
- />
- </p>
- </>
- )}
-
- {status === Status.tokenCreated && newToken && (
- <>
- <img
- alt=""
- aria-hidden
- className="big-spacer-top big-spacer-bottom padded-top"
- src="/images/check.svg"
- />
- <h1 className="big-spacer-bottom">
- {translate('sonarlint-connection.connection-error.title')}
- </h1>
- <p className="big big-spacer-top big-spacer-bottom">
- {translate('sonarlint-connection.connection-error.description')}
- </p>
- <div className="display-flex-center">
- <span className="field-label">
- {translate('sonarlint-connection.connection-error.token-name')}
- </span>
- {newToken.name}
- </div>
- <hr />
- <div className="display-flex-center">
- <span className="field-label">
- {translate('sonarlint-connection.connection-error.token-value')}
- </span>
- <span className="sonarlint-token-value">{newToken.token}</span>
- <ClipboardButton className="big-spacer-left" copyValue={newToken.token} />
- </div>
- <div className="big huge-spacer-top">
- <strong>{translate('sonarlint-connection.connection-error.next-steps')}</strong>
- </div>
- <ol className="big big-spacer-top big-spacer-bottom">
- <li>{translate('sonarlint-connection.connection-error.step1')}</li>
- <li>{translate('sonarlint-connection.connection-error.step2')}</li>
- </ol>
- </>
- )}
-
- {status === Status.tokenSent && newToken && (
- <>
- <h1 className="big-spacer-top big-spacer-bottom">
- {translate('sonarlint-connection.success.title')}
- </h1>
- <img
- alt=""
- aria-hidden
- className="big-spacer-bottom"
- src="/images/SonarLint-connection-ok.png"
- />
- <p className="big big-spacer-top big-spacer-bottom">
- {translateWithParameters('sonarlint-connection.success.description', newToken.name)}
- </p>
- <div className="big huge-spacer-top">
- <strong>{translate('sonarlint-connection.success.last-step')}</strong>
- </div>
- <div className="big big-spacer-top big-spacer-bottom">
- {translate('sonarlint-connection.success.step')}
- </div>
- </>
- )}
- </div>
- </div>
- </div>
+ <Card className="sw-mt-[10vh] sw-mx-auto sw-w-[650px] sw-text-center">
+ {status === Status.request && (
+ <>
+ <Title>{translate('sonarlint-connection.request.title')}</Title>
+ <img
+ alt=""
+ className="sw-my-4"
+ role="presentation"
+ src="/images/SonarLint-connection-request.png"
+ />
+ <p className="sw-my-4">
+ {translateWithParameters('sonarlint-connection.request.description', ideName)}
+ </p>
+ <p className="sw-mb-10">{translate('sonarlint-connection.request.description2')}</p>
+
+ <ButtonPrimary icon={<CheckIcon fill="currentColor" />} onClick={authorize}>
+ {translate('sonarlint-connection.request.action')}
+ </ButtonPrimary>
+ </>
+ )}
+
+ {status === Status.tokenError && (
+ <>
+ <img alt="" className="sw-my-4 sw-pt-2" role="presentation" src="/images/cross.svg" />
+ <Title>{translate('sonarlint-connection.token-error.title')}</Title>
+ <p className="sw-my-4">{translate('sonarlint-connection.token-error.description')}</p>
+ <p className="sw-mb-4">
+ <FormattedMessage
+ id="sonarlint-connection.token-error.description2"
+ defaultMessage={translate('sonarlint-connection.token-error.description2')}
+ values={{
+ link: (
+ <Link to="/account/security">
+ {translate('sonarlint-connection.token-error.description2.link')}
+ </Link>
+ ),
+ }}
+ />
+ </p>
+ </>
+ )}
+
+ {status === Status.tokenCreated && newToken && (
+ <>
+ <img alt="" className="sw-my-4 sw-pt-2" role="presentation" src="/images/check.svg" />
+ <Title>{translate('sonarlint-connection.connection-error.title')}</Title>
+ <p className="sw-my-6">
+ {translate('sonarlint-connection.connection-error.description')}
+ </p>
+ <div className="sw-flex sw-items-center">
+ <Note className="sw-w-abs-150 sw-text-start">
+ {translate('sonarlint-connection.connection-error.token-name')}
+ </Note>
+ {newToken.name}
+ </div>
+ <CardSeparator className="sw-my-3" />
+ <div className="sw-flex sw-items-center">
+ <Note className="sw-min-w-abs-150 sw-text-start">
+ {translate('sonarlint-connection.connection-error.token-value')}
+ </Note>
+ <InputField className="sw-cursor-text" disabled size="full" value={newToken.token} />
+ <ClipboardButton className="sw-ml-2" copyValue={newToken.token} />
+ </div>
+ <div className="sw-mt-10">
+ <strong>{translate('sonarlint-connection.connection-error.next-steps')}</strong>
+ </div>
+ <OrderedList className="sw-list-inside sw-mb-4">
+ <ListItem>{translate('sonarlint-connection.connection-error.step1')}</ListItem>
+ <ListItem>{translate('sonarlint-connection.connection-error.step2')}</ListItem>
+ </OrderedList>
+ </>
+ )}
+
+ {status === Status.tokenSent && newToken && (
+ <>
+ <Title>{translate('sonarlint-connection.success.title')}</Title>
+ <img
+ alt=""
+ className="sw-mb-4"
+ role="presentation"
+ src="/images/SonarLint-connection-ok.png"
+ />
+ <p className="sw-my-4">
+ {translateWithParameters('sonarlint-connection.success.description', newToken.name)}
+ </p>
+ <div className="sw-mt-10">
+ <strong>{translate('sonarlint-connection.success.last-step')}</strong>
+ </div>
+ <div className="sw-my-4">{translate('sonarlint-connection.success.step')}</div>
+ </>
+ )}
+ </Card>
);
}
diff --git a/server/sonar-web/src/main/js/app/components/SystemAnnouncement.css b/server/sonar-web/src/main/js/app/components/SystemAnnouncement.css
deleted file mode 100644
index aec6caf4da1..00000000000
--- a/server/sonar-web/src/main/js/app/components/SystemAnnouncement.css
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2024 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.
- */
-.system-announcement-wrapper {
- min-height: 34px;
-}
-
-.system-announcement-banner {
- box-sizing: border-box;
- width: 100%;
- z-index: var(--globalBannerZIndex);
- margin-bottom: 0% !important;
-}
diff --git a/server/sonar-web/src/main/js/app/components/SystemAnnouncement.tsx b/server/sonar-web/src/main/js/app/components/SystemAnnouncement.tsx
index c80e8c966ad..b64f10b404b 100644
--- a/server/sonar-web/src/main/js/app/components/SystemAnnouncement.tsx
+++ b/server/sonar-web/src/main/js/app/components/SystemAnnouncement.tsx
@@ -17,14 +17,13 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import classNames from 'classnames';
+import styled from '@emotion/styled';
+import { FlagWarningIcon, themeBorder, themeColor } from 'design-system';
import { keyBy, throttle } from 'lodash';
import * as React from 'react';
import { getValues } from '../../api/settings';
-import { Alert } from '../../components/ui/Alert';
import { Feature } from '../../types/features';
import { GlobalSettingKeys, SettingValue } from '../../types/settings';
-import './SystemAnnouncement.css';
import withAvailableFeatures, {
WithAvailableFeaturesProps,
} from './available-features/withAvailableFeatures';
@@ -78,20 +77,28 @@ export class SystemAnnouncement extends React.PureComponent<WithAvailableFeature
const { displayMessage, message } = this.state;
return (
- <div className={classNames({ 'system-announcement-wrapper': displayMessage && message })}>
- <Alert
- className="system-announcement-banner"
- title={message}
- display="banner"
- variant="warning"
- aria-live="assertive"
- role="alert"
- >
- {displayMessage && message}
- </Alert>
- </div>
+ <StyledBanner
+ className="sw-py-3 sw-px-4 sw-gap-3"
+ style={!(displayMessage && message.length > 0) ? { display: 'none' } : {}}
+ title={message}
+ aria-live="assertive"
+ role="alert"
+ >
+ <FlagWarningIcon />
+ <span>{displayMessage && message}</span>
+ </StyledBanner>
);
}
}
export default withAvailableFeatures(SystemAnnouncement);
+
+const StyledBanner = styled.div`
+ display: flex;
+ align-items: center;
+ box-sizing: border-box;
+ width: 100%;
+
+ background-color: ${themeColor('warningBackground')};
+ border-bottom: ${themeBorder('default', 'warningBorder')};
+`;
diff --git a/server/sonar-web/src/main/js/app/components/__tests__/NonAdminPagesContainer-test.tsx b/server/sonar-web/src/main/js/app/components/__tests__/NonAdminPagesContainer-test.tsx
index 20e7da86992..01f497a8d08 100644
--- a/server/sonar-web/src/main/js/app/components/__tests__/NonAdminPagesContainer-test.tsx
+++ b/server/sonar-web/src/main/js/app/components/__tests__/NonAdminPagesContainer-test.tsx
@@ -23,8 +23,8 @@ import { MemoryRouter, Route, Routes } from 'react-router-dom';
import { mockComponent } from '../../../helpers/mocks/component';
import { ComponentContextShape, ComponentQualifier } from '../../../types/component';
import { Component } from '../../../types/types';
-import { ComponentContext } from '../componentContext/ComponentContext';
import NonAdminPagesContainer from '../NonAdminPagesContainer';
+import { ComponentContext } from '../componentContext/ComponentContext';
function Child() {
return <div>Test Child</div>;
@@ -34,7 +34,7 @@ it('should render correctly for an user that does not have access to all childre
renderNonAdminPagesContainer(
mockComponent({ qualifier: ComponentQualifier.Application, canBrowseAllChildProjects: false }),
);
- expect(screen.getByText('application.cannot_access_all_child_projects1')).toBeInTheDocument();
+ expect(screen.getByText(/^application.cannot_access_all_child_projects1/)).toBeInTheDocument();
});
it('should render correctly', () => {
diff --git a/server/sonar-web/src/main/js/app/components/__tests__/SonarLintConnection-test.tsx b/server/sonar-web/src/main/js/app/components/__tests__/SonarLintConnection-test.tsx
index bf9552c0d2a..1914cdf185f 100644
--- a/server/sonar-web/src/main/js/app/components/__tests__/SonarLintConnection-test.tsx
+++ b/server/sonar-web/src/main/js/app/components/__tests__/SonarLintConnection-test.tsx
@@ -112,7 +112,7 @@ it('should handle connection errors', async () => {
).toBeInTheDocument();
const tokenValue = tokenMock.getLastToken()?.token ?? '';
- expect(await screen.findByText(tokenValue)).toBeInTheDocument();
+ expect(await screen.findByRole('textbox')).toHaveValue(tokenValue);
});
it('should require authentication if user is not logged in', () => {
diff --git a/server/sonar-web/src/main/js/app/components/extensions/Extension.tsx b/server/sonar-web/src/main/js/app/components/extensions/Extension.tsx
index 3d036ba3d86..1b79c3fad1c 100644
--- a/server/sonar-web/src/main/js/app/components/extensions/Extension.tsx
+++ b/server/sonar-web/src/main/js/app/components/extensions/Extension.tsx
@@ -17,6 +17,7 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+
import { withTheme } from '@emotion/react';
import { QueryClient } from '@tanstack/react-query';
import { Theme } from 'design-system';
@@ -35,30 +36,29 @@ import { AppState } from '../../../types/appstate';
import { ExtensionStartMethod } from '../../../types/extension';
import { Dict, Extension as TypeExtension } from '../../../types/types';
import { CurrentUser, HomePage } from '../../../types/users';
-import * as theme from '../../theme';
import withAppStateContext from '../app-state/withAppStateContext';
import withCurrentUserContext from '../current-user/withCurrentUserContext';
export interface ExtensionProps extends WrappedComponentProps {
- theme: Theme;
appState: AppState;
currentUser: CurrentUser;
extension: TypeExtension;
location: Location;
- options?: Dict<any>;
- router: Router;
+ options?: Dict<unknown>;
queryClient: QueryClient;
+ router: Router;
+ theme: Theme;
updateCurrentUserHomepage: (homepage: HomePage) => void;
}
interface State {
- extensionElement?: React.ReactElement<any>;
+ extensionElement?: React.ReactElement<unknown>;
}
class Extension extends React.PureComponent<ExtensionProps, State> {
container?: HTMLElement | null;
- stop?: Function;
state: State = {};
+ stop?: Function;
componentDidMount() {
this.startExtension();
@@ -78,23 +78,22 @@ class Extension extends React.PureComponent<ExtensionProps, State> {
}
handleStart = (start: ExtensionStartMethod) => {
- const { theme: dsTheme, queryClient } = this.props;
+ const { theme, queryClient } = this.props;
+
const result = start({
appState: this.props.appState,
- el: this.container,
+ baseUrl: getBaseUrl(),
currentUser: this.props.currentUser,
+ el: this.container,
intl: this.props.intl,
+ l10nBundle: getCurrentL10nBundle(),
location: this.props.location,
+ queryClient,
router: this.props.router,
theme,
- // New theme from design-system, we should drop old theme once the migration to miui is done
- dsTheme,
- baseUrl: getBaseUrl(),
- l10nBundle: getCurrentL10nBundle(),
// See SONAR-16207 and core-extension-enterprise-server/src/main/js/portfolios/components/Header.tsx
// for more information on why we're passing this as a prop to an extension.
updateCurrentUserHomepage: this.props.updateCurrentUserHomepage,
- queryClient,
...this.props.options,
});
diff --git a/server/sonar-web/src/main/js/app/components/indexation/IndexationNotification.css b/server/sonar-web/src/main/js/app/components/indexation/IndexationNotification.css
deleted file mode 100644
index 187579b9109..00000000000
--- a/server/sonar-web/src/main/js/app/components/indexation/IndexationNotification.css
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2024 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.
- */
-.indexation-notification-wrapper {
- height: 34px;
-}
-
-.indexation-notification-banner {
- width: 100%;
- z-index: var(--globalBannerZIndex);
- margin-bottom: 0 !important;
-}
diff --git a/server/sonar-web/src/main/js/app/components/indexation/IndexationNotification.tsx b/server/sonar-web/src/main/js/app/components/indexation/IndexationNotification.tsx
index bfbeafe96eb..665c9f333e9 100644
--- a/server/sonar-web/src/main/js/app/components/indexation/IndexationNotification.tsx
+++ b/server/sonar-web/src/main/js/app/components/indexation/IndexationNotification.tsx
@@ -26,7 +26,6 @@ import { IndexationNotificationType } from '../../../types/indexation';
import { Permissions } from '../../../types/permissions';
import { CurrentUser, isLoggedIn } from '../../../types/users';
import withCurrentUserContext from '../current-user/withCurrentUserContext';
-import './IndexationNotification.css';
import IndexationNotificationHelper from './IndexationNotificationHelper';
import IndexationNotificationRenderer from './IndexationNotificationRenderer';
diff --git a/server/sonar-web/src/main/js/app/components/indexation/IndexationNotificationRenderer.tsx b/server/sonar-web/src/main/js/app/components/indexation/IndexationNotificationRenderer.tsx
index bc46d8afb4d..5bb714c9697 100644
--- a/server/sonar-web/src/main/js/app/components/indexation/IndexationNotificationRenderer.tsx
+++ b/server/sonar-web/src/main/js/app/components/indexation/IndexationNotificationRenderer.tsx
@@ -19,12 +19,20 @@
*/
/* eslint-disable react/no-unused-prop-types */
-import classNames from 'classnames';
+import styled from '@emotion/styled';
+import {
+ FlagErrorIcon,
+ FlagSuccessIcon,
+ FlagWarningIcon,
+ Link,
+ Spinner,
+ ThemeColors,
+ themeBorder,
+ themeColor,
+} from 'design-system';
import * as React from 'react';
import { FormattedMessage } from 'react-intl';
-import DocLink from '../../../components/common/DocLink';
-import Link from '../../../components/common/Link';
-import { Alert, AlertProps } from '../../../components/ui/Alert';
+import DocumentationLink from '../../../components/common/DocumentationLink';
import { translate, translateWithParameters } from '../../../helpers/l10n';
import { queryToSearch } from '../../../helpers/urls';
import { IndexationNotificationType } from '../../../types/indexation';
@@ -36,26 +44,41 @@ export interface IndexationNotificationRendererProps {
type?: IndexationNotificationType;
}
-const NOTIFICATION_VARIANTS: { [key in IndexationNotificationType]: AlertProps['variant'] } = {
- [IndexationNotificationType.InProgress]: 'warning',
- [IndexationNotificationType.InProgressWithFailure]: 'error',
- [IndexationNotificationType.Completed]: 'success',
- [IndexationNotificationType.CompletedWithFailure]: 'error',
+const NOTIFICATION_COLORS: {
+ [key in IndexationNotificationType]: { background: ThemeColors; border: ThemeColors };
+} = {
+ [IndexationNotificationType.InProgress]: {
+ background: 'warningBackground',
+ border: 'warningBorder',
+ },
+ [IndexationNotificationType.InProgressWithFailure]: {
+ background: 'errorBackground',
+ border: 'errorBorder',
+ },
+ [IndexationNotificationType.Completed]: {
+ background: 'successBackground',
+ border: 'successBorder',
+ },
+ [IndexationNotificationType.CompletedWithFailure]: {
+ background: 'errorBackground',
+ border: 'errorBorder',
+ },
};
export default function IndexationNotificationRenderer(props: IndexationNotificationRendererProps) {
const { completedCount, total, type } = props;
return (
- <div className={classNames({ 'indexation-notification-wrapper': type })}>
- <Alert
- className="indexation-notification-banner"
- display="banner"
- variant={type ? NOTIFICATION_VARIANTS[type] : 'success'}
+ <div className={type === undefined ? 'sw-hidden' : ''}>
+ <StyledBanner
+ className="sw-body-sm sw-py-3 sw-px-4 sw-gap-4"
+ type={type ?? IndexationNotificationType.Completed}
aria-live="assertive"
+ role="alert"
>
{type !== undefined && (
- <div className="display-flex-center">
+ <>
+ {renderIcon(type)}
{type === IndexationNotificationType.Completed && renderCompletedBanner()}
{type === IndexationNotificationType.CompletedWithFailure &&
@@ -66,20 +89,34 @@ export default function IndexationNotificationRenderer(props: IndexationNotifica
{type === IndexationNotificationType.InProgressWithFailure &&
renderInProgressWithFailureBanner(completedCount as number, total as number)}
- </div>
+ </>
)}
- </Alert>
+ </StyledBanner>
</div>
);
}
+function renderIcon(type: IndexationNotificationType) {
+ switch (type) {
+ case IndexationNotificationType.Completed:
+ return <FlagSuccessIcon />;
+ case IndexationNotificationType.CompletedWithFailure:
+ case IndexationNotificationType.InProgressWithFailure:
+ return <FlagErrorIcon />;
+ case IndexationNotificationType.InProgress:
+ return <FlagWarningIcon />;
+ default:
+ return null;
+ }
+}
+
function renderCompletedBanner() {
- return <span className="spacer-right">{translate('indexation.completed')}</span>;
+ return <span>{translate('indexation.completed')}</span>;
}
function renderCompletedWithFailureBanner() {
return (
- <span className="spacer-right">
+ <span>
<FormattedMessage
defaultMessage={translate('indexation.completed_with_error')}
id="indexation.completed_with_error"
@@ -97,7 +134,7 @@ function renderCompletedWithFailureBanner() {
function renderInProgressBanner(completedCount: number, total: number) {
return (
<>
- <span className="spacer-right">
+ <span>
<FormattedMessage id="indexation.in_progress" />{' '}
<FormattedMessage
id="indexation.features_partly_available"
@@ -106,9 +143,9 @@ function renderInProgressBanner(completedCount: number, total: number) {
}}
/>
</span>
- <i className="spinner spacer-right" />
- <span className="spacer-right">
+ <span className="sw-flex sw-items-center">
+ <Spinner className="sw-mr-1 -sw-mb-1/2" />
{translateWithParameters(
'indexation.progression',
completedCount.toString(),
@@ -116,7 +153,7 @@ function renderInProgressBanner(completedCount: number, total: number) {
)}
</span>
- <span className="spacer-right">
+ <span>
<FormattedMessage
id="indexation.admin_link"
defaultMessage={translate('indexation.admin_link')}
@@ -132,7 +169,7 @@ function renderInProgressBanner(completedCount: number, total: number) {
function renderInProgressWithFailureBanner(completedCount: number, total: number) {
return (
<>
- <span className="spacer-right">
+ <span>
<FormattedMessage id="indexation.in_progress" />{' '}
<FormattedMessage
id="indexation.features_partly_available"
@@ -141,17 +178,15 @@ function renderInProgressWithFailureBanner(completedCount: number, total: number
}}
/>
</span>
- <i className="spinner spacer-right" />
- <span className="spacer-right">
+ <span className="sw-flex sw-items-center">
+ <Spinner className="sw-mr-1 -sw-mb-1/2" />
<FormattedMessage
+ tagName="span"
id="indexation.progression_with_error"
- defaultMessage={translateWithParameters(
- 'indexation.progression_with_error',
- completedCount.toString(),
- total.toString(),
- )}
values={{
+ count: completedCount,
+ total,
link: renderBackgroundTasksPageLink(
true,
translate('indexation.progression_with_error.link'),
@@ -181,8 +216,18 @@ function renderBackgroundTasksPageLink(hasError: boolean, text: string) {
function renderIndexationDocPageLink() {
return (
- <DocLink to="/instance-administration/reindexing/">
+ <DocumentationLink className="sw-whitespace-nowrap" to="/instance-administration/reindexing/">
<FormattedMessage id="indexation.features_partly_available.link" />
- </DocLink>
+ </DocumentationLink>
);
}
+
+const StyledBanner = styled.div<{ type: IndexationNotificationType }>`
+ display: flex;
+ align-items: center;
+ box-sizing: border-box;
+ width: 100%;
+
+ background-color: ${({ type }) => themeColor(NOTIFICATION_COLORS[type].background)};
+ border-bottom: ${({ type }) => themeBorder('default', NOTIFICATION_COLORS[type].border)};
+`;
diff --git a/server/sonar-web/src/main/js/app/components/indexation/PageUnavailableDueToIndexation.tsx b/server/sonar-web/src/main/js/app/components/indexation/PageUnavailableDueToIndexation.tsx
index 03444d3b0cc..c31b3f3d2f4 100644
--- a/server/sonar-web/src/main/js/app/components/indexation/PageUnavailableDueToIndexation.tsx
+++ b/server/sonar-web/src/main/js/app/components/indexation/PageUnavailableDueToIndexation.tsx
@@ -17,7 +17,7 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import { FlagMessage, Link } from 'design-system';
+import { CenteredLayout, FlagMessage, Link } from 'design-system';
import * as React from 'react';
import { FormattedMessage } from 'react-intl';
import withIndexationContext, {
@@ -37,8 +37,8 @@ export class PageUnavailableDueToIndexation extends React.PureComponent<WithInde
render() {
return (
- <div className="page-wrapper-simple">
- <FlagMessage className="sw-m-10" variant="info">
+ <CenteredLayout className="sw-flex sw-justify-around">
+ <FlagMessage className="sw-mt-32" variant="info">
<span className="sw-w-[290px]">
{translate('indexation.page_unavailable.description')}
<span className="sw-ml-1">
@@ -58,7 +58,7 @@ export class PageUnavailableDueToIndexation extends React.PureComponent<WithInde
</span>
</span>
</FlagMessage>
- </div>
+ </CenteredLayout>
);
}
}
diff --git a/server/sonar-web/src/main/js/app/components/indexation/__tests__/IndexationNotification-test.tsx b/server/sonar-web/src/main/js/app/components/indexation/__tests__/IndexationNotification-test.tsx
index 71426cd607f..d387a7e3554 100644
--- a/server/sonar-web/src/main/js/app/components/indexation/__tests__/IndexationNotification-test.tsx
+++ b/server/sonar-web/src/main/js/app/components/indexation/__tests__/IndexationNotification-test.tsx
@@ -103,7 +103,7 @@ describe('Completed banner', () => {
/>,
);
- expect(byText('indexation.progression_with_error').get()).toBeInTheDocument();
+ expect(byText(/^indexation\.progression_with_error\.link/).get()).toBeInTheDocument();
rerender(
<IndexationNotification
diff --git a/server/sonar-web/src/main/js/app/components/nav/component/ComponentNavProjectBindingErrorNotif.tsx b/server/sonar-web/src/main/js/app/components/nav/component/ComponentNavProjectBindingErrorNotif.tsx
index 9514729d9e4..803a51eaddf 100644
--- a/server/sonar-web/src/main/js/app/components/nav/component/ComponentNavProjectBindingErrorNotif.tsx
+++ b/server/sonar-web/src/main/js/app/components/nav/component/ComponentNavProjectBindingErrorNotif.tsx
@@ -17,11 +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.
*/
+import styled from '@emotion/styled';
+import { FlagWarningIcon, Link, themeBorder, themeColor } from 'design-system';
import * as React from 'react';
import { FormattedMessage } from 'react-intl';
import { PULL_REQUEST_DECORATION_BINDING_CATEGORY } from '../../../../apps/settings/constants';
-import Link from '../../../../components/common/Link';
-import { Alert } from '../../../../components/ui/Alert';
import { translate } from '../../../../helpers/l10n';
import { getProjectSettingsUrl } from '../../../../helpers/urls';
import { Component } from '../../../../types/types';
@@ -47,12 +47,20 @@ export default function ComponentNavProjectBindingErrorNotif(
}
return (
- <Alert display="banner" variant="warning">
- <FormattedMessage
- defaultMessage={translate('component_navigation.pr_deco.error_detected_X')}
- id="component_navigation.pr_deco.error_detected_X"
- values={{ action }}
- />
- </Alert>
+ <StyledBanner className="sw-body-sm sw-py-3 sw-px-4 sw-gap-4">
+ <FlagWarningIcon />
+ <FormattedMessage id="component_navigation.pr_deco.error_detected_X" values={{ action }} />
+ </StyledBanner>
);
}
+
+const StyledBanner = styled.div`
+ display: flex;
+ align-items: center;
+ box-sizing: border-box;
+ width: 100%;
+
+ background-color: ${themeColor('warningBackground')};
+ border-top: ${themeBorder('default', 'warningBorder')};
+ border-bottom: ${themeBorder('default', 'warningBorder')};
+`;
diff --git a/server/sonar-web/src/main/js/app/components/promotion-notification/PromotionNotification.tsx b/server/sonar-web/src/main/js/app/components/promotion-notification/PromotionNotification.tsx
index 35ae56d1015..cd423d7a912 100644
--- a/server/sonar-web/src/main/js/app/components/promotion-notification/PromotionNotification.tsx
+++ b/server/sonar-web/src/main/js/app/components/promotion-notification/PromotionNotification.tsx
@@ -45,19 +45,17 @@ export function PromotionNotification(props: CurrentUserContextInterface) {
};
return (
- <div className="toaster display-flex-center big-padded-left big-padded-right">
- <div className="toaster-icon spacer-right">
+ <div className="toaster sw-flex sw-items-center sw-px-4">
+ <div className="sw-mr-2">
<img alt="SonarQube + SonarLint" height={80} src={`${getBaseUrl()}/images/sq-sl.svg`} />
</div>
- <div className="toaster-content flex-1 padded-left padded-right big-padded-top big-padded-bottom">
- <span className="toaster-title text-bold medium">
- {translate('promotion.sonarlint.title')}
- </span>
- <p className="spacer-top">{translate('promotion.sonarlint.content')}</p>
+ <div className="toaster-content sw-flex-1 sw-px-2 sw-py-4">
+ <span className="sw-body-sm-highlight">{translate('promotion.sonarlint.title')}</span>
+ <p className="sw-mt-2">{translate('promotion.sonarlint.content')}</p>
</div>
<div className="toaster-actions spacer-left padded-left display-flex-column display-flex-center">
<a
- className="button button-primary big-spacer-bottom"
+ className="button button-primary sw-mb-4"
href="https://www.sonarsource.com/products/sonarlint/?referrer=sonarqube-welcome"
rel="noreferrer"
onClick={onClick}
diff --git a/server/sonar-web/src/main/js/app/components/update-notification/UpdateNotification.css b/server/sonar-web/src/main/js/app/components/update-notification/UpdateNotification.css
deleted file mode 100644
index baff8ae2ecf..00000000000
--- a/server/sonar-web/src/main/js/app/components/update-notification/UpdateNotification.css
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2024 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.
- */
-.promote-update-notification.dismissable-alert-wrapper {
- height: 42px;
-}
-
-.promote-update-notification .dismissable-alert-banner {
- margin-bottom: 0 !important;
- width: 100%;
- z-index: var(--globalBannerZIndex);
-}
diff --git a/server/sonar-web/src/main/js/app/components/update-notification/UpdateNotification.tsx b/server/sonar-web/src/main/js/app/components/update-notification/UpdateNotification.tsx
index 04cc3b0c81a..4ed1ad44c54 100644
--- a/server/sonar-web/src/main/js/app/components/update-notification/UpdateNotification.tsx
+++ b/server/sonar-web/src/main/js/app/components/update-notification/UpdateNotification.tsx
@@ -17,11 +17,10 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import { Variant } from 'design-system';
+import { Banner, Variant } from 'design-system';
import { groupBy, isEmpty, mapValues } from 'lodash';
import * as React from 'react';
import { getSystemUpgrades } from '../../../api/system';
-import { Alert } from '../../../components/ui/Alert';
import DismissableAlert from '../../../components/ui/DismissableAlert';
import SystemUpgradeButton from '../../../components/upgrade/SystemUpgradeButton';
import { UpdateUseCase, sortUpgrades } from '../../../components/upgrade/utils';
@@ -34,7 +33,6 @@ import { Dict } from '../../../types/types';
import { CurrentUser, isLoggedIn } from '../../../types/users';
import withAppStateContext from '../app-state/withAppStateContext';
import withCurrentUserContext from '../current-user/withCurrentUserContext';
-import './UpdateNotification.css';
const MONTH_BEFOR_PREVIOUS_LTS_NOTIFICATION = 6;
@@ -223,7 +221,7 @@ export class UpdateNotification extends React.PureComponent<Props, State> {
<DismissableAlert
alertKey={dismissKey}
variant={MAP_VARIANT[useCase]}
- className={`promote-update-notification it__upgrade-prompt-${useCase}`}
+ className={`it__promote-update-notification it__upgrade-prompt-${useCase}`}
>
{translate('admin_notification.update', useCase)}
<SystemUpgradeButton
@@ -233,14 +231,14 @@ export class UpdateNotification extends React.PureComponent<Props, State> {
/>
</DismissableAlert>
) : (
- <Alert variant={MAP_VARIANT[useCase]} className={`it__upgrade-prompt-${useCase}`}>
+ <Banner variant={MAP_VARIANT[useCase]} className={`it__upgrade-prompt-${useCase}`}>
{translate('admin_notification.update', useCase)}
<SystemUpgradeButton
systemUpgrades={systemUpgrades}
updateUseCase={useCase}
latestLTS={latestLTS}
/>
- </Alert>
+ </Banner>
);
}
}
diff --git a/server/sonar-web/src/main/js/components/ui/__tests__/DismissableAlertComponent-test.tsx b/server/sonar-web/src/main/js/app/styles/GlobalStyles.tsx
index 6fd6ead8b13..8db4ac406d6 100644
--- a/server/sonar-web/src/main/js/components/ui/__tests__/DismissableAlertComponent-test.tsx
+++ b/server/sonar-web/src/main/js/app/styles/GlobalStyles.tsx
@@ -17,31 +17,40 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import userEvent from '@testing-library/user-event';
+
+import { Global, css, useTheme } from '@emotion/react';
+import { themeColor } from 'design-system/lib';
import React from 'react';
-import { renderComponent } from '../../../helpers/testReactTestingUtils';
-import { byLabelText, byText } from '../../../helpers/testSelector';
-import DismissableAlertComponent, {
- DismissableAlertComponentProps,
-} from '../DismissableAlertComponent';
+import twDefaultTheme from 'tailwindcss/defaultTheme';
+
+export function GlobalStyles() {
+ const theme = useTheme();
+
+ return (
+ <Global
+ styles={css`
+ body {
+ font-family: Inter, ${twDefaultTheme.fontFamily.sans.join(', ')};
+ font-size: 0.875rem;
+ line-height: 1.25rem;
+ font-weight: 400;
-it('should render with children', () => {
- render();
- expect(byText('testing').get()).toBeVisible();
-});
+ color: ${themeColor('pageContent')({ theme })};
+ background-color: ${themeColor('backgroundPrimary')({ theme })};
+ }
-it('calls onDismiss', async () => {
- const onDismiss = jest.fn();
- render({ onDismiss });
- const user = userEvent.setup();
- await user.click(byLabelText('alert.dismiss').get());
- expect(onDismiss).toHaveBeenCalled();
-});
+ a {
+ outline: none;
+ text-decoration: none;
+ color: ${themeColor('pageContent')({ theme })};
+ }
-function render(props: Partial<DismissableAlertComponentProps> = {}) {
- return renderComponent(
- <DismissableAlertComponent onDismiss={jest.fn()} variant="info" {...props}>
- testing
- </DismissableAlertComponent>,
+ ol,
+ ul {
+ padding-left: 0;
+ list-style: none;
+ }
+ `}
+ />
);
}
diff --git a/server/sonar-web/src/main/js/app/styles/components/badges.css b/server/sonar-web/src/main/js/app/styles/components/badges.css
deleted file mode 100644
index d1e3398155e..00000000000
--- a/server/sonar-web/src/main/js/app/styles/components/badges.css
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2024 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.
- */
-.badge {
- display: inline-block;
- padding: 4px;
- background-color: var(--barBorderColor);
- border-radius: 3px;
- font-size: var(--smallFontSize);
- font-weight: 600;
- color: var(--baseFontColor);
- text-transform: uppercase;
- white-space: nowrap;
- line-height: 8px;
-}
-.badge:empty {
- display: none;
-}
-
-a.badge:hover,
-a.badge:focus,
-a.badge:active {
- text-decoration: underline;
-}
-a.badge {
- border-bottom: none;
-}
-
-.list-group-item-heading > .badge {
- float: right;
- margin: 3px;
-}
-.list-group-item-heading > .badge + .badge {
- margin-right: 5px;
-}
-
-.badge-info {
- background-color: var(--alertBackgroundInfo);
- color: var(--alertTextInfo);
-}
-
-.badge-success {
- background-color: var(--alertBackgroundSuccess);
- color: var(--alertTextSuccess);
-}
-
-.badge-warning {
- background-color: var(--alertBackgroundWarning);
- color: var(--alertTextWarning);
-}
-
-.badge-error {
- background-color: var(--alertBackgroundError);
- color: var(--alertTextError);
-}
-
-.counter-badge {
- color: var(--badgeBlueColor);
- background-color: var(--badgeBlueBackground);
- font-size: var(--smallFontSize);
- padding: 0.3em 0.8em;
- border-radius: 1em;
-}
-
-.counter-badge:empty {
- display: none;
-}
diff --git a/server/sonar-web/src/main/js/app/styles/components/boxed-group.css b/server/sonar-web/src/main/js/app/styles/components/boxed-group.css
index 687f776c397..02b5cb0f788 100644
--- a/server/sonar-web/src/main/js/app/styles/components/boxed-group.css
+++ b/server/sonar-web/src/main/js/app/styles/components/boxed-group.css
@@ -51,26 +51,6 @@
margin: calc(2 * var(--gridSize)) -20px;
}
-.boxed-group-header {
- position: relative;
- z-index: 10;
- padding: calc(2 * var(--gridSize)) 20px 0;
-}
-
-.boxed-group-header > h2 {
- display: inline-block;
- vertical-align: middle;
- line-height: var(--controlHeight);
-}
-
-.boxed-group-actions {
- position: relative;
- z-index: 12;
- float: right;
- margin-top: calc(2 * var(--gridSize));
- margin-right: 20px;
-}
-
.boxed-group-inner {
padding: calc(2 * var(--gridSize)) 20px;
}
@@ -101,15 +81,6 @@
border-color: var(--info400);
}
-.boxed-group-accordion .boxed-group-header {
- cursor: pointer;
- padding-bottom: calc(2 * var(--gridSize));
-}
-
-.boxed-group-accordion.not-clickable .boxed-group-header {
- cursor: default;
-}
-
.boxed-group-accordion.not-clickable .boxed-group-accordion-title > svg {
display: none;
}
diff --git a/server/sonar-web/src/main/js/app/styles/components/columns.css b/server/sonar-web/src/main/js/app/styles/components/columns.css
deleted file mode 100644
index ed27c68728e..00000000000
--- a/server/sonar-web/src/main/js/app/styles/components/columns.css
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2024 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.
- */
-.columns {
- margin-left: -10px;
- margin-right: -10px;
- overflow: hidden;
-}
-
-.columns:before,
-.columns:after {
- display: table;
- content: '';
- line-height: 0;
-}
-
-.columns:after {
- clear: both;
-}
-
-.column-half {
- float: left;
- width: 50%;
- padding: 0 10px;
- box-sizing: border-box;
-}
-
-.column-half.column-one {
- margin: 0 25%;
-}
-
-.flex-columns {
- display: flex;
-}
-
-.flex-column + .flex-column {
- margin-left: 20px;
-}
-
-.flex-column-full {
- width: 100%;
-}
-
-.flex-column-half {
- width: 50%;
-}
-
-.flex-column-third {
- width: calc(100% / 3);
-}
diff --git a/server/sonar-web/src/main/js/app/styles/components/component-name.css b/server/sonar-web/src/main/js/app/styles/components/component-name.css
deleted file mode 100644
index 78658b1d06b..00000000000
--- a/server/sonar-web/src/main/js/app/styles/components/component-name.css
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2024 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.
- */
-.component-name {
- line-height: 16px;
- font-size: var(--smallFontSize);
-}
-
-.component-name:before,
-.component-name:after {
- display: table;
- content: '';
- line-height: 0;
-}
-
-.component-name:after {
- clear: both;
-}
-
-.component-name-parent {
- float: left;
- margin-right: 20px;
-}
-
-.component-name-parent:last-child {
- margin-right: 0;
-}
-
-.component-name-path {
- float: left;
- clear: left;
-}
-
-.component-name-parent + .component-name-path {
- margin-top: 4px;
-}
-
-.component-name-favorite {
- margin-left: 4px;
- padding: 0;
-}
diff --git a/server/sonar-web/src/main/js/app/styles/components/dropdowns.css b/server/sonar-web/src/main/js/app/styles/components/dropdowns.css
deleted file mode 100644
index 0bf8a21f41d..00000000000
--- a/server/sonar-web/src/main/js/app/styles/components/dropdowns.css
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2024 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.
- */
-.dropdown {
- position: relative;
- display: inline-block;
- vertical-align: middle;
-}
-
-.dropdown-bottom-hint {
- line-height: 16px;
- margin-bottom: -5px;
- padding: 5px 10px;
- border-top: 1px solid var(--barBorderColor);
- background-color: var(--barBackgroundColor);
- color: var(--secondFontColor);
- font-size: 11px;
-}
diff --git a/server/sonar-web/src/main/js/app/styles/components/global-loading.css b/server/sonar-web/src/main/js/app/styles/components/global-loading.css
index 838578b6bdc..5aa658b9656 100644
--- a/server/sonar-web/src/main/js/app/styles/components/global-loading.css
+++ b/server/sonar-web/src/main/js/app/styles/components/global-loading.css
@@ -21,12 +21,42 @@
width: 300px;
margin: 200px auto 0;
white-space: nowrap;
+ font-family:
+ Inter,
+ ui-sans-serif,
+ system-ui,
+ -apple-system,
+ BlinkMacSystemFont,
+ 'Segoe UI',
+ Roboto,
+ 'Helvetica Neue',
+ Arial,
+ 'Noto Sans',
+ sans-serif,
+ 'Apple Color Emoji',
+ 'Segoe UI Emoji',
+ 'Segoe UI Symbol',
+ 'Noto Color Emoji';
+ color: rgb(62, 67, 87);
}
-.global-loading .global-loading-spinner {
+.global-loading-spinner {
+ display: inline-block;
+ box-sizing: border-box;
vertical-align: middle;
width: 80px;
height: 80px;
+ border: 2px solid transparent;
+ border-radius: 625rem;
+ background:
+ linear-gradient(0deg, rgb(93, 108, 208) 50%, transparent 50% 100%) border-box,
+ linear-gradient(90deg, rgb(93, 108, 208) 25%, transparent 75% 100%) border-box;
+ mask:
+ linear-gradient(white 0 0) padding-box,
+ linear-gradient(white 0 0);
+ -webkit-mask-composite: xor;
+ mask-composite: exclude;
+ animation: global-loading-spin 1s infinite linear;
}
.global-loading-text {
@@ -36,3 +66,12 @@
font-size: 36px;
font-weight: 300;
}
+
+@keyframes global-loading-spin {
+ from {
+ transform: rotate(0deg);
+ }
+ to {
+ transform: rotate(-360deg);
+ }
+}
diff --git a/server/sonar-web/src/main/js/app/styles/components/issues.css b/server/sonar-web/src/main/js/app/styles/components/issues.css
deleted file mode 100644
index 0d066e7e6c6..00000000000
--- a/server/sonar-web/src/main/js/app/styles/components/issues.css
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2024 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.
- */
-.issue-list {
- margin: 10px 0;
-}
-
-.issue-list,
-.issue {
- max-width: 980px;
-}
-
-.issue-filters-list {
- /*
- * On Firefox on Windows, the scrollbar hides the sidebar's content.
- * Using 'scrollbar-gutter:stable' is a workaround to ensure consistency with other browsers.
- * @see https://bugzilla.mozilla.org/show_bug.cgi?id=764076
- * @see https://discuss.sonarsource.com/t/unnecessary-horizontal-scrollbar-on-issues-page/14889/4
- */
- scrollbar-gutter: stable;
-}
diff --git a/server/sonar-web/src/main/js/app/styles/components/list-groups.css b/server/sonar-web/src/main/js/app/styles/components/list-groups.css
deleted file mode 100644
index c6774b23de2..00000000000
--- a/server/sonar-web/src/main/js/app/styles/components/list-groups.css
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2024 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.
- */
-.list-group {
- margin-bottom: 20px;
- padding-left: 0;
-}
-
-.list-group-item,
-button.list-group-item {
- position: relative;
- z-index: var(--normalZIndex);
- display: block;
- margin-bottom: -1px;
- padding: 5px 10px;
- border: 1px solid transparent;
- width: 100%;
- box-sizing: border-box;
- text-align: left;
-}
-
-.list-group-item.depth-1 {
- padding-left: 31px;
-}
-
-.list-group-item.depth-2 {
- padding-left: 51px;
-}
-
-.list-group-item.depth-3 {
- padding-left: 71px;
-}
-
-.list-group-item:last-child {
- margin-bottom: 0;
-}
-
-.list-group-item.active,
-.list-group-item.active:hover,
-.list-group-item.active:focus {
- z-index: var(--aboveNormalZIndex);
- border-color: var(--blue) !important;
- background-color: var(--lightBlue);
-}
-
-.list-group-item:hover {
- z-index: var(--aboveNormalZIndex);
- border-color: var(--blue) !important;
-}
-
-.list-group-item + .list-group-item {
- border-top-color: var(--barBorderColor);
-}
-
-a.list-group-item {
- color: var(--baseFontColor);
- transition: none;
-}
-
-.list-group-item-heading {
- margin-top: 5px;
- margin-bottom: 5px;
- text-overflow: ellipsis;
- overflow: hidden;
-}
-
-.list-group-item-heading:after {
- content: '';
- display: table;
- clear: both;
-}
diff --git a/server/sonar-web/src/main/js/app/styles/components/menu.css b/server/sonar-web/src/main/js/app/styles/components/menu.css
deleted file mode 100644
index 492d6283643..00000000000
--- a/server/sonar-web/src/main/js/app/styles/components/menu.css
+++ /dev/null
@@ -1,188 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2024 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.
- */
-.menu {
- min-width: 160px;
- padding: 5px 0;
- list-style: none;
- font-size: var(--smallFontSize);
- text-align: left;
- background-color: #fff;
- background-clip: padding-box;
-}
-
-.menu:not(:last-of-type) {
- padding-bottom: 12px;
-}
-
-.menu + .menu,
-.menu + .menu-header {
- border-top: 1px solid var(--barBorderColor);
-}
-
-.menu.is-container {
- padding: 5px;
-}
-
-.menu-item,
-.menu > li > a,
-.menu > li > button,
-.menu > li > span {
- display: block;
- padding: 4px 16px;
- line-height: 14px;
- clear: both;
- font-weight: normal;
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
-}
-
-.menu > li > a,
-.menu > li > button {
- color: var(--neutral800);
- border-width: 0 0 0 2px;
- border-style: solid;
- border-color: transparent;
- transition: none;
-}
-
-.menu > li > button {
- text-align: left;
- width: 100%;
-}
-
-.menu > li > a.disabled {
- color: var(--disableGrayText) !important;
- cursor: not-allowed !important;
- pointer-events: none !important;
-}
-
-.menu > li > a.text-muted {
- color: var(--secondFontColor);
-}
-
-.menu > li > a:hover,
-.menu > li > a.hover,
-.menu > li > button:hover {
- background-color: var(--neutral50);
- border-left-color: var(--blacka60);
-}
-
-.menu > li > a.active,
-.menu > li > button.active {
- background-color: var(--info50);
- border-left-color: var(--info500);
-}
-
-.menu > li > a.active:hover,
-.menu > li > a.active.hover,
-.menu > li > button.active:hover {
- background-color: var(--info100);
-}
-
-.menu.menu-vertically-limited {
- max-height: 300px;
- overflow-y: auto;
-}
-
-.menu .divider {
- height: 1px;
- margin: 6px 0;
- overflow: hidden;
- background-color: var(--barBorderColor);
-}
-
-.menu-vertically-limited.with-top-separator {
- border-top: 1px solid #e6e6e6;
-}
-
-.menu-vertically-limited.with-bottom-separator {
- border-bottom: 1px solid #e6e6e6;
-}
-
-.menu .menu-footer > a > span {
- border-bottom: 1px solid var(--gray80);
- color: var(--secondFontColor);
-}
-
-.menu .menu-footer-note {
- opacity: 0;
- transition: opacity 0.3s ease;
-}
-
-.menu .menu-footer.active .menu-footer-note {
- opacity: 1;
-}
-
-.menu-search {
- position: relative;
- padding: var(--gridSize) calc(2 * var(--gridSize)) 0;
-}
-
-.menu-search .search-box,
-.menu-search .search-box-input {
- max-width: none;
- min-width: 240px;
-}
-
-.menu-search ~ .menu > li > a:hover,
-.menu-search ~ .menu > li > a:focus {
- background-color: transparent;
-}
-
-.menu-search ~ .menu > .active > a,
-.menu-search ~ .menu > li > .active,
-.menu-search ~ .menu > .active > a:hover,
-.menu-search ~ .menu > li > .active:hover,
-.menu-search ~ .menu > .active > a:focus,
-.menu-search ~ .menu > li > .active:focus {
- background-color: var(--barBackgroundColor);
-}
-
-.menu-message {
- display: block;
- padding: 4px 16px;
- line-height: 16px;
-}
-
-.menu-header {
- padding: var(--gridSize);
- margin: -8px;
- font-size: 12px;
- color: var(--neutral600);
- white-space: nowrap;
- line-height: unset;
-}
-
-.menu-header + ul {
- padding-top: 8px;
-}
-
-.menu-header.no-margin + ul {
- padding-top: 0;
-}
-
-.menu-header.no-margin {
- margin: 0;
-}
-
-.divider + .menu-header {
- padding-top: calc(var(--gridSize) - 5px);
-}
diff --git a/server/sonar-web/src/main/js/app/styles/components/page.css b/server/sonar-web/src/main/js/app/styles/components/page.css
index 812f7cf6cc9..37962ef9a7c 100644
--- a/server/sonar-web/src/main/js/app/styles/components/page.css
+++ b/server/sonar-web/src/main/js/app/styles/components/page.css
@@ -61,20 +61,6 @@
flex: 1 0 auto;
}
-.page-wrapper-simple {
- display: flex;
- justify-content: center;
- align-items: center;
- margin: 100px 0;
-}
-
-.page-simple {
- width: 400px;
- padding: 40px;
- border: 1px solid var(--barBorderColor);
- background-color: #fff;
-}
-
.page-header {
position: relative;
margin-bottom: 20px;
@@ -91,12 +77,6 @@
clear: both;
}
-.page-header .spinner {
- position: relative;
- top: 3px;
- margin-left: 8px;
-}
-
.page-title {
float: left;
margin-bottom: 0;
@@ -115,10 +95,6 @@
margin: 3px 0;
}
-.page-actions .spinner {
- top: 0 !important;
-}
-
.page-description {
float: left;
clear: left;
diff --git a/server/sonar-web/src/main/js/app/styles/components/panels.css b/server/sonar-web/src/main/js/app/styles/components/panels.css
deleted file mode 100644
index 584d3390d36..00000000000
--- a/server/sonar-web/src/main/js/app/styles/components/panels.css
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2024 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.
- */
-.panel {
- padding: 10px;
-}
-
-.panel:not(:last-child) {
- border-bottom: 1px solid var(--barBorderColor);
-}
-
-.panel-vertical {
- padding-left: 0;
- padding-right: 0;
-}
-
-.panel-white {
- border: 1px solid var(--barBorderColor);
- background-color: #fff;
-}
-
-.panel-warning {
- border: 1px solid var(--alertBorderWarning);
- background-color: var(--alertBackgroundWarning);
- color: #8a6d3b;
-}
diff --git a/server/sonar-web/src/main/js/app/styles/components/spinner.css b/server/sonar-web/src/main/js/app/styles/components/spinner.css
deleted file mode 100644
index c33b3aa34fa..00000000000
--- a/server/sonar-web/src/main/js/app/styles/components/spinner.css
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2024 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.
- */
-.spinner {
- position: relative;
- vertical-align: middle;
- width: 16px;
- height: 16px;
- border: 2px solid var(--info500);
- border-radius: 50%;
- animation: spin 0.75s infinite linear;
-}
-
-.spinner:before,
-.spinner:after {
- left: -2px;
- top: -2px;
- display: none;
- position: absolute;
- content: '';
- width: inherit;
- height: inherit;
- border: inherit;
- border-radius: inherit;
-}
-
-.spinner,
-.spinner:before,
-.spinner:after {
- display: inline-block;
- box-sizing: border-box;
- border-color: transparent;
- border-top-color: var(--info500);
- animation-duration: 1.2s;
-}
-
-.spinner:before {
- transform: rotate(120deg);
-}
-
-.spinner:after {
- transform: rotate(240deg);
-}
-
-@keyframes spin {
- from {
- transform: rotate(0deg);
- }
-
- to {
- transform: rotate(360deg);
- }
-}
diff --git a/server/sonar-web/src/main/js/app/styles/components/ui.css b/server/sonar-web/src/main/js/app/styles/components/ui.css
deleted file mode 100644
index 8ec19539497..00000000000
--- a/server/sonar-web/src/main/js/app/styles/components/ui.css
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2024 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.
- */
-.shortcut-button {
- display: inline-block;
- min-width: 24px;
- height: var(--controlHeight);
- line-height: 21px;
- padding: 0 4px;
- box-sizing: border-box;
- border: 1px solid #ccc;
- border-radius: 3px;
- background-image: linear-gradient(to bottom, #f5f5f5, #eee);
- box-shadow:
- inset 0 1px 0 #fff,
- 0 1px 0 #ccc;
- color: var(--secondFontColor);
- font-size: 11px;
- text-align: center;
-}
-
-.shortcut-button-small {
- min-width: 16px;
- height: 16px;
- line-height: 14px;
- margin-left: 4px;
- margin-right: 4px;
-}
-
-.shortcut-button-tiny {
- width: 14px;
- min-width: auto;
- padding: 0;
- height: 14px;
- line-height: inherit;
- font-size: 6px;
-}
-
-.page-shortcuts-tooltip {
- line-height: 12px;
-}
-
-.identity-provider {
- display: inline-block;
- line-height: 14px;
- padding: 2px 5px;
- border: 1px solid rgba(0, 0, 0, 0.15);
- border-radius: 3px;
- box-sizing: border-box;
- background-color: var(--darkBlue);
- font-size: var(--smallFontSize);
- color: #fff;
-}
-
-.analysis-version {
- display: inline-block;
- vertical-align: middle;
- height: 20px;
- padding: 0 8px;
- background-color: var(--primary400);
- border-radius: 2px;
- line-height: 20px;
- font-size: var(--smallFontSize);
- color: var(--white);
- white-space: nowrap;
- text-align: center;
- font-weight: bold;
- letter-spacing: 0;
-}
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 41f506c63e7..a7f14eca985 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
@@ -29,12 +29,12 @@
}
*:focus-visible {
- outline: 2px dotted var(--primary400);
+ outline: 2px dotted #297bae;
}
html,
body {
- background-color: var(--barBackgroundColor);
+ background-color: white;
}
body {
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
deleted file mode 100644
index 64c35c5b8f8..00000000000
--- a/server/sonar-web/src/main/js/app/styles/init/forms.css
+++ /dev/null
@@ -1,220 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2024 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.
- */
-/*
- * Inputs
- */
-input[type='text'],
-input[type='password'],
-input[type='email'],
-input[type='search'],
-input[type='date'],
-input[type='number'],
-textarea,
-select {
- border: 1px solid var(--gray80);
- box-sizing: border-box;
- border-radius: 2px;
- background: #fff;
- color: var(--baseFontColor);
- transition: border-color 0.2s ease;
-}
-
-input[type='text']:active,
-input[type='password']:active,
-input[type='email']:active,
-input[type='search']:active,
-input[type='date']:active,
-input[type='number']:active,
-textarea:active,
-select:active,
-input[type='text']:focus,
-input[type='password']:focus,
-input[type='email']:focus,
-input[type='search']:focus,
-input[type='date']:focus,
-input[type='number']:focus,
-textarea:focus,
-select:focus {
- border-color: var(--blue);
- box-shadow: none;
- outline: none;
-}
-
-input[type='text']:invalid,
-input[type='password']:invalid,
-input[type='email']:invalid,
-input[type='search']:invalid,
-input[type='date']:invalid,
-input[type='number']:invalid,
-textarea:invalid,
-select:invalid {
- box-shadow: none;
- outline: none;
-}
-
-input::placeholder,
-textarea::placeholder {
- color: var(--neutral600);
- opacity: 0.9;
-}
-
-input[type='text'].is-valid,
-input[type='password'].is-valid,
-input[type='email'].is-valid,
-input[type='search'].is-valid,
-input[type='date'].is-valid,
-input[type='number'].is-valid,
-textarea.is-valid,
-select.is-valid {
- border-color: var(--green);
-}
-
-input[type='text'].is-invalid,
-input[type='password'].is-invalid,
-input[type='email'].is-invalid,
-input[type='search'].is-invalid,
-input[type='date'].is-invalid,
-input[type='number'].is-invalid,
-textarea.is-invalid,
-select.is-invalid {
- border-color: var(--red);
-}
-
-input.disabled,
-input:disabled,
-textarea.disabled,
-textarea:disabled,
-select.disabled,
-select:disabled {
- color: var(--disableGrayText) !important;
- border-color: var(--disableGrayBorder) !important;
- background: var(--disableGrayBg) !important;
- cursor: not-allowed !important;
- pointer-events: none !important;
- box-shadow: none !important;
-}
-
-input[type='text'],
-input[type='password'],
-input[type='email'],
-input[type='search'],
-input[type='date'],
-input[type='number'] {
- height: var(--controlHeight);
- padding: 0 6px;
-}
-
-input[type='search'] {
- -webkit-appearance: textfield;
-}
-
-input[type='search']::-webkit-search-decoration {
- -webkit-appearance: none;
-}
-
-textarea {
- padding: 3px;
-}
-
-textarea.width-100 {
- max-width: 100%;
-}
-
-textarea.fixed-width {
- resize: vertical;
-}
-
-select {
- height: var(--controlHeight);
- line-height: var(--controlHeight);
-}
-
-.input-tiny {
- width: 60px !important;
-}
-
-.input-small {
- width: 100px !important;
-}
-
-.input-medium {
- width: 150px !important;
-}
-
-.input-large {
- width: 200px !important;
-}
-
-.input-super-large {
- width: 100% !important;
- max-width: 300px;
- min-width: 200px;
-}
-
-.input-ghost {
- padding: 0 !important;
- border: none !important;
- background-color: transparent !important;
-}
-
-.input-clear {
- background-color: transparent !important;
-}
-
-.input-code {
- font-family: var(--sourceCodeFontFamily);
- font-size: var(--smallFontSize);
-}
-
-em.mandatory {
- color: var(--mandatoryFieldColor);
- font-style: italic;
-}
-
-.form-field {
- clear: both;
- display: block;
- padding-bottom: calc(2 * var(--gridSize));
-}
-
-.form-field label {
- display: block;
- 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 .react-select,
-.form-field .Select {
- width: 250px;
-}
diff --git a/server/sonar-web/src/main/js/app/styles/init/icons.css b/server/sonar-web/src/main/js/app/styles/init/icons.css
deleted file mode 100644
index 2206cde6208..00000000000
--- a/server/sonar-web/src/main/js/app/styles/init/icons.css
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2024 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.
- */
-[class^='icon-'],
-[class*=' icon-'] {
- line-height: 1;
- vertical-align: middle;
-}
-
-a[class^='icon-'],
-a[class*=' icon-'] {
- border-bottom: none;
-}
-
-/*
- * Colors
- */
-.icon-gray {
- color: #999;
-}
-
-.icon-gray path {
- fill: #999;
-}
-
-.icon-color-link {
- color: var(--darkBlue);
-}
-
-/*
- * Common
- */
-.icon-outline {
- transition: all 0.2s ease !important;
-}
-
-.icon-outline path {
- stroke: var(--secondFontColor);
- stroke-width: 1.41421356;
- stroke-opacity: 1;
- fill-opacity: 0;
- vector-effect: non-scaling-stroke;
- transition: all 0.2s ease;
-}
-
-.icon-outline.is-filled path {
- fill: currentColor;
- stroke: currentColor;
- fill-opacity: 1;
-}
diff --git a/server/sonar-web/src/main/js/app/styles/init/links.css b/server/sonar-web/src/main/js/app/styles/init/links.css
deleted file mode 100644
index b0e811ec87a..00000000000
--- a/server/sonar-web/src/main/js/app/styles/init/links.css
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2024 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.
- */
-a {
- border-bottom: 1px solid var(--primarya40);
- color: var(--primary);
- cursor: pointer;
- outline: none;
- text-decoration: none;
- transition:
- border-bottom-color 0.2s ease,
- color 0.2s ease;
-}
-
-a:hover,
-a:active,
-a:focus {
- border-bottom-color: var(--primary);
-}
-
-a svg,
-a img {
- vertical-align: middle;
-}
-
-.link-no-underline {
- border-bottom-color: transparent !important;
-}
-
-.link-no-underline:hover {
- border-bottom-color: var(--primary) !important;
-}
-
-.link-rating,
-.link-rating:hover {
- border-bottom: 0 !important;
-}
diff --git a/server/sonar-web/src/main/js/app/styles/init/lists.css b/server/sonar-web/src/main/js/app/styles/init/lists.css
deleted file mode 100644
index 4447df2b394..00000000000
--- a/server/sonar-web/src/main/js/app/styles/init/lists.css
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2024 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.
- */
-ol,
-ul {
- padding-left: 0;
- list-style: none;
-}
-
-.list-styled {
- margin-bottom: 10px;
- padding-left: 40px;
-}
-
-.list-styled.no-padding {
- padding-left: calc(var(--gridSize) * 2);
-}
-
-ul.list-styled {
- list-style: disc;
-}
-
-ol.list-styled {
- list-style: decimal;
-}
-
-.list-inline {
- padding-left: 0;
- margin-left: -5px;
- list-style: none;
-}
-
-.list-breadcrumbs > li {
- display: inline-block;
-}
-
-.list-breadcrumbs > li:not(:first-of-type)::before {
- content: '/' / '';
- margin-inline: 3px;
-}
-
-ul.list-inline > li,
-div.list-inline > div {
- display: inline-block;
- vertical-align: top;
- padding-right: 5px;
- padding-left: 5px;
-}
-
-.list-spaced {
- margin-bottom: 10px;
- list-style: none;
-}
-
-.list-spaced > li {
- margin-top: 10px;
-}
-
-.list-item-checkable-link {
- cursor: pointer;
-}
-
-.list-item-checkable-link:focus {
- outline: none;
-}
-
-.list-item-checkable-link.disabled {
- opacity: 0.7;
-}
-
-.list-item-checkable-link.disabled a::before {
- background-color: var(--gray80);
- border-color: var(--gray80);
-}
-
-dl {
- margin-top: 0;
- margin-bottom: 20px;
-}
-
-dt,
-dd {
- line-height: 1.42857143;
-}
-
-dt {
- font-weight: bold;
-}
-
-dd {
- margin-left: 0;
-}
diff --git a/server/sonar-web/src/main/js/app/styles/init/misc.css b/server/sonar-web/src/main/js/app/styles/init/misc.css
index d1d01be68f3..df466f2fa8a 100644
--- a/server/sonar-web/src/main/js/app/styles/init/misc.css
+++ b/server/sonar-web/src/main/js/app/styles/init/misc.css
@@ -34,32 +34,11 @@ th.hide-overflow {
overflow: hidden;
}
-.hidden {
- display: none !important;
- visibility: hidden !important;
-}
-
-.invisible {
- visibility: hidden;
-}
-
.note {
color: var(--secondFontColor);
font-size: var(--smallFontSize);
}
-.nudged-up {
- margin-top: -1px;
-}
-
-.nudged-down {
- margin-top: 1px;
-}
-
-.null-spacer-top {
- margin-top: 0 !important;
-}
-
.null-spacer-bottom {
margin-bottom: 0 !important;
}
@@ -84,46 +63,6 @@ th.hide-overflow {
margin-top: 8px !important;
}
-.big-spacer {
- margin: 16px !important;
-}
-
-.big-spacer-left {
- margin-left: 16px !important;
-}
-
-.big-spacer-right {
- margin-right: 16px !important;
-}
-
-.big-spacer-bottom {
- margin-bottom: 16px !important;
-}
-
-.big-spacer-top {
- margin-top: 16px !important;
-}
-
-.huge-spacer {
- margin: 40px !important;
-}
-
-.huge-spacer-bottom {
- margin-bottom: 40px !important;
-}
-
-.huge-spacer-top {
- margin-top: 40px !important;
-}
-
-.huge-spacer-left {
- margin-left: 40px !important;
-}
-
-.huge-spacer-right {
- margin-right: 40px !important;
-}
-
.little-spacer {
margin: 4px !important;
}
@@ -148,18 +87,10 @@ th.hide-overflow {
padding: var(--gridSize) !important;
}
-.little-padded {
- padding: calc(var(--gridSize) / 2) !important;
-}
-
.big-padded {
padding: calc(2 * var(--gridSize)) !important;
}
-.padded-top {
- padding-top: var(--gridSize) !important;
-}
-
.padded-right {
padding-right: var(--gridSize) !important;
}
@@ -172,45 +103,10 @@ th.hide-overflow {
padding-left: var(--gridSize) !important;
}
-.little-padded-top {
- padding-top: calc(var(--gridSize) / 2) !important;
-}
-
-.little-padded-right {
- padding-right: calc(var(--gridSize) / 2) !important;
-}
-
-.little-padded-bottom {
- padding-bottom: calc(var(--gridSize) / 2) !important;
-}
-
-.little-padded-left {
- padding-left: calc(var(--gridSize) / 2) !important;
-}
-
-.big-padded-top {
- padding-top: calc(2 * var(--gridSize));
-}
-.big-padded-bottom {
- padding-bottom: calc(2 * var(--gridSize));
-}
-
.big-padded-right {
padding-right: calc(2 * var(--gridSize));
}
-.big-padded-left {
- padding-left: calc(2 * var(--gridSize));
-}
-
-.huge-padded-top {
- padding-top: 40px;
-}
-
-.huge-padded-bottom {
- padding-bottom: 40px;
-}
-
td.little-spacer-left {
padding-left: 4px !important;
}
@@ -235,60 +131,10 @@ td.spacer-top {
padding-top: 8px !important;
}
-td.big-spacer-left,
-th.big-spacer-left {
- padding-left: 16px !important;
-}
-
-td.big-spacer-right {
- padding-right: 16px !important;
-}
-
-td.big-spacer-bottom {
- padding-bottom: 16px !important;
-}
-
-td.big-spacer-top {
- padding-top: 16px !important;
-}
-
-td.huge-spacer-right,
-th.huge-spacer-right {
- padding-right: 40px !important;
-}
-
.pull-left {
float: left !important;
}
-.pull-right {
- float: right !important;
-}
-
-.borderless {
- border: none !important;
-}
-
-.bordered {
- border: 1px solid var(--barBorderColor);
-}
-
-.bordered-left {
- border-left: 1px solid var(--barBorderColor);
-}
-
-.bordered-right {
- border-right: 1px solid var(--barBorderColor);
-}
-
-.bordered-bottom {
- border-bottom: 1px solid var(--barBorderColor);
-}
-
-.bordered-top {
- border-top: 1px solid var(--barBorderColor);
-}
-
.overflow-hidden {
overflow: hidden !important;
}
@@ -297,78 +143,14 @@ th.huge-spacer-right {
overflow-y: auto !important;
}
-.max-width-100 {
- max-width: 100% !important;
-}
-
-.max-width-80 {
- max-width: 80% !important;
-}
-
-.max-width-60 {
- max-width: 60% !important;
-}
-
.width-100 {
width: 100% !important;
}
-.width-80 {
- width: 80% !important;
-}
-
-.width-60 {
- width: 60% !important;
-}
-
-.width-55 {
- width: 55% !important;
-}
-
-.width-50 {
- width: 50% !important;
-}
-
-.width-40 {
- width: 40% !important;
-}
-
-.width-30 {
- width: 30% !important;
-}
-
-.width-25 {
- width: 25% !important;
-}
-
-.width-20 {
- width: 20% !important;
-}
-
-.width-15 {
- width: 15% !important;
-}
-
-.width-10 {
- width: 10% !important;
-}
-
.abs-width-100 {
width: 100px !important;
}
-.abs-width-150 {
- width: 150px !important;
-}
-
-.abs-width-240 {
- width: 240px !important;
-}
-
-.abs-width-300 {
- width: 300px !important;
-}
-
.abs-width-400 {
width: 400px !important;
}
@@ -377,58 +159,6 @@ th.huge-spacer-right {
width: 600px !important;
}
-.abs-width-800 {
- width: 800px !important;
-}
-
-.abs-height-50 {
- height: 50px !important;
-}
-
-.abs-height-100 {
- height: 100% !important;
-}
-
-.max-height-100 {
- max-height: 100% !important;
-}
-
-.justify {
- margin-bottom: -1em;
- text-align: justify;
-}
-
-.justify > .ib {
- display: inline-block;
-}
-
-.justify:after {
- display: inline-block;
- width: 100%;
- content: ' ';
-}
-
-.first-letter-uppercase::first-letter {
- text-transform: uppercase;
-}
-
-.disabled-pointer-events {
- pointer-events: none !important;
-}
-
-.display-block {
- display: block !important;
-}
-
-.display-inline-block {
- display: inline-block !important;
-}
-
-.display-flex-row {
- display: flex !important;
- flex-direction: row;
-}
-
.display-flex-column {
display: flex !important;
flex-direction: column;
@@ -439,108 +169,29 @@ th.huge-spacer-right {
align-items: center;
}
-.display-flex-justify-start {
- display: flex !important;
- justify-content: flex-start !important;
-}
-
.display-flex-justify-center {
display: flex !important;
justify-content: center;
}
-.display-flex-justify-end {
- display: flex !important;
- justify-content: flex-end;
-}
-
-.display-flex-space-around {
- display: flex !important;
- justify-content: space-around;
-}
-
-.display-flex-space-between {
- display: flex !important;
- justify-content: space-between;
-}
-
-.display-flex-stretch {
- display: flex !important;
- align-items: stretch;
-}
-
.display-flex-start {
display: flex !important;
align-items: flex-start !important;
}
-.display-flex-end {
- display: flex !important;
- align-items: flex-end;
-}
-
-.display-flex-wrap {
- display: flex !important;
- flex-wrap: wrap;
-}
-
-.display-inline-flex-baseline {
- display: inline-flex !important;
- align-items: baseline;
-}
-
-.display-inline-flex-start {
- display: inline-flex !important;
- align-items: flex-start;
-}
-
.display-inline-flex-center {
display: inline-flex !important;
align-items: center;
}
-.position-absolute {
- position: absolute !important;
-}
-
-.position-relative {
- position: relative !important;
-}
-
-.rounded {
- border-radius: 2px;
-}
-
.flex-1 {
flex: 1;
}
-.flex-1-0-auto {
- flex: 1 0 auto;
-}
-
-.flex-0 {
- flex: 0 0 auto;
-}
-
.flex-grow {
flex-grow: 1;
}
-.flex-shrink {
- flex-shrink: 1;
- min-width: 0;
-}
-
-.space-between {
- justify-content: space-between !important;
-}
-
-.new-loading {
- opacity: 0.5;
- transition: opacity 0.5s ease;
-}
-
.slash-separator {
margin-left: 5px;
margin-right: 5px;
@@ -550,99 +201,3 @@ th.huge-spacer-right {
content: '/';
color: rgba(68, 68, 68, 0.3);
}
-
-.horizontal-pipe-separator {
- display: flex;
- align-items: center;
- margin-top: calc(4 * var(--gridSize));
- margin-bottom: calc(4 * var(--gridSize));
-}
-
-.horizontal-pipe-separator > .horizontal-separator {
- margin: 0 4px;
-}
-
-.horizontal-separator {
- min-width: 16px;
- height: 1px;
- flex-grow: 1;
- background-color: var(--barBorderColor);
-}
-
-.vertical-separator {
- width: 1px;
- min-height: 16px;
- flex-grow: 1;
- background-color: var(--barBorderColor);
-}
-
-.vertical-pipe-separator {
- display: flex;
- flex-direction: column;
- margin-left: 60px;
- margin-right: 60px;
-}
-
-.vertical-pipe-separator > .vertical-separator {
- margin: 4px auto;
-}
-
-.capitalize {
- text-transform: capitalize !important;
-}
-
-.cursor-pointer {
- cursor: pointer;
-}
-
-.cursor-not-allowed {
- cursor: not-allowed !important;
-}
-
-.no-outline,
-.no-outline:focus {
- outline: none !important;
-}
-
-.bg-danger {
- background-color: var(--red);
- color: #fff;
-}
-
-.bg-warning {
- background-color: var(--alertBackgroundWarning);
- color: var(--alertTextWarning);
-}
-
-.bg-info {
- background-color: var(--blue);
- color: #fff;
-}
-
-.bg-success {
- background-color: var(--green);
- color: #fff;
-}
-
-.bg-muted {
- background-color: var(--barBackgroundColor);
- color: inherit;
-}
-
-.muted {
- color: var(--neutral600);
-}
-
-.leak-box {
- background-color: var(--leakPrimaryColor);
- border: 1px solid var(--leakSecondaryColor);
- padding: 4px 6px;
-}
-
-.break-word {
- word-break: break-word;
-}
-
-.no-margin-bottom {
- margin-bottom: 0 !important;
-}
diff --git a/server/sonar-web/src/main/js/app/styles/init/tables.css b/server/sonar-web/src/main/js/app/styles/init/tables.css
deleted file mode 100644
index 8ac6c5d3d6f..00000000000
--- a/server/sonar-web/src/main/js/app/styles/init/tables.css
+++ /dev/null
@@ -1,301 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2024 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.
- */
-table {
- border-collapse: collapse;
- border-spacing: 0;
-}
-
-table.form td {
- padding: 2px 5px;
- vertical-align: top;
-}
-
-table.form th {
- padding: 2px 5px;
- font-weight: 600;
-}
-
-table.form td.keyCell {
- width: 1%;
- white-space: nowrap;
- text-align: right;
- font-weight: bold;
- vertical-align: top;
-}
-
-table.form td img {
- vertical-align: bottom;
-}
-
-table.spaced th {
- font-weight: bold;
- color: #333;
- padding: 4px 5px;
-}
-
-table.spaced td {
- padding: 3px 5px;
- line-height: 18px;
-}
-
-table.spaced td img {
- vertical-align: text-bottom;
-}
-
-table.spacedicon th {
- font-weight: bold;
- color: #333;
- padding: 4px 5px;
-}
-
-table.spacedicon td {
- padding: 0 5px;
- height: 24px;
-}
-
-.thin {
- width: 1%;
-}
-
-.formError {
- display: inline-block;
- background-color: var(--orange);
- color: #000;
- padding: 0 5px;
-}
-
-.table > thead > tr > th {
- border-top: 0 none;
- font-weight: bold;
- line-height: 16px;
- padding: 4px 5px;
- vertical-align: bottom;
-}
-
-.table > tbody > tr > td {
- line-height: 16px;
- padding: 4px 5px;
- vertical-align: top;
-}
-
-.table > tfoot > tr > td {
- font-size: 93%;
- color: var(--secondFontColor);
- padding: 4px 5px;
-}
-
-.table > tfoot > tr > td a {
- color: var(--secondFontColor);
-}
-
-.hoverable:hover {
- background-color: var(--lightBlue);
-}
-
-.hoverable:hover a {
- color: #111;
-}
-
-.odd {
- background-color: #fff;
-}
-
-.even {
- background-color: #f5f5f5;
-}
-
-.odd.selected,
-.even.selected,
-.odd.selected a,
-.even.selected a,
-.even.selected span:not(.rating),
-.odd.selected span:not(.rating) {
- background-color: #d9edf7;
- color: var(--baseFontColor);
-}
-
-.table-cell-doc {
- position: absolute;
- z-index: var(--aboveNormalZIndex);
- right: -8px;
-}
-
-th > .table-cell-doc {
- top: 50%;
- margin-top: -6px;
-}
-
-td.sep {
- width: 10px;
-}
-
-table.matrix tfoot td {
- padding: 3px 5px;
- line-height: 18px;
-}
-
-table.data,
-table.spaced {
- width: 100%;
-}
-
-table.data td.small,
-table.data th.small {
- padding: 0;
- white-space: nowrap;
-}
-
-table.data > caption {
- padding: 8px 10px;
- text-align: center;
- font-weight: bold;
-}
-
-table.data > thead > tr > th {
- position: relative;
- vertical-align: top;
- line-height: 18px;
- padding: 8px 10px;
- border-bottom: 1px solid var(--barBorderColor);
- font-weight: 600;
-}
-
-table.data > thead > tr > th > .small {
- display: block;
- line-height: 1.4;
- font-weight: 400;
-}
-
-table.data > tfoot > tr > td {
- font-size: 93%;
- color: var(--secondFontColor);
- padding: 5px;
-}
-
-table.data > tbody > tr > td {
- position: relative;
- padding: 8px 10px;
- line-height: 16px;
-}
-
-table.data > tbody > tr > td.text-middle {
- vertical-align: middle;
-}
-
-.data thead tr.total {
- background-color: var(--gray94);
- font-weight: normal;
- border: 1px solid #ddd;
-}
-
-.data thead tr.total th {
- font-weight: normal;
-}
-
-.data tr.blank,
-.data tr.blank > td,
-.data td.blank {
- background-color: #fff !important;
- line-height: 15px;
-}
-
-.data tr.highlight {
- background-color: var(--lightBlue);
-}
-
-.data input,
-.data select,
-.data button {
- vertical-align: middle;
-}
-
-table.data.condensed > tbody > tr > td {
- padding-top: 5px;
- padding-bottom: 5px;
-}
-
-table.data tr.subheader th {
- font-size: var(--smallFontSize);
- border-bottom: none;
-}
-
-table.data:not(.boxed-padding) > thead:after {
- display: block;
- line-height: 5px;
- content: '\200C';
-}
-
-table.data.boxed-padding > thead > tr > th {
- padding-top: 24px;
-}
-
-table.data.boxed-padding > thead > tr > th:first-child,
-table.data.boxed-padding > tbody > tr > td:first-child,
-table.data.boxed-padding > thead > tr > th:last-child,
-table.data.boxed-padding > tbody > tr > td:last-child {
- width: 20px;
- padding: 8px 0;
-}
-
-table.data.no-outer-padding > thead > tr > th:first-child,
-table.data.no-outer-padding > tbody > tr > td:first-child {
- padding-left: 0;
-}
-
-table.data.no-outer-padding > thead > tr > th:last-child,
-table.data.no-outer-padding > tbody > tr > td:last-child {
- padding-right: 0;
-}
-
-table.data.boxed-padding > thead + tbody > tr:first-child > td {
- padding-top: 16px;
-}
-
-table.data.zebra-hover > tbody > tr:hover {
- background-color: var(--rowHoverHighlight) !important;
-}
-
-table.data.zebra > tbody > tr.selected {
- background-color: #d9edf7 !important;
-}
-
-table.data.zebra:not(.zebra-inversed) > tbody > tr:nth-child(even) {
- background-color: #f5f5f5;
-}
-
-table.data.zebra.zebra-inversed > tbody > tr:nth-child(odd) {
- background-color: #f5f5f5;
-}
-
-table#project-history tr > td {
- vertical-align: top;
-}
-
-table.fixed {
- table-layout: fixed;
-}
-
-table.fixed th.action-small {
- width: 30px;
-}
-
-table.fixed th.action {
- width: 50px;
-}
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
deleted file mode 100644
index 1ed45b8b67b..00000000000
--- a/server/sonar-web/src/main/js/app/styles/init/type.css
+++ /dev/null
@@ -1,304 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2024 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.
- */
-html,
-body {
- color: var(--baseFontColor);
-}
-
-body {
- font-family: var(--baseFontFamily);
- font-size: var(--baseFontSize);
- line-height: 1.23076923;
-}
-
-h1,
-.h1 {
- line-height: var(--controlHeight);
- color: var(--baseFontColor);
- font-size: var(--bigFontSize);
- font-weight: 400;
-}
-
-h1 img,
-.h1 img,
-h1 svg,
-.h1 svg {
- vertical-align: middle;
- transform: translateY(-1px);
-}
-
-h2,
-.h2 {
- line-height: var(--controlHeight);
- color: var(--baseFontColor);
- font-size: 15px;
- font-weight: 400;
-}
-
-h2 img,
-.h2 img,
-h2 svg,
-.h2 svg {
- vertical-align: middle;
- transform: translateY(-1px);
-}
-
-h3,
-.h3 {
- line-height: var(--controlHeight);
- color: var(--baseFontColor);
- font-size: var(--mediumFontSize);
- font-weight: 600;
-}
-
-h3 img,
-.h3 img,
-h3 svg,
-.h3 svg {
- vertical-align: middle;
- transform: translateY(-1px);
-}
-
-h4,
-.h4 {
- line-height: var(--controlHeight);
- color: var(--baseFontColor);
- font-size: var(--baseFontSize);
- font-weight: 600;
-}
-
-h4 img,
-.h4 img,
-h4 svg,
-.h4 svg {
- vertical-align: middle;
- transform: translateY(-1px);
-}
-
-h5,
-.h5 {
- line-height: var(--controlHeight);
- color: var(--baseFontColor);
- font-size: var(--baseFontSize);
- font-weight: 600;
-}
-
-h5 img,
-.h5 img,
-h5 svg,
-.h5 svg {
- vertical-align: middle;
- transform: translateY(-1px);
-}
-
-h6,
-.h6 {
- line-height: var(--controlHeight);
- color: var(--baseFontColor);
- font-size: var(--baseFontSize);
- font-weight: 600;
-}
-
-h6 img,
-.h6 img,
-h6 svg,
-.h6 svg {
- vertical-align: middle;
- transform: translateY(-1px);
-}
-
-em {
- font-style: italic;
-}
-
-strong {
- font-weight: 600;
-}
-
-.underline {
- text-decoration: underline;
-}
-
-mark {
- background: none;
- color: var(--baseFontColor);
- font-weight: bold;
-}
-
-blockquote {
- border-left: 3px solid var(--barBorderColor);
- padding: 0 8px;
- line-height: 1.5;
-}
-
-blockquote cite {
- line-height: 1.5;
- color: var(--secondFontColor);
- font-size: var(--smallFontSize);
-}
-
-small,
-.small {
- font-size: var(--smallFontSize) !important;
-}
-
-.medium {
- font-size: var(--mediumFontSize) !important;
-}
-
-.big {
- font-size: var(--bigFontSize) !important;
-}
-
-.huge {
- font-size: var(--hugeFontSize) !important;
-}
-
-.gigantic {
- font-size: var(--giganticFontSize) !important;
-}
-
-.zero-font-size {
- font-size: 0 !important;
-}
-
-.text-left {
- text-align: left;
-}
-
-.text-center {
- text-align: center;
-}
-
-.text-right {
- text-align: right;
-}
-
-.text-justify {
- text-align: justify;
-}
-
-.text-top {
- vertical-align: top !important;
-}
-
-.text-middle {
- vertical-align: middle !important;
-}
-
-.text-bottom {
- vertical-align: bottom !important;
-}
-
-.text-text-top {
- vertical-align: text-top !important;
-}
-
-.text-text-bottom {
- vertical-align: text-bottom !important;
-}
-
-.text-baseline {
- vertical-align: baseline !important;
-}
-
-.text-ellipsis {
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
-}
-
-.text-limited-small {
- display: inline-block;
- max-width: 8vw;
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
-}
-
-.text-limited {
- display: inline-block;
- max-width: 16vw;
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
-}
-
-.text-uppercase {
- text-transform: uppercase;
-}
-
-.text-lowercase {
- text-transform: lowercase;
-}
-
-.text-no-transform {
- text-transform: none;
-}
-
-.text-light {
- font-weight: 300 !important;
-}
-
-.text-normal {
- font-weight: normal !important;
-}
-
-.text-bold {
- font-weight: bold !important;
-}
-
-.text-muted {
- color: var(--secondFontColor);
-}
-
-.text-muted-2 {
- color: var(--gray71);
-}
-
-.text-danger {
- color: var(--red) !important;
-}
-
-.text-warning {
- color: var(--orange) !important;
-}
-
-.text-info {
- color: var(--blue) !important;
-}
-
-.text-success {
- color: var(--green) !important;
-}
-
-.monospaced {
- line-height: 18px;
- font-family: var(--sourceCodeFontFamily);
- font-size: var(--smallFontSize);
-}
-
-.new-background {
- background-color: #fcfcfd;
-}
-
-.white-background {
- background-color: #ffffff;
-}
diff --git a/server/sonar-web/src/main/js/app/styles/mixins.css b/server/sonar-web/src/main/js/app/styles/mixins.css
deleted file mode 100644
index ef4eeb2e0f2..00000000000
--- a/server/sonar-web/src/main/js/app/styles/mixins.css
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2024 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.
- */
-.clearfix:before,
-.clearfix:after {
- display: table;
- content: '';
- line-height: 0;
-}
-
-.clearfix:after {
- clear: both;
-}
diff --git a/server/sonar-web/src/main/js/app/styles/sonar.ts b/server/sonar-web/src/main/js/app/styles/sonar.ts
index fcc97c52a3d..aa6fdccd870 100644
--- a/server/sonar-web/src/main/js/app/styles/sonar.ts
+++ b/server/sonar-web/src/main/js/app/styles/sonar.ts
@@ -23,27 +23,10 @@
import '../../../../../public/fonts/Inter/inter.css';
import '../../../../../public/fonts/Ubuntu/Ubuntu.css';
-import './components/badges.css';
import './components/boxed-group.css';
-import './components/columns.css';
-import './components/component-name.css';
-import './components/dropdowns.css';
import './components/global-loading.css';
-import './components/issues.css';
-import './components/list-groups.css';
-import './components/menu.css';
import './components/page.css';
-import './components/panels.css';
-import './components/spinner.css';
-import './components/ui.css';
import './init/base.css';
-import './init/forms.css';
-import './init/icons.css';
-import './init/links.css';
-import './init/lists.css';
import './init/misc.css';
-import './init/tables.css';
-import './init/type.css';
-import './mixins.css';
import './print.css';
import './style.css';
diff --git a/server/sonar-web/src/main/js/app/utils/startReactApp.tsx b/server/sonar-web/src/main/js/app/utils/startReactApp.tsx
index b12aae3de32..553ec297a82 100644
--- a/server/sonar-web/src/main/js/app/utils/startReactApp.tsx
+++ b/server/sonar-web/src/main/js/app/utils/startReactApp.tsx
@@ -101,6 +101,7 @@ import PortfolioPage from '../components/extensions/PortfolioPage';
import PortfoliosPage from '../components/extensions/PortfoliosPage';
import ProjectAdminPageExtension from '../components/extensions/ProjectAdminPageExtension';
import ProjectPageExtension from '../components/extensions/ProjectPageExtension';
+import { GlobalStyles } from '../styles/GlobalStyles';
import exportModulesAsGlobals from './exportModulesAsGlobals';
function renderComponentRoutes() {
@@ -272,6 +273,7 @@ export default function startReactApp(
<RawIntlProvider value={l10nBundle}>
<ThemeProvider theme={lightTheme}>
<QueryClientProvider client={queryClient}>
+ <GlobalStyles />
<GlobalMessagesContainer />
<Helmet titleTemplate={translate('page_title.template.default')} />
<RouterProvider router={router} />
diff --git a/server/sonar-web/src/main/js/apps/account/projects/Projects.tsx b/server/sonar-web/src/main/js/apps/account/projects/Projects.tsx
index 08c88a3cd67..88e849dfa67 100644
--- a/server/sonar-web/src/main/js/apps/account/projects/Projects.tsx
+++ b/server/sonar-web/src/main/js/apps/account/projects/Projects.tsx
@@ -57,7 +57,6 @@ export default function Projects(props: Readonly<Props>) {
loading={props.loading}
ready={!props.loading}
total={props.total ?? 0}
- useMIUIButtons
/>
</>
)}
diff --git a/server/sonar-web/src/main/js/apps/background-tasks/components/BackgroundTasksApp.tsx b/server/sonar-web/src/main/js/apps/background-tasks/components/BackgroundTasksApp.tsx
index a340aae00fa..16e02e9b520 100644
--- a/server/sonar-web/src/main/js/apps/background-tasks/components/BackgroundTasksApp.tsx
+++ b/server/sonar-web/src/main/js/apps/background-tasks/components/BackgroundTasksApp.tsx
@@ -274,7 +274,6 @@ export class BackgroundTasksApp extends React.PureComponent<Props, State> {
loading={loading}
pageSize={pagination.pageSize}
total={pagination.total}
- useMIUIButtons
/>
</PageContentFontWrapper>
</LargeCenteredLayout>
diff --git a/server/sonar-web/src/main/js/apps/change-admin-password/ChangeAdminPasswordAppRenderer.tsx b/server/sonar-web/src/main/js/apps/change-admin-password/ChangeAdminPasswordAppRenderer.tsx
index 33db64d2bf7..12e71ce081d 100644
--- a/server/sonar-web/src/main/js/apps/change-admin-password/ChangeAdminPasswordAppRenderer.tsx
+++ b/server/sonar-web/src/main/js/apps/change-admin-password/ChangeAdminPasswordAppRenderer.tsx
@@ -17,13 +17,23 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+import {
+ ButtonPrimary,
+ Card,
+ CenteredLayout,
+ DarkLabel,
+ FlagMessage,
+ FormField,
+ InputField,
+ Link,
+ PageContentFontWrapper,
+ Spinner,
+ SubTitle,
+ Title,
+} from 'design-system';
import * as React from 'react';
import { Helmet } from 'react-helmet-async';
-import { SubmitButton } from '../../components/controls/buttons';
import { Location } from '../../components/hoc/withRouter';
-import { Alert } from '../../components/ui/Alert';
-import MandatoryFieldMarker from '../../components/ui/MandatoryFieldMarker';
-import MandatoryFieldsExplanation from '../../components/ui/MandatoryFieldsExplanation';
import { translate } from '../../helpers/l10n';
import { getReturnUrl } from '../../helpers/urls';
import Unauthorized from '../sessions/components/Unauthorized';
@@ -42,6 +52,9 @@ export interface ChangeAdminPasswordAppRendererProps {
location: Location;
}
+const PASSWORD_FIELD_ID = 'user-password';
+const CONFIRM_PASSWORD_FIELD_ID = 'confirm-user-password';
+
export default function ChangeAdminPasswordAppRenderer(props: ChangeAdminPasswordAppRendererProps) {
const {
canAdmin,
@@ -58,93 +71,94 @@ export default function ChangeAdminPasswordAppRenderer(props: ChangeAdminPasswor
}
return (
- <div className="page-wrapper-simple">
+ <CenteredLayout>
<Helmet defer={false} title={translate('users.change_admin_password.page')} />
- <div className="page-simple">
- {success ? (
- <Alert variant="success">
- <p className="spacer-bottom">{translate('users.change_admin_password.form.success')}</p>
- {/* We must not use Link here, because we need a refresh of the /api/navigation/global call. */}
- <a href={getReturnUrl(location)}>
- {translate('users.change_admin_password.form.continue_to_app')}
- </a>
- </Alert>
- ) : (
- <>
- <h1 className="text-center bg-danger big padded">
- {translate('users.change_admin_password.instance_is_at_risk')}
- </h1>
- <p className="text-center huge huge-spacer-top">
- {translate('users.change_admin_password.header')}
- </p>
- <p className="text-center huge-spacer-top huge-spacer-bottom">
- {translate('users.change_admin_password.description')}
- </p>
-
- <form
- className="text-center"
- onSubmit={(e: React.SyntheticEvent<HTMLFormElement>) => {
- e.preventDefault();
- props.onSubmit();
- }}
- >
- <h2 className="big-spacer-bottom big">
- {translate('users.change_admin_password.form.header')}
- </h2>
+ <PageContentFontWrapper className="sw-body-sm sw-flex sw-flex-col sw-items-center sw-justify-center">
+ <Card className="sw-mx-auto sw-mt-24 sw-w-abs-600 sw-flex sw-items-stretch sw-flex-col">
+ {success ? (
+ <FlagMessage className="sw-my-8" variant="success">
+ <div>
+ <p className="sw-mb-2">{translate('users.change_admin_password.form.success')}</p>
+ {/* We must reload because we need a refresh of the /api/navigation/global call. */}
+ <Link to={getReturnUrl(location)} reloadDocument>
+ {translate('users.change_admin_password.form.continue_to_app')}
+ </Link>
+ </div>
+ </FlagMessage>
+ ) : (
+ <>
+ <Title>{translate('users.change_admin_password.instance_is_at_risk')}</Title>
+ <DarkLabel className="sw-mb-2">
+ {translate('users.change_admin_password.header')}
+ </DarkLabel>
+ <p>{translate('users.change_admin_password.description')}</p>
- <MandatoryFieldsExplanation className="form-field" />
+ <form
+ className="sw-mt-8"
+ onSubmit={(e: React.SyntheticEvent<HTMLFormElement>) => {
+ e.preventDefault();
+ props.onSubmit();
+ }}
+ >
+ <SubTitle className="sw-mb-4">
+ {translate('users.change_admin_password.form.header')}
+ </SubTitle>
- <div className="form-field">
- <label htmlFor="user-password">
- {translate('users.change_admin_password.form.password')}
- <MandatoryFieldMarker />
- </label>
- <input
- id="user-password"
- name="password"
- onChange={(e: React.SyntheticEvent<HTMLInputElement>) => {
- props.onPasswordChange(e.currentTarget.value);
- }}
+ <FormField
+ label={translate('users.change_admin_password.form.password')}
+ htmlFor={PASSWORD_FIELD_ID}
required
- type="password"
- value={passwordValue}
- />
- </div>
+ >
+ <InputField
+ id={PASSWORD_FIELD_ID}
+ name="password"
+ onChange={(e: React.SyntheticEvent<HTMLInputElement>) => {
+ props.onPasswordChange(e.currentTarget.value);
+ }}
+ required
+ type="password"
+ value={passwordValue}
+ />
+ </FormField>
- <div className="form-field">
- <label htmlFor="confirm-user-password">
- {translate('users.change_admin_password.form.confirm')}
- <MandatoryFieldMarker />
- </label>
- <input
- id="confirm-user-password"
- name="confirm-password"
- onChange={(e: React.SyntheticEvent<HTMLInputElement>) => {
- props.onConfirmPasswordChange(e.currentTarget.value);
- }}
+ <FormField
+ label={translate('users.change_admin_password.form.confirm')}
+ htmlFor={CONFIRM_PASSWORD_FIELD_ID}
required
- type="password"
- value={confirmPasswordValue}
- />
+ description={
+ confirmPasswordValue === passwordValue &&
+ passwordValue === DEFAULT_ADMIN_PASSWORD && (
+ <FlagMessage className="sw-mt-2" variant="warning">
+ {translate('users.change_admin_password.form.cannot_use_default_password')}
+ </FlagMessage>
+ )
+ }
+ >
+ <InputField
+ id={CONFIRM_PASSWORD_FIELD_ID}
+ name="confirm-password"
+ onChange={(e: React.SyntheticEvent<HTMLInputElement>) => {
+ props.onConfirmPasswordChange(e.currentTarget.value);
+ }}
+ required
+ type="password"
+ value={confirmPasswordValue}
+ />
+ </FormField>
- {confirmPasswordValue === passwordValue &&
- passwordValue === DEFAULT_ADMIN_PASSWORD && (
- <Alert className="spacer-top" variant="warning">
- {translate('users.change_admin_password.form.cannot_use_default_password')}
- </Alert>
- )}
- </div>
-
- <div className="form-field">
- <SubmitButton disabled={!canSubmit || submitting}>
+ <ButtonPrimary
+ className="sw-mt-8"
+ disabled={!canSubmit || submitting}
+ type="submit"
+ >
+ <Spinner className="sw-mr-2" loading={submitting} />
{translate('update_verb')}
- {submitting && <i className="spinner spacer-left" />}
- </SubmitButton>
- </div>
- </form>
- </>
- )}
- </div>
- </div>
+ </ButtonPrimary>
+ </form>
+ </>
+ )}
+ </Card>
+ </PageContentFontWrapper>
+ </CenteredLayout>
);
}
diff --git a/server/sonar-web/src/main/js/apps/code/code.css b/server/sonar-web/src/main/js/apps/code/code.css
deleted file mode 100644
index 1f981c723fd..00000000000
--- a/server/sonar-web/src/main/js/apps/code/code.css
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2024 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.
- */
-.code-components .page-actions {
- margin-top: -35px;
-}
-
-.code-components .boxed-group.search-results {
- padding-top: 16px;
-}
-
-.code-components .boxed-group.search-results .page-actions {
- margin-top: -50px;
-}
-
-.code-components .boxed-group.search-results li {
- padding: var(--gridSize) calc(2 * var(--gridSize));
- border-width: 0 0 0 2px;
- border-style: solid;
- border-color: transparent;
-}
-
-.code-components .boxed-group.search-results li.selected {
- background-color: var(--info100);
- border-left-color: var(--info500);
-}
-
-.code-components .table-wrapper {
- margin: 0 20px;
-}
-
-.code-components table.data {
- table-layout: fixed;
-}
-
-.code-components table.data td {
- padding: 8px 6px;
- vertical-align: middle;
-}
-
-.code-components table.data th {
- padding-top: 24px;
-}
-
-.code-components table.data th,
-.code-components table.data td:not(.thin) {
- width: 84px;
-}
-
-.code-components table.data td.code-name-cell,
-.code-components table.data th.code-name-cell {
- width: auto;
-}
-
-.code-components table.data th.thin,
-.code-components table.data td.thin {
- width: 10px !important;
-}
-
-.code-components table.data tr.current-folder {
- border-bottom: 1px solid var(--barBorderColor);
-}
-
-.code-components table.data tr.current-folder td {
- padding-bottom: 16px !important;
- padding-top: 10px !important;
-}
-
-.code-breadcrumbs {
- display: flex;
- flex-wrap: wrap;
-}
-
-.code-breadcrumbs > li {
- padding: 5px 5px 3px;
- display: flex;
-}
-
-.code-breadcrumbs > li:first-child {
- padding-left: 0;
-}
-
-.code-breadcrumbs > li::after {
- position: relative;
- top: 1px;
- padding-left: 10px;
- color: var(--secondFontColor);
- font-size: 11px;
- content: '>';
-}
-
-.code-breadcrumbs > li:last-child::after {
- display: none;
-}
-
-@media (max-width: 1200px) {
- .code-name-cell .badge {
- display: none;
- }
-}
-
-.code-components-header {
- position: sticky;
- top: 105px;
- background-color: rgba(255, 255, 255, 0.9);
- z-index: 1;
-}
-
-table > thead > tr.code-components-header > th {
- vertical-align: middle;
-}
-
-.code-child-component-icon {
- display: inline-block;
- border-left: 1px solid var(--secondFontColor);
- border-bottom: 1px solid var(--secondFontColor);
- margin-left: 8px;
- margin-bottom: 8px;
- margin-right: 4px;
- height: 8px;
- width: 4px;
-}
-
-.code-components .no-file .h1 {
- position: fixed;
- top: 50%;
-}
diff --git a/server/sonar-web/src/main/js/apps/code/components/CodeApp.tsx b/server/sonar-web/src/main/js/apps/code/components/CodeApp.tsx
index 15fe0292fc6..9773a641bb9 100644
--- a/server/sonar-web/src/main/js/apps/code/components/CodeApp.tsx
+++ b/server/sonar-web/src/main/js/apps/code/components/CodeApp.tsx
@@ -26,7 +26,6 @@ import { WithBranchLikesProps, useBranchesQuery } from '../../../queries/branch'
import { ComponentQualifier, isPortfolioLike } from '../../../types/component';
import { Breadcrumb, Component, ComponentMeasure, Dict, Metric } from '../../../types/types';
import { addComponent, addComponentBreadcrumbs, clearBucket } from '../bucket';
-import '../code.css';
import { loadMoreChildren, retrieveComponent, retrieveComponentChildren } from '../utils';
import CodeAppRenderer from './CodeAppRenderer';
diff --git a/server/sonar-web/src/main/js/apps/code/components/CodeAppRenderer.tsx b/server/sonar-web/src/main/js/apps/code/components/CodeAppRenderer.tsx
index 715adcae902..ea6dca7d68b 100644
--- a/server/sonar-web/src/main/js/apps/code/components/CodeAppRenderer.tsx
+++ b/server/sonar-web/src/main/js/apps/code/components/CodeAppRenderer.tsx
@@ -39,7 +39,6 @@ import { translate } from '../../../helpers/l10n';
import { BranchLike } from '../../../types/branch-like';
import { isApplication, isPortfolioLike } from '../../../types/component';
import { Breadcrumb, Component, ComponentMeasure, Dict, Metric } from '../../../types/types';
-import '../code.css';
import { getCodeMetrics } from '../utils';
import CodeBreadcrumbs from './CodeBreadcrumbs';
import Components from './Components';
diff --git a/server/sonar-web/src/main/js/apps/code/components/Components.tsx b/server/sonar-web/src/main/js/apps/code/components/Components.tsx
index d0914e973a7..a604644e209 100644
--- a/server/sonar-web/src/main/js/apps/code/components/Components.tsx
+++ b/server/sonar-web/src/main/js/apps/code/components/Components.tsx
@@ -63,7 +63,7 @@ function Components(props: ComponentsProps) {
const columnCount = metrics.length + Number(canBePinned) + Number(showAnalysisDate) + 1;
return (
- <div className="big-spacer-bottom table-wrapper">
+ <div className="sw-mb-4">
<Table
columnCount={columnCount}
columnWidths={[
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 508eefcceb3..087d44a1086 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
@@ -30,7 +30,7 @@ import {
} from 'design-system';
import * as React from 'react';
import { Profile } from '../../../api/quality-profiles';
-import DocLink from '../../../components/common/DocLink';
+import DocumentationLink from '../../../components/common/DocumentationLink';
import { translate } from '../../../helpers/l10n';
import { sanitizeString } from '../../../helpers/sanitize';
import { useActivateRuleMutation } from '../../../queries/quality-profiles';
@@ -117,12 +117,12 @@ export default function ActivationFormModal(props: Readonly<Props>) {
<FlagMessage className="sw-mb-4" variant="info">
{translate('coding_rules.severity_deprecated')}
- <DocLink
+ <DocumentationLink
className="sw-ml-2 sw-whitespace-nowrap"
to="/user-guide/clean-code/introduction/"
>
{translate('learn_more')}
- </DocLink>
+ </DocumentationLink>
</FlagMessage>
<FormField
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/CodingRulesApp.tsx b/server/sonar-web/src/main/js/apps/coding-rules/components/CodingRulesApp.tsx
index 69664400d7d..01a7b24531d 100644
--- a/server/sonar-web/src/main/js/apps/coding-rules/components/CodingRulesApp.tsx
+++ b/server/sonar-web/src/main/js/apps/coding-rules/components/CodingRulesApp.tsx
@@ -690,7 +690,6 @@ export class CodingRulesApp extends React.PureComponent<Props, State> {
loadMore={this.fetchMoreRules}
ready={!this.state.loading}
total={paging.total}
- useMIUIButtons
/>
)}
</>
diff --git a/server/sonar-web/src/main/js/apps/component-measures/style.css b/server/sonar-web/src/main/js/apps/component-measures/style.css
index c7f9177c00c..d9177fc0aa3 100644
--- a/server/sonar-web/src/main/js/apps/component-measures/style.css
+++ b/server/sonar-web/src/main/js/apps/component-measures/style.css
@@ -21,34 +21,6 @@ button.search-navigator-facet {
text-align: start;
}
-.search-navigator-facet .leak-box {
- height: var(--controlHeight);
- line-height: var(--controlHeight);
- padding: 0 var(--gridSize);
- margin-top: -1px;
- margin-right: calc(-0.75 * var(--gridSize) - 1px);
- border-radius: 2px;
- box-sizing: border-box;
-}
-
-.search-navigator-facet:hover .leak-box,
-.search-navigator-facet.active .leak-box {
- height: calc(var(--controlHeight) - 2px);
- margin-top: 0;
- margin-right: calc(-0.75 * var(--gridSize));
- border-top: none;
- border-bottom: none;
- border-right: none;
- border-top-left-radius: 0;
- border-bottom-left-radius: 0;
-}
-
-.search-navigator-facet.active .leak-box {
- border-left: none;
- border-top-left-radius: 0;
- border-bottom-left-radius: 0;
-}
-
.measure-details-treemap-legend.color-box-legend {
margin-right: 0;
}
diff --git a/server/sonar-web/src/main/js/apps/create/project/Azure/AzureProjectAccordion.tsx b/server/sonar-web/src/main/js/apps/create/project/Azure/AzureProjectAccordion.tsx
index 42e0bf88c3e..72ebbb98a26 100644
--- a/server/sonar-web/src/main/js/apps/create/project/Azure/AzureProjectAccordion.tsx
+++ b/server/sonar-web/src/main/js/apps/create/project/Azure/AzureProjectAccordion.tsx
@@ -64,7 +64,7 @@ export default function AzureProjectAccordion(props: AzureProjectAccordionProps)
</span>
}
>
- {/* eslint-disable-next-line local-rules/no-conditional-rendering-of-deferredspinner*/}
+ {/* eslint-disable-next-line local-rules/no-conditional-rendering-of-spinner*/}
{open && (
<Spinner loading={loading}>
{repositories.length === 0 ? (
@@ -113,7 +113,6 @@ export default function AzureProjectAccordion(props: AzureProjectAccordionProps)
count={limitedRepositories.length}
total={repositories.length}
loadMore={() => setPage((p) => p + 1)}
- useMIUIButtons
/>
</>
)}
diff --git a/server/sonar-web/src/main/js/apps/create/project/Azure/AzureProjectsList.tsx b/server/sonar-web/src/main/js/apps/create/project/Azure/AzureProjectsList.tsx
index 70c5d97da40..8bb9844a973 100644
--- a/server/sonar-web/src/main/js/apps/create/project/Azure/AzureProjectsList.tsx
+++ b/server/sonar-web/src/main/js/apps/create/project/Azure/AzureProjectsList.tsx
@@ -131,7 +131,6 @@ export default function AzureProjectsList(props: AzureProjectsListProps) {
count={displayedProjects.length}
loadMore={() => setPage((p) => p + 1)}
total={filteredProjects.length}
- useMIUIButtons
/>
</div>
);
diff --git a/server/sonar-web/src/main/js/apps/create/project/BitbucketCloud/BitbucketCloudSearchForm.tsx b/server/sonar-web/src/main/js/apps/create/project/BitbucketCloud/BitbucketCloudSearchForm.tsx
index 1b1c60f2238..2b3fcd5c70e 100644
--- a/server/sonar-web/src/main/js/apps/create/project/BitbucketCloud/BitbucketCloudSearchForm.tsx
+++ b/server/sonar-web/src/main/js/apps/create/project/BitbucketCloud/BitbucketCloudSearchForm.tsx
@@ -115,7 +115,6 @@ export default function BitbucketCloudSearchForm(props: BitbucketCloudSearchForm
pageSize={BITBUCKET_CLOUD_PROJECTS_PAGESIZE}
loadMore={props.onLoadMore}
loading={loadingMore}
- useMIUIButtons
/>
</div>
);
diff --git a/server/sonar-web/src/main/js/apps/create/project/Github/GitHubProjectCreateRenderer.tsx b/server/sonar-web/src/main/js/apps/create/project/Github/GitHubProjectCreateRenderer.tsx
index e8bf697c7ab..9abb7f27e35 100644
--- a/server/sonar-web/src/main/js/apps/create/project/Github/GitHubProjectCreateRenderer.tsx
+++ b/server/sonar-web/src/main/js/apps/create/project/Github/GitHubProjectCreateRenderer.tsx
@@ -37,8 +37,8 @@ import {
import React, { useState } from 'react';
import { FormattedMessage } from 'react-intl';
import ListFooter from '../../../../components/controls/ListFooter';
-import { LabelValueSelectOption } from '../../../../components/controls/Select';
import { translate } from '../../../../helpers/l10n';
+import { LabelValueSelectOption } from '../../../../helpers/search';
import { getBaseUrl } from '../../../../helpers/system';
import { GithubOrganization, GithubRepository } from '../../../../types/alm-integration';
import { AlmKeys, AlmSettingsInstance } from '../../../../types/alm-settings';
@@ -167,7 +167,6 @@ function RepositoryList(props: RepositoryListProps) {
total={repositoryPaging.total}
loadMore={props.onLoadMore}
loading={loadingRepositories}
- useMIUIButtons
/>
</div>
);
diff --git a/server/sonar-web/src/main/js/apps/create/project/Gitlab/GitlabProjectSelectionForm.tsx b/server/sonar-web/src/main/js/apps/create/project/Gitlab/GitlabProjectSelectionForm.tsx
index c9f8f472d23..7d83e80aaa3 100644
--- a/server/sonar-web/src/main/js/apps/create/project/Gitlab/GitlabProjectSelectionForm.tsx
+++ b/server/sonar-web/src/main/js/apps/create/project/Gitlab/GitlabProjectSelectionForm.tsx
@@ -117,7 +117,6 @@ export default function GitlabProjectSelectionForm(props: GitlabProjectSelection
loading={loadingMore}
pageSize={projectsPaging.pageSize}
total={projectsPaging.total}
- useMIUIButtons
/>
</>
);
diff --git a/server/sonar-web/src/main/js/apps/groups/GroupsApp.tsx b/server/sonar-web/src/main/js/apps/groups/GroupsApp.tsx
index 7a37e33d652..952985e7ece 100644
--- a/server/sonar-web/src/main/js/apps/groups/GroupsApp.tsx
+++ b/server/sonar-web/src/main/js/apps/groups/GroupsApp.tsx
@@ -79,7 +79,6 @@ export default function GroupsApp() {
loadMore={fetchNextPage}
ready={!isLoading}
total={data?.pages[0].page.total}
- useMIUIButtons
/>
</main>
</PageContentFontWrapper>
diff --git a/server/sonar-web/src/main/js/apps/groups/components/ViewMembersModal.tsx b/server/sonar-web/src/main/js/apps/groups/components/ViewMembersModal.tsx
index 742251d696e..e124d8bf546 100644
--- a/server/sonar-web/src/main/js/apps/groups/components/ViewMembersModal.tsx
+++ b/server/sonar-web/src/main/js/apps/groups/components/ViewMembersModal.tsx
@@ -78,7 +78,6 @@ export default function ViewMembersModal(props: Readonly<Props>) {
count={users.length}
loadMore={fetchNextPage}
total={data?.pages[0].page.total}
- useMIUIButtons
/>
)}
</div>
diff --git a/server/sonar-web/src/main/js/apps/issues/components/ComponentBreadcrumbs.tsx b/server/sonar-web/src/main/js/apps/issues/components/ComponentBreadcrumbs.tsx
index 73663f19e71..926327f5f6f 100644
--- a/server/sonar-web/src/main/js/apps/issues/components/ComponentBreadcrumbs.tsx
+++ b/server/sonar-web/src/main/js/apps/issues/components/ComponentBreadcrumbs.tsx
@@ -18,9 +18,8 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import styled from '@emotion/styled';
-import { Badge, themeBorder, themeContrast } from 'design-system';
+import { Badge, BranchIcon, themeBorder, themeContrast } from 'design-system';
import * as React from 'react';
-import BranchIcon from '../../../components/icons/BranchIcon';
import { translate, translateWithParameters } from '../../../helpers/l10n';
import { collapsePath, limitComponentName } from '../../../helpers/path';
import { ComponentQualifier, isView } from '../../../types/component';
diff --git a/server/sonar-web/src/main/js/apps/issues/components/IssueGuide.tsx b/server/sonar-web/src/main/js/apps/issues/components/IssueGuide.tsx
index fa814f50376..32e94ac3f4b 100644
--- a/server/sonar-web/src/main/js/apps/issues/components/IssueGuide.tsx
+++ b/server/sonar-web/src/main/js/apps/issues/components/IssueGuide.tsx
@@ -23,7 +23,7 @@ import { FormattedMessage } from 'react-intl';
import { CallBackProps } from 'react-joyride';
import { dismissNotice } from '../../../api/users';
import { CurrentUserContext } from '../../../app/components/current-user/CurrentUserContext';
-import DocLink from '../../../components/common/DocLink';
+import DocumentationLink from '../../../components/common/DocumentationLink';
import { SCREEN_POSITION_COMPUTE_DELAY } from '../../../components/common/ScreenPositionHelper';
import { translate, translateWithParameters } from '../../../helpers/l10n';
import { NoticeType } from '../../../types/users';
@@ -153,9 +153,9 @@ export default function IssueGuide({ run }: Props) {
defaultMessage={translate('guiding.issue_list.5.content')}
values={{
link: (
- <DocLink to="/user-guide/clean-code/introduction" className="sw-capitalize">
+ <DocumentationLink to="/user-guide/clean-code/introduction" className="sw-capitalize">
{translate('documentation')}
- </DocLink>
+ </DocumentationLink>
),
}}
/>
diff --git a/server/sonar-web/src/main/js/apps/issues/components/IssueReviewHistory.tsx b/server/sonar-web/src/main/js/apps/issues/components/IssueReviewHistory.tsx
index 9bdc3e17d9b..36b99e6805b 100644
--- a/server/sonar-web/src/main/js/apps/issues/components/IssueReviewHistory.tsx
+++ b/server/sonar-web/src/main/js/apps/issues/components/IssueReviewHistory.tsx
@@ -33,7 +33,7 @@ import * as React from 'react';
import { getIssueChangelog } from '../../../api/issues';
import DateTimeFormatter from '../../../components/intl/DateTimeFormatter';
import IssueChangelogDiff from '../../../components/issue/components/IssueChangelogDiff';
-import LegacyAvatar from '../../../components/ui/LegacyAvatar';
+import Avatar from '../../../components/ui/Avatar';
import { translate, translateWithParameters } from '../../../helpers/l10n';
import { sanitizeUserInput } from '../../../helpers/sanitize';
import { ReviewHistoryType } from '../../../types/security-hotspots';
@@ -88,7 +88,7 @@ export default function IssueReviewHistory(props: HotspotReviewHistoryProps) {
<LightLabel as="div" className="sw-flex sw-gap-2">
{user.name && (
<div className="sw-flex sw-items-center sw-gap-1">
- <LegacyAvatar hash={user.avatar} name={user.name} size={20} />
+ <Avatar hash={user.avatar} name={user.name} size="xs" />
<span className="sw-body-sm-highlight">
{user.active ? user.name : translateWithParameters('user.x_deleted', user.name)}
</span>
diff --git a/server/sonar-web/src/main/js/apps/issues/components/IssuesApp.tsx b/server/sonar-web/src/main/js/apps/issues/components/IssuesApp.tsx
index f712711fcd7..f793b5bc2b6 100644
--- a/server/sonar-web/src/main/js/apps/issues/components/IssuesApp.tsx
+++ b/server/sonar-web/src/main/js/apps/issues/components/IssuesApp.tsx
@@ -1089,10 +1089,10 @@ export class App extends React.PureComponent<Props, State> {
<SideBarStyle>
<ScreenPositionHelper className="sw-z-filterbar">
{({ top }) => (
- <nav
+ <StyledNav
aria-label={openIssue ? translate('list_of_issues') : translate('filters')}
data-testid="issues-nav-bar"
- className="issues-nav-bar sw-overflow-y-auto issue-filters-list"
+ className="issues-nav-bar sw-overflow-y-auto"
style={{ height: `calc((100vh - ${top}px) - ${LAYOUT_FOOTER_HEIGHT}px)` }}
>
<div className="sw-w-[300px] lg:sw-w-[390px] sw-h-full">
@@ -1128,7 +1128,7 @@ export class App extends React.PureComponent<Props, State> {
this.renderFacets(warning)
)}
</div>
- </nav>
+ </StyledNav>
)}
</ScreenPositionHelper>
</SideBarStyle>
@@ -1189,7 +1189,6 @@ export class App extends React.PureComponent<Props, State> {
loading={loadingMore}
pageSize={ISSUES_PAGE_SIZE}
total={paging.total}
- useMIUIButtons
/>
)}
@@ -1393,3 +1392,13 @@ const StyledIssueWrapper = styled.div`
border-top: none;
}
`;
+
+const StyledNav = styled.nav`
+ /*
+* On Firefox on Windows, the scrollbar hides the sidebar's content.
+* Using 'scrollbar-gutter:stable' is a workaround to ensure consistency with other browsers.
+* @see https://bugzilla.mozilla.org/show_bug.cgi?id=764076
+* @see https://discuss.sonarsource.com/t/unnecessary-horizontal-scrollbar-on-issues-page/14889/4
+*/
+ scrollbar-gutter: stable;
+`;
diff --git a/server/sonar-web/src/main/js/apps/issues/components/IssuesList.tsx b/server/sonar-web/src/main/js/apps/issues/components/IssuesList.tsx
index ce4845a8874..840cc4a65b0 100644
--- a/server/sonar-web/src/main/js/apps/issues/components/IssuesList.tsx
+++ b/server/sonar-web/src/main/js/apps/issues/components/IssuesList.tsx
@@ -17,12 +17,12 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+import { Spinner } from 'design-system';
import { groupBy } from 'lodash';
import * as React from 'react';
import IssueItem from '../../../components/issue/Issue';
import { BranchLike } from '../../../types/branch-like';
import { Component, Issue } from '../../../types/types';
-
import ComponentBreadcrumbs from './ComponentBreadcrumbs';
interface Props {
@@ -93,7 +93,7 @@ export default class IssuesList extends React.PureComponent<Props, State> {
if (prerender) {
return (
<div>
- <i className="spinner" />
+ <Spinner />
</div>
);
}
diff --git a/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/CrossComponentSourceViewer.tsx b/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/CrossComponentSourceViewer.tsx
index 2369d3edba5..ff0e0c20fd1 100644
--- a/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/CrossComponentSourceViewer.tsx
+++ b/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/CrossComponentSourceViewer.tsx
@@ -17,7 +17,7 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import { Spinner } from 'design-system';
+import { FlagMessage, Spinner } from 'design-system';
import { findLastIndex, keyBy } from 'lodash';
import * as React from 'react';
import { getComponentForSourceViewer, getDuplications, getSources } from '../../../api/components';
@@ -33,7 +33,6 @@ import {
duplicationsByLine as getDuplicationsByLine,
issuesByComponentAndLine,
} from '../../../components/SourceViewer/helpers/indexing';
-import { Alert } from '../../../components/ui/Alert';
import { WorkspaceContext } from '../../../components/workspace/context';
import { getBranchLikeQuery } from '../../../helpers/branch-like';
import { throwGlobalError } from '../../../helpers/error';
@@ -199,9 +198,9 @@ export default class CrossComponentSourceViewer extends React.PureComponent<Prop
if (notAccessible) {
return (
- <Alert className="spacer-top" variant="warning">
+ <FlagMessage className="sw-mt-2" variant="warning">
{translate('code_viewer.no_source_code_displayed_due_to_security')}
- </Alert>
+ </FlagMessage>
);
}
diff --git a/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/IssueSourceViewerHeader.tsx b/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/IssueSourceViewerHeader.tsx
index a2391ff6f2a..9b2b3c398bb 100644
--- a/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/IssueSourceViewerHeader.tsx
+++ b/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/IssueSourceViewerHeader.tsx
@@ -22,7 +22,7 @@ import styled from '@emotion/styled';
import classNames from 'classnames';
import {
ChevronRightIcon,
- CopyIcon,
+ ClipboardIconButton,
HoverLink,
InteractiveIcon,
LightLabel,
@@ -34,8 +34,6 @@ import {
import * as React from 'react';
import { ComponentContext } from '../../../app/components/componentContext/ComponentContext';
import { useCurrentUser } from '../../../app/components/current-user/CurrentUserContext';
-import Tooltip from '../../../components/controls/Tooltip';
-import { ClipboardBase } from '../../../components/controls/clipboard';
import { DEFAULT_ISSUES_QUERY } from '../../../components/shared/utils';
import { getBranchLikeQuery, isBranch, isPullRequest } from '../../../helpers/branch-like';
import { translate } from '../../../helpers/l10n';
@@ -139,30 +137,11 @@ export function IssueSourceViewerHeader(props: Readonly<Props>) {
{collapsedDirFromPath(path)}
{fileFromPath(path)}
</LightLabel>
-
- <ClipboardBase>
- {({ setCopyButton, copySuccess }) => {
- return (
- <Tooltip
- mouseEnterDelay={INTERACTIVE_TOOLTIP_DELAY}
- overlay={
- <div className="sw-w-abs-150 sw-text-center">
- {translate(copySuccess ? 'copied_action' : 'copy_to_clipboard')}
- </div>
- }
- {...(copySuccess ? { visible: copySuccess } : undefined)}
- >
- <InteractiveIcon
- Icon={CopyIcon}
- aria-label={translate('source_viewer.click_to_copy_filepath')}
- data-clipboard-text={path}
- className="sw-h-6 sw-mx-2"
- innerRef={setCopyButton}
- />
- </Tooltip>
- );
- }}
- </ClipboardBase>
+ <ClipboardIconButton
+ className="sw-h-6 sw-mx-2"
+ copyValue={path}
+ copyLabel={translate('source_viewer.click_to_copy_filepath')}
+ />
</span>
)}
</div>
diff --git a/server/sonar-web/src/main/js/apps/issues/issues-subnavigation/SubnavigationIssuesList.tsx b/server/sonar-web/src/main/js/apps/issues/issues-subnavigation/SubnavigationIssuesList.tsx
index 535026d0c36..8d989f2d7f3 100644
--- a/server/sonar-web/src/main/js/apps/issues/issues-subnavigation/SubnavigationIssuesList.tsx
+++ b/server/sonar-web/src/main/js/apps/issues/issues-subnavigation/SubnavigationIssuesList.tsx
@@ -88,7 +88,6 @@ export default function SubnavigationIssuesList(props: Props) {
loadMore={props.fetchMoreIssues}
loading={loadingMore}
total={paging.total}
- useMIUIButtons
/>
)}
</StyledWrapper>
diff --git a/server/sonar-web/src/main/js/apps/issues/sidebar/ListStyleFacet.tsx b/server/sonar-web/src/main/js/apps/issues/sidebar/ListStyleFacet.tsx
index 4ccc67d78ef..0435087a152 100644
--- a/server/sonar-web/src/main/js/apps/issues/sidebar/ListStyleFacet.tsx
+++ b/server/sonar-web/src/main/js/apps/issues/sidebar/ListStyleFacet.tsx
@@ -420,7 +420,6 @@ export class ListStyleFacet<S> extends React.Component<Props<S>, State<S>> {
loadMoreAriaLabel={showMoreAriaLabel}
ready={!searching}
total={searchPaging.total}
- useMIUIButtons
/>
)}
</>
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 c16ff80ecf6..a728b86e641 100644
--- a/server/sonar-web/src/main/js/apps/issues/styles.css
+++ b/server/sonar-web/src/main/js/apps/issues/styles.css
@@ -17,14 +17,6 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-.issues-main-header .component-name {
- line-height: var(--controlHeight);
-}
-
-.issues-main-header-spinner {
- margin-right: 2px;
-}
-
.not-all-issue-warning.open-issue-list {
background-color: var(--barBackgroundColor);
box-sizing: border-box;
diff --git a/server/sonar-web/src/main/js/apps/maintenance/components/App.tsx b/server/sonar-web/src/main/js/apps/maintenance/components/App.tsx
index c1f776c0154..03f90e84fe0 100644
--- a/server/sonar-web/src/main/js/apps/maintenance/components/App.tsx
+++ b/server/sonar-web/src/main/js/apps/maintenance/components/App.tsx
@@ -18,7 +18,7 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import styled from '@emotion/styled';
-import { ButtonPrimary, Card, Link, Note, Spinner, Title } from 'design-system';
+import { ButtonPrimary, Card, CenteredLayout, Link, Note, Spinner, Title } from 'design-system';
import * as React from 'react';
import { Helmet } from 'react-helmet-async';
import { FormattedMessage } from 'react-intl';
@@ -137,7 +137,7 @@ export default class App extends React.PureComponent<Props, State> {
return (
<>
<Helmet defaultTitle={translate('maintenance.page')} defer={false} />
- <div className="page-wrapper-simple" id="bd">
+ <CenteredLayout className="sw-flex sw-justify-around sw-mt-32" id="bd">
<Card className="sw-body-sm sw-p-10 sw-w-abs-400" id="nonav">
{status === 'OFFLINE' && (
<>
@@ -307,7 +307,7 @@ export default class App extends React.PureComponent<Props, State> {
</>
)}
</Card>
- </div>
+ </CenteredLayout>
</>
);
}
diff --git a/server/sonar-web/src/main/js/apps/marketplace/App.tsx b/server/sonar-web/src/main/js/apps/marketplace/App.tsx
index 3ed9b3b018b..c122f26873a 100644
--- a/server/sonar-web/src/main/js/apps/marketplace/App.tsx
+++ b/server/sonar-web/src/main/js/apps/marketplace/App.tsx
@@ -220,11 +220,7 @@ class App extends React.PureComponent<Props, State> {
readOnly={!allowActions}
refreshPending={this.props.fetchPendingPlugins}
/>
- <ListFooter
- useMIUIButtons
- count={filteredPlugins.length}
- total={plugins.length}
- />
+ <ListFooter count={filteredPlugins.length} total={plugins.length} />
</>
)}
</Spinner>
diff --git a/server/sonar-web/src/main/js/apps/overview/branches/FirstAnalysisNextStepsNotif.tsx b/server/sonar-web/src/main/js/apps/overview/branches/FirstAnalysisNextStepsNotif.tsx
index 57ec902f31c..88737d1605f 100644
--- a/server/sonar-web/src/main/js/apps/overview/branches/FirstAnalysisNextStepsNotif.tsx
+++ b/server/sonar-web/src/main/js/apps/overview/branches/FirstAnalysisNextStepsNotif.tsx
@@ -17,10 +17,10 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+import { Link } from 'design-system';
import * as React from 'react';
import { FormattedMessage } from 'react-intl';
import withCurrentUserContext from '../../../app/components/current-user/withCurrentUserContext';
-import Link from '../../../components/common/Link';
import DismissableAlert from '../../../components/ui/DismissableAlert';
import { translate } from '../../../helpers/l10n';
import { queryToSearch } from '../../../helpers/urls';
@@ -84,46 +84,48 @@ export function FirstAnalysisNextStepsNotif(props: FirstAnalysisNextStepsNotifPr
return (
<DismissableAlert alertKey={`config_ci_pr_deco.${component.key}`} variant="info">
- {showOnlyConfigureCI && (
- <FormattedMessage
- defaultMessage={translate('overview.project.next_steps.set_up_ci')}
- id="overview.project.next_steps.set_up_ci"
- values={{
- link: tutorialsLink,
- }}
- />
- )}
-
- {showOnlyConfigurePR &&
- (isProjectAdmin ? (
+ <div>
+ {showOnlyConfigureCI && (
<FormattedMessage
- defaultMessage={translate('overview.project.next_steps.set_up_pr_deco.admin')}
- id="overview.project.next_steps.set_up_pr_deco.admin"
+ defaultMessage={translate('overview.project.next_steps.set_up_ci')}
+ id="overview.project.next_steps.set_up_ci"
values={{
- link_project_settings: projectSettingsLink,
+ link: tutorialsLink,
}}
/>
- ) : (
- translate('overview.project.next_steps.set_up_pr_deco')
- ))}
+ )}
- {showBoth &&
- (isProjectAdmin ? (
- <FormattedMessage
- defaultMessage={translate('overview.project.next_steps.set_up_pr_deco_and_ci.admin')}
- id="overview.project.next_steps.set_up_pr_deco_and_ci.admin"
- values={{
- link_ci: tutorialsLink,
- link_project_settings: projectSettingsLink,
- }}
- />
- ) : (
- <FormattedMessage
- defaultMessage={translate('overview.project.next_steps.set_up_pr_deco_and_ci')}
- id="overview.project.next_steps.set_up_pr_deco_and_ci"
- values={{ link_ci: tutorialsLink }}
- />
- ))}
+ {showOnlyConfigurePR &&
+ (isProjectAdmin ? (
+ <FormattedMessage
+ defaultMessage={translate('overview.project.next_steps.set_up_pr_deco.admin')}
+ id="overview.project.next_steps.set_up_pr_deco.admin"
+ values={{
+ link_project_settings: projectSettingsLink,
+ }}
+ />
+ ) : (
+ translate('overview.project.next_steps.set_up_pr_deco')
+ ))}
+
+ {showBoth &&
+ (isProjectAdmin ? (
+ <FormattedMessage
+ defaultMessage={translate('overview.project.next_steps.set_up_pr_deco_and_ci.admin')}
+ id="overview.project.next_steps.set_up_pr_deco_and_ci.admin"
+ values={{
+ link_ci: tutorialsLink,
+ link_project_settings: projectSettingsLink,
+ }}
+ />
+ ) : (
+ <FormattedMessage
+ defaultMessage={translate('overview.project.next_steps.set_up_pr_deco_and_ci')}
+ id="overview.project.next_steps.set_up_pr_deco_and_ci"
+ values={{ link_ci: tutorialsLink }}
+ />
+ ))}
+ </div>
</DismissableAlert>
);
}
diff --git a/server/sonar-web/src/main/js/apps/overview/branches/MeasuresPanelNoNewCode.tsx b/server/sonar-web/src/main/js/apps/overview/branches/MeasuresPanelNoNewCode.tsx
index 17bc57ee8fc..cc50083316b 100644
--- a/server/sonar-web/src/main/js/apps/overview/branches/MeasuresPanelNoNewCode.tsx
+++ b/server/sonar-web/src/main/js/apps/overview/branches/MeasuresPanelNoNewCode.tsx
@@ -17,11 +17,10 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+import { Link, Note, getTabPanelId } from 'design-system';
import * as React from 'react';
import { FormattedMessage } from 'react-intl';
-import DocLink from '../../../components/common/DocLink';
-import Link from '../../../components/common/Link';
-import { getTabPanelId } from '../../../components/controls/BoxedTabs';
+import DocumentationLink from '../../../components/common/DocumentationLink';
import { getBranchLikeQuery } from '../../../helpers/branch-like';
import { translate } from '../../../helpers/l10n';
import { getBaseUrl } from '../../../helpers/system';
@@ -62,18 +61,18 @@ export default function MeasuresPanelNoNewCode(props: MeasuresPanelNoNewCodeProp
return (
<div
- className="display-flex-center display-flex-justify-center"
+ className="sw-flex sw-items-center sw-justify-center"
id={getTabPanelId(CodeScope.New)}
style={{ height: 500 }}
>
<img
alt="" /* Make screen readers ignore this image; it's purely eye candy. */
- className="spacer-right"
+ className="sw-mr-2"
height={52}
src={`${getBaseUrl()}/images/source-code.svg`}
/>
- <div className="big-spacer-left text-muted" style={{ maxWidth: 500 }}>
- <p className="spacer-bottom big-spacer-top big">{translate(badExplanationKey)}</p>
+ <Note as="div" className="sw-ml-4 sw-max-w-abs-500">
+ <p className="sw-mb-2 sw-mt-4">{translate(badExplanationKey)}</p>
{hasBadNewCodeSettingSameRef ? (
showSettingsLink && (
<p>
@@ -102,13 +101,15 @@ export default function MeasuresPanelNoNewCode(props: MeasuresPanelNoNewCodeProp
id="overview.measures.empty_link"
values={{
learn_more_link: (
- <DocLink to="/user-guide/clean-as-you-code/">{translate('learn_more')}</DocLink>
+ <DocumentationLink to="/user-guide/clean-as-you-code/">
+ {translate('learn_more')}
+ </DocumentationLink>
),
}}
/>
</p>
)}
- </div>
+ </Note>
</div>
);
}
diff --git a/server/sonar-web/src/main/js/apps/overview/branches/NewCodeMeasuresPanel.tsx b/server/sonar-web/src/main/js/apps/overview/branches/NewCodeMeasuresPanel.tsx
index 20c966c44f5..48372174414 100644
--- a/server/sonar-web/src/main/js/apps/overview/branches/NewCodeMeasuresPanel.tsx
+++ b/server/sonar-web/src/main/js/apps/overview/branches/NewCodeMeasuresPanel.tsx
@@ -27,11 +27,11 @@ import {
TextError,
TextSubdued,
TrendUpCircleIcon,
+ getTabPanelId,
themeColor,
} from 'design-system';
import React from 'react';
import { useIntl } from 'react-intl';
-import { getTabPanelId } from '../../../components/controls/BoxedTabs';
import { getLeakValue } from '../../../components/measure/utils';
import { DEFAULT_ISSUES_QUERY } from '../../../components/shared/utils';
import { getBranchLikeQuery } from '../../../helpers/branch-like';
diff --git a/server/sonar-web/src/main/js/apps/overview/branches/TabsPanel.tsx b/server/sonar-web/src/main/js/apps/overview/branches/TabsPanel.tsx
index d4f366e35e1..489860ba459 100644
--- a/server/sonar-web/src/main/js/apps/overview/branches/TabsPanel.tsx
+++ b/server/sonar-web/src/main/js/apps/overview/branches/TabsPanel.tsx
@@ -29,7 +29,7 @@ import {
} from 'design-system';
import * as React from 'react';
import { FormattedMessage } from 'react-intl';
-import DocLink from '../../../components/common/DocLink';
+import DocumentationLink from '../../../components/common/DocumentationLink';
import { translate } from '../../../helpers/l10n';
import { isDiffMetric } from '../../../helpers/measures';
import { CodeScope } from '../../../helpers/urls';
@@ -182,12 +182,12 @@ export function TabsPanel(props: React.PropsWithChildren<MeasuresPanelProps>) {
{`${translate('indexation.in_progress')} ${translate(
'indexation.details_unavailable',
)}`}
- <DocLink
+ <DocumentationLink
className="sw-ml-1 sw-whitespace-nowrap"
to="/instance-administration/reindexing/"
>
{translate('learn_more')}
- </DocLink>
+ </DocumentationLink>
</span>
</FlagMessage>
)}
diff --git a/server/sonar-web/src/main/js/apps/overview/components/AnalysisErrorModal.tsx b/server/sonar-web/src/main/js/apps/overview/components/AnalysisErrorModal.tsx
index 3158d29fc17..933c04c4924 100644
--- a/server/sonar-web/src/main/js/apps/overview/components/AnalysisErrorModal.tsx
+++ b/server/sonar-web/src/main/js/apps/overview/components/AnalysisErrorModal.tsx
@@ -17,9 +17,9 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+
+import { Modal } from 'design-system';
import * as React from 'react';
-import Modal from '../../../components/controls/Modal';
-import { ResetButtonLink } from '../../../components/controls/buttons';
import { hasMessage, translate } from '../../../helpers/l10n';
import { Task } from '../../../types/tasks';
import { Component } from '../../../types/types';
@@ -32,36 +32,26 @@ interface Props {
onClose: () => void;
}
-export function AnalysisErrorModal(props: Props) {
- const { component, currentTask } = props;
+export function AnalysisErrorModal(props: Readonly<Props>) {
+ const { component, currentTask, onClose } = props;
const header = translate('error');
const licenseError =
- currentTask.errorType &&
+ currentTask.errorType !== undefined &&
hasMessage('license.component_navigation.button', currentTask.errorType);
return (
- <Modal contentLabel={header} onRequestClose={props.onClose}>
- <header className="modal-head">
- <h2>{header}</h2>
- </header>
-
- <div className="modal-body modal-container">
- {licenseError ? (
+ <Modal
+ body={
+ licenseError ? (
<AnalysisLicenseError currentTask={currentTask} />
) : (
- <AnalysisErrorMessage
- component={component}
- currentTask={currentTask}
- onLeave={props.onClose}
- />
- )}
- </div>
-
- <footer className="modal-foot">
- <ResetButtonLink onClick={props.onClose}>{translate('close')}</ResetButtonLink>
- </footer>
- </Modal>
+ <AnalysisErrorMessage component={component} currentTask={currentTask} onLeave={onClose} />
+ )
+ }
+ headerTitle={header}
+ onClose={onClose}
+ />
);
}
diff --git a/server/sonar-web/src/main/js/apps/overview/components/OverviewDisabledLinkTooltip.tsx b/server/sonar-web/src/main/js/apps/overview/components/OverviewDisabledLinkTooltip.tsx
index 630371452bb..7cc7dd68d74 100644
--- a/server/sonar-web/src/main/js/apps/overview/components/OverviewDisabledLinkTooltip.tsx
+++ b/server/sonar-web/src/main/js/apps/overview/components/OverviewDisabledLinkTooltip.tsx
@@ -18,7 +18,7 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
-import DocLink from '../../../components/common/DocLink';
+import DocumentationLink from '../../../components/common/DocumentationLink';
import { translate } from '../../../helpers/l10n';
export function OverviewDisabledLinkTooltip() {
@@ -34,9 +34,9 @@ export function OverviewDisabledLinkTooltip() {
<span className="sw-body-sm-highlight">{translate('indexation.learn_more')}</span>
- <DocLink className="sw-ml-1" to="/instance-administration/reindexing/">
+ <DocumentationLink className="sw-ml-1" to="/instance-administration/reindexing/">
{translate('indexation.reindexing')}
- </DocLink>
+ </DocumentationLink>
</div>
);
}
diff --git a/server/sonar-web/src/main/js/apps/overview/components/__tests__/AnalysisErrorModal-test.tsx b/server/sonar-web/src/main/js/apps/overview/components/__tests__/AnalysisErrorModal-test.tsx
new file mode 100644
index 00000000000..eaeb834eaf5
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/overview/components/__tests__/AnalysisErrorModal-test.tsx
@@ -0,0 +1,67 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2024 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 { screen } from '@testing-library/react';
+import * as React from 'react';
+import { mockComponent } from '../../../../helpers/mocks/component';
+import { mockTask } from '../../../../helpers/mocks/tasks';
+import { renderApp } from '../../../../helpers/testReactTestingUtils';
+import { AnalysisErrorModal } from '../AnalysisErrorModal';
+
+jest.mock('../AnalysisErrorMessage', () => ({
+ AnalysisErrorMessage: () => <div>analysis error message</div>,
+}));
+
+jest.mock('../AnalysisLicenseError', () => ({
+ AnalysisLicenseError: () => <div>analysis license error</div>,
+}));
+
+it('should show the license error message', () => {
+ renderAnalysisErrorModal({
+ currentTask: mockTask({ errorType: 'ANY_TYPE' }),
+ });
+
+ expect(screen.getByText('error')).toBeInTheDocument();
+ expect(screen.queryByText('analysis error message')).not.toBeInTheDocument();
+ expect(screen.getByText('analysis license error')).toBeInTheDocument();
+});
+
+it('should show the analysis error message', () => {
+ renderAnalysisErrorModal();
+
+ expect(screen.getByText('error')).toBeInTheDocument();
+ expect(screen.queryByText('analysis license error')).not.toBeInTheDocument();
+ expect(screen.getByText('analysis error message')).toBeInTheDocument();
+});
+
+function renderAnalysisErrorModal(
+ overrides: Partial<Parameters<typeof AnalysisErrorModal>[0]> = {},
+ location = '/',
+) {
+ return renderApp(
+ location,
+ <AnalysisErrorModal
+ component={mockComponent()}
+ currentTask={mockTask()}
+ onClose={jest.fn()}
+ {...overrides}
+ />,
+ );
+}
diff --git a/server/sonar-web/src/main/js/apps/projectBranches/components/BranchLikeTabs.tsx b/server/sonar-web/src/main/js/apps/projectBranches/components/BranchLikeTabs.tsx
index 62fe2d8dec5..acd2b984c92 100644
--- a/server/sonar-web/src/main/js/apps/projectBranches/components/BranchLikeTabs.tsx
+++ b/server/sonar-web/src/main/js/apps/projectBranches/components/BranchLikeTabs.tsx
@@ -17,10 +17,9 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import { BranchIcon, PullRequestIcon, ToggleButton } from 'design-system';
+import { BranchIcon, PullRequestIcon, ToggleButton, getTabId, getTabPanelId } from 'design-system';
import * as React from 'react';
import { useState } from 'react';
-import { getTabId, getTabPanelId } from '../../../components/controls/BoxedTabs';
import {
isBranch,
isMainBranch,
diff --git a/server/sonar-web/src/main/js/apps/projectInformation/about/AboutProject.tsx b/server/sonar-web/src/main/js/apps/projectInformation/about/AboutProject.tsx
index 0f2d3a3e87d..086ad5b782a 100644
--- a/server/sonar-web/src/main/js/apps/projectInformation/about/AboutProject.tsx
+++ b/server/sonar-web/src/main/js/apps/projectInformation/about/AboutProject.tsx
@@ -62,14 +62,11 @@ export default function AboutProject(props: AboutProjectProps) {
{!isApp &&
(component.qualityGate ||
(component.qualityProfiles && component.qualityProfiles.length > 0)) && (
- <ProjectInformationSection className="sw-pt-0">
+ <ProjectInformationSection className="sw-pt-0 sw-flex sw-flex-col sw-gap-4">
{component.qualityGate && <MetaQualityGate qualityGate={component.qualityGate} />}
{component.qualityProfiles && component.qualityProfiles.length > 0 && (
- <MetaQualityProfiles
- headerClassName={component.qualityGate ? 'big-spacer-top' : undefined}
- profiles={component.qualityProfiles}
- />
+ <MetaQualityProfiles profiles={component.qualityProfiles} />
)}
</ProjectInformationSection>
)}
diff --git a/server/sonar-web/src/main/js/apps/projectInformation/about/components/MetaDescription.tsx b/server/sonar-web/src/main/js/apps/projectInformation/about/components/MetaDescription.tsx
index 158ebb65466..1aaab5d3555 100644
--- a/server/sonar-web/src/main/js/apps/projectInformation/about/components/MetaDescription.tsx
+++ b/server/sonar-web/src/main/js/apps/projectInformation/about/components/MetaDescription.tsx
@@ -17,7 +17,7 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import { TextMuted } from 'design-system';
+import { SubHeading, TextMuted } from 'design-system';
import * as React from 'react';
import { translate } from '../../../../helpers/l10n';
@@ -29,7 +29,7 @@ interface Props {
export default function MetaDescription({ description, isApp }: Props) {
return (
<>
- <h3>{translate('project.info.description')}</h3>
+ <SubHeading>{translate('project.info.description')}</SubHeading>
<TextMuted
className="it__project-description"
text={description ?? translate(isApp ? 'application' : 'project', 'info.empty_description')}
diff --git a/server/sonar-web/src/main/js/apps/projectInformation/about/components/MetaKey.tsx b/server/sonar-web/src/main/js/apps/projectInformation/about/components/MetaKey.tsx
index 8c8adcab655..1aefa601e49 100644
--- a/server/sonar-web/src/main/js/apps/projectInformation/about/components/MetaKey.tsx
+++ b/server/sonar-web/src/main/js/apps/projectInformation/about/components/MetaKey.tsx
@@ -17,7 +17,7 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import { ClipboardIconButton, CodeSnippet, HelperHintIcon } from 'design-system';
+import { ClipboardIconButton, CodeSnippet, HelperHintIcon, SubHeading } from 'design-system';
import * as React from 'react';
import HelpTooltip from '../../../../components/controls/HelpTooltip';
import { translate } from '../../../../helpers/l10n';
@@ -30,8 +30,8 @@ interface MetaKeyProps {
export default function MetaKey({ componentKey, qualifier }: MetaKeyProps) {
return (
<>
- <div className="sw-flex sw-items-center">
- <h3>{translate('overview.project_key', qualifier)}</h3>
+ <div className="sw-flex sw-items-baseline">
+ <SubHeading>{translate('overview.project_key', qualifier)}</SubHeading>
<HelpTooltip
className="sw-ml-1"
overlay={
diff --git a/server/sonar-web/src/main/js/apps/projectInformation/about/components/MetaLinks.tsx b/server/sonar-web/src/main/js/apps/projectInformation/about/components/MetaLinks.tsx
index 8891c5311cc..bc1e8936ad4 100644
--- a/server/sonar-web/src/main/js/apps/projectInformation/about/components/MetaLinks.tsx
+++ b/server/sonar-web/src/main/js/apps/projectInformation/about/components/MetaLinks.tsx
@@ -17,6 +17,7 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+import { SubHeading } from 'design-system/lib';
import React from 'react';
import MetaLink from '../../../../components/common/MetaLink';
import { translate } from '../../../../helpers/l10n';
@@ -32,8 +33,8 @@ export default function MetaLinks({ links }: Readonly<Props>) {
return (
<>
- <h3 id="external-links">{translate('overview.external_links')}</h3>
- <ul className="project-info-list" aria-labelledby="external-links">
+ <SubHeading id="external-links">{translate('overview.external_links')}</SubHeading>
+ <ul className="sw-flex sw-flex-col sw-gap-2" aria-labelledby="external-links">
{orderedLinks.map((link) => (
<MetaLink key={link.id} link={link} />
))}
diff --git a/server/sonar-web/src/main/js/apps/projectInformation/about/components/MetaQualityGate.tsx b/server/sonar-web/src/main/js/apps/projectInformation/about/components/MetaQualityGate.tsx
index 94b77af304b..ada3ea85a58 100644
--- a/server/sonar-web/src/main/js/apps/projectInformation/about/components/MetaQualityGate.tsx
+++ b/server/sonar-web/src/main/js/apps/projectInformation/about/components/MetaQualityGate.tsx
@@ -17,7 +17,7 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import { Link } from 'design-system';
+import { Link, Note, SubHeading } from 'design-system';
import * as React from 'react';
import { translate } from '../../../../helpers/l10n';
import { getQualityGateUrl } from '../../../../helpers/urls';
@@ -28,17 +28,15 @@ interface Props {
export default function MetaQualityGate({ qualityGate }: Props) {
return (
- <>
- <h3 id="quality-gate-header">{translate('project.info.quality_gate')}</h3>
+ <div>
+ <SubHeading id="quality-gate-header">{translate('project.info.quality_gate')}</SubHeading>
- <ul className="project-info-list" aria-labelledby="quality-gate-header">
+ <ul className="sw-flex sw-flex-col sw-gap-2" aria-labelledby="quality-gate-header">
<li>
- {qualityGate.isDefault && (
- <span className="note spacer-right">({translate('default')})</span>
- )}
+ {qualityGate.isDefault && <Note className="sw-mr-2">({translate('default')})</Note>}
<Link to={getQualityGateUrl(qualityGate.name)}>{qualityGate.name}</Link>
</li>
</ul>
- </>
+ </div>
);
}
diff --git a/server/sonar-web/src/main/js/apps/projectInformation/about/components/MetaQualityProfiles.tsx b/server/sonar-web/src/main/js/apps/projectInformation/about/components/MetaQualityProfiles.tsx
index 6f2fbb1e7b9..8153c194e7e 100644
--- a/server/sonar-web/src/main/js/apps/projectInformation/about/components/MetaQualityProfiles.tsx
+++ b/server/sonar-web/src/main/js/apps/projectInformation/about/components/MetaQualityProfiles.tsx
@@ -17,7 +17,7 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import { Badge, Link } from 'design-system';
+import { Badge, Link, SubHeading } from 'design-system';
import React, { useContext, useEffect } from 'react';
import { searchRules } from '../../../../api/rules';
import { LanguagesContext } from '../../../../app/components/languages/LanguagesContext';
@@ -28,11 +28,10 @@ import { Languages } from '../../../../types/languages';
import { ComponentQualityProfile, Dict } from '../../../../types/types';
interface Props {
- headerClassName?: string;
profiles: ComponentQualityProfile[];
}
-export function MetaQualityProfiles({ headerClassName, profiles }: Props) {
+export function MetaQualityProfiles({ profiles }: Readonly<Props>) {
const [deprecatedByKey, setDeprecatedByKey] = React.useState<Dict<number>>({});
const languages = useContext(LanguagesContext);
@@ -61,11 +60,10 @@ export function MetaQualityProfiles({ headerClassName, profiles }: Props) {
}, [profiles]);
return (
- <>
- <h3 className={headerClassName} id="quality-profiles-list">
- {translate('overview.quality_profiles')}
- </h3>
- <ul className="project-info-list" aria-labelledby="quality-profiles-list">
+ <div>
+ <SubHeading id="quality-profiles-list">{translate('overview.quality_profiles')}</SubHeading>
+
+ <ul className="sw-flex sw-flex-col sw-gap-2" aria-labelledby="quality-profiles-list">
{profiles.map((profile) => (
<ProfileItem
key={profile.key}
@@ -75,7 +73,7 @@ export function MetaQualityProfiles({ headerClassName, profiles }: Props) {
/>
))}
</ul>
- </>
+ </div>
);
}
diff --git a/server/sonar-web/src/main/js/apps/projectInformation/about/components/MetaSize.tsx b/server/sonar-web/src/main/js/apps/projectInformation/about/components/MetaSize.tsx
index d3e361ba54d..7593c2a9b32 100644
--- a/server/sonar-web/src/main/js/apps/projectInformation/about/components/MetaSize.tsx
+++ b/server/sonar-web/src/main/js/apps/projectInformation/about/components/MetaSize.tsx
@@ -17,13 +17,13 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import { DrilldownLink, SizeIndicator } from 'design-system';
+import { DrilldownLink, Note, SizeIndicator, SubHeading } from 'design-system';
import * as React from 'react';
import { translate, translateWithParameters } from '../../../../helpers/l10n';
import { formatMeasure, localizeMetric } from '../../../../helpers/measures';
import { getComponentDrilldownUrl } from '../../../../helpers/urls';
import { ComponentQualifier } from '../../../../types/component';
-import { MetricKey } from '../../../../types/metrics';
+import { MetricKey, MetricType } from '../../../../types/metrics';
import { Component, Measure } from '../../../../types/types';
interface MetaSizeProps {
@@ -45,25 +45,25 @@ export default function MetaSize({ component, measures }: MetaSizeProps) {
return (
<>
- <div className="sw-flex sw-items-center">
- <h3>{localizeMetric(MetricKey.ncloc)}</h3>
- <span className="sw-ml-1 small">({translate('project.info.main_branch')})</span>
+ <div className="sw-flex sw-items-baseline">
+ <SubHeading>{localizeMetric(MetricKey.ncloc)}</SubHeading>
+ <span className="sw-ml-1">({translate('project.info.main_branch')})</span>
</div>
<div className="sw-flex sw-items-center">
{ncloc && ncloc.value ? (
<>
- <DrilldownLink className="huge" to={url}>
+ <DrilldownLink to={url}>
<span
aria-label={translateWithParameters(
'project.info.see_more_info_on_x_locs',
ncloc.value,
)}
>
- {formatMeasure(ncloc.value, 'SHORT_INT')}
+ {formatMeasure(ncloc.value, MetricType.ShortInteger)}
</span>
</DrilldownLink>
- <span className="spacer-left">
+ <span className="sw-ml-2">
<SizeIndicator value={Number(ncloc.value)} size="xs" />
</span>
</>
@@ -72,17 +72,15 @@ export default function MetaSize({ component, measures }: MetaSizeProps) {
)}
{isApp && (
- <span className="huge-spacer-left display-inline-flex-center">
+ <span className="sw-inline-flex sw-items-center sw-ml-10">
{projects ? (
<DrilldownLink to={url}>
- <span className="big">{formatMeasure(projects.value, 'SHORT_INT')}</span>
+ <span>{formatMeasure(projects.value, MetricType.ShortInteger)}</span>
</DrilldownLink>
) : (
- <span className="big">0</span>
+ <span>0</span>
)}
- <span className="little-spacer-left text-muted">
- {translate('metric.projects.name')}
- </span>
+ <Note className="sw-ml-1">{translate('metric.projects.name')}</Note>
</span>
)}
</div>
diff --git a/server/sonar-web/src/main/js/apps/projectInformation/about/components/MetaTags.tsx b/server/sonar-web/src/main/js/apps/projectInformation/about/components/MetaTags.tsx
index 3f483f7b257..7d43bf16fd1 100644
--- a/server/sonar-web/src/main/js/apps/projectInformation/about/components/MetaTags.tsx
+++ b/server/sonar-web/src/main/js/apps/projectInformation/about/components/MetaTags.tsx
@@ -17,7 +17,7 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import { MultiSelector, Tags } from 'design-system';
+import { MultiSelector, SubHeading, Tags } from 'design-system';
import { difference, without } from 'lodash';
import React, { useState } from 'react';
import { searchProjectTags, setApplicationTags, setProjectTags } from '../../../../api/components';
@@ -67,7 +67,7 @@ export default function MetaTags(props: Props) {
return (
<>
- <h3>{translate('tags')}</h3>
+ <SubHeading>{translate('tags')}</SubHeading>
<Tags
allowUpdate={canUpdateTags()}
ariaTagsListLabel={translate('tags')}
diff --git a/server/sonar-web/src/main/js/apps/projectInformation/about/components/MetaVisibility.tsx b/server/sonar-web/src/main/js/apps/projectInformation/about/components/MetaVisibility.tsx
index 28951a9cbc3..8237ef6ce20 100644
--- a/server/sonar-web/src/main/js/apps/projectInformation/about/components/MetaVisibility.tsx
+++ b/server/sonar-web/src/main/js/apps/projectInformation/about/components/MetaVisibility.tsx
@@ -17,6 +17,7 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+import { SubHeading } from 'design-system';
import * as React from 'react';
import PrivacyBadgeContainer from '../../../../components/common/PrivacyBadgeContainer';
import { translate } from '../../../../helpers/l10n';
@@ -30,7 +31,7 @@ interface Props {
export default function MetaVisibility({ qualifier, visibility }: Props) {
return (
<>
- <h3>{translate('visibility')}</h3>
+ <SubHeading>{translate('visibility')}</SubHeading>
<PrivacyBadgeContainer qualifier={qualifier} visibility={visibility} />
</>
);
diff --git a/server/sonar-web/src/main/js/apps/projectInformation/badges/ProjectBadges.tsx b/server/sonar-web/src/main/js/apps/projectInformation/badges/ProjectBadges.tsx
index e445738fe8d..90690bf1347 100644
--- a/server/sonar-web/src/main/js/apps/projectInformation/badges/ProjectBadges.tsx
+++ b/server/sonar-web/src/main/js/apps/projectInformation/badges/ProjectBadges.tsx
@@ -94,7 +94,7 @@ export default function ProjectBadges(props: ProjectBadgesProps) {
return (
<div>
<SubTitle>{translate('overview.badges.get_badge')}</SubTitle>
- <p className="big-spacer-bottom">{translate('overview.badges.description', qualifier)}</p>
+ <p className="sw-mb-4">{translate('overview.badges.description', qualifier)}</p>
<Spinner loading={isLoading || isEmpty(token)}>
<div className="sw-flex sw-space-x-4 sw-mb-4">
diff --git a/server/sonar-web/src/main/js/apps/projectInformation/projectRegulatoryReport/RegulatoryReport.tsx b/server/sonar-web/src/main/js/apps/projectInformation/projectRegulatoryReport/RegulatoryReport.tsx
index 52140babaff..285e4231f5d 100644
--- a/server/sonar-web/src/main/js/apps/projectInformation/projectRegulatoryReport/RegulatoryReport.tsx
+++ b/server/sonar-web/src/main/js/apps/projectInformation/projectRegulatoryReport/RegulatoryReport.tsx
@@ -31,14 +31,14 @@ import { useState } from 'react';
import { FormattedMessage } from 'react-intl';
import { getBranches } from '../../../api/branches';
import { getRegulatoryReportUrl } from '../../../api/regulatory-report';
-import DocLink from '../../../components/common/DocLink';
-import { LabelValueSelectOption } from '../../../components/controls/Select';
+import DocumentationLink from '../../../components/common/DocumentationLink';
import {
getBranchLikeDisplayName,
getBranchLikeKey,
isMainBranch,
} from '../../../helpers/branch-like';
import { translate } from '../../../helpers/l10n';
+import { LabelValueSelectOption } from '../../../helpers/search';
import { BranchLike } from '../../../types/branch-like';
import { Component } from '../../../types/types';
@@ -143,9 +143,9 @@ export default function RegulatoryReport({ component, branchLike }: Props) {
defaultMessage={translate('regulatory_page.available_branches_info.more_info')}
values={{
doc_link: (
- <DocLink to="/analyzing-source-code/branches/branch-analysis/#inactive-branches">
+ <DocumentationLink to="/analyzing-source-code/branches/branch-analysis/#inactive-branches">
{translate('regulatory_page.available_branches_info.more_info.doc_link')}
- </DocLink>
+ </DocumentationLink>
),
}}
/>
diff --git a/server/sonar-web/src/main/js/apps/projectNewCode/components/BranchNewCodeDefinitionSettingModal.tsx b/server/sonar-web/src/main/js/apps/projectNewCode/components/BranchNewCodeDefinitionSettingModal.tsx
index 4207fbf5d8d..9050575e121 100644
--- a/server/sonar-web/src/main/js/apps/projectNewCode/components/BranchNewCodeDefinitionSettingModal.tsx
+++ b/server/sonar-web/src/main/js/apps/projectNewCode/components/BranchNewCodeDefinitionSettingModal.tsx
@@ -216,8 +216,8 @@ export default class BranchNewCodeDefinitionSettingModal extends React.PureCompo
return (
<Modal
- isLarge
headerTitle={header}
+ isLarge
onClose={this.requestClose}
body={formBody}
primaryButton={
diff --git a/server/sonar-web/src/main/js/apps/projectQualityGate/ProjectQualityGateAppRenderer.tsx b/server/sonar-web/src/main/js/apps/projectQualityGate/ProjectQualityGateAppRenderer.tsx
index 06f1922b0ec..fba51f7f6db 100644
--- a/server/sonar-web/src/main/js/apps/projectQualityGate/ProjectQualityGateAppRenderer.tsx
+++ b/server/sonar-web/src/main/js/apps/projectQualityGate/ProjectQualityGateAppRenderer.tsx
@@ -38,10 +38,10 @@ import { OptionProps, components } from 'react-select';
import A11ySkipTarget from '../../components/a11y/A11ySkipTarget';
import DisableableSelectOption from '../../components/common/DisableableSelectOption';
import HelpTooltip from '../../components/controls/HelpTooltip';
-import { LabelValueSelectOption } from '../../components/controls/Select';
import Suggestions from '../../components/embed-docs-modal/Suggestions';
import { translate } from '../../helpers/l10n';
import { isDiffMetric } from '../../helpers/measures';
+import { LabelValueSelectOption } from '../../helpers/search';
import { getQualityGateUrl } from '../../helpers/urls';
import { QualityGate } from '../../types/types';
import BuiltInQualityGateBadge from '../quality-gates/components/BuiltInQualityGateBadge';
@@ -230,7 +230,7 @@ export default function ProjectQualityGateAppRenderer(props: ProjectQualityGateA
</FlagMessage>
)}
{needsReanalysis && (
- <FlagMessage className="big-spacer-top abs-width-600" variant="warning">
+ <FlagMessage className="sw-mt-4 sw-w-abs-600" variant="warning">
{translate('project_quality_gate.requires_new_analysis')}
</FlagMessage>
)}
diff --git a/server/sonar-web/src/main/js/apps/projectQualityProfiles/components/AddLanguageModal.tsx b/server/sonar-web/src/main/js/apps/projectQualityProfiles/components/AddLanguageModal.tsx
index 1ad3e8827fe..8f21bfd7f11 100644
--- a/server/sonar-web/src/main/js/apps/projectQualityProfiles/components/AddLanguageModal.tsx
+++ b/server/sonar-web/src/main/js/apps/projectQualityProfiles/components/AddLanguageModal.tsx
@@ -22,8 +22,8 @@ import { difference } from 'lodash';
import * as React from 'react';
import { Profile } from '../../../api/quality-profiles';
import withLanguagesContext from '../../../app/components/languages/withLanguagesContext';
-import { LabelValueSelectOption } from '../../../components/controls/Select';
import { translate } from '../../../helpers/l10n';
+import { LabelValueSelectOption } from '../../../helpers/search';
import { Languages } from '../../../types/languages';
import { Dict } from '../../../types/types';
import LanguageProfileSelectOption, { ProfileOption } from './LanguageProfileSelectOption';
diff --git a/server/sonar-web/src/main/js/apps/projectQualityProfiles/components/LanguageProfileSelectOption.tsx b/server/sonar-web/src/main/js/apps/projectQualityProfiles/components/LanguageProfileSelectOption.tsx
index 5cc1da17389..a73449bc232 100644
--- a/server/sonar-web/src/main/js/apps/projectQualityProfiles/components/LanguageProfileSelectOption.tsx
+++ b/server/sonar-web/src/main/js/apps/projectQualityProfiles/components/LanguageProfileSelectOption.tsx
@@ -21,8 +21,8 @@ import { Link } from 'design-system';
import * as React from 'react';
import { components, OptionProps } from 'react-select';
import DisableableSelectOption from '../../../components/common/DisableableSelectOption';
-import { LabelValueSelectOption } from '../../../components/controls/Select';
import { translate } from '../../../helpers/l10n';
+import { LabelValueSelectOption } from '../../../helpers/search';
import { getQualityProfileUrl } from '../../../helpers/urls';
export interface ProfileOption extends LabelValueSelectOption {
diff --git a/server/sonar-web/src/main/js/apps/projects/components/ProjectsList.tsx b/server/sonar-web/src/main/js/apps/projects/components/ProjectsList.tsx
index 35ba75d85cf..918592fceeb 100644
--- a/server/sonar-web/src/main/js/apps/projects/components/ProjectsList.tsx
+++ b/server/sonar-web/src/main/js/apps/projects/components/ProjectsList.tsx
@@ -71,7 +71,6 @@ export default class ProjectsList extends React.PureComponent<Props> {
loadMore={this.props.loadMore}
loading={loading}
ready={!loading}
- useMIUIButtons
total={total ?? 0}
/>
</div>
diff --git a/server/sonar-web/src/main/js/apps/projects/components/project-card/ProjectCard.tsx b/server/sonar-web/src/main/js/apps/projects/components/project-card/ProjectCard.tsx
index cf212c946d0..930793f377b 100644
--- a/server/sonar-web/src/main/js/apps/projects/components/project-card/ProjectCard.tsx
+++ b/server/sonar-web/src/main/js/apps/projects/components/project-card/ProjectCard.tsx
@@ -81,9 +81,9 @@ function renderFirstLine(
/>
)}
- <h1 className="it__project-card-name" title={name}>
+ <span className="it__project-card-name" title={name}>
<StandoutLink to={getProjectUrl(key)}>{name}</StandoutLink>
- </h1>
+ </span>
{qualifier === ComponentQualifier.Application && (
<Tooltip
diff --git a/server/sonar-web/src/main/js/apps/projects/styles.css b/server/sonar-web/src/main/js/apps/projects/styles.css
index 2fb03a022b1..b5dbdb5aded 100644
--- a/server/sonar-web/src/main/js/apps/projects/styles.css
+++ b/server/sonar-web/src/main/js/apps/projects/styles.css
@@ -27,10 +27,6 @@
padding-left: 24px;
}
-.projects-topbar-item .spinner {
- top: -1px;
-}
-
.projects-topbar-item.is-last {
margin-left: auto;
padding-left: 32px;
diff --git a/server/sonar-web/src/main/js/apps/projectsManagement/ProjectManagementApp.tsx b/server/sonar-web/src/main/js/apps/projectsManagement/ProjectManagementApp.tsx
index e75ef5ff0a2..94e0c495c6b 100644
--- a/server/sonar-web/src/main/js/apps/projectsManagement/ProjectManagementApp.tsx
+++ b/server/sonar-web/src/main/js/apps/projectsManagement/ProjectManagementApp.tsx
@@ -248,7 +248,6 @@ class ProjectManagementApp extends React.PureComponent<Props, State> {
loadMore={this.loadMore}
ready={this.state.ready}
total={this.state.total}
- useMIUIButtons
/>
</PageContentFontWrapper>
</LargeCenteredLayout>
diff --git a/server/sonar-web/src/main/js/apps/projectsManagement/Search.tsx b/server/sonar-web/src/main/js/apps/projectsManagement/Search.tsx
index 7648facfb78..e6cf3d19f8f 100644
--- a/server/sonar-web/src/main/js/apps/projectsManagement/Search.tsx
+++ b/server/sonar-web/src/main/js/apps/projectsManagement/Search.tsx
@@ -34,8 +34,8 @@ import { OptionProps, SingleValueProps, components } from 'react-select';
import { Project } from '../../api/project-management';
import withAppStateContext from '../../app/components/app-state/withAppStateContext';
import HelpTooltip from '../../components/controls/HelpTooltip';
-import { LabelValueSelectOption } from '../../components/controls/Select';
import { translate } from '../../helpers/l10n';
+import { LabelValueSelectOption } from '../../helpers/search';
import { AppState } from '../../types/appstate';
import { Visibility } from '../../types/component';
import BulkApplyTemplateModal from './BulkApplyTemplateModal';
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/CaycBadgeTooltip.tsx b/server/sonar-web/src/main/js/apps/quality-gates/components/CaycBadgeTooltip.tsx
index 94ce309dc64..f6ad7515ffd 100644
--- a/server/sonar-web/src/main/js/apps/quality-gates/components/CaycBadgeTooltip.tsx
+++ b/server/sonar-web/src/main/js/apps/quality-gates/components/CaycBadgeTooltip.tsx
@@ -18,7 +18,7 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
-import DocLink from '../../../components/common/DocLink';
+import DocumentationLink from '../../../components/common/DocumentationLink';
import { translate } from '../../../helpers/l10n';
export default function CaycBadgeTooltip() {
@@ -27,9 +27,9 @@ export default function CaycBadgeTooltip() {
<p className="spacer-bottom padded-bottom bordered-bottom-cayc">
{translate('quality_gates.cayc.tooltip.message')}
</p>
- <DocLink to="/user-guide/clean-as-you-code/">
+ <DocumentationLink to="/user-guide/clean-as-you-code/">
{translate('quality_gates.cayc.badge.tooltip.learn_more')}
- </DocLink>
+ </DocumentationLink>
</div>
);
}
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/Conditions.tsx b/server/sonar-web/src/main/js/apps/quality-gates/components/Conditions.tsx
index aeba605928c..dbac85e2521 100644
--- a/server/sonar-web/src/main/js/apps/quality-gates/components/Conditions.tsx
+++ b/server/sonar-web/src/main/js/apps/quality-gates/components/Conditions.tsx
@@ -172,7 +172,7 @@ export default function Conditions({ qualityGate, isFetching }: Readonly<Props>)
<HelperHintIcon />
</DocumentationTooltip>
)}
- <Spinner loading={isFetching} className="it__spinner sw-ml-4 sw-mt-1" />
+ <Spinner loading={isFetching} className="sw-ml-4 sw-mt-1" />
</div>
<div>
{(qualityGate.caycStatus === CaycStatus.NonCompliant || editing) && canEdit && (
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/ThresholdInput.tsx b/server/sonar-web/src/main/js/apps/quality-gates/components/ThresholdInput.tsx
index dea0d0bde68..b832428a495 100644
--- a/server/sonar-web/src/main/js/apps/quality-gates/components/ThresholdInput.tsx
+++ b/server/sonar-web/src/main/js/apps/quality-gates/components/ThresholdInput.tsx
@@ -19,7 +19,7 @@
*/
import { InputField, InputSelect } from 'design-system';
import * as React from 'react';
-import { LabelValueSelectOption } from '../../../components/controls/Select';
+import { LabelValueSelectOption } from '../../../helpers/search';
import { Metric } from '../../../types/types';
interface Props {
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileProjects.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileProjects.tsx
index 8bae6f32ed2..08309dd5457 100644
--- a/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileProjects.tsx
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileProjects.tsx
@@ -169,7 +169,6 @@ export default class ProfileProjects extends React.PureComponent<Props, State> {
</Table>
{projects.length > 0 && (
<ListFooter
- useMIUIButtons
count={projects.length}
loadMore={this.loadMore}
loading={this.state.loadingMore}
diff --git a/server/sonar-web/src/main/js/apps/security-hotspots/components/EmptyHotspotsPage.tsx b/server/sonar-web/src/main/js/apps/security-hotspots/components/EmptyHotspotsPage.tsx
index f9c5dce61cc..6e21825714a 100644
--- a/server/sonar-web/src/main/js/apps/security-hotspots/components/EmptyHotspotsPage.tsx
+++ b/server/sonar-web/src/main/js/apps/security-hotspots/components/EmptyHotspotsPage.tsx
@@ -19,7 +19,7 @@
*/
import { Note } from 'design-system';
import * as React from 'react';
-import DocLink from '../../../components/common/DocLink';
+import DocumentationLink from '../../../components/common/DocumentationLink';
import { translate } from '../../../helpers/l10n';
import { getBaseUrl } from '../../../helpers/system';
@@ -60,9 +60,9 @@ export default function EmptyHotspotsPage(props: EmptyHotspotsPageProps) {
{translate(`hotspots.${translationRoot}.description`)}
</Note>
{!(filtered || isStaticListOfHotspots) && (
- <DocLink className="big-spacer-top" to="/user-guide/security-hotspots/">
+ <DocumentationLink className="sw-mt-4" to="/user-guide/security-hotspots/">
{translate('hotspots.learn_more')}
- </DocLink>
+ </DocumentationLink>
)}
</div>
);
diff --git a/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotDisabledFilterTooltip.tsx b/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotDisabledFilterTooltip.tsx
index 6470813cbaf..48accb664f1 100644
--- a/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotDisabledFilterTooltip.tsx
+++ b/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotDisabledFilterTooltip.tsx
@@ -18,7 +18,7 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
-import DocLink from '../../../components/common/DocLink';
+import DocumentationLink from '../../../components/common/DocumentationLink';
import { translate } from '../../../helpers/l10n';
export function HotspotDisabledFilterTooltip() {
@@ -30,7 +30,7 @@ export function HotspotDisabledFilterTooltip() {
</p>
<hr className="sw-mx-0 sw-my-3 sw-p-0 sw-w-full" />
<span className="sw-body-sm-highlight">{translate('indexation.learn_more')}</span>
- <DocLink
+ <DocumentationLink
className="sw-ml-1"
onMouseDown={(e) => {
// This tooltip content is rendered in the context of a <Dropdown>, and <DropdownToggler>
@@ -41,7 +41,7 @@ export function HotspotDisabledFilterTooltip() {
to="/instance-administration/reindexing/"
>
{translate('indexation.reindexing')}
- </DocLink>
+ </DocumentationLink>
</div>
);
}
diff --git a/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotList.tsx b/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotList.tsx
index 4cfbdfe5f17..cbc7cc4ca8a 100644
--- a/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotList.tsx
+++ b/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotList.tsx
@@ -192,7 +192,6 @@ export default class HotspotList extends React.Component<Props, State> {
loadMore={!loadingMore ? this.props.onLoadMore : undefined}
loading={loadingMore}
total={hotspotsTotal}
- useMIUIButtons
/>
</StyledContainer>
);
diff --git a/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotReviewHistory.tsx b/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotReviewHistory.tsx
index 4f5e97340cd..45f70529053 100644
--- a/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotReviewHistory.tsx
+++ b/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotReviewHistory.tsx
@@ -32,7 +32,7 @@ import {
import * as React from 'react';
import DateTimeFormatter from '../../../components/intl/DateTimeFormatter';
import IssueChangelogDiff from '../../../components/issue/components/IssueChangelogDiff';
-import LegacyAvatar from '../../../components/ui/LegacyAvatar';
+import Avatar from '../../../components/ui/Avatar';
import { translate, translateWithParameters } from '../../../helpers/l10n';
import { sanitizeUserInput } from '../../../helpers/sanitize';
import { Hotspot, ReviewHistoryType } from '../../../types/security-hotspots';
@@ -63,7 +63,7 @@ export default function HotspotReviewHistory(props: HotspotReviewHistoryProps) {
<LightLabel as="div" className="sw-flex sw-gap-2">
{user.name && (
<div className="sw-flex sw-items-center sw-gap-1">
- <LegacyAvatar hash={user.avatar} name={user.name} size={20} />
+ <Avatar hash={user.avatar} name={user.name} size="xs" />
<span className="sw-body-sm-highlight">
{user.active ? user.name : translateWithParameters('user.x_deleted', user.name)}
</span>
diff --git a/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotSnippetContainerRenderer.tsx b/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotSnippetContainerRenderer.tsx
index b7efcfd4c82..7ff7023ca18 100644
--- a/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotSnippetContainerRenderer.tsx
+++ b/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotSnippetContainerRenderer.tsx
@@ -145,7 +145,7 @@ export default function HotspotSnippetContainerRenderer(
)}
<SourceFileWrapper className="sw-box-border sw-w-full sw-rounded-1" ref={scrollableRef}>
- <Spinner className="big-spacer" loading={loading} />
+ <Spinner className="sw-m-4" loading={loading} />
{!loading && sourceLines.length > 0 && (
<SnippetViewer
diff --git a/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/__snapshots__/HotspotDisabledFilterTooltip-test.tsx.snap b/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/__snapshots__/HotspotDisabledFilterTooltip-test.tsx.snap
index 6e09e138b58..d6d044ecf9d 100644
--- a/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/__snapshots__/HotspotDisabledFilterTooltip-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/__snapshots__/HotspotDisabledFilterTooltip-test.tsx.snap
@@ -1,6 +1,49 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`should render correctly and stop event propagation 1`] = `
+.emotion-0 {
+ color: var(--color);
+ border-bottom: var(--border);
+ font-weight: 600;
+ text-decoration-line: none;
+ --color: rgb(93,108,208);
+ --active: rgb(75,86,187);
+ --border: 1px solid rgb(159,169,237);
+ --borderActive: 1px solid rgb(159,169,237);
+}
+
+.emotion-0:visited {
+ color: var(--color);
+}
+
+.emotion-0:hover,
+.emotion-0:focus,
+.emotion-0:active {
+ color: var(--active);
+ border-bottom: var(--borderActive);
+}
+
+.emotion-0:hover .emotion-10,
+.emotion-0:focus .emotion-10,
+.emotion-0:active .emotion-10 {
+ color: rgb(123,135,217);
+}
+
+.emotion-0>svg {
+ vertical-align: text-bottom!important;
+}
+
+.e1vbniy50 .emotion-0 {
+ --color: rgb(189,198,255);
+ --active: rgb(209,215,254);
+ --border: 1px solid rgb(159,169,237);
+ --borderActive: 1px solid rgb(159,169,237);
+}
+
+.emotion-2 {
+ color: rgb(159,169,237);
+}
+
<div>
<div
class="sw-body-sm sw-w-[190px]"
@@ -19,30 +62,27 @@ exports[`should render correctly and stop event propagation 1`] = `
indexation.learn_more
</span>
<a
- class="sw-ml-1"
+ class="sw-ml-1 emotion-0 emotion-1"
href="https://docs.sonarsource.com/sonarqube/latest/instance-administration/reindexing/"
rel="noopener noreferrer"
target="_blank"
>
+ indexation.reindexing
<svg
- class="little-spacer-right"
- height="14"
- style="fill-rule: evenodd; clip-rule: evenodd; stroke-linejoin: round; stroke-miterlimit: 1.41421;"
- version="1.1"
+ aria-hidden="true"
+ class="sw-ml-1 emotion-2 emotion-10"
+ fill="currentColor"
+ focusable="false"
+ height="16"
+ role="img"
+ style="display: inline-block; user-select: none; vertical-align: middle; overflow: visible;"
viewBox="0 0 16 16"
- width="14"
- xml:space="preserve"
- xmlns:xlink="http://www.w3.org/1999/xlink"
+ width="16"
>
- <title>
- opens_in_new_window
- </title>
<path
- d="M12 9.25v2.5A2.25 2.25 0 0 1 9.75 14h-6.5A2.25 2.25 0 0 1 1 11.75v-6.5A2.25 2.25 0 0 1 3.25 3h5.5c.14 0 .25.11.25.25v.5c0 .14-.11.25-.25.25h-5.5C2.562 4 2 4.563 2 5.25v6.5c0 .688.563 1.25 1.25 1.25h6.5c.688 0 1.25-.563 1.25-1.25v-2.5c0-.14.11-.25.25-.25h.5c.14 0 .25.11.25.25zm3-6.75v4c0 .273-.227.5-.5.5a.497.497 0 0 1-.352-.148l-1.375-1.375L7.68 10.57a.27.27 0 0 1-.18.078.27.27 0 0 1-.18-.078l-.89-.89a.27.27 0 0 1-.078-.18.27.27 0 0 1 .078-.18l5.093-5.093-1.375-1.375A.497.497 0 0 1 10 2.5c0-.273.227-.5.5-.5h4c.273 0 .5.227.5.5z"
- style="fill: currentColor;"
+ d="M3.75 2h3.5a.75.75 0 0 1 0 1.5h-3.5a.25.25 0 0 0-.25.25v8.5c0 .138.112.25.25.25h8.5a.25.25 0 0 0 .25-.25v-3.5a.75.75 0 0 1 1.5 0v3.5A1.75 1.75 0 0 1 12.25 14h-8.5A1.75 1.75 0 0 1 2 12.25v-8.5C2 2.784 2.784 2 3.75 2Zm6.854-1h4.146a.25.25 0 0 1 .25.25v4.146a.25.25 0 0 1-.427.177L13.03 4.03 9.28 7.78a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042l3.75-3.75-1.543-1.543A.25.25 0 0 1 10.604 1Z"
/>
</svg>
- indexation.reindexing
</a>
</div>
</div>
diff --git a/server/sonar-web/src/main/js/apps/settings/components/NewCodeDefinition.tsx b/server/sonar-web/src/main/js/apps/settings/components/NewCodeDefinition.tsx
index 64e9416c45e..8191401ce61 100644
--- a/server/sonar-web/src/main/js/apps/settings/components/NewCodeDefinition.tsx
+++ b/server/sonar-web/src/main/js/apps/settings/components/NewCodeDefinition.tsx
@@ -21,7 +21,7 @@ import classNames from 'classnames';
import { Spinner } from 'design-system';
import React, { useCallback, useEffect } from 'react';
import { FormattedMessage } from 'react-intl';
-import DocLink from '../../../components/common/DocLink';
+import DocumentationLink from '../../../components/common/DocumentationLink';
import { ResetButtonLink, SubmitButton } from '../../../components/controls/buttons';
import NewCodeDefinitionDaysOption from '../../../components/new-code-definition/NewCodeDefinitionDaysOption';
import NewCodeDefinitionPreviousVersionOption from '../../../components/new-code-definition/NewCodeDefinitionPreviousVersionOption';
@@ -102,9 +102,9 @@ export default function NewCodeDefinition() {
id="settings.new_code_period.description3"
values={{
link: (
- <DocLink to="/project-administration/defining-new-code/">
+ <DocumentationLink to="/project-administration/defining-new-code/">
{translate('settings.new_code_period.description3.link')}
- </DocLink>
+ </DocumentationLink>
),
}}
/>
@@ -146,8 +146,12 @@ export default function NewCodeDefinition() {
}
settingLevel={NewCodeDefinitionLevels.Global}
/>
- <div className="big-spacer-top">
- <p className={classNames('spacer-bottom', { invisible: !isFormTouched })}>
+ <div className="sw-mt-4">
+ <p
+ className={classNames('spacer-bottom', {
+ 'sw-invisible': !isFormTouched,
+ })}
+ >
{translate('baseline.next_analysis_notice')}
</p>
<Spinner className="spacer-right" loading={isSaving} />
diff --git a/server/sonar-web/src/main/js/apps/settings/components/__tests__/NewCodeDefinition-it.tsx b/server/sonar-web/src/main/js/apps/settings/components/__tests__/NewCodeDefinition-it.tsx
index 48cb1b2c460..57910c31867 100644
--- a/server/sonar-web/src/main/js/apps/settings/components/__tests__/NewCodeDefinition-it.tsx
+++ b/server/sonar-web/src/main/js/apps/settings/components/__tests__/NewCodeDefinition-it.tsx
@@ -23,7 +23,7 @@ import { MessageTypes } from '../../../../api/messages';
import MessagesServiceMock from '../../../../api/mocks/MessagesServiceMock';
import NewCodeDefinitionServiceMock from '../../../../api/mocks/NewCodeDefinitionServiceMock';
import { renderComponent } from '../../../../helpers/testReactTestingUtils';
-import { byLabelText, byRole, byText } from '../../../../helpers/testSelector';
+import { byRole, byText } from '../../../../helpers/testSelector';
import { NewCodeDefinitionType } from '../../../../types/new-code-definition';
import NewCodeDefinition from '../NewCodeDefinition';
@@ -50,7 +50,7 @@ const ui = {
saveButton: byRole('button', { name: 'save' }),
cancelButton: byRole('button', { name: 'cancel' }),
ncdAutoUpdateMessage: byText(/new_code_definition.auto_update.ncd_page.message/),
- ncdAutoUpdateMessageDismiss: byLabelText('alert.dismiss'),
+ ncdAutoUpdateMessageDismiss: byRole('button', { name: 'dismiss' }),
};
it('renders and behaves as expected', async () => {
diff --git a/server/sonar-web/src/main/js/apps/settings/components/authentication/Authentication.tsx b/server/sonar-web/src/main/js/apps/settings/components/authentication/Authentication.tsx
index 1363b3287a3..0d0f10abceb 100644
--- a/server/sonar-web/src/main/js/apps/settings/components/authentication/Authentication.tsx
+++ b/server/sonar-web/src/main/js/apps/settings/components/authentication/Authentication.tsx
@@ -18,14 +18,13 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import classNames from 'classnames';
-import { FlagMessage, Link, SubTitle, ToggleButton } from 'design-system';
+import { FlagMessage, Link, SubTitle, ToggleButton, getTabId, getTabPanelId } from 'design-system';
import * as React from 'react';
import { FormattedMessage } from 'react-intl';
import { useSearchParams } from 'react-router-dom';
import withAvailableFeatures, {
WithAvailableFeaturesProps,
} from '../../../../app/components/available-features/withAvailableFeatures';
-import { getTabId, getTabPanelId } from '../../../../components/controls/BoxedTabs';
import { translate } from '../../../../helpers/l10n';
import { getBaseUrl } from '../../../../helpers/system';
import { searchParamsToQuery } from '../../../../helpers/urls';
@@ -152,7 +151,7 @@ export function Authentication(props: Props & WithAvailableFeaturesProps) {
{tabs.map((tab) => (
<div
className={classNames('sw-overflow-y-auto', {
- hidden: currentTab !== tab.value,
+ 'sw-hidden': currentTab !== tab.value,
})}
key={tab.value}
role="tabpanel"
diff --git a/server/sonar-web/src/main/js/apps/settings/components/authentication/AuthenticationFormFieldWrapper.tsx b/server/sonar-web/src/main/js/apps/settings/components/authentication/AuthenticationFormFieldWrapper.tsx
deleted file mode 100644
index 5aa58281303..00000000000
--- a/server/sonar-web/src/main/js/apps/settings/components/authentication/AuthenticationFormFieldWrapper.tsx
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2024 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 React, { PropsWithChildren } from 'react';
-import MandatoryFieldMarker from '../../../../components/ui/MandatoryFieldMarker';
-
-interface Props {
- readonly title: string;
- readonly description?: string;
- readonly defKey?: string;
- readonly mandatory?: boolean;
-}
-
-export default function AuthenticationFormFieldWrapper(props: PropsWithChildren<Props>) {
- const { mandatory = false, title, description, defKey, children } = props;
-
- return (
- <div className="settings-definition">
- <div className="settings-definition-left">
- <label className="h3" htmlFor={defKey}>
- {title}
- </label>
- {mandatory && <MandatoryFieldMarker />}
- {description && <div className="markdown small spacer-top">{description}</div>}
- </div>
- <div className="settings-definition-right big-padded-top display-flex-column">{children}</div>
- </div>
- );
-}
diff --git a/server/sonar-web/src/main/js/apps/settings/components/authentication/AutoProvisionningConsent.tsx b/server/sonar-web/src/main/js/apps/settings/components/authentication/AutoProvisionningConsent.tsx
index 14f5b85ba27..f41125fd67f 100644
--- a/server/sonar-web/src/main/js/apps/settings/components/authentication/AutoProvisionningConsent.tsx
+++ b/server/sonar-web/src/main/js/apps/settings/components/authentication/AutoProvisionningConsent.tsx
@@ -17,11 +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.
*/
+import { ButtonSecondary, Modal } from 'design-system';
+import { noop } from 'lodash';
import * as React from 'react';
import { FormattedMessage } from 'react-intl';
-import DocLink from '../../../../components/common/DocLink';
-import Modal from '../../../../components/controls/Modal';
-import { Button } from '../../../../components/controls/buttons';
+import DocumentationLink from '../../../../components/common/DocumentationLink';
import { translate } from '../../../../helpers/l10n';
import { useToggleGithubProvisioningMutation } from '../../../../queries/identity-provider/github';
import { useGetValueQuery, useResetSettingsMutation } from '../../../../queries/settings';
@@ -55,11 +55,9 @@ export default function AutoProvisioningConsent() {
}
return (
- <Modal contentLabel={header} shouldCloseOnOverlayClick={false} size="medium">
- <header className="modal-head">
- <h2>{header}</h2>
- </header>
- <div className="modal-body">
+ <Modal onClose={noop} closeOnOverlayClick={false} isLarge>
+ <Modal.Header title={header} />
+ <Modal.Body>
<FormattedMessage
tagName="p"
id="settings.authentication.github.confirm_auto_provisioning.description1"
@@ -69,9 +67,9 @@ export default function AutoProvisioningConsent() {
tagName="p"
values={{
documentation: (
- <DocLink to="/instance-administration/authentication/github/">
+ <DocumentationLink to="/instance-administration/authentication/github/">
<FormattedMessage id="documentation" />
- </DocLink>
+ </DocumentationLink>
),
}}
/>
@@ -79,15 +77,19 @@ export default function AutoProvisioningConsent() {
tagName="p"
id="settings.authentication.github.confirm_auto_provisioning.question"
/>
- </div>
- <footer className="modal-foot">
- <Button onClick={continueWithAuto}>
- {translate('settings.authentication.github.confirm_auto_provisioning.continue')}
- </Button>
- <Button onClick={switchToJIT}>
- {translate('settings.authentication.github.confirm_auto_provisioning.switch_jit')}
- </Button>
- </footer>
+ </Modal.Body>
+ <Modal.Footer
+ primaryButton={
+ <ButtonSecondary onClick={continueWithAuto}>
+ {translate('settings.authentication.github.confirm_auto_provisioning.continue')}
+ </ButtonSecondary>
+ }
+ secondaryButton={
+ <ButtonSecondary onClick={switchToJIT}>
+ {translate('settings.authentication.github.confirm_auto_provisioning.switch_jit')}
+ </ButtonSecondary>
+ }
+ />
</Modal>
);
}
diff --git a/server/sonar-web/src/main/js/apps/settings/components/inputs/InputForBoolean.tsx b/server/sonar-web/src/main/js/apps/settings/components/inputs/InputForBoolean.tsx
index 21e26e93f85..3dcc86492cd 100644
--- a/server/sonar-web/src/main/js/apps/settings/components/inputs/InputForBoolean.tsx
+++ b/server/sonar-web/src/main/js/apps/settings/components/inputs/InputForBoolean.tsx
@@ -19,7 +19,6 @@
*/
import { Note, Switch } from 'design-system';
import * as React from 'react';
-import { getToggleValue } from '../../../../components/controls/Toggle';
import { translate } from '../../../../helpers/l10n';
import { DefaultSpecializedInputProps, getPropertyName } from '../../utils';
@@ -47,3 +46,7 @@ export default function InputForBoolean({ onChange, name, value, setting }: Prop
</div>
);
}
+
+function getToggleValue(value: string | boolean) {
+ return typeof value === 'string' ? value === 'true' : value;
+}
diff --git a/server/sonar-web/src/main/js/apps/settings/encryption/GenerateSecretKeyForm.tsx b/server/sonar-web/src/main/js/apps/settings/encryption/GenerateSecretKeyForm.tsx
index ba081395b03..b120142bb20 100644
--- a/server/sonar-web/src/main/js/apps/settings/encryption/GenerateSecretKeyForm.tsx
+++ b/server/sonar-web/src/main/js/apps/settings/encryption/GenerateSecretKeyForm.tsx
@@ -22,6 +22,7 @@ import {
ClipboardIconButton,
CodeSnippet,
ListItem,
+ Spinner,
SubHeading,
UnorderedList,
} from 'design-system';
@@ -29,7 +30,6 @@ import * as React from 'react';
import { useCallback, useState } from 'react';
import { FormattedMessage } from 'react-intl';
import DocumentationLink from '../../../components/common/DocumentationLink';
-import Spinner from '../../../components/ui/Spinner';
import { translate } from '../../../helpers/l10n';
interface Props {
diff --git a/server/sonar-web/src/main/js/apps/tutorials/components/__tests__/TutorialsApp-test.tsx b/server/sonar-web/src/main/js/apps/tutorials/components/__tests__/TutorialsApp-test.tsx
index bcfd66e9ba8..d8730fc71ec 100644
--- a/server/sonar-web/src/main/js/apps/tutorials/components/__tests__/TutorialsApp-test.tsx
+++ b/server/sonar-web/src/main/js/apps/tutorials/components/__tests__/TutorialsApp-test.tsx
@@ -22,7 +22,7 @@ import UserTokensMock from '../../../../api/mocks/UserTokensMock';
import handleRequiredAuthentication from '../../../../helpers/handleRequiredAuthentication';
import { mockCurrentUser, mockLoggedInUser } from '../../../../helpers/testMocks';
import { renderAppWithComponentContext } from '../../../../helpers/testReactTestingUtils';
-import { byLabelText, byRole } from '../../../../helpers/testSelector';
+import { byRole, byText } from '../../../../helpers/testSelector';
import { Permissions } from '../../../../types/permissions';
import routes from '../../routes';
@@ -44,7 +44,7 @@ afterEach(() => {
beforeEach(jest.clearAllMocks);
const ui = {
- loading: byLabelText('loading'),
+ loading: byText('loading'),
localScanButton: byRole('heading', { name: 'onboarding.tutorial.choose_method' }),
};
diff --git a/server/sonar-web/src/main/js/apps/users/UsersApp.tsx b/server/sonar-web/src/main/js/apps/users/UsersApp.tsx
index 27ca918c6ca..13c7b35368e 100644
--- a/server/sonar-web/src/main/js/apps/users/UsersApp.tsx
+++ b/server/sonar-web/src/main/js/apps/users/UsersApp.tsx
@@ -35,10 +35,10 @@ import GitLabSynchronisationWarning from '../../app/components/GitLabSynchronisa
import HelpTooltip from '../../components/controls/HelpTooltip';
import ListFooter from '../../components/controls/ListFooter';
import { ManagedFilter } from '../../components/controls/ManagedFilter';
-import { LabelValueSelectOption } from '../../components/controls/Select';
import Suggestions from '../../components/embed-docs-modal/Suggestions';
import { now, toISO8601WithOffsetString } from '../../helpers/dates';
import { translate } from '../../helpers/l10n';
+import { LabelValueSelectOption } from '../../helpers/search';
import { useIdentityProviderQuery } from '../../queries/identity-provider/common';
import { useUsersQueries } from '../../queries/users';
import { IdentityProvider, Provider } from '../../types/types';
@@ -163,7 +163,6 @@ export default function UsersApp() {
loadMore={fetchNextPage}
ready={!isLoading}
total={data?.pages[0].page.total}
- useMIUIButtons
/>
</PageContentFontWrapper>
</LargeCenteredLayout>
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 5e08254328d..bb7ffc68318 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
@@ -105,7 +105,7 @@ export default function PasswordForm(props: Props) {
type="password"
value={oldPassword}
/>
- <input className="hidden" aria-hidden name="old-password-fake" type="password" />
+ <input className="sw-hidden" aria-hidden name="old-password-fake" type="password" />
</FormField>
)}
<FormField htmlFor="user-password" label={translate('my_profile.password.new')} required>
@@ -119,7 +119,7 @@ export default function PasswordForm(props: Props) {
value={newPassword}
size="full"
/>
- <input className="hidden" aria-hidden name="password-fake" type="password" />
+ <input className="sw-hidden" aria-hidden name="password-fake" type="password" />
</FormField>
<FormField
htmlFor="confirm-user-password"
@@ -136,7 +136,7 @@ export default function PasswordForm(props: Props) {
value={confirmPassword}
size="full"
/>
- <input className="hidden" aria-hidden name="confirm-password-fake" type="password" />
+ <input className="sw-hidden" aria-hidden name="confirm-password-fake" type="password" />
</FormField>
</form>
}
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 42b2cbaa6ae..ff745df2e81 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
@@ -33,8 +33,8 @@ import { isEmpty } from 'lodash';
import * as React from 'react';
import { getScannableProjects } from '../../../api/components';
import withCurrentUserContext from '../../../app/components/current-user/withCurrentUserContext';
-import { LabelValueSelectOption } from '../../../components/controls/Select';
import { translate } from '../../../helpers/l10n';
+import { LabelValueSelectOption } from '../../../helpers/search';
import {
EXPIRATION_OPTIONS,
computeTokenExpirationDate,
diff --git a/server/sonar-web/src/main/js/apps/users/constants.ts b/server/sonar-web/src/main/js/apps/users/constants.ts
index df9a71f5bab..374efbf68c6 100644
--- a/server/sonar-web/src/main/js/apps/users/constants.ts
+++ b/server/sonar-web/src/main/js/apps/users/constants.ts
@@ -17,8 +17,8 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import { LabelValueSelectOption } from '../../components/controls/Select';
import { translate } from '../../helpers/l10n';
+import { LabelValueSelectOption } from '../../helpers/search';
import { UserActivity } from './types';
// Nb of days without connection to SQ after which a user is considered inactive:
diff --git a/server/sonar-web/src/main/js/apps/web-api/__tests__/WebApi-it.tsx b/server/sonar-web/src/main/js/apps/web-api/__tests__/WebApi-it.tsx
index db5c836e043..18805f97356 100644
--- a/server/sonar-web/src/main/js/apps/web-api/__tests__/WebApi-it.tsx
+++ b/server/sonar-web/src/main/js/apps/web-api/__tests__/WebApi-it.tsx
@@ -88,8 +88,8 @@ function renderWebApi(navigateTo?: string) {
}
const ui = {
- domainMenuItems: byRole('menu').byRole('listitem'),
- domainMenuItemLink: (name: string) => byRole('menu').byRole('link', { name }),
+ domainMenuItems: byRole('navigation').byRole('link'),
+ domainMenuItemLink: (name: string) => byRole('navigation').byRole('link', { name }),
domainHeader: (name: string) => byRole('heading', { level: 2, name }),
sidebarHeader: byRole('heading', { name: 'api_documentation.page' }),
searchInput: byLabelText('api_documentation.search'),
diff --git a/server/sonar-web/src/main/js/apps/web-api/components/Action.tsx b/server/sonar-web/src/main/js/apps/web-api/components/Action.tsx
index 6ddbae01168..017c0096f98 100644
--- a/server/sonar-web/src/main/js/apps/web-api/components/Action.tsx
+++ b/server/sonar-web/src/main/js/apps/web-api/components/Action.tsx
@@ -17,10 +17,8 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import classNames from 'classnames';
+import { Badge, Card, LinkBox, LinkIcon, SubHeading, Tabs } from 'design-system';
import * as React from 'react';
-import Link from '../../../components/common/Link';
-import LinkIcon from '../../../components/icons/LinkIcon';
import { translate, translateWithParameters } from '../../../helpers/l10n';
import { queryToSearch } from '../../../helpers/urls';
import { WebApi } from '../../../types/types';
@@ -38,168 +36,88 @@ interface Props {
showInternal: boolean;
}
-interface State {
- showChangelog: boolean;
- showParams: boolean;
- showResponse: boolean;
+enum TabOption {
+ PARAMS = 'parameters',
+ RESPONSE = 'response_example',
+ CHANGELOG = 'changelog',
}
-export default class Action extends React.PureComponent<Props, State> {
- state: State = {
- showChangelog: false,
- showParams: false,
- showResponse: false,
- };
-
- handleShowParamsClick = (e: React.SyntheticEvent<HTMLElement>) => {
- e.preventDefault();
- this.setState((state) => ({
- showChangelog: false,
- showResponse: false,
- showParams: !state.showParams,
- }));
- };
-
- handleShowResponseClick = (e: React.SyntheticEvent<HTMLElement>) => {
- e.preventDefault();
- this.setState((state) => ({
- showChangelog: false,
- showParams: false,
- showResponse: !state.showResponse,
- }));
- };
-
- handleChangelogClick = (e: React.SyntheticEvent<HTMLElement>) => {
- e.preventDefault();
- this.setState((state) => ({
- showChangelog: !state.showChangelog,
- showParams: false,
- showResponse: false,
- }));
- };
-
- renderTabs() {
- const { action } = this.props;
- const { showChangelog, showParams, showResponse } = this.state;
-
- if (action.params || action.hasResponseExample || action.changelog.length > 0) {
- return (
- <ul className="web-api-action-actions tabs">
- {action.params && (
- <li>
- <a
- className={classNames({ selected: showParams })}
- href="#"
- onClick={this.handleShowParamsClick}
- >
- {translate('api_documentation.parameters')}
- </a>
- </li>
- )}
-
- {action.hasResponseExample && (
- <li>
- <a
- className={classNames({ selected: showResponse })}
- href="#"
- onClick={this.handleShowResponseClick}
- >
- {translate('api_documentation.response_example')}
- </a>
- </li>
- )}
-
- {action.changelog.length > 0 && (
- <li>
- <a
- className={classNames({ selected: showChangelog })}
- href="#"
- onClick={this.handleChangelogClick}
- >
- {translate('api_documentation.changelog')}
- </a>
- </li>
- )}
- </ul>
- );
- }
+export default function Action(props: Props) {
+ const { action, domain, showDeprecated, showInternal } = props;
+ const verb = action.post ? 'POST' : 'GET';
+ const actionKey = getActionKey(domain.path, action.key);
- return null;
- }
-
- render() {
- const { action, domain } = this.props;
- const { showChangelog, showParams, showResponse } = this.state;
- const verb = action.post ? 'POST' : 'GET';
- const actionKey = getActionKey(domain.path, action.key);
-
- return (
- <div className="boxed-group" id={actionKey}>
- <header className="web-api-action-header boxed-group-header">
- <Link
- className="spacer-right link-no-underline"
- to={{
- pathname: '/web_api/' + actionKey,
- search: queryToSearch(
- serializeQuery({
- deprecated: Boolean(action.deprecatedSince),
- internal: Boolean(action.internal),
- }),
- ),
- }}
- >
- <LinkIcon />
- </Link>
-
- <h3 className="web-api-action-title">
- {verb}
- &nbsp;
- {actionKey}
- </h3>
-
- {action.internal && (
- <span className="spacer-left">
- <InternalBadge />
- </span>
- )}
-
- {action.since && (
- <span className="spacer-left badge">
- {translateWithParameters('since_x', action.since)}
- </span>
- )}
-
- {action.deprecatedSince && (
- <span className="spacer-left">
- <DeprecatedBadge since={action.deprecatedSince} />
- </span>
- )}
- </header>
-
- <div className="boxed-group-inner">
- <div
- className="web-api-action-description markdown"
- // Safe: comes from the backend
- dangerouslySetInnerHTML={{ __html: action.description }}
- />
+ const [tab, setTab] = React.useState<TabOption | undefined>(undefined);
- {this.renderTabs()}
+ const tabOptions = React.useMemo(() => {
+ const opts = [];
- {showParams && action.params && (
- <Params
- params={action.params}
- showDeprecated={this.props.showDeprecated}
- showInternal={this.props.showInternal}
- />
- )}
+ if (action.params) {
+ opts.push(TabOption.PARAMS);
+ }
+ if (action.hasResponseExample) {
+ opts.push(TabOption.RESPONSE);
+ }
+ if (action.changelog.length > 0) {
+ opts.push(TabOption.CHANGELOG);
+ }
+
+ return opts.map((option) => ({ label: translate('api_documentation', option), value: option }));
+ }, [action]);
+
+ return (
+ <Card id={actionKey}>
+ <header className="sw-flex sw-items-baseline sw-gap-2">
+ <LinkBox
+ to={{
+ pathname: '/web_api/' + actionKey,
+ search: queryToSearch(
+ serializeQuery({
+ deprecated: Boolean(action.deprecatedSince),
+ internal: Boolean(action.internal),
+ }),
+ ),
+ }}
+ >
+ <LinkIcon />
+ </LinkBox>
+
+ <SubHeading className="sw-m-0">
+ {verb} {actionKey}
+ </SubHeading>
+
+ {action.internal && <InternalBadge />}
+
+ {action.since && (
+ <Badge variant="new">{translateWithParameters('since_x', action.since)}</Badge>
+ )}
+
+ {action.deprecatedSince && <DeprecatedBadge since={action.deprecatedSince} />}
+ </header>
+
+ <div
+ className="sw-mt-4 markdown"
+ // Safe: comes from the backend
+ dangerouslySetInnerHTML={{ __html: action.description }}
+ />
+
+ <div className="sw-mt-4">
+ <Tabs options={tabOptions} onChange={(opt) => setTab(opt)} value={tab} />
+
+ {tab === TabOption.PARAMS && action.params && (
+ <Params
+ params={action.params}
+ showDeprecated={showDeprecated}
+ showInternal={showInternal}
+ />
+ )}
- {showResponse && action.hasResponseExample && (
- <ResponseExample action={action} domain={domain} />
- )}
+ {tab === TabOption.RESPONSE && action.hasResponseExample && (
+ <ResponseExample action={action} domain={domain} />
+ )}
- {showChangelog && <ActionChangelog changelog={action.changelog} />}
- </div>
+ {tab === TabOption.CHANGELOG && <ActionChangelog changelog={action.changelog} />}
</div>
- );
- }
+ </Card>
+ );
}
diff --git a/server/sonar-web/src/main/js/apps/web-api/components/ActionChangelog.tsx b/server/sonar-web/src/main/js/apps/web-api/components/ActionChangelog.tsx
index 55d38486456..c49cfe00f72 100644
--- a/server/sonar-web/src/main/js/apps/web-api/components/ActionChangelog.tsx
+++ b/server/sonar-web/src/main/js/apps/web-api/components/ActionChangelog.tsx
@@ -17,6 +17,7 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+import { Badge, ListItem, UnorderedList } from 'design-system';
import * as React from 'react';
import { WebApi } from '../../../types/types';
@@ -26,13 +27,15 @@ interface Props {
export default function ActionChangelog({ changelog }: Props) {
return (
- <ul className="big-spacer-top">
+ <UnorderedList>
{changelog.map((item, index) => (
- <li className="spacer-top" key={index}>
- <span className="spacer-right badge">{item.version}</span>
+ <ListItem key={index}>
+ <Badge variant="default" className="sw-mr-2">
+ {item.version}
+ </Badge>
{item.description}
- </li>
+ </ListItem>
))}
- </ul>
+ </UnorderedList>
);
}
diff --git a/server/sonar-web/src/main/js/apps/web-api/components/DeprecatedBadge.tsx b/server/sonar-web/src/main/js/apps/web-api/components/DeprecatedBadge.tsx
index 11882f45566..6cf82658848 100644
--- a/server/sonar-web/src/main/js/apps/web-api/components/DeprecatedBadge.tsx
+++ b/server/sonar-web/src/main/js/apps/web-api/components/DeprecatedBadge.tsx
@@ -17,6 +17,7 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+import { Badge } from 'design-system';
import * as React from 'react';
import Tooltip from '../../../components/controls/Tooltip';
import { translate, translateWithParameters } from '../../../helpers/l10n';
@@ -32,7 +33,9 @@ export default function DeprecatedBadge({ since }: { since?: string }) {
: translate('api_documentation.deprecated');
return (
<Tooltip overlay={overlay}>
- <span className="badge badge-warning">{label}</span>
+ <span>
+ <Badge variant="default">{label}</Badge>
+ </span>
</Tooltip>
);
}
diff --git a/server/sonar-web/src/main/js/apps/web-api/components/Domain.tsx b/server/sonar-web/src/main/js/apps/web-api/components/Domain.tsx
index aa072b597ce..711dc6c8c4a 100644
--- a/server/sonar-web/src/main/js/apps/web-api/components/Domain.tsx
+++ b/server/sonar-web/src/main/js/apps/web-api/components/Domain.tsx
@@ -17,9 +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.
*/
+import { SubTitle } from 'design-system';
+import { isEmpty } from 'lodash';
import * as React from 'react';
import { WebApi } from '../../../types/types';
-import { actionsFilter, getActionKey, Query } from '../utils';
+import { Query, actionsFilter, getActionKey } from '../utils';
import Action from './Action';
import DeprecatedBadge from './DeprecatedBadge';
import InternalBadge from './InternalBadge';
@@ -33,32 +35,24 @@ export default function Domain({ domain, query }: Props) {
const filteredActions = domain.actions.filter((action) => actionsFilter(query, domain, action));
return (
- <div className="web-api-domain">
- <header className="web-api-domain-header">
- <h2 className="web-api-domain-title">{domain.path}</h2>
+ <div>
+ <header className="sw-flex sw-items-baseline sw-gap-3">
+ <SubTitle className="sw-m-0">{domain.path}</SubTitle>
- {domain.deprecatedSince && (
- <span className="spacer-left">
- <DeprecatedBadge since={domain.deprecatedSince} />
- </span>
- )}
+ {!isEmpty(domain.deprecatedSince) && <DeprecatedBadge since={domain.deprecatedSince} />}
- {domain.internal && (
- <span className="spacer-left">
- <InternalBadge />
- </span>
- )}
+ {domain.internal && <InternalBadge />}
</header>
- {domain.description && (
+ {!isEmpty(domain.description) && (
<div
- className="web-api-domain-description markdown"
+ className="sw-mt-3 markdown"
// Safe: comes from the backend
dangerouslySetInnerHTML={{ __html: domain.description }}
/>
)}
- <div className="web-api-domain-actions">
+ <div className="sw-mt-4 sw-flex sw-flex-col sw-gap-4">
{filteredActions.map((action) => (
<Action
action={action}
diff --git a/server/sonar-web/src/main/js/apps/web-api/components/InternalBadge.tsx b/server/sonar-web/src/main/js/apps/web-api/components/InternalBadge.tsx
index 271afad9c5b..f659020d052 100644
--- a/server/sonar-web/src/main/js/apps/web-api/components/InternalBadge.tsx
+++ b/server/sonar-web/src/main/js/apps/web-api/components/InternalBadge.tsx
@@ -17,6 +17,7 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+import { Badge } from 'design-system';
import * as React from 'react';
import Tooltip from '../../../components/controls/Tooltip';
import { translate } from '../../../helpers/l10n';
@@ -24,7 +25,9 @@ import { translate } from '../../../helpers/l10n';
export default function InternalBadge() {
return (
<Tooltip overlay={translate('api_documentation.internal_tooltip')}>
- <span className="badge badge-error">{translate('internal')}</span>
+ <span>
+ <Badge variant="deleted">{translate('internal')}</Badge>
+ </span>
</Tooltip>
);
}
diff --git a/server/sonar-web/src/main/js/apps/web-api/components/Menu.tsx b/server/sonar-web/src/main/js/apps/web-api/components/Menu.tsx
index f6fa855e78d..5943362405d 100644
--- a/server/sonar-web/src/main/js/apps/web-api/components/Menu.tsx
+++ b/server/sonar-web/src/main/js/apps/web-api/components/Menu.tsx
@@ -17,12 +17,12 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import classNames from 'classnames';
+import { SubnavigationGroup, SubnavigationItem } from 'design-system';
import * as React from 'react';
-import Link from '../../../components/common/Link';
+import { useNavigate } from 'react-router-dom';
import { queryToSearch } from '../../../helpers/urls';
import { WebApi } from '../../../types/types';
-import { actionsFilter, isDomainPathActive, Query, serializeQuery } from '../utils';
+import { Query, actionsFilter, isDomainPathActive, serializeQuery } from '../utils';
import DeprecatedBadge from './DeprecatedBadge';
import InternalBadge from './InternalBadge';
@@ -34,6 +34,19 @@ interface Props {
export default function Menu(props: Props) {
const { domains, query, splat } = props;
+
+ const navigateTo = useNavigate();
+
+ const showDomain = React.useCallback(
+ (domainPath: string) => {
+ navigateTo({
+ pathname: '/web_api/' + domainPath,
+ search: queryToSearch(serializeQuery(query)),
+ });
+ },
+ [query, navigateTo],
+ );
+
const filteredDomains = (domains || [])
.map((domain) => {
const filteredActions = domain.actions.filter((action) =>
@@ -45,29 +58,23 @@ export default function Menu(props: Props) {
const renderDomain = (domain: WebApi.Domain) => {
const internal = !domain.actions.find((action) => !action.internal);
+
return (
- <li
- className={classNames('list-group-item sw-p-0', {
- active: isDomainPathActive(domain.path, splat),
- })}
+ <SubnavigationItem
+ active={isDomainPathActive(domain.path, splat)}
+ onClick={() => showDomain(domain.path)}
key={domain.path}
>
- <Link
- to={{ pathname: '/web_api/' + domain.path, search: queryToSearch(serializeQuery(query)) }}
- >
- <h3 className="sw-truncate sw-px-2 sw-py-3">
- {domain.path}
- {domain.deprecatedSince && <DeprecatedBadge since={domain.deprecatedSince} />}
- {internal && <InternalBadge />}
- </h3>
- </Link>
- </li>
+ {domain.path}
+ {domain.deprecatedSince && <DeprecatedBadge since={domain.deprecatedSince} />}
+ {internal && <InternalBadge />}
+ </SubnavigationItem>
);
};
return (
- <div className="api-documentation-results panel" role="menu">
- <ul className="list-group">{filteredDomains.map(renderDomain)}</ul>
- </div>
+ <SubnavigationGroup className="sw-mt-4 sw-box-border">
+ {filteredDomains.map(renderDomain)}
+ </SubnavigationGroup>
);
}
diff --git a/server/sonar-web/src/main/js/apps/web-api/components/Params.tsx b/server/sonar-web/src/main/js/apps/web-api/components/Params.tsx
index 71af96386ce..865fc766b09 100644
--- a/server/sonar-web/src/main/js/apps/web-api/components/Params.tsx
+++ b/server/sonar-web/src/main/js/apps/web-api/components/Params.tsx
@@ -17,6 +17,7 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+import { ContentCell, DarkLabel, HtmlFormatter, Note, Table, TableRow } from 'design-system';
import * as React from 'react';
import { translate, translateWithParameters } from '../../../helpers/l10n';
import { WebApi } from '../../../types/types';
@@ -29,44 +30,54 @@ interface Props {
showInternal: boolean;
}
+const TABLE_COLUMNS = ['200', 'auto', '200'];
+
export default class Params extends React.PureComponent<Props> {
renderKey(param: WebApi.Param) {
return (
- <td className="markdown" style={{ width: 180 }}>
- <code>{param.key}</code>
+ <ContentCell>
+ <div>
+ <HtmlFormatter>
+ <code className="sw-code">{param.key}</code>
+ </HtmlFormatter>
- {param.internal && (
- <div className="little-spacer-top">
- <InternalBadge />
- </div>
- )}
+ {param.internal && (
+ <div className="sw-mt-1">
+ <InternalBadge />
+ </div>
+ )}
- {param.deprecatedSince && (
- <div className="little-spacer-top">
- <DeprecatedBadge since={param.deprecatedSince} />
- </div>
- )}
+ {param.deprecatedSince && (
+ <div className="sw-mt-1">
+ <DeprecatedBadge since={param.deprecatedSince} />
+ </div>
+ )}
- <div className="note little-spacer-top">{param.required ? 'required' : 'optional'}</div>
+ <Note as="div" className="sw-mt-1">
+ {param.required ? 'required' : 'optional'}
+ </Note>
- {param.since && (
- <div className="note little-spacer-top">
- {translateWithParameters('since_x', param.since)}
- </div>
- )}
+ {param.since && (
+ <Note as="div" className="sw-mt-1">
+ {translateWithParameters('since_x', param.since)}
+ </Note>
+ )}
- {this.props.showDeprecated && param.deprecatedKey && (
- <div className="big-spacer-top spacer-left">
- <div className="note little-spacer-bottom">{translate('replaces')}:</div>
- <code>{param.deprecatedKey}</code>
- {param.deprecatedKeySince && (
- <div className="little-spacer-top">
- <DeprecatedBadge since={param.deprecatedKeySince} />
- </div>
- )}
- </div>
- )}
- </td>
+ {this.props.showDeprecated && param.deprecatedKey && (
+ <div className="sw-ml-2 sw-mt-4">
+ <Note as="div" className="sw-mb-1">
+ {translate('replaces')}:
+ </Note>
+ <code className="sw-code">{param.deprecatedKey}</code>
+ {param.deprecatedKeySince && (
+ <div className="sw-mt-1">
+ <DeprecatedBadge since={param.deprecatedKeySince} />
+ </div>
+ )}
+ </div>
+ )}
+ </div>
+ </ContentCell>
);
}
@@ -74,9 +85,9 @@ export default class Params extends React.PureComponent<Props> {
const value = param[field];
if (value !== undefined) {
return (
- <div className="little-spacer-top">
- <h4>{translate('api_documentation', label)}</h4>
- <code>{value}</code>
+ <div className="sw-mt-1">
+ <DarkLabel as="div">{translate('api_documentation', label)}</DarkLabel>
+ <code className="sw-code">{value}</code>
</div>
);
} else {
@@ -90,28 +101,30 @@ export default class Params extends React.PureComponent<Props> {
.filter((p) => showDeprecated || !p.deprecatedSince)
.filter((p) => showInternal || !p.internal);
return (
- <div className="web-api-params">
- <table>
- <tbody>
- {displayedParameters.map((param) => (
- <tr key={param.key}>
- {this.renderKey(param)}
+ <div className="sw-mt-6">
+ <Table columnCount={TABLE_COLUMNS.length} columnWidths={TABLE_COLUMNS}>
+ {displayedParameters.map((param) => (
+ <TableRow key={param.key}>
+ {this.renderKey(param)}
- <td>
- <div
- className="markdown"
- // Safe: comes from the backend
- dangerouslySetInnerHTML={{ __html: param.description }}
- />
- </td>
+ <ContentCell>
+ <div
+ className="markdown"
+ // Safe: comes from the backend
+ dangerouslySetInnerHTML={{ __html: param.description }}
+ />
+ </ContentCell>
- <td style={{ width: 250 }}>
+ <ContentCell>
+ <div>
{param.possibleValues && (
<div>
- <h4>{translate('api_documentation.possible_values')}</h4>
- <ul className="list-styled">
+ <DarkLabel as="div">
+ {translate('api_documentation.possible_values')}
+ </DarkLabel>
+ <ul>
{param.possibleValues.map((value) => (
- <li className="little-spacer-top" key={value}>
+ <li className="sw-mt-1" key={value}>
<code>{value}</code>
</li>
))}
@@ -126,11 +139,11 @@ export default class Params extends React.PureComponent<Props> {
{this.renderConstraint(param, 'maximumValue', 'max_value')}
{this.renderConstraint(param, 'minimumLength', 'min_length')}
{this.renderConstraint(param, 'maximumLength', 'max_length')}
- </td>
- </tr>
- ))}
- </tbody>
- </table>
+ </div>
+ </ContentCell>
+ </TableRow>
+ ))}
+ </Table>
</div>
);
}
diff --git a/server/sonar-web/src/main/js/apps/web-api/components/ResponseExample.tsx b/server/sonar-web/src/main/js/apps/web-api/components/ResponseExample.tsx
index 71d23cd5051..06ef9f4f794 100644
--- a/server/sonar-web/src/main/js/apps/web-api/components/ResponseExample.tsx
+++ b/server/sonar-web/src/main/js/apps/web-api/components/ResponseExample.tsx
@@ -61,8 +61,10 @@ export default class ResponseExample extends React.PureComponent<Props, State> {
const { responseExample } = this.state;
return (
- <div className="web-api-response">
- {responseExample && <pre style={{ whiteSpace: 'pre-wrap' }}>{responseExample.example}</pre>}
+ <div className="sw-mt-6">
+ {responseExample && (
+ <pre className="sw-code sw-whitespace-pre-wrap">{responseExample.example}</pre>
+ )}
</div>
);
}
diff --git a/server/sonar-web/src/main/js/apps/web-api/components/Search.tsx b/server/sonar-web/src/main/js/apps/web-api/components/Search.tsx
index f1e86864bf2..be7bb705e78 100644
--- a/server/sonar-web/src/main/js/apps/web-api/components/Search.tsx
+++ b/server/sonar-web/src/main/js/apps/web-api/components/Search.tsx
@@ -17,10 +17,9 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+import { Checkbox, HelperHintIcon, InputSearch } from 'design-system';
import * as React from 'react';
-import Checkbox from '../../../components/controls/Checkbox';
import HelpTooltip from '../../../components/controls/HelpTooltip';
-import SearchBox from '../../../components/controls/SearchBox';
import { translate } from '../../../helpers/l10n';
import { Query } from '../utils';
@@ -35,35 +34,34 @@ export default function Search(props: Props) {
const { query, onToggleInternal, onToggleDeprecated } = props;
return (
- <div className="web-api-search">
+ <div>
<div>
- <SearchBox
+ <InputSearch
onChange={props.onSearch}
placeholder={translate('api_documentation.search')}
value={query.search}
/>
</div>
- <div className="big-spacer-top">
- <Checkbox checked={query.internal} className="text-middle" onCheck={onToggleInternal}>
- <span className="little-spacer-left">{translate('api_documentation.show_internal')}</span>
+ <div className="sw-flex sw-items-center sw-mt-4">
+ <Checkbox checked={query.internal} onCheck={onToggleInternal}>
+ <span className="sw-ml-2">{translate('api_documentation.show_internal')}</span>
</Checkbox>
- <HelpTooltip
- className="spacer-left"
- overlay={translate('api_documentation.internal_tooltip')}
- />
+ <HelpTooltip className="sw-ml-2" overlay={translate('api_documentation.internal_tooltip')}>
+ <HelperHintIcon />
+ </HelpTooltip>
</div>
- <div className="spacer-top">
- <Checkbox checked={query.deprecated} className="text-middle" onCheck={onToggleDeprecated}>
- <span className="little-spacer-left">
- {translate('api_documentation.show_deprecated')}
- </span>
+ <div className="sw-flex sw-items-center sw-mt-2">
+ <Checkbox checked={query.deprecated} onCheck={onToggleDeprecated}>
+ <span className="sw-ml-2">{translate('api_documentation.show_deprecated')}</span>
</Checkbox>
<HelpTooltip
- className="spacer-left"
+ className="sw-ml-2"
overlay={translate('api_documentation.deprecation_tooltip')}
- />
+ >
+ <HelperHintIcon />
+ </HelpTooltip>
</div>
</div>
);
diff --git a/server/sonar-web/src/main/js/apps/web-api/components/WebApiApp.tsx b/server/sonar-web/src/main/js/apps/web-api/components/WebApiApp.tsx
index a6b589c0d51..d0c92dab643 100644
--- a/server/sonar-web/src/main/js/apps/web-api/components/WebApiApp.tsx
+++ b/server/sonar-web/src/main/js/apps/web-api/components/WebApiApp.tsx
@@ -17,14 +17,20 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+import styled from '@emotion/styled';
+import {
+ LAYOUT_FOOTER_HEIGHT,
+ LAYOUT_GLOBAL_NAV_HEIGHT,
+ LargeCenteredLayout,
+ PageContentFontWrapper,
+ Title,
+} from 'design-system';
import { maxBy } from 'lodash';
import * as React from 'react';
import { Helmet } from 'react-helmet-async';
import { Params, useParams } from 'react-router-dom';
import { fetchWebApi } from '../../../api/web-api';
import A11ySkipTarget from '../../../components/a11y/A11ySkipTarget';
-import Link from '../../../components/common/Link';
-import ScreenPositionHelper from '../../../components/common/ScreenPositionHelper';
import Suggestions from '../../../components/embed-docs-modal/Suggestions';
import { Location, Router, withRouter } from '../../../components/hoc/withRouter';
import { translate } from '../../../helpers/l10n';
@@ -155,49 +161,47 @@ export class WebApiApp extends React.PureComponent<Props, State> {
};
render() {
- const splat = this.props.params.splat || '';
+ const splat = this.props.params.splat ?? '';
const query = parseQuery(this.props.location.query);
const { domains } = this.state;
const domain = domains.find((domain) => isDomainPathActive(domain.path, splat));
return (
- <div className="layout-page">
- <Suggestions suggestions="api_documentation" />
- <Helmet defer={false} title={translate('api_documentation.page')} />
- <ScreenPositionHelper className="layout-page-side-outer">
- {({ top }) => (
- <div className="layout-page-side" style={{ top }}>
- <div className="layout-page-side-inner">
- <div className="layout-page-filters">
- <A11ySkipTarget anchor="webapi_main" />
-
- <div className="web-api-page-header">
- <Link to="/web_api/">
- <h1>{translate('api_documentation.page')}</h1>
- </Link>
- </div>
-
- <Search
- onSearch={this.handleSearch}
- onToggleDeprecated={this.handleToggleDeprecated}
- onToggleInternal={this.handleToggleInternal}
- query={query}
- />
-
- <Menu domains={this.state.domains} query={query} splat={splat} />
- </div>
+ <LargeCenteredLayout>
+ <PageContentFontWrapper className="sw-body-sm sw-w-full sw-flex">
+ <Suggestions suggestions="api_documentation" />
+ <Helmet defer={false} title={translate('api_documentation.page')} />
+ <div className="sw-w-full sw-flex">
+ <NavContainer
+ aria-label={translate('api_documentation.domain_nav')}
+ className="sw--mx-2"
+ >
+ <A11ySkipTarget anchor="webapi_main" />
+
+ <Title>{translate('api_documentation.page')}</Title>
+
+ <Search
+ onSearch={this.handleSearch}
+ onToggleDeprecated={this.handleToggleDeprecated}
+ onToggleInternal={this.handleToggleInternal}
+ query={query}
+ />
+ <div className="sw-w-[300px] sw-mr-2">
+ <Menu domains={this.state.domains} query={query} splat={splat} />
</div>
- </div>
- )}
- </ScreenPositionHelper>
-
- <div className="layout-page-main">
- <div className="layout-page-main-inner">
- {domain && <Domain domain={domain} key={domain.path} query={query} />}
+ </NavContainer>
+ <main
+ style={{
+ height: `calc(100vh - ${LAYOUT_FOOTER_HEIGHT + LAYOUT_GLOBAL_NAV_HEIGHT}px`,
+ }}
+ className="sw-box-border sw-overflow-y-auto sw-relative sw-flex-1 sw-min-w-0 sw-ml-8 sw-py-8"
+ >
+ {domain && <Domain domain={domain} key={domain.path} query={query} />}
+ </main>
</div>
- </div>
- </div>
+ </PageContentFontWrapper>
+ </LargeCenteredLayout>
);
}
}
@@ -224,3 +228,13 @@ function getLatestDeprecatedAction(domain: Pick<WebApi.Domain, 'actions'>) {
});
return latestDeprecation || undefined;
}
+
+const NavContainer = styled.nav`
+ scrollbar-gutter: stable;
+ overflow-y: auto;
+ overflow-x: hidden;
+ box-sizing: border-box;
+ height: calc(100vh - ${LAYOUT_FOOTER_HEIGHT + LAYOUT_GLOBAL_NAV_HEIGHT}px);
+ padding-top: 1.5rem;
+ padding-bottom: 1.5rem;
+`;
diff --git a/server/sonar-web/src/main/js/apps/web-api/components/__tests__/Actions-test.tsx b/server/sonar-web/src/main/js/apps/web-api/components/__tests__/Actions-test.tsx
index 418d0a4acb8..b4f02541131 100644
--- a/server/sonar-web/src/main/js/apps/web-api/components/__tests__/Actions-test.tsx
+++ b/server/sonar-web/src/main/js/apps/web-api/components/__tests__/Actions-test.tsx
@@ -85,7 +85,7 @@ it('should allow to browse additional information', async () => {
expect(await byText('{"example": "response"}').find()).toBeInTheDocument();
});
-function renderAction(props: Partial<Action['props']> = {}) {
+function renderAction(props: Partial<React.ComponentProps<typeof Action>> = {}) {
renderComponent(
<Action
action={mockAction()}
@@ -98,7 +98,7 @@ function renderAction(props: Partial<Action['props']> = {}) {
}
const ui = {
- paramsTab: byRole('link', { name: 'api_documentation.parameters' }),
- responseExampleTab: byRole('link', { name: 'api_documentation.response_example' }),
- changelogTab: byRole('link', { name: 'api_documentation.changelog' }),
+ paramsTab: byRole('tab', { name: 'api_documentation.parameters' }),
+ responseExampleTab: byRole('tab', { name: 'api_documentation.response_example' }),
+ changelogTab: byRole('tab', { name: 'api_documentation.changelog' }),
};
diff --git a/server/sonar-web/src/main/js/apps/webhooks/components/DeliveriesForm.tsx b/server/sonar-web/src/main/js/apps/webhooks/components/DeliveriesForm.tsx
index 4ce3cea9fde..b3a06927a70 100644
--- a/server/sonar-web/src/main/js/apps/webhooks/components/DeliveriesForm.tsx
+++ b/server/sonar-web/src/main/js/apps/webhooks/components/DeliveriesForm.tsx
@@ -87,7 +87,6 @@ export default function DeliveriesForm({ onClose, webhook }: Props) {
loadMore={fetchMoreDeliveries}
ready={!loading}
total={paging.total}
- useMIUIButtons
/>
)}
</Spinner>
diff --git a/server/sonar-web/src/main/js/components/SourceViewer/SourceViewer.tsx b/server/sonar-web/src/main/js/components/SourceViewer/SourceViewer.tsx
index ea3a15bda4f..ae22f69629d 100644
--- a/server/sonar-web/src/main/js/components/SourceViewer/SourceViewer.tsx
+++ b/server/sonar-web/src/main/js/components/SourceViewer/SourceViewer.tsx
@@ -17,6 +17,7 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+import { FlagMessage } from 'design-system/lib';
import { intersection } from 'lodash';
import * as React from 'react';
import {
@@ -42,7 +43,6 @@ import {
SourceLine,
SourceViewerFile,
} from '../../types/types';
-import { Alert } from '../ui/Alert';
import { WorkspaceContext } from '../workspace/context';
import SourceViewerCode from './SourceViewerCode';
import { SourceViewerContext } from './SourceViewerContext';
@@ -591,17 +591,17 @@ export class SourceViewerClass extends React.PureComponent<Props, State> {
if (this.state.notExist) {
return (
- <Alert className="spacer-top" variant="warning">
+ <FlagMessage className="sw-mt-2" variant="warning">
{translate('component_viewer.no_component')}
- </Alert>
+ </FlagMessage>
);
}
if (notAccessible) {
return (
- <Alert className="spacer-top" variant="warning">
+ <FlagMessage className="sw-mt-2" variant="warning">
{translate('code_viewer.no_source_code_displayed_due_to_security')}
- </Alert>
+ </FlagMessage>
);
}
@@ -615,9 +615,9 @@ export class SourceViewerClass extends React.PureComponent<Props, State> {
{!hideHeader && this.renderHeader(component)}
{sourceRemoved && (
- <Alert className="spacer-top" variant="warning">
+ <FlagMessage className="sw-mt-4 sw-ml-4" variant="warning">
{translate('code_viewer.no_source_code_displayed_due_to_source_removed')}
- </Alert>
+ </FlagMessage>
)}
{!sourceRemoved && sources !== undefined && this.renderCode(sources)}
diff --git a/server/sonar-web/src/main/js/components/SourceViewer/styles.css b/server/sonar-web/src/main/js/components/SourceViewer/styles.css
index e978b985050..6c6c0f68c5c 100644
--- a/server/sonar-web/src/main/js/components/SourceViewer/styles.css
+++ b/server/sonar-web/src/main/js/components/SourceViewer/styles.css
@@ -42,10 +42,6 @@
text-align: center;
}
-.source-viewer-more-code .spinner {
- top: -1px;
-}
-
.source-table + .source-viewer-more-code {
border-bottom: none;
border-top: 1px solid var(--barBorderColor);
diff --git a/server/sonar-web/src/main/js/components/activity-graph/GraphsHistory.tsx b/server/sonar-web/src/main/js/components/activity-graph/GraphsHistory.tsx
index c14197d4c3f..9b51e8a5c14 100644
--- a/server/sonar-web/src/main/js/components/activity-graph/GraphsHistory.tsx
+++ b/server/sonar-web/src/main/js/components/activity-graph/GraphsHistory.tsx
@@ -73,7 +73,7 @@ export default class GraphsHistory extends React.PureComponent<Props, State> {
return (
<div className="sw-flex sw-justify-center sw-flex-col sw-items-stretch sw-grow">
<div className="sw-text-center">
- <Spinner ariaLabel={translate('loading')} loading={loading} />
+ <Spinner loading={loading} />
</div>
</div>
);
diff --git a/server/sonar-web/src/main/js/components/common/ActivityLink.css b/server/sonar-web/src/main/js/components/common/ActivityLink.css
deleted file mode 100644
index 52cf39a1024..00000000000
--- a/server/sonar-web/src/main/js/components/common/ActivityLink.css
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2024 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.
- */
-a.activity-link {
- border: none;
-}
-
-.activity-link > span {
- border-bottom: 1px solid var(--lightBlue);
-}
diff --git a/server/sonar-web/src/main/js/components/common/ActivityLink.tsx b/server/sonar-web/src/main/js/components/common/ActivityLink.tsx
index 5b3b5692cab..0a04807749c 100644
--- a/server/sonar-web/src/main/js/components/common/ActivityLink.tsx
+++ b/server/sonar-web/src/main/js/components/common/ActivityLink.tsx
@@ -24,7 +24,6 @@ import { getActivityUrl, getMeasureHistoryUrl } from '../../helpers/urls';
import { BranchLike } from '../../types/branch-like';
import { GraphType } from '../../types/project-activity';
import { isCustomGraph } from '../activity-graph/utils';
-import './ActivityLink.css';
export interface ActivityLinkProps {
branchLike?: BranchLike;
diff --git a/server/sonar-web/src/main/js/components/common/BranchStatus.tsx b/server/sonar-web/src/main/js/components/common/BranchStatus.tsx
deleted file mode 100644
index d782cb78f89..00000000000
--- a/server/sonar-web/src/main/js/components/common/BranchStatus.tsx
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2024 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 Level from '../../components/ui/Level';
-import { BranchLike } from '../../types/branch-like';
-
-export interface BranchStatusProps {
- branchLike: BranchLike;
-}
-
-export default function BranchStatus(props: BranchStatusProps) {
- const { branchLike } = props;
-
- if (!branchLike.status) {
- return null;
- }
-
- return <Level level={branchLike.status.qualityGateStatus} small />;
-}
diff --git a/server/sonar-web/src/main/js/components/common/CodeSnippet.css b/server/sonar-web/src/main/js/components/common/CodeSnippet.css
deleted file mode 100644
index d3396345e70..00000000000
--- a/server/sonar-web/src/main/js/components/common/CodeSnippet.css
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2024 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.
- */
-.code-snippet {
- background: var(--barBorderColor);
- border-radius: 3px;
-}
-
-.code-snippet pre {
- padding: var(--gridSize) calc(2 * var(--gridSize));
- border-right: 1px solid var(--transparentGray);
- overflow-y: hidden;
- overflow-x: auto;
-}
-
-.code-snippet > button {
- height: auto;
- border: 0;
- border-radius: 0;
- background: transparent;
- padding: var(--gridSize);
-}
-
-.code-snippet > button:hover,
-.code-snippet > button:focus,
-.code-snippet > button:active {
- background-color: var(--transparentGray);
- color: var(--darkBlue);
-}
diff --git a/server/sonar-web/src/main/js/components/common/CodeSnippet.tsx b/server/sonar-web/src/main/js/components/common/CodeSnippet.tsx
deleted file mode 100644
index 3169f8b62c7..00000000000
--- a/server/sonar-web/src/main/js/components/common/CodeSnippet.tsx
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2024 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 classNames from 'classnames';
-import * as React from 'react';
-import { ClipboardButton } from '../../components/controls/clipboard';
-import { isDefined } from '../../helpers/types';
-import './CodeSnippet.css';
-
-export interface CodeSnippetProps {
- isOneLine?: boolean;
- noCopy?: boolean;
- snippet: string | (string | undefined)[];
-}
-
-export default function CodeSnippet(props: CodeSnippetProps) {
- const { isOneLine, noCopy, snippet } = props;
- const snippetRef = React.useRef<HTMLPreElement>(null);
-
- let finalSnippet: string;
- if (Array.isArray(snippet)) {
- finalSnippet = snippet.filter((line) => isDefined(line)).join(isOneLine ? ' ' : ' \\\n ');
- } else {
- finalSnippet = snippet;
- }
-
- return (
- <div className={classNames('code-snippet spacer-top spacer-bottom display-flex-row', {})}>
- {/* eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex */}
- <pre className="flex-1" ref={snippetRef} tabIndex={0}>
- {finalSnippet}
- </pre>
- {!noCopy && <ClipboardButton copyValue={finalSnippet} />}
- </div>
- );
-}
diff --git a/server/sonar-web/src/main/js/components/common/DocLink.tsx b/server/sonar-web/src/main/js/components/common/DocLink.tsx
deleted file mode 100644
index bf5b2294a5a..00000000000
--- a/server/sonar-web/src/main/js/components/common/DocLink.tsx
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2024 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 { useDocUrl } from '../../helpers/docs';
-import Link, { LinkProps } from './Link';
-
-type Props = Omit<LinkProps, 'to'> & { to: string; innerRef?: React.Ref<HTMLAnchorElement> };
-
-/**
- * /!\ Deprecated: use DocumentationLink instead
- */
-export default function DocLink({ to, innerRef, ...props }: Props) {
- const toStatic = useDocUrl(to);
- return <Link ref={innerRef} to={toStatic} target="_blank" {...props} />;
-}
diff --git a/server/sonar-web/src/main/js/components/common/DocumentationTooltip.tsx b/server/sonar-web/src/main/js/components/common/DocumentationTooltip.tsx
index 7a6cd1b24eb..385f6670f47 100644
--- a/server/sonar-web/src/main/js/components/common/DocumentationTooltip.tsx
+++ b/server/sonar-web/src/main/js/components/common/DocumentationTooltip.tsx
@@ -22,7 +22,7 @@ import * as React from 'react';
import HelpTooltip from '../../components/controls/HelpTooltip';
import { KeyboardKeys } from '../../helpers/keycodes';
import { Placement } from '../controls/Tooltip';
-import DocLink from './DocLink';
+import DocumentationLink from './DocumentationLink';
import Link from './Link';
export interface DocumentationTooltipProps {
@@ -79,7 +79,7 @@ export default function DocumentationTooltip(props: DocumentationTooltipProps) {
isInteractive
innerRef={helpRef}
overlay={
- <div className="big-padded-top big-padded-bottom">
+ <div className="sw-py-4">
{title && (
<div className="spacer-bottom">
<strong>{title}</strong>
@@ -90,14 +90,17 @@ export default function DocumentationTooltip(props: DocumentationTooltipProps) {
{links && (
<>
- <hr className="big-spacer-top big-spacer-bottom" />
+ <hr className="sw-my-4" />
{links.map(({ href, label, inPlace, doc = true }, index) => (
<div className="little-spacer-bottom" key={label}>
{doc ? (
- <DocLink to={href} innerRef={(ref) => (linksRef.current[index] = ref)}>
+ <DocumentationLink
+ to={href}
+ innerRef={(ref) => (linksRef.current[index] = ref)}
+ >
{label}
- </DocLink>
+ </DocumentationLink>
) : (
<Link
to={href}
diff --git a/server/sonar-web/src/main/js/components/common/FiltersHeader.tsx b/server/sonar-web/src/main/js/components/common/FiltersHeader.tsx
deleted file mode 100644
index 8a6f4f55192..00000000000
--- a/server/sonar-web/src/main/js/components/common/FiltersHeader.tsx
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2024 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 { Button } from '../../components/controls/buttons';
-import { translate } from '../../helpers/l10n';
-
-interface Props {
- displayReset: boolean;
- onReset: () => void;
-}
-
-export default function FiltersHeader({ displayReset, onReset }: Props) {
- return (
- <div className="search-navigator-filters-header">
- {displayReset && (
- <div className="pull-right">
- <Button className="button-red" id="coding-rules-clear-all-filters" onClick={onReset}>
- {translate('clear_all_filters')}
- </Button>
- </div>
- )}
-
- <h2 className="h3">{translate('filters')}</h2>
- </div>
- );
-}
diff --git a/server/sonar-web/src/main/js/components/common/MetaLink.tsx b/server/sonar-web/src/main/js/components/common/MetaLink.tsx
index b4df193db66..86ecb5ce07c 100644
--- a/server/sonar-web/src/main/js/components/common/MetaLink.tsx
+++ b/server/sonar-web/src/main/js/components/common/MetaLink.tsx
@@ -55,7 +55,7 @@ export default function MetaLink({ iconOnly, link }: Readonly<Props>) {
to={link.url}
preventDefault={!isValid}
onClick={isValid ? undefined : handleClick}
- icon={<ProjectLinkIcon miui type={link.type} />}
+ icon={<ProjectLinkIcon type={link.type} />}
>
{!iconOnly && linkTitle}
</StyledLink>
diff --git a/server/sonar-web/src/main/js/components/common/MultiSelect.tsx b/server/sonar-web/src/main/js/components/common/MultiSelect.tsx
deleted file mode 100644
index e4fa098d3a4..00000000000
--- a/server/sonar-web/src/main/js/components/common/MultiSelect.tsx
+++ /dev/null
@@ -1,234 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2024 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 classNames from 'classnames';
-import { isEmpty, remove, xor } from 'lodash';
-import * as React from 'react';
-import SearchBox from '../../components/controls/SearchBox';
-import { translateWithParameters } from '../../helpers/l10n';
-import MultiSelectOption, { Element } from './MultiSelectOption';
-
-export interface MultiSelectProps {
- allowNewElements?: boolean;
- allowSelection?: boolean;
- legend: string;
- elements: string[];
- filterSelected?: (query: string, selectedElements: string[]) => string[];
- footerNode?: React.ReactNode;
- listSize?: number;
- onSearch: (query: string) => Promise<void>;
- onSelect: (item: string) => void;
- onUnselect: (item: string) => void;
- placeholder: string;
- renderLabel?: (element: string) => React.ReactNode;
- selectedElements: string[];
- validateSearchInput?: (value: string) => string;
-}
-
-interface State {
- loading: boolean;
- query: string;
- elements: Element[];
-}
-
-interface DefaultProps {
- // eslint-disable-next-line react/no-unused-prop-types
- filterSelected: (query: string, selectedElements: string[]) => string[];
- listSize: number;
- renderLabel: (element: string) => React.ReactNode;
- validateSearchInput: (value: string) => string;
-}
-
-type PropsWithDefault = MultiSelectProps & DefaultProps;
-
-export default class MultiSelect extends React.PureComponent<PropsWithDefault, State> {
- mounted = false;
-
- static defaultProps: DefaultProps = {
- filterSelected: (query: string, selectedElements: string[]) =>
- selectedElements.filter((elem) => elem.includes(query)),
- listSize: 0,
- renderLabel: (element: string) => element,
- validateSearchInput: (value: string) => value,
- };
-
- constructor(props: PropsWithDefault) {
- super(props);
- this.state = {
- loading: false,
- query: '',
- elements: [],
- };
- }
-
- componentDidMount() {
- this.mounted = true;
- this.onSearchQuery('');
- this.computeElements();
- }
-
- componentDidUpdate(prevProps: PropsWithDefault, prevState: State) {
- if (
- !isEmpty(
- xor(
- [...prevProps.selectedElements, ...prevProps.elements],
- [...this.props.selectedElements, ...this.props.elements],
- ),
- )
- ) {
- this.computeElements();
- }
-
- if (prevState.query !== this.state.query) {
- this.setState(({ query, elements }, props) => {
- const newElements = [...elements];
- this.appendCreateElelement(newElements, query, props);
- return { elements: newElements };
- });
- }
- }
-
- componentWillUnmount() {
- this.mounted = false;
- }
-
- computeElements() {
- this.setState(({ query }, props) => {
- const newStateElement: Element[] = [
- ...this.props
- .filterSelected(query, this.props.selectedElements)
- .map((e) => ({ value: e, selected: true })),
- ...this.props.elements.map((e) => ({
- value: e,
- selected: false,
- })),
- ];
-
- this.appendCreateElelement(newStateElement, query, props);
- return { elements: newStateElement };
- });
- }
-
- appendCreateElelement(elements: Element[], query: string, props: PropsWithDefault) {
- const { allowNewElements = true } = props;
- if (this.isNewElement(query, props) && allowNewElements) {
- const create = elements.find((e) => e.custom);
- if (create) {
- create.value = query;
- } else {
- elements.push({ value: query, selected: false, custom: true });
- }
- } else if (!this.isNewElement(query, props) && allowNewElements) {
- remove(elements, (e) => e.custom);
- }
- }
-
- handleSelectChange = (selected: boolean, item: string) => {
- this.setState(({ elements }) => {
- const newElements = elements.map((e) =>
- e.value === item ? { value: e.value, selected } : e,
- );
- return { elements: newElements };
- });
- if (selected) {
- this.onSelectItem(item);
- } else {
- this.onUnselectItem(item);
- }
- };
-
- handleSearchChange = (value: string) => {
- this.onSearchQuery((this.props as PropsWithDefault).validateSearchInput(value));
- };
-
- onSearchQuery = (query: string) => {
- const { allowNewElements = true } = this.props;
-
- this.props.onSearch(query).then(this.stopLoading, this.stopLoading);
- if (allowNewElements) {
- this.setState({
- loading: true,
- query,
- });
- }
- };
-
- onSelectItem = (item: string) => {
- if (this.isNewElement(item, this.props)) {
- this.onSearchQuery('');
- }
- this.props.onSelect(item);
- };
-
- onUnselectItem = (item: string) => this.props.onUnselect(item);
-
- isNewElement = (elem: string, { selectedElements, elements }: PropsWithDefault) =>
- !isEmpty(elem) && selectedElements.indexOf(elem) === -1 && elements.indexOf(elem) === -1;
-
- stopLoading = () => {
- if (this.mounted) {
- this.setState({ loading: false });
- }
- };
-
- render() {
- const { legend, allowSelection = true, footerNode = '' } = this.props;
- const { renderLabel } = this.props as PropsWithDefault;
- const { query, elements } = this.state;
- const infiniteList = this.props.listSize === 0;
- const listClasses = classNames('menu', {
- 'menu-vertically-limited': infiniteList,
- 'spacer-top': infiniteList,
- 'with-top-separator': infiniteList,
- 'with-bottom-separator': Boolean(footerNode),
- });
-
- return (
- <div className="multi-select">
- <div className="menu-search">
- <SearchBox
- autoFocus
- className="little-spacer-top"
- loading={this.state.loading}
- onChange={this.handleSearchChange}
- placeholder={this.props.placeholder}
- value={query}
- />
- </div>
- <fieldset aria-label={legend}>
- <ul className={listClasses}>
- {elements.map((e) => (
- <MultiSelectOption
- element={e}
- disabled={!allowSelection && !e.selected}
- key={e.value}
- onSelectChange={this.handleSelectChange}
- renderLabel={renderLabel}
- />
- ))}
- {isEmpty(elements) && (
- <li className="spacer-left">{translateWithParameters('no_results_for_x', query)}</li>
- )}
- </ul>
- </fieldset>
- {footerNode}
- </div>
- );
- }
-}
diff --git a/server/sonar-web/src/main/js/components/common/MultiSelectOption.tsx b/server/sonar-web/src/main/js/components/common/MultiSelectOption.tsx
deleted file mode 100644
index f9cea98cf22..00000000000
--- a/server/sonar-web/src/main/js/components/common/MultiSelectOption.tsx
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2024 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 classNames from 'classnames';
-import * as React from 'react';
-import Checkbox from '../../components/controls/Checkbox';
-import { translate } from '../../helpers/l10n';
-
-export interface Element {
- value: string;
- selected: boolean;
- custom?: boolean;
-}
-
-export interface MultiSelectOptionProps {
- disabled?: boolean;
- element: Element;
- onSelectChange: (selected: boolean, element: string) => void;
- renderLabel: (element: string) => React.ReactNode;
-}
-
-export default function MultiSelectOption(props: MultiSelectOptionProps) {
- const { disabled, element } = props;
- const [active, setActive] = React.useState(false);
- const className = classNames({ active, disabled });
- const label = props.renderLabel(element.value);
-
- return (
- <li
- onFocus={() => setActive(true)}
- onBlur={() => setActive(false)}
- onMouseLeave={() => setActive(false)}
- onMouseOver={() => setActive(true)}
- >
- <Checkbox
- checked={element.selected}
- className={className}
- disabled={disabled}
- id={element.value}
- onCheck={props.onSelectChange}
- >
- {element.custom ? (
- <span
- aria-label={`${translate('create_new_element')}: ${label}`}
- className="little-spacer-left"
- >
- <span aria-hidden className="little-spacer-right">
- +
- </span>
- {label}
- </span>
- ) : (
- <span className="little-spacer-left">{label}</span>
- )}
- </Checkbox>
- </li>
- );
-}
diff --git a/server/sonar-web/src/main/js/components/common/StatusIndicator.css b/server/sonar-web/src/main/js/components/common/StatusIndicator.css
deleted file mode 100644
index e116fd9da4f..00000000000
--- a/server/sonar-web/src/main/js/components/common/StatusIndicator.css
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2024 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.
- */
-.status-indicator {
- display: inline-block;
- box-sizing: border-box;
- width: 16px;
- height: 16px;
- border-radius: 16px;
- margin: 4px;
-}
-
-.status-indicator.small-status-indicator {
- width: 8px;
- height: 8px;
- border-radius: 8px;
- margin: 8px;
-}
-
-.status-indicator.big-status-indicator {
- width: var(--controlHeight);
- height: var(--controlHeight);
- border-radius: var(--controlHeight);
- margin: 0;
-}
-
-.status-indicator.red {
- position: relative;
- z-index: 2;
- background-color: var(--red);
-}
-
-.status-indicator.yellow {
- background-color: var(--yellow);
-}
-
-.status-indicator.green {
- background-color: var(--green);
-}
-
-.status-indicator.orange {
- background-color: var(--orange);
-}
-
-.status-indicator.gray {
- background-color: var(--gray71);
-}
diff --git a/server/sonar-web/src/main/js/components/common/StatusIndicator.tsx b/server/sonar-web/src/main/js/components/common/StatusIndicator.tsx
index c6cac3e2379..a7d6091a509 100644
--- a/server/sonar-web/src/main/js/components/common/StatusIndicator.tsx
+++ b/server/sonar-web/src/main/js/components/common/StatusIndicator.tsx
@@ -21,7 +21,6 @@ import { CheckIcon, FlagErrorIcon, FlagWarningIcon } from 'design-system';
import * as React from 'react';
import { translate } from '../../helpers/l10n';
import { HealthTypes } from '../../types/types';
-import './StatusIndicator.css';
export interface StatusIndicatorProps {
color: HealthTypes;
diff --git a/server/sonar-web/src/main/js/components/common/VisibilitySelector.tsx b/server/sonar-web/src/main/js/components/common/VisibilitySelector.tsx
index 0a52aa93bd3..4a71ec2eb19 100644
--- a/server/sonar-web/src/main/js/components/common/VisibilitySelector.tsx
+++ b/server/sonar-web/src/main/js/components/common/VisibilitySelector.tsx
@@ -39,7 +39,7 @@ export default function VisibilitySelector(props: VisibilitySelectorProps) {
<div className={classNames(className)}>
{Object.values(Visibility).map((v) => (
<RadioButton
- className={`huge-spacer-right visibility-${v}`}
+ className={`sw-mr-10 it__visibility-${v}`}
key={v}
value={v}
checked={v === visibility}
diff --git a/server/sonar-web/src/main/js/components/common/__tests__/BranchStatus-test.tsx b/server/sonar-web/src/main/js/components/common/__tests__/BranchStatus-test.tsx
deleted file mode 100644
index 114c58b37cd..00000000000
--- a/server/sonar-web/src/main/js/components/common/__tests__/BranchStatus-test.tsx
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2024 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 { screen } from '@testing-library/react';
-import * as React from 'react';
-import BranchesServiceMock from '../../../api/mocks/BranchesServiceMock';
-import { mockBranch } from '../../../helpers/mocks/branch-like';
-import { renderComponent } from '../../../helpers/testReactTestingUtils';
-import BranchStatus, { BranchStatusProps } from '../BranchStatus';
-
-const handler = new BranchesServiceMock();
-
-beforeEach(() => {
- handler.reset();
-});
-
-it('should render ok status', async () => {
- renderBranchStatus({ branchLike: mockBranch({ status: { qualityGateStatus: 'OK' } }) });
-
- expect(await screen.findByText('OK')).toBeInTheDocument();
-});
-
-it('should render error status', async () => {
- renderBranchStatus({ branchLike: mockBranch({ status: { qualityGateStatus: 'ERROR' } }) });
-
- expect(await screen.findByText('ERROR')).toBeInTheDocument();
-});
-
-function renderBranchStatus(overrides: Partial<BranchStatusProps> = {}) {
- const defaultProps = {
- branchLike: mockBranch(),
- } as const;
- return renderComponent(<BranchStatus {...defaultProps} {...overrides} />);
-}
diff --git a/server/sonar-web/src/main/js/components/common/__tests__/DocumentationTooltip-test.tsx b/server/sonar-web/src/main/js/components/common/__tests__/DocumentationTooltip-test.tsx
index f5ee4566e9f..d6a721b4719 100644
--- a/server/sonar-web/src/main/js/components/common/__tests__/DocumentationTooltip-test.tsx
+++ b/server/sonar-web/src/main/js/components/common/__tests__/DocumentationTooltip-test.tsx
@@ -31,7 +31,7 @@ const ui = {
helpIcon: byTestId('help-tooltip-activator'),
helpLink: byRole('link', { name: 'Icon' }),
linkInTooltip: byRole('link', { name: 'Label' }),
- linkInTooltip2: byRole('link', { name: 'opens_in_new_window Label2' }),
+ linkInTooltip2: byRole('link', { name: 'Label2' }),
afterLink: byRole('link', { name: 'Interactive element after' }),
};
diff --git a/server/sonar-web/src/main/js/components/common/__tests__/MultiSelect-test.tsx b/server/sonar-web/src/main/js/components/common/__tests__/MultiSelect-test.tsx
deleted file mode 100644
index 0e9e234137f..00000000000
--- a/server/sonar-web/src/main/js/components/common/__tests__/MultiSelect-test.tsx
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2024 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 { render } from '@testing-library/react';
-import userEvent from '@testing-library/user-event';
-import * as React from 'react';
-import { byRole, byText } from '../../../helpers/testSelector';
-import MultiSelect from '../MultiSelect';
-
-const ui = {
- checkbox: (name: string) => byRole('checkbox', { name }),
- search: byRole('searchbox', { name: 'search' }),
- noResult: byText('no_results_for_x.notfound'),
-};
-
-it('should handle selection', async () => {
- const user = userEvent.setup();
- const rerender = renderMultiSelect();
- expect(ui.checkbox('az').get()).toBeChecked();
- await user.click(ui.checkbox('az').get());
- expect(ui.checkbox('az').get()).not.toBeChecked();
-
- await user.type(ui.search.get(), 'create');
- await user.click(ui.checkbox('create_new_element: create').get());
- expect(ui.checkbox('create').get()).toBeChecked();
-
- // Custom label
- rerender({ renderLabel: (label) => `prefxed-${label}` });
- expect(ui.checkbox('prefxed-create').get()).toBeChecked();
-});
-
-it('should handle disable selection', () => {
- renderMultiSelect({ allowSelection: false });
- expect(ui.checkbox('az').get()).toBeChecked();
- expect(ui.checkbox('cx').get()).toHaveAttribute('aria-disabled', 'true');
-});
-
-it('should handle search', async () => {
- const user = userEvent.setup();
- const rerender = renderMultiSelect();
- expect(ui.checkbox('cx').get()).toBeInTheDocument();
- await user.type(ui.search.get(), 'az');
- expect(ui.checkbox('cx').query()).not.toBeInTheDocument();
- expect(ui.checkbox('az').get()).toBeInTheDocument();
- expect(ui.checkbox('az-new').get()).toBeInTheDocument();
-
- await user.clear(ui.search.get());
- await user.type(ui.search.get(), 'notfound');
- expect(ui.checkbox('create_new_element: notfound').get()).toBeInTheDocument();
-
- rerender({ allowNewElements: false });
- await user.clear(ui.search.get());
- await user.type(ui.search.get(), 'notfound');
- expect(ui.noResult.get()).toBeInTheDocument();
-});
-
-function renderMultiSelect(override?: Partial<MultiSelect['props']>) {
- const initial = ['cx', 'dw', 'ev', 'fu', 'gt', 'hs'];
- const initialSelected = ['az', 'by'];
-
- function Parent(props?: Partial<MultiSelect['props']>) {
- const [elements, setElements] = React.useState(initial);
- const [selected, setSelected] = React.useState(['az', 'by']);
- const onSearch = (query: string) => {
- if (query === 'notfound') {
- setElements([]);
- setSelected([]);
- } else if (query === '') {
- setElements(initial);
- setSelected(initialSelected);
- } else {
- setElements([...elements.filter((e) => e.indexOf(query) !== -1), `${query}-new`]);
- setSelected(selected.filter((e) => e.indexOf(query) !== -1));
- }
- return Promise.resolve();
- };
-
- const onSelect = (element: string) => {
- setSelected([...selected, element]);
- setElements(elements.filter((e) => e !== element));
- };
-
- const onUnselect = (element: string) => {
- setElements([...elements, element]);
- setSelected(selected.filter((e) => e !== element));
- };
- return (
- <MultiSelect
- selectedElements={selected}
- elements={elements}
- legend="multi select"
- onSearch={onSearch}
- onSelect={onSelect}
- onUnselect={onUnselect}
- placeholder="search"
- {...props}
- />
- );
- }
-
- const { rerender } = render(<Parent {...override} />);
- return function (reoverride?: Partial<MultiSelect['props']>) {
- rerender(<Parent {...override} {...reoverride} />);
- };
-}
diff --git a/server/sonar-web/src/main/js/components/controls/ActionsDropdown.tsx b/server/sonar-web/src/main/js/components/controls/ActionsDropdown.tsx
deleted file mode 100644
index 28e5987cb72..00000000000
--- a/server/sonar-web/src/main/js/components/controls/ActionsDropdown.tsx
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2024 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 classNames from 'classnames';
-import * as React from 'react';
-import { To } from 'react-router-dom';
-import Link from '../common/Link';
-import DropdownIcon from '../icons/DropdownIcon';
-import SettingsIcon from '../icons/SettingsIcon';
-import { PopupPlacement } from '../ui/popups';
-import Dropdown from './Dropdown';
-import Tooltip, { Placement } from './Tooltip';
-import { Button, ButtonPlain } from './buttons';
-
-export interface ActionsDropdownProps {
- className?: string;
- children: React.ReactNode;
- label?: string;
- onOpen?: () => void;
- overlayPlacement?: PopupPlacement;
- small?: boolean;
- toggleClassName?: string;
- disabled?: boolean;
-}
-
-export default function ActionsDropdown(props: ActionsDropdownProps) {
- const { children, className, label, overlayPlacement, small, toggleClassName } = props;
- return (
- <Dropdown
- className={className}
- onOpen={props.onOpen}
- overlay={<ul className="menu">{children}</ul>}
- overlayPlacement={overlayPlacement}
- >
- <Button
- aria-label={label}
- className={classNames('dropdown-toggle', toggleClassName, {
- 'button-small': small,
- })}
- disabled={props.disabled}
- >
- <SettingsIcon size={small ? 12 : 14} />
- <DropdownIcon className="little-spacer-left" />
- </Button>
- </Dropdown>
- );
-}
-
-interface ItemProps {
- className?: string;
- children: React.ReactNode;
- destructive?: boolean;
- disabled?: boolean;
- label?: string;
- tooltipOverlay?: React.ReactNode;
- tooltipPlacement?: Placement;
- /** used to pass a name of downloaded file */
- download?: string;
- id?: string;
- onClick?: () => void;
- to?: To;
-}
-
-export class ActionsDropdownItem extends React.PureComponent<ItemProps> {
- handleClick = (event?: React.SyntheticEvent<HTMLAnchorElement>) => {
- if (event) {
- event.preventDefault();
- event.currentTarget.blur();
- }
- if (this.props.onClick) {
- this.props.onClick();
- }
- };
-
- render() {
- const className = classNames(this.props.className, { 'text-danger': this.props.destructive });
- let { children } = this.props;
- const { tooltipOverlay, tooltipPlacement, label } = this.props;
-
- if (this.props.download && typeof this.props.to === 'string') {
- children = (
- <a
- className={className}
- aria-label={label}
- download={this.props.download}
- href={this.props.to}
- id={this.props.id}
- >
- {children}
- </a>
- );
- } else if (this.props.to) {
- children = (
- <Link className={className} id={this.props.id} to={this.props.to} aria-label={label}>
- {children}
- </Link>
- );
- } else {
- children = (
- <ButtonPlain
- className={className}
- disabled={this.props.disabled}
- preventDefault
- id={this.props.id}
- onClick={this.handleClick}
- aria-label={label}
- >
- {children}
- </ButtonPlain>
- );
- }
-
- if (tooltipOverlay !== undefined) {
- return (
- <Tooltip overlay={tooltipOverlay} placement={tooltipPlacement}>
- <li>{children}</li>
- </Tooltip>
- );
- }
-
- return <li>{children}</li>;
- }
-}
-
-export function ActionsDropdownDivider() {
- return <li className="divider" />;
-}
diff --git a/server/sonar-web/src/main/js/components/controls/BoxedGroupAccordion.tsx b/server/sonar-web/src/main/js/components/controls/BoxedGroupAccordion.tsx
deleted file mode 100644
index c8e14b63984..00000000000
--- a/server/sonar-web/src/main/js/components/controls/BoxedGroupAccordion.tsx
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2024 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 classNames from 'classnames';
-import { uniqueId } from 'lodash';
-import * as React from 'react';
-import OpenCloseIcon from '../icons/OpenCloseIcon';
-import { ButtonPlain } from './buttons';
-
-interface BoxedGroupAccordionProps {
- children: React.ReactNode;
- noBorder?: boolean;
- className?: string;
- data?: string;
- onClick: (data?: string) => void;
- open: boolean;
- renderHeader?: () => React.ReactNode;
- title: React.ReactNode;
-}
-
-export default function BoxedGroupAccordion(props: BoxedGroupAccordionProps) {
- const { className, noBorder, open, renderHeader, title, data, onClick } = props;
-
- const id = React.useMemo(() => uniqueId('accordion-'), []);
- const handleClick = React.useCallback(() => {
- onClick(data);
- }, [onClick, data]);
-
- return (
- <div
- className={classNames('boxed-group boxed-group-accordion', className, {
- 'no-border': noBorder,
- open,
- })}
- role="listitem"
- >
- {/* eslint-disable-next-line jsx-a11y/no-static-element-interactions */}
- <div onClick={handleClick} className="display-flex-center boxed-group-header">
- <ButtonPlain
- stopPropagation
- className="boxed-group-accordion-title flex-grow"
- onClick={handleClick}
- id={`${id}-header`}
- aria-controls={`${id}-panel`}
- aria-expanded={open}
- >
- {title}
- </ButtonPlain>
- {renderHeader && renderHeader()}
- <OpenCloseIcon aria-hidden className="spacer-left" open={open} />
- </div>
- <div id={`${id}-panel`} aria-labelledby={`${id}-header`} role="region">
- {open && <div className="boxed-group-inner">{props.children}</div>}
- </div>
- </div>
- );
-}
diff --git a/server/sonar-web/src/main/js/components/controls/BoxedTabs.tsx b/server/sonar-web/src/main/js/components/controls/BoxedTabs.tsx
deleted file mode 100644
index e53321cd490..00000000000
--- a/server/sonar-web/src/main/js/components/controls/BoxedTabs.tsx
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2024 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 styled from '@emotion/styled';
-import * as React from 'react';
-import { colors, sizes } from '../../app/theme';
-
-export interface BoxedTabsProps<K> {
- className?: string;
- onSelect: (key: K) => void;
- selected?: K;
- tabs: ReadonlyArray<{ key: K; label: React.ReactNode }>;
-}
-
-const TabContainer = styled.div`
- display: flex;
- flex-direction: row;
-`;
-
-const baseBorder = () => `1px solid ${colors.barBorderColor}`;
-
-const highlightHoverMixin = () => `
- &:hover {
- background-color: ${colors.barBackgroundColorHighlight};
- }
-`;
-
-const StyledTab = styled.button<{ active: boolean }>`
- position: relative;
- background-color: ${(props) => (props.active ? 'white' : colors.barBackgroundColor)};
- border-top: ${baseBorder};
- border-left: ${baseBorder};
- border-right: none;
- border-bottom: none;
- margin-bottom: -1px;
- min-width: 128px;
- min-height: 56px;
- ${(props) => !props.active && 'cursor: pointer;'}
- outline: 0;
- padding: calc(2 * ${sizes.gridSize});
-
- ${(props) => (!props.active ? highlightHoverMixin : null)}
-
- &:last-child {
- border-right: ${baseBorder};
- }
-`;
-
-const ActiveBorder = styled.div<{ active: boolean }>`
- display: ${(props) => (props.active ? 'block' : 'none')};
- background-color: ${colors.blue};
- height: 3px;
- width: 100%;
- position: absolute;
- left: 0;
- top: -1px;
-`;
-
-export default function BoxedTabs<K>(props: BoxedTabsProps<K>) {
- const { className, tabs, selected } = props;
-
- return (
- <TabContainer className={className} role="tablist">
- {tabs.map(({ key, label }, i) => (
- <StyledTab
- id={getTabId(key)}
- active={selected === key}
- aria-selected={selected === key}
- aria-controls={getTabPanelId(key)}
- // eslint-disable-next-line react/no-array-index-key
- key={i}
- onClick={() => selected !== key && props.onSelect(key)}
- role="tab"
- >
- <ActiveBorder active={selected === key} />
- {label}
- </StyledTab>
- ))}
- </TabContainer>
- );
-}
-
-export function getTabPanelId<K>(key: K) {
- return `tabpanel-${key}`;
-}
-
-export function getTabId<K>(key: K) {
- return `tab-${key}`;
-}
diff --git a/server/sonar-web/src/main/js/components/controls/ButtonToggle.css b/server/sonar-web/src/main/js/components/controls/ButtonToggle.css
deleted file mode 100644
index 1c2e00f07fe..00000000000
--- a/server/sonar-web/src/main/js/components/controls/ButtonToggle.css
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2024 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.
- */
-.button-toggle {
- display: flex;
- list-style: none !important;
-}
-
-.button-toggle li:not(:first-child) {
- margin-left: -1px;
-}
diff --git a/server/sonar-web/src/main/js/components/controls/ButtonToggle.tsx b/server/sonar-web/src/main/js/components/controls/ButtonToggle.tsx
deleted file mode 100644
index bfcfdf8140a..00000000000
--- a/server/sonar-web/src/main/js/components/controls/ButtonToggle.tsx
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2024 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 classNames from 'classnames';
-import * as React from 'react';
-import { Button } from './buttons';
-import './ButtonToggle.css';
-
-export type ButtonToggleValueType = string | number | boolean;
-
-export interface ButtonToggleOption {
- label: string;
- value: ButtonToggleValueType;
-}
-
-export interface ButtonToggleProps {
- label?: string;
- disabled?: boolean;
- options: ButtonToggleOption[];
- value?: ButtonToggleValueType;
- onCheck: (value: ButtonToggleValueType) => void;
-}
-
-export default function ButtonToggle(props: ButtonToggleProps) {
- const { disabled, label, options, value } = props;
-
- return (
- <ul aria-label={label} className="button-toggle">
- {options.map((option) => (
- <li key={option.value.toString()}>
- <Button
- onClick={() => option.value !== value && props.onCheck(option.value)}
- disabled={disabled}
- aria-current={option.value === value}
- data-value={option.value}
- className={classNames({ selected: option.value === value })}
- >
- {option.label}
- </Button>
- </li>
- ))}
- </ul>
- );
-}
diff --git a/server/sonar-web/src/main/js/components/controls/Checkbox.css b/server/sonar-web/src/main/js/components/controls/Checkbox.css
deleted file mode 100644
index 982bc8bcbd3..00000000000
--- a/server/sonar-web/src/main/js/components/controls/Checkbox.css
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2024 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.
- */
-.icon-checkbox {
- display: inline-block;
- line-height: 1;
- vertical-align: top;
- padding: 1px 2px;
- box-sizing: border-box;
-}
-
-a.icon-checkbox {
- border-bottom: none;
-}
-
-.icon-checkbox:focus {
- outline: none !important;
-}
-
-.icon-checkbox:before {
- content: ' ';
- display: inline-block;
- width: 10px;
- height: 10px;
- border: 1px solid var(--darkBlue);
- border-radius: 2px;
- transition:
- border-color 0.2s ease,
- background-color 0.2s ease,
- background-image 0.2s ease,
- box-shadow 0.4s ease;
-}
-
-.icon-checkbox:not(.icon-checkbox-disabled):focus:before,
-.link-checkbox:not(.disabled):focus:focus .icon-checkbox:before {
- box-shadow: 0 0 0 3px rgba(35, 106, 151, 0.25);
-}
-
-.icon-checkbox-checked:before {
- background-color: var(--blue);
- background-image: url('data:image/svg+xml,%3Csvg%20viewBox%3D%220%200%2014%2014%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20fill-rule%3D%22evenodd%22%20clip-rule%3D%22evenodd%22%20stroke-linejoin%3D%22round%22%20stroke-miterlimit%3D%221.414%22%3E%3Cpath%20d%3D%22M12%204.665c0%20.172-.06.318-.18.438l-5.55%205.55c-.12.12-.266.18-.438.18s-.318-.06-.438-.18L2.18%207.438C2.06%207.317%202%207.17%202%207s.06-.318.18-.44l.878-.876c.12-.12.267-.18.44-.18.17%200%20.317.06.437.18l1.897%201.903%204.233-4.24c.12-.12.266-.18.438-.18s.32.06.44.18l.876.88c.12.12.18.265.18.438z%22%20fill%3D%22%23fff%22%20fill-rule%3D%22nonzero%22%2F%3E%3C%2Fsvg%3E');
- border-color: var(--blue);
-}
-
-.icon-checkbox-checked.icon-checkbox-single:before {
- background-image: url('data:image/svg+xml,%3Csvg%20viewBox%3D%220%200%2014%2014%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20fill-rule%3D%22evenodd%22%20clip-rule%3D%22evenodd%22%20stroke-linejoin%3D%22round%22%20stroke-miterlimit%3D%221.414%22%3E%3Cpath%20d%3D%22M10%204.698C10%204.312%209.688%204%209.302%204H4.698C4.312%204%204%204.312%204%204.698v4.604c0%20.386.312.698.698.698h4.604c.386%200%20.698-.312.698-.698V4.698z%22%20fill%3D%22%23fff%22%2F%3E%3C%2Fsvg%3E');
-}
-
-.icon-checkbox-disabled:before {
- border: 1px solid var(--disableGrayText);
- cursor: not-allowed;
-}
-
-.icon-checkbox-disabled.icon-checkbox-checked:before {
- background-color: var(--disableGrayText);
-}
-
-.icon-checkbox-invisible {
- visibility: hidden;
-}
-
-.link-checkbox {
- color: inherit;
- border-bottom: none;
-}
-
-.link-checkbox.disabled,
-.link-checkbox.disabled:hover,
-.link-checkbox.disabled label {
- color: var(--secondFontColor);
- cursor: not-allowed;
-}
-
-.link-checkbox:hover,
-.link-checkbox:active,
-.link-checkbox:focus {
- color: inherit;
-}
diff --git a/server/sonar-web/src/main/js/components/controls/Checkbox.tsx b/server/sonar-web/src/main/js/components/controls/Checkbox.tsx
deleted file mode 100644
index f98878c66bf..00000000000
--- a/server/sonar-web/src/main/js/components/controls/Checkbox.tsx
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2024 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.
- */
-/* eslint-disable jsx-a11y/anchor-has-content */
-/* eslint-disable jsx-a11y/control-has-associated-label */
-
-import classNames from 'classnames';
-import * as React from 'react';
-import Spinner from '../ui/Spinner';
-import './Checkbox.css';
-
-interface Props {
- checked: boolean;
- disabled?: boolean;
- children?: React.ReactNode;
- className?: string;
- id?: string;
- label?: string;
- loading?: boolean;
- onCheck: (checked: boolean, id?: string) => void;
- right?: boolean;
- thirdState?: boolean;
- title?: string;
-}
-
-export default class Checkbox extends React.PureComponent<Props> {
- static defaultProps = {
- thirdState: false,
- };
-
- handleClick = (event: React.SyntheticEvent<HTMLElement>) => {
- event.preventDefault();
- event.currentTarget.blur();
- if (!this.props.disabled) {
- this.props.onCheck(!this.props.checked, this.props.id);
- }
- };
-
- render() {
- const { checked, children, disabled, id, label, loading, right, thirdState, title } =
- this.props;
- const className = classNames('icon-checkbox', {
- 'icon-checkbox-checked': checked,
- 'icon-checkbox-single': thirdState,
- 'icon-checkbox-disabled': disabled,
- });
-
- if (children) {
- return (
- <a
- aria-checked={thirdState ? 'mixed' : checked}
- aria-label={label}
- aria-disabled={disabled}
- className={classNames('link-checkbox', this.props.className, {
- disabled,
- })}
- href="#"
- id={id}
- onClick={this.handleClick}
- role="checkbox"
- title={title}
- >
- {right && children}
- <Spinner loading={Boolean(loading)}>
- <i className={className} />
- </Spinner>
- {!right && children}
- </a>
- );
- }
-
- if (loading) {
- return <Spinner ariaLabel={label} />;
- }
-
- return (
- <a
- aria-checked={thirdState ? 'mixed' : checked}
- aria-label={label}
- aria-disabled={disabled}
- className={classNames(className, this.props.className)}
- href="#"
- id={id}
- onClick={this.handleClick}
- role="checkbox"
- title={title}
- />
- );
- }
-}
diff --git a/server/sonar-web/src/main/js/components/controls/Dropdown.css b/server/sonar-web/src/main/js/components/controls/Dropdown.css
deleted file mode 100644
index 0bf8a21f41d..00000000000
--- a/server/sonar-web/src/main/js/components/controls/Dropdown.css
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2024 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.
- */
-.dropdown {
- position: relative;
- display: inline-block;
- vertical-align: middle;
-}
-
-.dropdown-bottom-hint {
- line-height: 16px;
- margin-bottom: -5px;
- padding: 5px 10px;
- border-top: 1px solid var(--barBorderColor);
- background-color: var(--barBackgroundColor);
- color: var(--secondFontColor);
- font-size: 11px;
-}
diff --git a/server/sonar-web/src/main/js/components/controls/Dropdown.tsx b/server/sonar-web/src/main/js/components/controls/Dropdown.tsx
index 4388f81add9..96cf2efb049 100644
--- a/server/sonar-web/src/main/js/components/controls/Dropdown.tsx
+++ b/server/sonar-web/src/main/js/components/controls/Dropdown.tsx
@@ -17,103 +17,9 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import classNames from 'classnames';
import * as React from 'react';
import { Popup, PopupPlacement } from '../ui/popups';
-import './Dropdown.css';
import ScreenPositionFixer from './ScreenPositionFixer';
-import Toggler from './Toggler';
-
-interface OnClickCallback {
- (event?: React.SyntheticEvent<HTMLElement>): void;
-}
-
-interface RenderProps {
- closeDropdown: () => void;
- onToggleClick: OnClickCallback;
- open: boolean;
-}
-
-interface Props {
- children:
- | ((renderProps: RenderProps) => JSX.Element)
- | React.ReactElement<{ onClick: OnClickCallback }>;
- className?: string;
- closeOnClick?: boolean;
- closeOnClickOutside?: boolean;
- onOpen?: () => void;
- overlay: React.ReactNode;
- overlayPlacement?: PopupPlacement;
- noOverlayPadding?: boolean;
- tagName?: string;
-}
-
-interface State {
- open: boolean;
-}
-
-export default class Dropdown extends React.PureComponent<Props, State> {
- state: State = { open: false };
-
- componentDidUpdate(_: Props, prevState: State) {
- if (!prevState.open && this.state.open && this.props.onOpen) {
- this.props.onOpen();
- }
- }
-
- closeDropdown = () => this.setState({ open: false });
-
- handleToggleClick = (event?: React.SyntheticEvent<HTMLElement>) => {
- if (event) {
- event.preventDefault();
- event.currentTarget.blur();
- }
- this.setState((state) => ({ open: !state.open }));
- };
-
- render() {
- const a11yAttrs = {
- 'aria-expanded': String(this.state.open),
- 'aria-haspopup': 'true',
- };
-
- const child = React.isValidElement(this.props.children)
- ? React.cloneElement(this.props.children, { onClick: this.handleToggleClick, ...a11yAttrs })
- : this.props.children({
- closeDropdown: this.closeDropdown,
- onToggleClick: this.handleToggleClick,
- open: this.state.open,
- });
-
- const { closeOnClick = true, closeOnClickOutside = false } = this.props;
-
- const toggler = (
- <Toggler
- closeOnClick={closeOnClick}
- closeOnClickOutside={closeOnClickOutside}
- onRequestClose={this.closeDropdown}
- open={this.state.open}
- overlay={
- <DropdownOverlay
- noPadding={this.props.noOverlayPadding}
- placement={this.props.overlayPlacement}
- useEventBoundary={!closeOnClick}
- >
- {this.props.overlay}
- </DropdownOverlay>
- }
- >
- {child}
- </Toggler>
- );
-
- return React.createElement(
- this.props.tagName || 'div',
- { className: classNames('dropdown', this.props.className) },
- toggler,
- );
- }
-}
interface OverlayProps {
className?: string;
diff --git a/server/sonar-web/src/main/js/components/controls/HelpTooltip.css b/server/sonar-web/src/main/js/components/controls/HelpTooltip.css
deleted file mode 100644
index 68ec76d3b06..00000000000
--- a/server/sonar-web/src/main/js/components/controls/HelpTooltip.css
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2024 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.
- */
-.help-tooltip {
- display: inline-flex;
- align-items: center;
- vertical-align: middle;
-}
-
-.help-toolip-link {
- display: block;
- width: 12px;
- height: 12px;
- border: none;
-}
diff --git a/server/sonar-web/src/main/js/components/controls/HelpTooltip.tsx b/server/sonar-web/src/main/js/components/controls/HelpTooltip.tsx
index 3dc45af16c8..1105fef9a03 100644
--- a/server/sonar-web/src/main/js/components/controls/HelpTooltip.tsx
+++ b/server/sonar-web/src/main/js/components/controls/HelpTooltip.tsx
@@ -18,15 +18,12 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import classNames from 'classnames';
+import { HelperHintIcon } from 'design-system';
import * as React from 'react';
-import { colors } from '../../app/theme';
import { translate } from '../../helpers/l10n';
-import HelpIcon from '../icons/HelpIcon';
-import { IconProps } from '../icons/Icon';
-import './HelpTooltip.css';
import Tooltip, { Placement } from './Tooltip';
-interface Props extends Pick<IconProps, 'size'> {
+interface Props {
className?: string;
children?: React.ReactNode;
onShow?: () => void;
@@ -35,6 +32,7 @@ interface Props extends Pick<IconProps, 'size'> {
placement?: Placement;
isInteractive?: boolean;
innerRef?: React.Ref<HTMLSpanElement>;
+ size?: number;
}
const DEFAULT_SIZE = 12;
@@ -42,7 +40,12 @@ const DEFAULT_SIZE = 12;
export default function HelpTooltip(props: Props) {
const { size = DEFAULT_SIZE, overlay, placement, isInteractive, innerRef, children } = props;
return (
- <div className={classNames('help-tooltip', props.className)}>
+ <div
+ className={classNames(
+ 'it__help-tooltip sw-inline-flex sw-items-center sw-align-middle',
+ props.className,
+ )}
+ >
<Tooltip
mouseLeaveDelay={0.25}
onShow={props.onShow}
@@ -52,15 +55,12 @@ export default function HelpTooltip(props: Props) {
isInteractive={isInteractive}
>
<span
- className="display-inline-flex-center"
+ className="sw-inline-flex sw-items-center"
data-testid="help-tooltip-activator"
ref={innerRef}
>
{children ?? (
- <HelpIcon
- fill={colors.gray60}
- size={size}
- role="img"
+ <HelperHintIcon
aria-label={isInteractive ? translate('tooltip_is_interactive') : translate('help')}
description={
isInteractive ? (
@@ -72,6 +72,8 @@ export default function HelpTooltip(props: Props) {
overlay
)
}
+ height={size}
+ width={size}
/>
)}
</span>
@@ -79,11 +81,3 @@ export default function HelpTooltip(props: Props) {
</div>
);
}
-
-export function DarkHelpTooltip({ size = DEFAULT_SIZE, ...props }: Omit<Props, 'children'>) {
- return (
- <HelpTooltip {...props}>
- <HelpIcon fill={colors.transparentBlack} fillInner={colors.white} size={size} />
- </HelpTooltip>
- );
-}
diff --git a/server/sonar-web/src/main/js/components/controls/IdentityProviderLink.css b/server/sonar-web/src/main/js/components/controls/IdentityProviderLink.css
deleted file mode 100644
index 8230ef0473b..00000000000
--- a/server/sonar-web/src/main/js/components/controls/IdentityProviderLink.css
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2024 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.
- */
-a.identity-provider-link {
- display: block;
- line-height: 22px;
- padding: var(--gridSize) calc(1.5 * var(--gridSize));
- border: 1px solid rgba(0, 0, 0, 0.15);
- border-radius: 2px;
- box-sizing: border-box;
- background-color: var(--darkBlue);
- color: #fff;
- white-space: nowrap;
-}
-
-a.identity-provider-link.small {
- line-height: 14px;
- padding: calc(var(--gridSize) / 2) var(--gridSize);
-}
-
-a.identity-provider-link:hover,
-a.identity-provider-link:focus {
- box-shadow: inset 0 0 0 100px rgba(255, 255, 255, 0.1);
-}
-
-a.identity-provider-link.dark-text {
- color: var(--secondFontColor);
-}
-
-a.identity-provider-link.dark-text:hover,
-a.identity-provider-link.dark-text:focus {
- box-shadow: inset 0 0 0 100px rgba(0, 0, 0, 0.1);
-}
-
-a.identity-provider-link > img {
- padding-right: calc(1.5 * var(--gridSize));
-}
-
-a.identity-provider-link.small > img {
- padding-right: var(--gridSize);
-}
-
-a.identity-provider-link > span::before {
- content: '';
- opacity: 0.4;
- border-left: 1px var(--gray71) solid;
- margin-right: calc(1.5 * var(--gridSize));
-}
diff --git a/server/sonar-web/src/main/js/components/controls/IdentityProviderLink.tsx b/server/sonar-web/src/main/js/components/controls/IdentityProviderLink.tsx
deleted file mode 100644
index 1757e49876a..00000000000
--- a/server/sonar-web/src/main/js/components/controls/IdentityProviderLink.tsx
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2024 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 classNames from 'classnames';
-import { isDarkColor } from 'design-system';
-import * as React from 'react';
-import { getBaseUrl } from '../../helpers/system';
-import './IdentityProviderLink.css';
-
-interface Props {
- backgroundColor: string;
- children: React.ReactNode;
- className?: string;
- iconPath: string;
- name: string;
- onClick?: () => void;
- small?: boolean;
- url: string | undefined;
-}
-
-export default function IdentityProviderLink({
- backgroundColor,
- children,
- className,
- iconPath,
- name,
- onClick,
- small,
- url,
-}: Props) {
- const size = small ? 14 : 20;
-
- return (
- <a
- className={classNames(
- 'identity-provider-link',
- { 'dark-text': !isDarkColor(backgroundColor), small },
- className,
- )}
- href={url}
- onClick={onClick}
- style={{ backgroundColor }}
- >
- <img alt={name} height={size} src={getBaseUrl() + iconPath} width={size} />
- {children}
- </a>
- );
-}
diff --git a/server/sonar-web/src/main/js/components/controls/ListFooter.tsx b/server/sonar-web/src/main/js/components/controls/ListFooter.tsx
index ce03c60eb7a..70071fb047d 100644
--- a/server/sonar-web/src/main/js/components/controls/ListFooter.tsx
+++ b/server/sonar-web/src/main/js/components/controls/ListFooter.tsx
@@ -24,8 +24,6 @@ import * as React from 'react';
import { translate, translateWithParameters } from '../../helpers/l10n';
import { formatMeasure } from '../../helpers/measures';
import { MetricType } from '../../types/metrics';
-import LegacySpinner from '../ui/Spinner';
-import { Button } from './buttons';
export interface ListFooterProps {
loadMoreAriaLabel?: string;
@@ -38,7 +36,6 @@ export interface ListFooterProps {
reload?: () => void;
ready?: boolean;
total?: number;
- useMIUIButtons?: boolean;
}
export default function ListFooter(props: ListFooterProps) {
@@ -52,7 +49,6 @@ export default function ListFooter(props: ListFooterProps) {
total,
pageSize,
ready = true,
- useMIUIButtons = false,
} = props;
const rootNode = React.useRef<HTMLDivElement>(null);
@@ -76,27 +72,27 @@ export default function ListFooter(props: ListFooterProps) {
let button;
if (needReload && props.reload) {
- button = React.createElement(
- useMIUIButtons ? ButtonSecondary : Button,
- {
- 'data-test': 'reload',
- className: classNames('sw-ml-2', { 'sw-body-sm': useMIUIButtons }),
- disabled: loading,
- onClick: props.reload,
- } as Button['props'],
- translate('reload'),
+ button = (
+ <ButtonSecondary
+ data-test="reload"
+ className="sw-ml-2 sw-body-sm"
+ disabled={loading}
+ onClick={props.reload}
+ >
+ {translate('reload')}
+ </ButtonSecondary>
);
} else if (hasMore && props.loadMore) {
- button = React.createElement(
- useMIUIButtons ? ButtonSecondary : Button,
- {
- 'aria-label': loadMoreAriaLabel,
- 'data-test': 'show-more',
- className: classNames('sw-ml-2', { 'sw-body-sm': useMIUIButtons }),
- disabled: loading,
- onClick: onLoadMore,
- } as Button['props'],
- translate('show_more'),
+ button = (
+ <ButtonSecondary
+ aria-label={loadMoreAriaLabel}
+ data-test="show-more"
+ className="sw-ml-2 sw-body-sm"
+ disabled={loading}
+ onClick={onLoadMore}
+ >
+ {translate('show_more')}
+ </ButtonSecondary>
);
}
@@ -121,12 +117,7 @@ export default function ListFooter(props: ListFooterProps) {
: translateWithParameters('x_show', formatMeasure(count, MetricType.Integer))}
</span>
{button}
- {/* eslint-disable local-rules/no-conditional-rendering-of-deferredspinner */}
- {useMIUIButtons ? (
- <Spinner loading={loading} className="sw-ml-2" />
- ) : (
- <LegacySpinner loading={loading} className="sw-ml-2" />
- )}
+ <Spinner loading={loading} className="sw-ml-2" />
</StyledDiv>
);
}
diff --git a/server/sonar-web/src/main/js/components/controls/Modal.css b/server/sonar-web/src/main/js/components/controls/Modal.css
deleted file mode 100644
index e193ffbef95..00000000000
--- a/server/sonar-web/src/main/js/components/controls/Modal.css
+++ /dev/null
@@ -1,228 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2024 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.
- */
-.modal,
-.ReactModal__Content {
- position: fixed;
- z-index: var(--modalZIndex);
- top: 0;
- left: 50%;
- margin-left: -270px;
- width: 540px;
- background-color: #fff;
- opacity: 0;
- transition: all 0.2s ease;
- border-radius: 3px;
-}
-
-.modal:focus,
-.ReactModal__Content:focus {
- outline: none;
-}
-
-.modal.in,
-.ReactModal__Content--after-open {
- top: 15%;
- opacity: 1;
-}
-
-.modal-small {
- width: 450px;
- margin-left: -225px;
-}
-
-.modal-medium {
- width: 830px;
- margin-left: -415px;
-}
-
-.modal-large {
- width: calc(100% - 40px);
- max-width: 1280px;
- min-width: 1040px;
- margin-left: 0;
- transform: translateX(-50%);
-}
-
-.modal-overlay,
-.ReactModal__Overlay {
- position: fixed;
- z-index: var(--modalOverlayZIndex);
- top: 0;
- bottom: 0;
- left: 0;
- right: 0;
- background-color: rgba(0, 0, 0, 0.7);
- opacity: 0;
- transition: all 0.2s ease;
-}
-
-.modal-overlay.in,
-.ReactModal__Overlay--after-open {
- opacity: 1;
-}
-
-.modal-no-backdrop {
- background-color: transparent;
-}
-
-.modal-open,
-.ReactModal__Body--open {
- overflow: hidden;
- margin-right: var(--sbw);
-}
-
-.modal-head {
- padding: calc(4 * var(--gridSize));
- padding-bottom: 0;
-}
-
-.modal-head h1,
-.modal-head h2 {
- margin: 0;
- font-size: var(--bigFontSize);
- font-weight: bold;
- line-height: normal;
- overflow-wrap: break-word;
-}
-
-.modal-body {
- padding: var(--pagePadding) calc(4 * var(--gridSize));
-}
-
-.modal-container {
- max-height: 60vh;
- box-sizing: border-box;
- overflow-y: auto;
- border-top: 1px solid var(--barBorderColor);
- margin-top: var(--pagePadding);
- padding-right: calc(4 * var(--gridSize));
-}
-
-.modal-container > :last-child {
- margin-bottom: var(--pagePadding);
-}
-
-.modal-field,
-.modal-validation-field {
- clear: both;
- display: block;
- padding: 0;
- margin-bottom: calc(var(--gridSize) * 2);
-}
-
-.modal-field label,
-.modal-validation-field label,
-.modal-field legend {
- display: block;
- font-weight: bold;
- padding-bottom: calc(var(--gridSize) / 2);
-}
-
-.modal-field a.icon-checkbox,
-.modal-field input,
-.modal-field select,
-.modal-field textarea,
-.modal-field .Select {
- margin-right: 5px;
-}
-
-.modal-field a.icon-checkbox {
- height: 24px;
-}
-
-.modal-field input[type='radio'],
-.modal-field input[type='checkbox'] {
- margin-top: 5px;
- margin-bottom: 4px;
-}
-
-.modal-field > .icon-checkbox {
- padding-top: 6px;
- padding-right: 8px;
-}
-
-.modal-field input[type='text'],
-.modal-field input[type='email'],
-.modal-field input[type='password'],
-.modal-field textarea,
-.modal-field select,
-.modal-field .Select {
- width: 100%;
-}
-
-.modal-validation-field input,
-.modal-validation-field textarea,
-.modal-validation-field .Select {
- margin-right: var(--gridSize);
- margin-bottom: 2px;
- width: calc(100% - 3 * var(--gridSize));
-}
-
-.modal-field textarea,
-.modal-validation-field textarea {
- 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: calc(var(--tinyControlHeight) + 2px);
-}
-
-.modal-field-description {
- line-height: 1.4;
- color: var(--secondFontColor);
- font-size: var(--smallFontSize);
- overflow: hidden;
- text-overflow: ellipsis;
- margin-top: 2px;
-}
-
-.modal-field input[type='text'].invalid {
- border-color: var(--red);
-}
-
-.modal-foot {
- 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,
-.modal-foot .button,
-.modal-foot input[type='submit'],
-.modal-foot input[type='button'] {
- margin-left: var(--gridSize);
-}
-
-.modal-foot button:first-of-type,
-.modal-foot .button:first-of-type,
-.modal-foot input[type='submit']:first-of-type,
-.modal-foot input[type='button']:first-of-type {
- margin-left: 0;
-}
-
-.modal-foot-clear {
- border-top: 0;
- background-color: transparent;
-}
diff --git a/server/sonar-web/src/main/js/components/controls/Modal.tsx b/server/sonar-web/src/main/js/components/controls/Modal.tsx
deleted file mode 100644
index f3c391f5cd6..00000000000
--- a/server/sonar-web/src/main/js/components/controls/Modal.tsx
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2024 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 classNames from 'classnames';
-import * as React from 'react';
-import ReactModal from 'react-modal';
-import { getReactDomContainerSelector } from '../../helpers/system';
-import './Modal.css';
-
-ReactModal.setAppElement(getReactDomContainerSelector());
-
-export interface ModalProps {
- children: React.ReactNode;
- size?: 'small' | 'medium' | 'large';
- noBackdrop?: boolean;
-}
-
-interface Props extends ModalProps {
- /* String or object className to be applied to the modal content. */
- className?: string;
-
- /* String or object className to be applied to the overlay. */
- overlayClassName?: string;
-
- /* Function that will be run after the modal has opened. */
- onAfterOpen?(): void;
-
- /* Function that will be run after the modal has closed. */
- onAfterClose?(): void;
-
- /* Function that will be run when the modal is requested to be closed, prior to actually closing. */
- onRequestClose?(event: React.MouseEvent | React.KeyboardEvent): void;
-
- /* Boolean indicating if the modal should be focused after render */
- shouldFocusAfterRender?: boolean;
-
- /* Boolean indicating if the overlay should close the modal. Defaults to true. */
- shouldCloseOnOverlayClick?: boolean;
-
- /* Boolean indicating if pressing the esc key should close the modal */
- shouldCloseOnEsc?: boolean;
-
- /* String indicating how the content container should be announced to screenreaders. */
- contentLabel?: string;
-}
-
-export default function Modal(props: Props) {
- return (
- <ReactModal
- className={classNames('modal', {
- 'modal-small': props.size === 'small',
- 'modal-medium': props.size === 'medium',
- 'modal-large': props.size === 'large',
- })}
- isOpen
- overlayClassName={classNames('modal-overlay', { 'modal-no-backdrop': props.noBackdrop })}
- {...props}
- />
- );
-}
diff --git a/server/sonar-web/src/main/js/components/controls/Radio.css b/server/sonar-web/src/main/js/components/controls/Radio.css
deleted file mode 100644
index 19c19819d5c..00000000000
--- a/server/sonar-web/src/main/js/components/controls/Radio.css
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2024 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.
- */
-.icon-radio {
- position: relative;
- display: inline-block;
- vertical-align: top;
- width: 14px;
- height: 14px;
- margin: 1px;
- border: 1px solid var(--gray60);
- border-radius: 12px;
- box-sizing: border-box;
- transition: border-color 0.3s ease;
- flex-shrink: 0;
-}
-
-.icon-radio:after {
- position: absolute;
- top: 2px;
- left: 2px;
- display: block;
- width: 8px;
- height: 8px;
- border-radius: 8px;
- background-color: var(--darkBlue);
- content: '';
- opacity: 0;
- transition: opacity 0.3s ease;
-}
-
-.link-radio .icon-radio.is-checked:after {
- opacity: 1;
-}
-
-.link-radio {
- color: inherit;
- border-bottom: none;
-}
-
-.link-radio:not(.disabled):hover,
-.link-radio:not(.disabled):active,
-.link-radio:not(.disabled):focus {
- color: inherit;
-}
-
-.link-radio:not(.disabled):hover > .icon-radio {
- border-color: var(--blue);
-}
-
-.link-radio.disabled,
-.link-radio.disabled:hover,
-.link-radio.disabled label {
- color: var(--disableGrayText);
- cursor: not-allowed;
-}
-
-.link-radio.disabled .icon-radio:after {
- background-color: var(--disableGrayBg);
-}
diff --git a/server/sonar-web/src/main/js/components/controls/Radio.tsx b/server/sonar-web/src/main/js/components/controls/Radio.tsx
deleted file mode 100644
index 0d6a505987e..00000000000
--- a/server/sonar-web/src/main/js/components/controls/Radio.tsx
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2024 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 classNames from 'classnames';
-import * as React from 'react';
-import './Radio.css';
-
-interface Props {
- checked: boolean;
- className?: string;
- alignLabel?: boolean;
- disabled?: boolean;
- onCheck: (value: string) => void;
- value: string;
- ariaLabel?: string;
-}
-
-export default class Radio extends React.PureComponent<React.PropsWithChildren<Props>> {
- handleClick = (event: React.MouseEvent<HTMLAnchorElement>) => {
- event.preventDefault();
-
- if (!this.props.disabled) {
- this.props.onCheck(this.props.value);
- }
- };
-
- render() {
- const { className, checked, children, disabled, alignLabel = false, ariaLabel } = this.props;
-
- return (
- <a
- aria-checked={checked}
- className={classNames(
- alignLabel ? 'display-inline-flex-start' : 'display-inline-flex-center',
- 'link-radio',
- className,
- { disabled },
- )}
- href="#"
- onClick={this.handleClick}
- role="radio"
- aria-label={ariaLabel}
- >
- <i className={classNames('icon-radio', 'spacer-right', { 'is-checked': checked })} />
- {children}
- </a>
- );
- }
-}
diff --git a/server/sonar-web/src/main/js/components/controls/RadioCard.css b/server/sonar-web/src/main/js/components/controls/RadioCard.css
deleted file mode 100644
index 8125e7cc17a..00000000000
--- a/server/sonar-web/src/main/js/components/controls/RadioCard.css
+++ /dev/null
@@ -1,130 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2024 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.
- */
-.radio-card {
- display: flex;
- flex-direction: column;
- width: 450px;
- min-height: 210px;
- background-color: #fff;
- border: solid 1px var(--barBorderColor);
- border-radius: 3px;
- box-sizing: border-box;
- margin-right: calc(2 * var(--gridSize));
- transition: all 0.2s ease;
-}
-
-.radio-card.animated {
- height: 0;
- border-width: 0;
- overflow: hidden;
-}
-
-.radio-card.animated.open {
- height: 210px;
- border-width: 1px;
-}
-
-.radio-card.highlight {
- box-shadow: var(--defaultShadow);
-}
-
-.radio-card:last-child {
- margin-right: 0;
-}
-
-.radio-card-vertical {
- width: 100%;
- min-height: auto;
-}
-
-.radio-card-actionable {
- cursor: pointer;
-}
-
-.radio-card-actionable:not(.disabled):hover {
- box-shadow: var(--defaultShadow);
- transform: translateY(-2px);
-}
-
-.radio-card-actionable.selected {
- border-color: var(--darkBlue);
-}
-
-/*
- * Disabled transform property because it moves the element to a new stacking context
- * creating z-index conflicts with other components.
- * This is a problem with a vertical list of RadioCards where a select might be above another RadioCard
- */
-.radio-card-actionable.radio-card-vertical:not(.disabled):hover {
- box-shadow: none;
- transform: none;
-}
-
-.radio-card-actionable.radio-card-vertical:not(.selected):not(.disabled):hover {
- border-color: var(--lightBlue);
-}
-
-.radio-card-actionable.selected .radio-card-recommended {
- border: solid 1px var(--darkBlue);
- border-top: none;
-}
-
-.radio-card-actionable.disabled {
- cursor: not-allowed;
- background-color: var(--disableGrayBg);
- border-color: var(--disableGrayBorder);
-}
-
-.radio-card-actionable.disabled h2,
-.radio-card-actionable.disabled ul {
- color: var(--disableGrayText);
-}
-
-.radio-card-header {
- display: flex;
- justify-content: space-between;
- align-items: center;
- padding: calc(2 * var(--gridSize)) calc(2 * var(--gridSize)) 0;
-}
-
-.radio-card-body {
- flex-grow: 1;
- display: flex;
- flex-direction: column;
- justify-content: space-between;
- padding: 0 calc(2 * var(--gridSize)) calc(2 * var(--gridSize));
-}
-
-.radio-card-body .alert {
- margin-bottom: 0;
-}
-
-.radio-card-recommended {
- position: relative;
- padding: 6px calc(var(--gridSize) * 2);
- left: -1px;
- bottom: -1px;
- width: 450px;
- color: #fff;
- background-color: var(--blue);
- border-radius: 0 0 3px 3px;
- box-sizing: border-box;
- font-size: var(--smallFontSize);
-}
diff --git a/server/sonar-web/src/main/js/components/controls/RadioCard.tsx b/server/sonar-web/src/main/js/components/controls/RadioCard.tsx
deleted file mode 100644
index 605735ec9c0..00000000000
--- a/server/sonar-web/src/main/js/components/controls/RadioCard.tsx
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2024 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 classNames from 'classnames';
-import * as React from 'react';
-import { FormattedMessage } from 'react-intl';
-import { KeyboardKeys } from '../../helpers/keycodes';
-import { translate } from '../../helpers/l10n';
-import RecommendedIcon from '../icons/RecommendedIcon';
-import './Radio.css';
-import './RadioCard.css';
-
-export interface RadioCardProps {
- className?: string;
- disabled?: boolean;
- onClick?: () => void;
- selected?: boolean;
- noRadio?: boolean;
-}
-
-interface Props extends RadioCardProps {
- children: React.ReactNode;
- recommended?: string;
- title: React.ReactNode;
- titleInfo?: React.ReactNode;
- vertical?: boolean;
- label?: string;
-}
-
-export default function RadioCard(props: Props) {
- const {
- className,
- disabled,
- onClick,
- recommended,
- selected,
- titleInfo,
- label,
- vertical = false,
- noRadio = false,
- } = props;
- const isActionable = Boolean(onClick);
- const clickHandler = isActionable && !disabled && !selected ? onClick : undefined;
-
- const keyPressHandler = (event: React.KeyboardEvent<HTMLDivElement>) => {
- if (event.code === KeyboardKeys.Enter) {
- clickHandler?.();
- }
- };
-
- return (
- <div
- aria-checked={selected}
- className={classNames(
- 'radio-card',
- {
- 'radio-card-actionable': isActionable,
- 'radio-card-vertical': vertical,
- disabled,
- selected,
- },
- className,
- )}
- onClick={clickHandler}
- onKeyPress={keyPressHandler}
- role="radio"
- aria-label={label}
- aria-disabled={disabled}
- tabIndex={disabled ? -1 : 0}
- >
- <h2 className="radio-card-header big-spacer-bottom">
- <span className="display-flex-center link-radio">
- {isActionable && !noRadio && (
- <i className={classNames('icon-radio', 'spacer-right', { 'is-checked': selected })} />
- )}
- {props.title}
- </span>
- {titleInfo}
- </h2>
- <div className="radio-card-body">{props.children}</div>
- {recommended && (
- <div className="radio-card-recommended">
- <RecommendedIcon className="spacer-right" />
- <FormattedMessage
- defaultMessage={recommended}
- id={recommended}
- values={{ recommended: <strong>{translate('recommended')}</strong> }}
- />
- </div>
- )}
- </div>
- );
-}
diff --git a/server/sonar-web/src/main/js/components/controls/SearchBox.css b/server/sonar-web/src/main/js/components/controls/SearchBox.css
deleted file mode 100644
index c00548245cd..00000000000
--- a/server/sonar-web/src/main/js/components/controls/SearchBox.css
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2024 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.
- */
-.search-box {
- position: relative;
- display: inline-block;
- vertical-align: middle;
- font-size: 0;
- white-space: nowrap;
-}
-
-.search-box,
-.search-box-input {
- width: 100%;
- max-width: 300px;
-}
-
-.search-box-input {
- /* for magnifier icon */
- padding-left: var(--controlHeight) !important;
- /* for clear button */
- padding-right: var(--controlHeight) !important;
- font-size: var(--baseFontSize);
-}
-
-.search-box-input::-webkit-search-decoration,
-.search-box-input::-webkit-search-cancel-button,
-.search-box-input::-webkit-search-results-button,
-.search-box-input::-webkit-search-results-decoration {
- -webkit-appearance: none;
- display: none;
-}
-
-.search-box-input::-ms-clear,
-.search-box-input::-ms-reveal {
- display: none;
- width: 0;
- height: 0;
-}
-
-.search-box-note {
- position: absolute;
- top: 1px;
- left: 40px;
- right: var(--controlHeight);
- line-height: var(--controlHeight);
- color: var(--secondFontColor);
- font-size: var(--smallFontSize);
- text-align: right;
- text-overflow: ellipsis;
- overflow: hidden;
- white-space: nowrap;
- pointer-events: none;
-}
-
-.search-box-input:focus ~ .search-box-magnifier {
- color: var(--darkBlue);
-}
-
-.search-box-magnifier {
- position: absolute;
- top: 4px;
- left: 4px;
- color: var(--gray52);
- transition: color 0.3s ease;
-}
-
-.search-box > .spinner {
- position: absolute;
- top: 4px;
- left: 5px;
-}
-
-.search-box-clear {
- position: absolute;
- top: 4px;
- right: 4px;
-}
-
-.search-box-clear.button:focus {
- box-shadow:
- 0 0 0 1px white,
- 0 0 0 4px rgba(35, 106, 151, 0.5);
-}
-
-.search-box-input-note {
- position: absolute;
- top: 100%;
- left: 0;
- line-height: 1;
- color: var(--secondFontColor);
- font-size: var(--smallFontSize);
- white-space: nowrap;
-}
diff --git a/server/sonar-web/src/main/js/components/controls/SearchBox.tsx b/server/sonar-web/src/main/js/components/controls/SearchBox.tsx
deleted file mode 100644
index d5ce452ffa4..00000000000
--- a/server/sonar-web/src/main/js/components/controls/SearchBox.tsx
+++ /dev/null
@@ -1,182 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2024 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 classNames from 'classnames';
-import { debounce, DebouncedFunc } from 'lodash';
-import * as React from 'react';
-import { KeyboardKeys } from '../../helpers/keycodes';
-import { translate, translateWithParameters } from '../../helpers/l10n';
-import SearchIcon from '../icons/SearchIcon';
-import Spinner from '../ui/Spinner';
-import { ClearButton } from './buttons';
-import './SearchBox.css';
-
-interface Props {
- autoFocus?: boolean;
- className?: string;
- id?: string;
- innerRef?: (node: HTMLInputElement | null) => void;
- loading?: boolean;
- maxLength?: number;
- minLength?: number;
- onChange: (value: string) => void;
- onClick?: React.MouseEventHandler<HTMLInputElement>;
- onFocus?: React.FocusEventHandler<HTMLInputElement>;
- onKeyDown?: React.KeyboardEventHandler<HTMLInputElement>;
- placeholder: string;
- value?: string;
-}
-
-interface State {
- value: string;
-}
-
-const DEFAULT_MAX_LENGTH = 100;
-const DEBOUNCE_DELAY = 250;
-
-export default class SearchBox extends React.PureComponent<Props, State> {
- debouncedOnChange: DebouncedFunc<(query: string) => void>;
- input?: HTMLInputElement | null;
-
- constructor(props: Props) {
- super(props);
- this.state = { value: props.value ?? '' };
- this.debouncedOnChange = debounce(this.props.onChange, DEBOUNCE_DELAY);
- }
-
- componentDidUpdate(prevProps: Props) {
- if (
- // input is controlled
- this.props.value !== undefined &&
- // parent is aware of last change
- // can happen when previous value was less than min length
- this.state.value === prevProps.value &&
- this.state.value !== this.props.value
- ) {
- this.setState({ value: this.props.value });
- }
- }
-
- changeValue = (value: string, debounced = true) => {
- const { minLength } = this.props;
- if (value.length === 0) {
- // immediately notify when value is empty
- this.props.onChange('');
- // and cancel scheduled callback
- this.debouncedOnChange.cancel();
- } else if (!minLength || minLength <= value.length) {
- if (debounced) {
- this.debouncedOnChange(value);
- } else {
- this.props.onChange(value);
- }
- }
- };
-
- handleInputChange = (event: React.SyntheticEvent<HTMLInputElement>) => {
- const { value } = event.currentTarget;
- this.setState({ value });
- this.changeValue(value);
- };
-
- handleInputKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => {
- if (event.nativeEvent.key === KeyboardKeys.Escape) {
- event.preventDefault();
- this.handleResetClick();
- }
- if (this.props.onKeyDown) {
- this.props.onKeyDown(event);
- }
- };
-
- handleResetClick = () => {
- this.changeValue('', false);
- if (this.props.value === undefined || this.props.value === '') {
- this.setState({ value: '' });
- }
- if (this.input) {
- this.input.focus();
- }
- };
-
- ref = (node: HTMLInputElement | null) => {
- this.input = node;
- if (this.props.innerRef) {
- this.props.innerRef(node);
- }
- };
-
- render() {
- const { loading, minLength, maxLength = DEFAULT_MAX_LENGTH } = this.props;
- const { value } = this.state;
-
- const inputClassName = classNames('search-box-input', {
- touched: value.length > 0 && (!minLength || minLength > value.length),
- });
-
- const tooShort = minLength !== undefined && value.length > 0 && value.length < minLength;
-
- return (
- <div
- className={classNames('search-box', this.props.className)}
- id={this.props.id}
- title={
- tooShort && minLength !== undefined
- ? translateWithParameters('select2.tooShort', minLength)
- : ''
- }
- >
- <input
- aria-label={this.props.placeholder}
- autoComplete="off"
- autoFocus={this.props.autoFocus}
- className={inputClassName}
- maxLength={maxLength}
- onChange={this.handleInputChange}
- onClick={this.props.onClick}
- onFocus={this.props.onFocus}
- onKeyDown={this.handleInputKeyDown}
- placeholder={this.props.placeholder}
- ref={this.ref}
- type="search"
- value={value}
- />
-
- <Spinner loading={loading !== undefined ? loading : false}>
- <SearchIcon className="search-box-magnifier" />
- </Spinner>
-
- {value && (
- <ClearButton
- aria-label={translate('clear')}
- className="button-tiny search-box-clear"
- iconProps={{ size: 12 }}
- onClick={this.handleResetClick}
- />
- )}
-
- <span aria-live="polite" className="search-box-note">
- {tooShort &&
- minLength !== undefined &&
- translateWithParameters('select2.tooShort', minLength)}
- </span>
- </div>
- );
- }
-}
diff --git a/server/sonar-web/src/main/js/components/controls/Select.tsx b/server/sonar-web/src/main/js/components/controls/Select.tsx
deleted file mode 100644
index ab222c15191..00000000000
--- a/server/sonar-web/src/main/js/components/controls/Select.tsx
+++ /dev/null
@@ -1,369 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2024 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 styled from '@emotion/styled';
-import classNames from 'classnames';
-import { omit } from 'lodash';
-import * as React from 'react';
-import ReactSelect, {
- ClearIndicatorProps,
- components,
- DropdownIndicatorProps,
- GroupBase,
- LoadingIndicatorProps,
- MultiValueRemoveProps,
- Props as NamedProps,
- StylesConfig,
-} from 'react-select';
-import AsyncReactSelect, { AsyncProps } from 'react-select/async';
-import AsyncCreatableReactSelect, { AsyncCreatableProps } from 'react-select/async-creatable';
-import { colors, others, sizes, zIndexes } from '../../app/theme';
-import { ClearButton } from './buttons';
-
-const ArrowSpan = styled.span`
- border-color: ${colors.gray52} transparent transparent;
- border-style: solid;
- border-width: 4px 4px 2px;
- display: inline-block;
- height: 0;
- width: 0;
-`;
-
-export interface LabelValueSelectOption<V = string> {
- label: string;
- value: V;
-}
-
-interface StyleExtensionProps {
- large?: boolean;
-}
-
-export function dropdownIndicator<
- Option = LabelValueSelectOption,
- IsMulti extends boolean = boolean,
- Group extends GroupBase<Option> = GroupBase<Option>,
->({ innerProps }: DropdownIndicatorProps<Option, IsMulti, Group>) {
- return <ArrowSpan {...innerProps} />;
-}
-
-export function clearIndicator<
- Option = LabelValueSelectOption,
- IsMulti extends boolean = boolean,
- Group extends GroupBase<Option> = GroupBase<Option>,
->(props: ClearIndicatorProps<Option, IsMulti, Group>) {
- const { innerProps } = props;
- return (
- <div {...innerProps} className="spacer-left spacer-right">
- {/* We use tabindex="-1" to prevent the clear button from being focused via tabbing.*/}
- {/* This is done to align with react-select default behavior and because backspace already clears the select value. */}
- <ClearButton className="button-tiny" iconProps={{ size: 12 }} tabIndex={-1} />
- </div>
- );
-}
-
-export function loadingIndicator<
- Option = LabelValueSelectOption,
- IsMulti extends boolean = boolean,
- Group extends GroupBase<Option> = GroupBase<Option>,
->({ innerProps }: LoadingIndicatorProps<Option, IsMulti, Group>) {
- return <i className={classNames('spinner spacer-left spacer-right', innerProps.className)} />;
-}
-
-export function multiValueRemove<
- Option = LabelValueSelectOption,
- IsMulti extends boolean = boolean,
- Group extends GroupBase<Option> = GroupBase<Option>,
->(props: MultiValueRemoveProps<Option, IsMulti, Group>) {
- return <components.MultiValueRemove {...props}>&times;</components.MultiValueRemove>;
-}
-
-export default function Select<
- Option = LabelValueSelectOption,
- IsMulti extends boolean = boolean,
- Group extends GroupBase<Option> = GroupBase<Option>,
->(props: NamedProps<Option, IsMulti, Group> & Readonly<StyleExtensionProps>) {
- return (
- <ReactSelect<Option, IsMulti, Group>
- {...omit(props, 'className', 'large')}
- styles={selectStyle<Option, IsMulti, Group>(props)}
- className={classNames('react-select', props.className)}
- classNamePrefix="react-select"
- components={{
- ...props.components,
- DropdownIndicator: dropdownIndicator,
- ClearIndicator: clearIndicator,
- MultiValueRemove: multiValueRemove,
- }}
- />
- );
-}
-
-export function CreatableSelect<
- Option = LabelValueSelectOption,
- IsMulti extends boolean = boolean,
- Group extends GroupBase<Option> = GroupBase<Option>,
->(props: AsyncCreatableProps<Option, IsMulti, Group>) {
- return (
- <AsyncCreatableReactSelect<Option, IsMulti, Group>
- {...props}
- styles={selectStyle<Option, IsMulti, Group>()}
- components={{
- ...props.components,
- DropdownIndicator: dropdownIndicator,
- ClearIndicator: clearIndicator,
- MultiValueRemove: multiValueRemove,
- LoadingIndicator: loadingIndicator,
- }}
- />
- );
-}
-
-export function SearchSelect<
- Option = LabelValueSelectOption,
- IsMulti extends boolean = boolean,
- Group extends GroupBase<Option> = GroupBase<Option>,
->(
- props: NamedProps<Option, IsMulti, Group> &
- AsyncProps<Option, IsMulti, Group> &
- Readonly<StyleExtensionProps>,
-) {
- return (
- <AsyncReactSelect<Option, IsMulti, Group>
- {...omit(props, 'className', 'large')}
- styles={selectStyle<Option, IsMulti, Group>(props)}
- className={classNames('react-select', props.className)}
- classNamePrefix="react-select"
- components={{
- ...props.components,
- DropdownIndicator: dropdownIndicator,
- ClearIndicator: clearIndicator,
- MultiValueRemove: multiValueRemove,
- LoadingIndicator: loadingIndicator,
- }}
- />
- );
-}
-
-export function selectStyle<
- Option = LabelValueSelectOption,
- IsMulti extends boolean = boolean,
- Group extends GroupBase<Option> = GroupBase<Option>,
->(
- props?: NamedProps<Option, IsMulti, Group> &
- AsyncProps<Option, IsMulti, Group> &
- Readonly<StyleExtensionProps>,
-): StylesConfig<Option, IsMulti, Group> {
- return {
- container: () => ({
- position: 'relative',
- display: 'inline-block',
- verticalAlign: 'middle',
- fontSize: '12px',
- textAlign: 'left',
- width: '100%',
- }),
- control: (_provided, state) => ({
- position: 'relative',
- display: 'flex',
- width: '100%',
- minHeight: `${sizes.controlHeight}`,
- lineHeight: `calc(${sizes.controlHeight} - 2px)`,
- border: `1px solid ${state.isFocused ? colors.blue : colors.gray80}`,
- borderCollapse: 'separate',
- borderRadius: '2px',
- backgroundColor: state.isDisabled ? colors.disableGrayBg : '#fff',
- boxSizing: 'border-box',
- color: `${colors.baseFontColor}`,
- cursor: 'default',
- outline: 'none',
- padding: props?.large ? '4px 0px' : '0',
- }),
- singleValue: () => ({
- bottom: 0,
- left: 0,
- lineHeight: '23px',
- padding: props?.large ? '4px 8px' : '0 8px',
- paddingLeft: '8px',
- paddingRight: '24px',
- position: 'absolute',
- right: 0,
- top: 0,
- maxWidth: '100%',
- overflow: 'hidden',
- textOverflow: 'ellipsis',
- whiteSpace: 'nowrap',
- }),
- valueContainer: (_provided, state) => {
- if (state.hasValue && state.isMulti) {
- return {
- lineHeight: '23px',
- paddingLeft: '1px',
- };
- }
-
- return {
- bottom: 0,
- left: 0,
- lineHeight: '23px',
- paddingLeft: '8px',
- paddingRight: '24px',
- position: 'absolute',
- right: 0,
- top: 0,
- maxWidth: '100%',
- overflow: 'hidden',
- textOverflow: 'ellipsis',
- whiteSpace: 'nowrap',
- display: 'flex',
- };
- },
- indicatorsContainer: (_provided, state) => ({
- position: 'relative',
- cursor: state.isDisabled ? 'default' : 'pointer',
- textAlign: 'end',
- verticalAlign: 'middle',
- width: '20px',
- paddingRight: '5px',
- flex: 1,
- display: 'flex',
- justifyContent: 'end',
- alignItems: 'center',
- }),
- indicatorSeparator: () => ({
- display: 'none',
- }),
- multiValue: () => ({
- display: 'inline-block',
- backgroundColor: 'rgba(0, 126, 255, 0.08)',
- borderRadius: '2px',
- border: '1px solid rgba(0, 126, 255, 0.24)',
- color: '#333',
- maxWidth: '200px',
- fontSize: '12px',
- lineHeight: '14px',
- margin: '1px 4px 1px 1px',
- verticalAlign: 'top',
- }),
- multiValueLabel: () => ({
- display: 'inline-block',
- cursor: 'default',
- padding: '2px 5px',
- overflow: 'hidden',
- marginRight: 'auto',
- maxWidth: 'calc(200px - 28px)',
- textOverflow: 'ellipsis',
- whiteSpace: 'nowrap',
- verticalAlign: 'middle',
- }),
- multiValueRemove: () => ({
- order: '-1',
- cursor: 'pointer',
- borderLeft: '1px solid rgba(0, 126, 255, 0.24)',
- verticalAlign: 'middle',
- padding: '1px 5px',
- fontSize: '12px',
- lineHeight: '14px',
- display: 'inline-block',
- }),
- menu: () => ({
- borderBottomRightRadius: '4px',
- borderBottomLeftRadius: '4px',
- backgroundColor: colors.white,
- border: `1px solid ${colors.neutral200}`,
- borderTopColor: `${colors.barBorderColor}`,
- boxSizing: 'border-box',
- marginTop: '-1px',
- maxHeight: '200px',
- zIndex: `${zIndexes.dropdownMenuZIndex}`,
- webkitOverflowScrolling: 'touch',
- boxShadow: `${others.defaultShadow}`,
- position: 'absolute',
- top: '100%',
- minWidth: '100%',
- }),
- menuPortal: (baseStyles) => ({
- ...baseStyles,
- borderBottomRightRadius: '4px',
- borderBottomLeftRadius: '4px',
- marginTop: '-1px',
- backgroundColor: colors.white,
- border: `1px solid ${colors.neutral200}`,
- borderTopColor: `${colors.barBorderColor}`,
- boxSizing: 'border-box',
- maxHeight: '200px',
- zIndex: `${zIndexes.dropdownMenuZIndex}`,
- webkitOverflowScrolling: 'touch',
- boxShadow: `${others.defaultShadow}`,
- }),
- menuList: () => ({
- boxSizing: 'border-box',
- maxHeight: '198px',
- padding: '5px 0',
- overflowY: 'auto',
- }),
- placeholder: () => ({
- position: 'absolute',
- color: '#666',
- }),
- option: (_provided, state) => {
- let borderLeftColor = 'transparent';
- let backgroundColor = colors.white;
-
- if (state.isFocused && state.isSelected) {
- borderLeftColor = colors.info500;
- backgroundColor = colors.info100;
- } else if (state.isFocused) {
- borderLeftColor = colors.blacka60;
- backgroundColor = colors.neutral50;
- } else if (state.isSelected) {
- borderLeftColor = colors.info500;
- backgroundColor = colors.info50;
- }
-
- return {
- display: 'block',
- lineHeight: '20px',
- padding: props?.large ? '4px 8px' : '0 8px',
- boxSizing: 'border-box',
- color: state.isDisabled ? colors.disableGrayText : colors.neutral800,
- backgroundColor,
- borderLeft: '2px solid transparent',
- borderLeftColor,
- fontSize: `${sizes.smallFontSize}`,
- cursor: state.isDisabled ? 'default' : 'pointer',
- whiteSpace: 'nowrap',
- overflow: 'hidden',
- textOverflow: 'ellipsis',
- };
- },
- input: () => ({
- display: 'flex',
- alignItems: 'center',
- }),
- loadingIndicator: () => ({
- position: 'absolute',
- padding: '8px',
- fontSize: '4px',
- }),
- noOptionsMessage: () => ({
- color: `${colors.gray60}`,
- padding: '8px 10px',
- }),
- };
-}
diff --git a/server/sonar-web/src/main/js/components/controls/SelectList.tsx b/server/sonar-web/src/main/js/components/controls/SelectList.tsx
index d68ad1c9f3e..2ae7e857c6f 100644
--- a/server/sonar-web/src/main/js/components/controls/SelectList.tsx
+++ b/server/sonar-web/src/main/js/components/controls/SelectList.tsx
@@ -195,7 +195,6 @@ export default class SelectList extends React.PureComponent<Props, State> {
needReload={this.props.needToReload}
reload={this.onReload}
total={this.props.elementsTotalCount}
- useMIUIButtons
/>
)}
</PageContentFontWrapper>
diff --git a/server/sonar-web/src/main/js/components/controls/SimpleModal.tsx b/server/sonar-web/src/main/js/components/controls/SimpleModal.tsx
deleted file mode 100644
index 1d3a408b2bc..00000000000
--- a/server/sonar-web/src/main/js/components/controls/SimpleModal.tsx
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2024 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 Modal, { ModalProps } from './Modal';
-
-export interface ChildrenProps {
- onCloseClick: (event?: React.SyntheticEvent<HTMLElement>) => void;
- onFormSubmit: (event: React.SyntheticEvent<HTMLFormElement>) => void;
- onSubmitClick: (event?: React.SyntheticEvent<HTMLElement>) => void;
- submitting: boolean;
-}
-
-interface Props extends Omit<ModalProps, 'children'> {
- children: (props: ChildrenProps) => React.ReactNode;
- header: string;
- onClose: () => void;
- onSubmit: () => void | Promise<void | Response>;
-}
-
-interface State {
- submitting: boolean;
-}
-
-export default class SimpleModal extends React.Component<Props, State> {
- mounted = false;
- state: State = { submitting: false };
-
- componentDidMount() {
- this.mounted = true;
- }
-
- componentWillUnmount() {
- this.mounted = false;
- }
-
- stopSubmitting = () => {
- if (this.mounted) {
- this.setState({ submitting: false });
- }
- };
-
- handleCloseClick = (event?: React.SyntheticEvent<HTMLElement>) => {
- if (event) {
- event.preventDefault();
- event.currentTarget.blur();
- }
- this.props.onClose();
- };
-
- handleFormSubmit = (event: React.SyntheticEvent<HTMLFormElement>) => {
- event.preventDefault();
- this.submit();
- };
-
- handleSubmitClick = (event?: React.SyntheticEvent<HTMLElement>) => {
- if (event) {
- event.preventDefault();
- event.currentTarget.blur();
- }
- this.submit();
- };
-
- submit = () => {
- const result = this.props.onSubmit();
- if (result) {
- this.setState({ submitting: true });
- result.then(this.stopSubmitting, this.stopSubmitting);
- }
- };
-
- render() {
- const { children, header, onClose, onSubmit, ...modalProps } = this.props;
- return (
- <Modal contentLabel={header} onRequestClose={onClose} {...modalProps}>
- {children({
- onCloseClick: this.handleCloseClick,
- onFormSubmit: this.handleFormSubmit,
- onSubmitClick: this.handleSubmitClick,
- submitting: this.state.submitting,
- })}
- </Modal>
- );
- }
-}
diff --git a/server/sonar-web/src/main/js/components/controls/Toggle.css b/server/sonar-web/src/main/js/components/controls/Toggle.css
deleted file mode 100644
index 8ed10d83a2f..00000000000
--- a/server/sonar-web/src/main/js/components/controls/Toggle.css
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2024 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.
- */
-.button.boolean-toggle {
- display: inline-block;
- vertical-align: middle;
- width: 48px;
- height: var(--controlHeight);
- padding: 1px;
- border: 1px solid var(--gray80);
- border-radius: var(--controlHeight);
- box-sizing: border-box;
- background-color: #fff;
- cursor: pointer;
- transition: all 0.3s ease;
-}
-
-.button.boolean-toggle:hover {
- background-color: #fff;
-}
-
-.button.boolean-toggle:focus {
- border-color: var(--blue);
- background-color: #f6f6f6;
-}
-
-.boolean-toggle-handle {
- display: flex;
- justify-content: center;
- align-items: center;
- width: 20px;
- height: 20px;
- border: 1px solid var(--gray80);
- border-radius: 22px;
- box-sizing: border-box;
- background-color: #f6f6f6;
- transition:
- transform 0.3s cubic-bezier(0.87, -0.41, 0.19, 1.44),
- border 0.3s ease;
-}
-
-.boolean-toggle-handle > * {
- opacity: 0;
- transition: opacity 0.3s ease;
-}
-
-.button.boolean-toggle-on {
- border-color: var(--darkBlue);
- background-color: var(--darkBlue);
- color: var(--darkBlue);
-}
-
-.button.boolean-toggle-on:hover {
- background-color: var(--darkBlue);
-}
-
-.button.boolean-toggle-on:focus {
- background-color: var(--darkBlue);
-}
-
-.button.boolean-toggle-on .boolean-toggle-handle {
- border-color: #f6f6f6;
- transform: translateX(var(--controlHeight));
-}
-
-.button.boolean-toggle-on .boolean-toggle-handle > * {
- opacity: 1;
-}
diff --git a/server/sonar-web/src/main/js/components/controls/Toggle.tsx b/server/sonar-web/src/main/js/components/controls/Toggle.tsx
deleted file mode 100644
index 6fe90a37c99..00000000000
--- a/server/sonar-web/src/main/js/components/controls/Toggle.tsx
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2024 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 classNames from 'classnames';
-import * as React from 'react';
-import CheckIcon from '../icons/CheckIcon';
-import { Button } from './buttons';
-import './Toggle.css';
-
-interface Props {
- id?: string;
- ariaLabel?: string;
- disabled?: boolean;
- name?: string;
- onChange?: (value: boolean) => void;
- value: boolean | string;
-}
-
-export function getToggleValue(value: string | boolean) {
- return typeof value === 'string' ? value === 'true' : value;
-}
-
-export default class Toggle extends React.PureComponent<Props> {
- getValue = () => {
- const { value } = this.props;
- return getToggleValue(value);
- };
-
- handleClick = () => {
- if (this.props.onChange) {
- const value = this.getValue();
- this.props.onChange(!value);
- }
- };
-
- render() {
- const { ariaLabel, disabled, name, id } = this.props;
- const value = this.getValue();
- const className = classNames('boolean-toggle', { 'boolean-toggle-on': value });
-
- return (
- <Button
- id={id}
- className={className}
- disabled={disabled}
- aria-label={ariaLabel}
- name={name}
- onClick={this.handleClick}
- role="switch"
- aria-checked={value}
- >
- <div className="boolean-toggle-handle">
- <CheckIcon size={12} />
- </div>
- </Button>
- );
- }
-}
diff --git a/server/sonar-web/src/main/js/components/controls/Tooltip.tsx b/server/sonar-web/src/main/js/components/controls/Tooltip.tsx
index cfe00ea8649..4119d20a857 100644
--- a/server/sonar-web/src/main/js/components/controls/Tooltip.tsx
+++ b/server/sonar-web/src/main/js/components/controls/Tooltip.tsx
@@ -406,7 +406,7 @@ export class TooltipInner extends React.Component<TooltipProps, State> {
return (
<div
className={classNames(`${classNameSpace}-inner sw-font-sans`, classNameInner, {
- hidden: !isVisible,
+ 'sw-hidden': !isVisible,
})}
id={this.id}
role="tooltip"
diff --git a/server/sonar-web/src/main/js/components/controls/__tests__/BoxedGroupAccordion-test.tsx b/server/sonar-web/src/main/js/components/controls/__tests__/BoxedGroupAccordion-test.tsx
deleted file mode 100644
index d73c06e7717..00000000000
--- a/server/sonar-web/src/main/js/components/controls/__tests__/BoxedGroupAccordion-test.tsx
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2024 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 { screen } from '@testing-library/react';
-import userEvent from '@testing-library/user-event';
-import * as React from 'react';
-import { renderComponent } from '../../../helpers/testReactTestingUtils';
-import BoxedGroupAccordion from '../BoxedGroupAccordion';
-
-it('should behave correctly', async () => {
- const user = userEvent.setup();
- renderDeliveryAccordion();
- expect(screen.queryByText('children')).not.toBeInTheDocument();
- await user.click(screen.getByRole('button', { expanded: false }));
- expect(screen.getByText('children')).toBeInTheDocument();
-});
-
-it('should render header correctly', () => {
- renderDeliveryAccordion(() => <div>header</div>);
- expect(screen.getByText('header')).toBeInTheDocument();
-});
-
-function renderDeliveryAccordion(renderHeader?: () => React.ReactNode) {
- function AccordionTest() {
- const [open, setOpen] = React.useState(false);
-
- return (
- <BoxedGroupAccordion
- onClick={() => setOpen(!open)}
- open={open}
- title="test"
- renderHeader={renderHeader}
- >
- <div>children</div>
- </BoxedGroupAccordion>
- );
- }
-
- return renderComponent(<AccordionTest />);
-}
diff --git a/server/sonar-web/src/main/js/components/controls/__tests__/ButtonToggle-test.tsx b/server/sonar-web/src/main/js/components/controls/__tests__/ButtonToggle-test.tsx
deleted file mode 100644
index e02771c5e91..00000000000
--- a/server/sonar-web/src/main/js/components/controls/__tests__/ButtonToggle-test.tsx
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2024 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 { screen } from '@testing-library/react';
-import userEvent from '@testing-library/user-event';
-import * as React from 'react';
-import { renderComponent } from '../../../helpers/testReactTestingUtils';
-import ButtonToggle, { ButtonToggleProps } from '../ButtonToggle';
-
-it('should behave properly', async () => {
- const onCheck = jest.fn();
- const user = userEvent.setup();
-
- render({ onCheck });
- expect(screen.getAllByRole('button')).toHaveLength(3);
-
- await user.click(screen.getByRole('button', { name: 'first' }));
- expect(onCheck).toHaveBeenCalledWith('one');
-
- await user.click(screen.getByRole('button', { name: 'second' }));
- expect(onCheck).not.toHaveBeenLastCalledWith('two');
-});
-
-it('should behave properly when disabled', async () => {
- const onCheck = jest.fn();
- const user = userEvent.setup();
-
- render({ disabled: true, onCheck });
-
- await user.click(screen.getByRole('button', { name: 'first' }));
- expect(onCheck).not.toHaveBeenCalled();
-});
-
-function render(props?: Partial<ButtonToggleProps>) {
- renderComponent(
- <ButtonToggle
- label="test-label"
- onCheck={jest.fn()}
- disabled={false}
- options={[
- { value: 'one', label: 'first' },
- { value: 'two', label: 'second' },
- { value: 'tree', label: 'third' },
- ]}
- value="two"
- {...props}
- />,
- );
-}
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
deleted file mode 100644
index 8883194940d..00000000000
--- a/server/sonar-web/src/main/js/components/controls/__tests__/Checkbox-test.tsx
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2024 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 { screen } from '@testing-library/react';
-import userEvent from '@testing-library/user-event';
-
-import * as React from 'react';
-import { renderComponent } from '../../../helpers/testReactTestingUtils';
-import Checkbox from '../Checkbox';
-
-describe.each([
- { children: null, describtion: 'with no children' },
- { children: <a>child</a>, describtion: 'with children' },
-])('Checkbox $describtion', ({ children }) => {
- it('should call check function', async () => {
- const user = userEvent.setup();
- const onCheck = jest.fn();
- const rerender = renderCheckbox({
- label: 'me',
- children,
- onCheck,
- checked: false,
- title: 'title',
- });
- await user.click(screen.getByRole('checkbox', { name: 'me' }));
- expect(onCheck).toHaveBeenCalledWith(true, undefined);
- expect(screen.getByTitle('title')).toBeInTheDocument();
- rerender({ checked: true });
- await user.click(screen.getByRole('checkbox', { name: 'me' }));
- expect(onCheck).toHaveBeenCalledWith(false, undefined);
- });
-
- it('should accept partial state', () => {
- renderCheckbox({ label: 'me', thirdState: true, children, checked: false });
- expect(screen.getByRole('checkbox', { name: 'me' })).not.toBeChecked();
- });
-
- it('should render loading state', () => {
- renderCheckbox({ label: 'me', children, loading: true });
- expect(screen.getByTestId('spinner')).toMatchSnapshot();
- });
-});
-
-it('should render the checkbox on the right', () => {
- renderCheckbox({ label: 'me', children: <a>child</a>, right: true });
- expect(screen.getByRole('checkbox', { name: 'me' })).toMatchSnapshot();
-});
-
-function renderCheckbox(override?: Partial<Checkbox['props']>) {
- const { rerender } = renderComponent(<Checkbox checked onCheck={jest.fn()} {...override} />);
- return function (reoverride?: Partial<Checkbox['props']>) {
- rerender(<Checkbox checked onCheck={jest.fn()} {...override} {...reoverride} />);
- };
-}
diff --git a/server/sonar-web/src/main/js/components/controls/__tests__/ListFooter-test.tsx b/server/sonar-web/src/main/js/components/controls/__tests__/ListFooter-test.tsx
index ac0551aa375..fe03b68b10b 100644
--- a/server/sonar-web/src/main/js/components/controls/__tests__/ListFooter-test.tsx
+++ b/server/sonar-web/src/main/js/components/controls/__tests__/ListFooter-test.tsx
@@ -80,64 +80,3 @@ describe('ListFooter', () => {
return renderComponent(<ListFooter count={3} loadMore={jest.fn()} total={5} {...props} />);
}
});
-
-// Once the MIUI buttons become the norm, we can use only the above test "suite" and drop this one.
-describe('ListFooter using MIUI buttons', () => {
- describe('rendering', () => {
- it('should render correctly when loading', async () => {
- renderListFooter({ loading: true });
- expect(await screen.findByText('loading')).toBeInTheDocument();
- });
-
- it('should not render if there are neither loadmore nor reload props', () => {
- renderListFooter({ loadMore: undefined, reload: undefined });
- expect(screen.queryByRole('button')).not.toBeInTheDocument();
- });
-
- it.each([
- [undefined, 60, 30, true],
- [undefined, 45, 30, false],
- [undefined, 60, undefined, false],
- [60, 60, 30, false],
- ])(
- 'should handle showing load more button based on total, count and pageSize',
- (total, count, pageSize, expected) => {
- renderListFooter({ total, count, pageSize });
-
- /* eslint-disable jest/no-conditional-in-test */
- /* eslint-disable jest/no-conditional-expect */
- if (expected) {
- expect(screen.getByRole('button')).toBeInTheDocument();
- } else {
- expect(screen.queryByRole('button')).not.toBeInTheDocument();
- }
- /* eslint-enable jest/no-conditional-in-test */
- /* eslint-enable jest/no-conditional-expect */
- },
- );
- });
-
- it('should properly call load more callback', async () => {
- const user = userEvent.setup();
- const loadMore = jest.fn();
- renderListFooter({ loadMore });
-
- await user.click(screen.getByRole('button'));
- expect(loadMore).toHaveBeenCalled();
- });
-
- it('should properly call reload callback', async () => {
- const user = userEvent.setup();
- const reload = jest.fn();
- renderListFooter({ needReload: true, reload });
-
- await user.click(screen.getByRole('button'));
- expect(reload).toHaveBeenCalled();
- });
-
- function renderListFooter(props: Partial<ListFooterProps> = {}) {
- return renderComponent(
- <ListFooter count={3} loadMore={jest.fn()} total={5} useMIUIButtons {...props} />,
- );
- }
-});
diff --git a/server/sonar-web/src/main/js/components/controls/__tests__/RadioCard-test.tsx b/server/sonar-web/src/main/js/components/controls/__tests__/RadioCard-test.tsx
deleted file mode 100644
index 145f33ac1fe..00000000000
--- a/server/sonar-web/src/main/js/components/controls/__tests__/RadioCard-test.tsx
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2024 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 userEvent from '@testing-library/user-event';
-import * as React from 'react';
-import { renderComponent } from '../../../helpers/testReactTestingUtils';
-import { byRole } from '../../../helpers/testSelector';
-import { FCProps } from '../../../types/misc';
-import RadioCard from '../RadioCard';
-
-describe('RadioCard component', () => {
- it('renders & handles selection', async () => {
- const user = userEvent.setup();
- const onClick = jest.fn();
- renderRadioCard({ onClick });
-
- const card = byRole('radio').get();
-
- expect(card).toBeInTheDocument();
- expect(byRole('heading', { name: 'body' }).get()).toBeInTheDocument();
-
- // Keyboard selection
- await user.keyboard('{Tab}');
- await user.keyboard('{Enter}');
- expect(onClick).toHaveBeenCalledTimes(1);
-
- // Mouse selection
- await user.click(card);
- expect(onClick).toHaveBeenCalledTimes(2);
- });
-});
-
-function renderRadioCard(overrides: Partial<FCProps<typeof RadioCard>>) {
- return renderComponent(
- <RadioCard title="Radio Card" {...overrides}>
- <h3>body</h3>
- </RadioCard>,
- );
-}
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
deleted file mode 100644
index 9d0ad090f98..00000000000
--- a/server/sonar-web/src/main/js/components/controls/__tests__/__snapshots__/Checkbox-test.tsx.snap
+++ /dev/null
@@ -1,55 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`Checkbox with children should render loading state 1`] = `
-<i
- aria-live="polite"
- class="spinner is-loading"
- data-testid="spinner"
->
- <span
- class="sw-sr-only"
- >
- loading
- </span>
-</i>
-`;
-
-exports[`Checkbox with no children should render loading state 1`] = `
-<i
- aria-live="polite"
- class="spinner is-loading"
- data-testid="spinner"
->
- <span
- class="sw-sr-only"
- >
- me
- </span>
-</i>
-`;
-
-exports[`should render the checkbox on the right 1`] = `
-<a
- aria-checked="true"
- aria-label="me"
- class="link-checkbox"
- href="#"
- role="checkbox"
->
- <a>
- child
- </a>
- <div
- class="sw-overflow-hidden sw-relative"
- >
- <i
- aria-live="polite"
- class="spinner sw-sr-only sw-left-[-10000px]"
- data-testid="spinner"
- />
- </div>
- <i
- class="icon-checkbox icon-checkbox-checked"
- />
-</a>
-`;
diff --git a/server/sonar-web/src/main/js/components/controls/__tests__/clipboard-test.tsx b/server/sonar-web/src/main/js/components/controls/__tests__/clipboard-test.tsx
deleted file mode 100644
index f08fd6ba341..00000000000
--- a/server/sonar-web/src/main/js/components/controls/__tests__/clipboard-test.tsx
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2024 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 { act, screen } from '@testing-library/react';
-import userEvent from '@testing-library/user-event';
-import * as React from 'react';
-import { renderComponent } from '../../../helpers/testReactTestingUtils';
-import {
- ClipboardBase,
- ClipboardButton,
- ClipboardButtonProps,
- ClipboardIconButton,
- ClipboardIconButtonProps,
-} from '../clipboard';
-
-beforeEach(() => {
- jest.useFakeTimers();
-});
-
-afterEach(() => {
- jest.runOnlyPendingTimers();
- jest.useRealTimers();
-});
-
-describe('ClipboardBase', () => {
- it('should display correctly', () => {
- renderClipboardBase();
- expect(screen.getByText('click to copy')).toBeInTheDocument();
- });
-
- it('should allow its content to be copied', async () => {
- const user = userEvent.setup({ advanceTimers: jest.advanceTimersByTime });
- renderClipboardBase();
-
- await user.click(screen.getByRole('button'));
- expect(await screen.findByText('copied')).toBeInTheDocument();
-
- act(() => jest.runAllTimers());
-
- expect(await screen.findByText('click to copy')).toBeInTheDocument();
- });
-
- function renderClipboardBase(props: Partial<ClipboardBase['props']> = {}) {
- return renderComponent(
- <ClipboardBase {...props}>
- {({ setCopyButton, copySuccess }) => (
- <span data-clipboard-text="foo" ref={setCopyButton} role="button">
- {copySuccess ? 'copied' : 'click to copy'}
- </span>
- )}
- </ClipboardBase>,
- );
- }
-});
-
-describe('ClipboardButton', () => {
- it('should display correctly', () => {
- renderClipboardButton();
- expect(screen.getByRole('button', { name: 'copy_to_clipboard' })).toBeInTheDocument();
- });
-
- it('should render a custom label if provided', () => {
- renderClipboardButton({ children: 'custom label' });
- expect(screen.getByRole('button', { name: 'copy_to_clipboard' })).toBeInTheDocument();
- expect(screen.getByText('custom label')).toBeInTheDocument();
- });
-
- it('should render a custom aria-label if provided', () => {
- renderClipboardButton({ 'aria-label': 'custom label' });
- expect(screen.getByRole('button', { name: 'custom label' })).toBeInTheDocument();
- });
-
- function renderClipboardButton(props: Partial<ClipboardButtonProps> = {}) {
- return renderComponent(<ClipboardButton copyValue="foo" {...props} />);
- }
-});
-
-describe('ClipboardIconButton', () => {
- it('should display correctly', () => {
- renderClipboardIconButton();
- expect(screen.getByRole('button', { name: 'copy_to_clipboard' })).toBeInTheDocument();
- });
-
- it('should render a custom aria-label if provided', () => {
- renderClipboardIconButton({ 'aria-label': 'custom label' });
- expect(screen.getByRole('button', { name: 'custom label' })).toBeInTheDocument();
- });
-
- function renderClipboardIconButton(props: Partial<ClipboardIconButtonProps> = {}) {
- return renderComponent(<ClipboardIconButton copyValue="foo" {...props} />);
- }
-});
diff --git a/server/sonar-web/src/main/js/components/controls/clipboard.tsx b/server/sonar-web/src/main/js/components/controls/clipboard.tsx
deleted file mode 100644
index 3075730dabe..00000000000
--- a/server/sonar-web/src/main/js/components/controls/clipboard.tsx
+++ /dev/null
@@ -1,165 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2024 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 classNames from 'classnames';
-import Clipboard from 'clipboard';
-import * as React from 'react';
-import { translate } from '../../helpers/l10n';
-import CopyIcon from '../icons/CopyIcon';
-import Tooltip from './Tooltip';
-import { Button, ButtonIcon } from './buttons';
-
-export interface State {
- copySuccess: boolean;
-}
-
-interface RenderProps {
- setCopyButton: (node: HTMLElement | null) => void;
- copySuccess: boolean;
-}
-
-interface Props {
- children: (props: RenderProps) => React.ReactNode;
-}
-
-export class ClipboardBase extends React.PureComponent<Props, State> {
- private clipboard?: Clipboard;
- private copyButton?: HTMLElement | null;
- mounted = false;
- state: State = { copySuccess: false };
-
- componentDidMount() {
- this.mounted = true;
- if (this.copyButton) {
- this.clipboard = new Clipboard(this.copyButton);
- this.clipboard.on('success', this.handleSuccessCopy);
- }
- }
-
- componentDidUpdate() {
- if (this.clipboard) {
- this.clipboard.destroy();
- }
- if (this.copyButton) {
- this.clipboard = new Clipboard(this.copyButton);
- this.clipboard.on('success', this.handleSuccessCopy);
- }
- }
-
- componentWillUnmount() {
- this.mounted = false;
- if (this.clipboard) {
- this.clipboard.destroy();
- }
- }
-
- setCopyButton = (node: HTMLElement | null) => {
- this.copyButton = node;
- };
-
- handleSuccessCopy = () => {
- if (this.mounted) {
- this.setState({ copySuccess: true }, () => {
- if (this.copyButton) {
- this.copyButton.focus();
- }
- setTimeout(() => {
- if (this.mounted) {
- this.setState({ copySuccess: false });
- }
- }, 1000);
- });
- }
- };
-
- render() {
- return this.props.children({
- setCopyButton: this.setCopyButton,
- copySuccess: this.state.copySuccess,
- });
- }
-}
-
-export interface ClipboardButtonProps {
- 'aria-label'?: string;
- className?: string;
- copyValue: string;
- children?: React.ReactNode;
-}
-
-export function ClipboardButton({
- className,
- children,
- copyValue,
- 'aria-label': ariaLabel,
-}: ClipboardButtonProps) {
- return (
- <ClipboardBase>
- {({ setCopyButton, copySuccess }) => (
- <Tooltip overlay={translate('copied_action')} visible={copySuccess}>
- <Button
- className={classNames('no-select', className)}
- data-clipboard-text={copyValue}
- innerRef={setCopyButton}
- aria-label={
- copySuccess ? translate('copied_action') : ariaLabel ?? translate('copy_to_clipboard')
- }
- >
- {children ?? (
- <>
- <CopyIcon className="little-spacer-right" />
- {translate('copy')}
- </>
- )}
- </Button>
- </Tooltip>
- )}
- </ClipboardBase>
- );
-}
-
-export interface ClipboardIconButtonProps {
- 'aria-label'?: string;
- className?: string;
- copyValue: string;
-}
-
-export function ClipboardIconButton(props: ClipboardIconButtonProps) {
- const { 'aria-label': ariaLabel, className, copyValue } = props;
- return (
- <ClipboardBase>
- {({ setCopyButton, copySuccess }) => {
- return (
- <ButtonIcon
- aria-label={
- copySuccess ? translate('copied_action') : ariaLabel ?? translate('copy_to_clipboard')
- }
- className={classNames('no-select', className)}
- data-clipboard-text={copyValue}
- innerRef={setCopyButton}
- tooltip={copySuccess ? translate('copied_action') : undefined}
- tooltipProps={copySuccess ? { visible: true } : undefined}
- >
- <CopyIcon />
- </ButtonIcon>
- );
- }}
- </ClipboardBase>
- );
-}
diff --git a/server/sonar-web/src/main/js/components/devops-platform/AlmSettingsInstanceSelector.tsx b/server/sonar-web/src/main/js/components/devops-platform/AlmSettingsInstanceSelector.tsx
index 2aae7daac97..035b70bd705 100644
--- a/server/sonar-web/src/main/js/components/devops-platform/AlmSettingsInstanceSelector.tsx
+++ b/server/sonar-web/src/main/js/components/devops-platform/AlmSettingsInstanceSelector.tsx
@@ -17,7 +17,7 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import { InputSelect, LabelValueSelectOption } from 'design-system';
+import { InputSelect, LabelValueSelectOption, Note } from 'design-system';
import * as React from 'react';
import { OptionProps, SingleValueProps, components } from 'react-select';
import { translate } from '../../helpers/l10n';
@@ -39,7 +39,7 @@ function customOptions(instance: AlmSettingsInstance) {
return instance.url ? (
<>
<span>{instance.key} — </span>
- <span className="text-muted">{instance.url}</span>
+ <Note>{instance.url}</Note>
</>
) : (
<span>{instance.key}</span>
diff --git a/server/sonar-web/src/main/js/components/icons/BranchIcon.tsx b/server/sonar-web/src/main/js/components/icons/BranchIcon.tsx
deleted file mode 100644
index 1a392e60a84..00000000000
--- a/server/sonar-web/src/main/js/components/icons/BranchIcon.tsx
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2024 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 { colors } from '../../app/theme';
-import Icon, { IconProps } from './Icon';
-
-export default function BranchIcon({ fill, ...iconProps }: IconProps) {
- return (
- <Icon {...iconProps}>
- <path
- d="M12.5 6.5c0-1.1-.9-2-2-2s-2 .9-2 2c0 .8.5 1.5 1.2 1.8-.3.6-.7 1.1-1.2 1.4-.9.5-1.9.5-2.5.4V4c.9-.2 1.5-1 1.5-1.9 0-1.1-.9-2-2-2s-2 .9-2 2C3.5 3 4.1 3.8 5 4v8c-.9.2-1.5 1-1.5 1.9 0 1.1.9 2 2 2s2-.9 2-2c0-.9-.6-1.7-1.5-1.9v-1c.2 0 .5.1.7.1.7 0 1.5-.1 2.2-.6.8-.5 1.4-1.2 1.7-2.1 1.1 0 1.9-.9 1.9-1.9zm-8-4.4c0-.6.4-1 1-1s1 .4 1 1-.4 1-1 1-1-.5-1-1zm2 11.9c0 .6-.4 1-1 1s-1-.4-1-1 .4-1 1-1 1 .4 1 1zm4-6.5c-.6 0-1-.4-1-1s.4-1 1-1 1 .4 1 1-.4 1-1 1z"
- style={{ fill: fill || colors.primary }}
- />
- </Icon>
- );
-}
diff --git a/server/sonar-web/src/main/js/components/icons/CheckIcon.tsx b/server/sonar-web/src/main/js/components/icons/CheckIcon.tsx
deleted file mode 100644
index 1015660e265..00000000000
--- a/server/sonar-web/src/main/js/components/icons/CheckIcon.tsx
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2024 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 Icon, { IconProps } from './Icon';
-
-export default function CheckIcon({ fill = 'currentColor', ...iconProps }: IconProps) {
- return (
- <Icon {...iconProps}>
- <path
- d="M14.92 4.804q0 0.357-0.25 0.607l-7.679 7.679q-0.25 0.25-0.607 0.25t-0.607-0.25l-4.446-4.446q-0.25-0.25-0.25-0.607t0.25-0.607l1.214-1.214q0.25-0.25 0.607-0.25t0.607 0.25l2.625 2.634 5.857-5.866q0.25-0.25 0.607-0.25t0.607 0.25l1.214 1.214q0.25 0.25 0.25 0.607z"
- style={{ fill }}
- />
- </Icon>
- );
-}
diff --git a/server/sonar-web/src/main/js/components/icons/CopyIcon.tsx b/server/sonar-web/src/main/js/components/icons/CopyIcon.tsx
deleted file mode 100644
index 0bfbb29d0b3..00000000000
--- a/server/sonar-web/src/main/js/components/icons/CopyIcon.tsx
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2024 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 Icon, { IconProps } from './Icon';
-
-export default function CopyIcon({ fill = 'currentColor', ...iconProps }: IconProps) {
- return (
- <Icon {...iconProps}>
- <g fill={fill} fillRule="nonzero">
- <path d="M2.931 15.005V3H2v13h9v-.995z" />
- <path d="M10 4.015h3V14H4V1h6v3.015zM9 8V6H8v2H6v1h2v2h1V9h2V8H9z" />
- <path d="M11 1v2h2a2.151 2.151 0 0 0-2-2z" />
- </g>
- </Icon>
- );
-}
diff --git a/server/sonar-web/src/main/js/components/icons/HelpIcon.tsx b/server/sonar-web/src/main/js/components/icons/HelpIcon.tsx
deleted file mode 100644
index ab3629a43a3..00000000000
--- a/server/sonar-web/src/main/js/components/icons/HelpIcon.tsx
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2024 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 Icon, { IconProps } from './Icon';
-
-interface Props extends IconProps {
- fillInner?: string;
-}
-
-export default function HelpIcon({ fill = 'currentColor', fillInner, ...iconProps }: Props) {
- return (
- <Icon {...iconProps}>
- <path
- d="M9.167 12.375v-1.75a.284.284 0 00-.082-.21.284.284 0 00-.21-.082h-1.75a.284.284 0 00-.21.082.284.284 0 00-.082.21v1.75c0 .085.028.155.082.21a.284.284 0 00.21.082h1.75a.284.284 0 00.21-.082.284.284 0 00.082-.21zM11.5 6.25c0-.535-.169-1.03-.506-1.486a3.452 3.452 0 00-1.262-1.057 3.462 3.462 0 00-1.55-.374c-1.476 0-2.603.647-3.381 1.942-.091.146-.067.273.073.383l1.203.911c.042.036.1.055.173.055a.269.269 0 00.228-.11c.322-.413.583-.692.784-.838.206-.146.468-.219.784-.219.291 0 .551.079.779.237.228.158.342.337.342.538 0 .23-.061.416-.183.556-.121.14-.328.276-.62.41a3.13 3.13 0 00-1.052.788c-.32.356-.479.737-.479 1.144v.328c0 .085.028.155.082.21a.284.284 0 00.21.082h1.75a.284.284 0 00.21-.082.284.284 0 00.082-.21c0-.115.065-.266.196-.45a1.54 1.54 0 01.496-.452c.195-.11.344-.196.447-.26a3.84 3.84 0 00.42-.319c.175-.149.31-.294.405-.437a2.407 2.407 0 00.369-1.29zM15 8c0 1.27-.313 2.441-.939 3.514a6.969 6.969 0 01-2.547 2.547A6.848 6.848 0 018 15a6.848 6.848 0 01-3.514-.939 6.969 6.969 0 01-2.547-2.547A6.848 6.848 0 011 8c0-1.27.313-2.441.939-3.514A6.969 6.969 0 014.486 1.94 6.848 6.848 0 018 1c1.27 0 2.441.313 3.514.939a6.969 6.969 0 012.547 2.547A6.848 6.848 0 0115 8z"
- fill={fill}
- />
- {fillInner && (
- <path
- d="M9.167 12.375v-1.75a.284.284 0 00-.082-.21.284.284 0 00-.21-.082h-1.75a.284.284 0 00-.21.082.284.284 0 00-.082.21v1.75c0 .085.028.155.082.21a.284.284 0 00.21.082h1.75a.284.284 0 00.21-.082.284.284 0 00.082-.21zM11.5 6.25c0-.535-.169-1.03-.506-1.486a3.452 3.452 0 00-1.262-1.057 3.462 3.462 0 00-1.55-.374c-1.476 0-2.603.647-3.381 1.942-.091.146-.067.273.073.383l1.203.911c.042.036.1.055.173.055a.269.269 0 00.228-.11c.322-.413.583-.692.784-.838.206-.146.468-.219.784-.219.291 0 .551.079.779.237.228.158.342.337.342.538 0 .23-.061.416-.183.556-.121.14-.328.276-.62.41a3.13 3.13 0 00-1.052.788c-.32.356-.479.737-.479 1.144v.328c0 .085.028.155.082.21a.284.284 0 00.21.082h1.75a.284.284 0 00.21-.082.284.284 0 00.082-.21c0-.115.065-.266.196-.45a1.54 1.54 0 01.496-.452c.195-.11.344-.196.447-.26a3.84 3.84 0 00.42-.319c.175-.149.31-.294.405-.437a2.407 2.407 0 00.369-1.29z"
- fill={fillInner}
- />
- )}
- </Icon>
- );
-}
diff --git a/server/sonar-web/src/main/js/components/icons/ProjectLinkIcon.tsx b/server/sonar-web/src/main/js/components/icons/ProjectLinkIcon.tsx
index 3c0af8dd946..e53096d3e0e 100644
--- a/server/sonar-web/src/main/js/components/icons/ProjectLinkIcon.tsx
+++ b/server/sonar-web/src/main/js/components/icons/ProjectLinkIcon.tsx
@@ -26,35 +26,24 @@ import {
SyncIcon,
} from '@primer/octicons-react';
import React, { FC } from 'react';
-import BugTrackerIcon from './BugTrackerIcon';
-import ContinuousIntegrationIcon from './ContinuousIntegrationIcon';
-import DetachIcon from './DetachIcon';
-import HouseIcon from './HouseIcon';
-import { IconProps } from './Icon';
-import SCMIcon from './SCMIcon';
interface ProjectLinkIconProps {
type: string;
- miui?: boolean;
}
-export default function ProjectLinkIcon({
- miui,
- type,
- ...iconProps
-}: IconProps & ProjectLinkIconProps) {
- const getIcon = (): FC<React.PropsWithChildren<IconProps | MIUIIconProps>> => {
+export default function ProjectLinkIcon({ type, ...iconProps }: ProjectLinkIconProps) {
+ const getIcon = (): FC<React.PropsWithChildren<MIUIIconProps>> => {
switch (type) {
case 'issue':
- return miui ? PulseIcon : BugTrackerIcon;
+ return PulseIcon;
case 'homepage':
- return miui ? HomeIcon : HouseIcon;
+ return HomeIcon;
case 'ci':
- return miui ? SyncIcon : ContinuousIntegrationIcon;
+ return SyncIcon;
case 'scm':
- return miui ? FileIcon : SCMIcon;
+ return FileIcon;
default:
- return miui ? LinkExternalIcon : DetachIcon;
+ return LinkExternalIcon;
}
};
diff --git a/server/sonar-web/src/main/js/components/issue/Issue.css b/server/sonar-web/src/main/js/components/issue/Issue.css
index ebe0ca83fc6..c1951b70480 100644
--- a/server/sonar-web/src/main/js/components/issue/Issue.css
+++ b/server/sonar-web/src/main/js/components/issue/Issue.css
@@ -241,10 +241,6 @@
overflow: auto;
}
-.issue .badge-error {
- background-color: var(--badgeRedBackgroundOnIssue);
-}
-
.issue-message-box {
background-color: var(--issueBgColor);
border: 2px solid transparent;
diff --git a/server/sonar-web/src/main/js/components/new-code-definition/NCDAutoUpdateMessage.tsx b/server/sonar-web/src/main/js/components/new-code-definition/NCDAutoUpdateMessage.tsx
index bdc4e6e66dd..e1e8dc53d1e 100644
--- a/server/sonar-web/src/main/js/components/new-code-definition/NCDAutoUpdateMessage.tsx
+++ b/server/sonar-web/src/main/js/components/new-code-definition/NCDAutoUpdateMessage.tsx
@@ -17,6 +17,7 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+import { Banner, Link } from 'design-system';
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { FormattedMessage, useIntl } from 'react-intl';
import { MessageTypes, checkMessageDismissed, setMessageDismissed } from '../../api/messages';
@@ -26,8 +27,6 @@ import { NEW_CODE_PERIOD_CATEGORY } from '../../apps/settings/constants';
import { queryToSearch } from '../../helpers/urls';
import { useNewCodeDefinitionQuery } from '../../queries/newCodeDefinition';
import { Component } from '../../types/types';
-import Link from '../common/Link';
-import DismissableAlertComponent from '../ui/DismissableAlertComponent';
import {
PreviouslyNonCompliantNCD,
isGlobalOrProjectAdmin,
@@ -117,26 +116,23 @@ function NCDAutoUpdateMessage(props: Readonly<NCDAutoUpdateMessageProps>) {
: 'new_code_definition.auto_update.project.message';
return (
- <DismissableAlertComponent
- onDismiss={handleBannerDismiss}
- variant="info"
- display="banner"
- bannerClassName="sw-mb-0"
- >
- <FormattedMessage
- id={bannerMessageId}
- values={{
- date: new Date(updatedAt).toLocaleDateString(),
- days: value,
- link: (
- <Link to={ncdReviewLinkTo}>
- {intl.formatMessage({ id: 'new_code_definition.auto_update.review_link' })}
- </Link>
- ),
- previousDays: previousNonCompliantValue,
- }}
- />
- </DismissableAlertComponent>
+ <Banner onDismiss={handleBannerDismiss} variant="info">
+ <p>
+ <FormattedMessage
+ id={bannerMessageId}
+ values={{
+ date: new Date(updatedAt).toLocaleDateString(),
+ days: value,
+ link: (
+ <Link to={ncdReviewLinkTo}>
+ {intl.formatMessage({ id: 'new_code_definition.auto_update.review_link' })}
+ </Link>
+ ),
+ previousDays: previousNonCompliantValue,
+ }}
+ />
+ </p>
+ </Banner>
);
}
diff --git a/server/sonar-web/src/main/js/components/new-code-definition/NewCodeDefinitionDaysOption.tsx b/server/sonar-web/src/main/js/components/new-code-definition/NewCodeDefinitionDaysOption.tsx
index 0b2be2cfa3a..b47d9bb154e 100644
--- a/server/sonar-web/src/main/js/components/new-code-definition/NewCodeDefinitionDaysOption.tsx
+++ b/server/sonar-web/src/main/js/components/new-code-definition/NewCodeDefinitionDaysOption.tsx
@@ -17,7 +17,13 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import { FlagErrorIcon, InputField, Note, SelectionCard } from 'design-system';
+import {
+ DismissableFlagMessage,
+ FlagErrorIcon,
+ InputField,
+ Note,
+ SelectionCard,
+} from 'design-system';
import { noop } from 'lodash';
import * as React from 'react';
import { useCallback, useEffect, useMemo, useState } from 'react';
@@ -30,8 +36,7 @@ import {
} from '../../helpers/new-code-definition';
import { isDefined } from '../../helpers/types';
import { NewCodeDefinitionType } from '../../types/new-code-definition';
-import DocLink from '../common/DocLink';
-import DismissableAlertComponent from '../ui/DismissableAlertComponent';
+import DocumentationLink from '../common/DocumentationLink';
import { NewCodeDefinitionLevels } from './utils';
export interface Props {
@@ -146,9 +151,8 @@ export default function NewCodeDefinitionDaysOption(props: Props) {
</Note>
{shouldShowAutoUpdateBanner && (
- <DismissableAlertComponent
+ <DismissableFlagMessage
variant="info"
- display="inline"
className="sw-mt-4 sw-max-w-[800px]"
onDismiss={handleBannerDismiss}
>
@@ -161,13 +165,13 @@ export default function NewCodeDefinitionDaysOption(props: Props) {
days: currentDaysValue,
date: isDefined(updatedAt) && new Date(updatedAt).toLocaleDateString(),
link: (
- <DocLink to="/project-administration/clean-as-you-code-settings/defining-new-code/#new-code-definition-options">
+ <DocumentationLink to="/project-administration/clean-as-you-code-settings/defining-new-code/#new-code-definition-options">
{translate('learn_more')}
- </DocLink>
+ </DocumentationLink>
),
}}
/>
- </DismissableAlertComponent>
+ </DismissableFlagMessage>
)}
</div>
)}
diff --git a/server/sonar-web/src/main/js/components/new-code-definition/__tests__/NCDAutoUpdateMessage-test.tsx b/server/sonar-web/src/main/js/components/new-code-definition/__tests__/NCDAutoUpdateMessage-test.tsx
index ae656c65f36..9a83f40f706 100644
--- a/server/sonar-web/src/main/js/components/new-code-definition/__tests__/NCDAutoUpdateMessage-test.tsx
+++ b/server/sonar-web/src/main/js/components/new-code-definition/__tests__/NCDAutoUpdateMessage-test.tsx
@@ -27,7 +27,7 @@ import NewCodeDefinitionServiceMock from '../../../api/mocks/NewCodeDefinitionSe
import { mockComponent } from '../../../helpers/mocks/component';
import { mockLoggedInUser } from '../../../helpers/testMocks';
import { renderAppRoutes } from '../../../helpers/testReactTestingUtils';
-import { byLabelText, byText } from '../../../helpers/testSelector';
+import { byRole, byText } from '../../../helpers/testSelector';
import { NewCodeDefinitionType } from '../../../types/new-code-definition';
import { Component } from '../../../types/types';
import NCDAutoUpdateMessage from '../NCDAutoUpdateMessage';
@@ -64,7 +64,7 @@ describe('Global NCD update notification banner', () => {
});
const ui = {
- dismissButton: byLabelText('alert.dismiss'),
+ dismissButton: byRole('button', { name: 'dismiss' }),
globalBannerContent: byText(/new_code_definition.auto_update.global.message/),
reviewLink: byText('new_code_definition.auto_update.review_link'),
adminNcdMessage: byText('Admin NCD'),
@@ -92,9 +92,7 @@ describe('Global NCD update notification banner', () => {
renderGlobalMessage();
expect(await ui.globalBannerContent.find()).toBeVisible();
const user = userEvent.setup();
- await act(async () => {
- await user.click(ui.dismissButton.get());
- });
+ await user.click(ui.dismissButton.get());
expect(ui.globalBannerContent.query()).not.toBeInTheDocument();
});
@@ -153,7 +151,7 @@ describe('Project NCD update notification banner', () => {
});
const ui = {
- dismissButton: byLabelText('alert.dismiss'),
+ dismissButton: byRole('button', { name: 'dismiss' }),
projectBannerContent: byText(/new_code_definition.auto_update.project.message/),
projectNcdMessage: byText('Project NCD'),
reviewLink: byText('new_code_definition.auto_update.review_link'),
@@ -189,9 +187,7 @@ describe('Project NCD update notification banner', () => {
renderProjectMessage(component);
expect(await ui.projectBannerContent.find()).toBeVisible();
const user = userEvent.setup();
- await act(async () => {
- await user.click(ui.dismissButton.get());
- });
+ await user.click(ui.dismissButton.get());
expect(ui.projectBannerContent.query()).not.toBeInTheDocument();
});
diff --git a/server/sonar-web/src/main/js/components/permissions/AllHoldersList.tsx b/server/sonar-web/src/main/js/components/permissions/AllHoldersList.tsx
index c7779ade579..5350a3ea9c3 100644
--- a/server/sonar-web/src/main/js/components/permissions/AllHoldersList.tsx
+++ b/server/sonar-web/src/main/js/components/permissions/AllHoldersList.tsx
@@ -125,7 +125,7 @@ export default class AllHoldersList extends React.PureComponent<Props> {
selectedPermission={selectedPermission}
users={users}
/>
- <ListFooter count={count} loadMore={this.props.onLoadMore} total={total} useMIUIButtons />
+ <ListFooter count={count} loadMore={this.props.onLoadMore} total={total} />
</>
);
}
diff --git a/server/sonar-web/src/main/js/components/rules/IssueTabViewer.tsx b/server/sonar-web/src/main/js/components/rules/IssueTabViewer.tsx
index c72772b31cd..6fe3d31ec38 100644
--- a/server/sonar-web/src/main/js/components/rules/IssueTabViewer.tsx
+++ b/server/sonar-web/src/main/js/components/rules/IssueTabViewer.tsx
@@ -383,7 +383,7 @@ export class IssueTabViewer extends React.PureComponent<IssueTabViewerProps, Sta
tabs.map((tab) => (
<div
className={classNames({
- hidden: tab.key !== selectedTab.key,
+ 'sw-hidden': tab.key !== selectedTab.key,
})}
key={tab.key}
>
diff --git a/server/sonar-web/src/main/js/components/rules/RuleTabViewer.tsx b/server/sonar-web/src/main/js/components/rules/RuleTabViewer.tsx
index aa7c18eea05..9096c29e98f 100644
--- a/server/sonar-web/src/main/js/components/rules/RuleTabViewer.tsx
+++ b/server/sonar-web/src/main/js/components/rules/RuleTabViewer.tsx
@@ -18,7 +18,7 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import classNames from 'classnames';
-import { ToggleButton } from 'design-system';
+import { ToggleButton, getTabId, getTabPanelId } from 'design-system';
import { cloneDeep, debounce, groupBy, isEqual } from 'lodash';
import * as React from 'react';
import { Location } from 'react-router-dom';
@@ -29,7 +29,6 @@ import { RuleDescriptionSections } from '../../apps/coding-rules/rule';
import { translate } from '../../helpers/l10n';
import { RuleDetails } from '../../types/types';
import { NoticeType } from '../../types/users';
-import { getTabId, getTabPanelId } from '../controls/BoxedTabs';
import withLocation from '../hoc/withLocation';
import MoreInfoRuleDescription from './MoreInfoRuleDescription';
import RuleDescription from './RuleDescription';
diff --git a/server/sonar-web/src/main/js/components/tags/TagsSelector.tsx b/server/sonar-web/src/main/js/components/tags/TagsSelector.tsx
deleted file mode 100644
index 66e028c807b..00000000000
--- a/server/sonar-web/src/main/js/components/tags/TagsSelector.tsx
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2024 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 { translate } from '../../helpers/l10n';
-import MultiSelect from '../common/MultiSelect';
-import './TagsList.css';
-
-interface Props {
- listSize: number;
- onSearch: (query: string) => Promise<void>;
- onSelect: (item: string) => void;
- onUnselect: (item: string) => void;
- selectedTags: string[];
- tags: string[];
-}
-
-export default function TagsSelector(props: Props) {
- return (
- <MultiSelect
- elements={props.tags}
- listSize={props.listSize}
- legend={translate('select_tags')}
- onSearch={props.onSearch}
- onSelect={props.onSelect}
- onUnselect={props.onUnselect}
- placeholder={translate('search.search_for_tags')}
- selectedElements={props.selectedTags}
- validateSearchInput={validateTag}
- />
- );
-}
-
-export function validateTag(value: string) {
- // Allow only a-z, 0-9, '+', '-', '#', '.'
- return value.toLowerCase().replace(/[^-a-z0-9+#.]/gi, '');
-}
diff --git a/server/sonar-web/src/main/js/components/tutorials/TutorialSelectionRenderer.tsx b/server/sonar-web/src/main/js/components/tutorials/TutorialSelectionRenderer.tsx
index b057238a387..35dd791d8f4 100644
--- a/server/sonar-web/src/main/js/components/tutorials/TutorialSelectionRenderer.tsx
+++ b/server/sonar-web/src/main/js/components/tutorials/TutorialSelectionRenderer.tsx
@@ -25,6 +25,7 @@ import {
HoverLink,
LightLabel,
LightPrimary,
+ Spinner,
StandoutLink,
SubTitle,
Title,
@@ -104,7 +105,7 @@ export default function TutorialSelectionRenderer(props: TutorialSelectionRender
DEFAULT_MAIN_BRANCH_NAME;
if (loading) {
- return <i aria-label={translate('loading')} className="spinner" />;
+ return <Spinner />;
}
if (!currentUserCanScanProject) {
diff --git a/server/sonar-web/src/main/js/components/tutorials/__tests__/TutorialSelection-it.tsx b/server/sonar-web/src/main/js/components/tutorials/__tests__/TutorialSelection-it.tsx
index 4814621ae0d..a2db9276d06 100644
--- a/server/sonar-web/src/main/js/components/tutorials/__tests__/TutorialSelection-it.tsx
+++ b/server/sonar-web/src/main/js/components/tutorials/__tests__/TutorialSelection-it.tsx
@@ -28,7 +28,7 @@ import UserTokensMock from '../../../api/mocks/UserTokensMock';
import { mockComponent } from '../../../helpers/mocks/component';
import { mockLoggedInUser } from '../../../helpers/testMocks';
import { renderApp } from '../../../helpers/testReactTestingUtils';
-import { byLabelText, byRole, byText } from '../../../helpers/testSelector';
+import { byRole, byText } from '../../../helpers/testSelector';
import { ComponentPropsType } from '../../../helpers/testUtils';
import { AlmKeys } from '../../../types/alm-settings';
import { Feature } from '../../../types/features';
@@ -69,7 +69,7 @@ beforeEach(() => {
});
const ui = {
- loading: byLabelText('loading'),
+ loading: byText('loading'),
noScanRights: byText('onboarding.tutorial.no_scan_rights'),
chooseTutorialLink: (mode: TutorialModes) =>
byRole('link', { name: `onboarding.tutorial.choose_method.${mode}` }),
diff --git a/server/sonar-web/src/main/js/components/ui/Alert.tsx b/server/sonar-web/src/main/js/components/ui/Alert.tsx
deleted file mode 100644
index 1416d309b47..00000000000
--- a/server/sonar-web/src/main/js/components/ui/Alert.tsx
+++ /dev/null
@@ -1,177 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2024 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 { css } from '@emotion/react';
-import styled from '@emotion/styled';
-import classNames from 'classnames';
-import * as React from 'react';
-import { colors, sizes } from '../../app/theme';
-import { translate } from '../../helpers/l10n';
-import AlertErrorIcon from '../icons/AlertErrorIcon';
-import AlertSuccessIcon from '../icons/AlertSuccessIcon';
-import AlertWarnIcon from '../icons/AlertWarnIcon';
-import InfoIcon from '../icons/InfoIcon';
-import Spinner from './Spinner';
-
-type AlertDisplay = 'banner' | 'inline' | 'block';
-export type AlertVariant = 'error' | 'warning' | 'success' | 'info' | 'loading';
-
-export interface AlertProps {
- display?: AlertDisplay;
- variant: AlertVariant;
- live?: boolean;
-}
-
-const DOUBLE = 2;
-const QUADRUPLE = 4;
-
-const alertInnerIsBannerMixin = () => css`
- min-width: ${sizes.minPageWidth};
- max-width: ${sizes.maxPageWidth};
- margin-left: auto;
- margin-right: auto;
- padding-left: ${sizes.pagePadding};
- padding-right: ${sizes.pagePadding};
- box-sizing: border-box;
-`;
-
-const StyledAlert = styled.div<{
- isInline: boolean;
- color: string;
- backGroundColor: string;
- borderColor: string;
- isBanner: boolean;
-}>`
- border: 1px solid;
- border-radius: 2px;
- margin-bottom: ${sizes.gridSize};
- border-color: ${({ borderColor }) => borderColor};
- background-color: ${({ backGroundColor }) => backGroundColor};
- color: ${({ color }) => color};
- display: ${({ isInline }) => (isInline ? 'inline-block' : 'block')};
-
- :empty {
- display: none;
- }
-
- a,
- .button-link {
- border-color: ${colors.primarya40};
- }
-
- a: hover,
- .button-link:hover {
- border-color: ${colors.darkBlue};
- }
-
- & .alert-inner {
- display: flex;
- align-items: stretch;
- ${({ isBanner }) => (isBanner ? alertInnerIsBannerMixin : null)}
- }
-
- & .alert-icon {
- flex: 0 0 auto;
- display: flex;
- justify-content: center;
- align-items: center;
- width: calc(${({ isBanner }) => (isBanner ? DOUBLE : QUADRUPLE)} * ${sizes.gridSize});
- border-right: ${({ isBanner }) => (!isBanner ? '1px solid' : 'none')};
- border-color: ${({ borderColor }) => borderColor};
- }
-
- & .alert-content {
- flex: 1 1 auto;
- overflow: auto;
- text-align: left;
- padding: ${sizes.gridSize} calc(2 * ${sizes.gridSize});
- }
-`;
-
-function getAlertVariantInfo(variant: AlertVariant) {
- const variantList = {
- error: {
- icon: (
- <AlertErrorIcon label={translate('alert.tooltip.error')} fill={colors.alertIconError} />
- ),
- color: colors.alertTextError,
- borderColor: colors.alertBorderError,
- backGroundColor: colors.alertBackgroundError,
- },
- warning: {
- icon: (
- <AlertWarnIcon label={translate('alert.tooltip.warning')} fill={colors.alertIconWarning} />
- ),
- color: colors.alertTextWarning,
- borderColor: colors.alertBorderWarning,
- backGroundColor: colors.alertBackgroundWarning,
- },
- success: {
- icon: (
- <AlertSuccessIcon
- label={translate('alert.tooltip.success')}
- fill={colors.alertIconSuccess}
- />
- ),
- color: colors.alertTextSuccess,
- borderColor: colors.alertBorderSuccess,
- backGroundColor: colors.alertBackgroundSuccess,
- },
- info: {
- icon: <InfoIcon label={translate('alert.tooltip.info')} fill={colors.alertIconInfo} />,
- color: colors.alertTextInfo,
- borderColor: colors.alertBorderInfo,
- backGroundColor: colors.alertBackgroundInfo,
- },
- loading: {
- icon: <Spinner />,
- color: colors.alertTextInfo,
- borderColor: colors.alertBorderInfo,
- backGroundColor: colors.alertBackgroundInfo,
- },
- } as const;
-
- return variantList[variant];
-}
-
-export function Alert(props: AlertProps & React.HTMLAttributes<HTMLDivElement>) {
- const { className, display, variant, children, live, ...domProps } = props;
- const isInline = display === 'inline';
- const isBanner = display === 'banner';
- const variantInfo = getAlertVariantInfo(variant);
-
- return (
- <StyledAlert
- className={classNames('alert', className)}
- isBanner={isBanner}
- isInline={isInline}
- color={variantInfo.color}
- borderColor={variantInfo.borderColor}
- backGroundColor={variantInfo.backGroundColor}
- {...domProps}
- >
- {children && (
- <div className="alert-inner">
- <div className="alert-icon">{variantInfo.icon}</div>
- <div className="alert-content">{children}</div>
- </div>
- )}
- </StyledAlert>
- );
-}
diff --git a/server/sonar-web/src/main/js/components/ui/CoverageRating.tsx b/server/sonar-web/src/main/js/components/ui/CoverageRating.tsx
deleted file mode 100644
index b2140fca775..00000000000
--- a/server/sonar-web/src/main/js/components/ui/CoverageRating.tsx
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2024 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 { colors } from '../../app/theme';
-import DonutChart from '../../components/charts/DonutChart';
-
-const SIZE_TO_WIDTH_MAPPING = { small: 20, normal: 24, big: 40, huge: 60 };
-const SIZE_TO_THICKNESS_MAPPING = { small: 3, normal: 3, big: 3, huge: 4 };
-
-const FULL_PERCENT = 100;
-
-type SIZE = 'small' | 'normal' | 'big' | 'huge';
-
-export interface CoverageRatingProps {
- muted?: boolean;
- size?: SIZE;
- value?: number | string;
-}
-
-export default function CoverageRating({
- muted = false,
- size = 'normal',
- value,
-}: CoverageRatingProps) {
- let data = [{ value: FULL_PERCENT, fill: colors.gray71 }];
- let padAngle = 0;
-
- if (value != null) {
- const numberValue = Number(value);
- data = [
- { value: numberValue, fill: muted ? colors.gray71 : colors.success500 },
- { value: FULL_PERCENT - numberValue, fill: muted ? 'transparent' : colors.error500 },
- ];
- if (numberValue !== 0 && numberValue < FULL_PERCENT) {
- padAngle = 0.1; // Same for all sizes, because it scales automatically
- }
- }
-
- const width = SIZE_TO_WIDTH_MAPPING[size];
- const thickness = SIZE_TO_THICKNESS_MAPPING[size];
-
- return (
- <DonutChart
- data={data}
- height={width}
- padAngle={padAngle}
- thickness={thickness}
- width={width}
- />
- );
-}
diff --git a/server/sonar-web/src/main/js/components/ui/DismissableAlert.css b/server/sonar-web/src/main/js/components/ui/DismissableAlert.css
deleted file mode 100644
index fcc90edffe0..00000000000
--- a/server/sonar-web/src/main/js/components/ui/DismissableAlert.css
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2024 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.
- */
-.dismissable-alert-banner .dismissable-alert-content {
- max-width: var(--maxPageWidth);
-}
-
-.dismissable-alert-banner .button-icon {
- height: var(--tinyControlHeight);
- width: var(--tinyControlHeight);
-}
diff --git a/server/sonar-web/src/main/js/components/ui/DismissableAlert.tsx b/server/sonar-web/src/main/js/components/ui/DismissableAlert.tsx
index 0b365831788..60f76ff541c 100644
--- a/server/sonar-web/src/main/js/components/ui/DismissableAlert.tsx
+++ b/server/sonar-web/src/main/js/components/ui/DismissableAlert.tsx
@@ -17,22 +17,22 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+import classNames from 'classnames';
+import { Banner, Variant } from 'design-system';
import * as React from 'react';
-import { AlertProps } from '../../components/ui/Alert';
import { get, save } from '../../helpers/storage';
-import './DismissableAlert.css';
-import DismissableAlertComponent from './DismissableAlertComponent';
-export interface DismissableAlertProps extends AlertProps {
+export interface DismissableAlertProps {
alertKey: string;
children?: React.ReactNode;
className?: string;
+ variant: Variant;
}
export const DISMISSED_ALERT_STORAGE_KEY = 'sonarqube.dismissed_alert';
export default function DismissableAlert(props: DismissableAlertProps) {
- const { alertKey, children } = props;
+ const { alertKey, children, className, variant } = props;
const [show, setShow] = React.useState(false);
React.useEffect(() => {
@@ -47,14 +47,15 @@ export default function DismissableAlert(props: DismissableAlertProps) {
};
return !show ? null : (
- <DismissableAlertComponent
+ <Banner
onDismiss={() => {
hideAlert();
setShow(false);
}}
- {...props}
+ className={classNames('sw-w-full', className)}
+ variant={variant}
>
{children}
- </DismissableAlertComponent>
+ </Banner>
);
}
diff --git a/server/sonar-web/src/main/js/components/ui/DismissableAlertComponent.tsx b/server/sonar-web/src/main/js/components/ui/DismissableAlertComponent.tsx
deleted file mode 100644
index f2bbe41acf4..00000000000
--- a/server/sonar-web/src/main/js/components/ui/DismissableAlertComponent.tsx
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2024 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 classNames from 'classnames';
-import * as React from 'react';
-import { translate } from '../../helpers/l10n';
-import { ButtonIcon } from '../controls/buttons';
-import ClearIcon from '../icons/ClearIcon';
-import { Alert, AlertProps } from './Alert';
-
-export interface DismissableAlertComponentProps extends AlertProps {
- bannerClassName?: string;
- className?: string;
- children: React.ReactNode;
- onDismiss: () => void;
-}
-
-export default function DismissableAlertComponent(props: DismissableAlertComponentProps) {
- const { bannerClassName, className, display = 'banner', variant, children, onDismiss } = props;
-
- return (
- <div className={classNames('dismissable-alert-wrapper', className)}>
- <Alert
- className={classNames(`dismissable-alert-${display}`, bannerClassName)}
- display={display}
- variant={variant}
- >
- <div className="display-flex-center dismissable-alert-content">
- <div className="flex-1">{children}</div>
- <ButtonIcon aria-label={translate('alert.dismiss')} onClick={onDismiss}>
- <ClearIcon size={12} thin />
- </ButtonIcon>
- </div>
- </Alert>
- </div>
- );
-}
diff --git a/server/sonar-web/src/main/js/components/ui/DuplicationsRating.css b/server/sonar-web/src/main/js/components/ui/DuplicationsRating.css
deleted file mode 100644
index d96ca84cadc..00000000000
--- a/server/sonar-web/src/main/js/components/ui/DuplicationsRating.css
+++ /dev/null
@@ -1,171 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2024 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.
- */
-.duplications-rating {
- position: relative;
- display: inline-flex;
- vertical-align: top;
- justify-content: center;
- align-items: center;
- width: var(--controlHeight);
- height: var(--controlHeight);
- border: 3px solid var(--orange);
- border-radius: var(--controlHeight);
- box-sizing: border-box;
-}
-
-.duplications-rating-small {
- width: 24px;
- height: 24px;
- border-width: 3px;
-}
-
-.duplications-rating-big {
- width: 40px;
- height: 40px;
- border-width: 3px;
-}
-
-.duplications-rating-huge {
- width: 60px;
- height: 60px;
- border-width: 4px;
- border-radius: 30px;
-}
-
-.duplications-rating-muted {
- border-color: var(--gray71) !important;
-}
-
-.duplications-rating-muted:after {
- background-color: var(--gray71) !important;
-}
-
-.duplications-rating:after {
- border-radius: var(--controlHeight);
- content: '';
-}
-
-.duplications-rating-A {
- border-color: var(--success500);
-}
-
-.duplications-rating-A:after {
- display: none;
-}
-
-.duplications-rating-B {
- border-color: var(--successVariant);
-}
-
-.duplications-rating-B:after {
- width: 6px;
- height: 6px;
- background-color: var(--successVariant);
-}
-
-.duplications-rating-small.duplications-rating-B:after {
- width: 2px;
- height: 2px;
-}
-
-.duplications-rating-big.duplications-rating-B:after {
- width: var(--smallFontSize);
- height: var(--smallFontSize);
-}
-
-.duplications-rating-huge.duplications-rating-B:after {
- width: 18px;
- height: 18px;
-}
-
-.duplications-rating-C {
- border-color: var(--warningVariant);
-}
-
-.duplications-rating-C:after {
- width: 8px;
- height: 8px;
- background-color: var(--warningVariant);
-}
-
-.duplications-rating-small.duplications-rating-C:after {
- width: 6px;
- height: 6px;
-}
-
-.duplications-rating-big.duplications-rating-C:after {
- width: 16px;
- height: 16px;
-}
-
-.duplications-rating-huge.duplications-rating-C:after {
- width: var(--controlHeight);
- height: var(--controlHeight);
-}
-
-.duplications-rating-D {
- border-color: var(--warningAccent);
-}
-
-.duplications-rating-D:after {
- width: var(--smallFontSize);
- height: var(--smallFontSize);
- background-color: var(--warningAccent);
-}
-
-.duplications-rating-small.duplications-rating-D:after {
- width: 8px;
- height: 8px;
-}
-
-.duplications-rating-big.duplications-rating-D:after {
- width: var(--controlHeight);
- height: var(--controlHeight);
-}
-
-.duplications-rating-huge.duplications-rating-D:after {
- width: 36px;
- height: 36px;
-}
-
-.duplications-rating-E {
- border-color: var(--error500);
-}
-
-.duplications-rating-E:after {
- width: 14px;
- height: 14px;
- background-color: var(--error500);
-}
-
-.duplications-rating-small.duplications-rating-E:after {
- width: 10px;
- height: 10px;
-}
-
-.duplications-rating-big.duplications-rating-E:after {
- width: 28px;
- height: 28px;
-}
-
-.duplications-rating-huge.duplications-rating-E:after {
- width: 42px;
- height: 42px;
-}
diff --git a/server/sonar-web/src/main/js/components/ui/DuplicationsRating.tsx b/server/sonar-web/src/main/js/components/ui/DuplicationsRating.tsx
deleted file mode 100644
index f65204196f0..00000000000
--- a/server/sonar-web/src/main/js/components/ui/DuplicationsRating.tsx
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2024 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 classNames from 'classnames';
-import { inRange } from 'lodash';
-import * as React from 'react';
-import './DuplicationsRating.css';
-
-interface Props {
- muted?: boolean;
- size?: 'small' | 'normal' | 'big' | 'huge';
- value?: number;
-}
-
-export default function DuplicationsRating({ muted = false, size = 'normal', value }: Props) {
- const className = classNames('duplications-rating', {
- 'duplications-rating-small': size === 'small',
- 'duplications-rating-big': size === 'big',
- 'duplications-rating-huge': size === 'huge',
- 'duplications-rating-muted': muted || value == null || isNaN(value),
- 'duplications-rating-A': inRange(value || 0, 0, 3),
- 'duplications-rating-B': inRange(value || 0, 3, 5),
- 'duplications-rating-C': inRange(value || 0, 5, 10),
- 'duplications-rating-D': inRange(value || 0, 10, 20),
- 'duplications-rating-E': (value || 0) >= 20,
- });
-
- return <div className={className} />;
-}
diff --git a/server/sonar-web/src/main/js/components/ui/GenericAvatar.tsx b/server/sonar-web/src/main/js/components/ui/GenericAvatar.tsx
deleted file mode 100644
index 7d15fbdb111..00000000000
--- a/server/sonar-web/src/main/js/components/ui/GenericAvatar.tsx
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2024 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 classNames from 'classnames';
-import { getTextColor, stringToColor } from 'design-system';
-import * as React from 'react';
-
-interface Props {
- className?: string;
- name: string;
- round?: boolean;
- size: number;
-}
-
-export default function GenericAvatar({ className, name, round, size }: Props) {
- const color = stringToColor(name);
-
- let text = '';
- const words = name.split(/\s+/).filter((word) => word.length > 0);
- if (words.length >= 2) {
- text = words[0][0] + words[1][0];
- } else if (name.length > 0) {
- text = name[0];
- }
-
- return (
- <div
- className={classNames(className, 'rounded')}
- style={{
- backgroundColor: color,
- borderRadius: round ? '50%' : undefined,
- color: getTextColor(color),
- display: 'inline-block',
- fontSize: Math.min(size / 2, 14),
- fontWeight: 'normal',
- height: size,
- lineHeight: `${size}px`,
- textAlign: 'center',
- verticalAlign: 'top',
- width: size,
- }}
- >
- {text.toUpperCase()}
- </div>
- );
-}
diff --git a/server/sonar-web/src/main/js/components/ui/LegacyAvatar.tsx b/server/sonar-web/src/main/js/components/ui/LegacyAvatar.tsx
deleted file mode 100644
index 27e7b35bcff..00000000000
--- a/server/sonar-web/src/main/js/components/ui/LegacyAvatar.tsx
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2024 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 classNames from 'classnames';
-import * as React from 'react';
-import withAppStateContext from '../../app/components/app-state/withAppStateContext';
-import { AppState } from '../../types/appstate';
-import { GlobalSettingKeys } from '../../types/settings';
-import GenericAvatar from './GenericAvatar';
-
-const GRAVATAR_SIZE_MULTIPLIER = 2;
-
-interface Props {
- appState: AppState;
- className?: string;
- hash?: string;
- name?: string;
- size: number;
-}
-
-/**
- * @deprecated Use Avatar instead
- */
-export function LegacyAvatar(props: Props) {
- const {
- appState: { settings },
- className,
- hash,
- name,
- size,
- } = props;
-
- const enableGravatar = settings[GlobalSettingKeys.EnableGravatar] === 'true';
-
- if (!enableGravatar || !hash) {
- if (!name) {
- return null;
- }
- return <GenericAvatar className={className} name={name} size={size} />;
- }
-
- const gravatarServerUrl = settings[GlobalSettingKeys.GravatarServerUrl] ?? '';
- const url = gravatarServerUrl
- .replace('{EMAIL_MD5}', hash)
- .replace('{SIZE}', String(size * GRAVATAR_SIZE_MULTIPLIER));
-
- return (
- <img
- alt={name}
- className={classNames(className, 'rounded')}
- height={size}
- src={url}
- width={size}
- />
- );
-}
-
-export default withAppStateContext(LegacyAvatar);
diff --git a/server/sonar-web/src/main/js/components/ui/Level.css b/server/sonar-web/src/main/js/components/ui/Level.css
deleted file mode 100644
index 95b991e14b0..00000000000
--- a/server/sonar-web/src/main/js/components/ui/Level.css
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2024 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.
- */
-.level {
- display: inline-flex;
- align-items: center;
- justify-content: center;
- width: auto;
- min-width: 80px;
- padding-left: 9px;
- padding-right: 9px;
- height: var(--controlHeight);
- border-radius: var(--controlHeight);
- box-sizing: border-box;
- color: #fff;
- letter-spacing: 0.02em;
- font-size: var(--baseFontSize);
- font-weight: 400;
-}
-
-.level-small {
- width: auto;
- min-width: 64px;
- padding-left: 9px;
- padding-right: 9px;
- margin-top: -1px;
- margin-bottom: -1px;
- height: var(--smallControlHeight);
- font-size: var(--smallFontSize);
- font-weight: bold;
-}
-
-.level-muted {
- background-color: var(--disabledQualityGate) !important;
-}
-
-a > .level {
- margin-bottom: -1px;
- border-bottom: 1px solid;
- transition: all 0.2s ease;
-}
-
-a > .level:hover {
- opacity: 0.8;
-}
-
-.level-OK {
- background-color: var(--success500);
-}
-
-.level-WARN {
- background-color: var(--orange);
-}
-
-.level-ERROR {
- background-color: var(--error700);
-}
-
-.level-NONE {
- background-color: var(--disabledQualityGate);
-}
-
-.level-NOT_COMPUTED {
- background-color: var(--disabledQualityGate);
-}
diff --git a/server/sonar-web/src/main/js/components/ui/Level.tsx b/server/sonar-web/src/main/js/components/ui/Level.tsx
deleted file mode 100644
index fbf66cbee47..00000000000
--- a/server/sonar-web/src/main/js/components/ui/Level.tsx
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2024 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 classNames from 'classnames';
-import * as React from 'react';
-import { formatMeasure } from '../../helpers/measures';
-import './Level.css';
-
-export interface LevelProps {
- 'aria-label'?: string;
- 'aria-labelledby'?: string;
- className?: string;
- level: string;
- small?: boolean;
- muted?: boolean;
-}
-
-export default function Level(props: LevelProps) {
- const formatted = formatMeasure(props.level, 'LEVEL');
- const className = classNames(props.className, 'level', 'level-' + props.level, {
- 'level-small': props.small,
- 'level-muted': props.muted,
- });
-
- return (
- <span
- aria-label={props['aria-label']}
- aria-labelledby={props['aria-labelledby']}
- className={className}
- >
- {formatted}
- </span>
- );
-}
diff --git a/server/sonar-web/src/main/js/components/ui/NavBarTabs.css b/server/sonar-web/src/main/js/components/ui/NavBarTabs.css
deleted file mode 100644
index a19b69e730f..00000000000
--- a/server/sonar-web/src/main/js/components/ui/NavBarTabs.css
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2024 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.
- */
-.navbar-tabs {
- display: flex;
- align-items: center;
- clear: left;
- height: var(--controlHeight);
- margin-top: var(--gridSize);
-}
-
-.navbar-tabs > li + li {
- margin-left: 20px;
-}
-
-.navbar-tabs > li > a,
-.navbar-tabs > li > .button-link {
- display: block;
- height: var(--controlHeight);
- line-height: 16px;
- padding-top: 2px;
- border-bottom: 3px solid transparent;
- box-sizing: border-box;
- color: var(--baseFontColor);
- transition: none;
-}
-
-.navbar-tabs > li > a.active,
-.navbar-tabs > li > a:hover,
-.navbar-tabs > li > a:focus,
-.navbar-tabs > li > .button-link.active,
-.navbar-tabs > li > .button-link:hover,
-.navbar-tabs > li > .button-link:focus {
- border-bottom-color: var(--blue);
-}
diff --git a/server/sonar-web/src/main/js/components/ui/NavBarTabs.tsx b/server/sonar-web/src/main/js/components/ui/NavBarTabs.tsx
deleted file mode 100644
index 406636c2e83..00000000000
--- a/server/sonar-web/src/main/js/components/ui/NavBarTabs.tsx
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2024 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 classNames from 'classnames';
-import * as React from 'react';
-import './NavBarTabs.css';
-
-interface Props extends React.HTMLAttributes<HTMLUListElement> {
- children?: React.ReactNode;
- className?: string;
-}
-
-export default function NavBarTabs({ children, className, ...other }: Props) {
- return (
- <ul {...other} className={classNames('it__navbar-tabs navbar-tabs', className)}>
- {children}
- </ul>
- );
-}
diff --git a/server/sonar-web/src/main/js/components/ui/PageShortcutsTooltip.tsx b/server/sonar-web/src/main/js/components/ui/PageShortcutsTooltip.tsx
deleted file mode 100644
index 7b6af58d0b3..00000000000
--- a/server/sonar-web/src/main/js/components/ui/PageShortcutsTooltip.tsx
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2024 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 classNames from 'classnames';
-import * as React from 'react';
-import Tooltip from '../../components/controls/Tooltip';
-import { translate, translateWithParameters } from '../../helpers/l10n';
-
-export interface PageShortcutsTooltipProps {
- className?: string;
- leftAndRightLabel?: string;
- leftLabel?: string;
- upAndDownLabel?: string;
- metaModifierLabel?: string;
-}
-
-export default function PageShortcutsTooltip(props: PageShortcutsTooltipProps) {
- const { className, leftAndRightLabel, leftLabel, upAndDownLabel, metaModifierLabel } = props;
- return (
- <Tooltip
- overlay={
- <div className="small nowrap">
- <div>
- {upAndDownLabel && (
- <span>
- <span className="shortcut-button little-spacer-right">↑</span>
- <span className="shortcut-button spacer-right">↓</span>
- {upAndDownLabel}
- </span>
- )}
- {leftAndRightLabel && (
- <span className={classNames({ 'big-spacer-left': upAndDownLabel })}>
- <span className="shortcut-button little-spacer-right">←</span>
- <span className="shortcut-button spacer-right">→</span>
- {leftAndRightLabel}
- </span>
- )}
- {leftLabel && (
- <span className={classNames({ 'big-spacer-left': upAndDownLabel })}>
- <span className="shortcut-button spacer-right">←</span>
- {leftLabel}
- </span>
- )}
- </div>
- {metaModifierLabel && (
- <div className="big-spacer-top big-padded-top bordered-top">
- <span className="shortcut-button little-spacer-right">alt</span>
- <span className="little-spacer-right">+</span>
- <span className="shortcut-button little-spacer-right">↑</span>
- <span className="shortcut-button spacer-right">↓</span>
- <span className="shortcut-button little-spacer-right">←</span>
- <span className="shortcut-button spacer-right">→</span>
- {metaModifierLabel}
- </div>
- )}
- </div>
- }
- >
- <aside
- aria-label={`
- ${translate('shortcuts.on_page.intro')}
- ${
- upAndDownLabel
- ? translateWithParameters('shortcuts.on_page.up_down_x', upAndDownLabel)
- : ''
- }
- ${
- leftAndRightLabel
- ? translateWithParameters('shortcuts.on_page.left_right_x', leftAndRightLabel)
- : ''
- }
- ${leftLabel ? translateWithParameters('shortcuts.on_page.left_x', leftLabel) : ''}
- ${
- metaModifierLabel
- ? translateWithParameters('shortcuts.on_page.meta_x', metaModifierLabel)
- : ''
- }
- `}
- className={classNames(
- className,
- 'page-shortcuts-tooltip note text-center display-inline-block',
- )}
- >
- <div aria-hidden>
- <div>
- <span className="shortcut-button shortcut-button-tiny">↑</span>
- </div>
- <div>
- <span className="shortcut-button shortcut-button-tiny">←</span>
- <span className="shortcut-button shortcut-button-tiny">↓</span>
- <span className="shortcut-button shortcut-button-tiny">→</span>
- </div>
- </div>
- </aside>
- </Tooltip>
- );
-}
diff --git a/server/sonar-web/src/main/js/components/ui/Spinner.css b/server/sonar-web/src/main/js/components/ui/Spinner.css
deleted file mode 100644
index ad0a6e8b477..00000000000
--- a/server/sonar-web/src/main/js/components/ui/Spinner.css
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2024 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.
- */
-.spinner {
- position: relative;
- vertical-align: middle;
- width: 16px;
- height: 16px;
- border: 2px solid var(--blue);
- border-radius: 50%;
- animation: spin 0.75s infinite linear;
-}
-
-.spinner:before,
-.spinner:after {
- left: -2px;
- top: -2px;
- display: none;
- position: absolute;
- content: '';
- width: inherit;
- height: inherit;
- border: inherit;
- border-radius: inherit;
-}
-
-.spinner,
-.spinner:before,
-.spinner:after {
- display: inline-block;
- box-sizing: border-box;
- border-color: transparent;
- border-top-color: var(--info500);
- animation-duration: 1.2s;
-}
-
-.spinner:before {
- transform: rotate(120deg);
-}
-
-.spinner:after {
- transform: rotate(240deg);
-}
-
-.spinner-placeholder {
- position: relative;
- display: inline-block;
- vertical-align: middle;
- width: 16px;
- height: 16px;
- visibility: hidden;
-}
-
-@keyframes spin {
- from {
- transform: rotate(0deg);
- }
-
- to {
- transform: rotate(360deg);
- }
-}
diff --git a/server/sonar-web/src/main/js/components/ui/Spinner.tsx b/server/sonar-web/src/main/js/components/ui/Spinner.tsx
deleted file mode 100644
index 24b0ea25f57..00000000000
--- a/server/sonar-web/src/main/js/components/ui/Spinner.tsx
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2024 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 classNames from 'classnames';
-import * as React from 'react';
-import { translate } from '../../helpers/l10n';
-import './Spinner.css';
-
-interface Props {
- ariaLabel?: string;
- children?: React.ReactNode;
- className?: string;
- customSpinner?: JSX.Element;
- loading?: boolean;
-}
-
-export default function Spinner(props: Props) {
- const {
- ariaLabel = translate('loading'),
- children,
- className,
- customSpinner,
- loading = true,
- } = props;
-
- if (customSpinner) {
- return <>{loading ? customSpinner : children}</>;
- }
-
- return (
- <>
- <div className="sw-overflow-hidden sw-relative">
- <i
- aria-live="polite"
- data-testid="spinner"
- className={classNames('spinner', className, {
- 'sw-sr-only sw-left-[-10000px]': !loading,
- 'is-loading': loading,
- })}
- >
- {loading && <span className="sw-sr-only">{ariaLabel}</span>}
- </i>
- </div>
- {!loading && children}
- </>
- );
-}
diff --git a/server/sonar-web/src/main/js/components/ui/__tests__/PageShortcutsTooltip-test.tsx b/server/sonar-web/src/main/js/components/ui/__tests__/PageShortcutsTooltip-test.tsx
deleted file mode 100644
index 610b40d59c5..00000000000
--- a/server/sonar-web/src/main/js/components/ui/__tests__/PageShortcutsTooltip-test.tsx
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2024 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 { screen } from '@testing-library/react';
-import userEvent from '@testing-library/user-event';
-import * as React from 'react';
-import { renderComponent } from '../../../helpers/testReactTestingUtils';
-import PageShortcutsTooltip, { PageShortcutsTooltipProps } from '../PageShortcutsTooltip';
-
-const leftAndRightLabel = 'left & right';
-const leftLabel = 'left';
-const upAndDownLabel = 'up & down';
-const metaModifierLabel = 'meta';
-
-it('should render all the labels', async () => {
- const user = userEvent.setup();
-
- renderPageShortcutsTooltip({
- leftAndRightLabel,
- leftLabel,
- upAndDownLabel,
- metaModifierLabel,
- });
-
- await user.hover(
- screen.getByLabelText(
- 'shortcuts.on_page.intro shortcuts.on_page.up_down_x.up & down shortcuts.on_page.left_right_x.left & right shortcuts.on_page.left_x.left shortcuts.on_page.meta_x.meta',
- ),
- );
-
- expect(await screen.findByText(leftAndRightLabel)).toBeInTheDocument();
- expect(screen.getByText(leftLabel)).toBeInTheDocument();
- expect(screen.getByText(upAndDownLabel)).toBeInTheDocument();
- expect(screen.getByText(metaModifierLabel)).toBeInTheDocument();
-});
-
-it('should render left & right labels without up&down', async () => {
- const user = userEvent.setup();
-
- renderPageShortcutsTooltip({
- leftAndRightLabel,
- leftLabel,
- });
-
- await user.hover(
- screen.getByLabelText(
- 'shortcuts.on_page.intro shortcuts.on_page.left_right_x.left & right shortcuts.on_page.left_x.left',
- ),
- );
-
- expect(await screen.findByText(leftAndRightLabel)).toBeInTheDocument();
- expect(screen.getByText(leftLabel)).toBeInTheDocument();
-});
-
-function renderPageShortcutsTooltip(props: Partial<PageShortcutsTooltipProps> = {}) {
- return renderComponent(<PageShortcutsTooltip {...props} />);
-}
diff --git a/server/sonar-web/src/main/js/components/ui/__tests__/Spinner-test.tsx b/server/sonar-web/src/main/js/components/ui/__tests__/Spinner-test.tsx
deleted file mode 100644
index c9d321d94bf..00000000000
--- a/server/sonar-web/src/main/js/components/ui/__tests__/Spinner-test.tsx
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2024 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 { render, screen } from '@testing-library/react';
-import * as React from 'react';
-import Spinner from '../Spinner';
-
-it('can be controlled by the loading prop', () => {
- const { rerender } = renderSpinner({ loading: true });
- expect(screen.getByText('loading')).toBeInTheDocument();
-
- rerender(prepareSpinner({ loading: false }));
- expect(screen.queryByText('loading')).not.toBeInTheDocument();
-});
-
-function renderSpinner(props: Partial<Parameters<typeof Spinner>[0]> = {}) {
- // We don't use our renderComponent() helper here, as we have some tests that
- // require changes in props.
- return render(prepareSpinner(props));
-}
-
-function prepareSpinner(props: Partial<Parameters<typeof Spinner>[0]> = {}) {
- return <Spinner ariaLabel="loading" {...props} />;
-}
diff --git a/server/sonar-web/src/main/js/components/ui/popups.tsx b/server/sonar-web/src/main/js/components/ui/popups.tsx
index 3932682648c..ad732d3dd99 100644
--- a/server/sonar-web/src/main/js/components/ui/popups.tsx
+++ b/server/sonar-web/src/main/js/components/ui/popups.tsx
@@ -20,11 +20,8 @@
/* eslint-disable prefer-destructuring */
import classNames from 'classnames';
-import { throttle } from 'lodash';
import * as React from 'react';
-import { createPortal, findDOMNode } from 'react-dom';
import ClickEventBoundary from '../controls/ClickEventBoundary';
-import ScreenPositionFixer from '../controls/ScreenPositionFixer';
import './popups.css';
/**
@@ -81,216 +78,14 @@ function PopupBase(props: PopupProps, ref: React.Ref<HTMLDivElement>) {
return inner;
}
-const PopupWithRef = React.forwardRef(PopupBase);
-PopupWithRef.displayName = 'Popup';
-
-export const Popup = PopupWithRef;
-
interface PopupArrowProps {
style?: React.CSSProperties;
}
-export function PopupArrow(props: PopupArrowProps) {
+function PopupArrow(props: PopupArrowProps) {
return <div className="popup-arrow" style={props.style} />;
}
-interface PortalPopupProps extends Omit<PopupProps, 'arrowStyle' | 'style'> {
- arrowOffset?: number;
- children: React.ReactNode;
- overlay: React.ReactNode;
-}
-
-interface Measurements {
- height: number;
- left: number;
- top: number;
- width: number;
-}
-
-type State = Partial<Measurements>;
-
-function isMeasured(state: State): state is Measurements {
- return state.height !== undefined;
-}
-
-export class PortalPopup extends React.Component<PortalPopupProps, State> {
- mounted = false;
- popupNode = React.createRef<HTMLDivElement>();
- throttledPositionTooltip: () => void;
-
- constructor(props: PortalPopupProps) {
- super(props);
- this.state = {};
- this.throttledPositionTooltip = throttle(this.positionPopup, 10);
- }
-
- componentDidMount() {
- this.mounted = true;
- this.positionPopup();
- this.addEventListeners();
- }
-
- componentDidUpdate(prevProps: PortalPopupProps) {
- if (this.props.placement !== prevProps.placement || this.props.overlay !== prevProps.overlay) {
- this.positionPopup();
- }
- }
-
- componentWillUnmount() {
- this.mounted = false;
- this.removeEventListeners();
- }
-
- addEventListeners = () => {
- window.addEventListener('resize', this.throttledPositionTooltip);
- window.addEventListener('scroll', this.throttledPositionTooltip);
- };
-
- removeEventListeners = () => {
- window.removeEventListener('resize', this.throttledPositionTooltip);
- window.removeEventListener('scroll', this.throttledPositionTooltip);
- };
-
- getPlacement = (): PopupPlacement => {
- return this.props.placement || PopupPlacement.Bottom;
- };
-
- adjustArrowPosition = (
- placement: PopupPlacement,
- { leftFix, topFix }: { leftFix: number; topFix: number },
- ) => {
- const { arrowOffset = 0 } = this.props;
- switch (placement) {
- case PopupPlacement.Bottom:
- case PopupPlacement.BottomLeft:
- case PopupPlacement.BottomRight:
- case PopupPlacement.TopLeft:
- return { marginLeft: -leftFix + arrowOffset };
- default:
- return { marginTop: -topFix + arrowOffset };
- }
- };
-
- positionPopup = () => {
- // `findDOMNode(this)` will search for the DOM node for the current component
- // first it will find a React.Fragment (see `render`),
- // so it will get the DOM node of the first child, i.e. DOM node of `this.props.children`
- // docs: https://reactjs.org/docs/refs-and-the-dom.html#exposing-dom-refs-to-parent-components
-
- // eslint-disable-next-line react/no-find-dom-node
- const toggleNode = findDOMNode(this);
-
- if (toggleNode && toggleNode instanceof Element && this.popupNode.current) {
- const toggleRect = toggleNode.getBoundingClientRect();
- const { width, height } = this.popupNode.current.getBoundingClientRect();
- let left = 0;
- let top = 0;
-
- switch (this.getPlacement()) {
- case PopupPlacement.Bottom:
- left = toggleRect.left + toggleRect.width / 2 - width / 2;
- top = toggleRect.top + toggleRect.height;
- break;
- case PopupPlacement.BottomLeft:
- left = toggleRect.left;
- top = toggleRect.top + toggleRect.height;
- break;
- case PopupPlacement.BottomRight:
- left = toggleRect.left + toggleRect.width - width;
- top = toggleRect.top + toggleRect.height;
- break;
- case PopupPlacement.LeftTop:
- left = toggleRect.left - width;
- top = toggleRect.top;
- break;
- case PopupPlacement.RightTop:
- left = toggleRect.left + toggleRect.width;
- top = toggleRect.top;
- break;
- case PopupPlacement.RightBottom:
- left = toggleRect.left + toggleRect.width;
- top = toggleRect.top + toggleRect.height - height;
- break;
- case PopupPlacement.TopLeft:
- left = toggleRect.left;
- top = toggleRect.top - height;
- break;
- }
-
- // save width and height (and later set in `render`) to avoid resizing the popup element,
- // when it's placed close to the window edge
- this.setState({
- left: window.pageXOffset + left,
- top: window.pageYOffset + top,
- width,
- height,
- });
- }
- };
-
- renderActual = ({ leftFix = 0, topFix = 0 }) => {
- const { className, overlay, noPadding } = this.props;
- const placement = this.getPlacement();
- let arrowStyle;
- let style;
- if (isMeasured(this.state)) {
- style = {
- left: this.state.left + leftFix,
- top: this.state.top + topFix,
- width: this.state.width,
- height: this.state.height,
- };
- arrowStyle = this.adjustArrowPosition(placement, { leftFix, topFix });
- }
-
- return (
- <Popup
- arrowStyle={arrowStyle}
- className={className}
- noPadding={noPadding}
- placement={placement}
- ref={this.popupNode}
- style={style}
- >
- {overlay}
- </Popup>
- );
- };
-
- render() {
- return (
- <>
- {this.props.children}
- {this.props.overlay && (
- <PortalWrapper>
- <ScreenPositionFixer ready={isMeasured(this.state)}>
- {this.renderActual}
- </ScreenPositionFixer>
- </PortalWrapper>
- )}
- </>
- );
- }
-}
-
-class PortalWrapper extends React.Component<React.PropsWithChildren> {
- el: HTMLElement;
-
- constructor(props: {}) {
- super(props);
- this.el = document.createElement('div');
- this.el.classList.add('popup-portal');
- }
-
- componentDidMount() {
- document.body.appendChild(this.el);
- }
-
- componentWillUnmount() {
- document.body.removeChild(this.el);
- }
-
- render() {
- return createPortal(this.props.children, this.el);
- }
-}
+const PopupWithRef = React.forwardRef(PopupBase);
+PopupWithRef.displayName = 'Popup';
+export const Popup = PopupWithRef;
diff --git a/server/sonar-web/src/main/js/components/upgrade/SystemUpgradeButton.tsx b/server/sonar-web/src/main/js/components/upgrade/SystemUpgradeButton.tsx
index b028aa6c3c6..211a546eb5c 100644
--- a/server/sonar-web/src/main/js/components/upgrade/SystemUpgradeButton.tsx
+++ b/server/sonar-web/src/main/js/components/upgrade/SystemUpgradeButton.tsx
@@ -17,10 +17,10 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+import { ButtonSecondary } from 'design-system';
import * as React from 'react';
import { translate } from '../../helpers/l10n';
import { SystemUpgrade } from '../../types/system';
-import { Button } from '../controls/buttons';
import SystemUpgradeForm from './SystemUpgradeForm';
import { groupUpgrades, sortUpgrades, UpdateUseCase } from './utils';
@@ -45,9 +45,9 @@ export default function SystemUpgradeButton(props: Readonly<Props>) {
return (
<>
- <Button className="sw-ml-2" onClick={openSystemUpgradeForm}>
+ <ButtonSecondary className="sw-ml-2" onClick={openSystemUpgradeForm}>
{translate('learn_more')}
- </Button>
+ </ButtonSecondary>
{isSystemUpgradeFormOpen && (
<SystemUpgradeForm
onClose={closeSystemUpgradeForm}
diff --git a/server/sonar-web/src/main/js/components/upgrade/SystemUpgradeForm.tsx b/server/sonar-web/src/main/js/components/upgrade/SystemUpgradeForm.tsx
index e09e96cd8d7..7d65a0dc3aa 100644
--- a/server/sonar-web/src/main/js/components/upgrade/SystemUpgradeForm.tsx
+++ b/server/sonar-web/src/main/js/components/upgrade/SystemUpgradeForm.tsx
@@ -17,16 +17,13 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+import { FlagMessage, Link, Modal, Variant } from 'design-system';
import { filter, flatMap, isEmpty, negate } from 'lodash';
import * as React from 'react';
import withAppStateContext from '../../app/components/app-state/withAppStateContext';
import { translate } from '../../helpers/l10n';
import { AppState } from '../../types/appstate';
import { SystemUpgrade } from '../../types/system';
-import Link from '../common/Link';
-import Modal from '../controls/Modal';
-import { ResetButtonLink } from '../controls/buttons';
-import { Alert, AlertVariant } from '../ui/Alert';
import SystemUpgradeItem from './SystemUpgradeItem';
import { SYSTEM_VERSION_REGEXP, UpdateUseCase } from './utils';
@@ -38,7 +35,7 @@ interface Props {
updateUseCase?: UpdateUseCase;
}
-const MAP_ALERT: { [key in UpdateUseCase]?: AlertVariant } = {
+const MAP_ALERT: { [key in UpdateUseCase]?: Variant } = {
[UpdateUseCase.NewPatch]: 'warning',
[UpdateUseCase.PreLTS]: 'warning',
[UpdateUseCase.PreviousLTS]: 'error',
@@ -79,38 +76,34 @@ export function SystemUpgradeForm(props: Readonly<Props>) {
}
return (
- <Modal contentLabel={header} onRequestClose={onClose}>
- <div className="modal-head">
- <h2>{header}</h2>
- </div>
-
- <div className="modal-body">
- {alertVariant && updateUseCase && (
- <Alert variant={alertVariant} className={`it__upgrade-alert-${updateUseCase}`}>
- {translate('admin_notification.update', updateUseCase)}
- </Alert>
- )}
- {systemUpgradesWithPatch.map((upgrades) => (
- <SystemUpgradeItem
- edition={appState.edition}
- key={upgrades[upgrades.length - 1].version}
- systemUpgrades={upgrades}
- isPatch={upgrades === patches}
- isLTSVersion={upgrades.some((upgrade) => upgrade.version.startsWith(latestLTS))}
- />
- ))}
- </div>
- <div className="modal-foot">
- <Link
- className="pull-left link-no-underline display-flex-center"
- to="https://www.sonarsource.com/products/sonarqube/downloads/?referrer=sonarqube"
- target="_blank"
- >
+ <Modal
+ headerTitle={header}
+ onClose={onClose}
+ body={
+ <>
+ {alertVariant && updateUseCase && (
+ <FlagMessage variant={alertVariant} className={`it__upgrade-alert-${updateUseCase}`}>
+ {translate('admin_notification.update', updateUseCase)}
+ </FlagMessage>
+ )}
+ {systemUpgradesWithPatch.map((upgrades) => (
+ <SystemUpgradeItem
+ edition={appState.edition}
+ key={upgrades[upgrades.length - 1].version}
+ systemUpgrades={upgrades}
+ isPatch={upgrades === patches}
+ isLTSVersion={upgrades.some((upgrade) => upgrade.version.startsWith(latestLTS))}
+ />
+ ))}
+ </>
+ }
+ primaryButton={
+ <Link to="https://www.sonarsource.com/products/sonarqube/downloads/?referrer=sonarqube">
{translate('system.see_sonarqube_downloads')}
</Link>
- <ResetButtonLink onClick={onClose}>{translate('cancel')}</ResetButtonLink>
- </div>
- </Modal>
+ }
+ secondaryButtonLabel={translate('cancel')}
+ />
);
}
diff --git a/server/sonar-web/src/main/js/components/upgrade/SystemUpgradeIntermediate.tsx b/server/sonar-web/src/main/js/components/upgrade/SystemUpgradeIntermediate.tsx
index 97ccc4c344b..47c53228c02 100644
--- a/server/sonar-web/src/main/js/components/upgrade/SystemUpgradeIntermediate.tsx
+++ b/server/sonar-web/src/main/js/components/upgrade/SystemUpgradeIntermediate.tsx
@@ -17,11 +17,10 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+import { Accordion, BasicSeparator, Link, Note } from 'design-system';
import * as React from 'react';
import { translate } from '../../helpers/l10n';
import { SystemUpgrade } from '../../types/system';
-import { ButtonLink } from '../controls/buttons';
-import DropdownIcon from '../icons/DropdownIcon';
import DateFormatter from '../intl/DateFormatter';
interface Props {
@@ -49,38 +48,38 @@ export default class SystemUpgradeIntermediate extends React.PureComponent<Props
return (
<div className={this.props.className}>
- <ButtonLink className="little-spacer-bottom" onClick={this.toggleIntermediatVersions}>
- {showMore
- ? translate('system.hide_intermediate_versions')
- : translate('system.show_intermediate_versions')}
- <DropdownIcon className="little-spacer-left" turned={showMore} />
- </ButtonLink>
- {showMore &&
- upgrades.map((upgrade) => (
- <div className="note system-upgrade-intermediate" key={upgrade.version}>
+ <Accordion
+ header={
+ showMore
+ ? translate('system.hide_intermediate_versions')
+ : translate('system.show_intermediate_versions')
+ }
+ open={showMore}
+ onClick={this.toggleIntermediatVersions}
+ >
+ {upgrades.map((upgrade, index) => (
+ <Note className="sw-block sw-mb-4" key={upgrade.version}>
{upgrade.releaseDate && (
<DateFormatter date={upgrade.releaseDate} long>
{(formattedDate) => (
<p>
- <b className="little-spacer-right">SonarQube {upgrade.version}</b>
+ <b className="sw-mr-1">SonarQube {upgrade.version}</b>
{formattedDate}
{upgrade.changeLogUrl && (
- <a
- className="spacer-left"
- href={upgrade.changeLogUrl}
- rel="noopener noreferrer"
- target="_blank"
- >
+ <Link className="sw-ml-2" to={upgrade.changeLogUrl}>
{translate('system.release_notes')}
- </a>
+ </Link>
)}
</p>
)}
</DateFormatter>
)}
- {upgrade.description && <p className="little-spacer-top">{upgrade.description}</p>}
- </div>
+ {upgrade.description && <p className="sw-mt-2">{upgrade.description}</p>}
+
+ {index !== upgrades.length - 1 && <BasicSeparator />}
+ </Note>
))}
+ </Accordion>
</div>
);
}
diff --git a/server/sonar-web/src/main/js/components/upgrade/SystemUpgradeItem.tsx b/server/sonar-web/src/main/js/components/upgrade/SystemUpgradeItem.tsx
index 89294461b1e..5fbb2353f22 100644
--- a/server/sonar-web/src/main/js/components/upgrade/SystemUpgradeItem.tsx
+++ b/server/sonar-web/src/main/js/components/upgrade/SystemUpgradeItem.tsx
@@ -17,6 +17,7 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+import { DownloadButton, Link, SubHeading } from 'design-system';
import * as React from 'react';
import { FormattedMessage } from 'react-intl';
import {
@@ -27,8 +28,7 @@ import {
import { translate, translateWithParameters } from '../../helpers/l10n';
import { EditionKey } from '../../types/editions';
import { SystemUpgrade } from '../../types/system';
-import DocLink from '../common/DocLink';
-import Link from '../common/Link';
+import DocumentationLink from '../common/DocumentationLink';
import DateFormatter from '../intl/DateFormatter';
import SystemUpgradeIntermediate from './SystemUpgradeIntermediate';
@@ -55,18 +55,17 @@ export default function SystemUpgradeItem(props: SystemUpgradeItemProps) {
return (
<div className="system-upgrade-version it__upgrade-list-item">
- <h3 className="h1 spacer-bottom">
+ <SubHeading as="h3">
<strong>{header}</strong>
{!isPatch && (
<Link
- className="spacer-left medium"
+ className="sw-ml-2"
to="https://www.sonarsource.com/products/sonarqube/whats-new/?referrer=sonarqube"
- target="_blank"
>
{translate('system.see_whats_new')}
</Link>
)}
- </h3>
+ </SubHeading>
<p>
<FormattedMessage
defaultMessage={translate('system.version_is_availble')}
@@ -74,8 +73,8 @@ export default function SystemUpgradeItem(props: SystemUpgradeItemProps) {
values={{ version: <b>SonarQube {lastUpgrade.version}</b> }}
/>
</p>
- <p className="spacer-top">{lastUpgrade.description}</p>
- <div className="big-spacer-top">
+ <p className="sw-mt-2">{lastUpgrade.description}</p>
+ <div className="sw-mt-4">
{lastUpgrade.releaseDate && (
<DateFormatter date={lastUpgrade.releaseDate} long>
{(formattedDate) => (
@@ -84,25 +83,23 @@ export default function SystemUpgradeItem(props: SystemUpgradeItemProps) {
</DateFormatter>
)}
{lastUpgrade.changeLogUrl && (
- <Link className="spacer-left" to={lastUpgrade.changeLogUrl} target="_blank">
+ <Link className="sw-ml-2" to={lastUpgrade.changeLogUrl}>
{translate('system.release_notes')}
</Link>
)}
</div>
- <SystemUpgradeIntermediate className="spacer-top" upgrades={systemUpgrades.slice(1)} />
- <div className="big-spacer-top">
- <a
- className="button"
- download={getEditionDownloadFilename(downloadUrl)}
- href={downloadUrl}
- rel="noopener noreferrer"
- target="_blank"
- >
+ <SystemUpgradeIntermediate className="sw-mt-2" upgrades={systemUpgrades.slice(1)} />
+ <div className="sw-mt-4">
+ <DownloadButton download={getEditionDownloadFilename(downloadUrl)} href={downloadUrl}>
{translateWithParameters('system.download_x', lastUpgrade.version)}
- </a>
- <DocLink className="spacer-left" to="/setup-and-upgrade/upgrade-the-server/upgrade-guide/">
+ </DownloadButton>
+
+ <DocumentationLink
+ className="sw-ml-2"
+ to="/setup-and-upgrade/upgrade-the-server/upgrade-guide/"
+ >
{translate('system.how_to_upgrade')}
- </DocLink>
+ </DocumentationLink>
</div>
</div>
);
diff --git a/server/sonar-web/src/main/js/helpers/search.tsx b/server/sonar-web/src/main/js/helpers/search.tsx
index b1bc97a005d..9dff03c7635 100644
--- a/server/sonar-web/src/main/js/helpers/search.tsx
+++ b/server/sonar-web/src/main/js/helpers/search.tsx
@@ -31,3 +31,8 @@ export function highlightTerm(str: string, term: string) {
str
);
}
+
+export interface LabelValueSelectOption<V = string> {
+ label: string;
+ value: V;
+}
diff --git a/server/sonar-web/src/main/js/types/extension.ts b/server/sonar-web/src/main/js/types/extension.ts
index f9ce8d25fc0..b6f5d72de02 100644
--- a/server/sonar-web/src/main/js/types/extension.ts
+++ b/server/sonar-web/src/main/js/types/extension.ts
@@ -17,13 +17,15 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+
+import { Theme } from '@emotion/react';
import { QueryClient } from '@tanstack/react-query';
-import { Theme } from 'design-system';
import { IntlShape } from 'react-intl';
import { Location, Router } from '../components/hoc/withRouter';
import { AppState } from './appstate';
+import { BranchLike } from './branch-like';
import { L10nBundle } from './l10nBundle';
-import { Component, Dict } from './types';
+import { Component } from './types';
import { CurrentUser, HomePage } from './users';
export enum AdminPageExtension {
@@ -31,39 +33,36 @@ export enum AdminPageExtension {
}
export interface ExtensionRegistryEntry {
- start: ExtensionStartMethod;
providesCSSFile: boolean;
+ start: ExtensionStartMethod;
}
-export interface ExtensionStartMethod {
- (params: ExtensionStartMethodParameter | string): ExtensionStartMethodReturnType;
+export type ExtensionStartMethod = (
+ params: ExtensionStartMethodParameter | string,
+) => ExtensionStartMethodReturnType;
+
+export interface ExtensionOptions {
+ branchLike?: BranchLike;
+ component: Component;
+ intl: IntlShape;
+ l10nBundle: L10nBundle;
+ location: Location;
+ router: Router;
+ theme: Theme;
}
-export interface ExtensionStartMethodParameter {
+export interface ExtensionStartMethodParameter extends Omit<ExtensionOptions, 'component'> {
appState: AppState;
- el: HTMLElement | undefined | null;
+ baseUrl: string;
component?: Component;
+ currentUser: CurrentUser;
+ el: HTMLElement | undefined | null;
onBranchesChange?: (updateBranches?: boolean, updatePRs?: boolean) => void;
onComponentChange?: (changes: Partial<Component>) => void;
- currentUser: CurrentUser;
- intl: IntlShape;
- location: Location;
- router: Router;
- theme: {
- colors: Dict<string>;
- sizes: Dict<string>;
- rawSizes: Dict<number>;
- fonts: Dict<string>;
- zIndexes: Dict<string>;
- others: Dict<string>;
- };
- dsTheme: Theme;
- baseUrl: string;
- l10nBundle: L10nBundle;
queryClient: QueryClient;
// See SONAR-16207 and core-extension-enterprise-server/src/main/js/portfolios/components/Header.tsx
// for more information on why we're passing this as a prop to an extension.
updateCurrentUserHomepage: (homepage: HomePage) => void;
}
-export type ExtensionStartMethodReturnType = React.ReactNode | Function | void | undefined | null;
+export type ExtensionStartMethodReturnType = React.ReactNode | Function | void;
diff --git a/server/sonar-web/tailwind.base.config.js b/server/sonar-web/tailwind.base.config.js
index 3352639715a..8f73e0c950b 100644
--- a/server/sonar-web/tailwind.base.config.js
+++ b/server/sonar-web/tailwind.base.config.js
@@ -29,6 +29,7 @@ module.exports = {
auto: 'auto',
default: 'default',
pointer: 'pointer',
+ text: 'text',
'not-allowed': 'not-allowed',
},
// Define font sizes
diff --git a/sonar-core/src/main/resources/org/sonar/l10n/core.properties b/sonar-core/src/main/resources/org/sonar/l10n/core.properties
index 38c11900b09..fd3927826d9 100644
--- a/sonar-core/src/main/resources/org/sonar/l10n/core.properties
+++ b/sonar-core/src/main/resources/org/sonar/l10n/core.properties
@@ -4053,6 +4053,7 @@ overview.activity.variations.first_analysis=First analysis:
# WS API
#
#------------------------------------------------------------------------------
+api_documentation.domain_nav=Web API domains navigation
api_documentation.deprecation_tooltip=An API deprecated in version X.Y will be dropped in version (X+1).0. Example: an API deprecated in 4.1 is supported in 4.X (4.2, 4.3, etc.) and will be dropped in version 5.0.
api_documentation.internal_tooltip=Use at your own risk; internal services are subject to change or removal without notice.
api_documentation.internal_tooltip_v2=Use at your own risk. Shows/hides the internal endpoints, parameters, and other details used for internal services. These are subject to change or removal without notice.
@@ -5160,7 +5161,7 @@ indexation.link_unavailable=The link to these results is unavailable until this
indexation.features_partly_available=Most features are available. Some details only show upon completion. {link}
indexation.features_partly_available.link=More info
indexation.progression={0} out of {1} projects reindexed.
-indexation.progression_with_error={0} out of {1} projects reindexed with some {link}.
+indexation.progression_with_error={count} out of {total} projects reindexed with some {link}.
indexation.progression_with_error.link=tasks failing
indexation.completed=All project data has been reloaded.
indexation.completed_with_error=SonarQube completed the reload of project data. Some {link} causing some projects to remain unavailable.