From 8335168f11c4b8b6bd905e3a883c001352ab234d Mon Sep 17 00:00:00 2001 From: Stas Vilchik Date: Tue, 13 Mar 2018 10:51:29 +0100 Subject: [PATCH] drop unused web components --- server/sonar-web/config/webpack.config.js | 5 +- .../nav/component/ComponentNavMenu.tsx | 53 +- .../__tests__/ComponentNavMenu-test.tsx | 20 +- .../ComponentNavMenu-test.tsx.snap | 1033 +++---- .../components/nav/global/GlobalNavMenu.tsx | 24 +- .../global/__tests__/GlobalNavMenu-test.tsx | 2 +- .../__snapshots__/GlobalNavMenu-test.tsx.snap | 116 +- .../components/nav/settings/SettingsNav.tsx | 204 +- .../settings/__tests__/SettingsNav-test.tsx | 1 + .../__snapshots__/SettingsNav-test.tsx.snap | 307 ++- .../main/js/app/styles/components/modals.css | 4 - .../src/main/js/app/styles/select2-sonar.css | 192 -- .../src/main/js/app/styles/select2.css | 493 ---- .../src/main/js/app/styles/sonar.css | 2 - .../src/main/js/apps/coding-rules/styles.css | 4 - .../components/MeasureContentContainer.js | 4 - .../components/MeasureHeader.js | 13 - .../drilldown/BubbleChart.js | 11 +- .../src/main/js/apps/issues/components/App.js | 51 +- .../apps/issues/sidebar/CreationDateFacet.js | 16 +- .../js/apps/projects/visualizations/Risk.tsx | 40 +- .../visualizations/SimpleBubbleChart.tsx | 43 +- .../__tests__/SimpleBubbleChart-test.tsx | 3 +- .../__snapshots__/Risk-test.tsx.snap | 37 +- .../SimpleBubbleChart-test.tsx.snap | 34 +- .../projectsManagement/ProjectRowActions.tsx | 4 +- .../__tests__/ProjectRowActions-test.tsx | 2 +- .../ProjectRowActions-test.tsx.snap | 6 +- .../home/ProfilesListHeader.tsx | 104 +- .../js/apps/system/components/PageActions.tsx | 95 +- .../components/__tests__/PageActions-test.tsx | 4 +- .../__snapshots__/PageActions-test.tsx.snap | 116 +- .../SourceViewer/SourceViewerHeader.tsx | 97 +- .../js/components/SourceViewer/styles.css | 6 +- .../js/components/charts/BubbleChart.d.ts | 2 +- .../main/js/components/charts/BubbleChart.js | 25 +- .../charts/__tests__/work-cloud-test.js | 32 - .../main/js/components/charts/bar-chart.js | 87 +- .../main/js/components/charts/donut-chart.js | 9 +- .../main/js/components/charts/line-chart.js | 3 +- .../main/js/components/charts/word-cloud.js | 87 - .../components/controls/ActionsDropdown.tsx | 41 +- .../js/components/mixins/tooltips-mixin.js | 106 - .../shared/ComplexityDistribution.js | 70 - .../js/libs/third-party/bootstrap/dropdown.js | 171 -- .../js/libs/third-party/bootstrap/tooltip.js | 487 ---- .../src/main/js/libs/third-party/select2.js | 2429 ----------------- 47 files changed, 1156 insertions(+), 5539 deletions(-) delete mode 100644 server/sonar-web/src/main/js/app/styles/select2-sonar.css delete mode 100644 server/sonar-web/src/main/js/app/styles/select2.css delete mode 100644 server/sonar-web/src/main/js/components/charts/__tests__/work-cloud-test.js delete mode 100644 server/sonar-web/src/main/js/components/charts/word-cloud.js delete mode 100644 server/sonar-web/src/main/js/components/mixins/tooltips-mixin.js delete mode 100644 server/sonar-web/src/main/js/components/shared/ComplexityDistribution.js delete mode 100644 server/sonar-web/src/main/js/libs/third-party/bootstrap/dropdown.js delete mode 100644 server/sonar-web/src/main/js/libs/third-party/bootstrap/tooltip.js delete mode 100644 server/sonar-web/src/main/js/libs/third-party/select2.js diff --git a/server/sonar-web/config/webpack.config.js b/server/sonar-web/config/webpack.config.js index 3d1e7aba4e6..5a7513de971 100644 --- a/server/sonar-web/config/webpack.config.js +++ b/server/sonar-web/config/webpack.config.js @@ -59,10 +59,7 @@ module.exports = ({ production = true, fast = false }) => ({ 'backbone', 'backbone.marionette', 'handlebars/runtime', - './src/main/js/libs/third-party/jquery-ui.js', - './src/main/js/libs/third-party/select2.js', - './src/main/js/libs/third-party/bootstrap/tooltip.js', - './src/main/js/libs/third-party/bootstrap/dropdown.js' + './src/main/js/libs/third-party/jquery-ui.js' ].filter(Boolean), app: [ diff --git a/server/sonar-web/src/main/js/app/components/nav/component/ComponentNavMenu.tsx b/server/sonar-web/src/main/js/app/components/nav/component/ComponentNavMenu.tsx index 80f9ebb31de..3a78c92dc46 100644 --- a/server/sonar-web/src/main/js/app/components/nav/component/ComponentNavMenu.tsx +++ b/server/sonar-web/src/main/js/app/components/nav/component/ComponentNavMenu.tsx @@ -22,6 +22,7 @@ import { Link } from 'react-router'; import * as classNames from 'classnames'; import * as PropTypes from 'prop-types'; import { BranchLike, Component, Extension } from '../../../types'; +import Dropdown from '../../../../components/controls/Dropdown'; import NavBarTabs from '../../../../components/nav/NavBarTabs'; import { isShortLivingBranch, @@ -182,17 +183,21 @@ export default class ComponentNavMenu extends React.PureComponent { } return ( -
  • - - {translate('layout.settings')}  - - -
      {adminLinks}
    -
  • + + {({ onToggleClick, open }) => ( +
  • + + {translate('layout.settings')} + + +
      {adminLinks}
    +
  • + )} +
    ); } @@ -416,17 +421,21 @@ export default class ComponentNavMenu extends React.PureComponent { } return ( -
  • - - {translate('more')}  - - -
      {extensions.map(e => this.renderExtension(e, false))}
    -
  • + + {({ onToggleClick, open }) => ( +
  • + + {translate('more')} + + +
      {extensions.map(e => this.renderExtension(e, false))}
    +
  • + )} +
    ); } diff --git a/server/sonar-web/src/main/js/app/components/nav/component/__tests__/ComponentNavMenu-test.tsx b/server/sonar-web/src/main/js/app/components/nav/component/__tests__/ComponentNavMenu-test.tsx index ef502e33aad..4ac93c4fb00 100644 --- a/server/sonar-web/src/main/js/app/components/nav/component/__tests__/ComponentNavMenu-test.tsx +++ b/server/sonar-web/src/main/js/app/components/nav/component/__tests__/ComponentNavMenu-test.tsx @@ -38,11 +38,11 @@ it('should work with extensions', () => { configuration: { showSettings: true, extensions: [{ key: 'foo', name: 'Foo' }] }, extensions: [{ key: 'component-foo', name: 'ComponentFoo' }] }; - expect( - shallow(, { - context: { branchesEnabled: true } - }) - ).toMatchSnapshot(); + const wrapper = shallow(, { + context: { branchesEnabled: true } + }); + expect(wrapper.find('Dropdown[data-test="extensions"]').dive()).toMatchSnapshot(); + expect(wrapper.find('Dropdown[data-test="admin-extensions"]').dive()).toMatchSnapshot(); }); it('should work with multiple extensions', () => { @@ -57,11 +57,11 @@ it('should work with multiple extensions', () => { { key: 'component-bar', name: 'ComponentBar' } ] }; - expect( - shallow(, { - context: { branchesEnabled: true } - }) - ).toMatchSnapshot(); + const wrapper = shallow(, { + context: { branchesEnabled: true } + }); + expect(wrapper.find('Dropdown[data-test="extensions"]').dive()).toMatchSnapshot(); + expect(wrapper.find('Dropdown[data-test="admin-extensions"]').dive()).toMatchSnapshot(); }); it('should work for short-living branches', () => { diff --git a/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavMenu-test.tsx.snap b/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavMenu-test.tsx.snap index 5665e5ab9b4..249f20e49dc 100644 --- a/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavMenu-test.tsx.snap +++ b/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavMenu-test.tsx.snap @@ -89,102 +89,9 @@ exports[`should work for all qualifiers 1`] = ` project_activity.page -
  • - - layout.settings -   - - -
      -
    • - - project_settings.page - -
    • -
    • - - project_branches.page - -
    • -
    • - - webhooks.page - -
    • -
    • - - deletion.page - -
    • -
    -
  • + `; @@ -277,45 +184,9 @@ exports[`should work for all qualifiers 2`] = ` project_activity.page -
  • - - layout.settings -   - - -
      -
    • - - project_settings.page - -
    • -
    -
  • + `; @@ -408,45 +279,9 @@ exports[`should work for all qualifiers 3`] = ` project_activity.page -
  • - - layout.settings -   - - -
      -
    • - - deletion.page - -
    • -
    -
  • + `; @@ -631,45 +466,9 @@ exports[`should work for all qualifiers 5`] = ` project_activity.page -
  • - - layout.settings -   - - -
      -
    • - - deletion.page - -
    • -
    -
  • + `; @@ -911,531 +710,355 @@ exports[`should work for short-living branches 1`] = ` `; exports[`should work with extensions 1`] = ` - -
  • - - overview.page - -
  • -
  • - - issues.page - -
  • -
  • - - layout.measures - -
  • -
  • - - code.page - -
  • -
  • - - project_activity.page - -
  • -
  • + - - layout.settings -   - - -
      + +
        +
      • -
      • - - project_settings.page - -
      • -
      • - +
      • +
      • + - project_branches.page - -
      • -
      • - +
      • +
      • + - webhooks.page - -
      • -
      • - +
      • +
      • + - Foo - -
      • -
      • - +
      • +
      • + - deletion.page - -
      • -
      - -
    • + deletion.page + +
    • +
    +
  • +`; + +exports[`should work with extensions 2`] = ` +
  • + - - more -   - - -
      + +
        +
      • -
      • - - ComponentFoo - -
      • -
      - - + } + > + ComponentFoo + + +
    +
  • `; exports[`should work with multiple extensions 1`] = ` - -
  • - - overview.page - -
  • -
  • - - issues.page - -
  • -
  • - - layout.measures - -
  • -
  • - - code.page - -
  • -
  • - - project_activity.page - -
  • -
  • + - - layout.settings -   - - -
      + +
        +
      • -
      • - - project_settings.page - -
      • -
      • - +
      • +
      • + - project_branches.page - -
      • -
      • - +
      • +
      • + - webhooks.page - -
      • -
      • - +
      • +
      • + - Foo - -
      • -
      • - +
      • +
      • + - Bar - -
      • -
      • - +
      • +
      • + - deletion.page - -
      • -
      - -
    • + deletion.page + +
    • +
    +
  • +`; + +exports[`should work with multiple extensions 2`] = ` +
  • + - - more -   - - -
      + +
        +
      • -
      • - - ComponentFoo - -
      • -
      • - +
      • +
      • + - ComponentBar - -
      • -
      - - + } + > + ComponentBar + + +
    +
  • `; diff --git a/server/sonar-web/src/main/js/app/components/nav/global/GlobalNavMenu.tsx b/server/sonar-web/src/main/js/app/components/nav/global/GlobalNavMenu.tsx index dacf97dc27f..443f074225c 100644 --- a/server/sonar-web/src/main/js/app/components/nav/global/GlobalNavMenu.tsx +++ b/server/sonar-web/src/main/js/app/components/nav/global/GlobalNavMenu.tsx @@ -19,10 +19,12 @@ */ import * as React from 'react'; import { Link } from 'react-router'; +import * as classNames from 'classnames'; import { isLoggedIn, CurrentUser, AppState, Extension } from '../../../../app/types'; import { translate } from '../../../../helpers/l10n'; import { getQualityGatesUrl, getBaseUrl } from '../../../../helpers/urls'; import { isMySet } from '../../../../apps/issues/utils'; +import Dropdown from '../../../../components/controls/Dropdown'; interface Props { appState: AppState; @@ -151,13 +153,21 @@ export default class GlobalNavMenu extends React.PureComponent { return null; } return ( -
  • - - {translate('more')}  - - -
      {withoutPortfolios.map(this.renderGlobalPageLink)}
    -
  • + + {({ onToggleClick, open }) => ( +
  • + + {translate('more')} + + +
      {withoutPortfolios.map(this.renderGlobalPageLink)}
    +
  • + )} +
    ); } diff --git a/server/sonar-web/src/main/js/app/components/nav/global/__tests__/GlobalNavMenu-test.tsx b/server/sonar-web/src/main/js/app/components/nav/global/__tests__/GlobalNavMenu-test.tsx index 5f3a4d7e656..22fa861f34f 100644 --- a/server/sonar-web/src/main/js/app/components/nav/global/__tests__/GlobalNavMenu-test.tsx +++ b/server/sonar-web/src/main/js/app/components/nav/global/__tests__/GlobalNavMenu-test.tsx @@ -32,7 +32,7 @@ it('should work with extensions', () => { const wrapper = shallow( ); - expect(wrapper).toMatchSnapshot(); + expect(wrapper.find('Dropdown').dive()).toMatchSnapshot(); }); it('should show administration menu if the user has the rights', () => { diff --git a/server/sonar-web/src/main/js/app/components/nav/global/__tests__/__snapshots__/GlobalNavMenu-test.tsx.snap b/server/sonar-web/src/main/js/app/components/nav/global/__tests__/__snapshots__/GlobalNavMenu-test.tsx.snap index 34dd8f91396..27714386573 100644 --- a/server/sonar-web/src/main/js/app/components/nav/global/__tests__/__snapshots__/GlobalNavMenu-test.tsx.snap +++ b/server/sonar-web/src/main/js/app/components/nav/global/__tests__/__snapshots__/GlobalNavMenu-test.tsx.snap @@ -77,98 +77,34 @@ exports[`should show administration menu if the user has the rights 1`] = ` `; exports[`should work with extensions 1`] = ` -
      -
    • - - projects.page - -
    • -
    • - - issues.page - -
    • -
    • - - coding_rules.page - -
    • -
    • - - quality_profiles.page - -
    • -
    • - - quality_gates.page - -
    • - +
    + `; diff --git a/server/sonar-web/src/main/js/app/components/nav/settings/SettingsNav.tsx b/server/sonar-web/src/main/js/app/components/nav/settings/SettingsNav.tsx index 688bce71d8e..3064c54212e 100644 --- a/server/sonar-web/src/main/js/app/components/nav/settings/SettingsNav.tsx +++ b/server/sonar-web/src/main/js/app/components/nav/settings/SettingsNav.tsx @@ -27,6 +27,7 @@ import NavBarTabs from '../../../../components/nav/NavBarTabs'; import { EditionStatus } from '../../../../api/marketplace'; import { Extension } from '../../../types'; import { translate } from '../../../../helpers/l10n'; +import Dropdown from '../../../../components/controls/Dropdown'; interface Props { editionStatus?: EditionStatus; @@ -82,118 +83,135 @@ export default class SettingsNav extends React.PureComponent { renderConfigurationTab() { const { organizationsEnabled } = this.props; - const configurationClassNames = classNames('dropdown-toggle', { - active: - !this.isSecurityActive() && - !this.isProjectsActive() && - !this.isSystemActive() && - !this.isSomethingActive(['/admin/extension/license/support']) && - !this.isMarketplace() - }); const extensionsWithoutSupport = this.props.extensions.filter( extension => extension.key !== 'license/support' ); return ( -
  • - - {translate('sidebar.project_settings')} - -
      -
    • - - {translate('settings.page')} - -
    • -
    • - - {translate('property.category.security.encryption')} - + + {({ onToggleClick, open }) => ( +
    • + + {translate('sidebar.project_settings')} + + +
        +
      • + + {translate('settings.page')} + +
      • +
      • + + {translate('property.category.security.encryption')} + +
      • +
      • + + {translate('custom_metrics.page')} + +
      • + {!organizationsEnabled && ( +
      • + + {translate('webhooks.page')} + +
      • + )} + {extensionsWithoutSupport.map(this.renderExtension)} +
    • -
    • - - {translate('custom_metrics.page')} - -
    • - {!organizationsEnabled && ( -
    • - - {translate('webhooks.page')} - -
    • - )} - {extensionsWithoutSupport.map(this.renderExtension)} -
    -
  • + )} +
    ); } renderProjectsTab() { const { organizationsEnabled } = this.props; - const projectsClassName = classNames('dropdown-toggle', { active: this.isProjectsActive() }); return ( -
  • - - {translate('sidebar.projects')} - -
      - {!organizationsEnabled && ( -
    • - - {translate('management')} - -
    • - )} -
    • - - {translate('background_tasks.page')} - + + {({ onToggleClick, open }) => ( +
    • + + {translate('sidebar.projects')} + +
        + {!organizationsEnabled && ( +
      • + + {translate('management')} + +
      • + )} +
      • + + {translate('background_tasks.page')} + +
      • +
    • -
    -
  • + )} +
    ); } renderSecurityTab() { const { organizationsEnabled } = this.props; - const securityClassName = classNames('dropdown-toggle', { active: this.isSecurityActive() }); return ( -
  • - - {translate('sidebar.security')} - -
      -
    • - - {translate('users.page')} - + + {({ onToggleClick, open }) => ( +
    • + + {translate('sidebar.security')} + +
        +
      • + + {translate('users.page')} + +
      • + {!organizationsEnabled && ( +
      • + + {translate('user_groups.page')} + +
      • + )} + {!organizationsEnabled && ( +
      • + + {translate('global_permissions.page')} + +
      • + )} + {!organizationsEnabled && ( +
      • + + {translate('permission_templates')} + +
      • + )} +
    • - {!organizationsEnabled && ( -
    • - - {translate('user_groups.page')} - -
    • - )} - {!organizationsEnabled && ( -
    • - - {translate('global_permissions.page')} - -
    • - )} - {!organizationsEnabled && ( -
    • - - {translate('permission_templates')} - -
    • - )} -
    -
  • + )} +
    ); } diff --git a/server/sonar-web/src/main/js/app/components/nav/settings/__tests__/SettingsNav-test.tsx b/server/sonar-web/src/main/js/app/components/nav/settings/__tests__/SettingsNav-test.tsx index 6deee4fd798..e589b879494 100644 --- a/server/sonar-web/src/main/js/app/components/nav/settings/__tests__/SettingsNav-test.tsx +++ b/server/sonar-web/src/main/js/app/components/nav/settings/__tests__/SettingsNav-test.tsx @@ -27,4 +27,5 @@ it('should work with extensions', () => { ); expect(wrapper).toMatchSnapshot(); + expect(wrapper.find('Dropdown').map(x => x.dive())).toMatchSnapshot(); }); diff --git a/server/sonar-web/src/main/js/app/components/nav/settings/__tests__/__snapshots__/SettingsNav-test.tsx.snap b/server/sonar-web/src/main/js/app/components/nav/settings/__tests__/__snapshots__/SettingsNav-test.tsx.snap index a80573d7d9d..439e362124e 100644 --- a/server/sonar-web/src/main/js/app/components/nav/settings/__tests__/__snapshots__/SettingsNav-test.tsx.snap +++ b/server/sonar-web/src/main/js/app/components/nav/settings/__tests__/__snapshots__/SettingsNav-test.tsx.snap @@ -13,156 +13,9 @@ exports[`should work with extensions 1`] = ` -
  • - - sidebar.project_settings - - - -
      -
    • - - settings.page - -
    • -
    • - - property.category.security.encryption - -
    • -
    • - - custom_metrics.page - -
    • -
    • - - webhooks.page - -
    • -
    • - - Foo - -
    • -
    -
  • -
  • - - sidebar.security - - - -
      -
    • - - users.page - -
    • -
    • - - user_groups.page - -
    • -
    • - - global_permissions.page - -
    • -
    • - - permission_templates - -
    • -
    -
  • -
  • - - sidebar.projects - - - -
      -
    • - - management - -
    • -
    • - - background_tasks.page - -
    • -
    -
  • + + +
  • `; + +exports[`should work with extensions 2`] = ` +Array [ +
  • + + sidebar.project_settings + + +
      +
    • + + settings.page + +
    • +
    • + + property.category.security.encryption + +
    • +
    • + + custom_metrics.page + +
    • +
    • + + webhooks.page + +
    • +
    • + + Foo + +
    • +
    +
  • , +
  • + + sidebar.security + + + +
      +
    • + + users.page + +
    • +
    • + + user_groups.page + +
    • +
    • + + global_permissions.page + +
    • +
    • + + permission_templates + +
    • +
    +
  • , +
  • + + sidebar.projects + + + +
      +
    • + + management + +
    • +
    • + + background_tasks.page + +
    • +
    +
  • , +] +`; diff --git a/server/sonar-web/src/main/js/app/styles/components/modals.css b/server/sonar-web/src/main/js/app/styles/components/modals.css index dbe8a64cb98..21d73c39a99 100644 --- a/server/sonar-web/src/main/js/app/styles/components/modals.css +++ b/server/sonar-web/src/main/js/app/styles/components/modals.css @@ -113,10 +113,6 @@ ul.modal-head-metadata li { padding: 10px; } -.modal-body-select2 { - margin-bottom: 10px; -} - .modal-body .notes { height: auto; } diff --git a/server/sonar-web/src/main/js/app/styles/select2-sonar.css b/server/sonar-web/src/main/js/app/styles/select2-sonar.css deleted file mode 100644 index 5987cc3da25..00000000000 --- a/server/sonar-web/src/main/js/app/styles/select2-sonar.css +++ /dev/null @@ -1,192 +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. - */ -.select2-container { - vertical-align: middle; -} - -.select2-container .select2-choice { - height: var(--controlHeight); - line-height: 22px; - border-color: var(--gray80); - border-radius: 2px; - box-sizing: border-box; - background: #fff; - font-size: var(--smallFontSize); - text-align: left; -} - -.select2-container .select2-choice, -.select2-container .select2-choices { - transition: border-color 0.2s ease; -} - -.select2-container .select2-choice abbr { - top: 4px; -} - -.select2-container .select2-choice div { - width: 19px; - border: none; - border-radius: 0; - background: #fff; -} - -.select2-container .select2-choice div b { - top: 4px; - background-position: 1px -1px; -} - -.select2-dropdown-open .select2-choice div b { - background-position: -17px -1px; -} - -.select2-container .select2-choice span i { - position: relative; - top: 2px; -} - -.select2-container-active .select2-choice, -.select2-container-active .select2-choices { - border-color: var(--blue); - box-shadow: none; -} - -.select2-dropdown-open .select2-choice { - box-shadow: none; -} - -.select2-drop { - z-index: var(--dropdownMenuZIndex); - border-color: var(--gray80); - border-radius: 0; -} - -.select2-drop-active { - border-color: var(--blue); -} - -.select2-dropdown-open.select2-drop-above .select2-choice, -.select2-dropdown-open.select2-drop-above .select2-choices { - border-color: var(--blue); - border-radius: 0; - background: #fff; -} - -.select2-drop.select2-drop-above.select2-drop-active { - border-color: var(--blue); - border-radius: 0; -} - -.select2-drop.select2-drop-above .select2-search input { - margin-top: 0; -} - -.select2-results { - margin: 0; - padding: 5px 0; - border-top: 1px solid var(--gray80); -} - -.select2-results .select2-result-label { - height: 20px; - line-height: 20px; - padding: 0 8px; - color: var(--baseFontColor); - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; -} - -.select2-results .select2-no-results, -.select2-results .select2-searching, -.select2-results .select2-selection-limit, -.select2-more-results.select2-active { - height: 20px; - line-height: 20px; - padding: 0 10px; -} - -.select2-results .select2-highlighted { - background: transparent; - color: var(--baseFontColor); -} - -.select2-results .select2-highlighted .select2-result-label { - background: #e2e2e2; -} - -.select2-search { - padding: 4px; -} - -.select2-search input { - height: 20px; - padding: 0 7px; - border-color: var(--gray80); - background: #fff !important; -} - -.select2-container-multi .select2-choices { - min-height: 19px; - padding-bottom: 1px; - border-color: var(--gray80); - background: #fff; -} - -.select2-container-multi.select2-container-active .select2-choices { - border-color: var(--blue); - box-shadow: none; -} - -.select2-container-multi .select2-choices .select2-search-field input { - height: 16px; - padding: 0 3px; -} - -.select2-container-multi .select2-choices .select2-search-choice { - margin: 1px 1px 0 1px; - padding: 1px 5px 2px 18px; - border-radius: 0; - border-color: var(--gray80); - background: var(--gray94); - box-shadow: none; -} - -.select2-search-choice-close { - top: 2px; -} - -.select2-search-choice-close, -.select2-container .select2-choice abbr, -.select2-container .select2-choice div b { - background-image: url('../images/select2x2.png'); - background-size: 60px 40px; -} - -.select2-search input.select2-active, -.select2-more-results.select2-active, -.select2-container-multi .select2-choices .select2-search-field input.select2-active { - background-image: url('../images/loading.gif'); -} - -.select2-offscreen { - left: 0; - top: -100000px; -} diff --git a/server/sonar-web/src/main/js/app/styles/select2.css b/server/sonar-web/src/main/js/app/styles/select2.css deleted file mode 100644 index f24b98de573..00000000000 --- a/server/sonar-web/src/main/js/app/styles/select2.css +++ /dev/null @@ -1,493 +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. - */ -/* -Version: 3.2 Timestamp: Mon Sep 10 10:38:04 PDT 2012 -*/ -.select2-container { - position: relative; - display: inline-block; - vertical-align: top; -} - -.select2-container, -.select2-drop, -.select2-search, -.select2-search input { - box-sizing: border-box; -} - -.select2-container .select2-choice { - background: #fff linear-gradient(to bottom, #eee 0%, #fff 50%); - border-radius: 4px; - background-clip: padding-box; - border: 1px solid #aaa; - display: block; - overflow: hidden; - white-space: nowrap; - position: relative; - height: 26px; - line-height: 26px; - padding: 0 0 0 8px; - color: var(--baseFontColor); - text-decoration: none; -} - -.select2-container.select2-drop-above .select2-choice { - border-bottom-color: #aaa; - border-radius: 0 0 4px 4px; - background-image: linear-gradient(to bottom, #eeeeee 0%, #ffffff 90%); -} - -.select2-container .select2-choice span { - margin-right: 26px; - display: block; - overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; -} - -.select2-container .select2-choice abbr { - display: block; - position: absolute; - right: 26px; - top: 8px; - width: 12px; - height: 12px; - font-size: 1px; - background: url('../images/select2.png') right top no-repeat; - cursor: pointer; - text-decoration: none; - border: 0; - outline: 0; -} - -.select2-container .select2-choice abbr:hover { - background-position: right -11px; - cursor: pointer; -} - -.select2-drop { - background: #fff; - color: #000; - border: 1px solid #aaa; - border-top: 0; - position: absolute; - top: 100%; - box-shadow: 0 4px 5px rgba(0, 0, 0, 0.15); - z-index: 9999; - width: 100%; - margin-top: -1px; - border-radius: 0 0 4px 4px; -} - -.select2-drop.select2-drop-above { - border-radius: 4px 4px 0 0; - margin-top: 1px; - border-top: 1px solid #aaa; - border-bottom: 0; - box-shadow: 0 -4px 5px rgba(0, 0, 0, 0.15); -} - -.select2-container .select2-choice div { - border-radius: 0 4px 4px 0; - background-clip: padding-box; - background: #ccc linear-gradient(to bottom, #ccc 0%, #eee 60%); - border-left: 1px solid #aaa; - position: absolute; - right: 0; - top: 0; - display: block; - height: 100%; - width: 18px; -} - -.select2-container .select2-choice div b { - background: url('../images/select2.png') no-repeat 0 1px; - display: block; - width: 100%; - height: 100%; -} - -.select2-search { - display: inline-block; - white-space: nowrap; - z-index: 10000; - min-height: 26px; - width: 100%; - margin: 0; - padding-left: 4px; - padding-right: 4px; -} - -.select2-search-hidden { - display: block; - position: absolute; - left: -10000px; -} - -.select2-search input { - background: #fff url('../images/select2.png') no-repeat 100% -22px, - linear-gradient(to bottom, #fff 85%, #eee 99%); - padding: 4px 20px 4px 5px; - outline: 0; - border: 1px solid #aaa; - font-family: sans-serif; - font-size: 1em; - width: 100%; - margin: 0; - height: auto !important; - min-height: 26px; - box-shadow: none; - border-radius: 0; -} - -.select2-drop.select2-drop-above .select2-search input { - margin-top: 4px; -} - -.select2-search input.select2-active { - background: #ffff f url('../images/loading.gif') no-repeat 100%, - linear-gradient(to bottom, #fff 85%, #eee 99%); -} - -.select2-container-active .select2-choice, -.select2-container-active .select2-choices { - box-shadow: 0 0 5px rgba(0, 0, 0, 0.3); - border: 1px solid #5897fb; - outline: none; -} - -.select2-dropdown-open .select2-choice { - border: 1px solid #aaa; - border-bottom-color: transparent; - box-shadow: 0 1px 0 #fff inset; - background: #eee linear-gradient(to bottom, #fff 0%, #eee 50%); - border-bottom-left-radius: 0; - border-bottom-right-radius: 0; -} - -.select2-dropdown-open .select2-choice div { - background: transparent; - border-left: none; -} - -.select2-dropdown-open .select2-choice div b { - background-position: -18px 1px; -} - -/* results */ -.select2-results { - margin: 4px 4px 4px 0; - padding: 0 0 0 4px; - position: relative; - overflow-x: hidden; - overflow-y: auto; - max-height: 200px; -} - -.select2-results ul.select2-result-sub { - margin: 0 0 0 0; -} - -.select2-results ul.select2-result-sub > li .select2-result-label { - padding-left: 20px; -} - -.select2-results ul.select2-result-sub ul.select2-result-sub > li .select2-result-label { - padding-left: 40px; -} - -.select2-results - ul.select2-result-sub - ul.select2-result-sub - ul.select2-result-sub - > li - .select2-result-label { - padding-left: 60px; -} - -.select2-results - ul.select2-result-sub - ul.select2-result-sub - ul.select2-result-sub - ul.select2-result-sub - > li - .select2-result-label { - padding-left: 80px; -} - -.select2-results - ul.select2-result-sub - ul.select2-result-sub - ul.select2-result-sub - ul.select2-result-sub - ul.select2-result-sub - > li - .select2-result-label { - padding-left: 100px; -} - -.select2-results - ul.select2-result-sub - ul.select2-result-sub - ul.select2-result-sub - ul.select2-result-sub - ul.select2-result-sub - ul.select2-result-sub - > li - .select2-result-label { - padding-left: 110px; -} - -.select2-results - ul.select2-result-sub - ul.select2-result-sub - ul.select2-result-sub - ul.select2-result-sub - ul.select2-result-sub - ul.select2-result-sub - ul.select2-result-sub - > li - .select2-result-label { - padding-left: 120px; -} - -.select2-results li { - list-style: none; - display: list-item; -} - -.select2-results li.select2-result-with-children > .select2-result-label { - font-weight: bold; -} - -.select2-results .select2-result-label { - padding: 3px 7px 4px; - margin: 0; - cursor: pointer; -} - -.select2-results .select2-highlighted { - background: #3875d7; - color: #fff; -} - -.select2-results li em { - background: #feffde; - font-style: normal; -} - -.select2-results .select2-highlighted em { - background: transparent; -} - -.select2-results .select2-no-results, -.select2-results .select2-searching, -.select2-results .select2-selection-limit { - background: #f4f4f4; - display: list-item; -} - -.select2-results .select2-disabled { - display: none; -} - -.select2-more-results.select2-active { - background: #f4f4f4 url('../images/loading.gif') no-repeat 100%; -} - -.select2-more-results { - background: #f4f4f4; - display: list-item; -} - -/* disabled styles */ -.select2-container.select2-container-disabled .select2-choice { - background-color: #f4f4f4; - background-image: none; - border: 1px solid #ddd; - cursor: default; -} - -.select2-container.select2-container-disabled .select2-choice div { - background-color: #f4f4f4; - background-image: none; - border-left: 0; -} - -/* multiselect */ -.select2-container-multi .select2-choices { - background: #fff linear-gradient(to bottom, #eee 1%, #fff 15%); - border: 1px solid #aaa; - margin: 0; - padding: 0; - cursor: text; - overflow: hidden; - height: auto !important; - height: 1%; - position: relative; -} - -.select2-container-multi .select2-choices { - min-height: 26px; -} - -.select2-container-multi.select2-container-active .select2-choices { - box-shadow: 0 0 5px rgba(0, 0, 0, 0.3); - border: 1px solid #5897fb; - outline: none; -} - -.select2-container-multi .select2-choices li { - float: left; - list-style: none; -} - -.select2-container-multi .select2-choices .select2-search-field { - white-space: nowrap; - margin: 0; - padding: 0; -} - -.select2-container-multi .select2-choices .select2-search-field input { - color: #666; - background: transparent !important; - font-family: sans-serif; - font-size: 100%; - height: 15px; - padding: 5px; - margin: 1px 0; - outline: 0; - border: 0; - box-shadow: none; -} - -.select2-container-multi .select2-choices .select2-search-field input.select2-active { - background: #fff url('../images/loading.gif') no-repeat 100% !important; -} - -.select2-default { - color: #999 !important; -} - -.select2-container-multi .select2-choices .select2-search-choice { - border-radius: 3px; - background-clip: padding-box; - background: #e4e4e4 linear-gradient(to bottom, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eee 100%); - box-shadow: 0 0 2px #ffffff inset, 0 1px 0 rgba(0, 0, 0, 0.05); - color: #333; - border: 1px solid #aaaaaa; - line-height: var(--baseFontSize); - padding: 3px 5px 3px 18px; - margin: 3px 0 3px 5px; - position: relative; - cursor: default; -} - -.select2-container-multi .select2-choices .select2-search-choice span { - cursor: default; -} - -.select2-container-multi .select2-choices .select2-search-choice-focus { - background: #d4d4d4; -} - -.select2-search-choice-close { - display: block; - position: absolute; - right: 3px; - top: 4px; - width: 12px; - height: 13px; - font-size: 1px; - background: url('../images/select2.png') right top no-repeat; - outline: none; -} - -.select2-container-multi .select2-search-choice-close { - left: 3px; -} - -.select2-container-multi - .select2-choices - .select2-search-choice - .select2-search-choice-close:hover { - background-position: right -11px; -} - -.select2-container-multi - .select2-choices - .select2-search-choice-focus - .select2-search-choice-close { - background-position: right -11px; -} - -/* disabled styles */ -.select2-container-multi.select2-container-disabled .select2-choices { - background-color: #f4f4f4; - background-image: none; - border: 1px solid #ddd; - cursor: default; -} - -.select2-container-multi.select2-container-disabled .select2-choices .select2-search-choice { - background-image: none; - background-color: #f4f4f4; - border: 1px solid #ddd; - padding: 3px 5px 3px 5px; -} - -.select2-container-multi.select2-container-disabled - .select2-choices - .select2-search-choice - .select2-search-choice-close { - display: none; -} - -/* end multiselect */ -.select2-result-selectable .select2-match, -.select2-result-unselectable .select2-result-selectable .select2-match { - text-decoration: underline; -} - -.select2-result-unselectable .select2-match { - text-decoration: none; -} - -.select2-offscreen { - position: absolute; - left: -10000px; -} - -/* Retina-ize icons */ -@media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) { - .select2-search input, - .select2-search-choice-close, - .select2-container .select2-choice abbr, - .select2-container .select2-choice div b { - background-image: url(../images/select2x2.png) !important; - background-repeat: no-repeat !important; - background-size: 60px 40px !important; - } - - .select2-search input { - background-position: 100% -21px !important; - } -} diff --git a/server/sonar-web/src/main/js/app/styles/sonar.css b/server/sonar-web/src/main/js/app/styles/sonar.css index eda7ed09c44..f48136b4f1f 100644 --- a/server/sonar-web/src/main/js/app/styles/sonar.css +++ b/server/sonar-web/src/main/js/app/styles/sonar.css @@ -18,8 +18,6 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ @import './jquery-ui.css'; -@import './select2.css'; -@import './select2-sonar.css'; @import './init/base.css'; @import './init/type.css'; diff --git a/server/sonar-web/src/main/js/apps/coding-rules/styles.css b/server/sonar-web/src/main/js/apps/coding-rules/styles.css index 5647de8b17c..b553e505507 100644 --- a/server/sonar-web/src/main/js/apps/coding-rules/styles.css +++ b/server/sonar-web/src/main/js/apps/coding-rules/styles.css @@ -85,10 +85,6 @@ font-size: var(--smallFontSize); } -.coding-rules-detail-property .select2-search-field { - line-height: 1; -} - .coding-rules-detail-tag + .coding-rules-detail-tag { margin-left: 10px; } diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/MeasureContentContainer.js b/server/sonar-web/src/main/js/apps/component-measures/components/MeasureContentContainer.js index 1998460996e..54d1e975031 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/components/MeasureContentContainer.js +++ b/server/sonar-web/src/main/js/apps/component-measures/components/MeasureContentContainer.js @@ -95,10 +95,6 @@ export default class MeasureContentContainer extends React.PureComponent { const metricKeys = [metric.key]; if (metric.key === 'ncloc') { metricKeys.push('ncloc_language_distribution'); - } else if (metric.key === 'function_complexity') { - metricKeys.push('function_complexity_distribution'); - } else if (metric.key === 'file_complexity') { - metricKeys.push('file_complexity_distribution'); } fetchMeasures(selected || rootComponent.key, metricKeys, branchLike).then( diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/MeasureHeader.js b/server/sonar-web/src/main/js/apps/component-measures/components/MeasureHeader.js index 5c809374492..abff84b4d2c 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/components/MeasureHeader.js +++ b/server/sonar-web/src/main/js/apps/component-measures/components/MeasureHeader.js @@ -21,7 +21,6 @@ import React from 'react'; import { Link } from 'react-router'; import LeakPeriodLegend from './LeakPeriodLegend'; -import ComplexityDistribution from '../../../components/shared/ComplexityDistribution'; import HistoryIcon from '../../../components/icons-components/HistoryIcon'; import IssueTypeIcon from '../../../components/ui/IssueTypeIcon'; import LanguageDistributionContainer from '../../../components/charts/LanguageDistributionContainer'; @@ -94,18 +93,6 @@ export default function MeasureHeader(props /*: Props*/) { /> )} - {secondaryMeasure && - secondaryMeasure.metric.key === 'function_complexity_distribution' && ( -
    - -
    - )} - {secondaryMeasure && - secondaryMeasure.metric.key === 'file_complexity_distribution' && ( -
    - -
    - )} ); } diff --git a/server/sonar-web/src/main/js/apps/component-measures/drilldown/BubbleChart.js b/server/sonar-web/src/main/js/apps/component-measures/drilldown/BubbleChart.js index 74fb563743e..4c6ae9e9b4e 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/drilldown/BubbleChart.js +++ b/server/sonar-web/src/main/js/apps/component-measures/drilldown/BubbleChart.js @@ -84,7 +84,16 @@ export default class BubbleChart extends React.PureComponent { } }); } - return `
    ${inner.join('
    ')}
    `; + return ( +
    + {inner.map((line, index) => ( + + {line} + {index < inner.length - 1 &&
    } +
    + ))} +
    + ); } handleBubbleClick = (component /*: ComponentEnhanced */) => diff --git a/server/sonar-web/src/main/js/apps/issues/components/App.js b/server/sonar-web/src/main/js/apps/issues/components/App.js index 85bf0fa458e..1eb850bc266 100644 --- a/server/sonar-web/src/main/js/apps/issues/components/App.js +++ b/server/sonar-web/src/main/js/apps/issues/components/App.js @@ -23,6 +23,7 @@ import Helmet from 'react-helmet'; import key from 'keymaster'; import { keyBy, union, without } from 'lodash'; import PropTypes from 'prop-types'; +import classNames from 'classnames'; import PageActions from './PageActions'; import MyIssuesFilter from './MyIssuesFilter'; import IssuesList from './IssuesList'; @@ -55,10 +56,12 @@ import { CurrentUser } from '../utils'; */ import handleRequiredAuthentication from '../../../app/utils/handleRequiredAuthentication'; +import Dropdown from '../../../components/controls/Dropdown'; import ListFooter from '../../../components/controls/ListFooter'; import EmptySearch from '../../../components/common/EmptySearch'; import FiltersHeader from '../../../components/common/FiltersHeader'; import ScreenPositionHelper from '../../../components/common/ScreenPositionHelper'; +import { Button } from '../../../components/ui/buttons'; import { isShortLivingBranch, isSameBranchLike, @@ -710,9 +713,7 @@ export default class App extends React.PureComponent { this.setState({ bulkChange: null }); }; - handleBulkChangeClick = (e /*: Event & { target: HTMLElement } */) => { - e.preventDefault(); - e.target.blur(); + handleBulkChangeClick = () => { this.openBulkChange('all'); }; @@ -779,28 +780,32 @@ export default class App extends React.PureComponent { thirdState={thirdState} /> {checked.length > 0 ? ( - + + {({ onToggleClick, open }) => ( + + )} + ) : ( - + )} {bulkChange != null && ( ' + - formatDate(startDate, longFormatterOption); const tooltipEndDate = endDate || Date.now(); - if (!isSameDay(tooltipEndDate, startDate)) { - tooltip += ' – ' + formatDate(tooltipEndDate, longFormatterOption); - } + const tooltip = ( + + {formatMeasure(stats[start], 'SHORT_INT')} +
    + {formatDate(startDate, longFormatterOption)} + {!isSameDay(tooltipEndDate, startDate) && + ` - ${formatDate(tooltipEndDate, longFormatterOption)}`} +
    + ); return { createdAfter: startDate, diff --git a/server/sonar-web/src/main/js/apps/projects/visualizations/Risk.tsx b/server/sonar-web/src/main/js/apps/projects/visualizations/Risk.tsx index 13ae5e5b9b0..d7c004af86c 100644 --- a/server/sonar-web/src/main/js/apps/projects/visualizations/Risk.tsx +++ b/server/sonar-web/src/main/js/apps/projects/visualizations/Risk.tsx @@ -45,7 +45,13 @@ export default class Risk extends React.PureComponent { getMetricTooltip(metric: { key: string; type: string }, value?: number) { const name = translate('metric', metric.key, 'name'); const formattedValue = value != null ? formatMeasure(value, metric.type) : '–'; - return `
    ${name}: ${formattedValue}
    `; + return ( +
    + {name} + {': '} + {formattedValue} +
    + ); } getTooltip( @@ -57,18 +63,26 @@ export default class Risk extends React.PureComponent { color2?: number ) { const fullProjectName = - this.props.displayOrganizations && project.organization - ? `${project.organization.name} / ${project.name}` - : `${project.name}`; - const inner = [ - `
    ${fullProjectName}
    `, - this.getMetricTooltip({ key: COLOR_METRIC_1, type: COLOR_METRIC_TYPE }, color1), - this.getMetricTooltip({ key: COLOR_METRIC_2, type: COLOR_METRIC_TYPE }, color2), - this.getMetricTooltip({ key: Y_METRIC, type: Y_METRIC_TYPE }, y), - this.getMetricTooltip({ key: X_METRIC, type: X_METRIC_TYPE }, x), - this.getMetricTooltip({ key: SIZE_METRIC, type: SIZE_METRIC_TYPE }, size) - ].join(''); - return `
    ${inner}
    `; + this.props.displayOrganizations && project.organization ? ( + <> + {project.organization.name} + {' / '} + {project.name} + + ) : ( + {project.name} + ); + + return ( +
    +
    {fullProjectName}
    + {this.getMetricTooltip({ key: COLOR_METRIC_1, type: COLOR_METRIC_TYPE }, color1)} + {this.getMetricTooltip({ key: COLOR_METRIC_2, type: COLOR_METRIC_TYPE }, color2)} + {this.getMetricTooltip({ key: Y_METRIC, type: Y_METRIC_TYPE }, y)} + {this.getMetricTooltip({ key: X_METRIC, type: X_METRIC_TYPE }, x)} + {this.getMetricTooltip({ key: SIZE_METRIC, type: SIZE_METRIC_TYPE }, size)} +
    + ); } render() { diff --git a/server/sonar-web/src/main/js/apps/projects/visualizations/SimpleBubbleChart.tsx b/server/sonar-web/src/main/js/apps/projects/visualizations/SimpleBubbleChart.tsx index 28af230caf5..9b9d8af06aa 100644 --- a/server/sonar-web/src/main/js/apps/projects/visualizations/SimpleBubbleChart.tsx +++ b/server/sonar-web/src/main/js/apps/projects/visualizations/SimpleBubbleChart.tsx @@ -45,28 +45,37 @@ export default class SimpleBubbleChart extends React.PureComponent { getMetricTooltip(metric: Metric, value?: number) { const name = translate('metric', metric.key, 'name'); const formattedValue = value != null ? formatMeasure(value, metric.type) : '–'; - return `
    ${name}: ${formattedValue}
    `; + return ( +
    + {name} + {': '} + {formattedValue} +
    + ); } getTooltip(project: Project, x?: number, y?: number, size?: number, color?: number) { const fullProjectName = - this.props.displayOrganizations && project.organization - ? `${project.organization.name} / ${project.name}` - : `${project.name}`; - - const inner = [ - `
    ${fullProjectName}
    `, - this.getMetricTooltip(this.props.xMetric, x), - this.getMetricTooltip(this.props.yMetric, y), - this.getMetricTooltip(this.props.sizeMetric, size) - ]; + this.props.displayOrganizations && project.organization ? ( + <> + {project.organization.name} + {' / '} + {project.name} + + ) : ( + {project.name} + ); - if (color) { - // if `color` is defined then `this.props.colorMetric` is defined too - this.getMetricTooltip({ key: this.props.colorMetric!, type: 'RATING' }, color); - } - - return `
    ${inner.join('')}
    `; + return ( +
    +
    {fullProjectName}
    + {this.getMetricTooltip(this.props.xMetric, x)} + {this.getMetricTooltip(this.props.yMetric, y)} + {this.getMetricTooltip(this.props.sizeMetric, size)} + {/* if `color` is defined then `this.props.colorMetric` is defined too */} + {color && this.getMetricTooltip({ key: this.props.colorMetric!, type: 'RATING' }, color)} +
    + ); } render() { diff --git a/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/SimpleBubbleChart-test.tsx b/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/SimpleBubbleChart-test.tsx index 3a19728ba66..e76c73076f0 100644 --- a/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/SimpleBubbleChart-test.tsx +++ b/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/SimpleBubbleChart-test.tsx @@ -24,7 +24,8 @@ import SimpleBubbleChart from '../SimpleBubbleChart'; it('renders', () => { const project1 = { key: 'foo', - measures: { complexity: '17.2', coverage: '53.5', ncloc: '1734' }, + // eslint-disable-next-line camelcase + measures: { complexity: '17.2', coverage: '53.5', ncloc: '1734', security_rating: '2' }, name: 'Foo', tags: [], visibility: 'public' diff --git a/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/__snapshots__/Risk-test.tsx.snap b/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/__snapshots__/Risk-test.tsx.snap index 733d95af43f..ecc8cc1fede 100644 --- a/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/__snapshots__/Risk-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/__snapshots__/Risk-test.tsx.snap @@ -22,7 +22,42 @@ exports[`renders 1`] = ` }, }, "size": 1734, - "tooltip": "
    Foo
    metric.reliability_rating.name: –
    metric.security_rating.name: –
    metric.coverage.name: 53.5%
    metric.sqale_index.name: –
    metric.ncloc.name: 1.7short_number_suffix.k
    ", + "tooltip":
    +
    + + Foo + +
    +
    + metric.reliability_rating.name + : + – +
    +
    + metric.security_rating.name + : + – +
    +
    + metric.coverage.name + : + 53.5% +
    +
    + metric.sqale_index.name + : + – +
    +
    + metric.ncloc.name + : + 1.7short_number_suffix.k +
    +
    , "x": 0, "y": 53.5, }, diff --git a/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/__snapshots__/SimpleBubbleChart-test.tsx.snap b/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/__snapshots__/SimpleBubbleChart-test.tsx.snap index b6e9c87e444..1c661eb1690 100644 --- a/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/__snapshots__/SimpleBubbleChart-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/__snapshots__/SimpleBubbleChart-test.tsx.snap @@ -13,7 +13,7 @@ exports[`renders 1`] = ` items={ Array [ Object { - "color": undefined, + "color": "#b0d513", "key": "foo", "link": Object { "pathname": "/dashboard", @@ -22,7 +22,37 @@ exports[`renders 1`] = ` }, }, "size": 1734, - "tooltip": "
    Foo
    metric.complexity.name: 17
    metric.coverage.name: 53.5%
    metric.ncloc.name: 1,734
    ", + "tooltip":
    +
    + + Foo + +
    +
    + metric.complexity.name + : + 17 +
    +
    + metric.coverage.name + : + 53.5% +
    +
    + metric.ncloc.name + : + 1,734 +
    +
    + metric.security_rating.name + : + B +
    +
    , "x": 17.2, "y": 53.5, }, diff --git a/server/sonar-web/src/main/js/apps/projectsManagement/ProjectRowActions.tsx b/server/sonar-web/src/main/js/apps/projectsManagement/ProjectRowActions.tsx index 2c58557ac4f..4a66fe59a8c 100644 --- a/server/sonar-web/src/main/js/apps/projectsManagement/ProjectRowActions.tsx +++ b/server/sonar-web/src/main/js/apps/projectsManagement/ProjectRowActions.tsx @@ -76,7 +76,7 @@ export default class ProjectRowActions extends React.PureComponent ); }; - handleDropdownClick = () => { + handleDropdownOpen = () => { if (this.state.hasAccess === undefined && !this.state.loading) { this.fetchPermissions(); } @@ -106,7 +106,7 @@ export default class ProjectRowActions extends React.PureComponent const { hasAccess } = this.state; return ( - + {hasAccess === true && ( {translate('edit_permissions')} diff --git a/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/ProjectRowActions-test.tsx b/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/ProjectRowActions-test.tsx index 4c9e74dc1b1..b30e553d91d 100644 --- a/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/ProjectRowActions-test.tsx +++ b/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/ProjectRowActions-test.tsx @@ -44,7 +44,7 @@ it('restores access', async () => { const wrapper = shallowRender(); expect(wrapper).toMatchSnapshot(); - wrapper.prop('onToggleClick')(); + wrapper.prop('onOpen')(); await waitAndUpdate(wrapper); expect(wrapper).toMatchSnapshot(); diff --git a/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/__snapshots__/ProjectRowActions-test.tsx.snap b/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/__snapshots__/ProjectRowActions-test.tsx.snap index b824855ce96..61ea2fee3de 100644 --- a/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/__snapshots__/ProjectRowActions-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/__snapshots__/ProjectRowActions-test.tsx.snap @@ -19,7 +19,7 @@ exports[`applies permission template 1`] = ` exports[`restores access 1`] = ` { - renderFilterToggle() { - const { languages, currentFilter } = this.props; - const currentLanguage = currentFilter && languages.find(l => l.key === currentFilter); - - const label = currentLanguage - ? translateWithParameters('quality_profiles.x_Profiles', currentLanguage.name) - : translate('quality_profiles.all_profiles'); - - return ( - - {label} - - ); +export default function ProfilesListHeader({ currentFilter, languages, organization }: Props) { + if (languages.length < 2) { + return null; } - renderFilterMenu() { - return ( -
      -
    • - - {translate('quality_profiles.all_profiles')} - -
    • - {this.props.languages.map(language => ( -
    • - - {language.name} - -
    • - ))} -
    - ); - } + const currentLanguage = currentFilter && languages.find(l => l.key === currentFilter); - render() { - if (this.props.languages.length < 2) { - return null; - } + // if unknown language, then + if (currentFilter && !currentLanguage) { + return null; + } - const { languages, currentFilter } = this.props; - const currentLanguage = currentFilter && languages.find(l => l.key === currentFilter); + const label = currentLanguage + ? translateWithParameters('quality_profiles.x_Profiles', currentLanguage.name) + : translate('quality_profiles.all_profiles'); - // if unknown language, then - if (currentFilter && !currentLanguage) { - return null; - } + return ( +
    + + {({ onToggleClick, open }) => ( +
    + + {label} + + - return ( -
    -
    - {this.renderFilterToggle()} - {this.renderFilterMenu()} -
    -
    - ); - } +
      +
    • + + {translate('quality_profiles.all_profiles')} + +
    • + {languages.map(language => ( +
    • + + {language.name} + +
    • + ))} +
    +
    + )} +
    +
    + ); } diff --git a/server/sonar-web/src/main/js/apps/system/components/PageActions.tsx b/server/sonar-web/src/main/js/apps/system/components/PageActions.tsx index 7b718fc5ad2..e34bbf7e10e 100644 --- a/server/sonar-web/src/main/js/apps/system/components/PageActions.tsx +++ b/server/sonar-web/src/main/js/apps/system/components/PageActions.tsx @@ -18,9 +18,11 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import * as React from 'react'; +import * as classNames from 'classnames'; import ChangeLogLevelForm from './ChangeLogLevelForm'; import RestartForm from '../../../components/common/RestartForm'; import { getFileNameSuffix } from '../utils'; +import Dropdown from '../../../components/controls/Dropdown'; import { EditButton, Button } from '../../../components/ui/buttons'; import { getBaseUrl } from '../../../helpers/urls'; import { translate } from '../../../helpers/l10n'; @@ -100,51 +102,54 @@ export default class PageActions extends React.PureComponent { /> {this.props.canDownloadLogs && ( -
    - {/* TODO use Dropdown component */} - - -
    + + {({ onToggleClick, open }) => ( +
    + + +
    + )} +
    )} ({ })); it('should render correctly', () => { - expect(getWrapper({ serverId: 'MyServerId' })).toMatchSnapshot(); + const wrapper = getWrapper({ serverId: 'MyServerId' }); + expect(wrapper).toMatchSnapshot(); + expect(wrapper.find('Dropdown').dive()).toMatchSnapshot(); }); it('should render without restart and log download', () => { diff --git a/server/sonar-web/src/main/js/apps/system/components/__tests__/__snapshots__/PageActions-test.tsx.snap b/server/sonar-web/src/main/js/apps/system/components/__tests__/__snapshots__/PageActions-test.tsx.snap index 252b7a95398..3c698c9ba19 100644 --- a/server/sonar-web/src/main/js/apps/system/components/__tests__/__snapshots__/PageActions-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/system/components/__tests__/__snapshots__/PageActions-test.tsx.snap @@ -22,62 +22,7 @@ exports[`should render correctly 1`] = ` onClick={[Function]} /> - + `; +exports[`should render correctly 2`] = ` + +`; + exports[`should render without restart and log download 1`] = `
    - + + {({ onToggleClick, open }) => ( + + )} +
    {isUnitTest && ( diff --git a/server/sonar-web/src/main/js/components/SourceViewer/styles.css b/server/sonar-web/src/main/js/components/SourceViewer/styles.css index 73521086ad3..60a57ab69ba 100644 --- a/server/sonar-web/src/main/js/components/SourceViewer/styles.css +++ b/server/sonar-web/src/main/js/components/SourceViewer/styles.css @@ -336,7 +336,11 @@ float: right; display: block; margin-left: 25px; - padding: 13px 5px; + padding: 8px 5px; +} + +.source-viewer-header-actions svg { + margin-top: 2px; } .source-viewer-header-more-actions { diff --git a/server/sonar-web/src/main/js/components/charts/BubbleChart.d.ts b/server/sonar-web/src/main/js/components/charts/BubbleChart.d.ts index d2f4947fd5f..f428115c4b3 100644 --- a/server/sonar-web/src/main/js/components/charts/BubbleChart.d.ts +++ b/server/sonar-web/src/main/js/components/charts/BubbleChart.d.ts @@ -26,7 +26,7 @@ interface Item { color?: string; key?: string; link?: any; - tooltip?: string; + tooltip?: React.ReactNode; } interface Props { diff --git a/server/sonar-web/src/main/js/components/charts/BubbleChart.js b/server/sonar-web/src/main/js/components/charts/BubbleChart.js index 6245210b1b0..9cb5681debf 100644 --- a/server/sonar-web/src/main/js/components/charts/BubbleChart.js +++ b/server/sonar-web/src/main/js/components/charts/BubbleChart.js @@ -24,7 +24,7 @@ import { min, max } from 'd3-array'; import { scaleLinear } from 'd3-scale'; import { sortBy, uniq } from 'lodash'; import AutoSizer from 'react-virtualized/dist/commonjs/AutoSizer'; -import { TooltipsContainer } from '../mixins/tooltips-mixin'; +import Tooltip from '../controls/Tooltip'; /*:: type Scale = { @@ -42,7 +42,7 @@ export class Bubble extends React.PureComponent { link?: string, onClick: (?string) => void, r: number, - tooltip?: string, + tooltip?: string | React$Element<*>, x: number, y: number }; @@ -55,23 +55,12 @@ export class Bubble extends React.PureComponent { }; render() { - const tooltipAttrs = this.props.tooltip - ? { - 'data-toggle': 'tooltip', - title: this.props.tooltip - } - : {}; - let circle = ( ); @@ -81,9 +70,9 @@ export class Bubble extends React.PureComponent { } return this.props.tooltip ? ( - + {circle} - + ) : ( circle ); @@ -99,7 +88,7 @@ export default class BubbleChart extends React.PureComponent { color?: string, key?: string, link?: string, - tooltip?: string + tooltip?: string | React$Element<*> |}>, sizeRange?: [number, number], displayXGrid: boolean, diff --git a/server/sonar-web/src/main/js/components/charts/__tests__/work-cloud-test.js b/server/sonar-web/src/main/js/components/charts/__tests__/work-cloud-test.js deleted file mode 100644 index 61c92f9baf3..00000000000 --- a/server/sonar-web/src/main/js/components/charts/__tests__/work-cloud-test.js +++ /dev/null @@ -1,32 +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 React from 'react'; -import { shallow } from 'enzyme'; -import { WordCloud, Word } from '../word-cloud'; - -it('should display', () => { - const items = [ - { size: 10, link: '#', text: 'SonarQube :: Server' }, - { size: 30, link: '#', text: 'SonarQube :: Web' }, - { size: 20, link: '#', text: 'SonarQube :: Search' } - ]; - const chart = shallow(); - expect(chart.find(Word).length).toBe(3); -}); diff --git a/server/sonar-web/src/main/js/components/charts/bar-chart.js b/server/sonar-web/src/main/js/components/charts/bar-chart.js index 14bec0cad3e..a312cefc95a 100644 --- a/server/sonar-web/src/main/js/components/charts/bar-chart.js +++ b/server/sonar-web/src/main/js/components/charts/bar-chart.js @@ -22,8 +22,8 @@ import createReactClass from 'create-react-class'; import PropTypes from 'prop-types'; import { max } from 'd3-array'; import { scaleLinear, scaleBand } from 'd3-scale'; -import { ResizeMixin } from './../mixins/resize-mixin'; -import { TooltipsContainer } from './../mixins/tooltips-mixin'; +import Tooltip from '../controls/Tooltip'; +import { ResizeMixin } from '../mixins/resize-mixin'; export const BarChart = createReactClass({ displayName: 'BarChart', @@ -74,24 +74,25 @@ export const BarChart = createReactClass({ const x = Math.round(xScale(point.x) + xScale.bandwidth() / 2); const y = yScale.range()[0]; const d = this.props.data[index]; - const tooltipAtts = {}; - if (d.tooltip) { - tooltipAtts['title'] = d.tooltip; - tooltipAtts['data-toggle'] = 'tooltip'; - } - return ( + const text = ( + style={{ cursor: this.props.onBarClick ? 'pointer' : 'default' }} + x={x} + y={y}> {tick} ); + return d.tooltip ? ( + + {text} + + ) : ( + text + ); }); return {ticks}; }, @@ -105,24 +106,25 @@ export const BarChart = createReactClass({ const x = Math.round(xScale(point.x) + xScale.bandwidth() / 2); const y = yScale(point.y); const d = this.props.data[index]; - const tooltipAtts = {}; - if (d.tooltip) { - tooltipAtts['title'] = d.tooltip; - tooltipAtts['data-toggle'] = 'tooltip'; - } - return ( + const text = ( + x={x} + y={y}> {value} ); + return d.tooltip ? ( + + {text} + + ) : ( + text + ); }); return {ticks}; }, @@ -133,24 +135,25 @@ export const BarChart = createReactClass({ const maxY = yScale.range()[0]; const y = Math.round(yScale(d.y)) - /* minimum bar height */ 1; const height = maxY - y; - const tooltipAtts = {}; - if (d.tooltip) { - tooltipAtts['title'] = d.tooltip; - tooltipAtts['data-toggle'] = 'tooltip'; - } - return ( + const rect = ( ); + return d.tooltip ? ( + + {rect} + + ) : ( + rect + ); }); return {bars}; }, @@ -178,15 +181,13 @@ export const BarChart = createReactClass({ .range([availableHeight, 0]); return ( - - - - {this.renderXTicks(xScale, yScale)} - {this.renderXValues(xScale, yScale)} - {this.renderBars(xScale, yScale)} - - - + + + {this.renderXTicks(xScale, yScale)} + {this.renderXValues(xScale, yScale)} + {this.renderBars(xScale, yScale)} + + ); } }); diff --git a/server/sonar-web/src/main/js/components/charts/donut-chart.js b/server/sonar-web/src/main/js/components/charts/donut-chart.js index eaa0c4ce6fd..08fd9b0bb24 100644 --- a/server/sonar-web/src/main/js/components/charts/donut-chart.js +++ b/server/sonar-web/src/main/js/components/charts/donut-chart.js @@ -22,7 +22,6 @@ import createReactClass from 'create-react-class'; import PropTypes from 'prop-types'; import { arc as d3Arc, pie as d3Pie } from 'd3-shape'; import { ResizeMixin } from './../mixins/resize-mixin'; -import { TooltipsMixin } from './../mixins/tooltips-mixin'; function Sector(props) { const arc = d3Arc() @@ -38,7 +37,7 @@ export const DonutChart = createReactClass({ data: PropTypes.arrayOf(PropTypes.object).isRequired }, - mixins: [ResizeMixin, TooltipsMixin], + mixins: [ResizeMixin], getDefaultProps() { return { thickness: 6, padding: [0, 0, 0, 0] }; @@ -65,17 +64,17 @@ export const DonutChart = createReactClass({ const sectors = pie(this.props.data).map((d, i) => { return ( ); }); return ( - + {sectors} diff --git a/server/sonar-web/src/main/js/components/charts/line-chart.js b/server/sonar-web/src/main/js/components/charts/line-chart.js index bb9a7a88098..af99622368b 100644 --- a/server/sonar-web/src/main/js/components/charts/line-chart.js +++ b/server/sonar-web/src/main/js/components/charts/line-chart.js @@ -24,7 +24,6 @@ import { extent, max } from 'd3-array'; import { scaleLinear } from 'd3-scale'; import { area as d3Area, line as d3Line, curveBasis } from 'd3-shape'; import { ResizeMixin } from './../mixins/resize-mixin'; -import { TooltipsMixin } from './../mixins/tooltips-mixin'; export const LineChart = createReactClass({ displayName: 'LineChart', @@ -41,7 +40,7 @@ export const LineChart = createReactClass({ height: PropTypes.number }, - mixins: [ResizeMixin, TooltipsMixin], + mixins: [ResizeMixin], getDefaultProps() { return { diff --git a/server/sonar-web/src/main/js/components/charts/word-cloud.js b/server/sonar-web/src/main/js/components/charts/word-cloud.js deleted file mode 100644 index 71f9ca10faf..00000000000 --- a/server/sonar-web/src/main/js/components/charts/word-cloud.js +++ /dev/null @@ -1,87 +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 React from 'react'; -import createReactClass from 'create-react-class'; -import PropTypes from 'prop-types'; -import { max } from 'd3-array'; -import { scaleLinear } from 'd3-scale'; -import { sortBy } from 'lodash'; -import { TooltipsMixin } from './../mixins/tooltips-mixin'; - -export function Word(props) { - let tooltipAttrs = {}; - if (props.tooltip) { - tooltipAttrs = { - 'data-toggle': 'tooltip', - title: props.tooltip - }; - } - return ( - - {props.text} - - ); -} - -Word.propTypes = { - size: PropTypes.number.isRequired, - text: PropTypes.string.isRequired, - tooltip: PropTypes.string, - link: PropTypes.string.isRequired -}; - -export const WordCloud = createReactClass({ - displayName: 'WordCloud', - - propTypes: { - items: PropTypes.arrayOf(PropTypes.object).isRequired, - sizeRange: PropTypes.arrayOf(PropTypes.number) - }, - - mixins: [TooltipsMixin], - - getDefaultProps() { - return { - sizeRange: [10, 24] - }; - }, - - render() { - const len = this.props.items.length; - const sortedItems = sortBy(this.props.items, (item, idx) => { - const index = len - idx; - return (index % 2) * (len - index) + index / 2; - }); - - const sizeScale = scaleLinear() - .domain([0, max(this.props.items, d => d.size)]) - .range(this.props.sizeRange); - const words = sortedItems.map((item, index) => ( - - )); - return
    {words}
    ; - } -}); diff --git a/server/sonar-web/src/main/js/components/controls/ActionsDropdown.tsx b/server/sonar-web/src/main/js/components/controls/ActionsDropdown.tsx index a20842524a6..f1173c88e75 100644 --- a/server/sonar-web/src/main/js/components/controls/ActionsDropdown.tsx +++ b/server/sonar-web/src/main/js/components/controls/ActionsDropdown.tsx @@ -21,6 +21,7 @@ import * as React from 'react'; import * as classNames from 'classnames'; import { Link } from 'react-router'; import { LocationDescriptor } from 'history'; +import Dropdown from './Dropdown'; import SettingsIcon from '../icons-components/SettingsIcon'; import { Button } from '../ui/buttons'; @@ -29,31 +30,33 @@ interface Props { children: React.ReactNode; menuClassName?: string; menuPosition?: 'left' | 'right'; - // TODO: replace with `onOpen` & `onClose` - onToggleClick?: () => void; + onOpen?: () => void; small?: boolean; toggleClassName?: string; } export default function ActionsDropdown({ menuPosition = 'right', ...props }: Props) { return ( -
    - -
      - {props.children} -
    -
    + + {({ onToggleClick, open }) => ( +
    + +
      + {props.children} +
    +
    + )} +
    ); } diff --git a/server/sonar-web/src/main/js/components/mixins/tooltips-mixin.js b/server/sonar-web/src/main/js/components/mixins/tooltips-mixin.js deleted file mode 100644 index ae61a8f868f..00000000000 --- a/server/sonar-web/src/main/js/components/mixins/tooltips-mixin.js +++ /dev/null @@ -1,106 +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 React from 'react'; -import ReactDOM from 'react-dom'; - -export const TooltipsMixin = { - componentDidMount() { - this.initTooltips(); - }, - - componentWillUpdate() { - this.hideTooltips(); - }, - - componentDidUpdate() { - this.initTooltips(); - }, - - componentWillUnmount() { - this.destroyTooltips(); - }, - - initTooltips() { - if ($.fn && $.fn.tooltip) { - $('[data-toggle="tooltip"]', ReactDOM.findDOMNode(this)).tooltip({ - container: 'body', - placement: 'bottom', - html: true - }); - } - }, - - hideTooltips() { - if ($.fn && $.fn.tooltip) { - $('[data-toggle="tooltip"]', ReactDOM.findDOMNode(this)).tooltip('hide'); - } - }, - - destroyTooltips() { - if ($.fn && $.fn.tooltip) { - $('[data-toggle="tooltip"]', ReactDOM.findDOMNode(this)).tooltip('destroy'); - } - } -}; - -export class TooltipsContainer extends React.PureComponent { - componentDidMount() { - this.initTooltips(); - } - - componentWillUpdate() { - this.destroyTooltips(); - } - - componentDidUpdate() { - this.initTooltips(); - } - - componentWillUnmount() { - this.destroyTooltips(); - } - - initTooltips = () => { - if ($.fn && $.fn.tooltip) { - const options = Object.assign( - { container: 'body', placement: 'bottom', html: true }, - this.props.options - ); - $('[data-toggle="tooltip"]', ReactDOM.findDOMNode(this)).tooltip(options); - } - }; - - hideTooltips = () => { - if ($.fn && $.fn.tooltip) { - $('[data-toggle="tooltip"]', ReactDOM.findDOMNode(this)).tooltip('hide'); - } - }; - - destroyTooltips = () => { - if ($.fn && $.fn.tooltip) { - $('[data-toggle="tooltip"]', ReactDOM.findDOMNode(this)).tooltip('destroy'); - } - }; - - render() { - return this.props.children; - } -} diff --git a/server/sonar-web/src/main/js/components/shared/ComplexityDistribution.js b/server/sonar-web/src/main/js/components/shared/ComplexityDistribution.js deleted file mode 100644 index 31ad96f632b..00000000000 --- a/server/sonar-web/src/main/js/components/shared/ComplexityDistribution.js +++ /dev/null @@ -1,70 +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 React from 'react'; -import PropTypes from 'prop-types'; -import { BarChart } from '../charts/bar-chart'; -import { formatMeasure } from '../../helpers/measures'; -import { translateWithParameters } from '../../helpers/l10n'; - -const HEIGHT = 80; - -export default class ComplexityDistribution extends React.PureComponent { - static propTypes = { - distribution: PropTypes.string.isRequired, - of: PropTypes.string.isRequired - }; - - renderBarChart = () => { - const data = this.props.distribution.split(';').map((point, index) => { - const tokens = point.split('='); - const y = parseInt(tokens[1], 10); - const value = parseInt(tokens[0], 10); - return { - x: index, - y, - value, - tooltip: translateWithParameters(`overview.complexity_tooltip.${this.props.of}`, y, value) - }; - }); - - const xTicks = data.map(point => point.value); - - const xValues = data.map(point => formatMeasure(point.y, 'INT')); - - return ( - - ); - }; - - render() { - return ( -
    - {this.renderBarChart()} -
    - ); - } -} diff --git a/server/sonar-web/src/main/js/libs/third-party/bootstrap/dropdown.js b/server/sonar-web/src/main/js/libs/third-party/bootstrap/dropdown.js deleted file mode 100644 index 3573bd500ec..00000000000 --- a/server/sonar-web/src/main/js/libs/third-party/bootstrap/dropdown.js +++ /dev/null @@ -1,171 +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. - */ -+function ($) { - 'use strict'; - - // DROPDOWN CLASS DEFINITION - // ========================= - - var backdrop = '.dropdown-backdrop' - var toggle = '[data-toggle="dropdown"]' - var Dropdown = function (element) { - $(element).on('click.bs.dropdown', this.toggle) - } - - Dropdown.VERSION = '3.3.1' - - Dropdown.prototype.toggle = function (e) { - var $this = $(this) - - if ($this.is('.disabled, :disabled')) return - - var $parent = getParent($this) - var isActive = $parent.hasClass('open') - - clearMenus() - - if (!isActive) { - if ('ontouchstart' in document.documentElement) { - // if mobile we use a backdrop because click events don't delegate - $('" , - "
      " , - "
    " , - "
    "].join("")); - return container; - }, - - // single - opening: function () { - this.search.show(); - this.parent.opening.apply(this, arguments); - this.dropdown.removeClass("select2-offscreen"); - }, - - // single - close: function () { - if (!this.opened()) return; - console.log('closing...'); - this.parent.close.apply(this, arguments); - this.dropdown.removeAttr("style").addClass("select2-offscreen").insertAfter(this.selection).show(); - }, - - // single - focus: function () { - this.close(); - this.selection.focus(); - }, - - // single - isFocused: function () { - return this.selection[0] === document.activeElement; - }, - - // single - cancel: function () { - this.parent.cancel.apply(this, arguments); - this.selection.focus(); - }, - - // single - initContainer: function () { - - var selection, - container = this.container, - dropdown = this.dropdown, - clickingInside = false; - - this.selection = selection = container.find(".select2-choice"); - - this.search.bind("keydown", this.bind(function (e) { - if (!this.enabled) return; - - if (e.which === KEY.PAGE_UP || e.which === KEY.PAGE_DOWN) { - // prevent the page from scrolling - killEvent(e); - return; - } - - if (this.opened()) { - switch (e.which) { - case KEY.UP: - case KEY.DOWN: - this.moveHighlight((e.which === KEY.UP) ? -1 : 1); - killEvent(e); - return; - case KEY.TAB: - case KEY.ENTER: - this.selectHighlighted(); - killEvent(e); - return; - case KEY.ESC: - this.cancel(e); - killEvent(e); - return; - } - } else { - - if (e.which === KEY.TAB || KEY.isControl(e) || KEY.isFunctionKey(e) || e.which === KEY.ESC) { - return; - } - - if (this.opts.openOnEnter === false && e.which === KEY.ENTER) { - return; - } - - this.open(); - - if (e.which === KEY.ENTER) { - // do not propagate the event otherwise we open, and propagate enter which closes - return; - } - } - })); - - this.search.bind("focus", this.bind(function() { - this.selection.attr("tabIndex", "-1"); - })); - this.search.bind("blur", this.bind(function() { - if (!this.opened()) this.container.removeClass("select2-container-active"); - window.setTimeout(this.bind(function() { this.selection.attr("tabIndex", this.opts.element.attr("tabIndex")); }), 10); - })); - - selection.bind("mousedown", this.bind(function (e) { - clickingInside = true; - - if (this.opened()) { - this.close(); - this.selection.focus(); - } else if (this.enabled) { - this.open(); - } - - clickingInside = false; - })); - - dropdown.bind("mousedown", this.bind(function() { this.search.focus(); })); - - selection.bind("focus", this.bind(function() { - this.container.addClass("select2-container-active"); - // hide the search so the tab key does not focus on it - this.search.attr("tabIndex", "-1"); - })); - - selection.bind("blur", this.bind(function() { - if (!this.opened()) { - this.container.removeClass("select2-container-active"); - } - window.setTimeout(this.bind(function() { this.search.attr("tabIndex", this.opts.element.attr("tabIndex")); }), 10); - })); - - selection.bind("keydown", this.bind(function(e) { - if (!this.enabled) return; - - if (e.which === KEY.PAGE_UP || e.which === KEY.PAGE_DOWN) { - // prevent the page from scrolling - killEvent(e); - return; - } - - if (e.which === KEY.TAB || KEY.isControl(e) || KEY.isFunctionKey(e) - || e.which === KEY.ESC) { - return; - } - - if (this.opts.openOnEnter === false && e.which === KEY.ENTER) { - return; - } - - if (e.which == KEY.DELETE) { - if (this.opts.allowClear) { - this.clear(); - } - return; - } - - this.open(); - - if (e.which === KEY.ENTER) { - // do not propagate the event otherwise we open, and propagate enter which closes - killEvent(e); - return; - } - - // do not set the search input value for non-alpha-numeric keys - // otherwise pressing down results in a '(' being set in the search field - if (e.which < 48 ) { // '0' == 48 - killEvent(e); - return; - } - - var keyWritten = String.fromCharCode(e.which).toLowerCase(); - - if (e.shiftKey) { - keyWritten = keyWritten.toUpperCase(); - } - - // focus the field before calling val so the cursor ends up after the value instead of before - this.search.focus(); - this.search.val(keyWritten); - - // prevent event propagation so it doesnt replay on the now focussed search field and result in double key entry - killEvent(e); - })); - - selection.delegate("abbr", "mousedown", this.bind(function (e) { - if (!this.enabled) return; - this.clear(); - killEvent(e); - this.close(); - this.triggerChange(); - this.selection.focus(); - })); - - this.setPlaceholder(); - - this.search.bind("focus", this.bind(function() { - this.container.addClass("select2-container-active"); - })); - }, - - // single - clear: function() { - this.opts.element.val(""); - this.selection.find("span").empty(); - this.selection.removeData("select2-data"); - this.setPlaceholder(); - }, - - /** - * Sets selection based on source element's value - */ - // single - initSelection: function () { - var selected; - if (this.opts.element.val() === "") { - this.close(); - this.setPlaceholder(); - } else { - var self = this; - this.opts.initSelection.call(null, this.opts.element, function(selected){ - if (selected !== undefined && selected !== null) { - self.updateSelection(selected); - self.close(); - self.setPlaceholder(); - } - }); - } - }, - - // single - prepareOpts: function () { - var opts = this.parent.prepareOpts.apply(this, arguments); - - if (opts.element.get(0).tagName.toLowerCase() === "select") { - // install the selection initializer - opts.initSelection = function (element, callback) { - var selected = element.find(":selected"); - // a single select box always has a value, no need to null check 'selected' - if ($.isFunction(callback)) - callback({id: selected.attr("value"), text: selected.text()}); - }; - } - - return opts; - }, - - // single - setPlaceholder: function () { - var placeholder = this.getPlaceholder(); - - if (this.opts.element.val() === "" && placeholder !== undefined) { - - // check for a first blank option if attached to a select - if (this.select && this.select.find("option:first").text() !== "") return; - - this.selection.find("span").html(this.opts.escapeMarkup(placeholder)); - - this.selection.addClass("select2-default"); - - this.selection.find("abbr").hide(); - } - }, - - // single - postprocessResults: function (data, initial) { - var selected = 0, self = this, showSearchInput = true; - - // find the selected element in the result list - - this.results.find(".select2-result-selectable").each2(function (i, elm) { - if (equal(self.id(elm.data("select2-data")), self.opts.element.val())) { - selected = i; - return false; - } - }); - - // and highlight it - - this.highlight(selected); - - // hide the search box if this is the first we got the results and there are a few of them - - if (initial === true) { - showSearchInput = this.showSearchInput = countResults(data.results) >= this.opts.minimumResultsForSearch; - this.dropdown.find(".select2-search")[showSearchInput ? "removeClass" : "addClass"]("select2-search-hidden"); - - //add "select2-with-searchbox" to the container if search box is shown - $(this.dropdown, this.container)[showSearchInput ? "addClass" : "removeClass"]("select2-with-searchbox"); - } - - }, - - // single - onSelect: function (data) { - var old = this.opts.element.val(); - - this.opts.element.val(this.id(data)); - this.updateSelection(data); - this.close(); - this.selection.focus(); - - if (!equal(old, this.id(data))) { this.triggerChange(); } - }, - - // single - updateSelection: function (data) { - - var container=this.selection.find("span"), formatted; - - this.selection.data("select2-data", data); - - container.empty(); - formatted=this.opts.formatSelection(data, container); - if (formatted !== undefined) { - container.append(this.opts.escapeMarkup(formatted)); - } - - this.selection.removeClass("select2-default"); - - if (this.opts.allowClear && this.getPlaceholder() !== undefined) { - this.selection.find("abbr").show(); - } - }, - - // single - val: function () { - var val, data = null, self = this; - - if (arguments.length === 0) { - return this.opts.element.val(); - } - - val = arguments[0]; - - if (this.select) { - this.select - .val(val) - .find(":selected").each2(function (i, elm) { - data = {id: elm.attr("value"), text: elm.text()}; - return false; - }); - this.updateSelection(data); - this.setPlaceholder(); - } else { - if (this.opts.initSelection === undefined) { - throw new Error("cannot call val() if initSelection() is not defined"); - } - // val is an id. !val is true for [undefined,null,''] - if (!val) { - this.clear(); - return; - } - this.opts.element.val(val); - this.opts.initSelection(this.opts.element, function(data){ - self.opts.element.val(!data ? "" : self.id(data)); - self.updateSelection(data); - self.setPlaceholder(); - }); - } - }, - - // single - clearSearch: function () { - this.search.val(""); - }, - - // single - data: function(value) { - var data; - - if (arguments.length === 0) { - data = this.selection.data("select2-data"); - if (data == undefined) data = null; - return data; - } else { - if (!value || value === "") { - this.clear(); - } else { - this.opts.element.val(!value ? "" : this.id(value)); - this.updateSelection(value); - } - } - } - }); - - MultiSelect2 = clazz(AbstractSelect2, { - - // multi - createContainer: function () { - var container = $("
    ", { - "class": "select2-container select2-container-multi" - }).html([ - "
      ", - //"
    • California
    • " , - "
    • " , - " " , - "
    • " , - "
    " , - ""].join("")); - return container; - }, - - // multi - prepareOpts: function () { - var opts = this.parent.prepareOpts.apply(this, arguments); - - // TODO validate placeholder is a string if specified - - if (opts.element.get(0).tagName.toLowerCase() === "select") { - // install sthe selection initializer - opts.initSelection = function (element,callback) { - - var data = []; - element.find(":selected").each2(function (i, elm) { - data.push({id: elm.attr("value"), text: elm.text()}); - }); - - if ($.isFunction(callback)) - callback(data); - }; - } - - return opts; - }, - - // multi - initContainer: function () { - - var selector = ".select2-choices", selection; - - this.searchContainer = this.container.find(".select2-search-field"); - this.selection = selection = this.container.find(selector); - - this.search.bind("keydown", this.bind(function (e) { - if (!this.enabled) return; - - if (e.which === KEY.BACKSPACE && this.search.val() === "") { - this.close(); - - var choices, - selected = selection.find(".select2-search-choice-focus"); - if (selected.length > 0) { - this.unselect(selected.first()); - this.search.width(10); - killEvent(e); - return; - } - - choices = selection.find(".select2-search-choice"); - if (choices.length > 0) { - choices.last().addClass("select2-search-choice-focus"); - } - } else { - selection.find(".select2-search-choice-focus").removeClass("select2-search-choice-focus"); - } - - if (this.opened()) { - switch (e.which) { - case KEY.UP: - case KEY.DOWN: - this.moveHighlight((e.which === KEY.UP) ? -1 : 1); - killEvent(e); - return; - case KEY.ENTER: - case KEY.TAB: - this.selectHighlighted(); - killEvent(e); - return; - case KEY.ESC: - this.cancel(e); - killEvent(e); - return; - } - } - - if (e.which === KEY.TAB || KEY.isControl(e) || KEY.isFunctionKey(e) - || e.which === KEY.BACKSPACE || e.which === KEY.ESC) { - return; - } - - if (this.opts.openOnEnter === false && e.which === KEY.ENTER) { - return; - } - - this.open(); - - if (e.which === KEY.PAGE_UP || e.which === KEY.PAGE_DOWN) { - // prevent the page from scrolling - killEvent(e); - } - })); - - this.search.bind("keyup", this.bind(this.resizeSearch)); - - this.search.bind("blur", this.bind(function(e) { - this.container.removeClass("select2-container-active"); - this.search.removeClass("select2-focused"); - this.clearSearch(); - e.stopImmediatePropagation(); - })); - - this.container.delegate(selector, "mousedown", this.bind(function (e) { - if (!this.enabled) return; - if ($(e.target).closest(".select2-search-choice").length > 0) { - // clicked inside a select2 search choice, do not open - return; - } - this.clearPlaceholder(); - this.open(); - this.focusSearch(); - e.preventDefault(); - })); - - this.container.delegate(selector, "focus", this.bind(function () { - if (!this.enabled) return; - this.container.addClass("select2-container-active"); - this.dropdown.addClass("select2-drop-active"); - this.clearPlaceholder(); - })); - - // set the placeholder if necessary - this.clearSearch(); - }, - - // multi - enable: function() { - if (this.enabled) return; - - this.parent.enable.apply(this, arguments); - - this.search.removeAttr("disabled"); - }, - - // multi - disable: function() { - if (!this.enabled) return; - - this.parent.disable.apply(this, arguments); - - this.search.attr("disabled", true); - }, - - // multi - initSelection: function () { - var data; - if (this.opts.element.val() === "") { - this.updateSelection([]); - this.close(); - // set the placeholder if necessary - this.clearSearch(); - } - if (this.select || this.opts.element.val() !== "") { - var self = this; - this.opts.initSelection.call(null, this.opts.element, function(data){ - if (data !== undefined && data !== null) { - self.updateSelection(data); - self.close(); - // set the placeholder if necessary - self.clearSearch(); - } - }); - } - }, - - // multi - clearSearch: function () { - var placeholder = this.getPlaceholder(); - - if (placeholder !== undefined && this.getVal().length === 0 && this.search.hasClass("select2-focused") === false) { - this.search.val(placeholder).addClass("select2-default"); - // stretch the search box to full width of the container so as much of the placeholder is visible as possible - this.resizeSearch(); - } else { - // we set this to " " instead of "" and later clear it on focus() because there is a firefox bug - // that does not properly render the caret when the field starts out blank - this.search.val(" ").width(10); - } - }, - - // multi - clearPlaceholder: function () { - if (this.search.hasClass("select2-default")) { - this.search.val("").removeClass("select2-default"); - } else { - // work around for the space character we set to avoid firefox caret bug - if (this.search.val() === " ") this.search.val(""); - } - }, - - // multi - opening: function () { - this.parent.opening.apply(this, arguments); - - this.clearPlaceholder(); - this.resizeSearch(); - this.focusSearch(); - }, - - // multi - close: function () { - if (!this.opened()) return; - this.parent.close.apply(this, arguments); - }, - - // multi - focus: function () { - this.close(); - this.search.focus(); - }, - - // multi - isFocused: function () { - return this.search.hasClass("select2-focused"); - }, - - // multi - updateSelection: function (data) { - var ids = [], filtered = [], self = this; - - // filter out duplicates - $(data).each(function () { - if (indexOf(self.id(this), ids) < 0) { - ids.push(self.id(this)); - filtered.push(this); - } - }); - data = filtered; - - this.selection.find(".select2-search-choice").remove(); - $(data).each(function () { - self.addSelectedChoice(this); - }); - self.postprocessResults(); - }, - - tokenize: function() { - var input = this.search.val(); - input = this.opts.tokenizer(input, this.data(), this.bind(this.onSelect), this.opts); - if (input != null && input != undefined) { - this.search.val(input); - if (input.length > 0) { - this.open(); - } - } - - }, - - // multi - onSelect: function (data) { - this.addSelectedChoice(data); - if (this.select) { this.postprocessResults(); } - - if (this.opts.closeOnSelect) { - this.close(); - this.search.width(10); - } else { - if (this.countSelectableResults()>0) { - this.search.width(10); - this.resizeSearch(); - this.positionDropdown(); - } else { - // if nothing left to select close - this.close(); - } - } - - // since its not possible to select an element that has already been - // added we do not need to check if this is a new element before firing change - this.triggerChange({ added: data }); - - this.focusSearch(); - }, - - // multi - cancel: function () { - this.close(); - this.focusSearch(); - }, - - // multi - addSelectedChoice: function (data) { - var choice=$( - "
  • " + - "
    " + - " " + - "
  • "), - id = this.id(data), - val = this.getVal(), - formatted; - - formatted=this.opts.formatSelection(data, choice); - choice.find("div").replaceWith("
    "+this.opts.escapeMarkup(formatted)+"
    "); - choice.find(".select2-search-choice-close") - .bind("mousedown", killEvent) - .bind("click dblclick", this.bind(function (e) { - if (!this.enabled) return; - - $(e.target).closest(".select2-search-choice").fadeOut('fast', this.bind(function(){ - this.unselect($(e.target)); - this.selection.find(".select2-search-choice-focus").removeClass("select2-search-choice-focus"); - this.close(); - this.focusSearch(); - })).dequeue(); - killEvent(e); - })).bind("focus", this.bind(function () { - if (!this.enabled) return; - this.container.addClass("select2-container-active"); - this.dropdown.addClass("select2-drop-active"); - })); - - choice.data("select2-data", data); - choice.insertBefore(this.searchContainer); - - val.push(id); - this.setVal(val); - }, - - // multi - unselect: function (selected) { - var val = this.getVal(), - data, - index; - - selected = selected.closest(".select2-search-choice"); - - if (selected.length === 0) { - throw "Invalid argument: " + selected + ". Must be .select2-search-choice"; - } - - data = selected.data("select2-data"); - - index = indexOf(this.id(data), val); - - if (index >= 0) { - val.splice(index, 1); - this.setVal(val); - if (this.select) this.postprocessResults(); - } - selected.remove(); - this.triggerChange({ removed: data }); - }, - - // multi - postprocessResults: function () { - var val = this.getVal(), - choices = this.results.find(".select2-result-selectable"), - compound = this.results.find(".select2-result-with-children"), - self = this; - - choices.each2(function (i, choice) { - var id = self.id(choice.data("select2-data")); - if (indexOf(id, val) >= 0) { - choice.addClass("select2-disabled").removeClass("select2-result-selectable"); - } else { - choice.removeClass("select2-disabled").addClass("select2-result-selectable"); - } - }); - - compound.each2(function(i, e) { - if (e.find(".select2-result-selectable").length==0) { - e.addClass("select2-disabled"); - } else { - e.removeClass("select2-disabled"); - } - }); - - choices.each2(function (i, choice) { - if (!choice.hasClass("select2-disabled") && choice.hasClass("select2-result-selectable")) { - self.highlight(0); - return false; - } - }); - - }, - - // multi - resizeSearch: function () { - - var minimumWidth, left, maxWidth, containerLeft, searchWidth, - sideBorderPadding = getSideBorderPadding(this.search); - - minimumWidth = measureTextWidth(this.search) + 10; - - left = this.search.offset().left; - - maxWidth = this.selection.width(); - containerLeft = this.selection.offset().left; - - searchWidth = maxWidth - (left - containerLeft) - sideBorderPadding; - if (searchWidth < minimumWidth) { - searchWidth = maxWidth - sideBorderPadding; - } - - if (searchWidth < 40) { - searchWidth = maxWidth - sideBorderPadding; - } - this.search.width(searchWidth); - }, - - // multi - getVal: function () { - var val; - if (this.select) { - val = this.select.val(); - return val === null ? [] : val; - } else { - val = this.opts.element.val(); - return splitVal(val, this.opts.separator); - } - }, - - // multi - setVal: function (val) { - var unique; - if (this.select) { - this.select.val(val); - } else { - unique = []; - // filter out duplicates - $(val).each(function () { - if (indexOf(this, unique) < 0) unique.push(this); - }); - this.opts.element.val(unique.length === 0 ? "" : unique.join(this.opts.separator)); - } - }, - - // multi - val: function () { - var val, data = [], self=this; - - if (arguments.length === 0) { - return this.getVal(); - } - - val = arguments[0]; - - if (!val) { - this.opts.element.val(""); - this.updateSelection([]); - this.clearSearch(); - return; - } - - // val is a list of ids - this.setVal(val); - - if (this.select) { - this.select.find(":selected").each(function () { - data.push({id: $(this).attr("value"), text: $(this).text()}); - }); - this.updateSelection(data); - } else { - if (this.opts.initSelection === undefined) { - throw new Error("val() cannot be called if initSelection() is not defined") - } - - this.opts.initSelection(this.opts.element, function(data){ - var ids=$(data).map(self.id); - self.setVal(ids); - self.updateSelection(data); - self.clearSearch(); - }); - } - this.clearSearch(); - }, - - // multi - onSortStart: function() { - if (this.select) { - throw new Error("Sorting of elements is not supported when attached to instead."); - } - - // collapse search field into 0 width so its container can be collapsed as well - this.search.width(0); - // hide the container - this.searchContainer.hide(); - }, - - // multi - onSortEnd:function() { - - var val=[], self=this; - - // show search and move it to the end of the list - this.searchContainer.show(); - // make sure the search container is the last item in the list - this.searchContainer.appendTo(this.searchContainer.parent()); - // since we collapsed the width in dragStarted, we resize it here - this.resizeSearch(); - - // update selection - - this.selection.find(".select2-search-choice").each(function() { - val.push(self.opts.id($(this).data("select2-data"))); - }); - this.setVal(val); - this.triggerChange(); - }, - - // multi - data: function(values) { - var self=this, ids; - if (arguments.length === 0) { - return this.selection - .find(".select2-search-choice") - .map(function() { return $(this).data("select2-data"); }) - .get(); - } else { - if (!values) { values = []; } - ids = $.map(values, function(e) { return self.opts.id(e)}); - this.setVal(ids); - this.updateSelection(values); - this.clearSearch(); - } - } - }); - - $.fn.select2 = function () { - - var args = Array.prototype.slice.call(arguments, 0), - opts, - select2, - value, multiple, allowedMethods = ["val", "destroy", "opened", "open", "close", "focus", "isFocused", "container", "onSortStart", "onSortEnd", "enable", "disable", "positionDropdown", "data"]; - - this.each(function () { - if (args.length === 0 || typeof(args[0]) === "object") { - opts = args.length === 0 ? {} : $.extend({}, args[0]); - opts.element = $(this); - - if (opts.element.get(0).tagName.toLowerCase() === "select") { - multiple = opts.element.attr("multiple"); - } else { - multiple = opts.multiple || false; - if ("tags" in opts) {opts.multiple = multiple = true;} - } - - select2 = multiple ? new MultiSelect2() : new SingleSelect2(); - select2.init(opts); - } else if (typeof(args[0]) === "string") { - - if (indexOf(args[0], allowedMethods) < 0) { - throw "Unknown method: " + args[0]; - } - - value = undefined; - select2 = $(this).data("select2"); - if (select2 === undefined) return; - if (args[0] === "container") { - value=select2.container; - } else { - value = select2[args[0]].apply(select2, args.slice(1)); - } - if (value !== undefined) {return false;} - } else { - throw "Invalid arguments to select2 plugin: " + args; - } - }); - return (value === undefined) ? this : value; - }; - - // plugin defaults, accessible to users - $.fn.select2.defaults = { - width: "copy", - closeOnSelect: true, - openOnEnter: true, - containerCss: {}, - dropdownCss: {}, - containerCssClass: "", - dropdownCssClass: "", - formatResult: function(result, container, query) { - var markup=[]; - markMatch(result.text, query.term, markup); - return markup.join(""); - }, - formatSelection: function (data, container) { - return data ? data.text : undefined; - }, - formatResultCssClass: function(data) {return undefined;}, - formatNoMatches: function () { return "No matches found"; }, - formatInputTooShort: function (input, min) { return "Please enter " + (min - input.length) + " more characters"; }, - formatSelectionTooBig: function (limit) { return "You can only select " + limit + " item" + (limit == 1 ? "" : "s"); }, - formatLoadMore: function (pageNumber) { return "Loading more results..."; }, - formatSearching: function () { return "Searching..."; }, - minimumResultsForSearch: 0, - minimumInputLength: 0, - maximumSelectionSize: 0, - id: function (e) { return e.id; }, - matcher: function(term, text) { - return text.toUpperCase().indexOf(term.toUpperCase()) >= 0; - }, - separator: ",", - tokenSeparators: [], - tokenizer: defaultTokenizer, - escapeMarkup: function (markup) { - if (markup && typeof(markup) === "string") { - return markup.replace(/&/g, "&"); - } - return markup; - }, - blurOnChange: false - }; - - // exports - window.Select2 = { - query: { - ajax: ajax, - local: local, - tags: tags - }, util: { - debounce: debounce, - markMatch: markMatch - }, "class": { - "abstract": AbstractSelect2, - "single": SingleSelect2, - "multi": MultiSelect2 - } - }; - -}(jQuery)); -- 2.39.5