Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
16e3c34cac
commit
4dff02cf71
|
@ -0,0 +1,16 @@
|
|||
#
|
||||
# This list of browsers is a conservative first definition, based on
|
||||
# https://docs.gitlab.com/ee/install/requirements.html#supported-web-browsers
|
||||
# with the following reasoning:
|
||||
#
|
||||
# - Edge: Pick the last two major version before the Chrome switch
|
||||
# - Rest: We should support the latest ESR of Firefox: 68, because it used quite a lot.
|
||||
# For the rest, pick browser versions that have a similar age to Firefox 68.
|
||||
#
|
||||
# See also this follow-up epic:
|
||||
# https://gitlab.com/groups/gitlab-org/-/epics/3957
|
||||
#
|
||||
chrome >= 73
|
||||
edge >= 17
|
||||
firefox >= 68
|
||||
safari >= 12
|
|
@ -31,7 +31,6 @@ If applicable, any groups/projects that are happy to have this feature turned on
|
|||
|
||||
## Roll Out Steps
|
||||
|
||||
- [ ] Confirm that QA tests pass with the feature flag enabled (if you're unsure how, contact the relevant [stable counterpart in the Quality department](https://about.gitlab.com/handbook/engineering/quality/#individual-contributors))
|
||||
- [ ] Enable on staging (`/chatops run feature set feature_name true --staging`)
|
||||
- [ ] Test on staging
|
||||
- [ ] Ensure that documentation has been updated
|
||||
|
|
2
Gemfile
2
Gemfile
|
@ -284,6 +284,7 @@ gem 'gitlab_chronic_duration', '~> 0.10.6.2'
|
|||
gem 'rack-proxy', '~> 0.6.0'
|
||||
|
||||
gem 'sassc-rails', '~> 2.1.0'
|
||||
gem 'autoprefixer-rails', '10.2.0.0'
|
||||
gem 'terser', '1.0.2'
|
||||
|
||||
gem 'addressable', '~> 2.7'
|
||||
|
@ -336,6 +337,7 @@ end
|
|||
group :development do
|
||||
gem 'brakeman', '~> 4.2', require: false
|
||||
gem 'danger', '~> 8.0.6', require: false
|
||||
gem 'lefthook', '~> 0.7', require: false
|
||||
|
||||
gem 'letter_opener_web', '~> 1.3.4'
|
||||
|
||||
|
|
|
@ -94,6 +94,8 @@ GEM
|
|||
attr_encrypted (3.1.0)
|
||||
encryptor (~> 3.0.0)
|
||||
attr_required (1.0.1)
|
||||
autoprefixer-rails (10.2.0.0)
|
||||
execjs
|
||||
awesome_print (1.8.0)
|
||||
awrence (1.1.1)
|
||||
aws-eventstream (1.1.0)
|
||||
|
@ -658,6 +660,7 @@ GEM
|
|||
rest-client (~> 2.0)
|
||||
launchy (2.4.3)
|
||||
addressable (~> 2.3)
|
||||
lefthook (0.7.2)
|
||||
letter_opener (1.7.0)
|
||||
launchy (~> 2.2)
|
||||
letter_opener_web (1.3.4)
|
||||
|
@ -1285,6 +1288,7 @@ DEPENDENCIES
|
|||
asciidoctor-plantuml (~> 0.0.12)
|
||||
atlassian-jwt (~> 0.2.0)
|
||||
attr_encrypted (~> 3.1.0)
|
||||
autoprefixer-rails (= 10.2.0.0)
|
||||
awesome_print
|
||||
aws-sdk-cloudformation (~> 1)
|
||||
aws-sdk-core (~> 3)
|
||||
|
@ -1410,6 +1414,7 @@ DEPENDENCIES
|
|||
knapsack (~> 1.17)
|
||||
kramdown (~> 2.3.0)
|
||||
kubeclient (~> 4.9.1)
|
||||
lefthook (~> 0.7)
|
||||
letter_opener_web (~> 1.3.4)
|
||||
license_finder (~> 6.0)
|
||||
licensee (~> 8.9)
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
<script>
|
||||
import BoardCardLayout from './board_card_layout.vue';
|
||||
import BoardCardLayoutDeprecated from './board_card_layout_deprecated.vue';
|
||||
import eventHub from '../eventhub';
|
||||
import sidebarEventHub from '~/sidebar/event_hub';
|
||||
import boardsStore from '../stores/boards_store';
|
||||
|
@ -7,7 +8,7 @@ import boardsStore from '../stores/boards_store';
|
|||
export default {
|
||||
name: 'BoardsIssueCard',
|
||||
components: {
|
||||
BoardCardLayout,
|
||||
BoardCardLayout: gon.features?.graphqlBoardLists ? BoardCardLayout : BoardCardLayoutDeprecated,
|
||||
},
|
||||
props: {
|
||||
list: {
|
||||
|
|
|
@ -1,17 +1,13 @@
|
|||
<script>
|
||||
import { mapActions, mapGetters } from 'vuex';
|
||||
import { mapActions, mapGetters, mapState } from 'vuex';
|
||||
import IssueCardInner from './issue_card_inner.vue';
|
||||
import IssueCardInnerDeprecated from './issue_card_inner_deprecated.vue';
|
||||
import boardsStore from '../stores/boards_store';
|
||||
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
|
||||
import { ISSUABLE } from '~/boards/constants';
|
||||
|
||||
export default {
|
||||
name: 'BoardCardLayout',
|
||||
components: {
|
||||
IssueCardInner: gon.features?.graphqlBoardLists ? IssueCardInner : IssueCardInnerDeprecated,
|
||||
IssueCardInner,
|
||||
},
|
||||
mixins: [glFeatureFlagMixin()],
|
||||
props: {
|
||||
list: {
|
||||
type: Object,
|
||||
|
@ -42,17 +38,17 @@ export default {
|
|||
data() {
|
||||
return {
|
||||
showDetail: false,
|
||||
multiSelect: boardsStore.multiSelect,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState(['selectedBoardItems']),
|
||||
...mapGetters(['isSwimlanesOn']),
|
||||
multiSelectVisible() {
|
||||
return this.multiSelect.list.findIndex((issue) => issue.id === this.issue.id) > -1;
|
||||
return this.selectedBoardItems.findIndex((boardItem) => boardItem.id === this.issue.id) > -1;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
...mapActions(['setActiveId']),
|
||||
...mapActions(['setActiveId', 'toggleBoardItemMultiSelection']),
|
||||
mouseDown() {
|
||||
this.showDetail = true;
|
||||
},
|
||||
|
@ -63,16 +59,16 @@ export default {
|
|||
// Don't do anything if this happened on a no trigger element
|
||||
if (e.target.classList.contains('js-no-trigger')) return;
|
||||
|
||||
if (this.glFeatures.graphqlBoardLists || this.isSwimlanesOn) {
|
||||
this.setActiveId({ id: this.issue.id, sidebarType: ISSUABLE });
|
||||
return;
|
||||
}
|
||||
|
||||
const isMultiSelect = e.ctrlKey || e.metaKey;
|
||||
|
||||
if (!isMultiSelect) {
|
||||
this.setActiveId({ id: this.issue.id, sidebarType: ISSUABLE });
|
||||
} else {
|
||||
this.toggleBoardItemMultiSelection(this.issue);
|
||||
}
|
||||
|
||||
if (this.showDetail || isMultiSelect) {
|
||||
this.showDetail = false;
|
||||
this.$emit('show', { event: e, isMultiSelect });
|
||||
}
|
||||
},
|
||||
},
|
||||
|
|
|
@ -0,0 +1,102 @@
|
|||
<script>
|
||||
import { mapActions, mapGetters } from 'vuex';
|
||||
import IssueCardInner from './issue_card_inner.vue';
|
||||
import IssueCardInnerDeprecated from './issue_card_inner_deprecated.vue';
|
||||
import boardsStore from '../stores/boards_store';
|
||||
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
|
||||
import { ISSUABLE } from '~/boards/constants';
|
||||
|
||||
export default {
|
||||
name: 'BoardCardLayout',
|
||||
components: {
|
||||
IssueCardInner: gon.features?.graphqlBoardLists ? IssueCardInner : IssueCardInnerDeprecated,
|
||||
},
|
||||
mixins: [glFeatureFlagMixin()],
|
||||
props: {
|
||||
list: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
required: false,
|
||||
},
|
||||
issue: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
required: false,
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
required: false,
|
||||
},
|
||||
index: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
required: false,
|
||||
},
|
||||
isActive: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
showDetail: false,
|
||||
multiSelect: boardsStore.multiSelect,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['isSwimlanesOn']),
|
||||
multiSelectVisible() {
|
||||
return this.multiSelect.list.findIndex((issue) => issue.id === this.issue.id) > -1;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
...mapActions(['setActiveId']),
|
||||
mouseDown() {
|
||||
this.showDetail = true;
|
||||
},
|
||||
mouseMove() {
|
||||
this.showDetail = false;
|
||||
},
|
||||
showIssue(e) {
|
||||
// Don't do anything if this happened on a no trigger element
|
||||
if (e.target.classList.contains('js-no-trigger')) return;
|
||||
|
||||
if (this.glFeatures.graphqlBoardLists || this.isSwimlanesOn) {
|
||||
this.setActiveId({ id: this.issue.id, sidebarType: ISSUABLE });
|
||||
return;
|
||||
}
|
||||
|
||||
const isMultiSelect = e.ctrlKey || e.metaKey;
|
||||
|
||||
if (this.showDetail || isMultiSelect) {
|
||||
this.showDetail = false;
|
||||
this.$emit('show', { event: e, isMultiSelect });
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<li
|
||||
:class="{
|
||||
'multi-select': multiSelectVisible,
|
||||
'user-can-drag': !disabled && issue.id,
|
||||
'is-disabled': disabled || !issue.id,
|
||||
'is-active': isActive,
|
||||
}"
|
||||
:index="index"
|
||||
:data-issue-id="issue.id"
|
||||
:data-issue-iid="issue.iid"
|
||||
:data-issue-path="issue.referencePath"
|
||||
data-testid="board_card"
|
||||
class="board-card gl-p-5 gl-rounded-base"
|
||||
@mousedown="mouseDown"
|
||||
@mousemove="mouseMove"
|
||||
@mouseup="showIssue($event)"
|
||||
>
|
||||
<issue-card-inner :list="list" :issue="issue" :update-filters="true" />
|
||||
</li>
|
||||
</template>
|
|
@ -14,6 +14,10 @@ export default {
|
|||
type: Boolean,
|
||||
required: true,
|
||||
},
|
||||
readonly: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
@ -28,12 +32,14 @@ export default {
|
|||
</p>
|
||||
<gl-form-checkbox
|
||||
:checked="!hideBacklogList"
|
||||
:disabled="readonly"
|
||||
data-testid="backlog-list-checkbox"
|
||||
@change="$emit('update:hideBacklogList', !hideBacklogList)"
|
||||
>{{ __('Show the Open list') }}
|
||||
</gl-form-checkbox>
|
||||
<gl-form-checkbox
|
||||
:checked="!hideClosedList"
|
||||
:disabled="readonly"
|
||||
data-testid="closed-list-checkbox"
|
||||
@change="$emit('update:hideClosedList', !hideClosedList)"
|
||||
>{{ __('Show the Closed list') }}
|
||||
|
|
|
@ -308,6 +308,7 @@ export default {
|
|||
<board-configuration-options
|
||||
:hide-backlog-list.sync="board.hide_backlog_list"
|
||||
:hide-closed-list.sync="board.hide_closed_list"
|
||||
:readonly="readonly"
|
||||
/>
|
||||
|
||||
<board-scope
|
||||
|
|
|
@ -8,9 +8,8 @@ import {
|
|||
GlDropdownDivider,
|
||||
GlLoadingIcon,
|
||||
} from '@gitlab/ui';
|
||||
import { fetchPolicies } from '~/lib/graphql';
|
||||
import BoardEditableItem from '~/boards/components/sidebar/board_editable_item.vue';
|
||||
import groupMilestones from '../../graphql/group_milestones.query.graphql';
|
||||
import projectMilestones from '../../graphql/project_milestones.query.graphql';
|
||||
import createFlash from '~/flash';
|
||||
import { __, s__ } from '~/locale';
|
||||
|
||||
|
@ -34,22 +33,21 @@ export default {
|
|||
},
|
||||
apollo: {
|
||||
milestones: {
|
||||
fetchPolicy: fetchPolicies.CACHE_AND_NETWORK,
|
||||
query: groupMilestones,
|
||||
query: projectMilestones,
|
||||
debounce: 250,
|
||||
skip() {
|
||||
return !this.edit;
|
||||
},
|
||||
variables() {
|
||||
return {
|
||||
fullPath: this.groupFullPath,
|
||||
fullPath: this.projectPath,
|
||||
searchTitle: this.searchTitle,
|
||||
state: 'active',
|
||||
includeDescendants: true,
|
||||
includeAncestors: true,
|
||||
};
|
||||
},
|
||||
update(data) {
|
||||
const edges = data?.group?.milestones?.edges ?? [];
|
||||
const edges = data?.project?.milestones?.edges ?? [];
|
||||
return edges.map((item) => item.node);
|
||||
},
|
||||
error() {
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
query groupMilestones(
|
||||
$fullPath: ID!
|
||||
$state: MilestoneStateEnum
|
||||
$includeDescendants: Boolean
|
||||
$includeAncestors: Boolean
|
||||
$searchTitle: String
|
||||
) {
|
||||
group(fullPath: $fullPath) {
|
||||
milestones(state: $state, includeDescendants: $includeDescendants, searchTitle: $searchTitle) {
|
||||
project(fullPath: $fullPath) {
|
||||
milestones(state: $state, includeAncestors: $includeAncestors, searchTitle: $searchTitle) {
|
||||
edges {
|
||||
node {
|
||||
id
|
|
@ -534,6 +534,17 @@ export default {
|
|||
commit(types.SET_SELECTED_PROJECT, project);
|
||||
},
|
||||
|
||||
toggleBoardItemMultiSelection: ({ commit, state }, boardItem) => {
|
||||
const { selectedBoardItems } = state;
|
||||
const index = selectedBoardItems.indexOf(boardItem);
|
||||
|
||||
if (index === -1) {
|
||||
commit(types.ADD_BOARD_ITEM_TO_SELECTION, boardItem);
|
||||
} else {
|
||||
commit(types.REMOVE_BOARD_ITEM_FROM_SELECTION, boardItem);
|
||||
}
|
||||
},
|
||||
|
||||
fetchBacklog: () => {
|
||||
notImplemented();
|
||||
},
|
||||
|
|
|
@ -40,3 +40,5 @@ export const REQUEST_GROUP_PROJECTS = 'REQUEST_GROUP_PROJECTS';
|
|||
export const RECEIVE_GROUP_PROJECTS_SUCCESS = 'RECEIVE_GROUP_PROJECTS_SUCCESS';
|
||||
export const RECEIVE_GROUP_PROJECTS_FAILURE = 'RECEIVE_GROUP_PROJECTS_FAILURE';
|
||||
export const SET_SELECTED_PROJECT = 'SET_SELECTED_PROJECT';
|
||||
export const ADD_BOARD_ITEM_TO_SELECTION = 'ADD_BOARD_ITEM_TO_SELECTION';
|
||||
export const REMOVE_BOARD_ITEM_FROM_SELECTION = 'REMOVE_BOARD_ITEM_FROM_SELECTION';
|
||||
|
|
|
@ -258,4 +258,16 @@ export default {
|
|||
[mutationTypes.SET_SELECTED_PROJECT]: (state, project) => {
|
||||
state.selectedProject = project;
|
||||
},
|
||||
|
||||
[mutationTypes.ADD_BOARD_ITEM_TO_SELECTION]: (state, boardItem) => {
|
||||
state.selectedBoardItems = [...state.selectedBoardItems, boardItem];
|
||||
},
|
||||
|
||||
[mutationTypes.REMOVE_BOARD_ITEM_FROM_SELECTION]: (state, boardItem) => {
|
||||
Vue.set(
|
||||
state,
|
||||
'selectedBoardItems',
|
||||
state.selectedBoardItems.filter((obj) => obj !== boardItem),
|
||||
);
|
||||
},
|
||||
};
|
||||
|
|
|
@ -15,6 +15,7 @@ export default () => ({
|
|||
filterParams: {},
|
||||
boardConfig: {},
|
||||
labels: [],
|
||||
selectedBoardItems: [],
|
||||
groupProjects: [],
|
||||
groupProjectsFlags: {
|
||||
isLoading: false,
|
||||
|
|
|
@ -39,13 +39,17 @@ export default {
|
|||
this.$emit('toggle');
|
||||
},
|
||||
},
|
||||
ICON_CLASS: 'gl-mr-3 gl-cursor-pointer',
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<li :class="className" class="replies-toggle js-toggle-replies">
|
||||
<li
|
||||
:class="className"
|
||||
class="replies-toggle js-toggle-replies gl-display-flex! gl-align-items-center gl-flex-wrap"
|
||||
>
|
||||
<template v-if="collapsed">
|
||||
<gl-icon name="chevron-right" @click.native="toggle" />
|
||||
<gl-icon :class="$options.ICON_CLASS" name="chevron-right" @click.native="toggle" />
|
||||
<div>
|
||||
<user-avatar-link
|
||||
v-for="author in uniqueAuthors"
|
||||
|
@ -59,7 +63,7 @@ export default {
|
|||
/>
|
||||
</div>
|
||||
<gl-button
|
||||
class="js-replies-text"
|
||||
class="js-replies-text gl-mr-2"
|
||||
category="tertiary"
|
||||
variant="link"
|
||||
data-qa-selector="expand_replies_button"
|
||||
|
@ -68,18 +72,19 @@ export default {
|
|||
{{ replies.length }} {{ n__('reply', 'replies', replies.length) }}
|
||||
</gl-button>
|
||||
{{ __('Last reply by') }}
|
||||
<a :href="lastReply.author.path" class="btn btn-link author-link">
|
||||
<a :href="lastReply.author.path" class="btn btn-link author-link gl-mx-2">
|
||||
{{ lastReply.author.name }}
|
||||
</a>
|
||||
<time-ago-tooltip :time="lastReply.created_at" tooltip-placement="bottom" />
|
||||
</template>
|
||||
<span
|
||||
<div
|
||||
v-else
|
||||
class="collapse-replies-btn js-collapse-replies"
|
||||
class="collapse-replies-btn js-collapse-replies gl-display-flex align-items-center"
|
||||
data-qa-selector="collapse_replies_button"
|
||||
@click="toggle"
|
||||
>
|
||||
<gl-icon name="chevron-down" /> {{ s__('Notes|Collapse replies') }}
|
||||
</span>
|
||||
<gl-icon :class="$options.ICON_CLASS" name="chevron-down" />
|
||||
<span class="gl-cursor-pointer">{{ s__('Notes|Collapse replies') }}</span>
|
||||
</div>
|
||||
</li>
|
||||
</template>
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
import Search from './search';
|
||||
import { initSearchApp } from '~/search';
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
initSearchApp(); // Vue Bootstrap
|
||||
return new Search(); // Legacy Search Methods
|
||||
initSearchApp();
|
||||
});
|
||||
|
|
|
@ -1,54 +0,0 @@
|
|||
import $ from 'jquery';
|
||||
import setHighlightClass from 'ee_else_ce/search/highlight_blob_search_result';
|
||||
import Project from '~/pages/projects/project';
|
||||
import { visitUrl } from '~/lib/utils/url_utility';
|
||||
import refreshCounts from './refresh_counts';
|
||||
|
||||
export default class Search {
|
||||
constructor() {
|
||||
this.searchInput = '.js-search-input';
|
||||
this.searchClear = '.js-search-clear';
|
||||
|
||||
setHighlightClass(); // Code Highlighting
|
||||
this.eventListeners(); // Search Form Actions
|
||||
refreshCounts(); // Other Scope Tab Counts
|
||||
Project.initRefSwitcher(); // Code Search Branch Picker
|
||||
}
|
||||
|
||||
eventListeners() {
|
||||
$(document).off('keyup', this.searchInput).on('keyup', this.searchInput, this.searchKeyUp);
|
||||
$(document)
|
||||
.off('click', this.searchClear)
|
||||
.on('click', this.searchClear, this.clearSearchField.bind(this));
|
||||
|
||||
$('a.js-search-clear').off('click', this.clearSearchFilter).on('click', this.clearSearchFilter);
|
||||
}
|
||||
|
||||
static submitSearch() {
|
||||
return $('.js-search-form').submit();
|
||||
}
|
||||
|
||||
searchKeyUp() {
|
||||
const $input = $(this);
|
||||
if ($input.val() === '') {
|
||||
$('.js-search-clear').addClass('hidden');
|
||||
} else {
|
||||
$('.js-search-clear').removeClass('hidden');
|
||||
}
|
||||
}
|
||||
|
||||
clearSearchField() {
|
||||
return $(this.searchInput).val('').trigger('keyup').focus();
|
||||
}
|
||||
|
||||
// We need to manually follow the link on the anchors
|
||||
// that have this event bound, as their `click` default
|
||||
// behavior is prevented by the toggle logic.
|
||||
/* eslint-disable-next-line class-methods-use-this */
|
||||
clearSearchFilter(ev) {
|
||||
const $target = $(ev.currentTarget);
|
||||
|
||||
visitUrl($target.href);
|
||||
ev.stopPropagation();
|
||||
}
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
export default () => {
|
||||
export default (search = '') => {
|
||||
const highlightLineClass = 'hll';
|
||||
const contentBody = document.getElementById('content-body');
|
||||
const searchTerm = contentBody.querySelector('.js-search-input').value.toLowerCase();
|
||||
const searchTerm = search.toLowerCase();
|
||||
const blobs = contentBody.querySelectorAll('.blob-result');
|
||||
|
||||
blobs.forEach((blob) => {
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
import setHighlightClass from 'ee_else_ce/search/highlight_blob_search_result';
|
||||
import Project from '~/pages/projects/project';
|
||||
import refreshCounts from '~/pages/search/show/refresh_counts';
|
||||
import { queryToObject } from '~/lib/utils/url_utility';
|
||||
import createStore from './store';
|
||||
import { initTopbar } from './topbar';
|
||||
|
@ -7,8 +10,14 @@ export const initSearchApp = () => {
|
|||
// Similar to url_utility.decodeUrlParameter
|
||||
// Our query treats + as %20. This replaces the query + symbols with %20.
|
||||
const sanitizedSearch = window.location.search.replace(/\+/g, '%20');
|
||||
const store = createStore({ query: queryToObject(sanitizedSearch) });
|
||||
const query = queryToObject(sanitizedSearch);
|
||||
|
||||
const store = createStore({ query });
|
||||
|
||||
initTopbar(store);
|
||||
initSidebar(store);
|
||||
|
||||
setHighlightClass(query.search); // Code Highlighting
|
||||
refreshCounts(); // Other Scope Tab Counts
|
||||
Project.initRefSwitcher(); // Code Search Branch Picker
|
||||
};
|
||||
|
|
|
@ -0,0 +1,73 @@
|
|||
<script>
|
||||
import { mapState, mapActions } from 'vuex';
|
||||
import { GlForm, GlSearchBoxByType, GlButton } from '@gitlab/ui';
|
||||
import GroupFilter from './group_filter.vue';
|
||||
import ProjectFilter from './project_filter.vue';
|
||||
|
||||
export default {
|
||||
name: 'GlobalSearchTopbar',
|
||||
components: {
|
||||
GlForm,
|
||||
GlSearchBoxByType,
|
||||
GroupFilter,
|
||||
ProjectFilter,
|
||||
GlButton,
|
||||
},
|
||||
props: {
|
||||
groupInitialData: {
|
||||
type: Object,
|
||||
required: false,
|
||||
default: () => ({}),
|
||||
},
|
||||
projectInitialData: {
|
||||
type: Object,
|
||||
required: false,
|
||||
default: () => ({}),
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
...mapState(['query']),
|
||||
search: {
|
||||
get() {
|
||||
return this.query ? this.query.search : '';
|
||||
},
|
||||
set(value) {
|
||||
this.setQuery({ key: 'search', value });
|
||||
},
|
||||
},
|
||||
showFilters() {
|
||||
return !this.query.snippets || this.query.snippets === 'false';
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
...mapActions(['applyQuery', 'setQuery']),
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<gl-form class="search-page-form" @submit.prevent="applyQuery">
|
||||
<section class="gl-display-lg-flex gl-align-items-flex-end">
|
||||
<div class="gl-flex-fill-1 gl-mb-4 gl-lg-mb-0 gl-lg-mr-2">
|
||||
<label>{{ __('What are you searching for?') }}</label>
|
||||
<gl-search-box-by-type
|
||||
id="dashboard_search"
|
||||
v-model="search"
|
||||
name="search"
|
||||
:placeholder="__(`Search for projects, issues, etc.`)"
|
||||
/>
|
||||
</div>
|
||||
<div v-if="showFilters" class="gl-mb-4 gl-lg-mb-0 gl-lg-mx-2">
|
||||
<label class="gl-display-block">{{ __('Group') }}</label>
|
||||
<group-filter :initial-data="groupInitialData" />
|
||||
</div>
|
||||
<div v-if="showFilters" class="gl-mb-4 gl-lg-mb-0 gl-lg-mx-2">
|
||||
<label class="gl-display-block">{{ __('Project') }}</label>
|
||||
<project-filter :initial-data="projectInitialData" />
|
||||
</div>
|
||||
<gl-button class="btn-search gl-lg-ml-2" variant="success" type="submit">{{
|
||||
__('Search')
|
||||
}}</gl-button>
|
||||
</section>
|
||||
</gl-form>
|
||||
</template>
|
|
@ -37,6 +37,7 @@ export default {
|
|||
|
||||
<template>
|
||||
<searchable-dropdown
|
||||
data-testid="group-filter"
|
||||
:header-text="$options.GROUP_DATA.headerText"
|
||||
:selected-display-value="$options.GROUP_DATA.selectedDisplayValue"
|
||||
:items-display-value="$options.GROUP_DATA.itemsDisplayValue"
|
||||
|
|
|
@ -40,6 +40,7 @@ export default {
|
|||
|
||||
<template>
|
||||
<searchable-dropdown
|
||||
data-testid="project-filter"
|
||||
:header-text="$options.PROJECT_DATA.headerText"
|
||||
:selected-display-value="$options.PROJECT_DATA.selectedDisplayValue"
|
||||
:items-display-value="$options.PROJECT_DATA.itemsDisplayValue"
|
||||
|
|
|
@ -101,7 +101,7 @@ export default {
|
|||
@keydown.enter.stop="resetDropdown"
|
||||
@click.stop="resetDropdown"
|
||||
>
|
||||
<gl-icon name="clear" class="gl-text-gray-200! gl-hover-text-blue-800!" />
|
||||
<gl-icon name="clear" />
|
||||
</gl-button>
|
||||
<gl-icon name="chevron-down" />
|
||||
</template>
|
||||
|
|
|
@ -1,44 +1,31 @@
|
|||
import Vue from 'vue';
|
||||
import Translate from '~/vue_shared/translate';
|
||||
import GroupFilter from './components/group_filter.vue';
|
||||
import ProjectFilter from './components/project_filter.vue';
|
||||
import GlobalSearchTopbar from './components/app.vue';
|
||||
|
||||
Vue.use(Translate);
|
||||
|
||||
const mountSearchableDropdown = (store, { id, component }) => {
|
||||
const el = document.getElementById(id);
|
||||
export const initTopbar = (store) => {
|
||||
const el = document.getElementById('js-search-topbar');
|
||||
|
||||
if (!el) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let { initialData } = el.dataset;
|
||||
let { groupInitialData, projectInitialData } = el.dataset;
|
||||
|
||||
initialData = JSON.parse(initialData);
|
||||
groupInitialData = JSON.parse(groupInitialData);
|
||||
projectInitialData = JSON.parse(projectInitialData);
|
||||
|
||||
return new Vue({
|
||||
el,
|
||||
store,
|
||||
render(createElement) {
|
||||
return createElement(component, {
|
||||
return createElement(GlobalSearchTopbar, {
|
||||
props: {
|
||||
initialData,
|
||||
groupInitialData,
|
||||
projectInitialData,
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const searchableDropdowns = [
|
||||
{
|
||||
id: 'js-search-group-dropdown',
|
||||
component: GroupFilter,
|
||||
},
|
||||
{
|
||||
id: 'js-search-project-dropdown',
|
||||
component: ProjectFilter,
|
||||
},
|
||||
];
|
||||
|
||||
export const initTopbar = (store) =>
|
||||
searchableDropdowns.map((dropdown) => mountSearchableDropdown(store, dropdown));
|
||||
|
|
|
@ -110,11 +110,6 @@ export default {
|
|||
return this.referencedUsers.length >= referencedUsersThreshold;
|
||||
},
|
||||
lineContent() {
|
||||
const [firstSuggestion] = this.suggestions;
|
||||
if (firstSuggestion) {
|
||||
return firstSuggestion.from_content;
|
||||
}
|
||||
|
||||
if (this.line) {
|
||||
const { rich_text: richText, text } = this.line;
|
||||
|
||||
|
|
|
@ -91,29 +91,10 @@ $note-form-margin-left: 72px;
|
|||
color: $blue-600;
|
||||
}
|
||||
|
||||
&.expanded {
|
||||
span {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
svg {
|
||||
position: relative;
|
||||
top: 3px;
|
||||
}
|
||||
}
|
||||
|
||||
&.collapsed {
|
||||
color: $gl-text-color-secondary;
|
||||
border-radius: 0 0 $border-radius-default $border-radius-default;
|
||||
|
||||
svg {
|
||||
float: left;
|
||||
position: relative;
|
||||
top: $gl-padding-4;
|
||||
margin-right: $gl-padding-8;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
img {
|
||||
margin: -2px 4px 0 0;
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
.col-sm-10
|
||||
= f.check_box :trusted
|
||||
%span.form-text.text-muted
|
||||
Trusted applications are automatically authorized on GitLab OAuth flow.
|
||||
Trusted applications are automatically authorized on GitLab OAuth flow. It's highly recommended for the security of users that trusted applications have the confidential setting set to true.
|
||||
|
||||
= content_tag :div, class: 'form-group row' do
|
||||
.col-sm-2.col-form-label.pt-0
|
||||
|
|
|
@ -1,14 +0,0 @@
|
|||
- if params[:group_id].present?
|
||||
= hidden_field_tag :group_id, params[:group_id]
|
||||
- if params[:project_id].present?
|
||||
= hidden_field_tag :project_id, params[:project_id]
|
||||
- project_attributes = @project&.attributes&.slice('id', 'namespace_id', 'name')&.merge(name_with_namespace: @project&.name_with_namespace)
|
||||
|
||||
.dropdown.form-group.mb-lg-0.mx-lg-1.gl-p-0{ data: { testid: "group-filter" } }
|
||||
%label.d-block{ for: "dashboard_search_group" }
|
||||
= _("Group")
|
||||
%input#js-search-group-dropdown.dropdown-menu-toggle{ value: "Loading...", data: { "initial-data": @group.to_json } }
|
||||
.dropdown.form-group.mb-lg-0.mx-lg-1.gl-p-0{ data: { testid: "project-filter" } }
|
||||
%label.d-block{ for: "dashboard_search_project" }
|
||||
= _("Project")
|
||||
%input#js-search-project-dropdown.dropdown-menu-toggle{ value: "Loading...", data: { "initial-data": project_attributes.to_json } }
|
|
@ -1,20 +0,0 @@
|
|||
= form_tag search_path, method: :get, class: 'search-page-form js-search-form' do |f|
|
||||
= hidden_field_tag :snippets, params[:snippets]
|
||||
= hidden_field_tag :scope, params[:scope]
|
||||
= hidden_field_tag :repository_ref, params[:repository_ref]
|
||||
|
||||
.d-lg-flex.align-items-end
|
||||
.search-field-holder.form-group.mr-lg-1.mb-lg-0
|
||||
%label{ for: "dashboard_search" }
|
||||
= _("What are you searching for?")
|
||||
.gl-search-box-by-type
|
||||
= search_field_tag :search, params[:search], placeholder: _("Search for projects, issues, etc."), class: "gl-form-input form-control search-text-input js-search-input", id: "dashboard_search", autofocus: true, spellcheck: false
|
||||
= sprite_icon('search', css_class: 'gl-search-box-by-type-search-icon gl-icon')
|
||||
%button.search-clear.js-search-clear{ class: [("hidden" if params[:search].blank?), "has-tooltip"], type: "button", tabindex: "-1", title: _('Clear') }
|
||||
= sprite_icon('clear')
|
||||
%span.sr-only
|
||||
= _("Clear search")
|
||||
- unless params[:snippets].eql? 'true'
|
||||
= render 'filter'
|
||||
.d-flex-center.flex-column.flex-lg-row
|
||||
= button_tag _("Search"), class: "gl-button btn btn-success btn-search mt-lg-0 ml-lg-1 align-self-end"
|
|
@ -1,6 +1,11 @@
|
|||
- @hide_top_links = true
|
||||
- page_title @search_term
|
||||
- @hide_breadcrumbs = true
|
||||
- if params[:group_id].present?
|
||||
= hidden_field_tag :group_id, params[:group_id]
|
||||
- if params[:project_id].present?
|
||||
= hidden_field_tag :project_id, params[:project_id]
|
||||
- project_attributes = @project&.attributes&.slice('id', 'namespace_id', 'name')&.merge(name_with_namespace: @project&.name_with_namespace)
|
||||
|
||||
- if @search_results
|
||||
- page_description(_("%{count} %{scope} for term '%{term}'") % { count: @search_results.formatted_count(@scope), scope: @scope, term: @search_term })
|
||||
|
@ -11,7 +16,7 @@
|
|||
= render_if_exists 'search/form_elasticsearch', attrs: { class: 'mb-2 mb-sm-0 align-self-center' }
|
||||
|
||||
.gl-mt-3
|
||||
= render 'search/form'
|
||||
#js-search-topbar{ data: { "group-initial-data": @group.to_json, "project-initial-data": project_attributes.to_json } }
|
||||
- if @search_term
|
||||
= render 'search/category'
|
||||
= render 'search/results'
|
||||
|
|
|
@ -12,5 +12,5 @@
|
|||
|
||||
.col-md-4.col-lg-5.text-right-md.gl-mt-2
|
||||
%span>= render 'shared/web_hooks/test_button', hook: hook, button_class: 'btn-sm gl-mr-3'
|
||||
%span>= link_to _('Edit'), edit_hook_path(hook), class: 'btn btn-sm gl-mr-3'
|
||||
= link_to _('Delete'), destroy_hook_path(hook), data: { confirm: _('Are you sure?') }, method: :delete, class: 'btn btn-sm'
|
||||
%span>= link_to _('Edit'), edit_hook_path(hook), class: 'gl-button btn btn-sm gl-mr-3'
|
||||
= link_to _('Delete'), destroy_hook_path(hook), data: { confirm: _('Are you sure?') }, method: :delete, class: 'gl-button btn btn-sm'
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
- triggers = hook.class.triggers
|
||||
|
||||
.hook-test-button.dropdown.inline>
|
||||
%button.btn{ 'data-toggle' => 'dropdown', class: button_class }
|
||||
%button.btn.gl-button{ 'data-toggle' => 'dropdown', class: button_class }
|
||||
= _('Test')
|
||||
= sprite_icon('chevron-down')
|
||||
%ul.dropdown-menu.dropdown-menu-right{ role: 'menu' }
|
||||
|
|
|
@ -9,24 +9,6 @@ let presets = [
|
|||
useBuiltIns: 'usage',
|
||||
corejs: { version: 3, proposals: true },
|
||||
modules: false,
|
||||
/**
|
||||
* This list of browsers is a conservative first definition, based on
|
||||
* https://docs.gitlab.com/ee/install/requirements.html#supported-web-browsers
|
||||
* with the following reasoning:
|
||||
*
|
||||
* - Edge: Pick the last two major version before the Chrome switch
|
||||
* - Rest: We should support the latest ESR of Firefox: 68, because it used quite a lot.
|
||||
* For the rest, pick browser versions that have a similar age to Firefox 68.
|
||||
*
|
||||
* See also this follow-up epic:
|
||||
* https://gitlab.com/groups/gitlab-org/-/epics/3957
|
||||
*/
|
||||
targets: {
|
||||
chrome: '73',
|
||||
edge: '17',
|
||||
firefox: '68',
|
||||
safari: '12',
|
||||
},
|
||||
},
|
||||
],
|
||||
];
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Global Search - UX Cleanup of Search Bar
|
||||
merge_request: 51409
|
||||
author:
|
||||
type: changed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Scope milestones on swimlane boards to project and its ancestors
|
||||
merge_request: 52199
|
||||
author:
|
||||
type: fixed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Disable board configuration options for users without edit permission
|
||||
merge_request: 52077
|
||||
author:
|
||||
type: fixed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Update buttons in _hook.html.haml to use GitLab UI
|
||||
merge_request: 51065
|
||||
author: nuwe1
|
||||
type: other
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Adds GitLabUI button styles in _test_button.html.haml
|
||||
merge_request: 51070
|
||||
author: nuwe1
|
||||
type: other
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Fixed sdiff suggestions not working when replying to comments
|
||||
merge_request: 52100
|
||||
author:
|
||||
type: fixed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Fix alignment of chevron-down icon in toggle replies
|
||||
merge_request: 51872
|
||||
author: Yogi (@yo)
|
||||
type: fixed
|
|
@ -0,0 +1,32 @@
|
|||
# This is a template for a "Whats New" release.
|
||||
# A release typically contains multiple entries of features that we'd like to highlight.
|
||||
#
|
||||
# Below is an example of what a single entry should look like, it's required attributes,
|
||||
# and what types we expect those attribute values to be. All attributes are required.
|
||||
#
|
||||
# For more information please refer to the handbook documentation here:
|
||||
# https://about.gitlab.com/handbook/marketing/blog/release-posts/index.html#create-mr-for-whats-new-entries
|
||||
#
|
||||
# Please delete this line and above before submitting your Merge Request.
|
||||
|
||||
- title: # Match the release post entry
|
||||
body: | # Do not modify this line, instead modify the lines below.
|
||||
<!-- START OF BODY COMMENT
|
||||
|
||||
This area supports markdown.
|
||||
|
||||
It is recommended to copy and paste the release post entry content here.
|
||||
|
||||
You can shorten it if needed, but remember that the entry itself has already been reviewed by Tech Writing so don't feel like you need to reword anything.
|
||||
|
||||
Delete this entire comment and replace it with your markdown content.
|
||||
|
||||
END OF BODY COMMENT -->
|
||||
stage: # String value of the stage that the feature was created in. e.g., Growth
|
||||
self-managed: # Boolean value (true or false)
|
||||
gitlab-com: # Boolean value (true or false)
|
||||
packages: # Array of strings. The Array brackets are required here. e.g., [Core, Starter, Premium, Ultimate]
|
||||
url: # This is the documentation URL, but can be a URL to a video if there is one
|
||||
image_url: # This should be a full URL, generally taken from the release post content. If a video, use the youtube thumbnail URL with the structure of https://img.youtube.com/vi/UNIQUEID/hqdefault.jpg
|
||||
published_at: # YYYY-MM-DD
|
||||
release: # XX.Y
|
|
@ -189,6 +189,7 @@ heatmaps
|
|||
Helm
|
||||
Heroku
|
||||
Herokuish
|
||||
Hexo
|
||||
HipChat
|
||||
hostname
|
||||
hostnames
|
||||
|
@ -204,6 +205,8 @@ inclusivity
|
|||
Ingress
|
||||
initializer
|
||||
initializers
|
||||
innersource
|
||||
innersourcing
|
||||
interdependencies
|
||||
interdependency
|
||||
interruptible
|
||||
|
@ -432,6 +435,7 @@ sbt
|
|||
scatterplot
|
||||
scatterplots
|
||||
Schemastore
|
||||
scrollable
|
||||
Sendmail
|
||||
Sentry
|
||||
serializer
|
||||
|
@ -556,6 +560,7 @@ unpublish
|
|||
unpublished
|
||||
unpublishes
|
||||
unpublishing
|
||||
unpushed
|
||||
unreferenced
|
||||
unregister
|
||||
unregistered
|
||||
|
|
|
@ -84,9 +84,10 @@ To enable merge trains for your project:
|
|||
1. If you are on a self-managed GitLab instance, ensure the [feature flag](#merge-trains-feature-flag) is set correctly.
|
||||
1. [Configure your CI/CD configuration file](../../index.md#configuring-pipelines-for-merge-requests)
|
||||
so that the pipeline or individual jobs run for merge requests.
|
||||
1. Visit your project's **Settings > General** and expand **Merge requests**
|
||||
1. Check **Enable merged results pipelines.** (if not enabled)
|
||||
1. Check **Enable merge trains.**
|
||||
1. Visit your project's **Settings > General** and expand **Merge requests**.
|
||||
1. In the **Merge method** section, verify that **Merge commit** is selected.
|
||||
You cannot use **Merge commit with semi-linear history** or **Fast-forward merge** with merge trains.
|
||||
1. In the **Merge options** section, select **Enable merged results pipelines.** (if not already selected) and **Enable merge trains.**
|
||||
1. Click **Save changes**
|
||||
|
||||
In GitLab 13.5 and earlier, there is only one checkbox, named
|
||||
|
|
|
@ -15,52 +15,83 @@ settings automatically by default. If your editor/IDE does not automatically sup
|
|||
we suggest investigating to see if a plugin exists. For instance here is the
|
||||
[plugin for vim](https://github.com/editorconfig/editorconfig-vim).
|
||||
|
||||
## Pre-push static analysis
|
||||
## Pre-push static analysis with Lefthook
|
||||
|
||||
We strongly recommend installing [Lefthook](https://github.com/Arkweid/lefthook) to automatically check
|
||||
for static analysis offenses before pushing your changes.
|
||||
[Lefthook](https://github.com/Arkweid/lefthook) is a Git hooks manager that allows
|
||||
custom logic to be executed prior to Git committing or pushing. GitLab comes with
|
||||
Lefthook configuration (`lefthook.yml`), but it must be installed.
|
||||
|
||||
To install `lefthook`, run the following in your GitLab source directory:
|
||||
We have a `lefthook.yml` checked in but it is ignored until Lefthook is installed.
|
||||
|
||||
### Uninstall Overcommit
|
||||
|
||||
We were using Overcommit prior to Lefthook, so you may want to uninstall it first with `overcommit --uninstall`.
|
||||
|
||||
### Install Lefthook
|
||||
|
||||
1. Install the `lefthook` Ruby gem:
|
||||
|
||||
```shell
|
||||
bundle install
|
||||
```
|
||||
|
||||
1. Install Lefthook managed Git hooks:
|
||||
|
||||
```shell
|
||||
bundle exec lefthook install
|
||||
```
|
||||
|
||||
1. Test Lefthook is working by running the Lefthook `prepare-commit-msg` Git hook:
|
||||
|
||||
```shell
|
||||
bundle exec lefthook run prepare-commit-msg
|
||||
```
|
||||
|
||||
This should return a fully qualified path command with no other output.
|
||||
|
||||
### Lefthook configuration
|
||||
|
||||
The current Lefthook configuration can be found in [`lefthook.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lefthook.yml).
|
||||
|
||||
Before you push your changes, Lefthook automatically runs the following checks:
|
||||
|
||||
- Danger: Runs a subset of checks that `danger-review` runs on your merge requests.
|
||||
- ES lint: Run `yarn eslint` checks (with the [`.eslintrc.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/.eslintrc.yml) config) on the modified `*.{js,vue}` files. Tags: `frontend`, `style`.
|
||||
- HAML lint: Run `bundle exec haml-lint` checks (with the [`.haml-lint.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/.haml-lint.yml) config) on the modified `*.html.haml` files. Tags: `view`, `haml`, `style`.
|
||||
- Markdown lint: Run `yarn markdownlint` checks on the modified `*.md` files. Tags: `documentation`, `style`.
|
||||
- SCSS lint: Run `bundle exec scss-lint` checks (with the [`.scss-lint.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/.scss-lint.yml) config) on the modified `*.scss{,.css}` files. Tags: `stylesheet`, `css`, `style`.
|
||||
- RuboCop: Run `bundle exec rubocop` checks (with the [`.rubocop.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/.rubocop.yml) config) on the modified `*.rb` files. Tags: `backend`, `style`.
|
||||
- Vale: Run `vale` checks (with the [`.vale.ini`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/.vale.ini) config) on the modified `*.md` files. Tags: `documentation`, `style`.
|
||||
|
||||
In addition to the default configuration, you can define a [local configuration](https://github.com/Arkweid/lefthook/blob/master/docs/full_guide.md#local-config).
|
||||
|
||||
### Disable Lefthook temporarily
|
||||
|
||||
To disable Lefthook temporarily, you can set the `LEFTHOOK` environment variable to `0`. For instance:
|
||||
|
||||
```shell
|
||||
# 1. Make sure to uninstall Overcommit first
|
||||
overcommit --uninstall
|
||||
|
||||
# If using rbenv, at this point you may need to do: rbenv rehash
|
||||
|
||||
# 2. Install lefthook...
|
||||
|
||||
## With Homebrew (macOS)
|
||||
brew install Arkweid/lefthook/lefthook
|
||||
|
||||
## Or with Go
|
||||
go get github.com/Arkweid/lefthook
|
||||
|
||||
## Or with Rubygems
|
||||
gem install lefthook
|
||||
|
||||
### You may need to run the following if you're using rbenv
|
||||
rbenv rehash
|
||||
|
||||
# 3. Install the Git hooks
|
||||
lefthook install -f
|
||||
LEFTHOOK=0 git push ...
|
||||
```
|
||||
|
||||
Before you push your changes, Lefthook then automatically run Danger checks, and other checks
|
||||
for changed files. This saves you time as you don't have to wait for the same errors to be detected
|
||||
by CI/CD.
|
||||
### Run Lefthook hooks manually
|
||||
|
||||
Lefthook relies on a pre-push hook to prevent commits that violate its ruleset.
|
||||
To override this behavior, pass the environment variable `LEFTHOOK=0`. That is,
|
||||
`LEFTHOOK=0 git push`.
|
||||
To run the `pre-push` Git hook, run:
|
||||
|
||||
You can also:
|
||||
```shell
|
||||
bundle exec lefthook run pre-push
|
||||
```
|
||||
|
||||
- Define [local configuration](https://github.com/Arkweid/lefthook/blob/master/docs/full_guide.md#local-config).
|
||||
- Skip [checks per tag on the fly](https://github.com/Arkweid/lefthook/blob/master/docs/full_guide.md#skip-some-tags-on-the-fly).
|
||||
For example, `LEFTHOOK_EXCLUDE=frontend git push origin`.
|
||||
- Run [hooks manually](https://github.com/Arkweid/lefthook/blob/master/docs/full_guide.md#run-githook-group-directly).
|
||||
For example, `lefthook run pre-push`.
|
||||
For more information, check out [Lefthook documentation](https://github.com/Arkweid/lefthook/blob/master/docs/full_guide.md#run-githook-group-directly).
|
||||
|
||||
### Skip Lefthook checks per tag
|
||||
|
||||
To skip some checks based on tags when pushing, you can set the `LEFTHOOK_EXCLUDE` environment variable. For instance:
|
||||
|
||||
```shell
|
||||
LEFTHOOK_EXCLUDE=frontend,documentation git push ...
|
||||
```
|
||||
|
||||
For more information, check out [Lefthook documentation](https://github.com/Arkweid/lefthook/blob/master/docs/full_guide.md#skip-some-tags-on-the-fly).
|
||||
|
||||
## Ruby, Rails, RSpec
|
||||
|
||||
|
|
|
@ -286,7 +286,7 @@ Configuration for `lefthook` is available in the [`lefthook.yml`](https://gitlab
|
|||
file for the [`gitlab`](https://gitlab.com/gitlab-org/gitlab) project.
|
||||
|
||||
To set up `lefthook` for documentation linting, see
|
||||
[Pre-push static analysis](../contributing/style_guides.md#pre-push-static-analysis).
|
||||
[Pre-push static analysis](../contributing/style_guides.md#pre-push-static-analysis-with-lefthook).
|
||||
|
||||
### Show subset of Vale alerts
|
||||
|
||||
|
|
|
@ -324,7 +324,7 @@ To delete a custom value stream:
|
|||
This chart visually depicts the total number of days it takes for cycles to be completed. (Totals are being replaced with averages in [this issue](https://gitlab.com/gitlab-org/gitlab/-/issues/262070).)
|
||||
|
||||
This chart uses the global page filters for displaying data based on the selected
|
||||
group, projects, and timeframe. In addition, specific stages can be selected
|
||||
group, projects, and time frame. In addition, specific stages can be selected
|
||||
from within the chart itself.
|
||||
|
||||
The chart data is limited to the last 500 items.
|
||||
|
@ -345,7 +345,7 @@ Feature.disable(:cycle_analytics_scatterplot_enabled)
|
|||
This chart shows a cumulative count of issues and merge requests per day.
|
||||
|
||||
This chart uses the global page filters for displaying data based on the selected
|
||||
group, projects, and timeframe. The chart defaults to showing counts for issues but can be
|
||||
group, projects, and time frame. The chart defaults to showing counts for issues but can be
|
||||
toggled to show data for merge requests and further refined for specific group-level labels.
|
||||
|
||||
By default the top group-level labels (max. 10) are pre-selected, with the ability to
|
||||
|
|
|
@ -29,7 +29,7 @@ You can use [GitLab CI/CD](../../../ci/README.md) to build packages.
|
|||
For Maven, NuGet, NPM, Conan, and PyPI packages, and Composer dependencies, you can
|
||||
authenticate with GitLab by using the `CI_JOB_TOKEN`.
|
||||
|
||||
CI/CD templates, which you can use to get started, are in [this repo](https://gitlab.com/gitlab-org/gitlab/-/tree/master/lib/gitlab/ci/templates).
|
||||
CI/CD templates, which you can use to get started, are in [this repository](https://gitlab.com/gitlab-org/gitlab/-/tree/master/lib/gitlab/ci/templates).
|
||||
|
||||
Learn more about using CI/CD to build:
|
||||
|
||||
|
|
|
@ -240,7 +240,7 @@ following desktop browsers:
|
|||
|
||||
NOTE:
|
||||
For Firefox 47-66, you can enable the FIDO U2F API in
|
||||
[about:config](https://support.mozilla.org/en-US/kb/about-config-editor-firefox).
|
||||
[`about:config`](https://support.mozilla.org/en-US/kb/about-config-editor-firefox).
|
||||
Search for `security.webauth.u2f` and double click on it to toggle to `true`.
|
||||
|
||||
To set up 2FA with a U2F device:
|
||||
|
|
|
@ -43,7 +43,7 @@ Google Kubernetes Engine integration. All you have to do is [follow this link](h
|
|||
## Creating a new project from a template
|
||||
|
||||
We use a GitLab project templates to get started. As the name suggests,
|
||||
those projects provide a barebones application built on some well-known frameworks.
|
||||
those projects provide a bare-bones application built on some well-known frameworks.
|
||||
|
||||
1. In GitLab, click the plus icon (**+**) at the top of the navigation bar and select
|
||||
**New project**.
|
||||
|
|
|
@ -563,7 +563,7 @@ another list. This makes it faster to reorder many issues at once.
|
|||
|
||||
To select and move multiple cards:
|
||||
|
||||
1. Select each card with <kbd>Ctrl</kbd>+`Click` on Windows or Linux, or <kbd>Cmd</kbd>+`Click` on MacOS.
|
||||
1. Select each card with <kbd>Control</kbd>+`Click` on Windows or Linux, or <kbd>Command</kbd>+`Click` on MacOS.
|
||||
1. Drag one of the selected cards to another position or list and all selected cards are moved.
|
||||
|
||||
![Multi-select Issue Cards](img/issue_boards_multi_select_v12_4.png)
|
||||
|
|
|
@ -23,7 +23,7 @@ of the merge request.
|
|||
|
||||
## Enabling commit edits from upstream members
|
||||
|
||||
From [GitLab 13.7 onwards](https://gitlab.com/gitlab-org/gitlab/-/issues/23308),
|
||||
In [GitLab 13.7 and later](https://gitlab.com/gitlab-org/gitlab/-/issues/23308),
|
||||
this setting is enabled by default. It can be changed by users with Developer
|
||||
permissions to the source project. Once enabled, upstream members will also be
|
||||
able to retry the pipelines and jobs of the merge request:
|
||||
|
|
|
@ -75,7 +75,7 @@ GitLab 11.4 or earlier, you can view the deprecated job definitions in the
|
|||
- Using shared runners, the job should be configured For the [Docker-in-Docker workflow](../../../ci/docker/using_docker_build.md#use-the-docker-executor-with-the-docker-image-docker-in-docker).
|
||||
- Using private runners, there is an [alternative configuration](#set-up-a-private-runner-for-code-quality-without-docker-in-docker) recommended for running CodeQuality analysis more efficiently.
|
||||
|
||||
In either configuration, the runner mmust have enough disk space to handle generated Code Quality files. For example on the [GitLab project](https://gitlab.com/gitlab-org/gitlab) the files are approximately 7 GB.
|
||||
In either configuration, the runner must have enough disk space to handle generated Code Quality files. For example on the [GitLab project](https://gitlab.com/gitlab-org/gitlab) the files are approximately 7 GB.
|
||||
|
||||
Once you set up GitLab Runner, include the Code Quality template in your CI configuration:
|
||||
|
||||
|
|
|
@ -89,7 +89,7 @@ From there, when reviewing merge requests' **Changes** tab, you will see only on
|
|||
|
||||
![File-by-file diff navigation](img/file_by_file_v13_2.png)
|
||||
|
||||
From [GitLab 13.7](https://gitlab.com/gitlab-org/gitlab/-/issues/233898) onwards, if you want to change
|
||||
In [GitLab 13.7](https://gitlab.com/gitlab-org/gitlab/-/issues/233898) and later, if you want to change
|
||||
this behavior, you can do so from your **User preferences** (as explained above) or directly in a
|
||||
merge request:
|
||||
|
||||
|
|
|
@ -52,5 +52,5 @@ You can take some **optional** further steps:
|
|||
|
||||
![Change repository's path](../img/change_path_v12_10.png)
|
||||
|
||||
- Now go to your SSG's configuration file and change the [base URL](../getting_started_part_one.md#urls-and-baseurls)
|
||||
- Now go to your SSG's configuration file and change the [base URL](../getting_started_part_one.md#urls-and-base-urls)
|
||||
from `"project-name"` to `""`. The project name setting varies by SSG and may not be in the configuration file.
|
||||
|
|
|
@ -4,7 +4,7 @@ group: Release
|
|||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
|
||||
---
|
||||
|
||||
# GitLab Pages domain names, URLs, and baseurls
|
||||
# GitLab Pages domain names, URLs, and base URLs
|
||||
|
||||
On this document, learn how to name your project for GitLab Pages
|
||||
according to your intended website's URL.
|
||||
|
@ -78,7 +78,7 @@ To understand Pages domains clearly, read the examples below.
|
|||
- On your GitLab instance, replace `gitlab.io` above with your
|
||||
Pages server domain. Ask your sysadmin for this information.
|
||||
|
||||
## URLs and baseurls
|
||||
## URLs and base URLs
|
||||
|
||||
NOTE:
|
||||
The `baseurl` option might be called named differently in some static site generators.
|
||||
|
|
|
@ -10,7 +10,7 @@ description: "The static site editor enables users to edit content on static web
|
|||
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/28758) in GitLab 12.10.
|
||||
> - WYSIWYG editor [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/214559) in GitLab 13.0.
|
||||
> - Non-Markdown content blocks uneditable on the WYSIWYG mode [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/216836) in GitLab 13.3.
|
||||
> - Non-Markdown content blocks not editable on the WYSIWYG mode [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/216836) in GitLab 13.3.
|
||||
> - Formatting Markdown [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/49052) in GitLab 13.7.
|
||||
|
||||
Static Site Editor (SSE) enables users to edit content on static websites without
|
||||
|
|
|
@ -26,7 +26,7 @@ and from merge requests.
|
|||
|
||||
The file finder allows you to quickly open files in the current branch by
|
||||
searching for fragments of the file path. The file finder is launched using the keyboard shortcut
|
||||
<kbd>Cmd</kbd>+<kbd>p</kbd>, <kbd>Ctrl</kbd>+<kbd>p</kbd>, or <kbd>t</kbd>
|
||||
<kbd>Command</kbd>+<kbd>p</kbd>, <kbd>Control</kbd>+<kbd>p</kbd>, or <kbd>t</kbd>
|
||||
(when editor is not in focus). Type the filename or file path fragments to
|
||||
start seeing results.
|
||||
|
||||
|
|
|
@ -42,10 +42,10 @@ for example comments, replies, issue descriptions, and merge request description
|
|||
| Keyboard Shortcut | Description |
|
||||
| ---------------------------------------------------------------------- | ----------- |
|
||||
| <kbd>↑</kbd> | Edit your last comment. You must be in a blank text field below a thread, and you must already have at least one comment in the thread. |
|
||||
| <kbd>⌘</kbd> (Mac) / <kbd>Ctrl</kbd> + <kbd>Shift</kbd> + <kbd>p</kbd> | Toggle Markdown preview, when editing text in a text field that has **Write** and **Preview** tabs at the top. |
|
||||
| <kbd>⌘</kbd> (Mac) / <kbd>Ctrl</kbd> + <kbd>b</kbd> | Bold the selected text (surround it with `**`). |
|
||||
| <kbd>⌘</kbd> (Mac) / <kbd>Ctrl</kbd> + <kbd>i</kbd> | Italicize the selected text (surround it with `_`). |
|
||||
| <kbd>⌘</kbd> (Mac) / <kbd>Ctrl</kbd> + <kbd>k</kbd> | Add a link (surround the selected text with `[]()`). |
|
||||
| <kbd>⌘</kbd> (Mac) / <kbd>Control</kbd> + <kbd>Shift</kbd> + <kbd>p</kbd> | Toggle Markdown preview, when editing text in a text field that has **Write** and **Preview** tabs at the top. |
|
||||
| <kbd>⌘</kbd> (Mac) / <kbd>Control</kbd> + <kbd>b</kbd> | Bold the selected text (surround it with `**`). |
|
||||
| <kbd>⌘</kbd> (Mac) / <kbd>Control</kbd> + <kbd>i</kbd> | Italicize the selected text (surround it with `_`). |
|
||||
| <kbd>⌘</kbd> (Mac) / <kbd>Control</kbd> + <kbd>k</kbd> | Add a link (surround the selected text with `[]()`). |
|
||||
|
||||
NOTE:
|
||||
The shortcuts for editing in text fields are always enabled, even when
|
||||
|
@ -104,7 +104,7 @@ These shortcuts are available when browsing the files in a project (navigate to
|
|||
| <kbd>↑</kbd> | Move selection up. |
|
||||
| <kbd>↓</kbd> | Move selection down. |
|
||||
| <kbd>enter</kbd> | Open selection. |
|
||||
| <kbd>esc</kbd> | Go back to file list screen (only while searching for files, **Repository > Files** then click on **Find File**). |
|
||||
| <kbd>Escape</kbd> | Go back to file list screen (only while searching for files, **Repository > Files** then click on **Find File**). |
|
||||
| <kbd>y</kbd> | Go to file permalink (only while viewing a file). |
|
||||
|
||||
### Web IDE
|
||||
|
@ -113,8 +113,8 @@ These shortcuts are available when editing a file with the [Web IDE](project/web
|
|||
|
||||
| Keyboard Shortcut | Description |
|
||||
| ------------------------------------------------------- | ----------- |
|
||||
| <kbd>⌘</kbd> (Mac) / <kbd>Ctrl</kbd> + <kbd>p</kbd> | Search for, and then open another file for editing. |
|
||||
| <kbd>⌘</kbd> (Mac) / <kbd>Ctrl</kbd> + <kbd>Enter</kbd> | Commit (when editing the commit message). |
|
||||
| <kbd>⌘</kbd> (Mac) / <kbd>Control</kbd> + <kbd>p</kbd> | Search for, and then open another file for editing. |
|
||||
| <kbd>⌘</kbd> (Mac) / <kbd>Control</kbd> + <kbd>Enter</kbd> | Commit (when editing the commit message). |
|
||||
|
||||
### Repository Graph
|
||||
|
||||
|
@ -145,7 +145,7 @@ These shortcuts are available when using a [filtered search input](search/index.
|
|||
| Keyboard Shortcut | Description |
|
||||
| ----------------------------------------------------- | ----------- |
|
||||
| <kbd>⌘</kbd> (Mac) + <kbd>⌫</kbd> | Clear entire search filter. |
|
||||
| <kbd>⌥</kbd> (Mac) / <kbd>Ctrl</kbd> + <kbd>⌫</kbd> | Clear one token at a time. |
|
||||
| <kbd>⌥</kbd> (Mac) / <kbd>Control</kbd> + <kbd>⌫</kbd> | Clear one token at a time. |
|
||||
|
||||
## Epics **(ULTIMATE)**
|
||||
|
||||
|
|
|
@ -8291,12 +8291,21 @@ msgstr ""
|
|||
msgid "CreateValueStreamForm|'%{name}' Value Stream created"
|
||||
msgstr ""
|
||||
|
||||
msgid "CreateValueStreamForm|Add another stage"
|
||||
msgstr ""
|
||||
|
||||
msgid "CreateValueStreamForm|Add stage"
|
||||
msgstr ""
|
||||
|
||||
msgid "CreateValueStreamForm|All default stages are currently visible"
|
||||
msgstr ""
|
||||
|
||||
msgid "CreateValueStreamForm|Create from default template"
|
||||
msgstr ""
|
||||
|
||||
msgid "CreateValueStreamForm|Create from no template"
|
||||
msgstr ""
|
||||
|
||||
msgid "CreateValueStreamForm|Default stages"
|
||||
msgstr ""
|
||||
|
||||
|
@ -8312,16 +8321,13 @@ msgstr ""
|
|||
msgid "CreateValueStreamForm|End event: "
|
||||
msgstr ""
|
||||
|
||||
msgid "CreateValueStreamForm|Enter a name for the stage"
|
||||
msgstr ""
|
||||
|
||||
msgid "CreateValueStreamForm|Enter stage name"
|
||||
msgstr ""
|
||||
|
||||
msgid "CreateValueStreamForm|Maximum length %{maxLength} characters"
|
||||
msgid "CreateValueStreamForm|Enter value stream name"
|
||||
msgstr ""
|
||||
|
||||
msgid "CreateValueStreamForm|Name"
|
||||
msgid "CreateValueStreamForm|Maximum length %{maxLength} characters"
|
||||
msgstr ""
|
||||
|
||||
msgid "CreateValueStreamForm|Name is required"
|
||||
|
@ -8333,6 +8339,9 @@ msgstr ""
|
|||
msgid "CreateValueStreamForm|Please select a start event first"
|
||||
msgstr ""
|
||||
|
||||
msgid "CreateValueStreamForm|Please select an end event"
|
||||
msgstr ""
|
||||
|
||||
msgid "CreateValueStreamForm|Recover hidden stage"
|
||||
msgstr ""
|
||||
|
||||
|
@ -8354,6 +8363,9 @@ msgstr ""
|
|||
msgid "CreateValueStreamForm|Stage name already exists"
|
||||
msgstr ""
|
||||
|
||||
msgid "CreateValueStreamForm|Stage name is required"
|
||||
msgstr ""
|
||||
|
||||
msgid "CreateValueStreamForm|Start event"
|
||||
msgstr ""
|
||||
|
||||
|
@ -8369,6 +8381,9 @@ msgstr ""
|
|||
msgid "CreateValueStreamForm|Update stage"
|
||||
msgstr ""
|
||||
|
||||
msgid "CreateValueStreamForm|Value Stream name"
|
||||
msgstr ""
|
||||
|
||||
msgid "Created"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -73,6 +73,23 @@ RSpec.describe 'User comments on a diff', :js do
|
|||
end
|
||||
end
|
||||
|
||||
it 'allows suggestions in replies' do
|
||||
click_diff_line(find("[id='#{sample_compare.changes[1][:line_code]}']"))
|
||||
|
||||
page.within('.js-discussion-note-form') do
|
||||
fill_in('note_note', with: "```suggestion\n# change to a comment\n```")
|
||||
click_button('Add comment now')
|
||||
end
|
||||
|
||||
wait_for_requests
|
||||
|
||||
click_button 'Reply...'
|
||||
|
||||
find('.js-suggestion-btn').click
|
||||
|
||||
expect(find('.js-vue-issue-note-form').value).to include("url = https://github.com/gitlabhq/gitlab-shell.git")
|
||||
end
|
||||
|
||||
it 'suggestion is appliable' do
|
||||
click_diff_line(find("[id='#{sample_compare.changes[1][:line_code]}']"))
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe 'User searches for commits' do
|
||||
RSpec.describe 'User searches for commits', :js do
|
||||
let(:project) { create(:project, :repository) }
|
||||
let(:sha) { '6d394385cf567f80a8fd85055db1ab4c5295806f' }
|
||||
let(:user) { create(:user) }
|
||||
|
@ -41,7 +41,7 @@ RSpec.describe 'User searches for commits' do
|
|||
submit_search('See merge request')
|
||||
select_search_scope('Commits')
|
||||
|
||||
expect(page).to have_selector('.commit-row-description', count: 9)
|
||||
expect(page).to have_selector('.commit-row-description', visible: false, count: 9)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe 'User searches for projects' do
|
||||
RSpec.describe 'User searches for projects', :js do
|
||||
let!(:project) { create(:project, :public, name: 'Shop') }
|
||||
|
||||
context 'when signed out' do
|
||||
|
|
|
@ -0,0 +1,159 @@
|
|||
/* global List */
|
||||
/* global ListLabel */
|
||||
|
||||
import Vuex from 'vuex';
|
||||
import { createLocalVue, shallowMount } from '@vue/test-utils';
|
||||
|
||||
import MockAdapter from 'axios-mock-adapter';
|
||||
import waitForPromises from 'helpers/wait_for_promises';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
|
||||
import '~/boards/models/label';
|
||||
import '~/boards/models/assignee';
|
||||
import '~/boards/models/list';
|
||||
import boardsVuexStore from '~/boards/stores';
|
||||
import boardsStore from '~/boards/stores/boards_store';
|
||||
import BoardCardLayout from '~/boards/components/board_card_layout_deprecated.vue';
|
||||
import issueCardInner from '~/boards/components/issue_card_inner.vue';
|
||||
import { listObj, boardsMockInterceptor, setMockEndpoints } from '../mock_data';
|
||||
|
||||
import { ISSUABLE } from '~/boards/constants';
|
||||
|
||||
describe('Board card layout', () => {
|
||||
let wrapper;
|
||||
let mock;
|
||||
let list;
|
||||
let store;
|
||||
|
||||
const localVue = createLocalVue();
|
||||
localVue.use(Vuex);
|
||||
|
||||
const createStore = ({ getters = {}, actions = {} } = {}) => {
|
||||
store = new Vuex.Store({
|
||||
...boardsVuexStore,
|
||||
actions,
|
||||
getters,
|
||||
});
|
||||
};
|
||||
|
||||
// this particular mount component needs to be used after the root beforeEach because it depends on list being initialized
|
||||
const mountComponent = ({ propsData = {}, provide = {} } = {}) => {
|
||||
wrapper = shallowMount(BoardCardLayout, {
|
||||
localVue,
|
||||
stubs: {
|
||||
issueCardInner,
|
||||
},
|
||||
store,
|
||||
propsData: {
|
||||
list,
|
||||
issue: list.issues[0],
|
||||
disabled: false,
|
||||
index: 0,
|
||||
...propsData,
|
||||
},
|
||||
provide: {
|
||||
groupId: null,
|
||||
rootPath: '/',
|
||||
scopedLabelsAvailable: false,
|
||||
...provide,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const setupData = () => {
|
||||
list = new List(listObj);
|
||||
boardsStore.create();
|
||||
boardsStore.detail.issue = {};
|
||||
const label1 = new ListLabel({
|
||||
id: 3,
|
||||
title: 'testing 123',
|
||||
color: '#000cff',
|
||||
text_color: 'white',
|
||||
description: 'test',
|
||||
});
|
||||
return waitForPromises().then(() => {
|
||||
list.issues[0].labels.push(label1);
|
||||
});
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
mock = new MockAdapter(axios);
|
||||
mock.onAny().reply(boardsMockInterceptor);
|
||||
setMockEndpoints();
|
||||
return setupData();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
wrapper.destroy();
|
||||
wrapper = null;
|
||||
list = null;
|
||||
mock.restore();
|
||||
});
|
||||
|
||||
describe('mouse events', () => {
|
||||
it('sets showDetail to true on mousedown', async () => {
|
||||
createStore();
|
||||
mountComponent();
|
||||
|
||||
wrapper.trigger('mousedown');
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
expect(wrapper.vm.showDetail).toBe(true);
|
||||
});
|
||||
|
||||
it('sets showDetail to false on mousemove', async () => {
|
||||
createStore();
|
||||
mountComponent();
|
||||
wrapper.trigger('mousedown');
|
||||
await wrapper.vm.$nextTick();
|
||||
expect(wrapper.vm.showDetail).toBe(true);
|
||||
wrapper.trigger('mousemove');
|
||||
await wrapper.vm.$nextTick();
|
||||
expect(wrapper.vm.showDetail).toBe(false);
|
||||
});
|
||||
|
||||
it("calls 'setActiveId' when 'graphqlBoardLists' feature flag is turned on", async () => {
|
||||
const setActiveId = jest.fn();
|
||||
createStore({
|
||||
actions: {
|
||||
setActiveId,
|
||||
},
|
||||
});
|
||||
mountComponent({
|
||||
provide: {
|
||||
glFeatures: { graphqlBoardLists: true },
|
||||
},
|
||||
});
|
||||
|
||||
wrapper.trigger('mouseup');
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
expect(setActiveId).toHaveBeenCalledTimes(1);
|
||||
expect(setActiveId).toHaveBeenCalledWith(expect.any(Object), {
|
||||
id: list.issues[0].id,
|
||||
sidebarType: ISSUABLE,
|
||||
});
|
||||
});
|
||||
|
||||
it("calls 'setActiveId' when epic swimlanes is active", async () => {
|
||||
const setActiveId = jest.fn();
|
||||
const isSwimlanesOn = () => true;
|
||||
createStore({
|
||||
getters: { isSwimlanesOn },
|
||||
actions: {
|
||||
setActiveId,
|
||||
},
|
||||
});
|
||||
mountComponent();
|
||||
|
||||
wrapper.trigger('mouseup');
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
expect(setActiveId).toHaveBeenCalledTimes(1);
|
||||
expect(setActiveId).toHaveBeenCalledWith(expect.any(Object), {
|
||||
id: list.issues[0].id,
|
||||
sidebarType: ISSUABLE,
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,28 +1,15 @@
|
|||
/* global List */
|
||||
/* global ListLabel */
|
||||
|
||||
import Vuex from 'vuex';
|
||||
import { createLocalVue, shallowMount } from '@vue/test-utils';
|
||||
|
||||
import MockAdapter from 'axios-mock-adapter';
|
||||
import waitForPromises from 'helpers/wait_for_promises';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
|
||||
import '~/boards/models/label';
|
||||
import '~/boards/models/assignee';
|
||||
import '~/boards/models/list';
|
||||
import boardsVuexStore from '~/boards/stores';
|
||||
import boardsStore from '~/boards/stores/boards_store';
|
||||
import defaultState from '~/boards/stores/state';
|
||||
import BoardCardLayout from '~/boards/components/board_card_layout.vue';
|
||||
import issueCardInner from '~/boards/components/issue_card_inner.vue';
|
||||
import { listObj, boardsMockInterceptor, setMockEndpoints } from '../mock_data';
|
||||
import IssueCardInner from '~/boards/components/issue_card_inner.vue';
|
||||
import { mockLabelList, mockIssue } from '../mock_data';
|
||||
|
||||
import { ISSUABLE } from '~/boards/constants';
|
||||
|
||||
describe('Board card layout', () => {
|
||||
let wrapper;
|
||||
let mock;
|
||||
let list;
|
||||
let store;
|
||||
|
||||
const localVue = createLocalVue();
|
||||
|
@ -30,7 +17,7 @@ describe('Board card layout', () => {
|
|||
|
||||
const createStore = ({ getters = {}, actions = {} } = {}) => {
|
||||
store = new Vuex.Store({
|
||||
...boardsVuexStore,
|
||||
state: defaultState,
|
||||
actions,
|
||||
getters,
|
||||
});
|
||||
|
@ -41,12 +28,12 @@ describe('Board card layout', () => {
|
|||
wrapper = shallowMount(BoardCardLayout, {
|
||||
localVue,
|
||||
stubs: {
|
||||
issueCardInner,
|
||||
IssueCardInner,
|
||||
},
|
||||
store,
|
||||
propsData: {
|
||||
list,
|
||||
issue: list.issues[0],
|
||||
list: mockLabelList,
|
||||
issue: mockIssue,
|
||||
disabled: false,
|
||||
index: 0,
|
||||
...propsData,
|
||||
|
@ -60,34 +47,9 @@ describe('Board card layout', () => {
|
|||
});
|
||||
};
|
||||
|
||||
const setupData = () => {
|
||||
list = new List(listObj);
|
||||
boardsStore.create();
|
||||
boardsStore.detail.issue = {};
|
||||
const label1 = new ListLabel({
|
||||
id: 3,
|
||||
title: 'testing 123',
|
||||
color: '#000cff',
|
||||
text_color: 'white',
|
||||
description: 'test',
|
||||
});
|
||||
return waitForPromises().then(() => {
|
||||
list.issues[0].labels.push(label1);
|
||||
});
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
mock = new MockAdapter(axios);
|
||||
mock.onAny().reply(boardsMockInterceptor);
|
||||
setMockEndpoints();
|
||||
return setupData();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
wrapper.destroy();
|
||||
wrapper = null;
|
||||
list = null;
|
||||
mock.restore();
|
||||
});
|
||||
|
||||
describe('mouse events', () => {
|
||||
|
@ -112,25 +74,21 @@ describe('Board card layout', () => {
|
|||
expect(wrapper.vm.showDetail).toBe(false);
|
||||
});
|
||||
|
||||
it("calls 'setActiveId' when 'graphqlBoardLists' feature flag is turned on", async () => {
|
||||
it("calls 'setActiveId'", async () => {
|
||||
const setActiveId = jest.fn();
|
||||
createStore({
|
||||
actions: {
|
||||
setActiveId,
|
||||
},
|
||||
});
|
||||
mountComponent({
|
||||
provide: {
|
||||
glFeatures: { graphqlBoardLists: true },
|
||||
},
|
||||
});
|
||||
mountComponent();
|
||||
|
||||
wrapper.trigger('mouseup');
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
expect(setActiveId).toHaveBeenCalledTimes(1);
|
||||
expect(setActiveId).toHaveBeenCalledWith(expect.any(Object), {
|
||||
id: list.issues[0].id,
|
||||
id: mockIssue.id,
|
||||
sidebarType: ISSUABLE,
|
||||
});
|
||||
});
|
||||
|
@ -151,7 +109,7 @@ describe('Board card layout', () => {
|
|||
|
||||
expect(setActiveId).toHaveBeenCalledTimes(1);
|
||||
expect(setActiveId).toHaveBeenCalledWith(expect.any(Object), {
|
||||
id: list.issues[0].id,
|
||||
id: mockIssue.id,
|
||||
sidebarType: ISSUABLE,
|
||||
});
|
||||
});
|
||||
|
|
|
@ -7,6 +7,7 @@ describe('BoardConfigurationOptions', () => {
|
|||
const defaultProps = {
|
||||
hideBacklogList: false,
|
||||
hideClosedList: false,
|
||||
readonly: false,
|
||||
};
|
||||
|
||||
const createComponent = (props = {}) => {
|
||||
|
@ -61,4 +62,18 @@ describe('BoardConfigurationOptions', () => {
|
|||
|
||||
expect(wrapper.emitted('update:hideClosedList')).toEqual([[true]]);
|
||||
});
|
||||
|
||||
it('renders checkboxes disabled when user does not have edit rights', () => {
|
||||
createComponent({ readonly: true });
|
||||
|
||||
expect(closedListCheckbox().attributes('disabled')).toBe('true');
|
||||
expect(backlogListCheckbox().attributes('disabled')).toBe('true');
|
||||
});
|
||||
|
||||
it('renders checkboxes enabled when user has edit rights', () => {
|
||||
createComponent();
|
||||
|
||||
expect(closedListCheckbox().attributes('disabled')).toBeUndefined();
|
||||
expect(backlogListCheckbox().attributes('disabled')).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -20,7 +20,7 @@ describe('~/boards/components/sidebar/board_sidebar_milestone_select.vue', () =>
|
|||
wrapper = null;
|
||||
});
|
||||
|
||||
const createWrapper = ({ milestone = null } = {}) => {
|
||||
const createWrapper = ({ milestone = null, loading = false } = {}) => {
|
||||
store = createStore();
|
||||
store.state.issues = { [TEST_ISSUE.id]: { ...TEST_ISSUE, milestone } };
|
||||
store.state.activeId = TEST_ISSUE.id;
|
||||
|
@ -38,7 +38,7 @@ describe('~/boards/components/sidebar/board_sidebar_milestone_select.vue', () =>
|
|||
},
|
||||
mocks: {
|
||||
$apollo: {
|
||||
loading: false,
|
||||
loading,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
@ -63,12 +63,7 @@ describe('~/boards/components/sidebar/board_sidebar_milestone_select.vue', () =>
|
|||
});
|
||||
|
||||
it('shows loader while Apollo is loading', async () => {
|
||||
createWrapper({ milestone: TEST_MILESTONE });
|
||||
|
||||
expect(findLoader().exists()).toBe(false);
|
||||
|
||||
wrapper.vm.$apollo.loading = true;
|
||||
await wrapper.vm.$nextTick();
|
||||
createWrapper({ milestone: TEST_MILESTONE, loading: true });
|
||||
|
||||
expect(findLoader().exists()).toBe(true);
|
||||
});
|
||||
|
@ -76,8 +71,7 @@ describe('~/boards/components/sidebar/board_sidebar_milestone_select.vue', () =>
|
|||
it('shows message when error or no milestones found', async () => {
|
||||
createWrapper();
|
||||
|
||||
wrapper.setData({ milestones: [] });
|
||||
await wrapper.vm.$nextTick();
|
||||
await wrapper.setData({ milestones: [] });
|
||||
|
||||
expect(findNoMilestonesFoundItem().text()).toBe('No milestones found');
|
||||
});
|
||||
|
|
|
@ -41,7 +41,7 @@ describe('Issue model', () => {
|
|||
});
|
||||
|
||||
expect(issue.labels.length).toBe(1);
|
||||
expect(issue.labels[0].color).toBe('red');
|
||||
expect(issue.labels[0].color).toBe('#F0AD4E');
|
||||
});
|
||||
|
||||
it('adds other label with same title', () => {
|
||||
|
|
|
@ -137,7 +137,7 @@ export const rawIssue = {
|
|||
{
|
||||
id: 1,
|
||||
title: 'test',
|
||||
color: 'red',
|
||||
color: '#F0AD4E',
|
||||
description: 'testing',
|
||||
},
|
||||
],
|
||||
|
@ -165,7 +165,7 @@ export const mockIssue = {
|
|||
{
|
||||
id: 1,
|
||||
title: 'test',
|
||||
color: 'red',
|
||||
color: '#F0AD4E',
|
||||
description: 'testing',
|
||||
},
|
||||
],
|
||||
|
|
|
@ -1222,6 +1222,40 @@ describe('setSelectedProject', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('toggleBoardItemMultiSelection', () => {
|
||||
const boardItem = mockIssue;
|
||||
|
||||
it('should commit mutation ADD_BOARD_ITEM_TO_SELECTION if item is not on selection state', () => {
|
||||
testAction(
|
||||
actions.toggleBoardItemMultiSelection,
|
||||
boardItem,
|
||||
{ selectedBoardItems: [] },
|
||||
[
|
||||
{
|
||||
type: types.ADD_BOARD_ITEM_TO_SELECTION,
|
||||
payload: boardItem,
|
||||
},
|
||||
],
|
||||
[],
|
||||
);
|
||||
});
|
||||
|
||||
it('should commit mutation REMOVE_BOARD_ITEM_FROM_SELECTION if item is on selection state', () => {
|
||||
testAction(
|
||||
actions.toggleBoardItemMultiSelection,
|
||||
boardItem,
|
||||
{ selectedBoardItems: [mockIssue] },
|
||||
[
|
||||
{
|
||||
type: types.REMOVE_BOARD_ITEM_FROM_SELECTION,
|
||||
payload: boardItem,
|
||||
},
|
||||
],
|
||||
[],
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('fetchBacklog', () => {
|
||||
expectNotImplemented(actions.fetchBacklog);
|
||||
});
|
||||
|
|
|
@ -594,4 +594,27 @@ describe('Board Store Mutations', () => {
|
|||
expect(state.selectedProject).toEqual(mockGroupProjects[0]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('ADD_BOARD_ITEM_TO_SELECTION', () => {
|
||||
it('Should add boardItem to selectedBoardItems state', () => {
|
||||
expect(state.selectedBoardItems).toEqual([]);
|
||||
|
||||
mutations[types.ADD_BOARD_ITEM_TO_SELECTION](state, mockIssue);
|
||||
|
||||
expect(state.selectedBoardItems).toEqual([mockIssue]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('REMOVE_BOARD_ITEM_FROM_SELECTION', () => {
|
||||
it('Should remove boardItem to selectedBoardItems state', () => {
|
||||
state = {
|
||||
...state,
|
||||
selectedBoardItems: [mockIssue],
|
||||
};
|
||||
|
||||
mutations[types.REMOVE_BOARD_ITEM_FROM_SELECTION](state, mockIssue);
|
||||
|
||||
expect(state.selectedBoardItems).toEqual([]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import setHighlightClass from '~/search/highlight_blob_search_result';
|
||||
|
||||
const fixture = 'search/blob_search_result.html';
|
||||
const searchKeyword = 'Send'; // spec/frontend/fixtures/search.rb#79
|
||||
|
||||
describe('search/highlight_blob_search_result', () => {
|
||||
preloadFixtures(fixture);
|
||||
|
@ -8,7 +9,7 @@ describe('search/highlight_blob_search_result', () => {
|
|||
beforeEach(() => loadFixtures(fixture));
|
||||
|
||||
it('highlights lines with search term occurrence', () => {
|
||||
setHighlightClass();
|
||||
setHighlightClass(searchKeyword);
|
||||
|
||||
expect(document.querySelectorAll('.blob-result .hll').length).toBe(4);
|
||||
});
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
import setHighlightClass from 'ee_else_ce/search/highlight_blob_search_result';
|
||||
import { initSearchApp } from '~/search';
|
||||
import createStore from '~/search/store';
|
||||
|
||||
jest.mock('~/search/store');
|
||||
jest.mock('~/search/topbar');
|
||||
jest.mock('~/search/sidebar');
|
||||
jest.mock('ee_else_ce/search/highlight_blob_search_result');
|
||||
|
||||
describe('initSearchApp', () => {
|
||||
let defaultLocation;
|
||||
|
@ -42,6 +44,7 @@ describe('initSearchApp', () => {
|
|||
|
||||
it(`decodes ${search} to ${decodedSearch}`, () => {
|
||||
expect(createStore).toHaveBeenCalledWith({ query: { search: decodedSearch } });
|
||||
expect(setHighlightClass).toHaveBeenCalledWith(decodedSearch);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -0,0 +1,113 @@
|
|||
import Vuex from 'vuex';
|
||||
import { createLocalVue, shallowMount } from '@vue/test-utils';
|
||||
import { GlForm, GlSearchBoxByType, GlButton } from '@gitlab/ui';
|
||||
import { MOCK_QUERY } from 'jest/search/mock_data';
|
||||
import GlobalSearchTopbar from '~/search/topbar/components/app.vue';
|
||||
import GroupFilter from '~/search/topbar/components/group_filter.vue';
|
||||
import ProjectFilter from '~/search/topbar/components/project_filter.vue';
|
||||
|
||||
const localVue = createLocalVue();
|
||||
localVue.use(Vuex);
|
||||
|
||||
describe('GlobalSearchTopbar', () => {
|
||||
let wrapper;
|
||||
|
||||
const actionSpies = {
|
||||
applyQuery: jest.fn(),
|
||||
setQuery: jest.fn(),
|
||||
};
|
||||
|
||||
const createComponent = (initialState) => {
|
||||
const store = new Vuex.Store({
|
||||
state: {
|
||||
query: MOCK_QUERY,
|
||||
...initialState,
|
||||
},
|
||||
actions: actionSpies,
|
||||
});
|
||||
|
||||
wrapper = shallowMount(GlobalSearchTopbar, {
|
||||
localVue,
|
||||
store,
|
||||
});
|
||||
};
|
||||
|
||||
afterEach(() => {
|
||||
wrapper.destroy();
|
||||
wrapper = null;
|
||||
});
|
||||
|
||||
const findTopbarForm = () => wrapper.find(GlForm);
|
||||
const findGlSearchBox = () => wrapper.find(GlSearchBoxByType);
|
||||
const findGroupFilter = () => wrapper.find(GroupFilter);
|
||||
const findProjectFilter = () => wrapper.find(ProjectFilter);
|
||||
const findSearchButton = () => wrapper.find(GlButton);
|
||||
|
||||
describe('template', () => {
|
||||
beforeEach(() => {
|
||||
createComponent();
|
||||
});
|
||||
|
||||
it('renders Topbar Form always', () => {
|
||||
expect(findTopbarForm().exists()).toBe(true);
|
||||
});
|
||||
|
||||
describe('Search box', () => {
|
||||
it('renders always', () => {
|
||||
expect(findGlSearchBox().exists()).toBe(true);
|
||||
});
|
||||
|
||||
describe('onSearch', () => {
|
||||
const testSearch = 'test search';
|
||||
|
||||
beforeEach(() => {
|
||||
findGlSearchBox().vm.$emit('input', testSearch);
|
||||
});
|
||||
|
||||
it('calls setQuery when input event is fired from GlSearchBoxByType', () => {
|
||||
expect(actionSpies.setQuery).toHaveBeenCalledWith(expect.any(Object), {
|
||||
key: 'search',
|
||||
value: testSearch,
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe.each`
|
||||
snippets | showFilters
|
||||
${null} | ${true}
|
||||
${{ query: { snippets: '' } }} | ${true}
|
||||
${{ query: { snippets: false } }} | ${true}
|
||||
${{ query: { snippets: true } }} | ${false}
|
||||
${{ query: { snippets: 'false' } }} | ${true}
|
||||
${{ query: { snippets: 'true' } }} | ${false}
|
||||
`('topbar filters', ({ snippets, showFilters }) => {
|
||||
beforeEach(() => {
|
||||
createComponent(snippets);
|
||||
});
|
||||
|
||||
it(`does${showFilters ? '' : ' not'} render when snippets is ${JSON.stringify(
|
||||
snippets,
|
||||
)}`, () => {
|
||||
expect(findGroupFilter().exists()).toBe(showFilters);
|
||||
expect(findProjectFilter().exists()).toBe(showFilters);
|
||||
});
|
||||
});
|
||||
|
||||
it('renders SearchButton always', () => {
|
||||
expect(findSearchButton().exists()).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('actions', () => {
|
||||
beforeEach(() => {
|
||||
createComponent();
|
||||
});
|
||||
|
||||
it('clicking SearchButton calls applyQuery', () => {
|
||||
findTopbarForm().vm.$emit('submit', { preventDefault: () => {} });
|
||||
|
||||
expect(actionSpies.applyQuery).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,23 +0,0 @@
|
|||
import setHighlightClass from 'ee_else_ce/search/highlight_blob_search_result';
|
||||
import Search from '~/pages/search/show/search';
|
||||
|
||||
jest.mock('~/api');
|
||||
jest.mock('ee_else_ce/search/highlight_blob_search_result');
|
||||
|
||||
describe('Search', () => {
|
||||
const fixturePath = 'search/show.html';
|
||||
|
||||
preloadFixtures(fixturePath);
|
||||
|
||||
describe('constructor side effects', () => {
|
||||
afterEach(() => {
|
||||
jest.restoreAllMocks();
|
||||
});
|
||||
|
||||
it('highlights lines with search terms in blob search results', () => {
|
||||
new Search(); // eslint-disable-line no-new
|
||||
|
||||
expect(setHighlightClass).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
|
@ -221,7 +221,6 @@ RSpec.configure do |config|
|
|||
# of older sidebar.
|
||||
# See https://gitlab.com/groups/gitlab-org/-/epics/1863
|
||||
stub_feature_flags(vue_issuable_sidebar: false)
|
||||
stub_feature_flags(vue_issuable_epic_sidebar: false)
|
||||
|
||||
# Merge request widget GraphQL requests are disabled in the tests
|
||||
# for now whilst we migrate as much as we can over the GraphQL
|
||||
|
|
|
@ -1,17 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe 'search/_filter' do
|
||||
context 'when the search page is opened' do
|
||||
it 'displays the correct elements' do
|
||||
render
|
||||
|
||||
expect(rendered).to have_selector('label[for="dashboard_search_group"]')
|
||||
expect(rendered).to have_selector('input#js-search-group-dropdown')
|
||||
|
||||
expect(rendered).to have_selector('label[for="dashboard_search_project"]')
|
||||
expect(rendered).to have_selector('input#js-search-project-dropdown')
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,14 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe 'search/_form' do
|
||||
context 'when the search page is opened' do
|
||||
it 'displays the correct elements' do
|
||||
render
|
||||
|
||||
expect(rendered).to have_selector('.search-field-holder.form-group')
|
||||
expect(rendered).to have_selector('label[for="dashboard_search"]')
|
||||
end
|
||||
end
|
||||
end
|
56
yarn.lock
56
yarn.lock
|
@ -2486,14 +2486,15 @@ browserify-zlib@^0.2.0:
|
|||
pako "~1.0.5"
|
||||
|
||||
browserslist@^4.12.0, browserslist@^4.6.3, browserslist@^4.8.3:
|
||||
version "4.12.0"
|
||||
resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.12.0.tgz#06c6d5715a1ede6c51fc39ff67fd647f740b656d"
|
||||
integrity sha512-UH2GkcEDSI0k/lRkuDSzFl9ZZ87skSy9w2XAn1MsZnL+4c4rqbBd3e82UWHbYDpztABrPBhZsTEeuxVfHppqDg==
|
||||
version "4.16.1"
|
||||
resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.16.1.tgz#bf757a2da376b3447b800a16f0f1c96358138766"
|
||||
integrity sha512-UXhDrwqsNcpTYJBTZsbGATDxZbiVDsx6UjpmRUmtnP10pr8wAYr5LgFoEFw9ixriQH2mv/NX2SfGzE/o8GndLA==
|
||||
dependencies:
|
||||
caniuse-lite "^1.0.30001043"
|
||||
electron-to-chromium "^1.3.413"
|
||||
node-releases "^1.1.53"
|
||||
pkg-up "^2.0.0"
|
||||
caniuse-lite "^1.0.30001173"
|
||||
colorette "^1.2.1"
|
||||
electron-to-chromium "^1.3.634"
|
||||
escalade "^3.1.1"
|
||||
node-releases "^1.1.69"
|
||||
|
||||
bs-logger@0.x:
|
||||
version "0.2.6"
|
||||
|
@ -2717,10 +2718,10 @@ camelcase@^6.0.0:
|
|||
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.0.0.tgz#5259f7c30e35e278f1bdc2a4d91230b37cad981e"
|
||||
integrity sha512-8KMDF1Vz2gzOq54ONPJS65IvTUaB1cHJ2DMM7MbPmLZljDH1qpzzLsWdiN9pHh6qvkRVDTi/07+eNGch/oLU4w==
|
||||
|
||||
caniuse-lite@^1.0.30000980, caniuse-lite@^1.0.30001043:
|
||||
version "1.0.30001081"
|
||||
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001081.tgz#40615a3c416a047c5a4d45673e5257bf128eb3b5"
|
||||
integrity sha512-iZdh3lu09jsUtLE6Bp8NAbJskco4Y3UDtkR3GTCJGsbMowBU5IWDFF79sV2ws7lSqTzWyKazxam2thasHymENQ==
|
||||
caniuse-lite@^1.0.30000980, caniuse-lite@^1.0.30001173:
|
||||
version "1.0.30001178"
|
||||
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001178.tgz#3ad813b2b2c7d585b0be0a2440e1e233c6eabdbc"
|
||||
integrity sha512-VtdZLC0vsXykKni8Uztx45xynytOi71Ufx9T8kHptSw9AL4dpqailUJJHavttuzUe1KYuBYtChiWv+BAb7mPmQ==
|
||||
|
||||
capture-exit@^2.0.0:
|
||||
version "2.0.0"
|
||||
|
@ -3050,6 +3051,11 @@ color-name@~1.1.4:
|
|||
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
|
||||
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
|
||||
|
||||
colorette@^1.2.1:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.2.1.tgz#4d0b921325c14faf92633086a536db6e89564b1b"
|
||||
integrity sha512-puCDz0CzydiSYOrnXpz/PKd69zRrribezjtE9yd4zvytoRc8+RY/KJPvtPFKZS3E3wP6neGyMe0vOTlHO5L3Pw==
|
||||
|
||||
colors@^1.1.0:
|
||||
version "1.3.3"
|
||||
resolved "https://registry.yarnpkg.com/colors/-/colors-1.3.3.tgz#39e005d546afe01e01f9c4ca8fa50f686a01205d"
|
||||
|
@ -4283,10 +4289,10 @@ ejs@^2.6.1:
|
|||
resolved "https://registry.yarnpkg.com/ejs/-/ejs-2.6.1.tgz#498ec0d495655abc6f23cd61868d926464071aa0"
|
||||
integrity sha512-0xy4A/twfrRCnkhfk8ErDi5DqdAsAqeGxht4xkCUrsvhhbQNs7E+4jV0CN7+NKIY0aHE72+XvqtBIXzD31ZbXQ==
|
||||
|
||||
electron-to-chromium@^1.3.413:
|
||||
version "1.3.466"
|
||||
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.466.tgz#89f716db3afc4bb482ea2aaaa16c4808f89f762a"
|
||||
integrity sha512-eieqkoM2hCkZZRhETKyCouMziDV3l4XEKHRLuzcHG+HV+P7PeODU/z9HAmBgMQkzvHg2DoyQhfIDmmeguLZT/Q==
|
||||
electron-to-chromium@^1.3.634:
|
||||
version "1.3.642"
|
||||
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.642.tgz#8b884f50296c2ae2a9997f024d0e3e57facc2b94"
|
||||
integrity sha512-cev+jOrz/Zm1i+Yh334Hed6lQVOkkemk2wRozfMF4MtTR7pxf3r3L5Rbd7uX1zMcEqVJ7alJBnJL7+JffkC6FQ==
|
||||
|
||||
elliptic@^6.0.0:
|
||||
version "6.4.0"
|
||||
|
@ -4480,6 +4486,11 @@ es6-promisify@^5.0.0:
|
|||
dependencies:
|
||||
es6-promise "^4.0.3"
|
||||
|
||||
escalade@^3.1.1:
|
||||
version "3.1.1"
|
||||
resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40"
|
||||
integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==
|
||||
|
||||
escape-goat@^2.0.0:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/escape-goat/-/escape-goat-2.1.1.tgz#1b2dc77003676c457ec760b2dc68edb648188675"
|
||||
|
@ -8636,10 +8647,10 @@ node-notifier@^8.0.0:
|
|||
uuid "^8.3.0"
|
||||
which "^2.0.2"
|
||||
|
||||
node-releases@^1.1.53:
|
||||
version "1.1.58"
|
||||
resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.58.tgz#8ee20eef30fa60e52755fcc0942def5a734fe935"
|
||||
integrity sha512-NxBudgVKiRh/2aPWMgPR7bPTX0VPmGx5QBwCtdHitnqFE5/O8DeBXuIMH1nwNnw/aMo6AjOrpsHzfY3UbUJ7yg==
|
||||
node-releases@^1.1.69:
|
||||
version "1.1.70"
|
||||
resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.70.tgz#66e0ed0273aa65666d7fe78febe7634875426a08"
|
||||
integrity sha512-Slf2s69+2/uAD79pVVQo8uSiC34+g8GWY8UH2Qtqv34ZfhYrxpYpfzs9Js9d6O0mbDmALuxaTlplnBTnSELcrw==
|
||||
|
||||
node-sass@^4.14.1:
|
||||
version "4.14.1"
|
||||
|
@ -9362,13 +9373,6 @@ pkg-dir@^4.1.0, pkg-dir@^4.2.0:
|
|||
dependencies:
|
||||
find-up "^4.0.0"
|
||||
|
||||
pkg-up@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/pkg-up/-/pkg-up-2.0.0.tgz#c819ac728059a461cab1c3889a2be3c49a004d7f"
|
||||
integrity sha1-yBmscoBZpGHKscOImivjxJoATX8=
|
||||
dependencies:
|
||||
find-up "^2.1.0"
|
||||
|
||||
pngjs@^3.0.0:
|
||||
version "3.3.3"
|
||||
resolved "https://registry.yarnpkg.com/pngjs/-/pngjs-3.3.3.tgz#85173703bde3edac8998757b96e5821d0966a21b"
|
||||
|
|
Loading…
Reference in New Issue