Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2020-08-27 09:10:18 +00:00
parent 2d075756ce
commit 1e5ef4fb10
17 changed files with 207 additions and 104 deletions

View file

@ -4,6 +4,9 @@ import {
GlLoadingIcon,
GlTable,
GlAlert,
GlAvatarsInline,
GlAvatarLink,
GlAvatar,
GlIcon,
GlLink,
GlTabs,
@ -12,6 +15,7 @@ import {
GlPagination,
GlSearchBoxByType,
GlSprintf,
GlTooltipDirective,
} from '@gitlab/ui';
import { debounce, trim } from 'lodash';
import { __, s__ } from '~/locale';
@ -57,6 +61,7 @@ export default {
"AlertManagement|There was an error displaying the alerts. Confirm your endpoint's configuration details to ensure alerts appear.",
),
searchPlaceholder: __('Search or filter results...'),
unassigned: __('Unassigned'),
},
fields: [
{
@ -113,6 +118,9 @@ export default {
GlLoadingIcon,
GlTable,
GlAlert,
GlAvatarsInline,
GlAvatarLink,
GlAvatar,
TimeAgo,
GlIcon,
GlLink,
@ -124,6 +132,9 @@ export default {
GlSprintf,
AlertStatus,
},
directives: {
GlTooltip: GlTooltipDirective,
},
props: {
projectPath: {
type: String,
@ -267,11 +278,8 @@ export default {
const { category, action, label } = trackAlertStatusUpdateOptions;
Tracking.event(category, action, { label, property: status });
},
getAssignees(assignees) {
// TODO: Update to show list of assignee(s) after https://gitlab.com/gitlab-org/gitlab/-/issues/218405
return assignees.nodes?.length > 0
? assignees.nodes[0]?.username
: s__('AlertManagement|Unassigned');
hasAssignees(assignees) {
return Boolean(assignees.nodes?.length);
},
getIssueLink(item) {
return joinPaths('/', this.projectPath, '-', 'issues', item.issueIid);
@ -418,8 +426,32 @@ export default {
</template>
<template #cell(assignees)="{ item }">
<div class="gl-max-w-full text-truncate" data-testid="assigneesField">
{{ getAssignees(item.assignees) }}
<div data-testid="assigneesField">
<template v-if="hasAssignees(item.assignees)">
<gl-avatars-inline
:avatars="item.assignees.nodes"
:collapsed="true"
:max-visible="4"
:avatar-size="24"
badge-tooltip-prop="name"
:badge-tooltip-max-chars="100"
>
<template #avatar="{ avatar }">
<gl-avatar-link
:key="avatar.username"
v-gl-tooltip
target="_blank"
:href="avatar.webUrl"
:title="avatar.name"
>
<gl-avatar :src="avatar.avatarUrl" :label="avatar.name" :size="24" />
</gl-avatar-link>
</template>
</gl-avatars-inline>
</template>
<template v-else>
{{ $options.i18n.unassigned }}
</template>
</div>
</template>

View file

@ -12,7 +12,7 @@ import {
} from '@gitlab/ui';
import { debounce } from 'lodash';
import axios from '~/lib/utils/axios_utils';
import { s__, __ } from '~/locale';
import { s__ } from '~/locale';
import alertSetAssignees from '../../graphql/mutations/alert_set_assignees.mutation.graphql';
import SidebarAssignee from './sidebar_assignee.vue';
@ -82,8 +82,11 @@ export default {
userName() {
return this.alert?.assignees?.nodes[0]?.username;
},
assignedUser() {
return this.userName || __('None');
userFullName() {
return this.alert?.assignees?.nodes[0]?.name;
},
userImg() {
return this.alert?.assignees?.nodes[0]?.avatarUrl;
},
sortedUsers() {
return this.users
@ -184,15 +187,15 @@ export default {
</script>
<template>
<div class="block alert-status">
<div ref="status" class="sidebar-collapsed-icon" @click="$emit('toggle-sidebar')">
<div class="block alert-assignees ">
<div ref="assignees" class="sidebar-collapsed-icon" @click="$emit('toggle-sidebar')">
<gl-icon name="user" :size="14" />
<gl-loading-icon v-if="isUpdating" />
</div>
<gl-tooltip :target="() => $refs.status" boundary="viewport" placement="left">
<gl-tooltip :target="() => $refs.assignees" boundary="viewport" placement="left">
<gl-sprintf :message="$options.i18n.ASSIGNEES_BLOCK">
<template #assignees>
{{ assignedUser }}
{{ userName }}
</template>
</gl-sprintf>
</gl-tooltip>
@ -215,7 +218,7 @@ export default {
<div class="dropdown dropdown-menu-selectable" :class="dropdownClass">
<gl-deprecated-dropdown
ref="dropdown"
:text="assignedUser"
:text="userName"
class="w-100"
toggle-class="dropdown-menu-toggle"
variant="outline-default"
@ -272,14 +275,28 @@ export default {
</div>
<gl-loading-icon v-if="isUpdating" :inline="true" />
<p v-else-if="!isDropdownShowing" class="value gl-m-0" :class="{ 'no-value': !userName }">
<span v-if="userName" class="gl-text-gray-500" data-testid="assigned-users">{{
assignedUser
}}</span>
<span v-else class="gl-display-flex gl-align-items-center">
<div v-else-if="!isDropdownShowing" class="value gl-m-0" :class="{ 'no-value': !userName }">
<div v-if="userName" class="gl-display-inline-flex gl-mt-2" data-testid="assigned-users">
<span class="gl-relative mr-2">
<img
:alt="userName"
:src="userImg"
:width="32"
class="avatar avatar-inline gl-m-0 s32"
data-qa-selector="avatar_image"
/>
</span>
<span class="gl-display-flex gl-flex-direction-column gl-overflow-hidden">
<strong class="dropdown-menu-user-full-name">
{{ userFullName }}
</strong>
<span class="dropdown-menu-user-username">{{ userName }}</span>
</span>
</div>
<span v-else class="gl-display-flex gl-align-items-center gl-line-height-normal">
{{ __('None') }} -
<gl-button
class="gl-pl-2"
class="gl-ml-2"
href="#"
variant="link"
data-testid="unassigned-users"
@ -288,7 +305,7 @@ export default {
{{ __('assign yourself') }}
</gl-button>
</span>
</p>
</div>
</div>
</div>
</template>

View file

@ -8,7 +8,10 @@ fragment AlertListItem on AlertManagementAlert {
issueIid
assignees {
nodes {
name
username
avatarUrl
webUrl
}
}
}

View file

@ -10,6 +10,9 @@ mutation alertSetAssignees($projectPath: ID!, $assigneeUsernames: [String!]!, $i
assignees {
nodes {
username
name
avatarUrl
webUrl
}
}
notes {

View file

@ -2,10 +2,9 @@
/* eslint-disable vue/no-v-html */
import { escape, debounce } from 'lodash';
import { mapActions, mapState } from 'vuex';
import { GlLoadingIcon, GlFormInput, GlFormGroup } from '@gitlab/ui';
import { GlLoadingIcon, GlFormInput, GlFormGroup, GlButton } from '@gitlab/ui';
import { deprecatedCreateFlash as createFlash } from '~/flash';
import { s__, sprintf } from '~/locale';
import LoadingButton from '~/vue_shared/components/loading_button.vue';
import createEmptyBadge from '../empty_badge';
import Badge from './badge.vue';
@ -15,7 +14,7 @@ export default {
name: 'BadgeForm',
components: {
Badge,
LoadingButton,
GlButton,
GlLoadingIcon,
GlFormInput,
GlFormGroup,
@ -220,23 +219,23 @@ export default {
</div>
<div v-if="isEditing" class="row-content-block gl-display-flex gl-justify-content-end">
<button class="btn btn-cancel gl-mr-4" type="button" @click="onCancel">
<gl-button class="btn-cancel gl-mr-4" data-testid="cancelEditing" @click="onCancel">
{{ __('Cancel') }}
</button>
<loading-button
</gl-button>
<gl-button
:loading="isSaving"
:label="s__('Badges|Save changes')"
type="submit"
container-class="btn btn-success"
/>
variant="success"
category="primary"
data-testid="saveEditing"
>
{{ s__('Badges|Save changes') }}
</gl-button>
</div>
<div v-else class="gl-display-flex gl-justify-content-end form-group">
<loading-button
:loading="isSaving"
:label="s__('Badges|Add badge')"
type="submit"
container-class="btn btn-success"
/>
<gl-button :loading="isSaving" type="submit" variant="success" category="primary">
{{ s__('Badges|Add badge') }}
</gl-button>
</div>
</form>
</template>

View file

@ -2,7 +2,7 @@
import { mapState, mapActions } from 'vuex';
import { GlIcon } from '@gitlab/ui';
import { deprecatedCreateFlash as createFlash } from '~/flash';
import { s__ } from '~/locale';
import { s__, sprintf } from '~/locale';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { UNFOLD_COUNT, INLINE_DIFF_VIEW_TYPE, PARALLEL_DIFF_VIEW_TYPE } from '../constants';
import * as utils from '../store/utils';
@ -18,11 +18,16 @@ const lineNumberByViewType = (viewType, diffLine) => {
[PARALLEL_DIFF_VIEW_TYPE]: line => (line?.right || line?.left)?.new_line,
};
const numberGetter = numberGetters[viewType];
return numberGetter && numberGetter(diffLine);
};
const i18n = {
showMore: sprintf(s__('Diffs|Show %{unfoldCount} lines'), { unfoldCount: UNFOLD_COUNT }),
showAll: s__('Diffs|Show all unchanged lines'),
};
export default {
i18n,
directives: {
tooltip,
},
@ -232,32 +237,27 @@ export default {
</script>
<template>
<td :colspan="colspan" class="text-center">
<td :colspan="colspan" class="text-center gl-font-regular">
<div class="content js-line-expansion-content">
<a
v-if="canExpandUp"
v-tooltip
class="cursor-pointer js-unfold unfold-icon d-inline-block pt-2 pb-2"
data-placement="top"
data-container="body"
:title="__('Expand up')"
@click="handleExpandLines(EXPAND_UP)"
>
<gl-icon :size="12" name="expand-up" aria-hidden="true" />
</a>
<a class="mx-2 cursor-pointer js-unfold-all" @click="handleExpandLines()">
<span>{{ s__('Diffs|Show unchanged lines') }}</span>
</a>
<a
v-if="canExpandDown"
v-tooltip
class="cursor-pointer js-unfold-down has-tooltip unfold-icon d-inline-block pt-2 pb-2"
data-placement="top"
data-container="body"
:title="__('Expand down')"
class="gl-mx-2 gl-cursor-pointer js-unfold-down gl-display-inline-block gl-py-4"
@click="handleExpandLines(EXPAND_DOWN)"
>
<gl-icon :size="12" name="expand-down" aria-hidden="true" />
<span>{{ $options.i18n.showMore }}</span>
</a>
<a class="gl-mx-2 cursor-pointer js-unfold-all" @click="handleExpandLines()">
<gl-icon :size="12" name="expand" aria-hidden="true" />
<span>{{ $options.i18n.showAll }}</span>
</a>
<a
v-if="canExpandUp"
class="gl-mx-2 gl-cursor-pointer js-unfold gl-display-inline-block gl-py-4"
@click="handleExpandLines(EXPAND_UP)"
>
<gl-icon :size="12" name="expand-up" aria-hidden="true" />
<span>{{ $options.i18n.showMore }}</span>
</a>
</div>
</td>

View file

@ -28,8 +28,8 @@ export default function initFrequentItemDropdowns() {
return;
}
$(navEl).on('shown.bs.dropdown', () =>
import('./components/app.vue').then(({ default: FrequentItems }) => {
import('./components/app.vue')
.then(({ default: FrequentItems }) => {
// eslint-disable-next-line no-new
new Vue({
el,
@ -59,9 +59,11 @@ export default function initFrequentItemDropdowns() {
});
},
});
})
.catch(() => {});
eventHub.$emit(`${namespace}-dropdownOpen`);
}),
);
$(navEl).on('shown.bs.dropdown', () => {
eventHub.$emit(`${namespace}-dropdownOpen`);
});
});
}

View file

@ -0,0 +1,5 @@
---
title: Re-order diff unfold buttons so that “show more lines above” appears first
merge_request: 39060
author:
type: changed

View file

@ -0,0 +1,5 @@
---
title: Add Alert Management assignee avatar for list and details view
merge_request: 40275
author:
type: changed

View file

@ -0,0 +1,5 @@
---
title: Replacing deprecated buttons and loading buttons with new buttons
merge_request: 40163
author:
type: other

View file

@ -0,0 +1,5 @@
---
title: Fix tracking of frequently visited projects and groups
merge_request: 40415
author:
type: fixed

View file

@ -2252,9 +2252,6 @@ msgstr ""
msgid "AlertManagement|Triggered"
msgstr ""
msgid "AlertManagement|Unassigned"
msgstr ""
msgid "AlertManagement|Unknown"
msgstr ""
@ -8601,7 +8598,10 @@ msgstr ""
msgid "Diffs|No file name available"
msgstr ""
msgid "Diffs|Show unchanged lines"
msgid "Diffs|Show %{unfoldCount} lines"
msgstr ""
msgid "Diffs|Show all unchanged lines"
msgstr ""
msgid "Diffs|Something went wrong while fetching diff lines."
@ -10112,9 +10112,6 @@ msgstr ""
msgid "Expand approvers"
msgstr ""
msgid "Expand down"
msgstr ""
msgid "Expand dropdown"
msgstr ""
@ -10124,9 +10121,6 @@ msgstr ""
msgid "Expand sidebar"
msgstr ""
msgid "Expand up"
msgstr ""
msgid "Expected documents: %{expected_documents}"
msgstr ""

View file

@ -11,6 +11,7 @@ import {
GlBadge,
GlPagination,
GlSearchBoxByType,
GlAvatar,
} from '@gitlab/ui';
import waitForPromises from 'helpers/wait_for_promises';
import { visitUrl } from '~/lib/utils/url_utility';
@ -268,18 +269,22 @@ describe('AlertManagementTable', () => {
).toBe('Unassigned');
});
it('renders username(s) when assignee(s) present', () => {
it('renders user avatar when assignee present', () => {
mountComponent({
props: { alertManagementEnabled: true, userCanEnableAlertManagement: true },
data: { alerts: { list: mockAlerts }, alertsCount, hasError: false },
loading: false,
});
expect(
findAssignees()
.at(1)
.text(),
).toBe(mockAlerts[1].assignees.nodes[0].username);
const avatar = findAssignees()
.at(1)
.find(GlAvatar);
const { src, label } = avatar.attributes();
const { name, avatarUrl } = mockAlerts[1].assignees.nodes[0];
expect(avatar.exists()).toBe(true);
expect(label).toBe(name);
expect(src).toBe(avatarUrl);
});
it('navigates to the detail page when alert row is clicked', () => {

View file

@ -56,6 +56,9 @@ describe('Alert Details Sidebar Assignees', () => {
mock.restore();
});
const findAssigned = () => wrapper.find('[data-testid="assigned-users"]');
const findUnassigned = () => wrapper.find('[data-testid="unassigned-users"]');
describe('updating the alert status', () => {
const mockUpdatedMutationResult = {
data: {
@ -100,28 +103,26 @@ describe('Alert Details Sidebar Assignees', () => {
});
});
it('renders a unassigned option', () => {
it('renders a unassigned option', async () => {
wrapper.setData({ isDropdownSearching: false });
return wrapper.vm.$nextTick().then(() => {
expect(wrapper.find(GlDeprecatedDropdownItem).text()).toBe('Unassigned');
});
await wrapper.vm.$nextTick();
expect(wrapper.find(GlDeprecatedDropdownItem).text()).toBe('Unassigned');
});
it('calls `$apollo.mutate` with `AlertSetAssignees` mutation and variables containing `iid`, `assigneeUsernames`, & `projectPath`', () => {
it('calls `$apollo.mutate` with `AlertSetAssignees` mutation and variables containing `iid`, `assigneeUsernames`, & `projectPath`', async () => {
jest.spyOn(wrapper.vm.$apollo, 'mutate').mockResolvedValue(mockUpdatedMutationResult);
wrapper.setData({ isDropdownSearching: false });
return wrapper.vm.$nextTick().then(() => {
wrapper.find(SidebarAssignee).vm.$emit('update-alert-assignees', 'root');
await wrapper.vm.$nextTick();
wrapper.find(SidebarAssignee).vm.$emit('update-alert-assignees', 'root');
expect(wrapper.vm.$apollo.mutate).toHaveBeenCalledWith({
mutation: AlertSetAssignees,
variables: {
iid: '1527542',
assigneeUsernames: ['root'],
projectPath: 'projectPath',
},
});
expect(wrapper.vm.$apollo.mutate).toHaveBeenCalledWith({
mutation: AlertSetAssignees,
variables: {
iid: '1527542',
assigneeUsernames: ['root'],
projectPath: 'projectPath',
},
});
});
@ -151,7 +152,34 @@ describe('Alert Details Sidebar Assignees', () => {
it('stops updating and cancels loading when the request fails', () => {
jest.spyOn(wrapper.vm.$apollo, 'mutate').mockReturnValue(Promise.reject(new Error()));
wrapper.vm.updateAlertAssignees('root');
expect(wrapper.find('[data-testid="unassigned-users"]').text()).toBe('assign yourself');
expect(findUnassigned().text()).toBe('assign yourself');
});
it('shows a user avatar, username and full name when a user is set', () => {
mountComponent({
data: { alert: mockAlerts[1] },
sidebarCollapsed: false,
loading: false,
stubs: {
SidebarAssignee,
},
});
expect(
findAssigned()
.find('img')
.attributes('src'),
).toBe('/url');
expect(
findAssigned()
.find('.dropdown-menu-user-full-name')
.text(),
).toBe('root');
expect(
findAssigned()
.find('.dropdown-menu-user-username')
.text(),
).toBe('root');
});
});
});

View file

@ -20,7 +20,7 @@
"startedAt": "2020-04-17T23:18:14.996Z",
"endedAt": "2020-04-17T23:18:14.996Z",
"status": "ACKNOWLEDGED",
"assignees": { "nodes": [{ "username": "root" }] },
"assignees": { "nodes": [{ "username": "root", "avatarUrl": "/url", "name": "root" }] },
"issueIid": "1",
"notes": {
"nodes": [
@ -49,7 +49,7 @@
"startedAt": "2020-04-17T23:18:14.996Z",
"endedAt": "2020-04-17T23:18:14.996Z",
"status": "RESOLVED",
"assignees": { "nodes": [{ "username": "root" }] },
"assignees": { "nodes": [{ "username": "root", "avatarUrl": "/url", "name": "root" }] },
"notes": {
"nodes": [
{

View file

@ -82,14 +82,14 @@ describe('BadgeSettings component', () => {
const form = vm.$el.querySelector('form:nth-of-type(1)');
expect(form).not.toBe(null);
const submitButton = form.querySelector('.btn-success');
expect(submitButton).not.toBe(null);
expect(submitButton).toHaveText(/Save changes/);
const cancelButton = form.querySelector('.btn-cancel');
const cancelButton = form.querySelector('[data-testid="cancelEditing"]');
expect(cancelButton).not.toBe(null);
expect(cancelButton).toHaveText(/Cancel/);
const submitButton = form.querySelector('[data-testid="saveEditing"]');
expect(submitButton).not.toBe(null);
expect(submitButton).toHaveText(/Save changes/);
});
it('displays no badge list', () => {

View file

@ -87,7 +87,7 @@ describe('DiffExpansionCell', () => {
const findExpandUp = () => vm.$el.querySelector(EXPAND_UP_CLASS);
const findExpandDown = () => vm.$el.querySelector(EXPAND_DOWN_CLASS);
const findExpandAll = () => getByText(vm.$el, 'Show unchanged lines');
const findExpandAll = () => getByText(vm.$el, 'Show all unchanged lines');
describe('top row', () => {
it('should have "expand up" and "show all" option', () => {