diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-11-06 00:08:51 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-11-06 00:08:51 +0300 |
commit | 1bc5af76617026dec53016cb0881ec834ccab807 (patch) | |
tree | d0bd460cfcf415248441a1ad79a8bf76b87aa27d /app/assets/javascripts/issuable_list | |
parent | 3e1c760141a27097d74d191a619fa6edecd86fe7 (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app/assets/javascripts/issuable_list')
3 files changed, 130 insertions, 3 deletions
diff --git a/app/assets/javascripts/issuable_list/components/issuable_bulk_edit_sidebar.vue b/app/assets/javascripts/issuable_list/components/issuable_bulk_edit_sidebar.vue new file mode 100644 index 00000000000..5ca9e50d854 --- /dev/null +++ b/app/assets/javascripts/issuable_list/components/issuable_bulk_edit_sidebar.vue @@ -0,0 +1,35 @@ +<script> +export default { + props: { + expanded: { + type: Boolean, + required: true, + }, + }, + watch: { + expanded(value) { + const layoutPageEl = document.querySelector('.layout-page'); + + if (layoutPageEl) { + layoutPageEl.classList.toggle('right-sidebar-expanded', value); + layoutPageEl.classList.toggle('right-sidebar-collapsed', !value); + } + }, + }, +}; +</script> + +<template> + <aside + :class="{ 'right-sidebar-expanded': expanded, 'right-sidebar-collapsed': !expanded }" + class="issues-bulk-update right-sidebar" + aria-live="polite" + > + <div + class="gl-display-flex gl-justify-content-space-between gl-p-4 gl-border-b-1 gl-border-b-solid gl-border-gray-100" + > + <slot name="bulk-edit-actions"></slot> + </div> + <slot name="sidebar-items"></slot> + </aside> +</template> diff --git a/app/assets/javascripts/issuable_list/components/issuable_item.vue b/app/assets/javascripts/issuable_list/components/issuable_item.vue index de5e47bfa9b..05bc3185cc8 100644 --- a/app/assets/javascripts/issuable_list/components/issuable_item.vue +++ b/app/assets/javascripts/issuable_list/components/issuable_item.vue @@ -1,5 +1,5 @@ <script> -import { GlLink, GlIcon, GlLabel, GlTooltipDirective } from '@gitlab/ui'; +import { GlLink, GlIcon, GlLabel, GlFormCheckbox, GlTooltipDirective } from '@gitlab/ui'; import { __, sprintf } from '~/locale'; import { getIdFromGraphQLId } from '~/graphql_shared/utils'; @@ -14,6 +14,7 @@ export default { GlLink, GlIcon, GlLabel, + GlFormCheckbox, IssuableAssignees, }, directives: { @@ -33,6 +34,15 @@ export default { type: Boolean, required: true, }, + showCheckbox: { + type: Boolean, + required: true, + }, + checked: { + type: Boolean, + required: false, + default: false, + }, }, computed: { author() { @@ -109,8 +119,15 @@ export default { </script> <template> - <li class="issue px-3"> + <li class="issue gl-px-5!"> <div class="issue-box"> + <div v-if="showCheckbox" class="issue-check"> + <gl-form-checkbox + class="gl-mr-0" + :checked="checked" + @input="$emit('checked-input', $event)" + /> + </div> <div class="issuable-info-container"> <div class="issuable-main-info"> <div data-testid="issuable-title" class="issue-title title"> diff --git a/app/assets/javascripts/issuable_list/components/issuable_list_root.vue b/app/assets/javascripts/issuable_list/components/issuable_list_root.vue index 7a733ec1318..0ac51c81d6f 100644 --- a/app/assets/javascripts/issuable_list/components/issuable_list_root.vue +++ b/app/assets/javascripts/issuable_list/components/issuable_list_root.vue @@ -1,11 +1,13 @@ <script> import { GlSkeletonLoading, GlPagination } from '@gitlab/ui'; +import { uniqueId } from 'lodash'; import { updateHistory, setUrlParams } from '~/lib/utils/url_utility'; import FilteredSearchBar from '~/vue_shared/components/filtered_search_bar/filtered_search_bar_root.vue'; import IssuableTabs from './issuable_tabs.vue'; import IssuableItem from './issuable_item.vue'; +import IssuableBulkEditSidebar from './issuable_bulk_edit_sidebar.vue'; import { DEFAULT_SKELETON_COUNT } from '../constants'; @@ -15,6 +17,7 @@ export default { IssuableTabs, FilteredSearchBar, IssuableItem, + IssuableBulkEditSidebar, GlPagination, }, props: { @@ -85,6 +88,11 @@ export default { required: false, default: false, }, + showBulkEditSidebar: { + type: Boolean, + required: false, + default: false, + }, defaultPageSize: { type: Number, required: false, @@ -116,6 +124,11 @@ export default { default: true, }, }, + data() { + return { + checkedIssuables: {}, + }; + }, computed: { skeletonItemCount() { const { totalItems, defaultPageSize, currentPage } = this; @@ -128,8 +141,40 @@ export default { } return DEFAULT_SKELETON_COUNT; }, + allIssuablesChecked() { + return this.bulkEditIssuables.length === this.issuables.length; + }, + /** + * Returns all the checked issuables from `checkedIssuables` map. + */ + bulkEditIssuables() { + return Object.keys(this.checkedIssuables).reduce((acc, issuableId) => { + if (this.checkedIssuables[issuableId].checked) { + acc.push(this.checkedIssuables[issuableId].issuable); + } + return acc; + }, []); + }, }, watch: { + issuables(list) { + this.checkedIssuables = list.reduce((acc, issuable) => { + const id = this.issuableId(issuable); + acc[id] = { + // By default, an issuable is not checked, + // But if `checkedIssuables` is already + // populated, use existing value. + checked: + typeof this.checkedIssuables[id] !== 'boolean' + ? false + : this.checkedIssuables[id].checked, + // We're caching issuable reference here + // for ease of populating in `bulkEditIssuables`. + issuable, + }; + return acc; + }, {}); + }, urlParams: { deep: true, immediate: true, @@ -144,6 +189,22 @@ export default { }, }, }, + methods: { + issuableId(issuable) { + return issuable.id || issuable.iid || uniqueId(); + }, + issuableChecked(issuable) { + return this.checkedIssuables[this.issuableId(issuable)]?.checked; + }, + handleIssuableCheckedInput(issuable, value) { + this.checkedIssuables[this.issuableId(issuable)].checked = value; + }, + handleAllIssuablesCheckedInput(value) { + Object.keys(this.checkedIssuables).forEach(issuableId => { + this.checkedIssuables[issuableId].checked = value; + }); + }, + }, }; </script> @@ -167,10 +228,21 @@ export default { :sort-options="sortOptions" :initial-filter-value="initialFilterValue" :initial-sort-by="initialSortBy" + :show-checkbox="showBulkEditSidebar" + :checkbox-checked="allIssuablesChecked" class="gl-flex-grow-1 row-content-block" + @checked-input="handleAllIssuablesCheckedInput" @onFilter="$emit('filter', $event)" @onSort="$emit('sort', $event)" /> + <issuable-bulk-edit-sidebar :expanded="showBulkEditSidebar"> + <template #bulk-edit-actions> + <slot name="bulk-edit-actions" :checked-issuables="bulkEditIssuables"></slot> + </template> + <template #sidebar-items> + <slot name="sidebar-items" :checked-issuables="bulkEditIssuables"></slot> + </template> + </issuable-bulk-edit-sidebar> <div class="issuables-holder"> <ul v-if="issuablesLoading" class="content-list"> <li v-for="n in skeletonItemCount" :key="n" class="issue gl-px-5! gl-py-5!"> @@ -183,10 +255,13 @@ export default { > <issuable-item v-for="issuable in issuables" - :key="issuable.id" + :key="issuableId(issuable)" :issuable-symbol="issuableSymbol" :issuable="issuable" :enable-label-permalinks="enableLabelPermalinks" + :show-checkbox="showBulkEditSidebar" + :checked="issuableChecked(issuable)" + @checked-input="handleIssuableCheckedInput(issuable, $event)" > <template #reference> <slot name="reference" :issuable="issuable"></slot> |