From cebce15815204aa189f63f9e1b86143b258898d2 Mon Sep 17 00:00:00 2001 From: Stas Vilchik Date: Mon, 29 Jan 2018 14:21:28 +0100 Subject: [PATCH] rewrite rules app with react (#2982) --- .../org/sonarqube/qa/util/QProfileTester.java | 13 + .../util/pageobjects/QualityProfilePage.java | 1 - .../qa/util/pageobjects/RuleDetails.java | 234 ++++++- .../qa/util/pageobjects/RuleItem.java | 19 +- .../qa/util/pageobjects/RulesPage.java | 99 ++- server/sonar-web/package.json | 1 + server/sonar-web/src/main/js/api/issues.ts | 21 +- .../src/main/js/api/quality-profiles.ts | 57 ++ server/sonar-web/src/main/js/api/rules.ts | 85 ++- .../styles/components/search-navigator.css | 10 +- .../src/main/js/app/styles/init/forms.css | 10 +- server/sonar-web/src/main/js/app/types.ts | 67 ++ .../apps/about/components/AboutStandards.js | 2 +- .../coding-rules/bulk-change-modal-view.js | 132 ---- .../coding-rules/bulk-change-popup-view.js | 57 -- .../components/ActivationButton.tsx | 86 +++ .../components/ActivationFormModal.tsx | 259 ++++++++ .../components/ActivationSeverityFacet.tsx | 48 ++ .../js/apps/coding-rules/components/App.tsx | 583 ++++++++++++++++++ .../components/AvailableSinceFacet.tsx | 79 +++ .../coding-rules/components/BulkChange.tsx | 154 +++++ .../components/BulkChangeModal.tsx | 256 ++++++++ .../components/CodingRulesAppContainer.js | 94 --- .../coding-rules/components/ConfirmButton.tsx | 99 +++ .../components/CustomRuleButton.tsx | 83 +++ .../components/CustomRuleFormModal.tsx | 415 +++++++++++++ .../DefaultSeverityFacet.tsx} | 41 +- .../js/apps/coding-rules/components/Facet.tsx | 123 ++++ .../coding-rules/components/FacetsList.tsx | 146 +++++ .../components/InheritanceFacet.tsx | 51 ++ .../coding-rules/components/LanguageFacet.tsx | 75 +++ .../components/LanguageFacetFooter.tsx | 54 ++ .../coding-rules/components/PageActions.tsx | 71 +++ .../coding-rules/components/ProfileFacet.tsx | 171 +++++ .../RemoveExtendedDescriptionModal.tsx | 60 ++ .../components/RepositoryFacet.tsx | 76 +++ .../coding-rules/components/RuleDetails.tsx | 242 ++++++++ .../components/RuleDetailsCustomRules.tsx | 179 ++++++ .../components/RuleDetailsDescription.tsx | 216 +++++++ .../components/RuleDetailsIssues.tsx | 159 +++++ .../components/RuleDetailsMeta.tsx | 236 +++++++ .../components/RuleDetailsParameters.tsx | 55 ++ .../components/RuleDetailsProfiles.tsx | 293 +++++++++ .../components/RuleDetailsTagsPopup.tsx | 90 +++ .../components/RuleInheritanceIcon.tsx | 44 ++ .../coding-rules/components/RuleListItem.tsx | 216 +++++++ .../components/SimilarRulesFilter.tsx | 133 ++++ .../coding-rules/components/StatusFacet.tsx} | 34 +- .../apps/coding-rules/components/TagFacet.tsx | 64 ++ .../coding-rules/components/TemplateFacet.tsx | 62 ++ .../components/TypeFacet.tsx} | 30 +- .../js/apps/coding-rules/confirm-dialog.js | 69 --- .../main/js/apps/coding-rules/controller.js | 200 ------ .../main/js/apps/coding-rules/facets-view.js | 57 -- .../facets/active-severity-facet.js | 61 -- .../facets/available-since-facet.js | 57 -- .../js/apps/coding-rules/facets/base-facet.js | 26 - .../facets/custom-labels-facet.js | 41 -- .../facets/custom-values-facet.js | 87 --- .../coding-rules/facets/inheritance-facet.js | 87 --- .../js/apps/coding-rules/facets/key-facet.js | 40 -- .../coding-rules/facets/language-facet.js | 63 -- .../facets/quality-profile-facet.js | 132 ---- .../apps/coding-rules/facets/query-facet.js | 83 --- .../coding-rules/facets/repository-facet.js | 70 --- .../coding-rules/facets/severity-facet.js | 32 - .../js/apps/coding-rules/facets/tag-facet.js | 46 -- .../coding-rules/facets/template-facet.js | 48 -- .../js/apps/coding-rules/facets/type-facet.js | 31 - .../src/main/js/apps/coding-rules/init.js | 129 ---- .../src/main/js/apps/coding-rules/layout.js | 55 -- .../main/js/apps/coding-rules/models/rule.js | 49 -- .../main/js/apps/coding-rules/models/rules.js | 59 -- .../main/js/apps/coding-rules/models/state.js | 39 -- .../src/main/js/apps/coding-rules/query.ts | 160 +++++ .../src/main/js/apps/coding-rules/routes.ts | 29 +- .../js/apps/coding-rules/rule-details-view.js | 185 ------ .../js/apps/coding-rules/rule-filter-view.js | 41 -- .../rule/custom-rule-creation-view.js | 224 ------- .../coding-rules/rule/custom-rule-view.js | 55 -- .../coding-rules/rule/custom-rules-view.js | 62 -- .../coding-rules/rule/delete-rule-view.js | 37 -- .../rule/profile-activation-view.js | 178 ------ .../rule/rule-description-view.js | 107 ---- .../coding-rules/rule/rule-filter-mixin.js | 42 -- .../coding-rules/rule/rule-issues-view.js | 97 --- .../apps/coding-rules/rule/rule-meta-view.js | 119 ---- .../coding-rules/rule/rule-parameters-view.js | 34 - .../coding-rules/rule/rule-profile-view.js | 180 ------ .../coding-rules/rule/rule-profiles-view.js | 101 --- .../src/main/js/apps/coding-rules/styles.css | 3 - .../coding-rules-bulk-change-modal.hbs | 41 -- .../coding-rules-bulk-change-popup.hbs | 41 -- .../templates/coding-rules-layout.hbs | 20 - .../templates/coding-rules-rule-details.hbs | 14 - .../coding-rules-rule-filter-form.hbs | 38 -- .../coding-rules-workspace-header.hbs | 41 -- .../coding-rules-workspace-list-item.hbs | 71 --- .../templates/coding-rules-workspace-list.hbs | 5 - .../facets/_coding-rules-facet-header.hbs | 6 - .../coding-rules-available-since-facet.hbs | 5 - .../facets/coding-rules-base-facet.hbs | 10 - .../coding-rules-custom-values-facet.hbs | 14 - .../facets/coding-rules-inheritance-facet.hbs | 9 - .../facets/coding-rules-key-facet.hbs | 5 - .../coding-rules-quality-profile-facet.hbs | 13 - .../facets/coding-rules-query-facet.hbs | 18 - .../facets/coding-rules-severity-facet.hbs | 10 - .../facets/coding-rules-template-facet.hbs | 12 - .../facets/coding-rules-type-facet.hbs | 11 - .../coding-rules-custom-rule-creation.hbs | 96 --- .../rule/coding-rules-custom-rule.hbs | 26 - .../rule/coding-rules-custom-rules.hbs | 11 - .../rule/coding-rules-delete-rule.hbs | 14 - .../rule/coding-rules-profile-activation.hbs | 78 --- .../rule/coding-rules-rule-description.hbs | 41 -- .../rule/coding-rules-rule-issues.hbs | 27 - .../templates/rule/coding-rules-rule-meta.hbs | 95 --- .../rule/coding-rules-rule-parameters.hbs | 27 - .../rule/coding-rules-rule-profile.hbs | 82 --- .../rule/coding-rules-rule-profiles.hbs | 19 - .../coding-rules/workspace-header-view.js | 72 --- .../coding-rules/workspace-list-empty-view.js | 29 - .../coding-rules/workspace-list-item-view.js | 145 ----- .../apps/coding-rules/workspace-list-view.js | 48 -- .../component-measures/sidebar/DomainFacet.js | 2 +- .../sidebar/ProjectOverviewFacet.js | 2 +- .../__snapshots__/DomainFacet-test.js.snap | 8 +- .../src/main/js/apps/issues/components/App.js | 2 +- .../apps/issues/components/BulkChangeModal.js | 1 - .../apps/issues/components/IssuesCounter.js | 27 +- .../IssuesContainer-test.js.snap | 30 +- .../js/apps/issues/sidebar/AssigneeFacet.js | 2 +- .../js/apps/issues/sidebar/AuthorFacet.js | 2 +- .../apps/issues/sidebar/CreationDateFacet.js | 2 +- .../js/apps/issues/sidebar/DirectoryFacet.js | 2 +- .../main/js/apps/issues/sidebar/FacetMode.js | 2 +- .../main/js/apps/issues/sidebar/FileFacet.js | 2 +- .../js/apps/issues/sidebar/LanguageFacet.js | 2 +- .../js/apps/issues/sidebar/ModuleFacet.js | 2 +- .../js/apps/issues/sidebar/ProjectFacet.js | 2 +- .../js/apps/issues/sidebar/ResolutionFacet.js | 2 +- .../main/js/apps/issues/sidebar/RuleFacet.js | 6 +- .../js/apps/issues/sidebar/SeverityFacet.js | 2 +- .../main/js/apps/issues/sidebar/Sidebar.js | 1 + .../js/apps/issues/sidebar/StatusFacet.js | 2 +- .../main/js/apps/issues/sidebar/TypeFacet.js | 2 +- .../__snapshots__/AssigneeFacet-test.js.snap | 16 +- .../src/main/js/apps/issues/styles.css | 10 - .../src/main/js/apps/organizations/routes.ts | 5 +- .../src/main/js/apps/overview/meta/Meta.tsx | 1 + .../apps/overview/meta/MetaQualityProfiles.js | 8 +- .../main/js/apps/overview/meta/MetaTags.tsx | 8 +- .../__snapshots__/MetaTags-test.tsx.snap | 1 + .../projects/components/ProjectCardLeak.tsx | 2 +- .../components/ProjectCardOverall.tsx | 2 +- .../changelog/__tests__/Changelog-test.tsx | 2 +- .../__tests__/ComparisonResults-test.tsx | 6 +- .../ProfileActions-test.tsx.snap | 20 +- .../quality-profiles/details/ProfileRules.tsx | 10 +- .../details/ProfileRulesDeprecatedWarning.tsx | 5 +- .../__snapshots__/ProfileRules-test.tsx.snap | 10 +- ...rofileRulesDeprecatedWarning-test.tsx.snap | 11 +- .../ProfileRulesRowOfType-test.tsx.snap | 44 +- .../ProfileRulesRowTotal-test.tsx.snap | 40 +- ...ofileRulesSonarWayComparison-test.tsx.snap | 12 +- .../quality-profiles/home/EvolutionRules.tsx | 7 +- .../main/js/components/common/BubblePopup.tsx | 5 +- .../js/components/common/DeferredSpinner.tsx | 2 +- .../common/FiltersHeader.tsx} | 32 +- .../{MarkdownTips.js => MarkdownTips.tsx} | 5 +- .../main/js/components/common/PageCounter.tsx | 41 ++ .../components/common/action-options-view.js | 133 ---- .../common/templates/_markdown-tips.hbs | 4 - .../js/components/controls/ReloadButton.tsx | 59 ++ .../main/js/components/controls/SearchBox.tsx | 4 +- .../{SearchSelect.js => SearchSelect.tsx} | 66 +- .../__snapshots__/SearchSelect-test.js.snap | 1 - .../js/components/controls/react-select.css | 7 +- .../facet/{FacetBox.js => FacetBox.tsx} | 24 +- .../facet/FacetFooter.tsx} | 30 +- .../facet/{FacetHeader.js => FacetHeader.tsx} | 33 +- .../facet/{FacetItem.js => FacetItem.tsx} | 39 +- .../{FacetItemsList.js => FacetItemsList.tsx} | 13 +- .../{FacetBox-test.js => FacetBox-test.tsx} | 5 +- ...cetFooter-test.js => FacetFooter-test.tsx} | 3 +- ...cetHeader-test.js => FacetHeader-test.tsx} | 5 +- .../{FacetItem-test.js => FacetItem-test.tsx} | 22 +- ...msList-test.js => FacetItemsList-test.tsx} | 3 +- ...ox-test.js.snap => FacetBox-test.tsx.snap} | 1 + ...test.js.snap => FacetFooter-test.tsx.snap} | 1 - ...test.js.snap => FacetHeader-test.tsx.snap} | 0 ...m-test.js.snap => FacetItem-test.tsx.snap} | 24 +- ...t.js.snap => FacetItemsList-test.tsx.snap} | 0 .../components/issue/components/IssueTags.js | 5 +- .../__snapshots__/IssueTags-test.js.snap | 1 + .../js/components/navigator/controller.js | 149 ----- .../js/components/navigator/facets-view.js | 47 -- .../components/navigator/facets/base-facet.js | 122 ---- .../js/components/navigator/models/facet.js | 37 -- .../js/components/navigator/models/facets.js | 25 - .../js/components/navigator/models/state.js | 70 --- .../main/js/components/navigator/router.js | 44 -- .../navigator/workspace-header-view.js | 94 --- .../navigator/workspace-list-item-view.js | 39 -- .../navigator/workspace-list-view.js | 133 ---- .../{SeverityHelper.js => SeverityHelper.tsx} | 14 +- .../shared/{TypeHelper.js => TypeHelper.tsx} | 28 +- .../src/main/js/components/tags/TagsList.tsx | 13 +- .../tags/__tests__/TagsList-test.tsx | 18 +- .../__snapshots__/TagsList-test.tsx.snap | 36 ++ .../components/workspace/views/rule-view.js | 10 +- .../src/main/js/helpers/constants.ts | 2 + .../src/main/js/helpers/handlebars/empty.js | 23 - .../src/main/js/helpers/handlebars/gt.js | 22 - .../main/js/helpers/handlebars/ifLength.js | 23 - .../src/main/js/helpers/handlebars/repeat.js | 26 - server/sonar-web/src/main/js/helpers/query.ts | 36 +- .../src/main/js/helpers/scrolling.ts | 2 +- server/sonar-web/src/main/js/helpers/urls.ts | 32 +- server/sonar-web/yarn.lock | 4 + .../resources/org/sonar/l10n/core.properties | 9 +- .../org/sonarqube/tests/Category6Suite.java | 2 + .../OrganizationQualityProfilesUiTest.java | 2 +- .../sonarqube/tests/rule/RulesPageTest.java | 331 +++++++++- .../should_display_list.html | 2 +- .../should_display_list.html | 7 +- 227 files changed, 6635 insertions(+), 5954 deletions(-) delete mode 100644 server/sonar-web/src/main/js/apps/coding-rules/bulk-change-modal-view.js delete mode 100644 server/sonar-web/src/main/js/apps/coding-rules/bulk-change-popup-view.js create mode 100644 server/sonar-web/src/main/js/apps/coding-rules/components/ActivationButton.tsx create mode 100644 server/sonar-web/src/main/js/apps/coding-rules/components/ActivationFormModal.tsx create mode 100644 server/sonar-web/src/main/js/apps/coding-rules/components/ActivationSeverityFacet.tsx create mode 100644 server/sonar-web/src/main/js/apps/coding-rules/components/App.tsx create mode 100644 server/sonar-web/src/main/js/apps/coding-rules/components/AvailableSinceFacet.tsx create mode 100644 server/sonar-web/src/main/js/apps/coding-rules/components/BulkChange.tsx create mode 100644 server/sonar-web/src/main/js/apps/coding-rules/components/BulkChangeModal.tsx delete mode 100644 server/sonar-web/src/main/js/apps/coding-rules/components/CodingRulesAppContainer.js create mode 100644 server/sonar-web/src/main/js/apps/coding-rules/components/ConfirmButton.tsx create mode 100644 server/sonar-web/src/main/js/apps/coding-rules/components/CustomRuleButton.tsx create mode 100644 server/sonar-web/src/main/js/apps/coding-rules/components/CustomRuleFormModal.tsx rename server/sonar-web/src/main/js/apps/coding-rules/{facets/status-facet.js => components/DefaultSeverityFacet.tsx} (56%) create mode 100644 server/sonar-web/src/main/js/apps/coding-rules/components/Facet.tsx create mode 100644 server/sonar-web/src/main/js/apps/coding-rules/components/FacetsList.tsx create mode 100644 server/sonar-web/src/main/js/apps/coding-rules/components/InheritanceFacet.tsx create mode 100644 server/sonar-web/src/main/js/apps/coding-rules/components/LanguageFacet.tsx create mode 100644 server/sonar-web/src/main/js/apps/coding-rules/components/LanguageFacetFooter.tsx create mode 100644 server/sonar-web/src/main/js/apps/coding-rules/components/PageActions.tsx create mode 100644 server/sonar-web/src/main/js/apps/coding-rules/components/ProfileFacet.tsx create mode 100644 server/sonar-web/src/main/js/apps/coding-rules/components/RemoveExtendedDescriptionModal.tsx create mode 100644 server/sonar-web/src/main/js/apps/coding-rules/components/RepositoryFacet.tsx create mode 100644 server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetails.tsx create mode 100644 server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetailsCustomRules.tsx create mode 100644 server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetailsDescription.tsx create mode 100644 server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetailsIssues.tsx create mode 100644 server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetailsMeta.tsx create mode 100644 server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetailsParameters.tsx create mode 100644 server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetailsProfiles.tsx create mode 100644 server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetailsTagsPopup.tsx create mode 100644 server/sonar-web/src/main/js/apps/coding-rules/components/RuleInheritanceIcon.tsx create mode 100644 server/sonar-web/src/main/js/apps/coding-rules/components/RuleListItem.tsx create mode 100644 server/sonar-web/src/main/js/apps/coding-rules/components/SimilarRulesFilter.tsx rename server/sonar-web/src/main/js/{components/facet/FacetFooter.js => apps/coding-rules/components/StatusFacet.tsx} (61%) create mode 100644 server/sonar-web/src/main/js/apps/coding-rules/components/TagFacet.tsx create mode 100644 server/sonar-web/src/main/js/apps/coding-rules/components/TemplateFacet.tsx rename server/sonar-web/src/main/js/apps/{organizations/components/OrganizationRules.js => coding-rules/components/TypeFacet.tsx} (53%) delete mode 100644 server/sonar-web/src/main/js/apps/coding-rules/confirm-dialog.js delete mode 100644 server/sonar-web/src/main/js/apps/coding-rules/controller.js delete mode 100644 server/sonar-web/src/main/js/apps/coding-rules/facets-view.js delete mode 100644 server/sonar-web/src/main/js/apps/coding-rules/facets/active-severity-facet.js delete mode 100644 server/sonar-web/src/main/js/apps/coding-rules/facets/available-since-facet.js delete mode 100644 server/sonar-web/src/main/js/apps/coding-rules/facets/base-facet.js delete mode 100644 server/sonar-web/src/main/js/apps/coding-rules/facets/custom-labels-facet.js delete mode 100644 server/sonar-web/src/main/js/apps/coding-rules/facets/custom-values-facet.js delete mode 100644 server/sonar-web/src/main/js/apps/coding-rules/facets/inheritance-facet.js delete mode 100644 server/sonar-web/src/main/js/apps/coding-rules/facets/key-facet.js delete mode 100644 server/sonar-web/src/main/js/apps/coding-rules/facets/language-facet.js delete mode 100644 server/sonar-web/src/main/js/apps/coding-rules/facets/quality-profile-facet.js delete mode 100644 server/sonar-web/src/main/js/apps/coding-rules/facets/query-facet.js delete mode 100644 server/sonar-web/src/main/js/apps/coding-rules/facets/repository-facet.js delete mode 100644 server/sonar-web/src/main/js/apps/coding-rules/facets/severity-facet.js delete mode 100644 server/sonar-web/src/main/js/apps/coding-rules/facets/tag-facet.js delete mode 100644 server/sonar-web/src/main/js/apps/coding-rules/facets/template-facet.js delete mode 100644 server/sonar-web/src/main/js/apps/coding-rules/facets/type-facet.js delete mode 100644 server/sonar-web/src/main/js/apps/coding-rules/init.js delete mode 100644 server/sonar-web/src/main/js/apps/coding-rules/layout.js delete mode 100644 server/sonar-web/src/main/js/apps/coding-rules/models/rule.js delete mode 100644 server/sonar-web/src/main/js/apps/coding-rules/models/rules.js delete mode 100644 server/sonar-web/src/main/js/apps/coding-rules/models/state.js create mode 100644 server/sonar-web/src/main/js/apps/coding-rules/query.ts delete mode 100644 server/sonar-web/src/main/js/apps/coding-rules/rule-details-view.js delete mode 100644 server/sonar-web/src/main/js/apps/coding-rules/rule-filter-view.js delete mode 100644 server/sonar-web/src/main/js/apps/coding-rules/rule/custom-rule-creation-view.js delete mode 100644 server/sonar-web/src/main/js/apps/coding-rules/rule/custom-rule-view.js delete mode 100644 server/sonar-web/src/main/js/apps/coding-rules/rule/custom-rules-view.js delete mode 100644 server/sonar-web/src/main/js/apps/coding-rules/rule/delete-rule-view.js delete mode 100644 server/sonar-web/src/main/js/apps/coding-rules/rule/profile-activation-view.js delete mode 100644 server/sonar-web/src/main/js/apps/coding-rules/rule/rule-description-view.js delete mode 100644 server/sonar-web/src/main/js/apps/coding-rules/rule/rule-filter-mixin.js delete mode 100644 server/sonar-web/src/main/js/apps/coding-rules/rule/rule-issues-view.js delete mode 100644 server/sonar-web/src/main/js/apps/coding-rules/rule/rule-meta-view.js delete mode 100644 server/sonar-web/src/main/js/apps/coding-rules/rule/rule-parameters-view.js delete mode 100644 server/sonar-web/src/main/js/apps/coding-rules/rule/rule-profile-view.js delete mode 100644 server/sonar-web/src/main/js/apps/coding-rules/rule/rule-profiles-view.js delete mode 100644 server/sonar-web/src/main/js/apps/coding-rules/templates/coding-rules-bulk-change-modal.hbs delete mode 100644 server/sonar-web/src/main/js/apps/coding-rules/templates/coding-rules-bulk-change-popup.hbs delete mode 100644 server/sonar-web/src/main/js/apps/coding-rules/templates/coding-rules-layout.hbs delete mode 100644 server/sonar-web/src/main/js/apps/coding-rules/templates/coding-rules-rule-details.hbs delete mode 100644 server/sonar-web/src/main/js/apps/coding-rules/templates/coding-rules-rule-filter-form.hbs delete mode 100644 server/sonar-web/src/main/js/apps/coding-rules/templates/coding-rules-workspace-header.hbs delete mode 100644 server/sonar-web/src/main/js/apps/coding-rules/templates/coding-rules-workspace-list-item.hbs delete mode 100644 server/sonar-web/src/main/js/apps/coding-rules/templates/coding-rules-workspace-list.hbs delete mode 100644 server/sonar-web/src/main/js/apps/coding-rules/templates/facets/_coding-rules-facet-header.hbs delete mode 100644 server/sonar-web/src/main/js/apps/coding-rules/templates/facets/coding-rules-available-since-facet.hbs delete mode 100644 server/sonar-web/src/main/js/apps/coding-rules/templates/facets/coding-rules-base-facet.hbs delete mode 100644 server/sonar-web/src/main/js/apps/coding-rules/templates/facets/coding-rules-custom-values-facet.hbs delete mode 100644 server/sonar-web/src/main/js/apps/coding-rules/templates/facets/coding-rules-inheritance-facet.hbs delete mode 100644 server/sonar-web/src/main/js/apps/coding-rules/templates/facets/coding-rules-key-facet.hbs delete mode 100644 server/sonar-web/src/main/js/apps/coding-rules/templates/facets/coding-rules-quality-profile-facet.hbs delete mode 100644 server/sonar-web/src/main/js/apps/coding-rules/templates/facets/coding-rules-query-facet.hbs delete mode 100644 server/sonar-web/src/main/js/apps/coding-rules/templates/facets/coding-rules-severity-facet.hbs delete mode 100644 server/sonar-web/src/main/js/apps/coding-rules/templates/facets/coding-rules-template-facet.hbs delete mode 100644 server/sonar-web/src/main/js/apps/coding-rules/templates/facets/coding-rules-type-facet.hbs delete mode 100644 server/sonar-web/src/main/js/apps/coding-rules/templates/rule/coding-rules-custom-rule-creation.hbs delete mode 100644 server/sonar-web/src/main/js/apps/coding-rules/templates/rule/coding-rules-custom-rule.hbs delete mode 100644 server/sonar-web/src/main/js/apps/coding-rules/templates/rule/coding-rules-custom-rules.hbs delete mode 100644 server/sonar-web/src/main/js/apps/coding-rules/templates/rule/coding-rules-delete-rule.hbs delete mode 100644 server/sonar-web/src/main/js/apps/coding-rules/templates/rule/coding-rules-profile-activation.hbs delete mode 100644 server/sonar-web/src/main/js/apps/coding-rules/templates/rule/coding-rules-rule-description.hbs delete mode 100644 server/sonar-web/src/main/js/apps/coding-rules/templates/rule/coding-rules-rule-issues.hbs delete mode 100644 server/sonar-web/src/main/js/apps/coding-rules/templates/rule/coding-rules-rule-meta.hbs delete mode 100644 server/sonar-web/src/main/js/apps/coding-rules/templates/rule/coding-rules-rule-parameters.hbs delete mode 100644 server/sonar-web/src/main/js/apps/coding-rules/templates/rule/coding-rules-rule-profile.hbs delete mode 100644 server/sonar-web/src/main/js/apps/coding-rules/templates/rule/coding-rules-rule-profiles.hbs delete mode 100644 server/sonar-web/src/main/js/apps/coding-rules/workspace-header-view.js delete mode 100644 server/sonar-web/src/main/js/apps/coding-rules/workspace-list-empty-view.js delete mode 100644 server/sonar-web/src/main/js/apps/coding-rules/workspace-list-item-view.js delete mode 100644 server/sonar-web/src/main/js/apps/coding-rules/workspace-list-view.js rename server/sonar-web/src/main/js/{apps/issues/components/FiltersHeader.js => components/common/FiltersHeader.tsx} (66%) rename server/sonar-web/src/main/js/components/common/{MarkdownTips.js => MarkdownTips.tsx} (94%) create mode 100644 server/sonar-web/src/main/js/components/common/PageCounter.tsx delete mode 100644 server/sonar-web/src/main/js/components/common/action-options-view.js delete mode 100644 server/sonar-web/src/main/js/components/common/templates/_markdown-tips.hbs create mode 100644 server/sonar-web/src/main/js/components/controls/ReloadButton.tsx rename server/sonar-web/src/main/js/components/controls/{SearchSelect.js => SearchSelect.tsx} (71%) rename server/sonar-web/src/main/js/components/facet/{FacetBox.js => FacetBox.tsx} (68%) rename server/sonar-web/src/main/js/{helpers/handlebars/avatarHelper.js => components/facet/FacetFooter.tsx} (62%) rename server/sonar-web/src/main/js/components/facet/{FacetHeader.js => FacetHeader.tsx} (86%) rename server/sonar-web/src/main/js/components/facet/{FacetItem.js => FacetItem.tsx} (70%) rename server/sonar-web/src/main/js/components/facet/{FacetItemsList.js => FacetItemsList.tsx} (85%) rename server/sonar-web/src/main/js/components/facet/__tests__/{FacetBox-test.js => FacetBox-test.tsx} (94%) rename server/sonar-web/src/main/js/components/facet/__tests__/{FacetFooter-test.js => FacetFooter-test.tsx} (96%) rename server/sonar-web/src/main/js/components/facet/__tests__/{FacetHeader-test.js => FacetHeader-test.tsx} (98%) rename server/sonar-web/src/main/js/components/facet/__tests__/{FacetItem-test.js => FacetItem-test.tsx} (83%) rename server/sonar-web/src/main/js/components/facet/__tests__/{FacetItemsList-test.js => FacetItemsList-test.tsx} (96%) rename server/sonar-web/src/main/js/components/facet/__tests__/__snapshots__/{FacetBox-test.js.snap => FacetBox-test.tsx.snap} (86%) rename server/sonar-web/src/main/js/components/facet/__tests__/__snapshots__/{FacetFooter-test.js.snap => FacetFooter-test.tsx.snap} (90%) rename server/sonar-web/src/main/js/components/facet/__tests__/__snapshots__/{FacetHeader-test.js.snap => FacetHeader-test.tsx.snap} (100%) rename server/sonar-web/src/main/js/components/facet/__tests__/__snapshots__/{FacetItem-test.js.snap => FacetItem-test.tsx.snap} (80%) rename server/sonar-web/src/main/js/components/facet/__tests__/__snapshots__/{FacetItemsList-test.js.snap => FacetItemsList-test.tsx.snap} (100%) delete mode 100644 server/sonar-web/src/main/js/components/navigator/controller.js delete mode 100644 server/sonar-web/src/main/js/components/navigator/facets-view.js delete mode 100644 server/sonar-web/src/main/js/components/navigator/facets/base-facet.js delete mode 100644 server/sonar-web/src/main/js/components/navigator/models/facet.js delete mode 100644 server/sonar-web/src/main/js/components/navigator/models/facets.js delete mode 100644 server/sonar-web/src/main/js/components/navigator/models/state.js delete mode 100644 server/sonar-web/src/main/js/components/navigator/router.js delete mode 100644 server/sonar-web/src/main/js/components/navigator/workspace-header-view.js delete mode 100644 server/sonar-web/src/main/js/components/navigator/workspace-list-item-view.js delete mode 100644 server/sonar-web/src/main/js/components/navigator/workspace-list-view.js rename server/sonar-web/src/main/js/components/shared/{SeverityHelper.js => SeverityHelper.tsx} (81%) rename server/sonar-web/src/main/js/components/shared/{TypeHelper.js => TypeHelper.tsx} (74%) create mode 100644 server/sonar-web/src/main/js/components/tags/__tests__/__snapshots__/TagsList-test.tsx.snap delete mode 100644 server/sonar-web/src/main/js/helpers/handlebars/empty.js delete mode 100644 server/sonar-web/src/main/js/helpers/handlebars/gt.js delete mode 100644 server/sonar-web/src/main/js/helpers/handlebars/ifLength.js delete mode 100644 server/sonar-web/src/main/js/helpers/handlebars/repeat.js diff --git a/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/QProfileTester.java b/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/QProfileTester.java index d295cbce3e2..2609faaa7c0 100644 --- a/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/QProfileTester.java +++ b/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/QProfileTester.java @@ -77,6 +77,19 @@ public class QProfileTester { return this; } + public QProfileTester activateRule(QualityProfile profile, String ruleKey, String severity) { + return activateRule(profile.getKey(), ruleKey, severity); + } + + public QProfileTester activateRule(String profileKey, String ruleKey, String severity) { + ActivateRuleRequest request = new ActivateRuleRequest() + .setKey(profileKey) + .setRule(ruleKey) + .setSeverity(severity); + service().activateRule(request); + return this; + } + public QProfileTester deactivateRule(QualityProfile profile, String ruleKey) { service().deactivateRule(new DeactivateRuleRequest().setKey(profile.getKey()).setRule(ruleKey)); return this; diff --git a/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/QualityProfilePage.java b/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/QualityProfilePage.java index 1366b7f9aa3..9dbb657f824 100644 --- a/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/QualityProfilePage.java +++ b/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/QualityProfilePage.java @@ -37,7 +37,6 @@ public class QualityProfilePage { public RulesPage showMissingSonarWayRules() { Selenide.$(".quality-profile-rules-sonarway-missing") .shouldBe(Condition.visible).$("a").click(); - Selenide.$(".coding-rules").shouldBe(Condition.visible); return Selenide.page(RulesPage.class); } diff --git a/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/RuleDetails.java b/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/RuleDetails.java index bbf79fc50c8..75bfe6ccb0c 100644 --- a/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/RuleDetails.java +++ b/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/RuleDetails.java @@ -19,15 +19,243 @@ */ package org.sonarqube.qa.util.pageobjects; -import com.codeborne.selenide.Selenide; +import com.codeborne.selenide.Condition; +import com.codeborne.selenide.SelenideElement; +import java.util.Locale; import static com.codeborne.selenide.Condition.text; +import static com.codeborne.selenide.Condition.visible; +import static com.codeborne.selenide.Selenide.$; public class RuleDetails { + RuleDetails() { + $(".coding-rule-details").shouldBe(visible); + } + + public RuleDetails shouldHaveType(String type) { + $(".coding-rules-detail-property[data-meta=\"type\"]").shouldHave(text(type)); + return this; + } + + public RuleDetails shouldHaveSeverity(String severity) { + $(".coding-rules-detail-property[data-meta=\"severity\"]").shouldHave(text(severity)); + return this; + } + + public RuleDetails shouldHaveDescription(String description) { + $(".js-rule-description").shouldHave(text(description)); + return this; + } + + public RuleDetails shouldBeActivatedOn(String profileKey) { + $("#coding-rules-detail-quality-profiles [data-profile=\"" + profileKey + "\"]").shouldBe(visible); + return this; + } + + public RuleDetails shouldNotBeActivatedOn(String profileName) { + $("#coding-rules-detail-quality-profiles").shouldNotHave(text(profileName)); + return this; + } + + public RuleDetails shouldHaveTotalIssues(int issues) { + $(".js-rule-issues h3").shouldHave(text(String.valueOf(issues))); + return this; + } + + public RuleDetails shouldHaveIssuesOnProject(String projectName, int issues) { + $(".coding-rules-most-violated-projects").shouldHave( + Condition.and("", text(projectName), text(String.valueOf(issues)))); + return this; + } + + public RuleDetails shouldHaveCustomRule(String ruleKey) { + takeCustomRule(ruleKey).shouldBe(visible); + return this; + } + + public RuleDetails shouldNotHaveCustomRule(String ruleKey) { + takeCustomRule(ruleKey).shouldNotBe(visible); + return this; + } + + public RuleDetails createCustomRule(String ruleName) { + $(".js-create-custom-rule").click(); + modal().shouldBe(visible); + + $("#coding-rules-custom-rule-creation-name").val(ruleName); + $("#coding-rules-custom-rule-creation-html-description").val("description"); + $("#coding-rules-custom-rule-creation-create").click(); + + modal().shouldNotBe(visible); + return this; + } + + public RuleDetails reactivateCustomRule(String ruleName) { + $(".js-create-custom-rule").click(); + modal().shouldBe(visible); + + $("#coding-rules-custom-rule-creation-name").val(ruleName); + $("#coding-rules-custom-rule-creation-html-description").val("description"); + $("#coding-rules-custom-rule-creation-create").click(); + + modal().find(".alert-warning").shouldBe(visible); + $("#coding-rules-custom-rule-creation-reactivate").click(); + + modal().shouldNotBe(visible); + return this; + } + + public RuleDetails deleteCustomRule(String ruleKey) { + takeCustomRule(ruleKey).$(".js-delete-custom-rule").click(); + modal().shouldBe(visible); + modal().find("button").click(); + modal().shouldNotBe(visible); + return this; + } + + public RuleActivation activate() { + $("#coding-rules-quality-profile-activate").click(); + modal().shouldBe(visible); + return new RuleActivation(); + } + + private static SelenideElement modal() { + return $(".modal"); + } + + private static SelenideElement takeCustomRule(String ruleKey) { + return $("#coding-rules-detail-custom-rules tr[data-rule=\"" + ruleKey + "\"]"); + } + + private static SelenideElement getActiveProfileElement(String profileKey) { + return $("#coding-rules-detail-quality-profiles [data-profile=\"" + profileKey + "\"]"); + } + + public ExtendedDescription extendDescription() { + return new ExtendedDescription().start(); + } + + public Tags tags() { + return new Tags(); + } + + public RuleActivation changeActivationOn(String profileKey) { + getActiveProfileElement(profileKey).$(".coding-rules-detail-quality-profile-change").click(); + modal().shouldBe(visible); + return new RuleActivation(); + } + + public RuleDetails activationShouldHaveParameter(String profileKey, String parameter, String value) { + getActiveProfileElement(profileKey).$$(".coding-rules-detail-quality-profile-parameter") + .findBy(Condition.and("", text(parameter), text(value))) + .shouldBe(visible); + return this; + } + + public RuleDetails activationShouldHaveSeverity(String profileKey, String severity) { + getActiveProfileElement(profileKey).$(".coding-rules-detail-quality-profile-severity .icon-severity-" + severity.toLowerCase(Locale.ENGLISH)).shouldBe(visible); + return this; + } - public RuleDetails shouldBeActivatedOn(String profileName) { - Selenide.$("#coding-rules-detail-quality-profiles").shouldHave(text(profileName)); + public RuleDetails revertActivationToParentDefinition(String profileKey) { + getActiveProfileElement(profileKey).$(".coding-rules-detail-quality-profile-revert").click(); + modal().shouldBe(visible); + $(".modal button").click(); + modal().shouldNotBe(visible); return this; } + public static class ExtendedDescription { + public ExtendedDescription start() { + $("#coding-rules-detail-extend-description").click(); + textArea().shouldBe(visible); + return this; + } + + public ExtendedDescription cancel() { + $("#coding-rules-detail-extend-description-cancel").click(); + textArea().shouldNotBe(visible); + return this; + } + + public ExtendedDescription type(String text) { + textArea().val(text); + return this; + } + + public ExtendedDescription submit() { + $("#coding-rules-detail-extend-description-submit").click(); + textArea().shouldNotBe(visible); + return this; + } + + public ExtendedDescription remove() { + $("#coding-rules-detail-extend-description-remove").click(); + modal().shouldBe(visible); + $("#coding-rules-detail-extend-description-remove-submit").click(); + modal().shouldNotBe(visible); + textArea().shouldNotBe(visible); + return this; + } + + private static SelenideElement textArea() { + return $("#coding-rules-detail-extend-description-text"); + } + } + + public static class Tags { + public Tags shouldHaveNoTags() { + element().shouldHave(text("No tags")); + return this; + } + + public Tags shouldHaveTags(String... tags) { + for (String tag : tags) { + element().shouldHave(text(tag)); + } + return this; + } + + public Tags edit() { + element().$("button").click(); + return this; + } + + public Tags select(String tag) { + element().$$(".menu a").findBy(text(tag)).click(); + return this; + } + + public Tags search(String query) { + element().$(".search-box-input").val(query); + return this; + } + + public Tags done() { + element().$(".search-box-input").pressEscape(); + return this; + } + + private static SelenideElement element() { + return $(".coding-rules-detail-property[data-meta=\"tags\"]"); + } + } + + public static class RuleActivation { + public RuleActivation select(String profileKey) { + $(".modal .js-profile .Select-input input").val(profileKey).pressEnter(); + return this; + } + + public RuleActivation fill(String parameter, String value) { + $(".modal-field input[name=\"" + parameter + "\"]").val(value); + return this; + } + + public RuleActivation save() { + $(".modal button").click(); + modal().shouldNotBe(visible); + return this; + } + } } diff --git a/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/RuleItem.java b/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/RuleItem.java index 4084ba9815c..a61a371ebde 100644 --- a/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/RuleItem.java +++ b/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/RuleItem.java @@ -21,21 +21,30 @@ package org.sonarqube.qa.util.pageobjects; import com.codeborne.selenide.SelenideElement; +import static com.codeborne.selenide.Condition.visible; + public class RuleItem { private final SelenideElement elt; - public RuleItem(SelenideElement elt) { + RuleItem(SelenideElement elt) { this.elt = elt; } - public SelenideElement getTitle() { - return elt.$(".coding-rule-title"); + public RuleItem filterSimilarRules(String field) { + elt.$(".js-rule-filter").click(); + elt.$(".dropdown-menu a[data-field=\"" + field + "\"]").click(); + return this; } - public SelenideElement getMetadata() { - return elt.$(".coding-rule-meta"); + public RuleDetails open() { + elt.$(".coding-rule-title a").click(); + return new RuleDetails(); } + public RuleItem shouldDisplayDeactivate() { + elt.$(".coding-rules-detail-quality-profile-deactivate").shouldBe(visible); + return this; + } } diff --git a/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/RulesPage.java b/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/RulesPage.java index 77b82e33060..1e4e0605bf6 100644 --- a/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/RulesPage.java +++ b/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/RulesPage.java @@ -21,46 +21,119 @@ package org.sonarqube.qa.util.pageobjects; import com.codeborne.selenide.Condition; import com.codeborne.selenide.ElementsCollection; -import com.codeborne.selenide.Selenide; import com.codeborne.selenide.SelenideElement; -import org.openqa.selenium.By; + +import static com.codeborne.selenide.Condition.exist; +import static com.codeborne.selenide.Condition.visible; +import static com.codeborne.selenide.Selenide.$; +import static com.codeborne.selenide.Selenide.$$; public class RulesPage extends Navigation { public RulesPage() { - Selenide.$(By.cssSelector(".coding-rules")).should(Condition.exist); + $("#coding-rules-page").should(exist); } public int getTotal() { // warning - number is localized - return Integer.parseInt(Selenide.$("#coding-rules-total").text()); + return Integer.parseInt($("#coding-rules-total").text()); } public ElementsCollection getSelectedFacetItems(String facetName) { - SelenideElement facet = Selenide.$(".search-navigator-facet-box[data-property='"+ facetName+"']").shouldBe(Condition.visible); - return facet.$$(".js-facet.active"); + return getFacetElement(facetName).$$(".facet.active"); } public RulesPage shouldHaveTotalRules(Integer total) { - Selenide.$("#coding-rules-total").shouldHave(Condition.text(total.toString())); + $(".js-page-counter-total").shouldHave(Condition.text(total.toString())); + return this; + } + + public RulesPage shouldDisplayRules(String... ruleKeys) { + for (String key : ruleKeys) { + getRuleElement(key).shouldBe(visible); + } + return this; + } + + public RulesPage shouldNotDisplayRules(String... ruleKeys) { + for (String key : ruleKeys) { + getRuleElement(key).shouldNotBe(visible); + } return this; } public RulesPage openFacet(String facet) { - Selenide.$(".search-navigator-facet-box[data-property=\"" + facet + "\"] .js-facet-toggle").click(); + getFacetElement(facet).$(".search-navigator-facet-header a").click(); + return this; + } + + public RulesPage selectFacetItem(String facet, String value) { + getFacetElement(facet).$(".facet[data-facet=\"" + value + "\"]").click(); return this; } - public RulesPage selectFacetItemByText(String facet, String itemText) { - Selenide.$$(".search-navigator-facet-box[data-property=\"" + facet + "\"] .js-facet") - .findBy(Condition.text(itemText)).click(); + public RulesPage selectInactive() { + getFacetElement("profile").$(".active .js-inactive").click(); + return this; + } + + public RulesPage shouldHaveDisabledFacet(String facet) { + $(".search-navigator-facet-box-forbidden[data-property=\"" + facet + "\"]").shouldBe(visible); + return this; + } + + public RulesPage shouldNotHaveDisabledFacet(String facet) { + $(".search-navigator-facet-box-forbidden[data-property=\"" + facet + "\"]").shouldNotBe(visible); return this; } public RuleDetails openFirstRule() { - Selenide.$$(".js-rule").first().click(); - Selenide.$(".coding-rules-details").shouldBe(Condition.visible); + $$(".coding-rule-title a").first().click(); return new RuleDetails(); } + public RuleItem takeRule(String ruleKey) { + return new RuleItem(getRuleElement(ruleKey)); + } + + public RulesPage search(String query) { + $("#coding-rules-search .search-box-input").val(query); + return this; + } + + public RulesPage clearAllFilters() { + $("#coding-rules-clear-all-filters").click(); + return this; + } + + public RulesPage closeDetails() { + $(".js-back").click(); + $(".coding-rule-details").shouldNotBe(visible); + return this; + } + + public RulesPage activateRule(String ruleKey) { + getRuleElement(ruleKey).$(".coding-rules-detail-quality-profile-activate").click(); + $(".modal").shouldBe(visible); + $(".modal button").click(); + $(".modal").shouldNotBe(visible); + getRuleElement(ruleKey).$(".coding-rules-detail-quality-profile-activate").shouldNotBe(visible); + return this; + } + + public RulesPage deactivateRule(String ruleKey) { + getRuleElement(ruleKey).$(".coding-rules-detail-quality-profile-deactivate").click(); + $(".modal button").click(); + getRuleElement(ruleKey).$(".coding-rules-detail-quality-profile-deactivate").shouldNotBe(visible); + return this; + } + + private static SelenideElement getRuleElement(String key) { + return $(".coding-rule[data-rule=\"" + key + "\"]"); + } + + private static SelenideElement getFacetElement(String facet) { + return $(".search-navigator-facet-box[data-property=\"" + facet + "\"]"); + } + } diff --git a/server/sonar-web/package.json b/server/sonar-web/package.json index 1be2cba9f28..8089b41e562 100644 --- a/server/sonar-web/package.json +++ b/server/sonar-web/package.json @@ -51,6 +51,7 @@ "@types/escape-html": "0.0.20", "@types/jest": "22.0.1", "@types/jquery": "3.2.11", + "@types/keymaster": "1.6.28", "@types/lodash": "4.14.80", "@types/prop-types": "15.5.2", "@types/react": "16.0.29", diff --git a/server/sonar-web/src/main/js/api/issues.ts b/server/sonar-web/src/main/js/api/issues.ts index 765dcb95efb..de740e4190c 100644 --- a/server/sonar-web/src/main/js/api/issues.ts +++ b/server/sonar-web/src/main/js/api/issues.ts @@ -28,7 +28,7 @@ export interface IssueResponse { } interface IssuesResponse { - components?: Array<{}>; + components?: { key: string; name: string; uuid: string }[]; debtTotal?: number; facets: Array<{}>; issues: RawIssue[]; @@ -38,7 +38,7 @@ interface IssuesResponse { total: number; }; rules?: Array<{}>; - users?: Array<{ login: string }>; + users?: { login: string }[]; } export function searchIssues(query: RequestData): Promise { @@ -57,7 +57,10 @@ export function getFacets(query: RequestData, facets: string[]): Promise { }); } -export function getFacet(query: RequestData, facet: string): Promise { +export function getFacet( + query: RequestData, + facet: string +): Promise<{ facet: { count: number; val: string }[]; response: IssuesResponse }> { return getFacets(query, [facet]).then(r => { return { facet: r.facets[0].values, response: r.response }; }); @@ -82,6 +85,18 @@ export function getAssignees(query: RequestData): Promise { return getFacet(query, 'assignees').then(r => extractAssignees(r.facet, r.response)); } +export function extractProjects(facet: { val: string }[], response: IssuesResponse) { + return facet.map(item => { + const project = + response.components && response.components.find(component => component.uuid === item.val); + return { ...item, project }; + }); +} + +export function getProjects(query: RequestData) { + return getFacet(query, 'projectUuids').then(r => extractProjects(r.facet, r.response)); +} + export function getIssuesCount(query: RequestData): Promise { const data = { ...query, ps: 1, facetMode: 'effort' }; return searchIssues(data).then(r => { diff --git a/server/sonar-web/src/main/js/api/quality-profiles.ts b/server/sonar-web/src/main/js/api/quality-profiles.ts index 80dd491a75e..188651ddc31 100644 --- a/server/sonar-web/src/main/js/api/quality-profiles.ts +++ b/server/sonar-web/src/main/js/api/quality-profiles.ts @@ -17,6 +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 { map } from 'lodash'; +import { csvEscape } from '../helpers/csv'; import { request, checkStatus, @@ -219,3 +221,58 @@ export function addGroup(parameters: AddRemoveGroupParameters): Promise { return post('/api/qualityprofiles/remove_group', parameters).catch(throwGlobalError); } + +export interface BulkActivateParameters { + /* eslint-disable camelcase */ + activation?: boolean; + active_severities?: string; + asc?: boolean; + available_since?: string; + compareToProfile?: string; + inheritance?: string; + is_template?: string; + languages?: string; + organization: string | undefined; + q?: string; + qprofile?: string; + repositories?: string; + rule_key?: string; + s?: string; + severities?: string; + statuses?: string; + tags?: string; + targetKey: string; + targetSeverity?: string; + template_key?: string; + types?: string; + /* eslint-enable camelcase */ +} + +export function bulkActivateRules(data: BulkActivateParameters) { + return postJSON('api/qualityprofiles/activate_rules', data); +} + +export function bulkDeactivateRules(data: BulkActivateParameters) { + return postJSON('api/qualityprofiles/deactivate_rules', data); +} + +export function activateRule(data: { + key: string; + organization: string | undefined; + params?: { [key: string]: string }; + reset?: boolean; + rule: string; + severity?: string; +}) { + const params = + data.params && map(data.params, (value, key) => `${key}=${csvEscape(value)}`).join(';'); + return post('/api/qualityprofiles/activate_rule', { ...data, params }).catch(throwGlobalError); +} + +export function deactivateRule(data: { + key: string; + organization: string | undefined; + rule: string; +}) { + return post('/api/qualityprofiles/deactivate_rule', data).catch(throwGlobalError); +} diff --git a/server/sonar-web/src/main/js/api/rules.ts b/server/sonar-web/src/main/js/api/rules.ts index 1a29eb863e8..ea42bf0a1a7 100644 --- a/server/sonar-web/src/main/js/api/rules.ts +++ b/server/sonar-web/src/main/js/api/rules.ts @@ -17,18 +17,34 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -import { post, getJSON, RequestData } from '../helpers/request'; +import { post, getJSON, postJSON } from '../helpers/request'; import throwGlobalError from '../app/utils/throwGlobalError'; +import { Rule, RuleDetails, RuleActivation } from '../app/types'; export interface GetRulesAppResponse { - respositories: Array<{ key: string; language: string; name: string }>; + canWrite?: boolean; + repositories: { key: string; language: string; name: string }[]; } -export function getRulesApp(): Promise { - return getJSON('/api/rules/app').catch(throwGlobalError); +export function getRulesApp(data: { + organization: string | undefined; +}): Promise { + return getJSON('/api/rules/app', data).catch(throwGlobalError); } -export function searchRules(data: RequestData) { +export interface SearchRulesResponse { + actives?: { [rule: string]: RuleActivation[] }; + facets?: { property: string; values: { count: number; val: string }[] }[]; + p: number; + ps: number; + rules: Rule[]; + total: number; +} + +export function searchRules(data: { + organization: string | undefined; + [x: string]: any; +}): Promise { return getJSON('/api/rules/search', data).catch(throwGlobalError); } @@ -37,20 +53,65 @@ export function takeFacet(response: any, property: string) { return facet ? facet.values : []; } -export interface GetRuleDetailsParameters { +export function getRuleDetails(parameters: { actives?: boolean; key: string; - organization?: string; -} - -export function getRuleDetails(parameters: GetRuleDetailsParameters): Promise { + organization: string | undefined; +}): Promise<{ actives?: RuleActivation[]; rule: RuleDetails }> { return getJSON('/api/rules/show', parameters).catch(throwGlobalError); } -export function getRuleTags(parameters: { organization?: string }): Promise { +export function getRuleTags(parameters: { + organization: string | undefined; + ps?: number; + q: string; +}): Promise { return getJSON('/api/rules/tags', parameters).then(r => r.tags, throwGlobalError); } -export function deleteRule(parameters: { key: string }) { +export function createRule(data: { + custom_key: string; + markdown_description: string; + name: string; + organization: string | undefined; + params?: string; + prevent_reactivation?: boolean; + severity?: string; + status?: string; + template_key: string; + type?: string; +}): Promise { + return postJSON('/api/rules/create', data).then( + r => r.rule, + error => { + // do not show global error if the status code is 409 + // this case should be handled inside a component + if (error && error.response && error.response.status === 409) { + return Promise.reject(error.response); + } else { + return throwGlobalError(error); + } + } + ); +} + +export function deleteRule(parameters: { key: string; organization: string | undefined }) { return post('/api/rules/delete', parameters).catch(throwGlobalError); } + +export function updateRule(data: { + key: string; + markdown_description?: string; + markdown_note?: string; + name?: string; + organization: string | undefined; + params?: string; + remediation_fn_base_effort?: string; + remediation_fn_type?: string; + remediation_fy_gap_multiplier?: string; + severity?: string; + status?: string; + tags?: string; +}): Promise { + return postJSON('/api/rules/update', data).then(r => r.rule, throwGlobalError); +} diff --git a/server/sonar-web/src/main/js/app/styles/components/search-navigator.css b/server/sonar-web/src/main/js/app/styles/components/search-navigator.css index f8b21277653..c997af3dae1 100644 --- a/server/sonar-web/src/main/js/app/styles/components/search-navigator.css +++ b/server/sonar-web/src/main/js/app/styles/components/search-navigator.css @@ -99,7 +99,6 @@ .search-navigator-facet-box-forbidden .search-navigator-facet-header { color: var(--secondFontColor); - font-weight: 400; } .search-navigator-facet-box-forbidden .search-navigator-facet-header:hover { @@ -484,7 +483,7 @@ a.search-navigator-facet:focus .facet-stat { } .search-navigator-facet-list { - padding-bottom: 10px; + padding-bottom: var(--gridSize); font-size: 0; } @@ -498,7 +497,7 @@ a.search-navigator-facet:focus .facet-stat { .search-navigator-facet-footer { display: block; - padding: 6px 10px; + padding-bottom: var(--gridSize); border-bottom: none; } @@ -689,8 +688,9 @@ a.search-navigator-facet:focus .facet-stat { } .search-navigator-filters-header { - float: left; - line-height: 22px; + margin-bottom: 12px; + padding-bottom: 11px; + border-bottom: 1px solid var(--barBorderColor); } .search-navigator-filters-name { diff --git a/server/sonar-web/src/main/js/app/styles/init/forms.css b/server/sonar-web/src/main/js/app/styles/init/forms.css index 9abdbddf828..beb0204de27 100644 --- a/server/sonar-web/src/main/js/app/styles/init/forms.css +++ b/server/sonar-web/src/main/js/app/styles/init/forms.css @@ -177,11 +177,11 @@ button:disabled:focus, .button:disabled:focus, input[type='submit']:disabled:focus, input[type='button']:disabled:focus { - color: #bbb; - border-color: #ddd; - background: #ebebeb; - cursor: not-allowed; - box-shadow: none; + color: #bbb !important; + border-color: #ddd !important; + background: #ebebeb !important; + cursor: not-allowed !important; + box-shadow: none !important; } .button svg { diff --git a/server/sonar-web/src/main/js/app/types.ts b/server/sonar-web/src/main/js/app/types.ts index dedfabe68c2..ef1f97b8cf1 100644 --- a/server/sonar-web/src/main/js/app/types.ts +++ b/server/sonar-web/src/main/js/app/types.ts @@ -17,6 +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. */ + +// Diff / Omit taken from https://github.com/Microsoft/TypeScript/issues/12215#issuecomment-311923766 +export type Diff = ({ [P in T]: P } & + { [P in U]: never } & { [x: string]: never })[T]; + +export type Omit = Pick>; + export enum BranchType { LONG = 'LONG', SHORT = 'SHORT' @@ -187,3 +194,63 @@ export interface AppState { organizationsEnabled?: boolean; qualifiers: string[]; } + +export interface Rule { + isTemplate?: boolean; + key: string; + lang: string; + langName: string; + name: string; + params?: RuleParameter[]; + severity: string; + status: string; + sysTags?: string[]; + tags?: string[]; + type: string; +} + +export interface RuleDetails extends Rule { + createdAt: string; + debtOverloaded?: boolean; + debtRemFnCoeff?: string; + debtRemFnOffset?: string; + debtRemFnType?: string; + defaultDebtRemFnOffset?: string; + defaultDebtRemFnType?: string; + defaultRemFnBaseEffort?: string; + defaultRemFnType?: string; + effortToFixDescription?: string; + htmlDesc?: string; + htmlNote?: string; + internalKey?: string; + mdDesc?: string; + mdNote?: string; + remFnBaseEffort?: string; + remFnOverloaded?: boolean; + remFnType?: string; + repo: string; + templateKey?: string; +} + +export interface RuleActivation { + createdAt: string; + inherit: RuleInheritance; + params: { key: string; value: string }[]; + qProfile: string; + severity: string; +} + +export interface RuleParameter { + // TODO is this extra really returned? + extra?: string; + defaultValue?: string; + htmlDesc?: string; + key: string; + type: string; +} + +export enum RuleInheritance { + NotInherited = 'NONE', + Inherited = 'INHERITED', + Overridden = 'OVERRIDES' +} diff --git a/server/sonar-web/src/main/js/apps/about/components/AboutStandards.js b/server/sonar-web/src/main/js/apps/about/components/AboutStandards.js index 40b5093a8ea..a4848285d9d 100644 --- a/server/sonar-web/src/main/js/apps/about/components/AboutStandards.js +++ b/server/sonar-web/src/main/js/apps/about/components/AboutStandards.js @@ -41,7 +41,7 @@ type Props = { export default function AboutStandards(props /*: Props */) { const organization = props.appState.organizationsEnabled ? props.appState.defaultOrganization - : null; + : undefined; return (
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/bulk-change-modal-view.js b/server/sonar-web/src/main/js/apps/coding-rules/bulk-change-modal-view.js deleted file mode 100644 index 1708a0c6887..00000000000 --- a/server/sonar-web/src/main/js/apps/coding-rules/bulk-change-modal-view.js +++ /dev/null @@ -1,132 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2018 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 ModalFormView from '../../components/common/modal-form'; -import Template from './templates/coding-rules-bulk-change-modal.hbs'; -import { translateWithParameters } from '../../helpers/l10n'; -import { postJSON } from '../../helpers/request'; - -export default ModalFormView.extend({ - template: Template, - - ui() { - return { - ...ModalFormView.prototype.ui.apply(this, arguments), - codingRulesSubmitBulkChange: '#coding-rules-submit-bulk-change' - }; - }, - - showSuccessMessage(profile, succeeded) { - const profileBase = this.options.app.qualityProfiles.find(p => p.key === profile); - const message = translateWithParameters( - 'coding_rules.bulk_change.success', - profileBase.name, - profileBase.language, - succeeded - ); - this.ui.messagesContainer.append(`
${message}
`); - }, - - showWarnMessage(profile, succeeded, failed) { - const profileBase = this.options.app.qualityProfiles.find(p => p.key === profile); - const message = translateWithParameters( - 'coding_rules.bulk_change.warning', - profileBase.name, - profileBase.language, - succeeded, - failed - ); - this.ui.messagesContainer.append(`
${message}
`); - }, - - onRender() { - ModalFormView.prototype.onRender.apply(this, arguments); - this.$('#coding-rules-bulk-change-profile').select2({ - width: '250px', - minimumResultsForSearch: 1, - openOnEnter: false - }); - }, - - onFormSubmit() { - ModalFormView.prototype.onFormSubmit.apply(this, arguments); - const url = `/api/qualityprofiles/${this.options.action}_rules`; - const options = { ...this.options.app.state.get('query'), wsAction: this.options.action }; - const profiles = this.$('#coding-rules-bulk-change-profile').val() || [this.options.param]; - this.ui.messagesContainer.empty(); - this.sendRequests(url, options, profiles); - }, - - sendRequests(url, options, profiles) { - const that = this; - let looper = Promise.resolve(); - this.disableForm(); - profiles.forEach(profile => { - const opts = { ...options, profile_key: profile }; - looper = looper.then(() => - postJSON(url, opts).then(r => { - if (!that.isDestroyed) { - if (r.failed) { - that.showWarnMessage(profile, r.succeeded, r.failed); - } else { - that.showSuccessMessage(profile, r.succeeded); - } - } - }) - ); - }); - looper.then( - () => { - that.options.app.controller.fetchList(); - if (!that.isDestroyed) { - that.$(that.ui.codingRulesSubmitBulkChange.selector).hide(); - that.enableForm(); - that.$('.modal-field').hide(); - that.$('.js-modal-close').focus(); - } - }, - () => {} - ); - }, - - getAvailableQualityProfiles() { - const queryLanguages = this.options.app.state.get('query').languages; - const languages = queryLanguages && queryLanguages.length > 0 ? queryLanguages.split(',') : []; - let profiles = this.options.app.qualityProfiles; - if (languages.length > 0) { - profiles = profiles.filter(profile => languages.indexOf(profile.language) !== -1); - } - return profiles - .filter(profile => profile.actions && profile.actions.edit) - .filter(profile => !profile.isBuiltIn); - }, - - serializeData() { - const profile = this.options.app.qualityProfiles.find(p => p.key === this.options.param); - return { - ...ModalFormView.prototype.serializeData.apply(this, arguments), - action: this.options.action, - state: this.options.app.state.toJSON(), - qualityProfile: this.options.param, - qualityProfileName: profile != null ? profile.name : null, - qualityProfiles: this.options.app.qualityProfiles, - availableQualityProfiles: this.getAvailableQualityProfiles() - }; - } -}); diff --git a/server/sonar-web/src/main/js/apps/coding-rules/bulk-change-popup-view.js b/server/sonar-web/src/main/js/apps/coding-rules/bulk-change-popup-view.js deleted file mode 100644 index 00b686f7f7c..00000000000 --- a/server/sonar-web/src/main/js/apps/coding-rules/bulk-change-popup-view.js +++ /dev/null @@ -1,57 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2018 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 $ from 'jquery'; -import PopupView from '../../components/common/popup'; -import BulkChangeModalView from './bulk-change-modal-view'; -import Template from './templates/coding-rules-bulk-change-popup.hbs'; - -export default PopupView.extend({ - template: Template, - - events: { - 'click .js-bulk-change': 'doAction' - }, - - doAction(e) { - const action = $(e.currentTarget).data('action'); - const param = $(e.currentTarget).data('param'); - new BulkChangeModalView({ - app: this.options.app, - action, - param - }).render(); - }, - - serializeData() { - const query = this.options.app.state.get('query'); - const profileKey = query.qprofile; - const profile = this.options.app.qualityProfiles.find(p => p.key === profileKey); - const activation = '' + query.activation; - const canChangeProfile = - profile != null && !profile.isBuiltIn && profile.actions && profile.actions.edit; - - return { - qualityProfile: profileKey, - qualityProfileName: profile != null ? profile.name : null, - allowActivateOnProfile: canChangeProfile && activation === 'false', - allowDeactivateOnProfile: canChangeProfile && activation === 'true' - }; - } -}); diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/ActivationButton.tsx b/server/sonar-web/src/main/js/apps/coding-rules/components/ActivationButton.tsx new file mode 100644 index 00000000000..b96a2513843 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/coding-rules/components/ActivationButton.tsx @@ -0,0 +1,86 @@ +/* + * SonarQube + * Copyright (C) 2009-2018 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 ActivationFormModal from './ActivationFormModal'; +import { Profile as BaseProfile } from '../../../api/quality-profiles'; +import { Rule, RuleDetails, RuleActivation } from '../../../app/types'; + +interface Props { + activation?: RuleActivation; + buttonText: string; + className?: string; + modalHeader: string; + onDone: (severity: string) => Promise; + organization: string | undefined; + profiles: BaseProfile[]; + rule: Rule | RuleDetails; + updateMode?: boolean; +} + +interface State { + modal: boolean; +} + +export default class ActivationButton extends React.PureComponent { + mounted: boolean; + state: State = { modal: false }; + + componentDidMount() { + this.mounted = true; + } + + componentWillUnmount() { + this.mounted = false; + } + + handleButtonClick = (event: React.SyntheticEvent) => { + event.preventDefault(); + event.currentTarget.blur(); + this.setState({ modal: true }); + }; + + handleCloseModal = () => this.setState({ modal: false }); + + render() { + return ( + <> + + + {this.state.modal && ( + + )} + + ); + } +} 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 new file mode 100644 index 00000000000..2fcc083b688 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/coding-rules/components/ActivationFormModal.tsx @@ -0,0 +1,259 @@ +/* + * SonarQube + * Copyright (C) 2009-2018 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 from '../../../components/controls/Modal'; +import Select from '../../../components/controls/Select'; +import SeverityHelper from '../../../components/shared/SeverityHelper'; +import Tooltip from '../../../components/controls/Tooltip'; +import { activateRule, Profile as BaseProfile } from '../../../api/quality-profiles'; +import { Rule, RuleDetails, RuleActivation } from '../../../app/types'; +import { SEVERITIES } from '../../../helpers/constants'; +import { translate } from '../../../helpers/l10n'; +import { sortProfiles } from '../../quality-profiles/utils'; + +interface Props { + activation?: RuleActivation; + modalHeader: string; + onClose: () => void; + onDone: (severity: string) => Promise; + organization: string | undefined; + profiles: BaseProfile[]; + rule: Rule | RuleDetails; + updateMode?: boolean; +} + +interface State { + params: { [p: string]: string }; + profile: string; + severity: string; + submitting: boolean; +} + +export default class ActivationFormModal extends React.PureComponent { + mounted: boolean; + + constructor(props: Props) { + super(props); + const profilesWithDepth = this.getQualityProfilesWithDepth(props); + this.state = { + params: this.getParams(props), + profile: profilesWithDepth.length > 0 ? profilesWithDepth[0].key : '', + severity: props.activation ? props.activation.severity : props.rule.severity, + submitting: false + }; + } + + componentDidMount() { + this.mounted = true; + } + + componentWillUnmount() { + this.mounted = false; + } + + getParams = ({ activation, rule } = this.props) => { + const params: { [p: string]: string } = {}; + if (rule && rule.params) { + for (const param of rule.params) { + params[param.key] = param.defaultValue || ''; + } + if (activation && activation.params) { + for (const param of activation.params) { + params[param.key] = param.value; + } + } + } + return params; + }; + + // Choose QP which a user can administrate, which are the same language and which are not built-in + getQualityProfilesWithDepth = ({ profiles } = this.props) => + sortProfiles( + profiles.filter( + profile => + !profile.isBuiltIn && + profile.actions && + profile.actions.edit && + profile.language === this.props.rule.lang + ) + ).map(profile => ({ + ...profile, + // Decrease depth by 1, so the top level starts at 0 + depth: profile.depth - 1 + })); + + handleCancelClick = (event: React.SyntheticEvent) => { + event.preventDefault(); + event.currentTarget.blur(); + this.props.onClose(); + }; + + handleFormSubmit = (event: React.SyntheticEvent) => { + event.preventDefault(); + this.setState({ submitting: true }); + const data = { + key: this.state.profile, + organization: this.props.organization, + params: this.state.params, + rule: this.props.rule.key, + severity: this.state.severity + }; + activateRule(data) + .then(() => this.props.onDone(data.severity)) + .then( + () => { + if (this.mounted) { + this.setState({ submitting: false }); + this.props.onClose(); + } + }, + () => { + if (this.mounted) { + this.setState({ submitting: false }); + } + } + ); + }; + + handleParameterChange = (event: React.SyntheticEvent) => { + const { name, value } = event.currentTarget; + this.setState((state: State) => ({ params: { ...state.params, [name]: value } })); + }; + + handleProfileChange = ({ value }: { value: string }) => this.setState({ profile: value }); + + handleSeverityChange = ({ value }: { value: string }) => this.setState({ severity: value }); + + renderSeverityOption = ({ value }: { value: string }) => ; + + render() { + const { activation, rule } = this.props; + const { profile, severity, submitting } = this.state; + const { params = [] } = rule; + const profilesWithDepth = this.getQualityProfilesWithDepth(); + const isCustomRule = !!(rule as RuleDetails).templateKey; + const activeInAllProfiles = profilesWithDepth.length <= 0; + const isUpdateMode = !!activation; + + return ( + +
+
+

{this.props.modalHeader}

+
+ +
+ {!isUpdateMode && + activeInAllProfiles && ( +
+ {translate('coding_rules.active_in_all_profiles')} +
+ )} + +
+ + ({ + label: translate('severity', severity), + value: severity + }))} + optionRenderer={this.renderSeverityOption} + searchable={false} + value={severity} + valueRenderer={this.renderSeverityOption} + /> +
+ {isCustomRule ? ( +
+

{translate('coding_rules.custom_rule.activation_notice')}

+
+ ) : ( + params.map(param => ( +
+ + + + {param.type === 'TEXT' ? ( + - {{> '../../../../components/common/templates/_markdown-tips' }} - - - -

{{t 'type'}}

- - - - - -

{{t 'severity'}}

- - - - - -

{{t 'coding_rules.filters.status'}}

- - - - - {{#each params}} - -

{{key}}

- - {{#eq type 'TEXT'}} - - {{else}} - - {{/eq}} -
{{{htmlDesc}}}
- {{#if extra}} -
{{extra}}
- {{/if}} - - - {{/each}} - -
- - - diff --git a/server/sonar-web/src/main/js/apps/coding-rules/templates/rule/coding-rules-custom-rule.hbs b/server/sonar-web/src/main/js/apps/coding-rules/templates/rule/coding-rules-custom-rule.hbs deleted file mode 100644 index 32fff8638c6..00000000000 --- a/server/sonar-web/src/main/js/apps/coding-rules/templates/rule/coding-rules-custom-rule.hbs +++ /dev/null @@ -1,26 +0,0 @@ - - {{name}} - - - - {{severityIcon severity}} {{t "severity" severity}} - - - - {{#each params}} - {{#if defaultValue}} -
- {{key}}{{defaultValue}} -
- {{/if}} - {{/each}} -   - - -{{#if canDeleteCustomRule}} - - - -{{/if}} diff --git a/server/sonar-web/src/main/js/apps/coding-rules/templates/rule/coding-rules-custom-rules.hbs b/server/sonar-web/src/main/js/apps/coding-rules/templates/rule/coding-rules-custom-rules.hbs deleted file mode 100644 index 402c99a16c8..00000000000 --- a/server/sonar-web/src/main/js/apps/coding-rules/templates/rule/coding-rules-custom-rules.hbs +++ /dev/null @@ -1,11 +0,0 @@ -
-
- -

{{t 'coding_rules.custom_rules'}}

- - {{#if canCreateCustomRule}} - - {{/if}} - -
-
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/templates/rule/coding-rules-delete-rule.hbs b/server/sonar-web/src/main/js/apps/coding-rules/templates/rule/coding-rules-delete-rule.hbs deleted file mode 100644 index 4c9a7f3fbf9..00000000000 --- a/server/sonar-web/src/main/js/apps/coding-rules/templates/rule/coding-rules-delete-rule.hbs +++ /dev/null @@ -1,14 +0,0 @@ -
- - - - - -
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/templates/rule/coding-rules-profile-activation.hbs b/server/sonar-web/src/main/js/apps/coding-rules/templates/rule/coding-rules-profile-activation.hbs deleted file mode 100644 index e2c87370259..00000000000 --- a/server/sonar-web/src/main/js/apps/coding-rules/templates/rule/coding-rules-profile-activation.hbs +++ /dev/null @@ -1,78 +0,0 @@ -
- - - - - -
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/templates/rule/coding-rules-rule-description.hbs b/server/sonar-web/src/main/js/apps/coding-rules/templates/rule/coding-rules-rule-description.hbs deleted file mode 100644 index 17b3e986141..00000000000 --- a/server/sonar-web/src/main/js/apps/coding-rules/templates/rule/coding-rules-rule-description.hbs +++ /dev/null @@ -1,41 +0,0 @@ -
{{{htmlDesc}}}
- -{{#unless isCustom}} -
-
- {{#if htmlNote}} -
{{{htmlNote}}}
- {{/if}} - {{#if canCustomizeRule}} - - {{/if}} -
- - {{#if canCustomizeRule}} - - {{/if}} -
-{{/unless}} diff --git a/server/sonar-web/src/main/js/apps/coding-rules/templates/rule/coding-rules-rule-issues.hbs b/server/sonar-web/src/main/js/apps/coding-rules/templates/rule/coding-rules-rule-issues.hbs deleted file mode 100644 index 32f822b85ff..00000000000 --- a/server/sonar-web/src/main/js/apps/coding-rules/templates/rule/coding-rules-rule-issues.hbs +++ /dev/null @@ -1,27 +0,0 @@ -
- -{{#if loading}} -

- {{t 'coding_rules.issues'}} -

-{{else}} -

- {{t 'coding_rules.issues'}} ({{total}}) -

- - {{#notEmpty projects}} - - - - - {{#each projects}} - - - - - {{/each}} -
{{t 'coding_rules.most_violating_projects'}}
{{name}} - {{count}} -
- {{/notEmpty}} -{{/if}} diff --git a/server/sonar-web/src/main/js/apps/coding-rules/templates/rule/coding-rules-rule-meta.hbs b/server/sonar-web/src/main/js/apps/coding-rules/templates/rule/coding-rules-rule-meta.hbs deleted file mode 100644 index 4483e6e034f..00000000000 --- a/server/sonar-web/src/main/js/apps/coding-rules/templates/rule/coding-rules-rule-meta.hbs +++ /dev/null @@ -1,95 +0,0 @@ - - -
    -
  • - {{issueTypeIcon this.type}} {{issueType this.type}} -
  • - -
  • - {{severityIcon severity}} {{t "severity" severity}} -
  • - - {{#notEq status 'READY'}} -
  • - - {{t 'rules.status' status}} - -
  • - {{/notEq}} - -
  • - - {{#if allTags}}{{join allTags ', '}}{{else}}{{t 'coding_rules.no_tags'}}{{/if}} - {{#if canCustomizeRule}}{{/if}} -
  • - - {{#if canCustomizeRule}} - - {{/if}} - -
  • {{t 'coding_rules.available_since'}} {{d createdAt}}
  • - -
  • - {{repoName}} ({{langName}}) -
  • - - {{#if isTemplate}} -
  • {{t 'coding_rules.rule_template'}}
  • - {{/if}} - - {{#if templateKey}} -
  • {{t 'coding_rules.custom_rule'}} - ({{t 'coding_rules.show_template'}}) -
  • - {{/if}} - - {{#if debtRemFnType}} -
  • - {{t 'coding_rules.remediation_function' debtRemFnType}}: - - {{#if debtRemFnOffset}}{{debtRemFnOffset}}{{/if}} - {{#if debtRemFnCoeff}}{{#if debtRemFnOffset}}+{{/if}}{{debtRemFnCoeff}}{{/if}} - {{#if effortToFixDescription}}{{effortToFixDescription}}{{/if}} -
  • - {{/if}} -
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/templates/rule/coding-rules-rule-parameters.hbs b/server/sonar-web/src/main/js/apps/coding-rules/templates/rule/coding-rules-rule-parameters.hbs deleted file mode 100644 index 0298c2aa33e..00000000000 --- a/server/sonar-web/src/main/js/apps/coding-rules/templates/rule/coding-rules-rule-parameters.hbs +++ /dev/null @@ -1,27 +0,0 @@ -

{{t 'coding_rules.parameters'}}

- - {{#each params}} - - - - - {{/each}} -
{{key}} -

{{{htmlDesc}}}

- {{#if ../../templateKey}} -
- {{#if defaultValue }} - {{defaultValue}} - {{else}} - {{t 'coding_rules.parameter.empty'}} - {{/if}} -
- {{else}} - {{#if defaultValue}} -
- {{t 'coding_rules.parameters.default_value'}}
- {{defaultValue}} -
- {{/if}} - {{/if}} -
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/templates/rule/coding-rules-rule-profile.hbs b/server/sonar-web/src/main/js/apps/coding-rules/templates/rule/coding-rules-rule-profile.hbs deleted file mode 100644 index c0b2bfe0619..00000000000 --- a/server/sonar-web/src/main/js/apps/coding-rules/templates/rule/coding-rules-rule-profile.hbs +++ /dev/null @@ -1,82 +0,0 @@ - - - {{name}} - - {{#if isBuiltIn}} - - {{t 'quality_profiles.built_in'}} - - {{/if}} - {{#if parent}} -
- {{#eq inherit 'OVERRIDES'}} - - {{/eq}} - {{#eq inherit 'INHERITED'}} - - {{/eq}} - - {{parent.name}} - -
- {{/if}} - - -{{#if severity}} - - - {{severityIcon severity}} {{t "severity" severity}} - - {{#if parent}}{{#notEq severity parent.severity}} -
- {{t 'coding_rules.original'}} {{t 'severity' parent.severity}} -
- {{/notEq}}{{/if}} - - - {{#unless templateKey}} - - {{#each parameters}} -
- {{key}}{{value}} - {{#if ../parent}}{{#notEq value original}} -
- {{t 'coding_rules.original'}} {{original}} -
- {{/notEq}}{{/if}} -
- {{/each}} -   - - {{/unless}} - - - {{#if actions.edit}} - {{#unless isBuiltIn}} - {{#unless isTemplate}} - - {{/unless}} - {{#if parent}} - {{#eq inherit 'OVERRIDES'}} - - {{/eq}} - {{else}} - - {{/if}} - {{/unless}} - {{/if}} - - -{{else}} - {{#if canWrite}}{{#unless isTemplate}} - - - - {{/unless}}{{/if}} -{{/if}} diff --git a/server/sonar-web/src/main/js/apps/coding-rules/templates/rule/coding-rules-rule-profiles.hbs b/server/sonar-web/src/main/js/apps/coding-rules/templates/rule/coding-rules-rule-profiles.hbs deleted file mode 100644 index 44131fc2f28..00000000000 --- a/server/sonar-web/src/main/js/apps/coding-rules/templates/rule/coding-rules-rule-profiles.hbs +++ /dev/null @@ -1,19 +0,0 @@ -
-
- -

{{t 'coding_rules.quality_profiles'}}

- - {{#if canActivate}} - {{#unless isTemplate}} - - {{/unless}} - {{/if}} - - {{#if isTemplate}} -
- {{t 'coding_rules.quality_profiles.template_caption'}} -
- {{/if}} - -
-
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/workspace-header-view.js b/server/sonar-web/src/main/js/apps/coding-rules/workspace-header-view.js deleted file mode 100644 index 703d8b7830a..00000000000 --- a/server/sonar-web/src/main/js/apps/coding-rules/workspace-header-view.js +++ /dev/null @@ -1,72 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2018 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 $ from 'jquery'; -import WorkspaceHeaderView from '../../components/navigator/workspace-header-view'; -import BulkChangePopup from './bulk-change-popup-view'; -import Template from './templates/coding-rules-workspace-header.hbs'; - -export default WorkspaceHeaderView.extend({ - template: Template, - - events() { - return { - ...WorkspaceHeaderView.prototype.events.apply(this, arguments), - 'click .js-back': 'onBackClick', - 'click .js-bulk-change': 'onBulkChangeClick', - 'click .js-reload': 'reload', - 'click .js-new-search': 'newSearch' - }; - }, - - onBackClick() { - this.options.app.controller.hideDetails(); - }, - - onBulkChangeClick(e) { - e.stopPropagation(); - $('body').click(); - new BulkChangePopup({ - app: this.options.app, - triggerEl: $(e.currentTarget), - bottomRight: true - }).render(); - }, - - reload(event) { - event.preventDefault(); - this.options.app.controller.fetchList(true); - }, - - newSearch() { - this.options.app.controller.newSearch(); - }, - - serializeData() { - // show "Bulk Change" button only if user has at least one QP which he administates - const canBulkChange = this.options.app.qualityProfiles.some( - profile => profile.actions && profile.actions.edit - ); - - return { - ...WorkspaceHeaderView.prototype.serializeData.apply(this, arguments), - canBulkChange - }; - } -}); diff --git a/server/sonar-web/src/main/js/apps/coding-rules/workspace-list-empty-view.js b/server/sonar-web/src/main/js/apps/coding-rules/workspace-list-empty-view.js deleted file mode 100644 index 31dfa26b8c6..00000000000 --- a/server/sonar-web/src/main/js/apps/coding-rules/workspace-list-empty-view.js +++ /dev/null @@ -1,29 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2018 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 Marionette from 'backbone.marionette'; -import { translate } from '../../helpers/l10n'; - -export default Marionette.ItemView.extend({ - className: 'search-navigator-no-results', - - template() { - return translate('coding_rules.no_results'); - } -}); diff --git a/server/sonar-web/src/main/js/apps/coding-rules/workspace-list-item-view.js b/server/sonar-web/src/main/js/apps/coding-rules/workspace-list-item-view.js deleted file mode 100644 index 9994a03d5b5..00000000000 --- a/server/sonar-web/src/main/js/apps/coding-rules/workspace-list-item-view.js +++ /dev/null @@ -1,145 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2018 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 $ from 'jquery'; -import { union } from 'lodash'; -import Backbone from 'backbone'; -import WorkspaceListItemView from '../../components/navigator/workspace-list-item-view'; -import ProfileActivationView from './rule/profile-activation-view'; -import RuleFilterMixin from './rule/rule-filter-mixin'; -import Template from './templates/coding-rules-workspace-list-item.hbs'; -import confirmDialog from './confirm-dialog'; -import { translate, translateWithParameters } from '../../helpers/l10n'; -import { getBaseUrl } from '../../helpers/urls'; - -export default WorkspaceListItemView.extend(RuleFilterMixin).extend({ - className: 'coding-rule', - template: Template, - - modelEvents: { - change: 'render' - }, - - events: { - click: 'selectCurrent', - dblclick: 'openRule', - 'click .js-rule': 'openRule', - 'click .js-rule-filter': 'onRuleFilterClick', - 'click .coding-rules-detail-quality-profile-activate': 'activate', - 'click .coding-rules-detail-quality-profile-change': 'change', - 'click .coding-rules-detail-quality-profile-revert': 'revert', - 'click .coding-rules-detail-quality-profile-deactivate': 'deactivate' - }, - - onRender() { - WorkspaceListItemView.prototype.onRender.apply(this, arguments); - this.$('[data-toggle="tooltip"]').tooltip({ - container: 'body' - }); - }, - - onDestroy() { - this.$('[data-toggle="tooltip"]').tooltip('destroy'); - }, - - selectCurrent() { - this.options.app.state.set({ selectedIndex: this.model.get('index') }); - }, - - openRule(event) { - const leftClick = - event.button === 0 && !(event.metaKey || event.altKey || event.ctrlKey || event.shiftKey); - if (leftClick) { - event.preventDefault(); - this.$('[data-toggle="tooltip"]').tooltip('destroy'); - this.options.app.controller.showDetails(this.model); - } - }, - - activate() { - const that = this; - const selectedProfile = this.options.app.state.get('query').qprofile; - const othersQualityProfiles = this.options.app.qualityProfiles.filter( - profile => profile.key !== selectedProfile - ); - const activationView = new ProfileActivationView({ - rule: this.model, - collection: new Backbone.Collection(othersQualityProfiles), - app: this.options.app - }); - activationView.on('profileActivated', (severity, params, profile) => { - const activation = { - severity, - params, - inherit: 'NONE', - qProfile: profile - }; - that.model.set({ activation }); - }); - activationView.render(); - }, - - deactivate() { - const that = this; - const ruleKey = this.model.get('key'); - const activation = this.model.get('activation'); - confirmDialog({ - title: translate('coding_rules.deactivate'), - html: translateWithParameters('coding_rules.deactivate.confirm'), - yesHandler() { - return $.ajax({ - type: 'POST', - url: window.baseUrl + '/api/qualityprofiles/deactivate_rule', - data: { - profile_key: activation.qProfile, - rule_key: ruleKey - } - }).done(() => { - that.model.unset('activation'); - }); - } - }); - }, - - serializeData() { - const selectedProfileKey = this.options.app.state.get('query').qprofile; - const selectedProfile = - selectedProfileKey && - this.options.app.qualityProfiles.find(profile => profile.key === selectedProfileKey); - const isSelectedProfileBuiltIn = selectedProfile != null && selectedProfile.isBuiltIn; - - const canEditQualityProfile = - selectedProfile && selectedProfile.actions && selectedProfile.actions.edit; - - const permalinkPath = this.options.app.organization - ? `/organizations/${this.options.app.organization}/rules` - : '/coding_rules'; - const permalink = - getBaseUrl() + permalinkPath + '#rule_key=' + encodeURIComponent(this.model.id); - - return { - ...WorkspaceListItemView.prototype.serializeData.apply(this, arguments), - canEditQualityProfile, - tags: union(this.model.get('sysTags'), this.model.get('tags')), - selectedProfile: selectedProfileKey, - isSelectedProfileBuiltIn, - permalink - }; - } -}); diff --git a/server/sonar-web/src/main/js/apps/coding-rules/workspace-list-view.js b/server/sonar-web/src/main/js/apps/coding-rules/workspace-list-view.js deleted file mode 100644 index 2baba45a4e2..00000000000 --- a/server/sonar-web/src/main/js/apps/coding-rules/workspace-list-view.js +++ /dev/null @@ -1,48 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2018 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 key from 'keymaster'; -import WorkspaceListView from '../../components/navigator/workspace-list-view'; -import WorkspaceListItemView from './workspace-list-item-view'; -import WorkspaceListEmptyView from './workspace-list-empty-view'; -import Template from './templates/coding-rules-workspace-list.hbs'; - -export default WorkspaceListView.extend({ - template: Template, - childView: WorkspaceListItemView, - childViewContainer: '.js-list', - emptyView: WorkspaceListEmptyView, - - bindShortcuts() { - WorkspaceListView.prototype.bindShortcuts.apply(this, arguments); - const that = this; - key('right', 'list', () => { - that.options.app.controller.showDetailsForSelected(); - return false; - }); - key('a', () => { - that.options.app.controller.activateCurrent(); - return false; - }); - key('d', () => { - that.options.app.controller.deactivateCurrent(); - return false; - }); - } -}); diff --git a/server/sonar-web/src/main/js/apps/component-measures/sidebar/DomainFacet.js b/server/sonar-web/src/main/js/apps/component-measures/sidebar/DomainFacet.js index 8c7eeff5622..3fd472649cd 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/sidebar/DomainFacet.js +++ b/server/sonar-web/src/main/js/apps/component-measures/sidebar/DomainFacet.js @@ -134,7 +134,7 @@ export default class DomainFacet extends React.PureComponent { const helper = `component_measures.domain_facets.${domain.name}.help`; const translatedHelper = translate(helper); return ( - + + + + ( - - - {props.current != null && ( - - {formatMeasure(props.current + 1, 'INT')} - {' / '} - - )} - {formatMeasure(props.total, 'INT')} - {' '} - {translate('issues.issues')} - -); - -export default IssuesCounter; +export default function IssuesCounter(props /*:Props*/) { + return ( + + ); +} diff --git a/server/sonar-web/src/main/js/apps/issues/components/__tests__/__snapshots__/IssuesContainer-test.js.snap b/server/sonar-web/src/main/js/apps/issues/components/__tests__/__snapshots__/IssuesContainer-test.js.snap index 0af64820202..2d597c7ccae 100644 --- a/server/sonar-web/src/main/js/apps/issues/components/__tests__/__snapshots__/IssuesContainer-test.js.snap +++ b/server/sonar-web/src/main/js/apps/issues/components/__tests__/__snapshots__/IssuesContainer-test.js.snap @@ -1,25 +1,19 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`does not show current 1`] = ` - - - 987,654,321 - - - issues.issues - + `; exports[`formats numbers 1`] = ` - - - - 1,235 - / - - 987,654,321 - - - issues.issues - + `; diff --git a/server/sonar-web/src/main/js/apps/issues/sidebar/AssigneeFacet.js b/server/sonar-web/src/main/js/apps/issues/sidebar/AssigneeFacet.js index ba1d3703588..f48516ee94f 100644 --- a/server/sonar-web/src/main/js/apps/issues/sidebar/AssigneeFacet.js +++ b/server/sonar-web/src/main/js/apps/issues/sidebar/AssigneeFacet.js @@ -195,7 +195,7 @@ export default class AssigneeFacet extends React.PureComponent { render() { return ( - + + + collapsePath(dir)); return ( - + + diff --git a/server/sonar-web/src/main/js/apps/issues/sidebar/FileFacet.js b/server/sonar-web/src/main/js/apps/issues/sidebar/FileFacet.js index ec8e9d5980c..9d2587536a3 100644 --- a/server/sonar-web/src/main/js/apps/issues/sidebar/FileFacet.js +++ b/server/sonar-web/src/main/js/apps/issues/sidebar/FileFacet.js @@ -115,7 +115,7 @@ export default class FileFacet extends React.PureComponent { render() { const values = this.props.files.map(file => this.getFileName(file)); return ( - + this.getLanguageName(language)); return ( - + this.getModuleName(module)); return ( - + this.getProjectName(project)); return ( - + this.getFacetItemName(resolution)); return ( - + , onChange: (changes: { [string]: Array }) => void, onToggle: (property: string) => void, + organization: string | void; open: boolean, stats?: { [string]: number }, referencedRules: { [string]: { name: string } }, @@ -68,10 +69,11 @@ export default class RuleFacet extends React.PureComponent { }; handleSearch = (query /*: string */) => { - const { languages } = this.props; + const { languages, organization } = this.props; return searchRules({ f: 'name,langName', languages: languages.length ? languages.join() : undefined, + organization, q: query }).then(response => response.rules.map(rule => ({ label: `(${rule.langName}) ${rule.name}`, value: rule.key })) @@ -129,7 +131,7 @@ export default class RuleFacet extends React.PureComponent { render() { const values = this.props.rules.map(rule => this.getRuleName(rule)); return ( - + translate('severity', severity)); return ( - + translate('issue.status', status)); return ( - + translate('issue.type', type)); return ( - + + + + + { )} diff --git a/server/sonar-web/src/main/js/apps/overview/meta/MetaQualityProfiles.js b/server/sonar-web/src/main/js/apps/overview/meta/MetaQualityProfiles.js index 9b08058c508..c39bcf3846d 100644 --- a/server/sonar-web/src/main/js/apps/overview/meta/MetaQualityProfiles.js +++ b/server/sonar-web/src/main/js/apps/overview/meta/MetaQualityProfiles.js @@ -34,6 +34,7 @@ class MetaQualityProfiles extends React.PureComponent { component: { organization: string }, customOrganizations: boolean, languages: { [string]: { name: string } }, + organization: string | void; profiles: Array<{ key: string, language: string, name: string }> }; */ @@ -72,10 +73,11 @@ class MetaQualityProfiles extends React.PureComponent { loadDeprecatedRulesForProfile(profileKey) { const data = { - qprofile: profileKey, activation: 'true', - statuses: 'DEPRECATED', - ps: 1 + organization: this.props.organization, + ps: 1, + qprofile: profileKey, + statuses: 'DEPRECATED' }; return searchRules(data).then(r => r.total); } diff --git a/server/sonar-web/src/main/js/apps/overview/meta/MetaTags.tsx b/server/sonar-web/src/main/js/apps/overview/meta/MetaTags.tsx index f08c08252fe..0e22da39691 100644 --- a/server/sonar-web/src/main/js/apps/overview/meta/MetaTags.tsx +++ b/server/sonar-web/src/main/js/apps/overview/meta/MetaTags.tsx @@ -104,7 +104,7 @@ export default class MetaTags extends React.PureComponent { className="button-link" onClick={this.handleClick} ref={tagsList => (this.tagsList = tagsList)}> - + {popupOpen && (
(this.tagsSelector = tagsSelector)}> @@ -121,7 +121,11 @@ export default class MetaTags extends React.PureComponent { } else { return (
- +
); } diff --git a/server/sonar-web/src/main/js/apps/overview/meta/__tests__/__snapshots__/MetaTags-test.tsx.snap b/server/sonar-web/src/main/js/apps/overview/meta/__tests__/__snapshots__/MetaTags-test.tsx.snap index 875302462d5..f7c3da2dfca 100644 --- a/server/sonar-web/src/main/js/apps/overview/meta/__tests__/__snapshots__/MetaTags-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/overview/meta/__tests__/__snapshots__/MetaTags-test.tsx.snap @@ -108,6 +108,7 @@ exports[`should render without tags and admin rights 1`] = ` > )} - {hasTags && } + {hasTags && }
{project.analysisDate && project.leakPeriodDate && ( diff --git a/server/sonar-web/src/main/js/apps/projects/components/ProjectCardOverall.tsx b/server/sonar-web/src/main/js/apps/projects/components/ProjectCardOverall.tsx index 104be36dca3..275aa8fb49a 100644 --- a/server/sonar-web/src/main/js/apps/projects/components/ProjectCardOverall.tsx +++ b/server/sonar-web/src/main/js/apps/projects/components/ProjectCardOverall.tsx @@ -62,7 +62,7 @@ export default function ProjectCardOverall({ organization, project }: Props) { {isPrivate && ( )} - {hasTags && } + {hasTags && }
{project.analysisDate && (
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/changelog/__tests__/Changelog-test.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/changelog/__tests__/Changelog-test.tsx index 1983078ec2d..1c9a7da96c9 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/changelog/__tests__/Changelog-test.tsx +++ b/server/sonar-web/src/main/js/apps/quality-profiles/changelog/__tests__/Changelog-test.tsx @@ -68,7 +68,7 @@ it('should render action', () => { it('should render rule', () => { const events = [createEvent()]; const changelog = shallow(); - expect(changelog.find('Link').prop('to')).toContain('rule_key=squid1234'); + expect(changelog.find('Link').prop('to')).toHaveProperty('query', { rule_key: 'squid1234' }); }); it('should render ChangesList', () => { diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/compare/__tests__/ComparisonResults-test.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/compare/__tests__/ComparisonResults-test.tsx index 76136ad67d6..4acaf357bd9 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/compare/__tests__/ComparisonResults-test.tsx +++ b/server/sonar-web/src/main/js/apps/quality-profiles/compare/__tests__/ComparisonResults-test.tsx @@ -73,7 +73,7 @@ it('should compare', () => { const leftDiffs = output.find('.js-comparison-in-left'); expect(leftDiffs.length).toBe(1); expect(leftDiffs.find(Link).length).toBe(1); - expect(leftDiffs.find(Link).prop('to')).toContain('rule_key=rule1'); + expect(leftDiffs.find(Link).prop('to')).toHaveProperty('query', { rule_key: 'rule1' }); expect(leftDiffs.find(Link).prop('children')).toContain('rule1'); expect(leftDiffs.find(SeverityIcon).length).toBe(1); expect(leftDiffs.find(SeverityIcon).prop('severity')).toBe('BLOCKER'); @@ -86,7 +86,7 @@ it('should compare', () => { .at(0) .find(Link) .prop('to') - ).toContain('rule_key=rule2'); + ).toHaveProperty('query', { rule_key: 'rule2' }); expect( rightDiffs .at(0) @@ -108,7 +108,7 @@ it('should compare', () => { .find(Link) .at(0) .prop('to') - ).toContain('rule_key=rule4'); + ).toHaveProperty('query', { rule_key: 'rule4' }); expect( modifiedDiffs .find(Link) diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/components/__tests__/__snapshots__/ProfileActions-test.tsx.snap b/server/sonar-web/src/main/js/apps/quality-profiles/components/__tests__/__snapshots__/ProfileActions-test.tsx.snap index 7e53929904b..01f38890790 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/components/__tests__/__snapshots__/ProfileActions-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/quality-profiles/components/__tests__/__snapshots__/ProfileActions-test.tsx.snap @@ -4,7 +4,15 @@ exports[`renders with all permissions 1`] = ` quality_profiles.activate_more_rules @@ -88,7 +96,15 @@ exports[`renders with permission to edit only 1`] = ` quality_profiles.activate_more_rules diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileRules.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileRules.tsx index c7a20068385..93bad2515b0 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileRules.tsx +++ b/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileRules.tsx @@ -91,17 +91,19 @@ export default class ProfileRules extends React.PureComponent { loadAllRules() { return searchRules({ languages: this.props.profile.language, - ps: 1, - facets: 'types' + facets: 'types', + organization: this.props.organization || undefined, + ps: 1 }); } loadActivatedRules() { return searchRules({ - qprofile: this.props.profile.key, activation: 'true', + facets: 'types', + organization: this.props.organization || undefined, ps: 1, - facets: 'types' + qprofile: this.props.profile.key }); } diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileRulesDeprecatedWarning.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileRulesDeprecatedWarning.tsx index 5ea85d8a669..e2cd08698ce 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileRulesDeprecatedWarning.tsx +++ b/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileRulesDeprecatedWarning.tsx @@ -30,7 +30,6 @@ interface Props { } export default function ProfileRulesDeprecatedWarning(props: Props) { - const url = getDeprecatedActiveRulesUrl({ qprofile: props.profile }, props.organization); return (
@@ -39,7 +38,9 @@ export default function ProfileRulesDeprecatedWarning(props: Props) { - + {props.activeDeprecatedRules}
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfileRules-test.tsx.snap b/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfileRules-test.tsx.snap index c366398e56e..2fd0450b1bd 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfileRules-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfileRules-test.tsx.snap @@ -74,7 +74,15 @@ exports[`should show a button to activate more rules for admins 1`] = ` className="button js-activate-rules" onlyActiveOnIndex={false} style={Object {}} - to="/organizations/foo/rules#qprofile=foo|activation=false" + to={ + Object { + "pathname": "/organizations/foo/rules", + "query": Object { + "activation": "false", + "qprofile": "foo", + }, + } + } > quality_profiles.activate_more diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfileRulesDeprecatedWarning-test.tsx.snap b/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfileRulesDeprecatedWarning-test.tsx.snap index 54fa91cc49d..4072c84db21 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfileRulesDeprecatedWarning-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfileRulesDeprecatedWarning-test.tsx.snap @@ -21,7 +21,16 @@ exports[`should render correctly 1`] = ` className="pull-right" onlyActiveOnIndex={false} style={Object {}} - to="/organizations/foo/rules#qprofile=bar|activation=true|statuses=DEPRECATED" + to={ + Object { + "pathname": "/organizations/foo/rules", + "query": Object { + "activation": "true", + "qprofile": "bar", + "statuses": "DEPRECATED", + }, + } + } > 18 diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfileRulesRowOfType-test.tsx.snap b/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfileRulesRowOfType-test.tsx.snap index 62866095c17..1a879d91adf 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfileRulesRowOfType-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfileRulesRowOfType-test.tsx.snap @@ -17,7 +17,16 @@ exports[`should render correctly 1`] = ` 3 @@ -29,7 +38,16 @@ exports[`should render correctly 1`] = ` className="small text-muted" onlyActiveOnIndex={false} style={Object {}} - to="/organizations/foo/rules#qprofile=bar|activation=false|types=BUG" + to={ + Object { + "pathname": "/organizations/foo/rules", + "query": Object { + "activation": "false", + "qprofile": "bar", + "types": "BUG", + }, + } + } > 7 @@ -54,7 +72,16 @@ exports[`should render correctly if there is 0 rules 1`] = ` 0 @@ -88,7 +115,16 @@ exports[`should render correctly if there is missing data 1`] = ` 5 diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfileRulesRowTotal-test.tsx.snap b/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfileRulesRowTotal-test.tsx.snap index f8686659326..05ad9a31580 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfileRulesRowTotal-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfileRulesRowTotal-test.tsx.snap @@ -13,7 +13,15 @@ exports[`should render correctly 1`] = ` 3 @@ -27,7 +35,15 @@ exports[`should render correctly 1`] = ` className="small text-muted" onlyActiveOnIndex={false} style={Object {}} - to="/organizations/foo/rules#qprofile=bar|activation=false" + to={ + Object { + "pathname": "/organizations/foo/rules", + "query": Object { + "activation": "false", + "qprofile": "bar", + }, + } + } > 7 @@ -50,7 +66,15 @@ exports[`should render correctly if there is 0 rules 1`] = ` 0 @@ -82,7 +106,15 @@ exports[`should render correctly if there is missing data 1`] = ` 5 diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfileRulesSonarWayComparison-test.tsx.snap b/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfileRulesSonarWayComparison-test.tsx.snap index 481846b23ad..7edb44e1c23 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfileRulesSonarWayComparison-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfileRulesSonarWayComparison-test.tsx.snap @@ -21,7 +21,17 @@ exports[`should render correctly 1`] = ` className="pull-right" onlyActiveOnIndex={false} style={Object {}} - to="/organizations/foo/rules#qprofile=bar|activation=false|compareToProfile=baz|languages=Java" + to={ + Object { + "pathname": "/organizations/foo/rules", + "query": Object { + "activation": "false", + "compareToProfile": "baz", + "languages": "Java", + "qprofile": "bar", + }, + } + } > 158 diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/home/EvolutionRules.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/home/EvolutionRules.tsx index af4f60216e0..d4b1df5a548 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/home/EvolutionRules.tsx +++ b/server/sonar-web/src/main/js/apps/quality-profiles/home/EvolutionRules.tsx @@ -75,11 +75,12 @@ export default class EvolutionRules extends React.PureComponent { loadLatestRules() { const data = { - available_since: this.periodStartDate, - s: 'createdAt', asc: false, + available_since: this.periodStartDate, + f: 'name,langName,actives', + organization: this.props.organization || undefined, ps: RULES_LIMIT, - f: 'name,langName,actives' + s: 'createdAt' }; searchRules(data).then( diff --git a/server/sonar-web/src/main/js/components/common/BubblePopup.tsx b/server/sonar-web/src/main/js/components/common/BubblePopup.tsx index 18b7dbd6f95..38ebd834989 100644 --- a/server/sonar-web/src/main/js/components/common/BubblePopup.tsx +++ b/server/sonar-web/src/main/js/components/common/BubblePopup.tsx @@ -21,8 +21,9 @@ import * as React from 'react'; import * as classNames from 'classnames'; export interface BubblePopupPosition { - top: number; - right: number; + top?: number; + left?: number; + right?: number; } interface Props { diff --git a/server/sonar-web/src/main/js/components/common/DeferredSpinner.tsx b/server/sonar-web/src/main/js/components/common/DeferredSpinner.tsx index dee23f8152b..ffc8b0668b7 100644 --- a/server/sonar-web/src/main/js/components/common/DeferredSpinner.tsx +++ b/server/sonar-web/src/main/js/components/common/DeferredSpinner.tsx @@ -21,7 +21,7 @@ import * as React from 'react'; import * as classNames from 'classnames'; interface Props { - children?: JSX.Element | JSX.Element[]; + children?: React.ReactNode; className?: string; loading?: boolean; customSpinner?: JSX.Element; diff --git a/server/sonar-web/src/main/js/apps/issues/components/FiltersHeader.js b/server/sonar-web/src/main/js/components/common/FiltersHeader.tsx similarity index 66% rename from server/sonar-web/src/main/js/apps/issues/components/FiltersHeader.js rename to server/sonar-web/src/main/js/components/common/FiltersHeader.tsx index c0f0e8b0998..33567469928 100644 --- a/server/sonar-web/src/main/js/apps/issues/components/FiltersHeader.js +++ b/server/sonar-web/src/main/js/components/common/FiltersHeader.tsx @@ -17,32 +17,30 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -// @flow -import React from 'react'; -import { translate } from '../../../helpers/l10n'; +import * as React from 'react'; +import { translate } from '../../helpers/l10n'; -/*:: -type Props = { - displayReset: boolean, - onReset: () => void -}; -*/ - -export default class FiltersHeader extends React.PureComponent { - /*:: props: Props; */ +interface Props { + displayReset: boolean; + onReset: () => void; +} - handleResetClick = (e /*: Event & { currentTarget: HTMLElement } */) => { - e.preventDefault(); - e.currentTarget.blur(); +export default class FiltersHeader extends React.PureComponent { + handleResetClick = (event: React.SyntheticEvent) => { + event.preventDefault(); + event.currentTarget.blur(); this.props.onReset(); }; render() { return ( -
+
{this.props.displayReset && (
-
diff --git a/server/sonar-web/src/main/js/components/common/MarkdownTips.js b/server/sonar-web/src/main/js/components/common/MarkdownTips.tsx similarity index 94% rename from server/sonar-web/src/main/js/components/common/MarkdownTips.js rename to server/sonar-web/src/main/js/components/common/MarkdownTips.tsx index aeb95c07d36..93755541652 100644 --- a/server/sonar-web/src/main/js/components/common/MarkdownTips.js +++ b/server/sonar-web/src/main/js/components/common/MarkdownTips.tsx @@ -17,13 +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. */ -// @flow -import React from 'react'; +import * as React from 'react'; import { getMarkdownHelpUrl } from '../../helpers/urls'; import { translate } from '../../helpers/l10n'; export default class MarkdownTips extends React.PureComponent { - handleClick(evt /*: MouseEvent */) { + handleClick(evt: React.SyntheticEvent) { evt.preventDefault(); window.open(getMarkdownHelpUrl(), 'Markdown', 'height=300,width=600,scrollbars=1,resizable=1'); } diff --git a/server/sonar-web/src/main/js/components/common/PageCounter.tsx b/server/sonar-web/src/main/js/components/common/PageCounter.tsx new file mode 100644 index 00000000000..2ef4e27df67 --- /dev/null +++ b/server/sonar-web/src/main/js/components/common/PageCounter.tsx @@ -0,0 +1,41 @@ +/* + * SonarQube + * Copyright (C) 2009-2018 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import * as React from 'react'; +import * as classNames from 'classnames'; +import { formatMeasure } from '../../helpers/measures'; + +interface Props { + className?: string; + current?: number; + label: string; + total: number; +} + +export default function PageCounter({ className, current, label, total }: Props) { + return ( +
+ + {current !== undefined && formatMeasure(current + 1, 'INT') + ' / '} + {formatMeasure(total, 'INT')} + + {label} +
+ ); +} diff --git a/server/sonar-web/src/main/js/components/common/action-options-view.js b/server/sonar-web/src/main/js/components/common/action-options-view.js deleted file mode 100644 index 8e65eef4c5a..00000000000 --- a/server/sonar-web/src/main/js/components/common/action-options-view.js +++ /dev/null @@ -1,133 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2018 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 $ from 'jquery'; -import key from 'keymaster'; -import PopupView from './popup'; - -export default PopupView.extend({ - className: 'bubble-popup bubble-popup-menu', - keyScope: 'action-options', - - ui: { - options: '.menu > li > a' - }, - - events() { - return { - 'click @ui.options': 'selectOption', - 'mouseenter @ui.options': 'activateOptionByPointer' - }; - }, - - initialize() { - this.bindShortcuts(); - }, - - onRender() { - PopupView.prototype.onRender.apply(this, arguments); - this.selectInitialOption(); - }, - - getOptions() { - return this.$('.menu > li > a'); - }, - - getActiveOption() { - return this.getOptions().filter('.active'); - }, - - makeActive(option) { - if (option.length > 0) { - this.getOptions() - .removeClass('active') - .tooltip('hide'); - option.addClass('active').tooltip('show'); - } - }, - - selectInitialOption() { - this.makeActive(this.getOptions().first()); - }, - - selectNextOption() { - this.makeActive( - this.getActiveOption() - .parent() - .nextAll('li:not(.divider)') - .first() - .children('a') - ); - return false; - }, - - selectPreviousOption() { - this.makeActive( - this.getActiveOption() - .parent() - .prevAll('li:not(.divider)') - .first() - .children('a') - ); - return false; - }, - - activateOptionByPointer(e) { - this.makeActive($(e.currentTarget)); - }, - - bindShortcuts() { - const that = this; - this.currentKeyScope = key.getScope(); - key.setScope(this.keyScope); - key('down', this.keyScope, () => that.selectNextOption()); - key('up', this.keyScope, () => that.selectPreviousOption()); - key('return', this.keyScope, () => that.selectActiveOption()); - key('escape', this.keyScope, () => that.destroy()); - key('backspace', this.keyScope, () => false); - key('shift+tab', this.keyScope, () => false); - }, - - unbindShortcuts() { - key.unbind('down', this.keyScope); - key.unbind('up', this.keyScope); - key.unbind('return', this.keyScope); - key.unbind('escape', this.keyScope); - key.unbind('backspace', this.keyScope); - key.unbind('tab', this.keyScope); - key.unbind('shift+tab', this.keyScope); - key.setScope(this.currentKeyScope); - }, - - onDestroy() { - PopupView.prototype.onDestroy.apply(this, arguments); - this.unbindShortcuts(); - this.$('[data-toggle="tooltip"]').tooltip('destroy'); - $('.tooltip').remove(); - }, - - selectOption(e) { - e.preventDefault(); - this.destroy(); - }, - - selectActiveOption() { - this.getActiveOption().click(); - } -}); diff --git a/server/sonar-web/src/main/js/components/common/templates/_markdown-tips.hbs b/server/sonar-web/src/main/js/components/common/templates/_markdown-tips.hbs deleted file mode 100644 index d6e538797c3..00000000000 --- a/server/sonar-web/src/main/js/components/common/templates/_markdown-tips.hbs +++ /dev/null @@ -1,4 +0,0 @@ -
- {{t 'markdown.helplink'}} : -   *{{t 'bold'}}*    ``{{t 'code'}}``    * {{t 'bulleted_point'}} -
diff --git a/server/sonar-web/src/main/js/components/controls/ReloadButton.tsx b/server/sonar-web/src/main/js/components/controls/ReloadButton.tsx new file mode 100644 index 00000000000..134e61adc7e --- /dev/null +++ b/server/sonar-web/src/main/js/components/controls/ReloadButton.tsx @@ -0,0 +1,59 @@ +/* + * SonarQube + * Copyright (C) 2009-2018 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import * as React from 'react'; +import * as classNames from 'classnames'; +import Tooltip from './Tooltip'; +import * as theme from '../../app/theme'; +import { translate } from '../../helpers/l10n'; + +interface Props { + className?: string; + onClick: () => void; +} + +export default class ReloadButton extends React.PureComponent { + handleClick = (event: React.SyntheticEvent) => { + event.preventDefault(); + event.currentTarget.blur(); + this.props.onClick(); + }; + + renderIcon = () => ( + + + + ); + + render() { + return ( + + + {this.renderIcon()} + + + ); + } +} diff --git a/server/sonar-web/src/main/js/components/controls/SearchBox.tsx b/server/sonar-web/src/main/js/components/controls/SearchBox.tsx index d85afa7fb5e..a4bb8196492 100644 --- a/server/sonar-web/src/main/js/components/controls/SearchBox.tsx +++ b/server/sonar-web/src/main/js/components/controls/SearchBox.tsx @@ -29,7 +29,9 @@ import './SearchBox.css'; interface Props { autoFocus?: boolean; + className?: string; innerRef?: (node: HTMLInputElement | null) => void; + id?: string; minLength?: number; onChange: (value: string) => void; onClick?: React.MouseEventHandler; @@ -127,7 +129,7 @@ export default class SearchBox extends React.PureComponent { const tooShort = minLength !== undefined && value.length > 0 && value.length < minLength; return ( -
+
Promise>, - onSelect: (value: string) => void, - renderOption?: (option: Object) => React.Element<*>, - resetOnBlur: boolean, - value?: string -|}; -*/ +interface Props { + autofocus?: boolean; + minimumQueryLength?: number; + onSearch: (query: string) => Promise; + onSelect: (value: string) => void; + renderOption?: (option: Object) => JSX.Element; + resetOnBlur?: boolean; + value?: string; +} -/*:: -type State = { - loading: boolean, - options: Array