diff --git a/app/assets/javascripts/issuable_bulk_update_actions.js b/app/assets/javascripts/issuable_bulk_update_actions.js index 8bb76edbd47..b9e8c015d19 100644 --- a/app/assets/javascripts/issuable_bulk_update_actions.js +++ b/app/assets/javascripts/issuable_bulk_update_actions.js @@ -50,6 +50,7 @@ export default { subscription_event: this.form.find('input[name="update[subscription_event]"]').val(), health_status: this.form.find('input[name="update[health_status]"]').val(), epic_id: this.form.find('input[name="update[epic_id]"]').val(), + sprint_id: this.form.find('input[name="update[iteration_id]"]').val(), add_label_ids: [], remove_label_ids: [], }, diff --git a/app/assets/javascripts/issuable_bulk_update_sidebar.js b/app/assets/javascripts/issuable_bulk_update_sidebar.js index b9daa16874a..d6fa0f42bd4 100644 --- a/app/assets/javascripts/issuable_bulk_update_sidebar.js +++ b/app/assets/javascripts/issuable_bulk_update_sidebar.js @@ -79,6 +79,16 @@ export default class IssuableBulkUpdateSidebar { }) .catch(() => {}); } + + if (IS_EE) { + import('ee/vue_shared/components/sidebar/iterations_dropdown_bundle') + .then(({ default: iterationsDropdown }) => { + iterationsDropdown(); + }) + .catch((e) => { + throw e; + }); + } } setupBulkUpdateActions() { diff --git a/app/assets/javascripts/pages/projects/edit/index.js b/app/assets/javascripts/pages/projects/edit/index.js index 5f1d3edc3ba..9c1148e66e2 100644 --- a/app/assets/javascripts/pages/projects/edit/index.js +++ b/app/assets/javascripts/pages/projects/edit/index.js @@ -10,7 +10,7 @@ import initProjectPermissionsSettings from '../shared/permissions'; import initProjectDeleteButton from '~/projects/project_delete_button'; import UserCallout from '~/user_callout'; import initServiceDesk from '~/projects/settings_service_desk'; -import mountSearchSettings from './mount_search_settings'; +import initSearchSettings from '~/search_settings'; document.addEventListener('DOMContentLoaded', () => { initFilePickers(); @@ -32,5 +32,5 @@ document.addEventListener('DOMContentLoaded', () => { ), ); - mountSearchSettings(); + initSearchSettings(); }); diff --git a/app/assets/javascripts/pages/projects/edit/mount_search_settings.js b/app/assets/javascripts/pages/projects/edit/mount_search_settings.js deleted file mode 100644 index 6c477dd7e80..00000000000 --- a/app/assets/javascripts/pages/projects/edit/mount_search_settings.js +++ /dev/null @@ -1,12 +0,0 @@ -const mountSearchSettings = async () => { - const el = document.querySelector('.js-search-settings-app'); - - if (el) { - const { default: initSearch } = await import( - /* webpackChunkName: 'search_settings' */ '~/search_settings' - ); - initSearch({ el }); - } -}; - -export default mountSearchSettings; diff --git a/app/assets/javascripts/search_settings/index.js b/app/assets/javascripts/search_settings/index.js index 1fb1a378ffb..676c43c5631 100644 --- a/app/assets/javascripts/search_settings/index.js +++ b/app/assets/javascripts/search_settings/index.js @@ -1,23 +1,10 @@ -import Vue from 'vue'; -import $ from 'jquery'; -import { expandSection, closeSection } from '~/settings_panels'; -import SearchSettings from '~/search_settings/components/search_settings.vue'; +const initSearch = async () => { + const el = document.querySelector('.js-search-settings-app'); -const initSearch = ({ el }) => - new Vue({ - el, - render: (h) => - h(SearchSettings, { - ref: 'searchSettings', - props: { - searchRoot: document.querySelector('#content-body'), - sectionSelector: 'section.settings', - }, - on: { - collapse: (section) => closeSection($(section)), - expand: (section) => expandSection($(section)), - }, - }), - }); + if (el) { + const { default: mount } = await import(/* webpackChunkName: 'search_settings' */ './mount'); + mount({ el }); + } +}; export default initSearch; diff --git a/app/assets/javascripts/search_settings/mount.js b/app/assets/javascripts/search_settings/mount.js new file mode 100644 index 00000000000..85ad3b28d59 --- /dev/null +++ b/app/assets/javascripts/search_settings/mount.js @@ -0,0 +1,23 @@ +import Vue from 'vue'; +import $ from 'jquery'; +import { expandSection, closeSection } from '~/settings_panels'; +import SearchSettings from '~/search_settings/components/search_settings.vue'; + +const mountSearch = ({ el }) => + new Vue({ + el, + render: (h) => + h(SearchSettings, { + ref: 'searchSettings', + props: { + searchRoot: document.querySelector('#content-body'), + sectionSelector: 'section.settings', + }, + on: { + collapse: (section) => closeSection($(section)), + expand: (section) => expandSection($(section)), + }, + }), + }); + +export default mountSearch; diff --git a/app/views/projects/_activity.html.haml b/app/views/projects/_activity.html.haml index c47ca81c431..db0f13843dd 100644 --- a/app/views/projects/_activity.html.haml +++ b/app/views/projects/_activity.html.haml @@ -1,15 +1,14 @@ - is_project_overview = local_assigns.fetch(:is_project_overview, false) -%div{ class: container_class } - .nav-block.d-none.d-sm-flex.activities.gl-static - = render 'shared/event_filter' - .controls.gl-display-flex - = link_to project_path(@project, rss_url_options), title: s_("ProjectActivityRSS|Subscribe"), class: 'btn gl-button btn-default btn-icon d-none d-sm-inline-flex has-tooltip' do - = sprite_icon('rss', css_class: 'qa-rss-icon gl-icon') - - if is_project_overview && can?(current_user, :download_code, @project) - .project-clone-holder.d-none.d-md-inline-flex.gl-ml-2 - = render "projects/buttons/clone", dropdown_class: 'dropdown-menu-right' +.nav-block.d-none.d-sm-flex.activities.gl-static + = render 'shared/event_filter' + .controls.gl-display-flex + = link_to project_path(@project, rss_url_options), title: s_("ProjectActivityRSS|Subscribe"), class: 'btn gl-button btn-default btn-icon d-none d-sm-inline-flex has-tooltip' do + = sprite_icon('rss', css_class: 'qa-rss-icon gl-icon') + - if is_project_overview && can?(current_user, :download_code, @project) + .project-clone-holder.d-none.d-md-inline-flex.gl-ml-2 + = render "projects/buttons/clone", dropdown_class: 'dropdown-menu-right' - .content_list.project-activity{ :"data-href" => activity_project_path(@project) } - .loading - .spinner.spinner-md +.content_list.project-activity{ :"data-href" => activity_project_path(@project) } +.loading + .spinner.spinner-md diff --git a/app/views/projects/protected_branches/shared/_protected_branch.html.haml b/app/views/projects/protected_branches/shared/_protected_branch.html.haml index d62e9513d56..02ec778b97c 100644 --- a/app/views/projects/protected_branches/shared/_protected_branch.html.haml +++ b/app/views/projects/protected_branches/shared/_protected_branch.html.haml @@ -5,7 +5,7 @@ %span.ref-name= protected_branch.name - if @project.root_ref?(protected_branch.name) - %span.badge.badge-info.d-inline default + %span.badge.gl-badge.badge-pill.badge-info.d-inline default %div - if protected_branch.wildcard? @@ -20,4 +20,4 @@ - if can_admin_project %td - = link_to 'Unprotect', [@project, protected_branch, { update_section: 'js-protected-branches-settings' }], disabled: local_assigns[:disabled], data: { confirm: 'Branch will be writable for developers. Are you sure?' }, method: :delete, class: "btn btn-warning" + = link_to 'Unprotect', [@project, protected_branch, { update_section: 'js-protected-branches-settings' }], disabled: local_assigns[:disabled], data: { confirm: 'Branch will be writable for developers. Are you sure?' }, method: :delete, class: "btn gl-button btn-warning" diff --git a/app/views/shared/issuable/_bulk_update_sidebar.html.haml b/app/views/shared/issuable/_bulk_update_sidebar.html.haml index fd27fac7fee..96960e733fb 100644 --- a/app/views/shared/issuable/_bulk_update_sidebar.html.haml +++ b/app/views/shared/issuable/_bulk_update_sidebar.html.haml @@ -1,6 +1,7 @@ - type = local_assigns.fetch(:type) - bulk_issue_health_status_flag = type == :issues && @project&.group&.feature_available?(:issuable_health_status) - epic_bulk_edit_flag = @project&.group&.feature_available?(:epics) && type == :issues +- bulk_iterations_flag = @project.feature_available?(:iterations) && @project&.group.present? && type == :issues %aside.issues-bulk-update.js-right-sidebar.right-sidebar{ "aria-live" => "polite", data: { 'signed-in': current_user.present? } } .issuable-sidebar.hidden @@ -41,6 +42,8 @@ = _('Milestone') .filter-item = dropdown_tag(_("Select milestone"), options: { title: _("Assign milestone"), toggle_class: "js-milestone-select js-extra-options js-filter-submit js-filter-bulk-update", filter: true, dropdown_class: "dropdown-menu-selectable dropdown-menu-milestone", placeholder: _("Search milestones"), data: { show_no: true, field_name: "update[milestone_id]", project_id: @project.id, use_id: true, default_label: _("Milestone") } }) + - if bulk_iterations_flag + = render_if_exists 'shared/iterations_dropdown', path: @project.group.full_path .block .title = _('Labels') diff --git a/changelogs/unreleased/yo-gl-badge-protected-branch.yml b/changelogs/unreleased/yo-gl-badge-protected-branch.yml new file mode 100644 index 00000000000..52513d57679 --- /dev/null +++ b/changelogs/unreleased/yo-gl-badge-protected-branch.yml @@ -0,0 +1,5 @@ +--- +title: Apply new GitLab UI for badge and button in project's protected branch settings +merge_request: 52207 +author: Yogi (@yo) +type: other diff --git a/changelogs/unreleased/yo-remove-activity-container.yml b/changelogs/unreleased/yo-remove-activity-container.yml new file mode 100644 index 00000000000..b16b12ff50d --- /dev/null +++ b/changelogs/unreleased/yo-remove-activity-container.yml @@ -0,0 +1,5 @@ +--- +title: Remove container_class in project activity which removes extra padding +merge_request: 51878 +author: Yogi (@yo) +type: fixed diff --git a/doc/user/group/bulk_editing/index.md b/doc/user/group/bulk_editing/index.md index 8db9c7fd76c..22001b317f7 100644 --- a/doc/user/group/bulk_editing/index.md +++ b/doc/user/group/bulk_editing/index.md @@ -20,19 +20,21 @@ Only the items visible on the current page are selected for bulk editing (up to ## Bulk edit issues at the group level -> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/7249) in [GitLab Premium](https://about.gitlab.com/pricing/) 12.1. +> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/7249) in GitLab 12.1. +> - Assigning epic ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/210470) in GitLab 13.2. +> - Editing health status [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/218395) in GitLab 13.2. +> - Editing iteration [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/196806) in GitLab 13.9. NOTE: You need a permission level of [Reporter or higher](../../permissions.md) to manage issues. When bulk editing issues in a group, you can edit the following attributes: -- Epic ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/210470) in - [GitLab Premium](https://about.gitlab.com/pricing/) 13.2.) **(PREMIUM)** -- Milestone -- Labels -- Health status ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/218395) in - [GitLab Ultimate](https://about.gitlab.com/pricing/) 13.2.) **(ULTIMATE)** +- [Epic](../epics/index.md) +- [Milestone](../../project/milestones/index.md) +- [Labels](../../project/labels.md) +- [Health status](../../project/issues/index.md#health-status) +- [Iteration](../iterations/index.md) To update multiple project issues at the same time: diff --git a/doc/user/project/bulk_editing.md b/doc/user/project/bulk_editing.md index a29c0754d31..19f17ad91d7 100644 --- a/doc/user/project/bulk_editing.md +++ b/doc/user/project/bulk_editing.md @@ -21,6 +21,10 @@ Only the items visible on the current page are selected for bulk editing (up to ## Bulk edit issues at the project level +> - Assigning epic ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/210470) in GitLab 13.2. +> - Editing health status [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/218395) in GitLab 13.2. +> - Editing iteration [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/196806) in GitLab 13.9. + NOTE: You need a permission level of [Reporter or higher](../permissions.md) to manage issues. @@ -28,13 +32,12 @@ When bulk editing issues in a project, you can edit the following attributes: - Status (open/closed) - Assignee -- Epic ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/210470) in - [GitLab Premium](https://about.gitlab.com/pricing/) 13.2.) **(PREMIUM)** -- Milestone -- Labels -- Health status ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/218395) in - [GitLab Ultimate](https://about.gitlab.com/pricing/) 13.2.) **(ULTIMATE)** -- Subscriptions +- [Epic](../group/epics/index.md) +- [Milestone](milestones/index.md) +- [Labels](labels.md) +- [Health status](issues/index.md#health-status) +- Notification subscription +- [Iteration](../group/iterations/index.md) To update multiple project issues at the same time: diff --git a/locale/gitlab.pot b/locale/gitlab.pot index ae18b37bd84..4d10c490aee 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -25996,6 +25996,9 @@ msgstr "" msgid "Select health status" msgstr "" +msgid "Select iteration" +msgstr "" + msgid "Select label" msgstr "" diff --git a/spec/frontend/pages/projects/edit/mount_search_settings_spec.js b/spec/frontend/pages/projects/edit/mount_search_settings_spec.js deleted file mode 100644 index b48809b3d00..00000000000 --- a/spec/frontend/pages/projects/edit/mount_search_settings_spec.js +++ /dev/null @@ -1,25 +0,0 @@ -import { setHTMLFixture, resetHTMLFixture } from 'helpers/fixtures'; -import initSearch from '~/search_settings'; -import mountSearchSettings from '~/pages/projects/edit/mount_search_settings'; - -jest.mock('~/search_settings'); - -describe('pages/projects/edit/mount_search_settings', () => { - afterEach(() => { - resetHTMLFixture(); - }); - - it('initializes search settings when js-search-settings-app is available', async () => { - setHTMLFixture('
'); - - await mountSearchSettings(); - - expect(initSearch).toHaveBeenCalled(); - }); - - it('does not initialize search settings when js-search-settings-app is unavailable', async () => { - await mountSearchSettings(); - - expect(initSearch).not.toHaveBeenCalled(); - }); -}); diff --git a/spec/frontend/search_settings/index_spec.js b/spec/frontend/search_settings/index_spec.js index 122ee1251bb..1d56d054eea 100644 --- a/spec/frontend/search_settings/index_spec.js +++ b/spec/frontend/search_settings/index_spec.js @@ -1,36 +1,25 @@ -import $ from 'jquery'; -import { setHTMLFixture } from 'helpers/fixtures'; +import { setHTMLFixture, resetHTMLFixture } from 'helpers/fixtures'; import initSearch from '~/search_settings'; -import { expandSection, closeSection } from '~/settings_panels'; +import mount from '~/search_settings/mount'; -jest.mock('~/settings_panels'); - -describe('search_settings/index', () => { - let app; - - beforeEach(() => { - const el = document.createElement('div'); - - setHTMLFixture('
'); - - app = initSearch({ el }); - }); +jest.mock('~/search_settings/mount'); +describe('~/search_settings', () => { afterEach(() => { - app.$destroy(); + resetHTMLFixture(); }); - it('calls settings_panel.onExpand when expand event is emitted', () => { - const section = { name: 'section' }; - app.$refs.searchSettings.$emit('expand', section); + it('initializes search settings when js-search-settings-app is available', async () => { + setHTMLFixture('
'); - expect(expandSection).toHaveBeenCalledWith($(section)); + await initSearch(); + + expect(mount).toHaveBeenCalled(); }); - it('calls settings_panel.closeSection when collapse event is emitted', () => { - const section = { name: 'section' }; - app.$refs.searchSettings.$emit('collapse', section); + it('does not initialize search settings when js-search-settings-app is unavailable', async () => { + await initSearch(); - expect(closeSection).toHaveBeenCalledWith($(section)); + expect(mount).not.toHaveBeenCalled(); }); }); diff --git a/spec/frontend/search_settings/mount_spec.js b/spec/frontend/search_settings/mount_spec.js new file mode 100644 index 00000000000..b35266e56ae --- /dev/null +++ b/spec/frontend/search_settings/mount_spec.js @@ -0,0 +1,36 @@ +import $ from 'jquery'; +import { setHTMLFixture } from 'helpers/fixtures'; +import mount from '~/search_settings/mount'; +import { expandSection, closeSection } from '~/settings_panels'; + +jest.mock('~/settings_panels'); + +describe('search_settings/mount', () => { + let app; + + beforeEach(() => { + const el = document.createElement('div'); + + setHTMLFixture('
'); + + app = mount({ el }); + }); + + afterEach(() => { + app.$destroy(); + }); + + it('calls settings_panel.onExpand when expand event is emitted', () => { + const section = { name: 'section' }; + app.$refs.searchSettings.$emit('expand', section); + + expect(expandSection).toHaveBeenCalledWith($(section)); + }); + + it('calls settings_panel.closeSection when collapse event is emitted', () => { + const section = { name: 'section' }; + app.$refs.searchSettings.$emit('collapse', section); + + expect(closeSection).toHaveBeenCalledWith($(section)); + }); +});