Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-11-06 00:08:51 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2020-11-06 00:08:51 +0300
commit1bc5af76617026dec53016cb0881ec834ccab807 (patch)
treed0bd460cfcf415248441a1ad79a8bf76b87aa27d /app/assets/javascripts/issuable_list
parent3e1c760141a27097d74d191a619fa6edecd86fe7 (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app/assets/javascripts/issuable_list')
-rw-r--r--app/assets/javascripts/issuable_list/components/issuable_bulk_edit_sidebar.vue35
-rw-r--r--app/assets/javascripts/issuable_list/components/issuable_item.vue21
-rw-r--r--app/assets/javascripts/issuable_list/components/issuable_list_root.vue77
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>