Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2021-01-22 09:08:53 +00:00
parent 16e3c34cac
commit 4dff02cf71
79 changed files with 904 additions and 418 deletions

16
.browserslistrc Normal file
View File

@ -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

View File

@ -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

View File

@ -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'

View File

@ -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)

View File

@ -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: {

View File

@ -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 });
}
},
},

View File

@ -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>

View File

@ -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') }}

View File

@ -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

View File

@ -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() {

View File

@ -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

View File

@ -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();
},

View File

@ -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';

View File

@ -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),
);
},
};

View File

@ -15,6 +15,7 @@ export default () => ({
filterParams: {},
boardConfig: {},
labels: [],
selectedBoardItems: [],
groupProjects: [],
groupProjectsFlags: {
isLoading: false,

View File

@ -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>

View File

@ -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();
});

View File

@ -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();
}
}

View File

@ -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) => {

View File

@ -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
};

View File

@ -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>

View File

@ -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"

View File

@ -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"

View File

@ -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>

View File

@ -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));

View File

@ -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;

View File

@ -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;
}

View File

@ -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

View File

@ -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 } }

View File

@ -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"

View File

@ -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'

View File

@ -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'

View File

@ -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' }

View File

@ -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',
},
},
],
];

View File

@ -0,0 +1,5 @@
---
title: Global Search - UX Cleanup of Search Bar
merge_request: 51409
author:
type: changed

View File

@ -0,0 +1,5 @@
---
title: Scope milestones on swimlane boards to project and its ancestors
merge_request: 52199
author:
type: fixed

View File

@ -0,0 +1,5 @@
---
title: Disable board configuration options for users without edit permission
merge_request: 52077
author:
type: fixed

View File

@ -0,0 +1,5 @@
---
title: Update buttons in _hook.html.haml to use GitLab UI
merge_request: 51065
author: nuwe1
type: other

View File

@ -0,0 +1,5 @@
---
title: Adds GitLabUI button styles in _test_button.html.haml
merge_request: 51070
author: nuwe1
type: other

View File

@ -0,0 +1,5 @@
---
title: Fixed sdiff suggestions not working when replying to comments
merge_request: 52100
author:
type: fixed

View File

@ -0,0 +1,5 @@
---
title: Fix alignment of chevron-down icon in toggle replies
merge_request: 51872
author: Yogi (@yo)
type: fixed

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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:

View File

@ -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:

View File

@ -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**.

View File

@ -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)

View File

@ -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:

View File

@ -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:

View File

@ -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:

View File

@ -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.

View 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.

View File

@ -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

View File

@ -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.

View File

@ -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)**

View File

@ -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 ""

View File

@ -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]}']"))

View File

@ -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

View File

@ -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

View File

@ -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,
});
});
});
});

View File

@ -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,
});
});

View File

@ -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();
});
});

View File

@ -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');
});

View File

@ -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', () => {

View File

@ -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',
},
],

View File

@ -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);
});

View File

@ -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([]);
});
});
});

View File

@ -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);
});

View File

@ -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);
});
});
});

View File

@ -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();
});
});
});

View File

@ -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();
});
});
});

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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"