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>2022-10-31 18:11:37 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2022-10-31 18:11:37 +0300
commita5519693560d1ac4e120e1afd7d806d13a2d09fd (patch)
tree7aadd6e5ddf3b5f19bc444714f63db8dd1001633 /app/assets/javascripts/sidebar
parent004274926a034decf20948c34e290267dffbdfde (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app/assets/javascripts/sidebar')
-rw-r--r--app/assets/javascripts/sidebar/components/sidebar_dropdown.vue240
-rw-r--r--app/assets/javascripts/sidebar/components/sidebar_dropdown_widget.vue185
2 files changed, 263 insertions, 162 deletions
diff --git a/app/assets/javascripts/sidebar/components/sidebar_dropdown.vue b/app/assets/javascripts/sidebar/components/sidebar_dropdown.vue
new file mode 100644
index 00000000000..60180d6d44a
--- /dev/null
+++ b/app/assets/javascripts/sidebar/components/sidebar_dropdown.vue
@@ -0,0 +1,240 @@
+<script>
+import {
+ GlDropdown,
+ GlDropdownDivider,
+ GlDropdownItem,
+ GlDropdownText,
+ GlLoadingIcon,
+ GlSearchBoxByType,
+} from '@gitlab/ui';
+import { kebabCase, snakeCase } from 'lodash';
+import { IssuableType } from '~/issues/constants';
+import { __ } from '~/locale';
+import {
+ defaultEpicSort,
+ dropdowni18nText,
+ epicIidPattern,
+ issuableAttributesQueries,
+ IssuableAttributeState,
+ IssuableAttributeType,
+ IssuableAttributeTypeKeyMap,
+ LocalizedIssuableAttributeType,
+ noAttributeId,
+} from 'ee_else_ce/sidebar/constants';
+import { createAlert } from '~/flash';
+import { PathIdSeparator } from '~/related_issues/constants';
+
+export default {
+ noAttributeId,
+ i18n: {
+ expired: __('(expired)'),
+ },
+ components: {
+ GlDropdown,
+ GlDropdownItem,
+ GlDropdownText,
+ GlDropdownDivider,
+ GlSearchBoxByType,
+ GlLoadingIcon,
+ },
+ inject: {
+ issuableAttributesQueries: {
+ default: issuableAttributesQueries,
+ },
+ issuableAttributesState: {
+ default: IssuableAttributeState,
+ },
+ widgetTitleText: {
+ default: {
+ [IssuableAttributeType.Milestone]: __('Milestone'),
+ expired: __('(expired)'),
+ none: __('None'),
+ },
+ },
+ },
+ props: {
+ attrWorkspacePath: {
+ required: true,
+ type: String,
+ },
+ currentAttribute: {
+ type: Object,
+ required: false,
+ default: () => ({}),
+ },
+ issuableAttribute: {
+ type: String,
+ required: true,
+ },
+ issuableType: {
+ type: String,
+ required: true,
+ validator(value) {
+ return [IssuableType.Issue, IssuableType.MergeRequest].includes(value);
+ },
+ },
+ },
+ data() {
+ return {
+ attributesList: [],
+ searchTerm: '',
+ skipQuery: true,
+ };
+ },
+ apollo: {
+ attributesList: {
+ query() {
+ const { list } = this.issuableAttributeQuery;
+ const { query } = list[this.issuableType];
+ return query;
+ },
+ variables() {
+ if (!this.isEpic) {
+ return {
+ fullPath: this.attrWorkspacePath,
+ title: this.searchTerm,
+ state: this.issuableAttributesState[this.issuableAttribute],
+ };
+ }
+
+ const variables = {
+ fullPath: this.attrWorkspacePath,
+ state: this.issuableAttributesState[this.issuableAttribute],
+ sort: defaultEpicSort,
+ };
+
+ if (epicIidPattern.test(this.searchTerm)) {
+ const matches = this.searchTerm.match(epicIidPattern);
+ variables.iidStartsWith = matches.groups.iid;
+ } else if (this.searchTerm !== '') {
+ variables.in = 'TITLE';
+ variables.title = this.searchTerm;
+ }
+
+ return variables;
+ },
+ update: (data) => data?.workspace?.attributes?.nodes ?? [],
+ error(error) {
+ createAlert({ message: this.i18n.listFetchError, captureError: true, error });
+ },
+ skip() {
+ if (
+ this.isEpic &&
+ this.searchTerm.startsWith(PathIdSeparator.Epic) &&
+ this.searchTerm.length < 2
+ ) {
+ return true;
+ }
+ return this.skipQuery;
+ },
+ debounce: 250,
+ },
+ },
+ computed: {
+ attributeTypeTitle() {
+ return this.widgetTitleText[this.issuableAttribute];
+ },
+ dropdownText() {
+ return this.currentAttribute ? this.currentAttribute?.title : this.attributeTypeTitle;
+ },
+ emptyPropsList() {
+ return this.attributesList.length === 0;
+ },
+ i18n() {
+ const localizedAttribute =
+ LocalizedIssuableAttributeType[IssuableAttributeTypeKeyMap[this.issuableAttribute]];
+ return dropdowni18nText(localizedAttribute, this.issuableType);
+ },
+ isEpic() {
+ // MV to EE https://gitlab.com/gitlab-org/gitlab/-/issues/345311
+ return this.issuableAttribute === IssuableType.Epic;
+ },
+ issuableAttributeQuery() {
+ return this.issuableAttributesQueries[this.issuableAttribute];
+ },
+ formatIssuableAttribute() {
+ return {
+ kebab: kebabCase(this.issuableAttribute),
+ snake: snakeCase(this.issuableAttribute),
+ };
+ },
+ },
+ methods: {
+ isAttributeChecked(attributeId) {
+ return (
+ attributeId === this.currentAttribute?.id || (!this.currentAttribute?.id && !attributeId)
+ );
+ },
+ isAttributeOverdue(attribute) {
+ return this.issuableAttribute === IssuableAttributeType.Milestone
+ ? attribute?.expired
+ : false;
+ },
+ handleShow() {
+ this.skipQuery = false;
+ },
+ setFocus() {
+ this.$refs.search.focusInput();
+ },
+ show() {
+ this.$refs.dropdown.show();
+ },
+ updateAttribute(attribute) {
+ this.$emit('change', attribute);
+ },
+ },
+};
+</script>
+
+<template>
+ <gl-dropdown
+ ref="dropdown"
+ block
+ :header-text="i18n.assignAttribute"
+ lazy
+ :text="dropdownText"
+ @show="handleShow"
+ @shown="setFocus"
+ >
+ <gl-search-box-by-type ref="search" v-model="searchTerm" :placeholder="__('Search')" />
+ <gl-dropdown-item
+ :data-testid="`no-${formatIssuableAttribute.kebab}-item`"
+ is-check-item
+ :is-checked="isAttributeChecked($options.noAttributeId)"
+ @click="$emit('change', { id: $options.noAttributeId })"
+ >
+ {{ i18n.noAttribute }}
+ </gl-dropdown-item>
+ <gl-dropdown-divider />
+ <gl-loading-icon
+ v-if="$apollo.queries.attributesList.loading"
+ size="sm"
+ class="gl-py-4"
+ data-testid="loading-icon-dropdown"
+ />
+ <template v-else>
+ <gl-dropdown-text v-if="emptyPropsList">
+ {{ i18n.noAttributesFound }}
+ </gl-dropdown-text>
+ <slot
+ v-else
+ name="list"
+ :attributes-list="attributesList"
+ :is-attribute-checked="isAttributeChecked"
+ :update-attribute="updateAttribute"
+ >
+ <gl-dropdown-item
+ v-for="attrItem in attributesList"
+ :key="attrItem.id"
+ is-check-item
+ :is-checked="isAttributeChecked(attrItem.id)"
+ :data-testid="`${formatIssuableAttribute.kebab}-items`"
+ @click="updateAttribute(attrItem)"
+ >
+ {{ attrItem.title }}
+ <template v-if="isAttributeOverdue(attrItem)">{{ $options.i18n.expired }}</template>
+ </gl-dropdown-item>
+ </slot>
+ </template>
+ </gl-dropdown>
+</template>
diff --git a/app/assets/javascripts/sidebar/components/sidebar_dropdown_widget.vue b/app/assets/javascripts/sidebar/components/sidebar_dropdown_widget.vue
index 7f0def725a2..a685929cdea 100644
--- a/app/assets/javascripts/sidebar/components/sidebar_dropdown_widget.vue
+++ b/app/assets/javascripts/sidebar/components/sidebar_dropdown_widget.vue
@@ -1,17 +1,5 @@
<script>
-import {
- GlLink,
- GlDropdown,
- GlDropdownItem,
- GlDropdownText,
- GlSearchBoxByType,
- GlDropdownDivider,
- GlLoadingIcon,
- GlIcon,
- GlTooltipDirective,
- GlPopover,
- GlButton,
-} from '@gitlab/ui';
+import { GlButton, GlIcon, GlLink, GlPopover, GlTooltipDirective } from '@gitlab/ui';
import { kebabCase, snakeCase } from 'lodash';
import { createAlert } from '~/flash';
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
@@ -22,19 +10,15 @@ import SidebarEditableItem from '~/sidebar/components/sidebar_editable_item.vue'
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import {
dropdowni18nText,
- Tracking,
- IssuableAttributeState,
- IssuableAttributeType,
LocalizedIssuableAttributeType,
IssuableAttributeTypeKeyMap,
issuableAttributesQueries,
- noAttributeId,
- defaultEpicSort,
- epicIidPattern,
+ IssuableAttributeType,
+ Tracking,
} from 'ee_else_ce/sidebar/constants';
+import SidebarDropdown from './sidebar_dropdown.vue';
export default {
- noAttributeId,
i18n: {
expired: __('(expired)'),
none: __('None'),
@@ -43,17 +27,12 @@ export default {
GlTooltip: GlTooltipDirective,
},
components: {
- SidebarEditableItem,
GlLink,
- GlDropdown,
- GlDropdownItem,
- GlDropdownText,
- GlDropdownDivider,
- GlSearchBoxByType,
GlIcon,
- GlLoadingIcon,
GlPopover,
GlButton,
+ SidebarDropdown,
+ SidebarEditableItem,
},
mixins: [glFeatureFlagMixin()],
inject: {
@@ -63,9 +42,6 @@ export default {
issuableAttributesQueries: {
default: issuableAttributesQueries,
},
- issuableAttributesState: {
- default: IssuableAttributeState,
- },
widgetTitleText: {
default: {
[IssuableAttributeType.Milestone]: __('Milestone'),
@@ -74,7 +50,6 @@ export default {
},
},
},
-
props: {
issuableAttribute: {
type: String,
@@ -134,67 +109,14 @@ export default {
});
},
},
- attributesList: {
- query() {
- const { list } = this.issuableAttributeQuery;
- const { query } = list[this.issuableType];
-
- return query;
- },
- skip() {
- if (this.isEpic && this.searchTerm.startsWith('&') && this.searchTerm.length < 2) {
- return true;
- }
-
- return !this.editing;
- },
- debounce: 250,
- variables() {
- if (!this.isEpic) {
- return {
- fullPath: this.attrWorkspacePath,
- title: this.searchTerm,
- state: this.issuableAttributesState[this.issuableAttribute],
- };
- }
-
- const variables = {
- fullPath: this.attrWorkspacePath,
- state: this.issuableAttributesState[this.issuableAttribute],
- sort: defaultEpicSort,
- };
-
- if (epicIidPattern.test(this.searchTerm)) {
- const matches = this.searchTerm.match(epicIidPattern);
- variables.iidStartsWith = matches.groups.iid;
- } else if (this.searchTerm !== '') {
- variables.in = 'TITLE';
- variables.title = this.searchTerm;
- }
-
- return variables;
- },
- update(data) {
- if (data?.workspace) {
- return data?.workspace?.attributes.nodes;
- }
- return [];
- },
- error(error) {
- createAlert({ message: this.i18n.listFetchError, captureError: true, error });
- },
- },
},
data() {
return {
- searchTerm: '',
- editing: false,
updating: false,
selectedTitle: null,
currentAttribute: null,
hasCurrentAttribute: false,
editConfirmation: false,
- attributesList: [],
tracking: {
event: Tracking.editEvent,
label: Tracking.rightSidebarLabel,
@@ -212,15 +134,9 @@ export default {
attributeUrl() {
return this.currentAttribute?.webUrl;
},
- dropdownText() {
- return this.currentAttribute ? this.currentAttribute?.title : this.attributeTypeTitle;
- },
loading() {
return this.$apollo.queries.currentAttribute.loading;
},
- emptyPropsList() {
- return this.attributesList.length === 0;
- },
attributeTypeTitle() {
return this.widgetTitleText[this.issuableAttribute];
},
@@ -256,16 +172,12 @@ export default {
},
},
methods: {
- updateAttribute(attributeId) {
- if (this.currentAttribute === null && attributeId === null) return;
- if (attributeId === this.currentAttribute?.id) return;
+ updateAttribute({ id }) {
+ if (this.currentAttribute === null && id === null) return;
+ if (id === this.currentAttribute?.id) return;
this.updating = true;
- const selectedAttribute =
- Boolean(attributeId) && this.attributesList.find((p) => p.id === attributeId);
- this.selectedTitle = selectedAttribute ? selectedAttribute.title : this.widgetTitleText.none;
-
const { current } = this.issuableAttributeQuery;
const { mutation } = current[this.issuableType];
@@ -277,8 +189,8 @@ export default {
attributeId:
this.issuableAttribute === IssuableAttributeType.Milestone &&
this.issuableType === IssuableType.Issue
- ? getIdFromGraphQLId(attributeId)
- : attributeId,
+ ? getIdFromGraphQLId(id)
+ : id,
iid: this.iid,
},
})
@@ -298,32 +210,16 @@ export default {
})
.finally(() => {
this.updating = false;
- this.searchTerm = '';
this.selectedTitle = null;
});
},
- isAttributeChecked(attributeId = undefined) {
- return (
- attributeId === this.currentAttribute?.id || (!this.currentAttribute?.id && !attributeId)
- );
- },
isAttributeOverdue(attribute) {
return this.issuableAttribute === IssuableAttributeType.Milestone
? attribute?.expired
: false;
},
showDropdown() {
- this.$refs.newDropdown.show();
- },
- handleOpen() {
- this.editing = true;
- this.showDropdown();
- },
- handleClose() {
- this.editing = false;
- },
- setFocus() {
- this.$refs.search.focusInput();
+ this.$refs.dropdown.show();
},
handlePopoverClose() {
this.$refs.popover.$emit('close');
@@ -349,8 +245,7 @@ export default {
:tracking="tracking"
:should-show-confirmation-popover="shouldShowConfirmationPopover"
:loading="updating || loading"
- @open="handleOpen"
- @close="handleClose"
+ @open="showDropdown"
@edit-confirm="handleEditConfirmation"
>
<template #collapsed>
@@ -432,58 +327,24 @@ export default {
</gl-popover>
</template>
<template v-else #default>
- <gl-dropdown
- ref="newDropdown"
- lazy
- :header-text="i18n.assignAttribute"
- :text="dropdownText"
- :loading="loading"
- class="gl-w-full"
- toggle-class="gl-max-w-100"
- block
- @shown="setFocus"
+ <sidebar-dropdown
+ ref="dropdown"
+ :attr-workspace-path="attrWorkspacePath"
+ :current-attribute="currentAttribute"
+ :issuable-attribute="issuableAttribute"
+ :issuable-type="issuableType"
+ @change="updateAttribute"
>
- <gl-search-box-by-type ref="search" v-model="searchTerm" :placeholder="__('Search')" />
- <gl-dropdown-item
- :data-testid="`no-${formatIssuableAttribute.kebab}-item`"
- is-check-item
- :is-checked="isAttributeChecked($options.noAttributeId)"
- @click="updateAttribute($options.noAttributeId)"
- >
- {{ i18n.noAttribute }}
- </gl-dropdown-item>
- <gl-dropdown-divider />
- <gl-loading-icon
- v-if="$apollo.queries.attributesList.loading"
- size="sm"
- class="gl-py-4"
- data-testid="loading-icon-dropdown"
- />
- <template v-else>
- <gl-dropdown-text v-if="emptyPropsList">
- {{ i18n.noAttributesFound }}
- </gl-dropdown-text>
+ <template #list="{ attributesList, isAttributeChecked, updateAttribute: update }">
<slot
- v-else
name="list"
:attributes-list="attributesList"
:is-attribute-checked="isAttributeChecked"
- :update-attribute="updateAttribute"
+ :update-attribute="update"
>
- <gl-dropdown-item
- v-for="attrItem in attributesList"
- :key="attrItem.id"
- is-check-item
- :is-checked="isAttributeChecked(attrItem.id)"
- :data-testid="`${formatIssuableAttribute.kebab}-items`"
- @click="updateAttribute(attrItem.id)"
- >
- {{ attrItem.title }}
- <span v-if="isAttributeOverdue(attrItem)">{{ $options.i18n.expired }}</span>
- </gl-dropdown-item>
</slot>
</template>
- </gl-dropdown>
+ </sidebar-dropdown>
</template>
</sidebar-editable-item>
</template>