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
path: root/app
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2023-08-04 18:10:27 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2023-08-04 18:10:27 +0300
commitd984d4a092018d86eec724a06ce2e6c066c3fb44 (patch)
treeb72454a613480658d9b2b14b74eb7b8c75858544 /app
parent30a8e054751fe9020a9ed526434db83af35b4718 (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
-rw-r--r--app/assets/javascripts/issues/create_merge_request_dropdown.js36
-rw-r--r--app/assets/javascripts/super_sidebar/components/flyout_menu.vue8
-rw-r--r--app/assets/javascripts/super_sidebar/components/nav_item.vue5
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/widget/app.vue11
-rw-r--r--app/assets/javascripts/vue_shared/components/groups_list/groups_list.stories.js19
-rw-r--r--app/assets/javascripts/vue_shared/components/groups_list/groups_list.vue29
-rw-r--r--app/assets/javascripts/vue_shared/components/groups_list/groups_list_item.vue168
7 files changed, 241 insertions, 35 deletions
diff --git a/app/assets/javascripts/issues/create_merge_request_dropdown.js b/app/assets/javascripts/issues/create_merge_request_dropdown.js
index de0334b4ffe..98888f9f9b2 100644
--- a/app/assets/javascripts/issues/create_merge_request_dropdown.js
+++ b/app/assets/javascripts/issues/create_merge_request_dropdown.js
@@ -137,8 +137,6 @@ export default class CreateMergeRequestDropdown {
this.refInput.addEventListener('keyup', this.onChangeInput.bind(this));
// Detect when user clicks inside the input to apply the suggested ref
this.refInput.addEventListener('click', this.onChangeInput.bind(this));
- // Detect when user clicks outside the input to apply the suggested ref
- this.refInput.addEventListener('blur', this.onChangeInput.bind(this));
// Detect when user presses tab to apply the suggested ref
this.refInput.addEventListener('keydown', CreateMergeRequestDropdown.processTab.bind(this));
}
@@ -178,8 +176,16 @@ export default class CreateMergeRequestDropdown {
createBranch(navigateToBranch = true) {
this.isCreatingBranch = true;
+ const endpoint = createEndpoint(
+ this.projectPath,
+ mergeUrlParams(
+ { ref: this.refInput.value, branch_name: this.branchInput.value },
+ this.createBranchPath,
+ ),
+ );
+
return axios
- .post(createEndpoint(this.projectPath, this.createBranchPath), {
+ .post(endpoint, {
confidential_issue_project_id: canCreateConfidentialMergeRequest() ? this.projectId : null,
})
.then(({ data }) => {
@@ -407,9 +413,6 @@ export default class CreateMergeRequestDropdown {
// If the input is empty, use the original value generated by the backend.
if (!value) {
- this.createBranchPath = this.wrapperEl.dataset.createBranchPath;
- this.createMrPath = this.wrapperEl.dataset.createMrPath;
-
if (target === INPUT_TARGET_BRANCH) {
this.branchIsValid = true;
} else {
@@ -539,7 +542,6 @@ export default class CreateMergeRequestDropdown {
updateBranchName(suggestedBranchName) {
this.branchInput.value = suggestedBranchName;
this.updateInputState(INPUT_TARGET_BRANCH, suggestedBranchName, '');
- this.updateCreatePaths(INPUT_TARGET_BRANCH, suggestedBranchName);
}
updateInputState(target, ref, result) {
@@ -561,7 +563,6 @@ export default class CreateMergeRequestDropdown {
if (ref === result) {
this.refIsValid = true;
this.showAvailableMessage(INPUT_TARGET_REF);
- this.updateCreatePaths(INPUT_TARGET_REF, ref);
} else {
this.refIsValid = false;
this.refInput.dataset.value = ref;
@@ -585,7 +586,6 @@ export default class CreateMergeRequestDropdown {
// Or user typed input contains invalid chars,
// that means a new branch cannot be created as it already exists.
this.showAvailableMessage(INPUT_TARGET_BRANCH, VALIDATION_TYPE_BRANCH_UNAVAILABLE);
- this.updateCreatePaths(INPUT_TARGET_BRANCH, ref);
} else if (isInvalidString) {
this.branchIsValid = false;
this.showNotAvailableMessage(INPUT_TARGET_BRANCH, VALIDATION_TYPE_INVALID_CHARS);
@@ -594,22 +594,4 @@ export default class CreateMergeRequestDropdown {
this.showNotAvailableMessage(INPUT_TARGET_BRANCH);
}
}
-
- // target - 'branch' or 'ref'
- // ref - string - the new value to use as branch or ref
- updateCreatePaths(target, ref) {
- const pathReplacement = `$1${encodeURIComponent(ref)}`;
-
- this.createBranchPath = this.createBranchPath.replace(
- this.regexps[target].createBranchPath,
- pathReplacement,
- );
- this.createMrPath = this.createMrPath.replace(
- this.regexps[target].createMrPath,
- pathReplacement,
- );
-
- this.wrapperEl.dataset.createBranchPath = this.createBranchPath;
- this.wrapperEl.dataset.createMrPath = this.createMrPath;
- }
}
diff --git a/app/assets/javascripts/super_sidebar/components/flyout_menu.vue b/app/assets/javascripts/super_sidebar/components/flyout_menu.vue
index 285fa8ee90b..fa7960da2f4 100644
--- a/app/assets/javascripts/super_sidebar/components/flyout_menu.vue
+++ b/app/assets/javascripts/super_sidebar/components/flyout_menu.vue
@@ -1,5 +1,5 @@
<script>
-import { computePosition, autoUpdate, flip, shift } from '@floating-ui/dom';
+import { computePosition, autoUpdate, offset, flip, shift } from '@floating-ui/dom';
import NavItem from './nav_item.vue';
export default {
@@ -22,7 +22,7 @@ export default {
function updatePosition() {
return computePosition(target, flyout, {
- middleware: [flip(), shift()],
+ middleware: [offset({ alignmentAxis: -12 }), flip(), shift()],
placement: 'right-start',
strategy: 'fixed',
}).then(({ x, y }) => {
@@ -44,13 +44,13 @@ export default {
<template>
<div
:id="`${targetId}-flyout`"
- class="gl-fixed gl-pl-3 gl-z-index-9999 gl-max-h-full gl-overflow-y-auto"
+ class="gl-fixed gl-p-4 gl-mx-n1 gl-z-index-9999 gl-max-h-full gl-overflow-y-auto"
@mouseover="$emit('mouseover')"
@mouseleave="$emit('mouseleave')"
>
<ul
v-if="items.length > 0"
- class="gl-min-w-20 gl-max-w-34 gl-border-1 gl-rounded-base gl-border-solid gl-border-gray-100 gl-shadow gl-bg-white gl-p-2 gl-pb-1 gl-list-style-none"
+ class="gl-min-w-20 gl-max-w-34 gl-border-1 gl-rounded-base gl-border-solid gl-border-gray-100 gl-shadow-md gl-bg-white gl-p-2 gl-pb-1 gl-list-style-none"
>
<nav-item
v-for="item of items"
diff --git a/app/assets/javascripts/super_sidebar/components/nav_item.vue b/app/assets/javascripts/super_sidebar/components/nav_item.vue
index 842893af931..79d476ee74b 100644
--- a/app/assets/javascripts/super_sidebar/components/nav_item.vue
+++ b/app/assets/javascripts/super_sidebar/components/nav_item.vue
@@ -109,7 +109,8 @@ export default {
return {
'gl-px-2 gl-mx-2 gl-line-height-normal': this.isSubitem,
'gl-px-3': !this.isSubitem,
- 'gl-pl-5!': this.isFlyout,
+ 'gl-pl-5! gl-rounded-small': this.isFlyout,
+ 'gl-rounded-base': !this.isFlyout,
[this.item.link_classes]: this.item.link_classes,
...this.linkClasses,
};
@@ -127,7 +128,7 @@ export default {
:is="navItemLinkComponent"
#default="{ isActive }"
v-bind="linkProps"
- class="nav-item-link gl-rounded-base gl-relative gl-display-flex gl-align-items-center gl-min-h-7 gl-gap-3 gl-mb-1 gl-py-2 gl-text-black-normal! gl-hover-bg-t-gray-a-08 gl-focus-bg-t-gray-a-08 gl-text-decoration-none! gl-focus--focus"
+ class="nav-item-link gl-relative gl-display-flex gl-align-items-center gl-min-h-7 gl-gap-3 gl-mb-1 gl-py-2 gl-text-black-normal! gl-hover-bg-t-gray-a-08 gl-focus-bg-t-gray-a-08 gl-text-decoration-none! gl-focus--focus"
:class="computedLinkClasses"
data-qa-selector="nav_item_link"
data-testid="nav-item-link"
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/widget/app.vue b/app/assets/javascripts/vue_merge_request_widget/components/widget/app.vue
index 258fa4edcda..9bb39ba22e0 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/widget/app.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/widget/app.vue
@@ -5,8 +5,9 @@ export default {
import(
'~/vue_merge_request_widget/extensions/security_reports/mr_widget_security_reports.vue'
),
-
MrTerraformWidget: () => import('~/vue_merge_request_widget/extensions/terraform/index.vue'),
+ MrCodeQualityWidget: () =>
+ import('~/vue_merge_request_widget/extensions/code_quality/index.vue'),
},
props: {
@@ -21,8 +22,14 @@ export default {
return this.mr.terraformReportsPath && 'MrTerraformWidget';
},
+ codeQualityWidget() {
+ return this.mr.codequalityReportsPath ? 'MrCodeQualityWidget' : undefined;
+ },
+
widgets() {
- return [this.terraformPlansWidget, 'MrSecurityWidget'].filter((w) => w);
+ return [this.codeQualityWidget, this.terraformPlansWidget, 'MrSecurityWidget'].filter(
+ (w) => w,
+ );
},
},
};
diff --git a/app/assets/javascripts/vue_shared/components/groups_list/groups_list.stories.js b/app/assets/javascripts/vue_shared/components/groups_list/groups_list.stories.js
new file mode 100644
index 00000000000..235523054c3
--- /dev/null
+++ b/app/assets/javascripts/vue_shared/components/groups_list/groups_list.stories.js
@@ -0,0 +1,19 @@
+import { groups } from 'jest/vue_shared/components/groups_list/mock_data';
+import GroupsList from './groups_list.vue';
+
+export default {
+ component: GroupsList,
+ title: 'vue_shared/groups_list',
+};
+
+const Template = (args, { argTypes }) => ({
+ components: { GroupsList },
+ props: Object.keys(argTypes),
+ template: '<groups-list v-bind="$props" />',
+});
+
+export const Default = Template.bind({});
+Default.args = {
+ groups,
+ showGroupIcon: true,
+};
diff --git a/app/assets/javascripts/vue_shared/components/groups_list/groups_list.vue b/app/assets/javascripts/vue_shared/components/groups_list/groups_list.vue
new file mode 100644
index 00000000000..7da45169fee
--- /dev/null
+++ b/app/assets/javascripts/vue_shared/components/groups_list/groups_list.vue
@@ -0,0 +1,29 @@
+<script>
+import GroupsListItem from './groups_list_item.vue';
+
+export default {
+ components: { GroupsListItem },
+ props: {
+ groups: {
+ type: Array,
+ required: true,
+ },
+ showGroupIcon: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ },
+};
+</script>
+
+<template>
+ <ul class="gl-p-0 gl-list-style-none">
+ <groups-list-item
+ v-for="group in groups"
+ :key="group.id"
+ :group="group"
+ :show-group-icon="showGroupIcon"
+ />
+ </ul>
+</template>
diff --git a/app/assets/javascripts/vue_shared/components/groups_list/groups_list_item.vue b/app/assets/javascripts/vue_shared/components/groups_list/groups_list_item.vue
new file mode 100644
index 00000000000..8a301cd0dd0
--- /dev/null
+++ b/app/assets/javascripts/vue_shared/components/groups_list/groups_list_item.vue
@@ -0,0 +1,168 @@
+<script>
+import { GlAvatarLabeled, GlIcon, GlTooltipDirective, GlTruncateText } from '@gitlab/ui';
+
+import { VISIBILITY_TYPE_ICON, GROUP_VISIBILITY_TYPE } from '~/visibility_level/constants';
+import { ACCESS_LEVEL_LABELS } from '~/access_level/constants';
+import UserAccessRoleBadge from '~/vue_shared/components/user_access_role_badge.vue';
+import { __ } from '~/locale';
+import { numberToMetricPrefix } from '~/lib/utils/number_utils';
+import SafeHtml from '~/vue_shared/directives/safe_html';
+
+export default {
+ i18n: {
+ subgroups: __('Subgroups'),
+ projects: __('Projects'),
+ directMembers: __('Direct members'),
+ showMore: __('Show more'),
+ showLess: __('Show less'),
+ },
+ avatarSize: { default: 32, md: 48 },
+ safeHtmlConfig: {
+ ADD_TAGS: ['gl-emoji'],
+ },
+ components: {
+ GlAvatarLabeled,
+ GlIcon,
+ UserAccessRoleBadge,
+ GlTruncateText,
+ },
+ directives: {
+ GlTooltip: GlTooltipDirective,
+ SafeHtml,
+ },
+ props: {
+ group: {
+ type: Object,
+ required: true,
+ },
+ showGroupIcon: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ },
+ computed: {
+ visibility() {
+ return this.group.visibility;
+ },
+ visibilityIcon() {
+ return VISIBILITY_TYPE_ICON[this.visibility];
+ },
+ visibilityTooltip() {
+ return GROUP_VISIBILITY_TYPE[this.visibility];
+ },
+ accessLevel() {
+ return this.group.accessLevel?.integerValue;
+ },
+ accessLevelLabel() {
+ return ACCESS_LEVEL_LABELS[this.accessLevel];
+ },
+ shouldShowAccessLevel() {
+ return this.accessLevel !== undefined;
+ },
+ groupIconName() {
+ return this.group.parent ? 'subgroup' : 'group';
+ },
+ statsPadding() {
+ return this.showGroupIcon ? 'gl-pl-11' : 'gl-pl-8';
+ },
+ descendantGroupsCount() {
+ return numberToMetricPrefix(this.group.descendantGroupsCount);
+ },
+ projectsCount() {
+ return numberToMetricPrefix(this.group.projectsCount);
+ },
+ groupMembersCount() {
+ return numberToMetricPrefix(this.group.groupMembersCount);
+ },
+ },
+};
+</script>
+
+<template>
+ <li class="groups-list-item gl-py-5 gl-md-display-flex gl-align-items-center gl-border-b">
+ <div class="gl-display-flex gl-flex-grow-1">
+ <gl-icon
+ v-if="showGroupIcon"
+ class="gl-mr-3 gl-mt-3 gl-md-mt-5 gl-flex-shrink-0 gl-text-secondary"
+ :name="groupIconName"
+ />
+ <gl-avatar-labeled
+ :entity-id="group.id"
+ :entity-name="group.fullName"
+ :label="group.fullName"
+ :label-link="group.webUrl"
+ shape="rect"
+ :size="$options.avatarSize"
+ >
+ <template #meta>
+ <div class="gl-px-2">
+ <div class="gl-mx-n2 gl-display-flex gl-align-items-center gl-flex-wrap">
+ <div class="gl-px-2">
+ <gl-icon
+ v-if="visibility"
+ v-gl-tooltip="visibilityTooltip"
+ :name="visibilityIcon"
+ class="gl-text-secondary"
+ />
+ </div>
+ <div class="gl-px-2">
+ <user-access-role-badge v-if="shouldShowAccessLevel">{{
+ accessLevelLabel
+ }}</user-access-role-badge>
+ </div>
+ </div>
+ </div>
+ </template>
+ <gl-truncate-text
+ v-if="group.descriptionHtml"
+ :lines="2"
+ :mobile-lines="2"
+ :show-more-text="$options.i18n.showMore"
+ :show-less-text="$options.i18n.showLess"
+ class="gl-mt-2"
+ >
+ <div
+ v-safe-html:[$options.safeHtmlConfig]="group.descriptionHtml"
+ class="gl-font-sm md"
+ data-testid="group-description"
+ ></div>
+ </gl-truncate-text>
+ </gl-avatar-labeled>
+ </div>
+ <div
+ class="gl-md-display-flex gl-flex-direction-column gl-align-items-flex-end gl-flex-shrink-0 gl-mt-3 gl-md-pl-0 gl-md-mt-0 gl-md-ml-3"
+ :class="statsPadding"
+ >
+ <div class="gl-display-flex gl-align-items-center gl-gap-x-3">
+ <div
+ v-gl-tooltip="$options.i18n.subgroups"
+ :aria-label="$options.i18n.subgroups"
+ class="gl-text-secondary"
+ data-testid="subgroups-count"
+ >
+ <gl-icon name="subgroup" />
+ <span>{{ descendantGroupsCount }}</span>
+ </div>
+ <div
+ v-gl-tooltip="$options.i18n.projects"
+ :aria-label="$options.i18n.projects"
+ class="gl-text-secondary"
+ data-testid="projects-count"
+ >
+ <gl-icon name="project" />
+ <span>{{ projectsCount }}</span>
+ </div>
+ <div
+ v-gl-tooltip="$options.i18n.directMembers"
+ :aria-label="$options.i18n.directMembers"
+ class="gl-text-secondary"
+ data-testid="members-count"
+ >
+ <gl-icon name="users" />
+ <span>{{ groupMembersCount }}</span>
+ </div>
+ </div>
+ </div>
+ </li>
+</template>