Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
3088254c32
commit
a4cfb7653b
|
|
@ -244,3 +244,11 @@ overrides:
|
|||
- 'ee/app/assets/javascripts/projects/settings/repository/branch_rules/graphql/queries/branch_rules.query.graphql'
|
||||
rules:
|
||||
'@graphql-eslint/require-id-when-available': off
|
||||
- files:
|
||||
- '{,spec/}tooling/**/*'
|
||||
rules:
|
||||
'no-undef': off
|
||||
'import/no-commonjs': off
|
||||
'import/no-extraneous-dependencies': off
|
||||
'no-restricted-syntax': off
|
||||
'@gitlab/require-i18n-strings': off
|
||||
|
|
|
|||
|
|
@ -2078,7 +2078,6 @@ RSpec/ContextWording:
|
|||
- 'spec/models/packages/dependency_spec.rb'
|
||||
- 'spec/models/packages/package_file_spec.rb'
|
||||
- 'spec/models/packages/package_spec.rb'
|
||||
- 'spec/models/pages_domain_spec.rb'
|
||||
- 'spec/models/personal_access_token_spec.rb'
|
||||
- 'spec/models/plan_limits_spec.rb'
|
||||
- 'spec/models/preloaders/labels_preloader_spec.rb'
|
||||
|
|
|
|||
|
|
@ -2648,7 +2648,6 @@ RSpec/NamedSubject:
|
|||
- 'spec/models/packages/package_file_spec.rb'
|
||||
- 'spec/models/packages/package_spec.rb'
|
||||
- 'spec/models/packages/rpm/repository_file_spec.rb'
|
||||
- 'spec/models/pages_domain_spec.rb'
|
||||
- 'spec/models/personal_access_token_spec.rb'
|
||||
- 'spec/models/plan_limits_spec.rb'
|
||||
- 'spec/models/project_authorization_spec.rb'
|
||||
|
|
|
|||
|
|
@ -143,7 +143,6 @@ RSpec/ReturnFromStub:
|
|||
- 'spec/models/internal_id_spec.rb'
|
||||
- 'spec/models/issue_spec.rb'
|
||||
- 'spec/models/merge_request_spec.rb'
|
||||
- 'spec/models/pages_domain_spec.rb'
|
||||
- 'spec/models/project_spec.rb'
|
||||
- 'spec/models/project_statistics_spec.rb'
|
||||
- 'spec/models/snippet_statistics_spec.rb'
|
||||
|
|
|
|||
34
.stylelintrc
34
.stylelintrc
|
|
@ -1,18 +1,32 @@
|
|||
{
|
||||
"extends": ["@gitlab/stylelint-config"],
|
||||
"ignoreFiles": [
|
||||
"app/assets/stylesheets/pages/emojis.scss",
|
||||
"app/assets/stylesheets/startup/startup-*.scss",
|
||||
"ee/app/assets/stylesheets/startup/startup-*.scss",
|
||||
"app/assets/stylesheets/highlight/themes/*.scss",
|
||||
"app/assets/stylesheets/lazy_bundles/cropper.css"
|
||||
],
|
||||
"overrides": [
|
||||
"extends": ["@gitlab/stylelint-config"],
|
||||
"plugins": ["./tooling/stylelint/gitlab_no_gl_class.plugin.js"],
|
||||
"rules": {
|
||||
"gitlab/no-gl-class": true
|
||||
},
|
||||
"ignoreFiles": [
|
||||
"app/assets/stylesheets/pages/emojis.scss",
|
||||
"app/assets/stylesheets/startup/startup-*.scss",
|
||||
"ee/app/assets/stylesheets/startup/startup-*.scss",
|
||||
"app/assets/stylesheets/highlight/themes/*.scss",
|
||||
"app/assets/stylesheets/lazy_bundles/cropper.css"
|
||||
],
|
||||
"overrides": [
|
||||
{
|
||||
"files": ["app/assets/stylesheets/mailers/mailer.scss"],
|
||||
"rules": {
|
||||
"color-hex-length": "long"
|
||||
}
|
||||
},
|
||||
{
|
||||
"files": [
|
||||
"app/assets/stylesheets/framework/**/*.scss",
|
||||
"app/assets/stylesheets/themes/dark_mode_overrides.scss",
|
||||
"app/assets/stylesheets/page_bundles/_ide_theme_overrides.scss"
|
||||
],
|
||||
"rules": {
|
||||
"gitlab/no-gl-class": null
|
||||
}
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,18 +5,24 @@ import produce from 'immer';
|
|||
import Draggable from 'vuedraggable';
|
||||
import BoardAddNewColumn from 'ee_else_ce/boards/components/board_add_new_column.vue';
|
||||
import BoardAddNewColumnTrigger from '~/boards/components/board_add_new_column_trigger.vue';
|
||||
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
|
||||
import WorkItemDrawer from '~/work_items/components/work_item_drawer.vue';
|
||||
import { s__ } from '~/locale';
|
||||
import { defaultSortableOptions, DRAG_DELAY } from '~/sortable/constants';
|
||||
import { mapWorkItemWidgetsToIssueFields } from '~/issues/list/utils';
|
||||
import {
|
||||
DraggableItemTypes,
|
||||
flashAnimationDuration,
|
||||
listsQuery,
|
||||
updateListQueries,
|
||||
ListType,
|
||||
listIssuablesQueries,
|
||||
DEFAULT_BOARD_LIST_ITEMS_SIZE,
|
||||
} from 'ee_else_ce/boards/constants';
|
||||
import { calculateNewPosition } from 'ee_else_ce/boards/boards_util';
|
||||
import { setError } from '../graphql/cache_updates';
|
||||
import BoardColumn from './board_column.vue';
|
||||
import BoardDrawerWrapper from './board_drawer_wrapper.vue';
|
||||
|
||||
export default {
|
||||
draggableItemTypes: DraggableItemTypes,
|
||||
|
|
@ -24,13 +30,25 @@ export default {
|
|||
BoardAddNewColumn,
|
||||
BoardAddNewColumnTrigger,
|
||||
BoardColumn,
|
||||
BoardDrawerWrapper,
|
||||
BoardContentSidebar: () => import('~/boards/components/board_content_sidebar.vue'),
|
||||
EpicBoardContentSidebar: () =>
|
||||
import('ee_component/boards/components/epic_board_content_sidebar.vue'),
|
||||
EpicsSwimlanes: () => import('ee_component/boards/components/epics_swimlanes.vue'),
|
||||
GlAlert,
|
||||
WorkItemDrawer,
|
||||
},
|
||||
inject: ['boardType', 'canAdminList', 'isIssueBoard', 'isEpicBoard', 'disabled', 'issuableType'],
|
||||
mixins: [glFeatureFlagsMixin()],
|
||||
inject: [
|
||||
'boardType',
|
||||
'canAdminList',
|
||||
'isIssueBoard',
|
||||
'isEpicBoard',
|
||||
'disabled',
|
||||
'issuableType',
|
||||
'isGroupBoard',
|
||||
'fullPath',
|
||||
],
|
||||
props: {
|
||||
boardId: {
|
||||
type: String,
|
||||
|
|
@ -108,6 +126,9 @@ export default {
|
|||
const closedList = this.boardListsToUse.find((list) => list.listType === ListType.closed);
|
||||
return closedList?.id || '';
|
||||
},
|
||||
issuesDrawerEnabled() {
|
||||
return this.glFeatures.issuesListDrawer;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
afterFormEnters() {
|
||||
|
|
@ -193,6 +214,24 @@ export default {
|
|||
});
|
||||
}
|
||||
},
|
||||
updateBoardCard(workItem, activeCard) {
|
||||
const { cache } = this.$apollo.provider.clients.defaultClient;
|
||||
|
||||
const variables = {
|
||||
id: activeCard.listId,
|
||||
filters: this.filterParams,
|
||||
fullPath: this.fullPath,
|
||||
boardId: this.boardId,
|
||||
isGroup: this.isGroupBoard,
|
||||
isProject: !this.isGroupBoard,
|
||||
first: DEFAULT_BOARD_LIST_ITEMS_SIZE,
|
||||
};
|
||||
|
||||
cache.updateQuery(
|
||||
{ query: listIssuablesQueries[this.issuableType].query, variables },
|
||||
(boardList) => mapWorkItemWidgetsToIssueFields(boardList, workItem, true),
|
||||
);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
@ -283,9 +322,34 @@ export default {
|
|||
/>
|
||||
</div>
|
||||
</epics-swimlanes>
|
||||
<board-drawer-wrapper
|
||||
v-if="issuesDrawerEnabled"
|
||||
:backlog-list-id="backlogListId"
|
||||
:closed-list-id="closedListId"
|
||||
>
|
||||
<template
|
||||
#default="{
|
||||
activeIssuable,
|
||||
onDrawerClosed,
|
||||
onAttributeUpdated,
|
||||
onIssuableDeleted,
|
||||
onStateUpdated,
|
||||
}"
|
||||
>
|
||||
<work-item-drawer
|
||||
:open="Boolean(activeIssuable && activeIssuable.iid)"
|
||||
:active-item="activeIssuable"
|
||||
@close="onDrawerClosed"
|
||||
@work-item-updated="updateBoardCard($event, activeIssuable)"
|
||||
@workItemDeleted="onIssuableDeleted(activeIssuable)"
|
||||
@attributesUpdated="onAttributeUpdated"
|
||||
@workItemStateUpdated="onStateUpdated"
|
||||
/>
|
||||
</template>
|
||||
</board-drawer-wrapper>
|
||||
|
||||
<board-content-sidebar
|
||||
v-if="isIssueBoard"
|
||||
v-if="isIssueBoard && !issuesDrawerEnabled"
|
||||
:backlog-list-id="backlogListId"
|
||||
:closed-list-id="closedListId"
|
||||
data-testid="issue-boards-sidebar"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,134 @@
|
|||
<script>
|
||||
import { union } from 'lodash';
|
||||
import activeBoardItemQuery from 'ee_else_ce/boards/graphql/client/active_board_item.query.graphql';
|
||||
import setActiveBoardItemMutation from 'ee_else_ce/boards/graphql/client/set_active_board_item.mutation.graphql';
|
||||
import { TYPE_ISSUE } from '~/issues/constants';
|
||||
import { ListType } from 'ee_else_ce/boards/constants';
|
||||
import { identifyAffectedLists } from '../graphql/cache_updates';
|
||||
|
||||
export default {
|
||||
name: 'BoardDrawerWrapper',
|
||||
inject: {
|
||||
issuableType: {
|
||||
default: TYPE_ISSUE,
|
||||
},
|
||||
},
|
||||
props: {
|
||||
backlogListId: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
closedListId: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
affectedListTypes: [],
|
||||
updatedAttributeIds: [],
|
||||
};
|
||||
},
|
||||
apollo: {
|
||||
activeBoardItem: {
|
||||
query: activeBoardItemQuery,
|
||||
variables: {
|
||||
isIssue: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
apolloClient() {
|
||||
return this.$apollo.getClient();
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
async onDrawerClosed() {
|
||||
const item = this.activeBoardItem;
|
||||
|
||||
await this.$apollo.mutate({
|
||||
mutation: setActiveBoardItemMutation,
|
||||
variables: {
|
||||
boardItem: null,
|
||||
listId: null,
|
||||
},
|
||||
});
|
||||
|
||||
if (item.listId !== this.closedListId || this.affectedListTypes.includes(ListType.closed)) {
|
||||
await this.refetchAffectedLists(item);
|
||||
}
|
||||
this.affectedListTypes = [];
|
||||
this.updatedAttributeIds = [];
|
||||
},
|
||||
onAttributeUpdated({ ids, type }) {
|
||||
if (!this.affectedListTypes.includes(type)) {
|
||||
this.affectedListTypes.push(type);
|
||||
}
|
||||
this.updatedAttributeIds = union(this.updatedAttributeIds, ids);
|
||||
},
|
||||
refetchAffectedLists(item) {
|
||||
if (!this.affectedListTypes.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
const affectedLists = identifyAffectedLists({
|
||||
client: this.apolloClient,
|
||||
item,
|
||||
issuableType: TYPE_ISSUE,
|
||||
affectedListTypes: this.affectedListTypes,
|
||||
updatedAttributeIds: this.updatedAttributeIds,
|
||||
});
|
||||
|
||||
if (this.backlogListId && !affectedLists.includes(this.backlogListId)) {
|
||||
affectedLists.push(this.backlogListId);
|
||||
}
|
||||
|
||||
if (this.closedListId && this.affectedListTypes.includes(ListType.closed)) {
|
||||
affectedLists.push(this.closedListId);
|
||||
}
|
||||
|
||||
this.refetchActiveIssuableLists(item);
|
||||
|
||||
this.apolloClient.refetchQueries({
|
||||
updateCache(cache) {
|
||||
affectedLists.forEach((listId) => {
|
||||
cache.evict({
|
||||
id: cache.identify({
|
||||
__typename: 'BoardList',
|
||||
id: listId,
|
||||
}),
|
||||
fieldName: 'issues',
|
||||
});
|
||||
cache.evict({
|
||||
id: cache.identify({
|
||||
__typename: 'BoardList',
|
||||
id: listId,
|
||||
}),
|
||||
fieldName: 'issuesCount',
|
||||
});
|
||||
});
|
||||
},
|
||||
});
|
||||
},
|
||||
refetchActiveIssuableLists(item) {
|
||||
this.apolloClient.refetchQueries({
|
||||
updateCache(cache) {
|
||||
cache.evict({ id: cache.identify(item) });
|
||||
},
|
||||
});
|
||||
},
|
||||
onStateUpdated() {
|
||||
this.affectedListTypes.push(ListType.closed);
|
||||
},
|
||||
},
|
||||
render() {
|
||||
return this.$scopedSlots.default({
|
||||
activeIssuable: this.activeBoardItem,
|
||||
onDrawerClosed: this.onDrawerClosed,
|
||||
onAttributeUpdated: this.onAttributeUpdated,
|
||||
onIssuableDeleted: this.refetchActiveIssuableLists,
|
||||
onStateUpdated: this.onStateUpdated,
|
||||
});
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
@ -22,7 +22,15 @@ const apolloProvider = new VueApollo({
|
|||
});
|
||||
|
||||
function mountBoardApp(el) {
|
||||
const { boardId, groupId, fullPath, rootPath, hasScopedLabelsFeature } = el.dataset;
|
||||
const {
|
||||
boardId,
|
||||
groupId,
|
||||
fullPath,
|
||||
rootPath,
|
||||
hasScopedLabelsFeature,
|
||||
wiGroupPath,
|
||||
wiCanAdminLabel,
|
||||
} = el.dataset;
|
||||
|
||||
const rawFilterParams = queryToObject(window.location.search, { gatherArrays: true });
|
||||
|
||||
|
|
@ -43,9 +51,11 @@ function mountBoardApp(el) {
|
|||
groupId: Number(groupId),
|
||||
rootPath,
|
||||
fullPath,
|
||||
groupPath: wiGroupPath,
|
||||
initialFilterParams,
|
||||
boardBaseUrl: el.dataset.boardBaseUrl,
|
||||
boardType,
|
||||
isGroup: boardType === WORKSPACE_GROUP,
|
||||
isGroupBoard: boardType === WORKSPACE_GROUP,
|
||||
isProjectBoard: boardType === WORKSPACE_PROJECT,
|
||||
currentUserId: gon.current_user_id || null,
|
||||
|
|
@ -60,6 +70,8 @@ function mountBoardApp(el) {
|
|||
weights: el.dataset.weights ? JSON.parse(el.dataset.weights) : [],
|
||||
isIssueBoard: true,
|
||||
isEpicBoard: false,
|
||||
reportAbusePath: el.dataset.wiReportAbusePath,
|
||||
issuesListPath: el.dataset.wiIssuesListPath,
|
||||
// Permissions
|
||||
canUpdate: parseBoolean(el.dataset.canUpdate),
|
||||
canAdminList: parseBoolean(el.dataset.canAdminList),
|
||||
|
|
@ -67,6 +79,7 @@ function mountBoardApp(el) {
|
|||
allowLabelCreate: parseBoolean(el.dataset.canUpdate),
|
||||
allowLabelEdit: parseBoolean(el.dataset.canUpdate),
|
||||
isSignedIn: isLoggedIn(),
|
||||
canAdminLabel: parseBoolean(wiCanAdminLabel),
|
||||
// Features
|
||||
multipleAssigneesFeatureAvailable: parseBoolean(el.dataset.multipleAssigneesFeatureAvailable),
|
||||
epicFeatureAvailable: parseBoolean(el.dataset.epicFeatureAvailable),
|
||||
|
|
@ -83,6 +96,10 @@ function mountBoardApp(el) {
|
|||
scopedIssueBoardFeatureEnabled: parseBoolean(el.dataset.scopedIssueBoardFeatureEnabled),
|
||||
allowSubEpics: false,
|
||||
hasScopedLabelsFeature: parseBoolean(hasScopedLabelsFeature),
|
||||
hasIterationsFeature: parseBoolean(el.dataset.iterationFeatureAvailable),
|
||||
hasIssueWeightsFeature: parseBoolean(el.dataset.weightFeatureAvailable),
|
||||
hasIssuableHealthStatusFeature: parseBoolean(el.dataset.healthStatusFeatureAvailable),
|
||||
hasSubepicsFeature: parseBoolean(el.dataset.subEpicsFeatureAvailable),
|
||||
},
|
||||
render: (createComponent) => createComponent(BoardApp),
|
||||
});
|
||||
|
|
|
|||
|
|
@ -70,7 +70,6 @@ import IssuableList from '~/vue_shared/issuable/list/components/issuable_list_ro
|
|||
import { DEFAULT_PAGE_SIZE, issuableListTabs } from '~/vue_shared/issuable/list/constants';
|
||||
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
|
||||
import NewResourceDropdown from '~/vue_shared/components/new_resource_dropdown/new_resource_dropdown.vue';
|
||||
import deleteWorkItemMutation from '~/work_items/graphql/delete_work_item.mutation.graphql';
|
||||
import { WORK_ITEM_TYPE_ENUM_OBJECTIVE } from '~/work_items/constants';
|
||||
import WorkItemDrawer from '~/work_items/components/work_item_drawer.vue';
|
||||
import {
|
||||
|
|
@ -864,23 +863,9 @@ export default {
|
|||
this.$apollo.queries.issues.refetch();
|
||||
this.$apollo.queries.issuesCounts.refetch();
|
||||
},
|
||||
deleteIssuable({ workItemId }) {
|
||||
this.$apollo
|
||||
.mutate({
|
||||
mutation: deleteWorkItemMutation,
|
||||
variables: { input: { id: workItemId } },
|
||||
})
|
||||
.then(({ data }) => {
|
||||
if (data.workItemDelete.errors?.length) {
|
||||
throw new Error(data.workItemDelete.errors[0]);
|
||||
}
|
||||
this.activeIssuable = null;
|
||||
this.refetchIssuables();
|
||||
})
|
||||
.catch((error) => {
|
||||
this.issuesError = __('An error occurred while deleting an issuable.');
|
||||
Sentry.captureException(error);
|
||||
});
|
||||
deleteIssuable() {
|
||||
this.activeIssuable = null;
|
||||
this.refetchIssuables();
|
||||
},
|
||||
updateIssuableEmojis(workItem) {
|
||||
const client = this.$apollo.provider.clients.defaultClient;
|
||||
|
|
@ -907,7 +892,8 @@ export default {
|
|||
@work-item-updated="updateIssuablesCache"
|
||||
@work-item-emoji-updated="updateIssuableEmojis"
|
||||
@addChild="refetchIssuables"
|
||||
@deleteWorkItem="deleteIssuable"
|
||||
@deleteWorkItemError="issuesError = __('An error occurred while deleting an issuable.')"
|
||||
@workItemDeleted="deleteIssuable"
|
||||
@promotedToObjective="promoteToObjective"
|
||||
/>
|
||||
<issuable-list
|
||||
|
|
|
|||
|
|
@ -429,9 +429,13 @@ export function findWidget(type, workItem) {
|
|||
return workItem?.widgets?.find((widget) => widget.type === type);
|
||||
}
|
||||
|
||||
export function mapWorkItemWidgetsToIssueFields(issuesList, workItem) {
|
||||
export function mapWorkItemWidgetsToIssueFields(issuesList, workItem, isBoard = false) {
|
||||
return produce(issuesList, (draftData) => {
|
||||
const activeItem = draftData.project.issues.nodes.find((issue) => issue.iid === workItem.iid);
|
||||
const activeList = isBoard
|
||||
? draftData.project.board.lists.nodes[0].issues.nodes
|
||||
: draftData.project.issues.nodes;
|
||||
|
||||
const activeItem = activeList.find((issue) => issue.iid === workItem.iid);
|
||||
|
||||
Object.keys(WORK_ITEM_TO_ISSUE_MAP).forEach((type) => {
|
||||
const currentWidget = findWidget(type, workItem);
|
||||
|
|
|
|||
|
|
@ -86,7 +86,8 @@ export default {
|
|||
},
|
||||
workItemState: {
|
||||
type: String,
|
||||
required: true,
|
||||
required: false,
|
||||
default: null,
|
||||
},
|
||||
workItemId: {
|
||||
type: String,
|
||||
|
|
@ -403,6 +404,7 @@ export default {
|
|||
:full-path="fullPath"
|
||||
show-as-dropdown-item
|
||||
@error="emitStateToggleError"
|
||||
@workItemStateUpdated="$emit('workItemStateUpdated')"
|
||||
/>
|
||||
|
||||
<gl-disclosure-dropdown-item
|
||||
|
|
|
|||
|
|
@ -274,6 +274,7 @@ export default {
|
|||
return;
|
||||
}
|
||||
this.track('updated_assignees');
|
||||
this.$emit('assigneesUpdated', localAssigneeIds);
|
||||
} catch {
|
||||
this.throwUpdateError();
|
||||
} finally {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
<script>
|
||||
import Participants from '~/sidebar/components/participants/participants.vue';
|
||||
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
|
||||
import { ListType } from '~/boards/constants';
|
||||
import {
|
||||
WIDGET_TYPE_ASSIGNEES,
|
||||
WIDGET_TYPE_HEALTH_STATUS,
|
||||
|
|
@ -27,6 +28,7 @@ import WorkItemTimeTracking from './work_item_time_tracking.vue';
|
|||
import WorkItemDevelopment from './work_item_development/work_item_development.vue';
|
||||
|
||||
export default {
|
||||
ListType,
|
||||
components: {
|
||||
Participants,
|
||||
WorkItemLabels,
|
||||
|
|
@ -159,6 +161,9 @@ export default {
|
|||
:work-item-type="workItemType"
|
||||
:can-invite-members="workItemAssignees.canInviteMembers"
|
||||
@error="$emit('error', $event)"
|
||||
@assigneesUpdated="
|
||||
$emit('attributesUpdated', { type: $options.ListType.assignee, ids: $event })
|
||||
"
|
||||
/>
|
||||
</template>
|
||||
<template v-if="workItemLabels">
|
||||
|
|
@ -170,6 +175,7 @@ export default {
|
|||
:work-item-iid="workItem.iid"
|
||||
:work-item-type="workItemType"
|
||||
@error="$emit('error', $event)"
|
||||
@labelsUpdated="$emit('attributesUpdated', { type: $options.ListType.label, ids: $event })"
|
||||
/>
|
||||
</template>
|
||||
<template v-if="workItemWeight">
|
||||
|
|
@ -207,6 +213,9 @@ export default {
|
|||
:work-item-type="workItemType"
|
||||
:can-update="canUpdate"
|
||||
@error="$emit('error', $event)"
|
||||
@milestoneUpdated="
|
||||
$emit('attributesUpdated', { type: $options.ListType.milestone, ids: [$event] })
|
||||
"
|
||||
/>
|
||||
</template>
|
||||
<template v-if="workItemIteration">
|
||||
|
|
@ -219,6 +228,9 @@ export default {
|
|||
:work-item-iid="workItem.iid"
|
||||
:work-item-type="workItemType"
|
||||
@error="$emit('error', $event)"
|
||||
@iterationUpdated="
|
||||
$emit('attributesUpdated', { type: $options.ListType.iteration, ids: [$event] })
|
||||
"
|
||||
/>
|
||||
</template>
|
||||
<template v-if="workItemDueDate && !showRolledupDates">
|
||||
|
|
|
|||
|
|
@ -53,6 +53,7 @@ import WorkItemLoading from './work_item_loading.vue';
|
|||
import DesignWidget from './design_management/design_management_widget.vue';
|
||||
|
||||
export default {
|
||||
name: 'WorkItemDetail',
|
||||
i18n,
|
||||
directives: {
|
||||
GlTooltip: GlTooltipDirective,
|
||||
|
|
@ -257,7 +258,7 @@ export default {
|
|||
return this.isWidgetPresent(WIDGET_TYPE_DESCRIPTION);
|
||||
},
|
||||
hasDesignWidget() {
|
||||
return this.isWidgetPresent(WIDGET_TYPE_DESIGNS);
|
||||
return this.isWidgetPresent(WIDGET_TYPE_DESIGNS) && this.$router;
|
||||
},
|
||||
workItemNotificationsSubscribed() {
|
||||
return Boolean(this.isWidgetPresent(WIDGET_TYPE_NOTIFICATIONS)?.subscribed);
|
||||
|
|
@ -495,6 +496,7 @@ export default {
|
|||
@error="updateError = $event"
|
||||
@promotedToObjective="$emit('promotedToObjective', workItemIid)"
|
||||
@toggleEditMode="enableEditMode"
|
||||
@workItemStateUpdated="$emit('workItemStateUpdated')"
|
||||
/>
|
||||
<section class="work-item-view">
|
||||
<section v-if="updateError" class="flash-container flash-container-page sticky">
|
||||
|
|
@ -585,6 +587,7 @@ export default {
|
|||
@toggleWorkItemConfidentiality="toggleConfidentiality"
|
||||
@error="updateError = $event"
|
||||
@promotedToObjective="$emit('promotedToObjective', workItemIid)"
|
||||
@workItemStateUpdated="$emit('workItemStateUpdated')"
|
||||
/>
|
||||
</div>
|
||||
<gl-button
|
||||
|
|
@ -650,6 +653,7 @@ export default {
|
|||
:work-item="workItem"
|
||||
:group-path="groupPath"
|
||||
@error="updateError = $event"
|
||||
@attributesUpdated="$emit('attributesUpdated', $event)"
|
||||
/>
|
||||
</aside>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,13 +1,14 @@
|
|||
<script>
|
||||
import { GlLink, GlDrawer } from '@gitlab/ui';
|
||||
import WorkItemDetail from '~/work_items/components/work_item_detail.vue';
|
||||
import deleteWorkItemMutation from '~/work_items/graphql/delete_work_item.mutation.graphql';
|
||||
import * as Sentry from '~/sentry/sentry_browser_wrapper';
|
||||
|
||||
export default {
|
||||
name: 'WorkItemDrawer',
|
||||
components: {
|
||||
GlLink,
|
||||
GlDrawer,
|
||||
WorkItemDetail,
|
||||
WorkItemDetail: () => import('~/work_items/components/work_item_detail.vue'),
|
||||
},
|
||||
inheritAttrs: false,
|
||||
props: {
|
||||
|
|
@ -21,12 +22,30 @@ export default {
|
|||
default: () => ({}),
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
async deleteWorkItem({ workItemId }) {
|
||||
try {
|
||||
const { data } = await this.$apollo.mutate({
|
||||
mutation: deleteWorkItemMutation,
|
||||
variables: { input: { id: workItemId } },
|
||||
});
|
||||
if (data.workItemDelete.errors?.length) {
|
||||
throw new Error(data.workItemDelete.errors[0]);
|
||||
}
|
||||
this.$emit('workItemDeleted');
|
||||
} catch (error) {
|
||||
this.$emit('deleteWorkItemError');
|
||||
Sentry.captureException(error);
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<gl-drawer
|
||||
:open="open"
|
||||
data-testid="work-item-drawer"
|
||||
header-height="calc(var(--top-bar-height) + var(--performance-bar-height))"
|
||||
class="gl-w-full gl-sm-w-40p gl-leading-reset"
|
||||
@close="$emit('close')"
|
||||
|
|
@ -42,6 +61,7 @@ export default {
|
|||
:work-item-iid="activeItem.iid"
|
||||
is-drawer
|
||||
class="gl-pt-0! work-item-drawer"
|
||||
@deleteWorkItem="deleteWorkItem"
|
||||
v-on="$listeners"
|
||||
/>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -260,6 +260,7 @@ export default {
|
|||
}
|
||||
|
||||
this.track('updated_labels');
|
||||
this.$emit('labelsUpdated', [...this.addLabelIds, ...this.removeLabelIds]);
|
||||
} catch {
|
||||
this.$emit('error', i18n.updateError);
|
||||
} finally {
|
||||
|
|
|
|||
|
|
@ -163,6 +163,7 @@ export default {
|
|||
if (data.workItemUpdate.errors.length) {
|
||||
throw new Error(data.workItemUpdate.errors.join('\n'));
|
||||
}
|
||||
this.$emit('milestoneUpdated', selectedMilestoneId);
|
||||
})
|
||||
.catch((error) => {
|
||||
this.localMilestone = this.workItemMilestone;
|
||||
|
|
|
|||
|
|
@ -193,6 +193,7 @@ export default {
|
|||
if (this.hasComment) {
|
||||
this.$emit('submit-comment');
|
||||
}
|
||||
this.$emit('workItemStateUpdated');
|
||||
|
||||
this.updateInProgress = false;
|
||||
},
|
||||
|
|
|
|||
|
|
@ -159,6 +159,7 @@ export default {
|
|||
"
|
||||
@error="$emit('error')"
|
||||
@promotedToObjective="$emit('promotedToObjective')"
|
||||
@workItemStateUpdated="$emit('workItemStateUpdated')"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -108,7 +108,7 @@ export default {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<div data-testid="work-item-time-tracking">
|
||||
<div class="gl-display-flex gl-align-items-center gl-justify-content-space-between">
|
||||
<h3 class="gl-heading-5 gl-mb-2!">
|
||||
{{ __('Time tracking') }}
|
||||
|
|
|
|||
|
|
@ -297,9 +297,11 @@ export const WORK_ITEM_TO_ISSUE_MAP = {
|
|||
[WIDGET_TYPE_LABELS]: 'labels',
|
||||
[WIDGET_TYPE_MILESTONE]: 'milestone',
|
||||
[WIDGET_TYPE_WEIGHT]: 'weight',
|
||||
[WIDGET_TYPE_ITERATION]: 'iteration',
|
||||
[WIDGET_TYPE_START_AND_DUE_DATE]: 'dueDate',
|
||||
[WIDGET_TYPE_HEALTH_STATUS]: 'healthStatus',
|
||||
[WIDGET_TYPE_AWARD_EMOJI]: 'awardEmoji',
|
||||
[WIDGET_TYPE_TIME_TRACKING]: 'timeEstimate',
|
||||
};
|
||||
|
||||
export const LINKED_CATEGORIES_MAP = {
|
||||
|
|
|
|||
|
|
@ -5,7 +5,15 @@ import IssueCardStatistics from 'ee_else_ce/issues/list/components/issue_card_st
|
|||
import IssueCardTimeInfo from 'ee_else_ce/issues/list/components/issue_card_time_info.vue';
|
||||
import { TYPENAME_USER } from '~/graphql_shared/constants';
|
||||
import { convertToGraphQLId } from '~/graphql_shared/utils';
|
||||
import { STATUS_ALL, STATUS_CLOSED, STATUS_OPEN } from '~/issues/constants';
|
||||
import {
|
||||
STATUS_ALL,
|
||||
STATUS_CLOSED,
|
||||
STATUS_OPEN,
|
||||
WORKSPACE_GROUP,
|
||||
WORKSPACE_PROJECT,
|
||||
} from '~/issues/constants';
|
||||
import { defaultTypeTokenOptions } from '~/issues/list/constants';
|
||||
import searchLabelsQuery from '~/issues/list/queries/search_labels.query.graphql';
|
||||
import setSortPreferenceMutation from '~/issues/list/queries/set_sort_preference.mutation.graphql';
|
||||
import {
|
||||
convertToApiParams,
|
||||
|
|
@ -13,22 +21,37 @@ import {
|
|||
deriveSortKey,
|
||||
getInitialPageParams,
|
||||
} from '~/issues/list/utils';
|
||||
import { fetchPolicies } from '~/lib/graphql';
|
||||
import { scrollUp } from '~/lib/utils/scroll_utils';
|
||||
import { __, s__ } from '~/locale';
|
||||
import {
|
||||
OPERATORS_IS,
|
||||
OPERATORS_IS_NOT_OR,
|
||||
TOKEN_TITLE_ASSIGNEE,
|
||||
TOKEN_TITLE_AUTHOR,
|
||||
TOKEN_TITLE_LABEL,
|
||||
TOKEN_TITLE_MILESTONE,
|
||||
TOKEN_TITLE_SEARCH_WITHIN,
|
||||
TOKEN_TITLE_TYPE,
|
||||
TOKEN_TYPE_ASSIGNEE,
|
||||
TOKEN_TYPE_AUTHOR,
|
||||
TOKEN_TYPE_LABEL,
|
||||
TOKEN_TYPE_MILESTONE,
|
||||
TOKEN_TYPE_SEARCH_WITHIN,
|
||||
TOKEN_TYPE_TYPE,
|
||||
} from '~/vue_shared/components/filtered_search_bar/constants';
|
||||
import IssuableList from '~/vue_shared/issuable/list/components/issuable_list_root.vue';
|
||||
import { DEFAULT_PAGE_SIZE, issuableListTabs } from '~/vue_shared/issuable/list/constants';
|
||||
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
|
||||
import { STATE_CLOSED } from '../../constants';
|
||||
import { sortOptions, urlSortParams } from '../constants';
|
||||
import getWorkItemsQuery from '../queries/get_work_items.query.graphql';
|
||||
|
||||
const UserToken = () => import('~/vue_shared/components/filtered_search_bar/tokens/user_token.vue');
|
||||
const LabelToken = () =>
|
||||
import('~/vue_shared/components/filtered_search_bar/tokens/label_token.vue');
|
||||
const MilestoneToken = () =>
|
||||
import('~/vue_shared/components/filtered_search_bar/tokens/milestone_token.vue');
|
||||
|
||||
export default {
|
||||
issuableListTabs,
|
||||
|
|
@ -39,7 +62,8 @@ export default {
|
|||
IssueCardStatistics,
|
||||
IssueCardTimeInfo,
|
||||
},
|
||||
inject: ['fullPath', 'initialSort', 'isSignedIn', 'workItemType'],
|
||||
mixins: [glFeatureFlagMixin()],
|
||||
inject: ['fullPath', 'initialSort', 'isGroup', 'isSignedIn', 'workItemType'],
|
||||
props: {
|
||||
eeCreatedWorkItemsCount: {
|
||||
type: Number,
|
||||
|
|
@ -73,7 +97,7 @@ export default {
|
|||
search: this.searchQuery,
|
||||
...this.apiFilterParams,
|
||||
...this.pageParams,
|
||||
types: [this.workItemType],
|
||||
types: this.apiFilterParams.types || [this.workItemType],
|
||||
};
|
||||
},
|
||||
update(data) {
|
||||
|
|
@ -115,6 +139,9 @@ export default {
|
|||
isOpenTab() {
|
||||
return this.state === STATUS_OPEN;
|
||||
},
|
||||
namespace() {
|
||||
return this.isGroup ? WORKSPACE_GROUP : WORKSPACE_PROJECT;
|
||||
},
|
||||
searchQuery() {
|
||||
return convertToSearchQuery(this.filterTokens);
|
||||
},
|
||||
|
|
@ -130,7 +157,20 @@ export default {
|
|||
});
|
||||
}
|
||||
|
||||
return [
|
||||
const tokens = [
|
||||
{
|
||||
type: TOKEN_TYPE_ASSIGNEE,
|
||||
title: TOKEN_TITLE_ASSIGNEE,
|
||||
icon: 'user',
|
||||
token: UserToken,
|
||||
dataType: 'user',
|
||||
operators: OPERATORS_IS_NOT_OR,
|
||||
fullPath: this.fullPath,
|
||||
isProject: !this.isGroup,
|
||||
recentSuggestionsStorageKey: `${this.fullPath}-issues-recent-tokens-assignee`,
|
||||
preloadedUsers,
|
||||
multiSelect: this.glFeatures.groupMultiSelectTokens,
|
||||
},
|
||||
{
|
||||
type: TOKEN_TYPE_AUTHOR,
|
||||
title: TOKEN_TITLE_AUTHOR,
|
||||
|
|
@ -138,11 +178,33 @@ export default {
|
|||
token: UserToken,
|
||||
dataType: 'user',
|
||||
defaultUsers: [],
|
||||
operators: OPERATORS_IS,
|
||||
operators: OPERATORS_IS_NOT_OR,
|
||||
fullPath: this.fullPath,
|
||||
isProject: false,
|
||||
isProject: !this.isGroup,
|
||||
recentSuggestionsStorageKey: `${this.fullPath}-issues-recent-tokens-author`,
|
||||
preloadedUsers,
|
||||
multiSelect: this.glFeatures.groupMultiSelectTokens,
|
||||
},
|
||||
{
|
||||
type: TOKEN_TYPE_LABEL,
|
||||
title: TOKEN_TITLE_LABEL,
|
||||
icon: 'labels',
|
||||
token: LabelToken,
|
||||
operators: OPERATORS_IS_NOT_OR,
|
||||
fetchLabels: this.fetchLabels,
|
||||
fetchLatestLabels: this.glFeatures.frontendCaching ? this.fetchLatestLabels : null,
|
||||
recentSuggestionsStorageKey: `${this.fullPath}-issues-recent-tokens-label`,
|
||||
multiSelect: this.glFeatures.groupMultiSelectTokens,
|
||||
},
|
||||
{
|
||||
type: TOKEN_TYPE_MILESTONE,
|
||||
title: TOKEN_TITLE_MILESTONE,
|
||||
icon: 'milestone',
|
||||
token: MilestoneToken,
|
||||
recentSuggestionsStorageKey: `${this.fullPath}-issues-recent-tokens-milestone`,
|
||||
shouldSkipSort: true,
|
||||
fullPath: this.fullPath,
|
||||
isProject: !this.isGroup,
|
||||
},
|
||||
{
|
||||
type: TOKEN_TYPE_SEARCH_WITHIN,
|
||||
|
|
@ -157,6 +219,21 @@ export default {
|
|||
],
|
||||
},
|
||||
];
|
||||
|
||||
if (!this.workItemType) {
|
||||
tokens.push({
|
||||
type: TOKEN_TYPE_TYPE,
|
||||
title: TOKEN_TITLE_TYPE,
|
||||
icon: 'issues',
|
||||
token: GlFilteredSearchToken,
|
||||
operators: OPERATORS_IS,
|
||||
options: defaultTypeTokenOptions,
|
||||
});
|
||||
}
|
||||
|
||||
tokens.sort((a, b) => a.title.localeCompare(b.title));
|
||||
|
||||
return tokens;
|
||||
},
|
||||
showPaginationControls() {
|
||||
return !this.isLoading && (this.pageInfo.hasNextPage || this.pageInfo.hasPreviousPage);
|
||||
|
|
@ -175,6 +252,26 @@ export default {
|
|||
},
|
||||
},
|
||||
methods: {
|
||||
fetchLabelsWithFetchPolicy(search, fetchPolicy = fetchPolicies.CACHE_FIRST) {
|
||||
return this.$apollo
|
||||
.query({
|
||||
query: searchLabelsQuery,
|
||||
variables: { fullPath: this.fullPath, search, isProject: !this.isGroup },
|
||||
fetchPolicy,
|
||||
})
|
||||
.then(({ data }) => {
|
||||
// TODO remove once we can search by title-only on the backend
|
||||
// https://gitlab.com/gitlab-org/gitlab/-/issues/346353
|
||||
const labels = data[this.namespace]?.labels.nodes;
|
||||
return labels.filter((label) => label.title.toLowerCase().includes(search.toLowerCase()));
|
||||
});
|
||||
},
|
||||
fetchLabels(search) {
|
||||
return this.fetchLabelsWithFetchPolicy(search);
|
||||
},
|
||||
fetchLatestLabels(search) {
|
||||
return this.fetchLabelsWithFetchPolicy(search, fetchPolicies.NETWORK_ONLY);
|
||||
},
|
||||
getStatus(issue) {
|
||||
return issue.state === STATE_CLOSED ? __('Closed') : undefined;
|
||||
},
|
||||
|
|
@ -256,6 +353,7 @@ export default {
|
|||
namespace="work-items"
|
||||
recent-searches-storage-key="issues"
|
||||
:search-tokens="searchTokens"
|
||||
show-filtered-search-friendly-text
|
||||
:show-page-size-selector="showPageSizeSelector"
|
||||
:show-pagination-controls="showPaginationControls"
|
||||
show-work-item-type-icon
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ export const mountWorkItemsListApp = () => {
|
|||
hasEpicsFeature,
|
||||
hasIssuableHealthStatusFeature,
|
||||
hasIssueWeightsFeature,
|
||||
hasScopedLabelsFeature,
|
||||
initialSort,
|
||||
isSignedIn,
|
||||
showNewIssueLink,
|
||||
|
|
@ -37,6 +38,7 @@ export const mountWorkItemsListApp = () => {
|
|||
hasEpicsFeature: parseBoolean(hasEpicsFeature),
|
||||
hasIssuableHealthStatusFeature: parseBoolean(hasIssuableHealthStatusFeature),
|
||||
hasIssueWeightsFeature: parseBoolean(hasIssueWeightsFeature),
|
||||
hasScopedLabelsFeature: parseBoolean(hasScopedLabelsFeature),
|
||||
initialSort,
|
||||
isSignedIn: parseBoolean(isSignedIn),
|
||||
isGroup: true,
|
||||
|
|
|
|||
|
|
@ -6,17 +6,37 @@ query getWorkItems(
|
|||
$search: String
|
||||
$sort: WorkItemSort
|
||||
$state: IssuableState
|
||||
$assigneeWildcardId: AssigneeWildcardId
|
||||
$assigneeUsernames: [String!]
|
||||
$authorUsername: String
|
||||
$labelName: [String!]
|
||||
$milestoneTitle: [String!]
|
||||
$milestoneWildcardId: MilestoneWildcardId
|
||||
$types: [IssueType!]
|
||||
$in: [IssuableSearchableField!]
|
||||
$not: NegatedWorkItemFilterInput
|
||||
$or: UnionedWorkItemFilterInput
|
||||
$afterCursor: String
|
||||
$beforeCursor: String
|
||||
$firstPageSize: Int
|
||||
$lastPageSize: Int
|
||||
$types: [IssueType!] = null
|
||||
) {
|
||||
group(fullPath: $fullPath) {
|
||||
id
|
||||
workItemStateCounts(includeDescendants: true, sort: $sort, state: $state, types: $types) {
|
||||
workItemStateCounts(
|
||||
includeDescendants: true
|
||||
sort: $sort
|
||||
state: $state
|
||||
assigneeUsernames: $assigneeUsernames
|
||||
assigneeWildcardId: $assigneeWildcardId
|
||||
authorUsername: $authorUsername
|
||||
labelName: $labelName
|
||||
milestoneTitle: $milestoneTitle
|
||||
milestoneWildcardId: $milestoneWildcardId
|
||||
types: $types
|
||||
not: $not
|
||||
or: $or
|
||||
) {
|
||||
all
|
||||
closed
|
||||
opened
|
||||
|
|
@ -26,13 +46,20 @@ query getWorkItems(
|
|||
search: $search
|
||||
sort: $sort
|
||||
state: $state
|
||||
assigneeUsernames: $assigneeUsernames
|
||||
assigneeWildcardId: $assigneeWildcardId
|
||||
authorUsername: $authorUsername
|
||||
labelName: $labelName
|
||||
milestoneTitle: $milestoneTitle
|
||||
milestoneWildcardId: $milestoneWildcardId
|
||||
types: $types
|
||||
in: $in
|
||||
not: $not
|
||||
or: $or
|
||||
after: $afterCursor
|
||||
before: $beforeCursor
|
||||
first: $firstPageSize
|
||||
last: $lastPageSize
|
||||
types: $types
|
||||
) {
|
||||
pageInfo {
|
||||
...PageInfo
|
||||
|
|
|
|||
|
|
@ -79,6 +79,7 @@ input[type='file'] {
|
|||
|
||||
// Add to .label so that old system notes that are saved to the db
|
||||
// will still receive the correct styling
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.badge:not(.gl-badge),
|
||||
.label {
|
||||
padding: 4px 5px;
|
||||
|
|
|
|||
|
|
@ -3,17 +3,17 @@
|
|||
display: block;
|
||||
padding: $gl-spacing-scale-5;
|
||||
border-radius: $gl-border-radius-base;
|
||||
|
||||
|
||||
&-default {
|
||||
border: 1px solid $gray-100;
|
||||
background-color: $white;
|
||||
}
|
||||
|
||||
|
||||
&-success {
|
||||
border: 1px solid $green-100;
|
||||
background-color: $green-50;
|
||||
}
|
||||
|
||||
|
||||
&-promo {
|
||||
border: 1px solid $purple-100;
|
||||
background-color: $purple-50;
|
||||
|
|
@ -25,11 +25,13 @@
|
|||
gap: $gl-spacing-scale-2;
|
||||
font-weight: $gl-font-weight-bold;
|
||||
text-wrap: balance;
|
||||
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
&:is(.gl-link):not(:hover) {
|
||||
color: $gl-text-color;
|
||||
}
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
&:is(.gl-link) {
|
||||
transition: color .2s cubic-bezier(0.22, 0.61, 0.36, 1);
|
||||
|
||||
|
|
|
|||
|
|
@ -195,10 +195,12 @@ $avatar-sizes: (
|
|||
|
||||
// Max width of popover container is set by gl-max-w-48
|
||||
// so we need to ensure that name/username/status container doesn't overflow
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-avatar-labeled-labels {
|
||||
max-width: px-to-rem(290px);
|
||||
}
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-avatar-labeled-label,
|
||||
.gl-avatar-labeled-sublabel {
|
||||
@include gl-text-truncate;
|
||||
|
|
|
|||
|
|
@ -283,9 +283,11 @@
|
|||
}
|
||||
}
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-new-dropdown-inner li {
|
||||
margin-left: 0 !important;
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
&.gl-new-dropdown-item {
|
||||
padding-left: $gl-spacing-scale-2;
|
||||
padding-right: $gl-spacing-scale-2;
|
||||
|
|
@ -358,15 +360,18 @@
|
|||
}
|
||||
}
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.content-editor-table-dropdown .gl-new-dropdown-panel {
|
||||
min-width: auto;
|
||||
}
|
||||
|
||||
.content-editor-suggestions-dropdown {
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-new-dropdown-panel {
|
||||
width: max-content;
|
||||
}
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
li.focused div.gl-new-dropdown-item-content {
|
||||
@include gl-focus($inset: true);
|
||||
background-color: $gray-50;
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
overflow: hidden;
|
||||
width: 20rem;
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
&,
|
||||
.gl-dropdown-inner {
|
||||
max-height: $dropdown-max-height-lg;
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ $item-remove-button-space: 42px;
|
|||
|
||||
.related-items-tree {
|
||||
.card-header {
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-label {
|
||||
line-height: $gl-line-height;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,14 +3,17 @@
|
|||
overflow-y: hidden;
|
||||
width: 500px;
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-infinite-scroll-legend {
|
||||
display: none;
|
||||
}
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-tabs {
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-tabs-nav {
|
||||
flex-wrap: nowrap;
|
||||
overflow-x: scroll;
|
||||
|
|
@ -26,6 +29,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-spinner-container {
|
||||
width: 100%;
|
||||
position: absolute;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
@import 'notify_base';
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-label-scoped {
|
||||
border: 2px solid currentColor;
|
||||
box-sizing: border-box;
|
||||
|
|
@ -8,18 +9,22 @@
|
|||
line-height: 14px;
|
||||
}
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-label-text {
|
||||
padding: 0 5px;
|
||||
}
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-label-text-light {
|
||||
color: $white;
|
||||
}
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-label-text-dark {
|
||||
color: $gl-text-color;
|
||||
}
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-label-text-scoped {
|
||||
padding: 0 5px;
|
||||
color: $gl-text-color;
|
||||
|
|
|
|||
|
|
@ -36,10 +36,12 @@ pre {
|
|||
font-size: 14px;
|
||||
}
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-mb-5 {
|
||||
margin-bottom: $gl-spacing-scale-5;
|
||||
}
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-mt-5 {
|
||||
margin-top: $gl-spacing-scale-5;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@
|
|||
background-color: var(--gray-50, $gray-50);
|
||||
}
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-spinner,
|
||||
svg {
|
||||
width: $ci-action-dropdown-svg-size;
|
||||
|
|
|
|||
|
|
@ -8,30 +8,33 @@ Shared styles for system note dot and icon styles used for MR, Issue, Work Item
|
|||
margin-left: 12px;
|
||||
margin-right: 8px;
|
||||
border: 2px solid var(--gray-50, $gray-50);
|
||||
|
||||
|
||||
.gl-dark .modal-body & {
|
||||
border-color: var(--gray-100, $gray-100);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.system-note-icon {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
margin-left: 6px;
|
||||
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
&.gl-bg-green-100 {
|
||||
--bg-color: var(--green-100, #{$green-100});
|
||||
}
|
||||
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
&.gl-bg-red-100 {
|
||||
--bg-color: var(--red-100, #{$red-100});
|
||||
}
|
||||
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
&.gl-bg-blue-100 {
|
||||
--bg-color: var(--blue-100, #{$blue-100});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.system-note-icon:not(.mr-system-note-empty)::before {
|
||||
content: '';
|
||||
display: block;
|
||||
|
|
@ -41,12 +44,12 @@ Shared styles for system note dot and icon styles used for MR, Issue, Work Item
|
|||
width: 2px;
|
||||
height: 20px;
|
||||
background: linear-gradient(to bottom, transparent, var(--bg-color));
|
||||
|
||||
|
||||
.system-note:first-child & {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.system-note-icon:not(.mr-system-note-empty)::after {
|
||||
content: '';
|
||||
display: block;
|
||||
|
|
@ -56,8 +59,8 @@ Shared styles for system note dot and icon styles used for MR, Issue, Work Item
|
|||
width: 2px;
|
||||
height: 20px;
|
||||
background: linear-gradient(to bottom, var(--bg-color), transparent);
|
||||
|
||||
|
||||
.system-note:last-child & {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@
|
|||
padding-top: $gl-spacing-scale-8;
|
||||
}
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-dropdown-item-text-wrapper {
|
||||
padding-top: 0;
|
||||
padding-bottom: 0;
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
width: 100%;
|
||||
order: -1;
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-dropdown,
|
||||
.split-content-button {
|
||||
width: 100%;
|
||||
|
|
@ -26,6 +27,7 @@
|
|||
}
|
||||
|
||||
.select-agent-dropdown {
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-button-text {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
}
|
||||
|
||||
.add-review-item {
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-tab-nav-item {
|
||||
height: 100%;
|
||||
}
|
||||
|
|
@ -22,6 +23,7 @@
|
|||
color: var(--gl-text-color-subtle);
|
||||
}
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-filtered-search-suggestion-list.dropdown-menu {
|
||||
width: $gl-max-dropdown-max-height;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -48,6 +48,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-responsive-table-row {
|
||||
.branch-commit {
|
||||
max-width: 100%;
|
||||
|
|
|
|||
|
|
@ -97,6 +97,7 @@ $ide-commit-header-height: 48px;
|
|||
border-right: 1px solid var(--ide-border-color, $border-color);
|
||||
border-bottom: 1px solid var(--ide-border-color, $border-color);
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
&.active,
|
||||
.gl-tab-nav-item-active {
|
||||
background-color: var(--ide-highlight-background, $white);
|
||||
|
|
@ -116,10 +117,12 @@ $ide-commit-header-height: 48px;
|
|||
}
|
||||
}
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-tab-content {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-tabs-nav {
|
||||
border-width: 0;
|
||||
|
||||
|
|
@ -139,6 +142,7 @@ $ide-commit-header-height: 48px;
|
|||
border-right: 1px solid var(--ide-border-color, $border-color);
|
||||
border-bottom: 1px solid var(--ide-border-color, $border-color);
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
&.gl-tab-nav-item-active {
|
||||
background-color: var(--ide-highlight-background, $white);
|
||||
border-color: var(--ide-border-color, $border-color);
|
||||
|
|
@ -562,6 +566,7 @@ $ide-commit-header-height: 48px;
|
|||
}
|
||||
}
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-form-radio,
|
||||
.gl-form-checkbox {
|
||||
color: var(--ide-text-color, $gl-text-color);
|
||||
|
|
@ -644,6 +649,7 @@ $ide-commit-header-height: 48px;
|
|||
height: 100%;
|
||||
}
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.nav-links,
|
||||
.gl-tabs-nav {
|
||||
height: 30px;
|
||||
|
|
@ -925,6 +931,7 @@ $ide-commit-header-height: 48px;
|
|||
--svg-status-bg: var(--ide-background, #{$white});
|
||||
}
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-empty-state {
|
||||
p {
|
||||
margin: $grid-size 0;
|
||||
|
|
@ -938,6 +945,7 @@ $ide-commit-header-height: 48px;
|
|||
}
|
||||
}
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-tab-content {
|
||||
color: var(--ide-text-color, $gl-text-color);
|
||||
}
|
||||
|
|
@ -980,6 +988,7 @@ $ide-commit-header-height: 48px;
|
|||
}
|
||||
|
||||
.ide-nav-form {
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.nav-links li,
|
||||
.gl-tabs-nav li {
|
||||
width: 50%;
|
||||
|
|
@ -991,11 +1000,13 @@ $ide-commit-header-height: 48px;
|
|||
font-size: 14px;
|
||||
line-height: 30px;
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
&:not(.active),
|
||||
&:not(.gl-tab-nav-item-active) {
|
||||
background-color: var(--ide-dropdown-background, $gray-10);
|
||||
}
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
&.gl-tab-nav-item-active {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@
|
|||
.create-timeline-event,
|
||||
.edit-timeline-event {
|
||||
.md-area {
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-form-textarea {
|
||||
@include gl-shadow-none;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -97,10 +97,12 @@
|
|||
}
|
||||
|
||||
.merge-request-notification-toggle {
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-toggle {
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-toggle-label {
|
||||
font-weight: $gl-font-weight-normal;
|
||||
}
|
||||
|
|
@ -109,6 +111,7 @@
|
|||
.comment-templates-modal {
|
||||
padding: 3rem 0.5rem 0;
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
&.gl-modal .modal-dialog {
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
|
@ -121,6 +124,7 @@
|
|||
padding-left: 0;
|
||||
}
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.comment-templates-options .gl-new-dropdown-item {
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
|
|
|
|||
|
|
@ -59,12 +59,14 @@
|
|||
color: var(--gl-text-color, $gl-text-color);
|
||||
}
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-label-link {
|
||||
color: inherit;
|
||||
|
||||
&:hover {
|
||||
text-decoration: none;
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-label-text:last-of-type {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,10 +36,12 @@
|
|||
}
|
||||
|
||||
.work-item-labels {
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-token {
|
||||
padding-left: $gl-spacing-scale-1;
|
||||
}
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-token-close {
|
||||
display: none;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -94,6 +94,7 @@
|
|||
.label-name {
|
||||
width: 200px;
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-label {
|
||||
line-height: $gl-line-height;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@
|
|||
margin-right: $gl-spacing-scale-3;
|
||||
margin-left: $gl-spacing-scale-3;
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-icon {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
|
|
|
|||
|
|
@ -84,6 +84,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-datepicker-input {
|
||||
width: px-to-rem(165px);
|
||||
max-width: 100%;
|
||||
|
|
|
|||
|
|
@ -121,6 +121,7 @@ $comparison-empty-state-height: 62px;
|
|||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-dropdown-custom-toggle {
|
||||
width: 100%;
|
||||
}
|
||||
|
|
@ -139,6 +140,7 @@ $comparison-empty-state-height: 62px;
|
|||
}
|
||||
}
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.issuable-form-label-select-holder .gl-dropdown-toggle {
|
||||
@include media-breakpoint-up(md) {
|
||||
width: 250px;
|
||||
|
|
@ -304,6 +306,7 @@ $comparison-empty-state-height: 62px;
|
|||
.mr-compare-dropdown {
|
||||
width: 100%;
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-button-text {
|
||||
width: 100%;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -360,6 +360,7 @@
|
|||
margin-bottom: $gl-padding-8;
|
||||
}
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-button {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
|
@ -470,6 +471,7 @@
|
|||
display: none;
|
||||
}
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-skeleton-loader {
|
||||
display: block;
|
||||
}
|
||||
|
|
@ -817,6 +819,7 @@
|
|||
z-index: 199;
|
||||
white-space: nowrap;
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-dropdown-toggle {
|
||||
width: auto;
|
||||
max-width: 170px;
|
||||
|
|
@ -838,10 +841,12 @@
|
|||
top: 1px;
|
||||
margin: 0 $gl-spacing-scale-1;
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.dropdown-toggle.gl-button {
|
||||
padding: $gl-spacing-scale-2 2px $gl-spacing-scale-2 $gl-spacing-scale-2;
|
||||
font-weight: $gl-font-weight-bold;
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-button-icon {
|
||||
margin-left: $gl-spacing-scale-1;
|
||||
}
|
||||
|
|
@ -887,6 +892,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.submit-review-dropdown .gl-new-dropdown-panel {
|
||||
max-width: none;
|
||||
}
|
||||
|
|
@ -947,10 +953,12 @@
|
|||
}
|
||||
|
||||
.merge-request-notification-toggle {
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-toggle {
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-toggle-label {
|
||||
font-weight: $gl-font-weight-normal;
|
||||
}
|
||||
|
|
@ -1075,6 +1083,7 @@
|
|||
vertical-align: middle;
|
||||
}
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-skeleton-loader {
|
||||
max-width: 334px;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@
|
|||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-label-link {
|
||||
color: inherit;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
}
|
||||
|
||||
.invalid-dropdown {
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-button.gl-dropdown-toggle {
|
||||
@include inset-border-1-red-500;
|
||||
|
||||
|
|
@ -19,10 +20,12 @@
|
|||
}
|
||||
|
||||
.rotations-modal {
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-card {
|
||||
min-width: 75%;
|
||||
}
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
&.gl-modal .modal-md {
|
||||
max-width: 640px;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,12 +5,15 @@
|
|||
background-color: var(--green-50, $green-50);
|
||||
}
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-tabs-nav {
|
||||
border-bottom-width: 0;
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-tab-nav-item {
|
||||
color: var(--gray-500, $gray-500);
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
> .gl-tab-counter-badge {
|
||||
color: inherit;
|
||||
font-size: $gl-font-size-sm;
|
||||
|
|
|
|||
|
|
@ -124,10 +124,12 @@
|
|||
// These are single-value classes to use with utility-class style CSS.
|
||||
// They are here to still access a variable or because they use magic values.
|
||||
// scoped to the graph. Do not add other styles.
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-pipeline-min-h {
|
||||
min-height: calc(#{$dropdown-max-height-lg} + #{$gl-spacing-scale-6});
|
||||
}
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-pipeline-job-width {
|
||||
width: 100%;
|
||||
max-width: 400px;
|
||||
|
|
@ -137,6 +139,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-downstream-pipeline-job-width {
|
||||
width: 8rem;
|
||||
|
||||
|
|
@ -149,10 +152,12 @@
|
|||
}
|
||||
}
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-linked-pipeline-padding {
|
||||
padding-right: 120px;
|
||||
}
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-ci-action-icon-container {
|
||||
position: absolute;
|
||||
right: 5px;
|
||||
|
|
@ -170,6 +175,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.stage-column-title .gl-ci-action-icon-container {
|
||||
right: 11px;
|
||||
}
|
||||
|
|
@ -315,10 +321,12 @@
|
|||
}
|
||||
|
||||
.stage-column .ci-job-group-dropdown {
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
&,
|
||||
.gl-new-dropdown-custom-toggle {
|
||||
width: 100%;
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-badge.badge-muted {
|
||||
.gl-dark & {
|
||||
@apply gl-bg-gray-100;
|
||||
|
|
@ -328,6 +336,7 @@
|
|||
|
||||
// Reset padding, as inner element will
|
||||
// define padding
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-new-dropdown-item-content,
|
||||
.gl-new-dropdown-item-text-wrapper {
|
||||
padding: 0;
|
||||
|
|
@ -335,6 +344,7 @@
|
|||
|
||||
// Set artificial focus on the menu-item to keep
|
||||
// it consistent with the original dropdown items
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-new-dropdown-item:focus,
|
||||
.gl-new-dropdown-item-content:focus {
|
||||
outline: none;
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.file-tree-container > div.gl-overflow-y-auto {
|
||||
max-height: 220px;
|
||||
|
||||
|
|
|
|||
|
|
@ -96,6 +96,7 @@
|
|||
padding-left: 40px;
|
||||
}
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-label-scoped {
|
||||
--label-inset-border: inset 0 0 0 1px currentColor;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -462,6 +462,7 @@
|
|||
}
|
||||
|
||||
@media (min-width: $breakpoint-lg) {
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-card {
|
||||
width: calc(50% - 15px);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -273,6 +273,7 @@ $language-filter-max-height: 20rem;
|
|||
display: flex;
|
||||
}
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.btn-search,
|
||||
.btn-success,
|
||||
.dropdown-menu-toggle,
|
||||
|
|
@ -301,6 +302,7 @@ $language-filter-max-height: 20rem;
|
|||
width: 100%;
|
||||
}
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.dropdown-menu-toggle,
|
||||
.gl-dropdown {
|
||||
@include media-breakpoint-up(lg) {
|
||||
|
|
|
|||
|
|
@ -138,6 +138,7 @@
|
|||
}
|
||||
|
||||
.instance-runners-info {
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-alert-body {
|
||||
p:last-child {
|
||||
margin-bottom: 0;
|
||||
|
|
@ -166,12 +167,14 @@
|
|||
}
|
||||
|
||||
.prometheus-metrics-monitoring {
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-card {
|
||||
.badge.badge-pill {
|
||||
font-size: 12px;
|
||||
line-height: 12px;
|
||||
}
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-card-header .label-count {
|
||||
color: var(--white, $white);
|
||||
background: var(--gray-800, $gray-800);
|
||||
|
|
|
|||
|
|
@ -19,7 +19,9 @@
|
|||
padding-top: $gl-padding;
|
||||
}
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-card {
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-card-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
|
|
|||
|
|
@ -89,6 +89,7 @@
|
|||
color: var(--gl-text-color, $gl-text-color);
|
||||
}
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-label-scoped {
|
||||
--label-inset-border: inset 0 0 0 1px currentColor;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -162,6 +162,7 @@ ul.wiki-pages-list.content-list {
|
|||
max-width: 100%;
|
||||
}
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-disclosure-dropdown {
|
||||
display: none !important;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ $work-item-overview-right-sidebar-width: 20rem;
|
|||
$work-item-sticky-header-height: 52px;
|
||||
$work-item-overview-gap-width: 2rem;
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-token-selector-token-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
|
@ -32,12 +33,14 @@ $work-item-overview-gap-width: 2rem;
|
|||
}
|
||||
|
||||
.work-item-due-date {
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-datepicker-input.gl-form-input.form-control {
|
||||
width: 10rem;
|
||||
|
||||
&:not(:focus, :hover) {
|
||||
box-shadow: none;
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
~ .gl-datepicker-actions {
|
||||
display: none;
|
||||
}
|
||||
|
|
@ -47,12 +50,14 @@ $work-item-overview-gap-width: 2rem;
|
|||
background-color: var(--white, $white);
|
||||
box-shadow: none;
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
~ .gl-datepicker-actions {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-datepicker-actions:focus,
|
||||
.gl-datepicker-actions:hover {
|
||||
display: flex !important;
|
||||
|
|
@ -60,10 +65,12 @@ $work-item-overview-gap-width: 2rem;
|
|||
}
|
||||
|
||||
.work-item-labels {
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-token {
|
||||
padding-left: $gl-spacing-scale-1;
|
||||
}
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-token-close {
|
||||
display: none;
|
||||
}
|
||||
|
|
@ -78,15 +85,18 @@ $work-item-overview-gap-width: 2rem;
|
|||
|
||||
|
||||
.work-item-notifications-form {
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-toggle {
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-toggle-label {
|
||||
font-weight: $gl-font-weight-normal;
|
||||
}
|
||||
}
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-modal .work-item-view,
|
||||
.work-item-drawer .work-item-view:not(:has(.design-detail)) {
|
||||
container-name: work-item-view;
|
||||
|
|
@ -172,6 +182,7 @@ $work-item-overview-gap-width: 2rem;
|
|||
max-width: 65%;
|
||||
}
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
&.gl-form-select {
|
||||
&:hover,
|
||||
&:focus {
|
||||
|
|
@ -194,10 +205,12 @@ $work-item-overview-gap-width: 2rem;
|
|||
}
|
||||
|
||||
.work-item-notification-toggle {
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-toggle {
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-toggle-label {
|
||||
font-weight: normal;
|
||||
}
|
||||
|
|
@ -345,6 +358,7 @@ $disclosure-hierarchy-chevron-dimension: 1.2rem;
|
|||
}
|
||||
}
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.work-item-sidebar-dropdown .gl-new-dropdown-panel {
|
||||
width: 100% !important;
|
||||
max-width: 19rem !important;
|
||||
|
|
|
|||
|
|
@ -80,6 +80,7 @@
|
|||
color: $gl-text-color;
|
||||
}
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
&.gl-inline-flex {
|
||||
gap: 0.5ch;
|
||||
}
|
||||
|
|
@ -119,6 +120,7 @@
|
|||
.commit-sha-group {
|
||||
display: inline-flex;
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.label,
|
||||
.btn:not(.gl-button) {
|
||||
padding: $gl-vert-padding $gl-btn-padding;
|
||||
|
|
|
|||
|
|
@ -277,10 +277,12 @@ ul.related-merge-requests > li gl-emoji {
|
|||
top: $calc-application-header-height;
|
||||
}
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-drawer .md-header {
|
||||
top: 0;
|
||||
}
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-modal .md-header {
|
||||
top: -$gl-padding-8;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -124,13 +124,14 @@
|
|||
flex-flow: row wrap;
|
||||
width: 100%;
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-float-right {
|
||||
// Flexbox quirk to make sure right-aligned items stay right-aligned.
|
||||
margin-left: auto;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.md-header .gl-tabs-nav {
|
||||
border-bottom: 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -81,6 +81,7 @@ $system-note-icon-m-left: $avatar-m-left + $icon-size-diff / $avatar-m-ratio;
|
|||
height: 2rem;
|
||||
}
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-avatar {
|
||||
border-color: var(--gray-50, $gray-50);
|
||||
}
|
||||
|
|
@ -378,6 +379,7 @@ $system-note-icon-m-left: $avatar-m-left + $icon-size-diff / $avatar-m-ratio;
|
|||
text-transform: lowercase;
|
||||
}
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
a:not(.gl-link) {
|
||||
color: $blue-600;
|
||||
}
|
||||
|
|
@ -823,10 +825,12 @@ $system-note-icon-m-left: $avatar-m-left + $icon-size-diff / $avatar-m-ratio;
|
|||
text-decoration: underline;
|
||||
}
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-label-link:hover {
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-label-text:last-of-type {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
|
@ -1005,6 +1009,7 @@ $system-note-icon-m-left: $avatar-m-left + $icon-size-diff / $avatar-m-ratio;
|
|||
padding: 0 8px !important;
|
||||
box-shadow: none !important;
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-button-loading-indicator {
|
||||
margin-right: 0 !important;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
// until this gitlab-ui issue is resolved: https://gitlab.com/gitlab-org/gitlab-ui/-/issues/1079
|
||||
//
|
||||
// See app/assets/javascripts/registry/explorer/components/registry_breadcrumb.vue when this is changed.
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.breadcrumbs .gl-breadcrumbs {
|
||||
padding: 0;
|
||||
box-shadow: none;
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@
|
|||
color: initial;
|
||||
}
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-link,
|
||||
.gl-button {
|
||||
color: $white;
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
margin: 20px;
|
||||
font-weight: $gl-font-weight-normal;
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-snippet-icon {
|
||||
display: inline-block;
|
||||
background: url('ext_snippet_icons/ext_snippet_icons.png') no-repeat;
|
||||
|
|
@ -22,9 +23,13 @@
|
|||
height: 16px;
|
||||
background-size: cover;
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
&.gl-snippet-icon-doc-code { background-position: 0 0; }
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
&.gl-snippet-icon-doc-text { background-position: 0 -16px; }
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
&.gl-snippet-icon-download { background-position: 0 -32px; }
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
&.gl-snippet-icon-copy-to-clipboard { background-position: 0 -48px; }
|
||||
}
|
||||
|
||||
|
|
@ -136,6 +141,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
img,
|
||||
.gl-snippet-icon {
|
||||
display: inline-block;
|
||||
|
|
|
|||
|
|
@ -45,16 +45,19 @@
|
|||
top: $calc-system-headers-height;
|
||||
}
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-top-app-header {
|
||||
top: $calc-application-header-height;
|
||||
}
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-children-ml-sm-3 > * {
|
||||
@include media-breakpoint-up(sm) {
|
||||
margin-left: $gl-spacing-scale-3;
|
||||
}
|
||||
}
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-first-child-ml-sm-0 > a:first-child,
|
||||
.gl-first-child-ml-sm-0 > button:first-child {
|
||||
@include media-breakpoint-up(sm) {
|
||||
|
|
@ -69,22 +72,29 @@
|
|||
min-width: 0;
|
||||
}
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-w-16 { width: px-to-rem($grid-size * 2); }
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-w-64 { width: px-to-rem($grid-size * 8); }
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-h-32 { height: px-to-rem($grid-size * 4); }
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-h-64 { height: px-to-rem($grid-size * 8); }
|
||||
|
||||
// Migrate this to Gitlab UI when FF is removed
|
||||
// https://gitlab.com/groups/gitlab-org/-/epics/2882
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-h-200\! { height: px-to-rem($grid-size * 25) !important; }
|
||||
|
||||
// This utility is used to force the z-index to match that of dropdown menu's
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-z-dropdown-menu\! {
|
||||
z-index: $zindex-dropdown-menu !important;
|
||||
}
|
||||
|
||||
// This is used to help prevent issues with margin collapsing.
|
||||
// See https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Box_Model/Mastering_margin_collapsing.
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-force-block-formatting-context::after {
|
||||
content: '';
|
||||
display: flex;
|
||||
|
|
@ -102,6 +112,7 @@
|
|||
|
||||
See: https://developer.mozilla.org/en-US/docs/Web/CSS/::-webkit-scrollbar
|
||||
**/
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-webkit-scrollbar-display-none {
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
|
|
@ -109,39 +120,47 @@
|
|||
}
|
||||
|
||||
// Will be moved to @gitlab/ui by https://gitlab.com/gitlab-org/gitlab-ui/-/issues/1465
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-focus-ring-border-1-gray-900\! {
|
||||
@include gl-focus($gl-border-size-1, $gray-900, true);
|
||||
}
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-sm-mb-5 {
|
||||
@include gl-media-breakpoint-down(md) {
|
||||
margin-bottom: $gl-spacing-scale-5;
|
||||
}
|
||||
}
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-fill-orange-500 {
|
||||
fill: $orange-500;
|
||||
}
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-fill-red-500 {
|
||||
fill: $red-500;
|
||||
}
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-last-of-type-border-b-0:last-of-type {
|
||||
border-bottom-width: 0;
|
||||
}
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-md-h-9 {
|
||||
@include gl-media-breakpoint-up(md) {
|
||||
height: $gl-spacing-scale-9;
|
||||
}
|
||||
}
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-pl-12 {
|
||||
padding-left: $gl-spacing-scale-12;
|
||||
}
|
||||
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-min-w-12 {
|
||||
min-width: $gl-spacing-scale-12;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@ class Admin::HooksController < Admin::ApplicationController
|
|||
|
||||
urgency :low, [:test]
|
||||
|
||||
before_action :not_found, unless: -> { system_hooks? }
|
||||
|
||||
def test
|
||||
result = TestHooks::SystemService.new(hook, current_user, params[:trigger]).execute
|
||||
|
||||
|
|
@ -30,4 +32,8 @@ class Admin::HooksController < Admin::ApplicationController
|
|||
def trigger_values
|
||||
SystemHook.triggers.values
|
||||
end
|
||||
|
||||
def system_hooks?
|
||||
!Gitlab.com? # rubocop:disable Gitlab/AvoidGitlabInstanceChecks -- Not related to SaaS offerings
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ class Projects::BoardsController < Projects::ApplicationController
|
|||
before_action :check_issues_available!
|
||||
before_action do
|
||||
push_frontend_feature_flag(:board_multi_select, project)
|
||||
push_frontend_feature_flag(:issues_list_drawer, project)
|
||||
end
|
||||
|
||||
feature_category :team_planning
|
||||
|
|
|
|||
|
|
@ -24,10 +24,15 @@ module BoardsHelper
|
|||
board_type: board.to_type,
|
||||
has_missing_boards: has_missing_boards?.to_s,
|
||||
multiple_boards_available: multiple_boards_available?.to_s,
|
||||
board_base_url: board_base_url
|
||||
board_base_url: board_base_url,
|
||||
wi: work_items_show_data(board_namespace)
|
||||
}
|
||||
end
|
||||
|
||||
def board_namespace
|
||||
board.group_board? ? @group : @project
|
||||
end
|
||||
|
||||
def group_id
|
||||
return @group.id if board.group_board?
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ class KeyPresenter < Gitlab::View::Presenter::Delegated # rubocop:disable Gitlab
|
|||
if !key_object.public_key.valid?
|
||||
help_link = help_page_link(_('supported SSH public key.'), 'user/ssh', 'supported-ssh-key-types')
|
||||
|
||||
_('%{type} must be a %{help_link}').html_safe % { type: type.to_s.titleize, help_link: help_link }
|
||||
_('%{type} must be a %{help_link}').html_safe % { type: type.to_s.humanize, help_link: help_link }
|
||||
else
|
||||
key_object.errors.full_messages.join(', ').html_safe
|
||||
end
|
||||
|
|
|
|||
|
|
@ -36,4 +36,4 @@
|
|||
- else
|
||||
.empty-state
|
||||
.text-center
|
||||
%h4= _("No connection could be made to a Gitaly Server, please check your logs!")
|
||||
%h4= _("No connection could be made to a Gitaly server, please check your logs!")
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@
|
|||
%span.cgray= n_('parent', 'parents', @commit.parents.count)
|
||||
- @commit.parents.each do |parent|
|
||||
= link_to parent.short_id, project_commit_path(@project, parent), class: "commit-sha"
|
||||
#js-commit-branches-and-tags{ data: { full_path: @project.full_path, commit_sha: @commit.short_id } }
|
||||
#js-commit-branches-and-tags{ data: { full_path: @project.full_path, commit_sha: @commit.id } }
|
||||
|
||||
.well-segment.merge-request-info
|
||||
.icon-container
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
- page_title _('Edit Deploy Key')
|
||||
%h1.page-title.gl-font-size-h-display= _('Edit Deploy Key')
|
||||
- page_title _('Edit deploy key')
|
||||
%h1.page-title.gl-font-size-h-display= _('Edit deploy key')
|
||||
|
||||
= gitlab_ui_form_for [@project, @deploy_key], include_id: false, html: { class: 'js-requires-input' } do |f|
|
||||
= render partial: 'shared/deploy_keys/form', locals: { form: f, deploy_key: @deploy_key }
|
||||
|
|
|
|||
|
|
@ -18,12 +18,14 @@ module ConcurrencyLimit
|
|||
reschedule_job = false
|
||||
|
||||
workers.each do |worker|
|
||||
next unless jobs_in_the_queue?(worker)
|
||||
limit = ::Gitlab::SidekiqMiddleware::ConcurrencyLimit::WorkersMap.limit_for(worker: worker)&.call
|
||||
queue_size = queue_size(worker)
|
||||
report_prometheus_metrics(worker, queue_size, limit)
|
||||
|
||||
next unless queue_size > 0
|
||||
|
||||
reschedule_job = true
|
||||
|
||||
limit = ::Gitlab::SidekiqMiddleware::ConcurrencyLimit::WorkersMap.limit_for(worker: worker)&.call
|
||||
|
||||
processing_limit = if limit
|
||||
current = current_concurrency(worker: worker)
|
||||
limit - current
|
||||
|
|
@ -49,8 +51,8 @@ module ConcurrencyLimit
|
|||
@current_concurrency[worker.name].to_i
|
||||
end
|
||||
|
||||
def jobs_in_the_queue?(worker)
|
||||
Gitlab::SidekiqMiddleware::ConcurrencyLimit::ConcurrencyLimitService.has_jobs_in_queue?(worker.name)
|
||||
def queue_size(worker)
|
||||
Gitlab::SidekiqMiddleware::ConcurrencyLimit::ConcurrencyLimitService.queue_size(worker.name)
|
||||
end
|
||||
|
||||
def resume_processing!(worker, limit:)
|
||||
|
|
@ -60,5 +62,18 @@ module ConcurrencyLimit
|
|||
def workers
|
||||
Gitlab::SidekiqMiddleware::ConcurrencyLimit::WorkersMap.workers
|
||||
end
|
||||
|
||||
def report_prometheus_metrics(worker, queue_size, limit)
|
||||
queue_size_metric = Gitlab::Metrics.gauge(:sidekiq_concurrency_limit_queue_jobs,
|
||||
'Number of jobs queued by the concurrency limit middleware.',
|
||||
{},
|
||||
:max)
|
||||
queue_size_metric.set({ worker: worker.name }, queue_size)
|
||||
|
||||
limit_metric = Gitlab::Metrics.gauge(:sidekiq_concurrency_limit_max_concurrent_jobs,
|
||||
'Max number of concurrent running jobs.',
|
||||
{})
|
||||
limit_metric.set({ worker: worker.name }, limit || DEFAULT_LIMIT)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,9 +0,0 @@
|
|||
---
|
||||
name: synced_epic_work_item_editable
|
||||
feature_issue_url:
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/156648
|
||||
rollout_issue_url:
|
||||
milestone: '17.2'
|
||||
group: group::product planning
|
||||
type: wip
|
||||
default_enabled: false
|
||||
|
|
@ -323,13 +323,13 @@ To merge topics:
|
|||
|
||||
## Administering Gitaly servers
|
||||
|
||||
You can list all Gitaly servers in the GitLab instance from the Admin area's **Gitaly Servers**
|
||||
You can list all Gitaly servers in the GitLab instance from the Admin area's **Gitaly servers**
|
||||
page. For more details, see [Gitaly](gitaly/index.md).
|
||||
|
||||
To access the **Gitaly Servers** page:
|
||||
To access the **Gitaly servers** page:
|
||||
|
||||
1. On the left sidebar, at the bottom, select **Admin area**.
|
||||
1. Select **Overview > Gitaly Servers**.
|
||||
1. Select **Overview > Gitaly servers**.
|
||||
|
||||
For each Gitaly server, the following details are listed:
|
||||
|
||||
|
|
@ -431,7 +431,7 @@ For each job, the following details are listed:
|
|||
|
||||
The following topics document the **Monitoring** section of the Admin area.
|
||||
|
||||
### System Information
|
||||
### System information
|
||||
|
||||
> - Support for relative time [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/341248) in GitLab 15.2. "Uptime" statistic was renamed to "System started".
|
||||
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ When using standalone Gitaly servers, you must make sure they are the same versi
|
|||
as GitLab to ensure full compatibility:
|
||||
|
||||
1. On the left sidebar, at the bottom, select **Admin area**.
|
||||
1. Select **Overview > Gitaly Servers**.
|
||||
1. Select **Overview > Gitaly servers**.
|
||||
1. Confirm all Gitaly servers indicate that they are up to date.
|
||||
|
||||
## Find storage resource details
|
||||
|
|
|
|||
|
|
@ -232,6 +232,8 @@ configuration option in `gitlab.yml`. These metrics are served from the
|
|||
| `sidekiq_running_jobs` | Gauge | 12.2 | Number of Sidekiq jobs running | `queue`, `boundary`, `external_dependencies`, `feature_category`, `urgency` |
|
||||
| `sidekiq_concurrency` | Gauge | 12.5 | Maximum number of Sidekiq jobs | |
|
||||
| `sidekiq_mem_total_bytes` | Gauge | 15.3 | Number of bytes allocated for both objects consuming an object slot and objects that required a malloc'| |
|
||||
| `sidekiq_concurrency_limit_queue_jobs` | Gauge | 17.3 | Number of Sidekiq jobs waiting in the concurrency limit queue| `worker` |
|
||||
| `sidekiq_concurrency_limit_max_concurrent_jobs` | Gauge | 17.3 | Max number of concurrent running Sidekiq jobs | `worker` |
|
||||
| `geo_db_replication_lag_seconds` | Gauge | 10.2 | Database replication lag (seconds) | `url` |
|
||||
| `geo_repositories` | Gauge | 10.2 | Total number of repositories available on primary | `url` |
|
||||
| `geo_lfs_objects` | Gauge | 10.2 | Number of LFS objects on primary | `url` |
|
||||
|
|
|
|||
|
|
@ -385,6 +385,20 @@ four standard [pagination arguments](#pagination-arguments):
|
|||
| ---- | ---- | ----------- |
|
||||
| <a id="queryduoworkfloweventsworkflowid"></a>`workflowId` | [`AiDuoWorkflowsWorkflowID!`](#aiduoworkflowsworkflowid) | Array of request IDs to fetch. |
|
||||
|
||||
### `Query.duoWorkflowWorkflows`
|
||||
|
||||
List the workflows owned by the current user.
|
||||
|
||||
DETAILS:
|
||||
**Introduced** in GitLab 17.2.
|
||||
**Status**: Experiment.
|
||||
|
||||
Returns [`DuoWorkflowConnection!`](#duoworkflowconnection).
|
||||
|
||||
This field returns a [connection](#connections). It accepts the
|
||||
four standard [pagination arguments](#pagination-arguments):
|
||||
`before: String`, `after: String`, `first: Int`, and `last: Int`.
|
||||
|
||||
### `Query.echo`
|
||||
|
||||
Testing endpoint to validate the API with.
|
||||
|
|
@ -12554,6 +12568,29 @@ The edge type for [`DoraPerformanceScoreCount`](#doraperformancescorecount).
|
|||
| <a id="doraperformancescorecountedgecursor"></a>`cursor` | [`String!`](#string) | A cursor for use in pagination. |
|
||||
| <a id="doraperformancescorecountedgenode"></a>`node` | [`DoraPerformanceScoreCount`](#doraperformancescorecount) | The item at the end of the edge. |
|
||||
|
||||
#### `DuoWorkflowConnection`
|
||||
|
||||
The connection type for [`DuoWorkflow`](#duoworkflow).
|
||||
|
||||
##### Fields
|
||||
|
||||
| Name | Type | Description |
|
||||
| ---- | ---- | ----------- |
|
||||
| <a id="duoworkflowconnectionedges"></a>`edges` | [`[DuoWorkflowEdge]`](#duoworkflowedge) | A list of edges. |
|
||||
| <a id="duoworkflowconnectionnodes"></a>`nodes` | [`[DuoWorkflow]`](#duoworkflow) | A list of nodes. |
|
||||
| <a id="duoworkflowconnectionpageinfo"></a>`pageInfo` | [`PageInfo!`](#pageinfo) | Information to aid in pagination. |
|
||||
|
||||
#### `DuoWorkflowEdge`
|
||||
|
||||
The edge type for [`DuoWorkflow`](#duoworkflow).
|
||||
|
||||
##### Fields
|
||||
|
||||
| Name | Type | Description |
|
||||
| ---- | ---- | ----------- |
|
||||
| <a id="duoworkflowedgecursor"></a>`cursor` | [`String!`](#string) | A cursor for use in pagination. |
|
||||
| <a id="duoworkflowedgenode"></a>`node` | [`DuoWorkflow`](#duoworkflow) | The item at the end of the edge. |
|
||||
|
||||
#### `DuoWorkflowEventConnection`
|
||||
|
||||
The connection type for [`DuoWorkflowEvent`](#duoworkflowevent).
|
||||
|
|
@ -20766,6 +20803,21 @@ Aggregated DORA score counts for projects for the last complete month.
|
|||
| <a id="doraperformancescorecountmetricname"></a>`metricName` | [`String!`](#string) | Name of the DORA metric. |
|
||||
| <a id="doraperformancescorecountnodataprojectscount"></a>`noDataProjectsCount` | [`Int`](#int) | Number of projects with no data for the metric. |
|
||||
|
||||
### `DuoWorkflow`
|
||||
|
||||
A Duo Workflow.
|
||||
|
||||
#### Fields
|
||||
|
||||
| Name | Type | Description |
|
||||
| ---- | ---- | ----------- |
|
||||
| <a id="duoworkflowcreatedat"></a>`createdAt` | [`Time!`](#time) | Timestamp of when the workflow was created. |
|
||||
| <a id="duoworkflowhumanstatus"></a>`humanStatus` | [`String!`](#string) | Human-readable status of the workflow. |
|
||||
| <a id="duoworkflowid"></a>`id` | [`ID!`](#id) | ID of the workflow. |
|
||||
| <a id="duoworkflowprojectid"></a>`projectId` | [`ProjectID!`](#projectid) | ID of the project. |
|
||||
| <a id="duoworkflowupdatedat"></a>`updatedAt` | [`Time!`](#time) | Timestamp of when the workflow was last updated. |
|
||||
| <a id="duoworkflowuserid"></a>`userId` | [`UserID!`](#userid) | ID of the user. |
|
||||
|
||||
### `DuoWorkflowEvent`
|
||||
|
||||
Events that describe the history and progress of a Duo Workflow.
|
||||
|
|
|
|||
|
|
@ -129,7 +129,7 @@ We'll use this variable in the `.gitlab-ci.yml` later, to easily connect to our
|
|||
|
||||

|
||||
|
||||
We also need to add the public key to **Project** > **Settings** > **Repository** as a [Deploy Key](../../../user/project/deploy_keys/index.md), which gives us the ability to access our repository from the server through the SSH protocol.
|
||||
We also need to add the public key to **Project** > **Settings** > **Repository** as a [deploy key](../../../user/project/deploy_keys/index.md), which gives us the ability to access our repository from the server through the SSH protocol.
|
||||
|
||||
```shell
|
||||
# As the deployer user on the server
|
||||
|
|
|
|||
|
|
@ -158,6 +158,7 @@ If multiple topics are provided, all topics must match for the project to be inc
|
|||
### AI Impact analytics
|
||||
|
||||
DETAILS:
|
||||
**Tier:** For a limited time, Ultimate. In the future, GitLab Duo Enterprise.
|
||||
**Offering:** GitLab.com, Self-managed
|
||||
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/443696) in GitLab 16.11 [with a flag](../../administration/feature_flags.md) named `ai_impact_analytics_dashboard`. Disabled by default.
|
||||
|
|
|
|||
|
|
@ -709,7 +709,7 @@ Unlike regular container scanning, the scan results do not include a security re
|
|||
When security findings are identified, GitLab populates the [Vulnerability Report](../vulnerability_report/index.md) with these findings. Vulnerabilities can be viewed under the **Container registry vulnerabilities** tab of the Vulnerability Report page.
|
||||
|
||||
NOTE:
|
||||
Container Scanning for Registry only populates the Vulnerability Report when a new advisory is published to the [GitLab Advisory Database](../gitlab_advisory_database/index.md). Future iterations will populate the Vulnerability Report with all present advisory data (instead of only newely detected data). For more details, see [epic 8026](https://gitlab.com/groups/gitlab-org/-/epics/8026).
|
||||
Container Scanning for Registry populates the Vulnerability Report only when a new advisory is published to the [GitLab Advisory Database](../gitlab_advisory_database/index.md). Support for populating the Vulnerability Report with all present advisory data, instead of only newly-detected data, is proposed in [epic 8026](https://gitlab.com/groups/gitlab-org/-/epics/8026).
|
||||
|
||||
### Prerequisites
|
||||
|
||||
|
|
|
|||
|
|
@ -96,7 +96,7 @@ Streaming audit events can be sent when authenticated users push, pull, or clone
|
|||
|
||||
Audit events are not captured for users that are not signed in. For example, when downloading a public project.
|
||||
|
||||
### Example: audit event payloads for Git over SSH events with Deploy Key
|
||||
### Example: audit event payloads for Git over SSH events with deploy key
|
||||
|
||||
Fetch:
|
||||
|
||||
|
|
|
|||
|
|
@ -50,7 +50,6 @@ Because this is an experimental feature,
|
|||
| Flag | Description | Actor | Status | Milestone |
|
||||
| --------------------------------------------- | -------------------------------------------------------------------------------------------------------- | ----- | ------ | ------ |
|
||||
| `work_item_epics` | Consolidated flag that contains all the changes needed to get epic work items to work for a given group. | Group | **Required** | 17.2 |
|
||||
| `synced_epic_work_item_editable` | Allows editing epic work items when they have a legacy epic. | Group | **Required** | 17.2 |
|
||||
| `work_items_rolledup_dates` | Calculates the start and due dates in a hierarchy for work items. | Group | **Required** | 17.2 |
|
||||
| `epic_and_work_item_associations_unification` | Delegates other epic and work item associations. | Group | **Required** | 17.2 |
|
||||
| `work_item_epics_rollout` | Feature flag per user to enable or disable the new work item view as the default experience. | User | **Required** | 17.3 |
|
||||
|
|
|
|||
|
|
@ -204,7 +204,7 @@ module API
|
|||
if key
|
||||
present key, with: Entities::DeployKey
|
||||
else
|
||||
not_found!('Deploy Key')
|
||||
not_found!('Deploy key')
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -222,7 +222,7 @@ module API
|
|||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
delete ":id/deploy_keys/:key_id" do
|
||||
deploy_key_project = user_project.deploy_keys_projects.find_by(deploy_key_id: params[:key_id])
|
||||
not_found!('Deploy Key') unless deploy_key_project
|
||||
not_found!('Deploy key') unless deploy_key_project
|
||||
|
||||
destroy_conditionally!(deploy_key_project)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ module Gitlab
|
|||
end
|
||||
|
||||
def name
|
||||
@name || _('Deploy Key')
|
||||
@name || _('Deploy key')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ module Sidebars
|
|||
add_menu(Sidebars::Admin::Menus::AnalyticsMenu.new(context))
|
||||
add_menu(Sidebars::Admin::Menus::MonitoringMenu.new(context))
|
||||
add_menu(Sidebars::Admin::Menus::MessagesMenu.new(context))
|
||||
add_menu(Sidebars::Admin::Menus::SystemHooksMenu.new(context))
|
||||
add_menu(Sidebars::Admin::Menus::SystemHooksMenu.new(context)) if system_hooks?
|
||||
add_menu(Sidebars::Admin::Menus::ApplicationsMenu.new(context))
|
||||
add_menu(Sidebars::Admin::Menus::AbuseReportsMenu.new(context))
|
||||
add_menu(Sidebars::Admin::Menus::KubernetesMenu.new(context))
|
||||
|
|
@ -34,6 +34,12 @@ module Sidebars
|
|||
add_menu(Sidebars::Admin::Menus::LabelsMenu.new(context))
|
||||
add_menu(Sidebars::Admin::Menus::AdminSettingsMenu.new(context))
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def system_hooks?
|
||||
!Gitlab.com? # rubocop:disable Gitlab/AvoidGitlabInstanceChecks -- Not related to SaaS offerings
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -17903,9 +17903,6 @@ msgid_plural "Deploys"
|
|||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
|
||||
msgid "Deploy Key"
|
||||
msgstr ""
|
||||
|
||||
msgid "Deploy Token"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -17915,6 +17912,9 @@ msgstr ""
|
|||
msgid "Deploy freezes"
|
||||
msgstr ""
|
||||
|
||||
msgid "Deploy key"
|
||||
msgstr ""
|
||||
|
||||
msgid "Deploy key was successfully updated."
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -19679,9 +19679,6 @@ msgstr ""
|
|||
msgid "Edit Comment"
|
||||
msgstr ""
|
||||
|
||||
msgid "Edit Deploy Key"
|
||||
msgstr ""
|
||||
|
||||
msgid "Edit Geo Site"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -34878,7 +34875,7 @@ msgstr ""
|
|||
msgid "No confirmation email received? Check your spam folder or %{request_link_start}request new confirmation email%{request_link_end}."
|
||||
msgstr ""
|
||||
|
||||
msgid "No connection could be made to a Gitaly Server, please check your logs!"
|
||||
msgid "No connection could be made to a Gitaly server, please check your logs!"
|
||||
msgstr ""
|
||||
|
||||
msgid "No contributions"
|
||||
|
|
|
|||
|
|
@ -113,7 +113,7 @@ class Cli
|
|||
def proceed_to_event_definition
|
||||
new_page!
|
||||
|
||||
cli.say format_info("Okay! The next step is adding a new event! (~5 min)\n")
|
||||
cli.say format_info("Okay! The next step is adding a new event! (~5-10 min)\n")
|
||||
|
||||
return not_ready_error('New Event') unless cli.yes?(format_prompt('Ready to start?'))
|
||||
|
||||
|
|
|
|||
|
|
@ -9,12 +9,42 @@ RSpec.describe Admin::HooksController, feature_category: :webhooks do
|
|||
sign_in(admin)
|
||||
end
|
||||
|
||||
shared_examples 'disabled on GitLab.com' do
|
||||
let(:gitlab_com?) { false }
|
||||
|
||||
before do
|
||||
allow(::Gitlab).to receive(:com?) { gitlab_com? }
|
||||
end
|
||||
|
||||
context 'when on GitLab.com' do
|
||||
let(:gitlab_com?) { true }
|
||||
|
||||
it 'responds with a not_found status' do
|
||||
subject
|
||||
expect(response).to have_gitlab_http_status(:not_found)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when not on GitLab.com' do
|
||||
it 'does not respond with a not_found status' do
|
||||
subject
|
||||
expect(response).not_to have_gitlab_http_status(:not_found)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'GET #index' do
|
||||
subject(:get_index) { get :index }
|
||||
|
||||
it_behaves_like 'disabled on GitLab.com'
|
||||
end
|
||||
|
||||
describe 'POST #create' do
|
||||
it 'sets all parameters' do
|
||||
hook_params = {
|
||||
let_it_be(:hook_params) do
|
||||
{
|
||||
enable_ssl_verification: true,
|
||||
token: "TEST TOKEN",
|
||||
url: "http://example.com",
|
||||
token: 'TEST TOKEN',
|
||||
url: 'http://example.com',
|
||||
|
||||
push_events: true,
|
||||
tag_push_events: false,
|
||||
|
|
@ -22,39 +52,27 @@ RSpec.describe Admin::HooksController, feature_category: :webhooks do
|
|||
merge_requests_events: false,
|
||||
url_variables: [{ key: 'token', value: 'some secret value' }]
|
||||
}
|
||||
end
|
||||
|
||||
post :create, params: { hook: hook_params }
|
||||
subject(:post_create) { post :create, params: { hook: hook_params } }
|
||||
|
||||
it 'sets all parameters' do
|
||||
post_create
|
||||
|
||||
expect(response).to have_gitlab_http_status(:found)
|
||||
expect(SystemHook.all.size).to eq(1)
|
||||
expect(SystemHook.first).to have_attributes(hook_params.except(:url_variables))
|
||||
expect(SystemHook.first).to have_attributes(url_variables: { 'token' => 'some secret value' })
|
||||
end
|
||||
|
||||
it_behaves_like 'disabled on GitLab.com'
|
||||
end
|
||||
|
||||
describe 'POST #update' do
|
||||
let_it_be_with_reload(:hook) { create(:system_hook) }
|
||||
|
||||
context 'with an existing token' do
|
||||
hook_params = {
|
||||
token: WebHook::SECRET_MASK,
|
||||
url: "http://example.com"
|
||||
}
|
||||
|
||||
it 'does not change a token' do
|
||||
expect do
|
||||
post :update, params: { id: hook.id, hook: hook_params }
|
||||
end.not_to change { hook.reload.token }
|
||||
|
||||
expect(response).to have_gitlab_http_status(:found)
|
||||
expect(flash[:alert]).to be_blank
|
||||
end
|
||||
end
|
||||
|
||||
it 'sets all parameters' do
|
||||
hook.update!(url_variables: { 'foo' => 'bar', 'baz' => 'woo' })
|
||||
|
||||
hook_params = {
|
||||
let_it_be(:hook_params) do
|
||||
{
|
||||
url: 'http://example.com/{bar}?token={token}',
|
||||
enable_ssl_verification: false,
|
||||
url_variables: [
|
||||
|
|
@ -64,8 +82,30 @@ RSpec.describe Admin::HooksController, feature_category: :webhooks do
|
|||
{ key: 'bar', value: 'qux' }
|
||||
]
|
||||
}
|
||||
end
|
||||
|
||||
put :update, params: { id: hook.id, hook: hook_params }
|
||||
subject(:put_update) { put :update, params: { id: hook.id, hook: hook_params } }
|
||||
|
||||
context 'with an existing token' do
|
||||
let_it_be(:hook_params) do
|
||||
{
|
||||
token: WebHook::SECRET_MASK,
|
||||
url: 'http://example.com'
|
||||
}
|
||||
end
|
||||
|
||||
it 'does not change a token' do
|
||||
expect { put_update }.not_to change { hook.reload.token }
|
||||
|
||||
expect(response).to have_gitlab_http_status(:found)
|
||||
expect(flash[:alert]).to be_blank
|
||||
end
|
||||
end
|
||||
|
||||
it 'sets all parameters' do
|
||||
hook.update!(url_variables: { 'foo' => 'bar', 'baz' => 'woo' })
|
||||
|
||||
put_update
|
||||
|
||||
hook.reload
|
||||
|
||||
|
|
@ -76,6 +116,8 @@ RSpec.describe Admin::HooksController, feature_category: :webhooks do
|
|||
url_variables: { 'token' => 'some secret value', 'bar' => 'qux' }
|
||||
)
|
||||
end
|
||||
|
||||
it_behaves_like 'disabled on GitLab.com'
|
||||
end
|
||||
|
||||
describe 'DELETE #destroy' do
|
||||
|
|
@ -84,5 +126,9 @@ RSpec.describe Admin::HooksController, feature_category: :webhooks do
|
|||
let(:params) { { id: hook } }
|
||||
|
||||
it_behaves_like 'Web hook destroyer'
|
||||
|
||||
it_behaves_like 'disabled on GitLab.com' do
|
||||
subject { delete :destroy, params: params }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -147,7 +147,7 @@ RSpec.describe Projects::DeployKeysController, feature_category: :continuous_del
|
|||
post :create, params: create_params
|
||||
|
||||
expect(assigns(:key).errors.count).to be > 1
|
||||
expect(flash[:alert]).to eq('Deploy Key must be a <a target="_blank" rel="noopener noreferrer" ' \
|
||||
expect(flash[:alert]).to eq('Deploy key must be a <a target="_blank" rel="noopener noreferrer" ' \
|
||||
'href="/help/user/ssh#supported-ssh-key-types">supported SSH public key.</a>')
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -13,220 +13,226 @@ RSpec.describe 'Issue Boards new issue', :js, feature_category: :portfolio_manag
|
|||
let(:board_list_header) { first('[data-testid="board-list-header"]') }
|
||||
let(:project_select_dropdown) { find_by_testid('project-select-dropdown') }
|
||||
|
||||
context 'authorized user' do
|
||||
context 'when issues drawer is disabled' do
|
||||
before do
|
||||
project.add_maintainer(user)
|
||||
|
||||
sign_in(user)
|
||||
|
||||
visit project_board_path(project, board)
|
||||
|
||||
wait_for_requests
|
||||
|
||||
expect(page).to have_selector('.board', count: 3)
|
||||
stub_feature_flags(issues_list_drawer: false)
|
||||
end
|
||||
|
||||
it 'displays new issue button' do
|
||||
expect(first('.board')).to have_button('Create new issue', count: 1)
|
||||
end
|
||||
context 'authorized user' do
|
||||
before do
|
||||
project.add_maintainer(user)
|
||||
|
||||
it 'does not display new issue button in closed list' do
|
||||
page.within('.board:nth-child(3)') do
|
||||
expect(page).not_to have_button('Create new issue')
|
||||
end
|
||||
end
|
||||
sign_in(user)
|
||||
|
||||
it 'shows form when clicking button' do
|
||||
page.within(first('.board')) do
|
||||
click_button 'Create new issue'
|
||||
|
||||
expect(page).to have_selector('.board-new-issue-form')
|
||||
end
|
||||
end
|
||||
|
||||
it 'hides form when clicking cancel' do
|
||||
page.within(first('.board')) do
|
||||
click_button 'Create new issue'
|
||||
|
||||
expect(page).to have_selector('.board-new-issue-form')
|
||||
|
||||
click_button 'Cancel'
|
||||
|
||||
expect(page).not_to have_selector('.board-new-issue-form')
|
||||
end
|
||||
end
|
||||
|
||||
it 'creates new issue, places it on top of the list, and opens sidebar' do
|
||||
page.within(first('.board')) do
|
||||
click_button 'Create new issue'
|
||||
end
|
||||
|
||||
page.within(first('.board-new-issue-form')) do
|
||||
find('.form-control').set('bug')
|
||||
click_button 'Create issue'
|
||||
end
|
||||
|
||||
wait_for_requests
|
||||
|
||||
page.within(first('.board [data-testid="issue-count-badge"]')) do
|
||||
expect(page).to have_content('2')
|
||||
end
|
||||
|
||||
page.within(first('.board-card')) do
|
||||
issue = project.issues.find_by_title('bug')
|
||||
|
||||
expect(issue.relative_position).to be < existing_issue.relative_position
|
||||
|
||||
expect(page).to have_content(issue.to_reference)
|
||||
expect(page).to have_link(issue.title, href: /#{issue_path(issue)}/)
|
||||
end
|
||||
|
||||
expect(page).to have_selector('[data-testid="issue-boards-sidebar"]')
|
||||
end
|
||||
|
||||
it 'successfully loads labels to be added to newly created issue' do
|
||||
page.within(first('.board')) do
|
||||
click_button 'Create new issue'
|
||||
end
|
||||
|
||||
page.within(first('.board-new-issue-form')) do
|
||||
find('.form-control').set('new issue')
|
||||
click_button 'Create issue'
|
||||
end
|
||||
|
||||
wait_for_requests
|
||||
|
||||
within_testid('sidebar-labels') do
|
||||
click_button 'Edit'
|
||||
visit project_board_path(project, board)
|
||||
|
||||
wait_for_requests
|
||||
|
||||
expect(page).to have_content 'Label 1'
|
||||
expect(page).to have_selector('.board', count: 3)
|
||||
end
|
||||
end
|
||||
|
||||
it 'allows creating an issue in newly created list' do
|
||||
click_button 'New list'
|
||||
wait_for_all_requests
|
||||
it 'displays new issue button' do
|
||||
expect(first('.board')).to have_button('Create new issue', count: 1)
|
||||
end
|
||||
|
||||
click_button 'Select a label'
|
||||
find('label', text: label.title).click
|
||||
click_button 'Add to board'
|
||||
it 'does not display new issue button in closed list' do
|
||||
page.within('.board:nth-child(3)') do
|
||||
expect(page).not_to have_button('Create new issue')
|
||||
end
|
||||
end
|
||||
|
||||
wait_for_all_requests
|
||||
it 'shows form when clicking button' do
|
||||
page.within(first('.board')) do
|
||||
click_button 'Create new issue'
|
||||
|
||||
page.within('.board:nth-child(2)') do
|
||||
click_button('Create new issue')
|
||||
expect(page).to have_selector('.board-new-issue-form')
|
||||
end
|
||||
end
|
||||
|
||||
it 'hides form when clicking cancel' do
|
||||
page.within(first('.board')) do
|
||||
click_button 'Create new issue'
|
||||
|
||||
expect(page).to have_selector('.board-new-issue-form')
|
||||
|
||||
click_button 'Cancel'
|
||||
|
||||
expect(page).not_to have_selector('.board-new-issue-form')
|
||||
end
|
||||
end
|
||||
|
||||
it 'creates new issue, places it on top of the list, and opens sidebar' do
|
||||
page.within(first('.board')) do
|
||||
click_button 'Create new issue'
|
||||
end
|
||||
|
||||
page.within(first('.board-new-issue-form')) do
|
||||
find('.form-control').set('bug')
|
||||
click_button 'Create issue'
|
||||
end
|
||||
|
||||
wait_for_requests
|
||||
|
||||
page.within(first('.board [data-testid="issue-count-badge"]')) do
|
||||
expect(page).to have_content('2')
|
||||
end
|
||||
|
||||
page.within(first('.board-card')) do
|
||||
issue = project.issues.find_by_title('bug')
|
||||
|
||||
expect(issue.relative_position).to be < existing_issue.relative_position
|
||||
|
||||
expect(page).to have_content(issue.to_reference)
|
||||
expect(page).to have_link(issue.title, href: /#{issue_path(issue)}/)
|
||||
end
|
||||
|
||||
expect(page).to have_selector('[data-testid="issue-boards-sidebar"]')
|
||||
end
|
||||
|
||||
it 'successfully loads labels to be added to newly created issue' do
|
||||
page.within(first('.board')) do
|
||||
click_button 'Create new issue'
|
||||
end
|
||||
|
||||
page.within(first('.board-new-issue-form')) do
|
||||
find('.form-control').set('new issue')
|
||||
click_button 'Create issue'
|
||||
end
|
||||
|
||||
wait_for_requests
|
||||
|
||||
within_testid('sidebar-labels') do
|
||||
click_button 'Edit'
|
||||
|
||||
wait_for_requests
|
||||
|
||||
expect(page).to have_content 'Label 1'
|
||||
end
|
||||
end
|
||||
|
||||
it 'allows creating an issue in newly created list' do
|
||||
click_button 'New list'
|
||||
wait_for_all_requests
|
||||
|
||||
page.within('.board-card') do
|
||||
expect(page).to have_content 'new issue'
|
||||
click_button 'Select a label'
|
||||
find('label', text: label.title).click
|
||||
click_button 'Add to board'
|
||||
|
||||
wait_for_all_requests
|
||||
|
||||
page.within('.board:nth-child(2)') do
|
||||
click_button('Create new issue')
|
||||
|
||||
page.within(first('.board-new-issue-form')) do
|
||||
find('.form-control').set('new issue')
|
||||
click_button 'Create issue'
|
||||
end
|
||||
|
||||
wait_for_all_requests
|
||||
|
||||
page.within('.board-card') do
|
||||
expect(page).to have_content 'new issue'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'unauthorized user' do
|
||||
before do
|
||||
visit project_board_path(project, board)
|
||||
wait_for_requests
|
||||
end
|
||||
|
||||
it 'does not display new issue button in open list' do
|
||||
expect(first('.board')).not_to have_button('Create new issue')
|
||||
end
|
||||
|
||||
it 'does not display new issue button in label list' do
|
||||
page.within('.board:nth-child(2)') do
|
||||
expect(page).not_to have_button('Create new issue')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'group boards' do
|
||||
let_it_be(:group) { create(:group, :public) }
|
||||
let_it_be(:project) { create(:project, namespace: group, name: "root project") }
|
||||
let_it_be(:subgroup) { create(:group, parent: group) }
|
||||
let_it_be(:subproject1) { create(:project, group: subgroup, name: "sub project1") }
|
||||
let_it_be(:subproject2) { create(:project, group: subgroup, name: "sub project2") }
|
||||
let_it_be(:group_board) { create(:board, group: group) }
|
||||
let_it_be(:project_label) { create(:label, project: project, name: 'label') }
|
||||
let_it_be(:list) { create(:list, board: group_board, label: project_label, position: 0) }
|
||||
|
||||
context 'for unauthorized users' do
|
||||
context 'unauthorized user' do
|
||||
before do
|
||||
visit group_board_path(group, group_board)
|
||||
visit project_board_path(project, board)
|
||||
wait_for_requests
|
||||
end
|
||||
|
||||
context 'when backlog does not exist' do
|
||||
it 'does not display new issue button in label list' do
|
||||
page.within('.board.is-draggable') do
|
||||
expect(page).not_to have_button('Create new issue')
|
||||
end
|
||||
end
|
||||
it 'does not display new issue button in open list' do
|
||||
expect(first('.board')).not_to have_button('Create new issue')
|
||||
end
|
||||
|
||||
context 'when backlog list already exists' do
|
||||
it 'does not display new issue button in open list' do
|
||||
expect(first('.board')).not_to have_button('Create new issue')
|
||||
end
|
||||
|
||||
it 'does not display new issue button in label list' do
|
||||
page.within('.board.is-draggable') do
|
||||
expect(page).not_to have_button('Create new issue')
|
||||
end
|
||||
it 'does not display new issue button in label list' do
|
||||
page.within('.board:nth-child(2)') do
|
||||
expect(page).not_to have_button('Create new issue')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'for authorized users' do
|
||||
before do
|
||||
project.add_reporter(user)
|
||||
subproject1.add_reporter(user)
|
||||
context 'group boards' do
|
||||
let_it_be(:group) { create(:group, :public) }
|
||||
let_it_be(:project) { create(:project, namespace: group, name: "root project") }
|
||||
let_it_be(:subgroup) { create(:group, parent: group) }
|
||||
let_it_be(:subproject1) { create(:project, group: subgroup, name: "sub project1") }
|
||||
let_it_be(:subproject2) { create(:project, group: subgroup, name: "sub project2") }
|
||||
let_it_be(:group_board) { create(:board, group: group) }
|
||||
let_it_be(:project_label) { create(:label, project: project, name: 'label') }
|
||||
let_it_be(:list) { create(:list, board: group_board, label: project_label, position: 0) }
|
||||
|
||||
sign_in(user)
|
||||
visit group_board_path(group, group_board)
|
||||
wait_for_requests
|
||||
end
|
||||
|
||||
context 'when backlog does not exist' do
|
||||
context 'for unauthorized users' do
|
||||
before do
|
||||
group_board.lists.backlog.delete_all
|
||||
end
|
||||
|
||||
it 'display new issue button in label list' do
|
||||
expect(board_list_header).to have_button('Create new issue')
|
||||
end
|
||||
end
|
||||
|
||||
context 'project select dropdown' do
|
||||
before do
|
||||
page.within(board_list_header) do
|
||||
click_button 'Create new issue'
|
||||
end
|
||||
|
||||
project_select_dropdown.click
|
||||
|
||||
visit group_board_path(group, group_board)
|
||||
wait_for_requests
|
||||
end
|
||||
|
||||
it 'lists a project which is a direct descendant of the top-level group' do
|
||||
expect(project_select_dropdown).to have_selector("li", text: "root project")
|
||||
context 'when backlog does not exist' do
|
||||
it 'does not display new issue button in label list' do
|
||||
page.within('.board.is-draggable') do
|
||||
expect(page).not_to have_button('Create new issue')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it 'lists a project that belongs to a subgroup' do
|
||||
expect(project_select_dropdown).to have_selector("li", text: "sub project1")
|
||||
context 'when backlog list already exists' do
|
||||
it 'does not display new issue button in open list' do
|
||||
expect(first('.board')).not_to have_button('Create new issue')
|
||||
end
|
||||
|
||||
it 'does not display new issue button in label list' do
|
||||
page.within('.board.is-draggable') do
|
||||
expect(page).not_to have_button('Create new issue')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'for authorized users' do
|
||||
before do
|
||||
project.add_reporter(user)
|
||||
subproject1.add_reporter(user)
|
||||
|
||||
sign_in(user)
|
||||
visit group_board_path(group, group_board)
|
||||
wait_for_requests
|
||||
end
|
||||
|
||||
it "does not list projects to which user doesn't have access" do
|
||||
expect(project_select_dropdown).not_to have_selector("li", text: "sub project2")
|
||||
context 'when backlog does not exist' do
|
||||
before do
|
||||
group_board.lists.backlog.delete_all
|
||||
end
|
||||
|
||||
it 'display new issue button in label list' do
|
||||
expect(board_list_header).to have_button('Create new issue')
|
||||
end
|
||||
end
|
||||
|
||||
context 'project select dropdown' do
|
||||
before do
|
||||
page.within(board_list_header) do
|
||||
click_button 'Create new issue'
|
||||
end
|
||||
|
||||
project_select_dropdown.click
|
||||
|
||||
wait_for_requests
|
||||
end
|
||||
|
||||
it 'lists a project which is a direct descendant of the top-level group' do
|
||||
expect(project_select_dropdown).to have_selector("li", text: "root project")
|
||||
end
|
||||
|
||||
it 'lists a project that belongs to a subgroup' do
|
||||
expect(project_select_dropdown).to have_selector("li", text: "sub project1")
|
||||
end
|
||||
|
||||
it "does not list projects to which user doesn't have access" do
|
||||
expect(project_select_dropdown).not_to have_selector("li", text: "sub project2")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -27,14 +27,17 @@ RSpec.describe 'Project issue boards sidebar labels', :js, feature_category: :po
|
|||
|
||||
before do
|
||||
project.add_maintainer(user)
|
||||
|
||||
sign_in(user)
|
||||
|
||||
visit project_board_path(project, board)
|
||||
wait_for_requests
|
||||
end
|
||||
|
||||
context 'labels' do
|
||||
context 'when issues drawer is disabled' do
|
||||
before do
|
||||
stub_feature_flags(issues_list_drawer: false)
|
||||
sign_in(user)
|
||||
|
||||
visit project_board_path(project, board)
|
||||
wait_for_requests
|
||||
end
|
||||
|
||||
it 'shows current labels when editing' do
|
||||
click_card(card)
|
||||
|
||||
|
|
@ -222,4 +225,196 @@ RSpec.describe 'Project issue boards sidebar labels', :js, feature_category: :po
|
|||
expect(page).to have_selector('.board', count: 4)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when issues drawer is enabled' do
|
||||
let(:labels_widget) { find_by_testid('work-item-labels') }
|
||||
|
||||
before do
|
||||
sign_in(user)
|
||||
|
||||
visit project_board_path(project, board)
|
||||
wait_for_requests
|
||||
end
|
||||
|
||||
it 'shows current labels when editing' do
|
||||
click_card(card)
|
||||
|
||||
page.within(labels_widget) do
|
||||
click_button 'Edit'
|
||||
|
||||
wait_for_requests
|
||||
|
||||
expect(page).to have_selector('.gl-new-dropdown-item-check-icon', count: 2)
|
||||
expect(page).to have_content(development.title)
|
||||
expect(page).to have_content(stretch.title)
|
||||
end
|
||||
end
|
||||
|
||||
it 'adds a single label' do
|
||||
click_card(card)
|
||||
|
||||
page.within(labels_widget) do
|
||||
click_button 'Edit'
|
||||
|
||||
wait_for_requests
|
||||
|
||||
find_label(bug.title).click
|
||||
click_button 'Apply'
|
||||
|
||||
wait_for_requests
|
||||
|
||||
expect(page).to have_selector('.gl-label-text', count: 3)
|
||||
expect(page).to have_content(bug.title)
|
||||
end
|
||||
|
||||
find_by_testid('close-icon').click
|
||||
|
||||
wait_for_requests
|
||||
|
||||
# 'Development' label does not show since the card is in a 'Development' list label
|
||||
expect(card).to have_selector('.gl-label', count: 2)
|
||||
expect(card).to have_content(bug.title)
|
||||
|
||||
# Card is duplicated in the 'Bug' list
|
||||
page.within(bug_list) do
|
||||
expect(page).to have_selector('.board-card', count: 1)
|
||||
expect(page).to have_content(issue2.title)
|
||||
expect(find('.board-card')).to have_content(development.title)
|
||||
end
|
||||
end
|
||||
|
||||
it 'adds a multiple labels' do
|
||||
click_card(card)
|
||||
|
||||
page.within(labels_widget) do
|
||||
click_button 'Edit'
|
||||
|
||||
wait_for_requests
|
||||
|
||||
find_label(bug.title).click
|
||||
find_label(regression.title).click
|
||||
|
||||
click_button 'Apply'
|
||||
|
||||
wait_for_requests
|
||||
|
||||
expect(page).to have_selector('.gl-label-text', count: 4)
|
||||
expect(page).to have_content(bug.title)
|
||||
expect(page).to have_content(regression.title)
|
||||
end
|
||||
|
||||
# 'Development' label does not show since the card is in a 'Development' list label
|
||||
expect(card).to have_selector('.gl-label', count: 3)
|
||||
expect(card).to have_content(bug.title)
|
||||
expect(card).to have_content(regression.title)
|
||||
end
|
||||
|
||||
it 'removes a label and moves card to backlog' do
|
||||
click_card(card)
|
||||
|
||||
page.within(labels_widget) do
|
||||
click_button 'Edit'
|
||||
|
||||
wait_for_requests
|
||||
|
||||
find_label(development.title).click
|
||||
|
||||
click_button 'Apply'
|
||||
|
||||
wait_for_requests
|
||||
end
|
||||
|
||||
find_by_testid('close-icon').click
|
||||
|
||||
wait_for_requests
|
||||
|
||||
# Card is moved to the 'Backlog' list
|
||||
page.within(backlog_list) do
|
||||
expect(page).to have_selector('.board-card', count: 2)
|
||||
expect(page).to have_content(issue2.title)
|
||||
end
|
||||
|
||||
# Card is moved away from the 'Development' list
|
||||
page.within(development_list) do
|
||||
expect(page).to have_selector('.board-card', count: 1)
|
||||
expect(page).not_to have_content(issue2.title)
|
||||
end
|
||||
end
|
||||
|
||||
it 'adds a label to backlog card and moves the card to the list' do
|
||||
click_card(backlog_card)
|
||||
|
||||
page.within(labels_widget) do
|
||||
click_button 'Edit'
|
||||
|
||||
wait_for_requests
|
||||
|
||||
find_label(development.title).click
|
||||
|
||||
click_button 'Apply'
|
||||
|
||||
wait_for_requests
|
||||
end
|
||||
|
||||
find_by_testid('close-icon').click
|
||||
|
||||
wait_for_requests
|
||||
|
||||
# Card is removed from backlog
|
||||
page.within(backlog_list) do
|
||||
expect(page).to have_selector('.board-card', count: 0)
|
||||
end
|
||||
|
||||
# Card is shown in the 'Development' list
|
||||
page.within(development_list) do
|
||||
expect(page).to have_selector('.board-card', count: 3)
|
||||
expect(page).to have_content(issue3.title)
|
||||
end
|
||||
end
|
||||
|
||||
it 'removes a label' do
|
||||
click_card(card)
|
||||
|
||||
page.within(labels_widget) do
|
||||
click_button 'Edit'
|
||||
|
||||
wait_for_requests
|
||||
|
||||
find_label(stretch.title).click
|
||||
|
||||
click_button 'Apply'
|
||||
|
||||
wait_for_requests
|
||||
|
||||
expect(page).to have_selector('.gl-label-text', count: 1)
|
||||
expect(page).not_to have_content(stretch.title)
|
||||
end
|
||||
|
||||
# 'Development' label does not show since the card is in a 'Development' list label
|
||||
expect(card).to have_selector('.gl-label-text', count: 0)
|
||||
expect(card).not_to have_content(stretch.title)
|
||||
end
|
||||
|
||||
it 'creates project label' do
|
||||
click_card(card)
|
||||
|
||||
page.within(labels_widget) do
|
||||
click_button 'Edit'
|
||||
wait_for_requests
|
||||
|
||||
click_on 'Create project label'
|
||||
fill_in 'Label name', with: 'test label'
|
||||
first('.suggested-colors a').click
|
||||
click_button 'Create'
|
||||
wait_for_requests
|
||||
|
||||
expect(page).to have_content('test label')
|
||||
end
|
||||
expect(page).to have_selector('.board', count: 4)
|
||||
end
|
||||
end
|
||||
|
||||
def find_label(title)
|
||||
find('li', text: title, match: :prefer_exact)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -16,15 +16,32 @@ RSpec.describe 'Project issue boards sidebar', :js, feature_category: :portfolio
|
|||
|
||||
before do
|
||||
project.add_maintainer(user)
|
||||
|
||||
sign_in(user)
|
||||
|
||||
visit project_board_path(project, board)
|
||||
|
||||
wait_for_requests
|
||||
end
|
||||
|
||||
it_behaves_like 'issue boards sidebar'
|
||||
context 'when issues drawer is disabled' do
|
||||
before do
|
||||
stub_feature_flags(issues_list_drawer: false)
|
||||
sign_in(user)
|
||||
|
||||
visit project_board_path(project, board)
|
||||
|
||||
wait_for_requests
|
||||
end
|
||||
|
||||
it_behaves_like 'issue boards sidebar'
|
||||
end
|
||||
|
||||
context 'when issues drawer is enabled' do
|
||||
before do
|
||||
sign_in(user)
|
||||
|
||||
visit project_board_path(project, board)
|
||||
|
||||
wait_for_requests
|
||||
end
|
||||
|
||||
it_behaves_like 'work item drawer'
|
||||
end
|
||||
|
||||
def first_card
|
||||
find('.board:nth-child(1)').first("[data-testid='board-card']")
|
||||
|
|
|
|||
|
|
@ -0,0 +1,208 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe 'Work items list filters', :js, feature_category: :team_planning do
|
||||
include FilteredSearchHelpers
|
||||
|
||||
let_it_be(:user1) { create(:user) }
|
||||
let_it_be(:user2) { create(:user) }
|
||||
|
||||
let_it_be(:group) { create(:group) }
|
||||
let_it_be(:project) { create(:project, :public, group: group, developers: [user1, user2]) }
|
||||
|
||||
let_it_be(:label1) { create(:label, project: project) }
|
||||
let_it_be(:label2) { create(:label, project: project) }
|
||||
|
||||
let_it_be(:milestone1) { create(:milestone, group: group, start_date: 5.days.ago, due_date: 13.days.from_now) }
|
||||
let_it_be(:milestone2) { create(:milestone, group: group, start_date: 2.days.from_now, due_date: 9.days.from_now) }
|
||||
|
||||
let_it_be(:incident) do
|
||||
create(:incident, project: project, assignees: [user1], author: user1, labels: [label1], description: 'aaa')
|
||||
end
|
||||
|
||||
let_it_be(:issue) do
|
||||
create(:issue, project: project, author: user1, labels: [label1, label2], milestone: milestone1, title: 'eee')
|
||||
end
|
||||
|
||||
let_it_be(:task) do
|
||||
create(:work_item, :task, project: project, assignees: [user2], author: user2, milestone: milestone2)
|
||||
end
|
||||
|
||||
context 'for signed in user' do
|
||||
before do
|
||||
sign_in(user1)
|
||||
visit group_work_items_path(group)
|
||||
end
|
||||
|
||||
describe 'assignees' do
|
||||
it 'filters', :aggregate_failures do
|
||||
select_tokens 'Assignee', '=', user1.username, submit: true
|
||||
|
||||
expect(page).to have_css('.issue', count: 1)
|
||||
expect(page).to have_link(incident.title)
|
||||
|
||||
click_button 'Clear'
|
||||
|
||||
select_tokens 'Assignee', '!=', user1.username, submit: true
|
||||
|
||||
expect(page).to have_css('.issue', count: 2)
|
||||
expect(page).to have_link(issue.title)
|
||||
expect(page).to have_link(task.title)
|
||||
|
||||
click_button 'Clear'
|
||||
|
||||
select_tokens 'Assignee', '||', user1.username, 'Assignee', '||', user2.username, submit: true
|
||||
|
||||
expect(page).to have_css('.issue', count: 2)
|
||||
expect(page).to have_link(incident.title)
|
||||
expect(page).to have_link(task.title)
|
||||
|
||||
click_button 'Clear'
|
||||
|
||||
select_tokens 'Assignee', '=', 'None', submit: true
|
||||
|
||||
expect(page).to have_css('.issue', count: 1)
|
||||
expect(page).to have_link(issue.title)
|
||||
|
||||
click_button 'Clear'
|
||||
|
||||
select_tokens 'Assignee', '=', 'Any', submit: true
|
||||
|
||||
expect(page).to have_css('.issue', count: 2)
|
||||
expect(page).to have_link(incident.title)
|
||||
expect(page).to have_link(task.title)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'author' do
|
||||
it 'filters', :aggregate_failures do
|
||||
select_tokens 'Author', '=', user1.username, submit: true
|
||||
|
||||
expect(page).to have_css('.issue', count: 2)
|
||||
expect(page).to have_link(incident.title)
|
||||
expect(page).to have_link(issue.title)
|
||||
|
||||
click_button 'Clear'
|
||||
|
||||
select_tokens 'Author', '!=', user1.username, submit: true
|
||||
|
||||
expect(page).to have_css('.issue', count: 1)
|
||||
expect(page).to have_link(task.title)
|
||||
|
||||
click_button 'Clear'
|
||||
|
||||
select_tokens 'Author', '||', user1.username, 'Author', '||', user2.username, submit: true
|
||||
|
||||
expect(page).to have_css('.issue', count: 3)
|
||||
expect(page).to have_link(incident.title)
|
||||
expect(page).to have_link(issue.title)
|
||||
expect(page).to have_link(task.title)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'labels' do
|
||||
it 'filters', :aggregate_failures do
|
||||
select_tokens 'Label', '=', label1.title, submit: true
|
||||
|
||||
expect(page).to have_css('.issue', count: 2)
|
||||
expect(page).to have_link(incident.title)
|
||||
expect(page).to have_link(issue.title)
|
||||
|
||||
click_button 'Clear'
|
||||
|
||||
select_tokens 'Label', '!=', label1.title, submit: true
|
||||
|
||||
expect(page).to have_css('.issue', count: 1)
|
||||
expect(page).to have_link(task.title)
|
||||
|
||||
click_button 'Clear'
|
||||
|
||||
select_tokens 'Label', '||', label1.title, 'Label', '||', label2.title, submit: true
|
||||
|
||||
expect(page).to have_css('.issue', count: 2)
|
||||
expect(page).to have_link(incident.title)
|
||||
expect(page).to have_link(issue.title)
|
||||
|
||||
click_button 'Clear'
|
||||
|
||||
select_tokens 'Label', '=', 'None', submit: true
|
||||
|
||||
expect(page).to have_css('.issue', count: 1)
|
||||
expect(page).to have_link(task.title)
|
||||
|
||||
click_button 'Clear'
|
||||
|
||||
select_tokens 'Label', '=', 'Any', submit: true
|
||||
|
||||
expect(page).to have_css('.issue', count: 2)
|
||||
expect(page).to have_link(incident.title)
|
||||
expect(page).to have_link(issue.title)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'milestones' do
|
||||
it 'filters', :aggregate_failures do
|
||||
select_tokens 'Milestone', '=', milestone1.title, submit: true
|
||||
|
||||
expect(page).to have_css('.issue', count: 1)
|
||||
expect(page).to have_link(issue.title)
|
||||
|
||||
click_button 'Clear'
|
||||
|
||||
select_tokens 'Milestone', '!=', milestone1.title, submit: true
|
||||
|
||||
expect(page).to have_css('.issue', count: 2)
|
||||
expect(page).to have_link(incident.title)
|
||||
expect(page).to have_link(task.title)
|
||||
|
||||
click_button 'Clear'
|
||||
|
||||
select_tokens 'Milestone', '=', 'None', submit: true
|
||||
|
||||
expect(page).to have_css('.issue', count: 1)
|
||||
expect(page).to have_link(incident.title)
|
||||
|
||||
click_button 'Clear'
|
||||
|
||||
select_tokens 'Milestone', '=', 'Any', submit: true
|
||||
|
||||
expect(page).to have_css('.issue', count: 2)
|
||||
expect(page).to have_link(issue.title)
|
||||
expect(page).to have_link(task.title)
|
||||
|
||||
click_button 'Clear'
|
||||
|
||||
select_tokens 'Milestone', '=', 'Upcoming', submit: true
|
||||
|
||||
expect(page).to have_css('.issue', count: 1)
|
||||
expect(page).to have_link(task.title)
|
||||
|
||||
click_button 'Clear'
|
||||
|
||||
select_tokens 'Milestone', '=', 'Started', submit: true
|
||||
|
||||
expect(page).to have_css('.issue', count: 1)
|
||||
expect(page).to have_link(issue.title)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'search within' do
|
||||
it 'filters', :aggregate_failures do
|
||||
select_tokens 'Search Within', 'Titles'
|
||||
send_keys 'eee', :enter, :enter
|
||||
|
||||
expect(page).to have_css('.issue', count: 1)
|
||||
expect(page).to have_link(issue.title)
|
||||
|
||||
click_button 'Clear'
|
||||
|
||||
select_tokens 'Search Within', 'Descriptions'
|
||||
send_keys 'aaa', :enter, :enter
|
||||
|
||||
expect(page).to have_css('.issue', count: 1)
|
||||
expect(page).to have_link(incident.title)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -15,6 +15,8 @@ import BoardContentSidebar from '~/boards/components/board_content_sidebar.vue';
|
|||
import updateBoardListMutation from '~/boards/graphql/board_list_update.mutation.graphql';
|
||||
import BoardAddNewColumn from 'ee_else_ce/boards/components/board_add_new_column.vue';
|
||||
import BoardAddNewColumnTrigger from '~/boards/components/board_add_new_column_trigger.vue';
|
||||
import BoardDrawerWrapper from '~/boards/components/board_drawer_wrapper.vue';
|
||||
import WorkItemDrawer from '~/work_items/components/work_item_drawer.vue';
|
||||
import { DraggableItemTypes } from 'ee_else_ce/boards/constants';
|
||||
import boardListsQuery from 'ee_else_ce/boards/graphql/board_lists.query.graphql';
|
||||
import {
|
||||
|
|
@ -33,6 +35,7 @@ describe('BoardContent', () => {
|
|||
const updateListHandler = jest.fn().mockResolvedValue(updateBoardListResponse);
|
||||
const errorMessage = 'Failed to update list';
|
||||
const updateListHandlerFailure = jest.fn().mockRejectedValue(new Error(errorMessage));
|
||||
const mockUpdateCache = jest.fn();
|
||||
|
||||
const createComponent = ({
|
||||
props = {},
|
||||
|
|
@ -41,8 +44,10 @@ describe('BoardContent', () => {
|
|||
isIssueBoard = true,
|
||||
isEpicBoard = false,
|
||||
handler = updateListHandler,
|
||||
workItemDrawerEnabled = false,
|
||||
} = {}) => {
|
||||
mockApollo = createMockApollo([[updateBoardListMutation, handler]]);
|
||||
mockApollo.clients.defaultClient.cache.updateQuery = mockUpdateCache;
|
||||
const listQueryVariables = { isProject: true };
|
||||
|
||||
mockApollo.clients.defaultClient.writeQuery({
|
||||
|
|
@ -70,11 +75,26 @@ describe('BoardContent', () => {
|
|||
isEpicBoard,
|
||||
isGroupBoard: true,
|
||||
disabled: false,
|
||||
fullPath: 'project-path',
|
||||
glFeatures: {
|
||||
issuesListDrawer: workItemDrawerEnabled,
|
||||
},
|
||||
},
|
||||
stubs: {
|
||||
BoardContentSidebar: stubComponent(BoardContentSidebar, {
|
||||
template: '<div></div>',
|
||||
}),
|
||||
BoardDrawerWrapper: stubComponent(BoardDrawerWrapper, {
|
||||
template: `
|
||||
<div>
|
||||
<slot
|
||||
:active-issuable="{ listId: 1 }"
|
||||
:onDrawerClosed="() => {}"
|
||||
:onIssuableDeleted="() => {}"
|
||||
:onAttributeUpdated="() => {}"
|
||||
:onStateUpdated="() => {}"/>
|
||||
</div>`,
|
||||
}),
|
||||
},
|
||||
});
|
||||
};
|
||||
|
|
@ -83,6 +103,8 @@ describe('BoardContent', () => {
|
|||
const findBoardAddNewColumn = () => wrapper.findComponent(BoardAddNewColumn);
|
||||
const findDraggable = () => wrapper.findComponent(Draggable);
|
||||
const findError = () => wrapper.findComponent(GlAlert);
|
||||
const findDrawerWrapper = () => wrapper.findComponent(BoardDrawerWrapper);
|
||||
const findWorkItemDrawer = () => wrapper.findComponent(WorkItemDrawer);
|
||||
|
||||
const moveList = () => {
|
||||
const movableListsOrder = [mockLists[0].id, mockLists[1].id];
|
||||
|
|
@ -114,6 +136,10 @@ describe('BoardContent', () => {
|
|||
expect(wrapper.findComponent(BoardContentSidebar).exists()).toBe(true);
|
||||
});
|
||||
|
||||
it('does not render board drawer wrapper', () => {
|
||||
expect(findDrawerWrapper().exists()).toBe(false);
|
||||
});
|
||||
|
||||
it('does not display EpicsSwimlanes component', () => {
|
||||
expect(wrapper.findComponent(EpicsSwimlanes).exists()).toBe(false);
|
||||
expect(findError().exists()).toBe(false);
|
||||
|
|
@ -173,6 +199,10 @@ describe('BoardContent', () => {
|
|||
it('does not render BoardContentSidebar', () => {
|
||||
expect(wrapper.findComponent(BoardContentSidebar).exists()).toBe(false);
|
||||
});
|
||||
|
||||
it('does not render board drawer wrapper', () => {
|
||||
expect(findDrawerWrapper().exists()).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('can admin list', () => {
|
||||
|
|
@ -217,4 +247,20 @@ describe('BoardContent', () => {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when work item drawer is enabled', () => {
|
||||
beforeEach(() => {
|
||||
createComponent({ workItemDrawerEnabled: true });
|
||||
});
|
||||
|
||||
it('renders board drawer wrapper', () => {
|
||||
expect(findDrawerWrapper().exists()).toBe(true);
|
||||
});
|
||||
|
||||
it('updates Apollo cache when work item in the drawer is updated', () => {
|
||||
findWorkItemDrawer().vm.$emit('work-item-updated', { iid: '1' });
|
||||
|
||||
expect(mockUpdateCache).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue