From 20a70acea9fad007677c6e5ba28b99a6a7b532cc Mon Sep 17 00:00:00 2001 From: Stas Vilchik Date: Tue, 31 Oct 2017 16:26:09 +0100 Subject: [PATCH] SONAR-10022 Grouping actions must be consistent --- .../js/app/styles/components/dropdowns.css | 5 + .../apps/background-tasks/components/Stats.js | 8 +- .../components/TaskActions.tsx | 93 +++--- .../background-tasks/components/Workers.tsx | 8 +- .../components/__tests__/Workers-test.tsx | 2 +- .../__snapshots__/TaskActions-test.tsx.snap | 258 +++++----------- .../__snapshots__/Workers-test.tsx.snap | 48 ++- .../templates/custom-measures-list-item.hbs | 16 +- .../groups/templates/groups-list-item.hbs | 16 +- .../metrics/templates/metrics-list-item.hbs | 16 +- .../components/MembersListItem.js | 43 ++- .../MembersListItem-test.js.snap | 198 +++++-------- .../forms/ManageMemberGroupsForm.js | 32 +- .../components/forms/RemoveMemberForm.js | 8 +- .../__tests__/ManageMemberGroupsForm-test.js | 2 +- .../forms/__tests__/RemoveMemberForm-test.js | 2 +- .../ManageMemberGroupsForm-test.js.snap | 11 +- .../RemoveMemberForm-test.js.snap | 13 +- .../components/ActionsCell.js | 99 +++---- .../components/ProjectActivityAnalysis.js | 65 ++--- .../components/forms/AddEventForm.js | 13 +- .../components/forms/RemoveAnalysisForm.js | 16 +- .../projectBranches/components/BranchRow.tsx | 128 ++++---- .../__snapshots__/BranchRow-test.tsx.snap | 79 ++--- .../js/apps/projectsManagement/Header.tsx | 8 +- .../js/apps/projectsManagement/ProjectRow.tsx | 32 +- .../__snapshots__/Header-test.tsx.snap | 8 +- .../__snapshots__/ProjectRow-test.tsx.snap | 118 +++----- .../components/ProfileActions.tsx | 154 +++++----- .../ProfileActions-test.tsx.snap | 275 ++++++++---------- .../details/ProfileHeader.tsx | 18 +- .../quality-profiles/home/ProfilesListRow.tsx | 19 +- .../components/inputs/MultiValueInput.js | 3 +- .../components/inputs/PropertySetInput.js | 3 +- .../tutorials/onboarding/ProjectKeyStep.js | 3 +- .../__tests__/ProjectKeyStep-test.js | 8 + .../__snapshots__/ProjectKeyStep-test.js.snap | 8 +- .../apps/users/templates/users-deactivate.hbs | 2 +- .../apps/users/templates/users-list-item.hbs | 25 +- .../components/controls/ActionsDropdown.tsx | 108 +++++++ .../issue/components/IssueCommentLine.js | 16 +- .../IssueCommentLine-test.js.snap | 24 +- .../js/helpers/handlebars/settingsIcon.js | 33 +++ .../should_restore.html | 10 - .../QualityProfilesUiTest/should_restore.html | 10 - .../create-and-delete-user.html | 5 + .../admin_should_change_its_own_password.html | 5 + 47 files changed, 965 insertions(+), 1109 deletions(-) create mode 100644 server/sonar-web/src/main/js/components/controls/ActionsDropdown.tsx create mode 100644 server/sonar-web/src/main/js/helpers/handlebars/settingsIcon.js diff --git a/server/sonar-web/src/main/js/app/styles/components/dropdowns.css b/server/sonar-web/src/main/js/app/styles/components/dropdowns.css index e11684d5660..6de1a659782 100644 --- a/server/sonar-web/src/main/js/app/styles/components/dropdowns.css +++ b/server/sonar-web/src/main/js/app/styles/components/dropdowns.css @@ -65,6 +65,11 @@ transition: none; } +.dropdown-menu > li > a.text-danger, +.dropdown-menu > li > a.text-danger:hover { + color: var(--red) !important; +} + .dropdown-menu .divider { height: 1px; margin: 6px 0; diff --git a/server/sonar-web/src/main/js/apps/background-tasks/components/Stats.js b/server/sonar-web/src/main/js/apps/background-tasks/components/Stats.js index 22c58b2902a..8c239e9d812 100644 --- a/server/sonar-web/src/main/js/apps/background-tasks/components/Stats.js +++ b/server/sonar-web/src/main/js/apps/background-tasks/components/Stats.js @@ -20,6 +20,7 @@ /* @flow */ import React from 'react'; import Tooltip from '../../../components/controls/Tooltip'; +import DeleteIcon from '../../../components/icons-components/DeleteIcon'; import { translate } from '../../../helpers/l10n'; /*:: @@ -65,10 +66,11 @@ export default class Stats extends React.PureComponent { {this.props.isSystemAdmin && ( + onClick={this.handleCancelAllPending}> + + )} diff --git a/server/sonar-web/src/main/js/apps/background-tasks/components/TaskActions.tsx b/server/sonar-web/src/main/js/apps/background-tasks/components/TaskActions.tsx index d6510541fc2..d944ebba4ef 100644 --- a/server/sonar-web/src/main/js/apps/background-tasks/components/TaskActions.tsx +++ b/server/sonar-web/src/main/js/apps/background-tasks/components/TaskActions.tsx @@ -23,6 +23,7 @@ import Stacktrace from './Stacktrace'; import { STATUSES } from './../constants'; import { translate, translateWithParameters } from '../../../helpers/l10n'; import { Task } from '../types'; +import ActionsDropdown, { ActionsDropdownItem } from '../../../components/controls/ActionsDropdown'; interface Props { component?: {}; @@ -42,25 +43,21 @@ export default class TaskActions extends React.PureComponent { stacktraceOpen: false }; - handleFilterClick = (event: React.SyntheticEvent) => { - event.preventDefault(); + handleFilterClick = () => { this.props.onFilterTask(this.props.task); }; - handleCancelClick = (event: React.SyntheticEvent) => { - event.preventDefault(); + handleCancelClick = () => { this.props.onCancelTask(this.props.task); }; - handleShowScannerContextClick = (event: React.SyntheticEvent) => { - event.preventDefault(); + handleShowScannerContextClick = () => { this.setState({ scannerContextOpen: true }); }; closeScannerContext = () => this.setState({ scannerContextOpen: false }); - handleShowStacktraceClick = (event: React.SyntheticEvent) => { - event.preventDefault(); + handleShowStacktraceClick = () => { this.setState({ stacktraceOpen: true }); }; @@ -80,55 +77,39 @@ export default class TaskActions extends React.PureComponent { return ( -
- - -
+ {canCancel && ( + + {translate('background_tasks.cancel_task')} + + )} + {task.hasScannerContext && ( + + {translate('background_tasks.show_scanner_context')} + + )} + {canShowStacktrace && ( + + {translate('background_tasks.show_stacktrace')} + + )} + {this.state.scannerContextOpen && ( diff --git a/server/sonar-web/src/main/js/apps/background-tasks/components/Workers.tsx b/server/sonar-web/src/main/js/apps/background-tasks/components/Workers.tsx index ee8225ac722..a5eef35810f 100644 --- a/server/sonar-web/src/main/js/apps/background-tasks/components/Workers.tsx +++ b/server/sonar-web/src/main/js/apps/background-tasks/components/Workers.tsx @@ -26,6 +26,7 @@ import { getWorkers } from '../../../api/ce'; import { translate } from '../../../helpers/l10n'; import HelpIcon from '../../../components/icons-components/HelpIcon'; import BubblePopupHelper from '../../../components/common/BubblePopupHelper'; +import EditIcon from '../../../components/icons-components/EditIcon'; interface State { canSetWorkerCount: boolean; @@ -121,7 +122,12 @@ export default class Workers extends React.PureComponent<{}, State> { {!loading && canSetWorkerCount && ( - + + + )} diff --git a/server/sonar-web/src/main/js/apps/background-tasks/components/__tests__/Workers-test.tsx b/server/sonar-web/src/main/js/apps/background-tasks/components/__tests__/Workers-test.tsx index 953e79d282a..cc07d0d4510 100644 --- a/server/sonar-web/src/main/js/apps/background-tasks/components/__tests__/Workers-test.tsx +++ b/server/sonar-web/src/main/js/apps/background-tasks/components/__tests__/Workers-test.tsx @@ -50,7 +50,7 @@ it('opens form', () => { }); expect(wrapper).toMatchSnapshot(); - click(wrapper.find('.icon-edit')); + click(wrapper.find('.js-edit')); expect(wrapper).toMatchSnapshot(); }); diff --git a/server/sonar-web/src/main/js/apps/background-tasks/components/__tests__/__snapshots__/TaskActions-test.tsx.snap b/server/sonar-web/src/main/js/apps/background-tasks/components/__tests__/__snapshots__/TaskActions-test.tsx.snap index aabe0783b3d..84a4e16e597 100644 --- a/server/sonar-web/src/main/js/apps/background-tasks/components/__tests__/__snapshots__/TaskActions-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/background-tasks/components/__tests__/__snapshots__/TaskActions-test.tsx.snap @@ -4,46 +4,23 @@ exports[`renders 1`] = ` - + background_tasks.cancel_task + + `; @@ -51,34 +28,16 @@ exports[`renders 2`] = ` - + background_tasks.filter_by_component_x.foo + + `; @@ -86,58 +45,29 @@ exports[`renders 3`] = ` - + background_tasks.show_scanner_context + + `; @@ -145,58 +75,29 @@ exports[`renders 4`] = ` - + background_tasks.cancel_task + + + background_tasks.show_stacktrace + + `; @@ -204,34 +105,17 @@ exports[`renders 5`] = ` - + background_tasks.cancel_task + + `; diff --git a/server/sonar-web/src/main/js/apps/background-tasks/components/__tests__/__snapshots__/Workers-test.tsx.snap b/server/sonar-web/src/main/js/apps/background-tasks/components/__tests__/__snapshots__/Workers-test.tsx.snap index d9d6b6fe2f2..73abbbf04b4 100644 --- a/server/sonar-web/src/main/js/apps/background-tasks/components/__tests__/__snapshots__/Workers-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/background-tasks/components/__tests__/__snapshots__/Workers-test.tsx.snap @@ -13,10 +13,14 @@ exports[`opens form 1`] = ` placement="bottom" > + > + + `; @@ -34,10 +38,14 @@ exports[`opens form 2`] = ` placement="bottom" > + > + + + > + + `; @@ -97,10 +109,14 @@ exports[`renders 3`] = ` placement="bottom" > + > + + `; @@ -157,10 +173,14 @@ exports[`updates worker count 1`] = ` placement="bottom" > + > + + + > + + `; diff --git a/server/sonar-web/src/main/js/apps/custom-measures/templates/custom-measures-list-item.hbs b/server/sonar-web/src/main/js/apps/custom-measures/templates/custom-measures-list-item.hbs index 8efd57f4c11..0d40e51c4db 100644 --- a/server/sonar-web/src/main/js/apps/custom-measures/templates/custom-measures-list-item.hbs +++ b/server/sonar-web/src/main/js/apps/custom-measures/templates/custom-measures-list-item.hbs @@ -32,6 +32,18 @@ - - + diff --git a/server/sonar-web/src/main/js/apps/groups/templates/groups-list-item.hbs b/server/sonar-web/src/main/js/apps/groups/templates/groups-list-item.hbs index 9f6bab62321..6d2600c9e41 100644 --- a/server/sonar-web/src/main/js/apps/groups/templates/groups-list-item.hbs +++ b/server/sonar-web/src/main/js/apps/groups/templates/groups-list-item.hbs @@ -1,7 +1,19 @@
{{#unless default}} - - + {{/unless}}
diff --git a/server/sonar-web/src/main/js/apps/metrics/templates/metrics-list-item.hbs b/server/sonar-web/src/main/js/apps/metrics/templates/metrics-list-item.hbs index 4f696e1d491..4033a0c9024 100644 --- a/server/sonar-web/src/main/js/apps/metrics/templates/metrics-list-item.hbs +++ b/server/sonar-web/src/main/js/apps/metrics/templates/metrics-list-item.hbs @@ -1,6 +1,18 @@
- - +
diff --git a/server/sonar-web/src/main/js/apps/organizations/components/MembersListItem.js b/server/sonar-web/src/main/js/apps/organizations/components/MembersListItem.js index 7c5c784dfd6..1147d0239be 100644 --- a/server/sonar-web/src/main/js/apps/organizations/components/MembersListItem.js +++ b/server/sonar-web/src/main/js/apps/organizations/components/MembersListItem.js @@ -24,7 +24,9 @@ import { translateWithParameters } from '../../../helpers/l10n'; import { formatMeasure } from '../../../helpers/measures'; import RemoveMemberForm from './forms/RemoveMemberForm'; import ManageMemberGroupsForm from './forms/ManageMemberGroupsForm'; -import SettingsIcon from '../../../components/icons-components/SettingsIcon'; +import ActionsDropdown, { + ActionsDropdownDivider +} from '../../../components/controls/ActionsDropdown'; /*:: import type { Member } from '../../../store/organizationsMembers/actions'; */ /*:: import type { Organization, OrgGroup } from '../../../store/organizations/duck'; */ @@ -64,31 +66,20 @@ export default class MembersListItem extends React.PureComponent { )} {organization.canAdmin && ( -
- -
    -
  • - -
  • -
  • -
  • - -
  • -
-
+ + + + + )} diff --git a/server/sonar-web/src/main/js/apps/organizations/components/__tests__/__snapshots__/MembersListItem-test.js.snap b/server/sonar-web/src/main/js/apps/organizations/components/__tests__/__snapshots__/MembersListItem-test.js.snap index 0bfde40983a..b88cbc6b739 100644 --- a/server/sonar-web/src/main/js/apps/organizations/components/__tests__/__snapshots__/MembersListItem-test.js.snap +++ b/server/sonar-web/src/main/js/apps/organizations/components/__tests__/__snapshots__/MembersListItem-test.js.snap @@ -31,70 +31,41 @@ exports[`should groups at 0 if the groupCount field is not defined (just added u -
- -
    -
  • - -
  • -
  • -
  • - -
  • -
-
+ } + /> + `; @@ -156,72 +127,43 @@ exports[`should render actions and groups for admin 1`] = ` -
- -
    -
  • - -
  • -
  • -
  • - -
  • -
-
+ } + /> + `; diff --git a/server/sonar-web/src/main/js/apps/organizations/components/forms/ManageMemberGroupsForm.js b/server/sonar-web/src/main/js/apps/organizations/components/forms/ManageMemberGroupsForm.js index 6e8818b6e85..58f6838bc5d 100644 --- a/server/sonar-web/src/main/js/apps/organizations/components/forms/ManageMemberGroupsForm.js +++ b/server/sonar-web/src/main/js/apps/organizations/components/forms/ManageMemberGroupsForm.js @@ -24,6 +24,7 @@ import { getUserGroups } from '../../../../api/users'; import Modal from '../../../../components/controls/Modal'; import { translate, translateWithParameters } from '../../../../helpers/l10n'; import OrganizationGroupCheckbox from '../OrganizationGroupCheckbox'; +import { ActionsDropdownItem } from '../../../../components/controls/ActionsDropdown'; /*:: import type { Member } from '../../../../store/organizationsMembers/actions'; */ /*:: import type { Organization, OrgGroup } from '../../../../store/organizations/duck'; */ @@ -45,14 +46,22 @@ type State = { */ export default class ManageMemberGroupsForm extends React.PureComponent { + /*:: mounted: boolean */ /*:: props: Props; */ state /*: State */ = { open: false }; - openForm = (evt /*: MouseEvent */) => { - evt.preventDefault(); + componentDidMount() { + this.mounted = true; + } + + componentWillUnmount() { + this.mounted = false; + } + + openForm = () => { this.loadUserGroups(); this.setState({ open: true }); }; @@ -63,9 +72,18 @@ export default class ManageMemberGroupsForm extends React.PureComponent { loadUserGroups = () => { this.setState({ loading: true }); - getUserGroups(this.props.member.login, this.props.organization.key).then(response => { - this.setState({ loading: false, userGroups: keyBy(response.groups, 'name') }); - }); + getUserGroups(this.props.member.login, this.props.organization.key).then( + response => { + if (this.mounted) { + this.setState({ loading: false, userGroups: keyBy(response.groups, 'name') }); + } + }, + () => { + if (this.mounted) { + this.setState({ loading: false }); + } + } + ); }; isGroupSelected = (groupName /*: string */) => { @@ -148,9 +166,9 @@ export default class ManageMemberGroupsForm extends React.PureComponent { render() { const buttonComponent = ( - + {translate('organization.members.manage_groups')} - + ); if (this.state.open) { return [buttonComponent, this.renderModal()]; diff --git a/server/sonar-web/src/main/js/apps/organizations/components/forms/RemoveMemberForm.js b/server/sonar-web/src/main/js/apps/organizations/components/forms/RemoveMemberForm.js index 94fb72dd24f..9a0db75be63 100644 --- a/server/sonar-web/src/main/js/apps/organizations/components/forms/RemoveMemberForm.js +++ b/server/sonar-web/src/main/js/apps/organizations/components/forms/RemoveMemberForm.js @@ -20,6 +20,7 @@ // @flow import React from 'react'; import Modal from '../../../../components/controls/Modal'; +import { ActionsDropdownItem } from '../../../../components/controls/ActionsDropdown'; import { translate, translateWithParameters } from '../../../../helpers/l10n'; /*:: import type { Member } from '../../../../store/organizationsMembers/actions'; */ /*:: import type { Organization } from '../../../../store/organizations/duck'; */ @@ -45,8 +46,7 @@ export default class RemoveMemberForm extends React.PureComponent { open: false }; - openForm = (evt /*: MouseEvent */) => { - evt.preventDefault(); + openForm = () => { this.setState({ open: true }); }; @@ -94,9 +94,9 @@ export default class RemoveMemberForm extends React.PureComponent { render() { const buttonComponent = ( - + {translate('organization.members.remove')} - + ); if (this.state.open) { return [buttonComponent, this.renderModal()]; diff --git a/server/sonar-web/src/main/js/apps/organizations/components/forms/__tests__/ManageMemberGroupsForm-test.js b/server/sonar-web/src/main/js/apps/organizations/components/forms/__tests__/ManageMemberGroupsForm-test.js index c28b5cbcd9a..63503e40918 100644 --- a/server/sonar-web/src/main/js/apps/organizations/components/forms/__tests__/ManageMemberGroupsForm-test.js +++ b/server/sonar-web/src/main/js/apps/organizations/components/forms/__tests__/ManageMemberGroupsForm-test.js @@ -82,7 +82,7 @@ it('should render and open the modal', () => { it('should correctly handle user interactions', () => { const form = getMountedForm(); - click(form.wrapper.find('a')); + form.wrapper.find('ActionsDropdownItem').prop('onClick')(); expect(form.wrapper.state('open')).toBeTruthy(); expect(form.instance.loadUserGroups).toBeCalled(); expect(form.wrapper.state()).toMatchSnapshot(); diff --git a/server/sonar-web/src/main/js/apps/organizations/components/forms/__tests__/RemoveMemberForm-test.js b/server/sonar-web/src/main/js/apps/organizations/components/forms/__tests__/RemoveMemberForm-test.js index 5c007ae4123..0fee4e72b58 100644 --- a/server/sonar-web/src/main/js/apps/organizations/components/forms/__tests__/RemoveMemberForm-test.js +++ b/server/sonar-web/src/main/js/apps/organizations/components/forms/__tests__/RemoveMemberForm-test.js @@ -42,7 +42,7 @@ it('should correctly handle user interactions', () => { ); const instance = wrapper.instance(); - click(wrapper.find('a')); + wrapper.find('ActionsDropdownItem').prop('onClick')(); expect(wrapper.state('open')).toBeTruthy(); instance.handleSubmit(mockEvent); expect(removeMember.mock.calls).toMatchSnapshot(); diff --git a/server/sonar-web/src/main/js/apps/organizations/components/forms/__tests__/__snapshots__/ManageMemberGroupsForm-test.js.snap b/server/sonar-web/src/main/js/apps/organizations/components/forms/__tests__/__snapshots__/ManageMemberGroupsForm-test.js.snap index 51d9f708ee7..94ff9bb6705 100644 --- a/server/sonar-web/src/main/js/apps/organizations/components/forms/__tests__/__snapshots__/ManageMemberGroupsForm-test.js.snap +++ b/server/sonar-web/src/main/js/apps/organizations/components/forms/__tests__/__snapshots__/ManageMemberGroupsForm-test.js.snap @@ -69,23 +69,20 @@ Object { `; exports[`should render and open the modal 1`] = ` - organization.members.manage_groups - + `; exports[`should render and open the modal 2`] = ` Array [ - organization.members.manage_groups - , + , organization.members.remove - + `; exports[`should render and open the modal 2`] = ` Array [ - organization.members.remove - , + , { new UpdateView({ model: new Backbone.Model(this.props.permissionTemplate), refresh: this.props.refresh }).render(); - } + }; - handleDeleteClick(e) { - e.preventDefault(); + handleDeleteClick = () => { new DeleteView({ model: new Backbone.Model(this.props.permissionTemplate) }) @@ -67,14 +66,14 @@ export default class ActionsCell extends React.PureComponent { this.props.refresh(); }) .render(); - } + }; - setDefault(qualifier, e) { - e.preventDefault(); + setDefault = qualifier => () => { setDefaultPermissionTemplate(this.props.permissionTemplate.id, qualifier).then( - this.props.refresh + this.props.refresh, + () => {} ); - } + }; getAvailableQualifiers() { const topQualifiers = @@ -84,16 +83,6 @@ export default class ActionsCell extends React.PureComponent { return difference(topQualifiers, this.props.permissionTemplate.defaultFor); } - renderDropdownIcon(icon) { - const style = { - display: 'inline-block', - width: 16, - marginRight: 4, - textAlign: 'center' - }; - return
{icon}
; - } - renderSetDefaultsControl() { const availableQualifiers = this.getAvailableQualifiers(); @@ -108,16 +97,13 @@ export default class ActionsCell extends React.PureComponent { renderSetDefaultLink(qualifier, child) { return ( -
  • - - {this.renderDropdownIcon()} - {child} - -
  • + + {child} + ); } @@ -150,40 +136,25 @@ export default class ActionsCell extends React.PureComponent { : '/permission_templates'; return ( -
    - - - -
    + + {this.renderSetDefaultsControl()} + + {!this.props.fromDetails && ( + + {translate('edit_permissions')} + + )} + + + {translate('update_details')} + + + {t.defaultFor.length === 0 && ( + + {translate('delete')} + + )} + ); } } diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityAnalysis.js b/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityAnalysis.js index 517e60cc26f..1f9453b9630 100644 --- a/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityAnalysis.js +++ b/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityAnalysis.js @@ -24,7 +24,9 @@ import Events from './Events'; import AddEventForm from './forms/AddEventForm'; import RemoveAnalysisForm from './forms/RemoveAnalysisForm'; import TimeTooltipFormatter from '../../../components/intl/TimeTooltipFormatter'; -import SettingsIcon from '../../../components/icons-components/SettingsIcon'; +import ActionsDropdown, { + ActionsDropdownDivider +} from '../../../components/controls/ActionsDropdown'; import { translate } from '../../../helpers/l10n'; /*:: import type { Analysis } from '../types'; */ @@ -78,44 +80,29 @@ export default class ProjectActivityAnalysis extends React.PureComponent { {(canAddVersion || canAddEvent || canDeleteAnalyses) && (
    -
    - -
      - {canAddVersion && ( -
    • - -
    • - )} - {canAddEvent && ( -
    • - -
    • - )} - {(canAddVersion || canAddEvent) && - canDeleteAnalyses &&
    • } - {canDeleteAnalyses && ( -
    • - -
    • - )} -
    -
    + + {canAddVersion && ( + + )} + {canAddEvent && ( + + )} + {(canAddVersion || canAddEvent) && canDeleteAnalyses && } + {canDeleteAnalyses && ( + + )} +
    )} diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/forms/AddEventForm.js b/server/sonar-web/src/main/js/apps/projectActivity/components/forms/AddEventForm.js index 520a159d0c0..50ff6ca1680 100644 --- a/server/sonar-web/src/main/js/apps/projectActivity/components/forms/AddEventForm.js +++ b/server/sonar-web/src/main/js/apps/projectActivity/components/forms/AddEventForm.js @@ -20,6 +20,7 @@ // @flow import React from 'react'; import Modal from '../../../../components/controls/Modal'; +import { ActionsDropdownItem } from '../../../../components/controls/ActionsDropdown'; import { translate } from '../../../../helpers/l10n'; /*:: import type { Analysis } from '../../types'; */ @@ -56,12 +57,8 @@ export default class AddEventForm extends React.PureComponent { this.mounted = false; } - openForm = (e /*: Event */) => { - e.preventDefault(); - e.stopPropagation(); - if (this.mounted) { - this.setState({ open: true }); - } + openForm = () => { + this.setState({ open: true }); }; closeForm = () => { @@ -137,9 +134,9 @@ export default class AddEventForm extends React.PureComponent { render() { const linkComponent = ( - + {translate(this.props.addEventButtonText)} - + ); if (this.state.open) { return [linkComponent, this.renderModal()]; diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/forms/RemoveAnalysisForm.js b/server/sonar-web/src/main/js/apps/projectActivity/components/forms/RemoveAnalysisForm.js index b3395d92a72..1203ace800c 100644 --- a/server/sonar-web/src/main/js/apps/projectActivity/components/forms/RemoveAnalysisForm.js +++ b/server/sonar-web/src/main/js/apps/projectActivity/components/forms/RemoveAnalysisForm.js @@ -20,6 +20,7 @@ // @flow import React from 'react'; import Modal from '../../../../components/controls/Modal'; +import { ActionsDropdownItem } from '../../../../components/controls/ActionsDropdown'; import { translate } from '../../../../helpers/l10n'; /*:: import type { Analysis } from '../../types'; */ @@ -53,12 +54,8 @@ export default class RemoveAnalysisForm extends React.PureComponent { this.mounted = false; } - openForm = (e /*: Event */) => { - e.preventDefault(); - e.stopPropagation(); - if (this.mounted) { - this.setState({ open: true }); - } + openForm = () => { + this.setState({ open: true }); }; closeForm = () => { @@ -119,9 +116,12 @@ export default class RemoveAnalysisForm extends React.PureComponent { render() { const linkComponent = ( - + {translate('project_activity.delete_analysis')} - + ); if (this.state.open) { return [linkComponent, this.renderModal()]; diff --git a/server/sonar-web/src/main/js/apps/projectBranches/components/BranchRow.tsx b/server/sonar-web/src/main/js/apps/projectBranches/components/BranchRow.tsx index 5290f17718c..608c98e27b6 100644 --- a/server/sonar-web/src/main/js/apps/projectBranches/components/BranchRow.tsx +++ b/server/sonar-web/src/main/js/apps/projectBranches/components/BranchRow.tsx @@ -28,7 +28,10 @@ import { isShortLivingBranch, isLongLivingBranch } from '../../../helpers/branch import { translate } from '../../../helpers/l10n'; import RenameBranchModal from './RenameBranchModal'; import DateFromNow from '../../../components/intl/DateFromNow'; -import SettingsIcon from '../../../components/icons-components/SettingsIcon'; +import ActionsDropdown, { + ActionsDropdownItem, + ActionsDropdownDivider +} from '../../../components/controls/ActionsDropdown'; interface Props { branch: Branch; @@ -54,9 +57,7 @@ export default class BranchRow extends React.PureComponent { this.mounted = false; } - handleDeleteClick = (event: React.SyntheticEvent) => { - event.preventDefault(); - event.currentTarget.blur(); + handleDeleteClick = () => { this.setState({ deleting: true }); }; @@ -64,9 +65,7 @@ export default class BranchRow extends React.PureComponent { this.setState({ deleting: false }); }; - handleRenameClick = (event: React.SyntheticEvent) => { - event.preventDefault(); - event.currentTarget.blur(); + handleRenameClick = () => { this.setState({ renaming: true }); }; @@ -81,9 +80,7 @@ export default class BranchRow extends React.PureComponent { this.setState({ renaming: false }); }; - handleChangeLeakClick = (event: React.SyntheticEvent) => { - event.preventDefault(); - event.currentTarget.blur(); + handleChangeLeakClick = () => { this.setState({ changingLeak: true }); }; @@ -93,69 +90,32 @@ export default class BranchRow extends React.PureComponent { } }; - render() { + renderActions() { const { branch, component } = this.props; - return ( - - - - {branch.name} - {branch.isMain && ( -
    {translate('branches.main_branch')}
    + + + {isLongLivingBranch(branch) && ( + + {translate('branches.set_leak_period')} + )} - - - - - - {branch.analysisDate && } - - -
    - - -
    - + {isLongLivingBranch(branch) && !branch.isMain && } + {branch.isMain ? ( + + {translate('branches.rename')} + + ) : ( + + {translate('branches.delete')} + + )} +
    {this.state.deleting && ( { project={component} /> )} + + ); + } + + render() { + const { branch } = this.props; + + return ( + + + + {branch.name} + {branch.isMain && ( +
    {translate('branches.main_branch')}
    + )} + + + + + + {branch.analysisDate && } + + {this.renderActions()} ); } diff --git a/server/sonar-web/src/main/js/apps/projectBranches/components/__tests__/__snapshots__/BranchRow-test.tsx.snap b/server/sonar-web/src/main/js/apps/projectBranches/components/__tests__/__snapshots__/BranchRow-test.tsx.snap index febb99812b8..5a28f352855 100644 --- a/server/sonar-web/src/main/js/apps/projectBranches/components/__tests__/__snapshots__/BranchRow-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/projectBranches/components/__tests__/__snapshots__/BranchRow-test.tsx.snap @@ -37,39 +37,16 @@ exports[`renders main branch 1`] = ` -
    - - -
    + branches.rename + + `; @@ -116,39 +93,17 @@ exports[`renders short-living branch 1`] = ` -
    - - -
    + branches.delete + + `; diff --git a/server/sonar-web/src/main/js/apps/projectsManagement/Header.tsx b/server/sonar-web/src/main/js/apps/projectsManagement/Header.tsx index f3ea88491a2..5dd7604092d 100644 --- a/server/sonar-web/src/main/js/apps/projectsManagement/Header.tsx +++ b/server/sonar-web/src/main/js/apps/projectsManagement/Header.tsx @@ -21,6 +21,7 @@ import * as React from 'react'; import ChangeVisibilityForm from './ChangeVisibilityForm'; import { Organization, Visibility } from '../../app/types'; import { translate } from '../../helpers/l10n'; +import EditIcon from '../../components/icons-components/EditIcon'; export interface Props { hasProvisionPermission?: boolean; @@ -62,10 +63,11 @@ export default class Header extends React.PureComponent { {translate('organization.default_visibility_of_new_projects')}{' '} {translate('visibility', organization.projectVisibility)} + onClick={this.handleChangeVisibilityClick}> + + {this.props.hasProvisionPermission && ( - -
    + + + {translate('edit_permissions')} + + + {translate('projects_role.apply_template')} + + ); diff --git a/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/__snapshots__/Header-test.tsx.snap b/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/__snapshots__/Header-test.tsx.snap index 9fb16cfbed5..c94a5a5cb29 100644 --- a/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/__snapshots__/Header-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/__snapshots__/Header-test.tsx.snap @@ -35,10 +35,14 @@ exports[`renders 1`] = ` visibility.public + > + + - - + projects_role.apply_template + + `; @@ -173,49 +150,26 @@ exports[`renders 2`] = ` -
    - - -
    + projects_role.apply_template + + `; diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileActions.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileActions.tsx index 8906db09a3b..0a2aff5cffb 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileActions.tsx +++ b/server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileActions.tsx @@ -19,7 +19,6 @@ */ import * as React from 'react'; import * as PropTypes from 'prop-types'; -import { Link } from 'react-router'; import RenameProfileForm from './RenameProfileForm'; import CopyProfileForm from './CopyProfileForm'; import DeleteProfileForm from './DeleteProfileForm'; @@ -28,8 +27,13 @@ import { getRulesUrl } from '../../../helpers/urls'; import { setDefaultProfile } from '../../../api/quality-profiles'; import { getProfilePath, getProfileComparePath, getProfilesPath } from '../utils'; import { Profile } from '../types'; +import ActionsDropdown, { + ActionsDropdownItem, + ActionsDropdownDivider +} from '../../../components/controls/ActionsDropdown'; interface Props { + className?: string; fromList?: boolean; onRequestFail: (reasong: any) => void; organization: string | null; @@ -57,50 +61,52 @@ export default class ProfileActions extends React.PureComponent { }; } - handleRenameClick = (event: React.SyntheticEvent) => { - event.preventDefault(); + handleRenameClick = () => { this.setState({ renameFormOpen: true }); }; handleProfileRename = (name: string) => { this.closeRenameForm(); - this.props.updateProfiles().then(() => { - if (!this.props.fromList) { - this.context.router.replace( - getProfilePath(name, this.props.profile.language, this.props.organization) - ); - } - }); + this.props.updateProfiles().then( + () => { + if (!this.props.fromList) { + this.context.router.replace( + getProfilePath(name, this.props.profile.language, this.props.organization) + ); + } + }, + () => {} + ); }; closeRenameForm = () => { this.setState({ renameFormOpen: false }); }; - handleCopyClick = (event: React.SyntheticEvent) => { - event.preventDefault(); + handleCopyClick = () => { this.setState({ copyFormOpen: true }); }; handleProfileCopy = (name: string) => { - this.props.updateProfiles().then(() => { - this.context.router.push( - getProfilePath(name, this.props.profile.language, this.props.organization) - ); - }); + this.props.updateProfiles().then( + () => { + this.context.router.push( + getProfilePath(name, this.props.profile.language, this.props.organization) + ); + }, + () => {} + ); }; closeCopyForm = () => { this.setState({ copyFormOpen: false }); }; - handleSetDefaultClick = (e: React.SyntheticEvent) => { - e.preventDefault(); - setDefaultProfile(this.props.profile.key).then(this.props.updateProfiles); + handleSetDefaultClick = () => { + setDefaultProfile(this.props.profile.key).then(this.props.updateProfiles, () => {}); }; - handleDeleteClick = (event: React.SyntheticEvent) => { - event.preventDefault(); + handleDeleteClick = () => { this.setState({ deleteFormOpen: true }); }; @@ -131,60 +137,64 @@ export default class ProfileActions extends React.PureComponent { this.props.organization ); + const canActivateRules = actions.edit && !profile.isBuiltIn; + const canRename = actions.edit && !profile.isBuiltIn; + const canSetAsDefault = actions.setAsDefault && !profile.isDefault; + const canDelete = actions.edit && !profile.isDefault && !profile.isBuiltIn; + return ( -
      - {actions.edit && - !profile.isBuiltIn && ( -
    • - {translate('quality_profiles.activate_more_rules')} -
    • - )} + + {canActivateRules && ( + + {translate('quality_profiles.activate_more_rules')} + + )} + {!profile.isBuiltIn && ( -
    • - - {translate('backup_verb')} - -
    • + + {translate('backup_verb')} + )} -
    • - - {translate('compare')} - -
    • + + + {translate('compare')} + + {actions.copy && ( -
    • - - {translate('copy')} - -
    • + + {translate('copy')} + + )} + + {canRename && ( + + {translate('rename')} + + )} + + {canSetAsDefault && ( + + {translate('set_as_default')} + + )} + + {canDelete && } + + {canDelete && ( + + {translate('delete')} + )} - {actions.edit && - !profile.isBuiltIn && ( -
    • - - {translate('rename')} - -
    • - )} - {actions.setAsDefault && - !profile.isDefault && ( -
    • - - {translate('set_as_default')} - -
    • - )} - {actions.edit && - !profile.isDefault && - !profile.isBuiltIn && ( -
    • - - {translate('delete')} - -
    • - )} {this.state.copyFormOpen && ( { profile={profile} /> )} -
    + ); } } diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/components/__tests__/__snapshots__/ProfileActions-test.tsx.snap b/server/sonar-web/src/main/js/apps/quality-profiles/components/__tests__/__snapshots__/ProfileActions-test.tsx.snap index dae1d0887fc..ba760e5cb62 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/components/__tests__/__snapshots__/ProfileActions-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/quality-profiles/components/__tests__/__snapshots__/ProfileActions-test.tsx.snap @@ -1,172 +1,129 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`renders with all permissions 1`] = ` - + } + > + compare + + + copy + + + rename + + + set_as_default + + + + delete + + `; exports[`renders with no permissions 1`] = ` - + } + > + compare + + `; exports[`renders with permission to edit 1`] = ` - + } + > + compare + + + rename + + + + delete + + `; diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileHeader.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileHeader.tsx index 5b04d60fdd4..b131bb089de 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileHeader.tsx +++ b/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileHeader.tsx @@ -107,17 +107,13 @@ export default class ProfileHeader extends React.PureComponent {
  • -
    - - -
    +
  • diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/home/ProfilesListRow.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/home/ProfilesListRow.tsx index b838725a953..ff75c1cef3b 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/home/ProfilesListRow.tsx +++ b/server/sonar-web/src/main/js/apps/quality-profiles/home/ProfilesListRow.tsx @@ -139,18 +139,13 @@ export default class ProfilesListRow extends React.PureComponent { {this.renderUsageDate()} -
    - - -
    + ); diff --git a/server/sonar-web/src/main/js/apps/settings/components/inputs/MultiValueInput.js b/server/sonar-web/src/main/js/apps/settings/components/inputs/MultiValueInput.js index 14b98fceadb..76850ecbf06 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/inputs/MultiValueInput.js +++ b/server/sonar-web/src/main/js/apps/settings/components/inputs/MultiValueInput.js @@ -21,6 +21,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import PrimitiveInput from './PrimitiveInput'; import { getEmptyValue } from '../../utils'; +import DeleteIcon from '../../../../components/icons-components/DeleteIcon'; export default class MultiValueInput extends React.PureComponent { static propTypes = { @@ -72,7 +73,7 @@ export default class MultiValueInput extends React.PureComponent { )} diff --git a/server/sonar-web/src/main/js/apps/settings/components/inputs/PropertySetInput.js b/server/sonar-web/src/main/js/apps/settings/components/inputs/PropertySetInput.js index 845ca62d952..4016656f336 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/inputs/PropertySetInput.js +++ b/server/sonar-web/src/main/js/apps/settings/components/inputs/PropertySetInput.js @@ -21,6 +21,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import PrimitiveInput from './PrimitiveInput'; import { getEmptyValue, getUniqueName } from '../../utils'; +import DeleteIcon from '../../../../components/icons-components/DeleteIcon'; export default class PropertySetInput extends React.PureComponent { static propTypes = { @@ -74,7 +75,7 @@ export default class PropertySetInput extends React.PureComponent { )} diff --git a/server/sonar-web/src/main/js/apps/tutorials/onboarding/ProjectKeyStep.js b/server/sonar-web/src/main/js/apps/tutorials/onboarding/ProjectKeyStep.js index fc74db42541..6d84a73fc55 100644 --- a/server/sonar-web/src/main/js/apps/tutorials/onboarding/ProjectKeyStep.js +++ b/server/sonar-web/src/main/js/apps/tutorials/onboarding/ProjectKeyStep.js @@ -20,6 +20,7 @@ // @flow import React from 'react'; import { createProject, deleteProject } from '../../../api/components'; +import DeleteIcon from '../../../components/icons-components/DeleteIcon'; import { translate } from '../../../helpers/l10n'; /*:: @@ -116,7 +117,7 @@ export default class ProjectKeyStep extends React.PureComponent { ) : ( )} diff --git a/server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/ProjectKeyStep-test.js b/server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/ProjectKeyStep-test.js index 8d36627114f..962b33b1acb 100644 --- a/server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/ProjectKeyStep-test.js +++ b/server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/ProjectKeyStep-test.js @@ -28,6 +28,14 @@ jest.mock('../../../../api/components', () => ({ deleteProject: () => Promise.resolve() })); +jest.mock( + '../../../../components/icons-components/DeleteIcon', + () => + function DeleteIcon() { + return null; + } +); + it('creates new project', async () => { const onDone = jest.fn(); const wrapper = mount(); diff --git a/server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/__snapshots__/ProjectKeyStep-test.js.snap b/server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/__snapshots__/ProjectKeyStep-test.js.snap index cbfbf1da7ca..21ad43db2e6 100644 --- a/server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/__snapshots__/ProjectKeyStep-test.js.snap +++ b/server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/__snapshots__/ProjectKeyStep-test.js.snap @@ -105,9 +105,7 @@ exports[`creates new project 3`] = ` @@ -138,9 +136,7 @@ exports[`deletes project 1`] = ` diff --git a/server/sonar-web/src/main/js/apps/users/templates/users-deactivate.hbs b/server/sonar-web/src/main/js/apps/users/templates/users-deactivate.hbs index 6ff6e8692e1..5ecce84bcdd 100644 --- a/server/sonar-web/src/main/js/apps/users/templates/users-deactivate.hbs +++ b/server/sonar-web/src/main/js/apps/users/templates/users-deactivate.hbs @@ -7,7 +7,7 @@ {{tp 'users.deactivate_user.confirmation' name login}} diff --git a/server/sonar-web/src/main/js/apps/users/templates/users-list-item.hbs b/server/sonar-web/src/main/js/apps/users/templates/users-list-item.hbs index 63136b96402..5ac0db657c7 100644 --- a/server/sonar-web/src/main/js/apps/users/templates/users-list-item.hbs +++ b/server/sonar-web/src/main/js/apps/users/templates/users-list-item.hbs @@ -63,10 +63,23 @@ - - {{#if local}} - - {{/if}} - + diff --git a/server/sonar-web/src/main/js/components/controls/ActionsDropdown.tsx b/server/sonar-web/src/main/js/components/controls/ActionsDropdown.tsx new file mode 100644 index 00000000000..fbcb30b299c --- /dev/null +++ b/server/sonar-web/src/main/js/components/controls/ActionsDropdown.tsx @@ -0,0 +1,108 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import * as React from 'react'; +import * as classNames from 'classnames'; +import { Link } from 'react-router'; +import { LocationDescriptor } from 'history'; +import SettingsIcon from '../icons-components/SettingsIcon'; + +interface Props { + className?: string; + children: React.ReactNode; + small?: boolean; + toggleClassName?: string; +} + +export default function ActionsDropdown(props: Props) { + return ( +
    + +
      {props.children}
    +
    + ); +} + +interface ItemProps { + className?: string; + children: React.ReactNode; + destructive?: boolean; + /** used to pass a name of downloaded file */ + download?: string; + id?: string; + onClick?: () => void; + to?: LocationDescriptor; +} + +export class ActionsDropdownItem extends React.PureComponent { + handleClick = (event: React.SyntheticEvent) => { + event.preventDefault(); + event.currentTarget.blur(); + if (this.props.onClick) { + this.props.onClick(); + } + }; + + render() { + const className = classNames(this.props.className, { 'text-danger': this.props.destructive }); + + if (this.props.download && typeof this.props.to === 'string') { + return ( +
  • + + {this.props.children} + +
  • + ); + } + + if (this.props.to) { + return ( +
  • + + {this.props.children} + +
  • + ); + } + + return ( +
  • + + {this.props.children} + +
  • + ); + } +} + +export function ActionsDropdownDivider() { + return
  • ; +} diff --git a/server/sonar-web/src/main/js/components/issue/components/IssueCommentLine.js b/server/sonar-web/src/main/js/components/issue/components/IssueCommentLine.js index f6c73044282..79c2f1c7b18 100644 --- a/server/sonar-web/src/main/js/components/issue/components/IssueCommentLine.js +++ b/server/sonar-web/src/main/js/components/issue/components/IssueCommentLine.js @@ -21,6 +21,8 @@ import React from 'react'; import Avatar from '../../../components/ui/Avatar'; import BubblePopupHelper from '../../../components/common/BubblePopupHelper'; +import EditIcon from '../../../components/icons-components/EditIcon'; +import DeleteIcon from '../../../components/icons-components/DeleteIcon'; import CommentDeletePopup from '../popups/CommentDeletePopup'; import CommentPopup from '../popups/CommentPopup'; import DateFromNow from '../../../components/intl/DateFromNow'; @@ -118,9 +120,10 @@ export default class IssueCommentLine extends React.PureComponent { /> }> )} {comment.updatable && ( @@ -132,9 +135,10 @@ export default class IssueCommentLine extends React.PureComponent { togglePopup={this.toggleDeletePopup} popup={}> )} diff --git a/server/sonar-web/src/main/js/components/issue/components/__tests__/__snapshots__/IssueCommentLine-test.js.snap b/server/sonar-web/src/main/js/components/issue/components/__tests__/__snapshots__/IssueCommentLine-test.js.snap index d31a9e8eba2..2ffdaa63fd4 100644 --- a/server/sonar-web/src/main/js/components/issue/components/__tests__/__snapshots__/IssueCommentLine-test.js.snap +++ b/server/sonar-web/src/main/js/components/issue/components/__tests__/__snapshots__/IssueCommentLine-test.js.snap @@ -80,9 +80,11 @@ exports[`should open the right popups when the buttons are clicked 3`] = ` togglePopup={[Function]} > @@ -218,9 +222,11 @@ exports[`should render correctly a comment that is updatable 1`] = ` togglePopup={[Function]} > diff --git a/server/sonar-web/src/main/js/helpers/handlebars/settingsIcon.js b/server/sonar-web/src/main/js/helpers/handlebars/settingsIcon.js new file mode 100644 index 00000000000..48db75bfaeb --- /dev/null +++ b/server/sonar-web/src/main/js/helpers/handlebars/settingsIcon.js @@ -0,0 +1,33 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 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. + */ +const Handlebars = require('handlebars/runtime'); + +module.exports = function() { + return new Handlebars.default.SafeString(` + + + + + + `); +}; diff --git a/tests/src/test/resources/organization/OrganizationQualityProfilesUiTest/should_restore.html b/tests/src/test/resources/organization/OrganizationQualityProfilesUiTest/should_restore.html index dd2d21ba31f..3091176ebae 100644 --- a/tests/src/test/resources/organization/OrganizationQualityProfilesUiTest/should_restore.html +++ b/tests/src/test/resources/organization/OrganizationQualityProfilesUiTest/should_restore.html @@ -44,16 +44,6 @@ /organizations/test-org/quality_profiles - - waitForElementPresent - css=.js-more-admin-actions - - - - click - css=.js-more-admin-actions - - click css=#quality-profiles-restore diff --git a/tests/src/test/resources/qualityProfile/QualityProfilesUiTest/should_restore.html b/tests/src/test/resources/qualityProfile/QualityProfilesUiTest/should_restore.html index 8def7727001..792e12c864c 100644 --- a/tests/src/test/resources/qualityProfile/QualityProfilesUiTest/should_restore.html +++ b/tests/src/test/resources/qualityProfile/QualityProfilesUiTest/should_restore.html @@ -39,16 +39,6 @@ /profiles - - waitForElementPresent - css=.js-more-admin-actions - - - - click - css=.js-more-admin-actions - - click css=#quality-profiles-restore diff --git a/tests/src/test/resources/user/ExternalAuthenticationTest/create-and-delete-user.html b/tests/src/test/resources/user/ExternalAuthenticationTest/create-and-delete-user.html index f7d0dd6ffb6..9be06a9320a 100644 --- a/tests/src/test/resources/user/ExternalAuthenticationTest/create-and-delete-user.html +++ b/tests/src/test/resources/user/ExternalAuthenticationTest/create-and-delete-user.html @@ -88,6 +88,11 @@ id=users-list *${userId}* + + click + css=[data-login="${userId}"] .dropdown-toggle + + click css=[data-login="${userId}"] .js-user-deactivate diff --git a/tests/src/test/resources/user/UsersPageTest/admin_should_change_its_own_password.html b/tests/src/test/resources/user/UsersPageTest/admin_should_change_its_own_password.html index bc17a7e90c9..228686fcd62 100644 --- a/tests/src/test/resources/user/UsersPageTest/admin_should_change_its_own_password.html +++ b/tests/src/test/resources/user/UsersPageTest/admin_should_change_its_own_password.html @@ -50,6 +50,11 @@ css=[data-login=admin-user] + + click + css=[data-login=admin-user] .dropdown-toggle + + click css=[data-login=admin-user] .js-user-change-password -- 2.39.5