Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2022-02-07 06:16:03 +00:00
parent b475a8f853
commit 4ea8f39241
9 changed files with 129 additions and 146 deletions

View file

@ -3,6 +3,7 @@ import { GlTable, GlButton, GlModalDirective, GlIcon, GlTooltipDirective } from
import { mapState, mapActions } from 'vuex';
import { s__, __ } from '~/locale';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import TooltipOnTruncate from '~/vue_shared/components/tooltip_on_truncate/tooltip_on_truncate.vue';
import { ADD_CI_VARIABLE_MODAL_ID } from '../constants';
import CiVariablePopover from './ci_variable_popover.vue';
@ -52,10 +53,11 @@ export default {
},
],
components: {
GlTable,
CiVariablePopover,
GlButton,
GlIcon,
CiVariablePopover,
GlTable,
TooltipOnTruncate,
},
directives: {
GlModalDirective,
@ -67,8 +69,8 @@ export default {
valuesButtonText() {
return this.valuesHidden ? __('Reveal values') : __('Hide values');
},
tableIsNotEmpty() {
return this.variables && this.variables.length > 0;
isTableEmpty() {
return !this.variables || this.variables.length === 0;
},
fields() {
return this.$options.fields;
@ -103,12 +105,14 @@ export default {
<col v-for="field in scope.fields" :key="field.key" :style="field.customStyle" />
</template>
<template #cell(key)="{ item }">
<div class="gl-display-flex truncated-container gl-align-items-center">
<span
:id="`ci-variable-key-${item.id}`"
class="gl-display-inline-block gl-max-w-full gl-text-truncate"
>{{ item.key }}</span
>
<div class="gl-display-flex gl-align-items-center">
<tooltip-on-truncate :title="item.key" truncate-target="child">
<span
:id="`ci-variable-key-${item.id}`"
class="gl-display-inline-block gl-max-w-full gl-text-truncate"
>{{ item.key }}</span
>
</tooltip-on-truncate>
<gl-button
v-gl-tooltip
category="tertiary"
@ -120,7 +124,7 @@ export default {
</div>
</template>
<template #cell(value)="{ item }">
<div class="gl-display-flex gl-align-items-center truncated-container">
<div class="gl-display-flex gl-align-items-center">
<span v-if="valuesHidden">*********************</span>
<span
v-else
@ -147,10 +151,12 @@ export default {
<gl-icon v-else :size="$options.iconSize" :name="$options.falseIcon" />
</template>
<template #cell(environment_scope)="{ item }">
<div class="d-flex truncated-container">
<span :id="`ci-variable-env-${item.id}`" class="d-inline-block mw-100 text-truncate">{{
item.environment_scope
}}</span>
<div class="gl-display-flex">
<span
:id="`ci-variable-env-${item.id}`"
class="gl-display-inline-block gl-max-w-full gl-text-truncate"
>{{ item.environment_scope }}</span
>
<ci-variable-popover
:target="`ci-variable-env-${item.id}`"
:value="item.environment_scope"
@ -160,7 +166,6 @@ export default {
</template>
<template #cell(actions)="{ item }">
<gl-button
ref="edit-ci-variable"
v-gl-modal-directive="$options.modalId"
icon="pencil"
:aria-label="__('Edit')"
@ -169,17 +174,16 @@ export default {
/>
</template>
<template #empty>
<p ref="empty-variables" class="text-center empty-variables text-plain">
<p class="gl-text-center gl-py-6 gl-text-black-normal gl-mb-0">
{{ __('There are no variables yet.') }}
</p>
</template>
</gl-table>
<div
class="ci-variable-actions gl-display-flex"
:class="{ 'justify-content-center': !tableIsNotEmpty }"
:class="{ 'gl-justify-content-center': isTableEmpty }"
>
<gl-button
ref="add-ci-variable"
v-gl-modal-directive="$options.modalId"
class="gl-mr-3"
data-qa-selector="add_ci_variable_button"
@ -188,8 +192,7 @@ export default {
>{{ __('Add variable') }}</gl-button
>
<gl-button
v-if="tableIsNotEmpty"
ref="secret-value-reveal-button"
v-if="!isTableEmpty"
data-qa-selector="reveal_ci_variable_value_button"
@click="toggleValues(!valuesHidden)"
>{{ valuesButtonText }}</gl-button

View file

@ -10,16 +10,30 @@ import {
} from '@gitlab/ui';
import * as Sentry from '@sentry/browser';
import fuzzaldrinPlus from 'fuzzaldrin-plus';
import { orderBy } from 'lodash';
import IssueCardTimeInfo from 'ee_else_ce/issues/list/components/issue_card_time_info.vue';
import getIssuesQuery from 'ee_else_ce/issues/list/queries/get_issues.query.graphql';
import getIssuesCountsQuery from 'ee_else_ce/issues/list/queries/get_issues_counts.query.graphql';
import IssueCardTimeInfo from 'ee_else_ce/issues/list/components/issue_card_time_info.vue';
import createFlash, { FLASH_TYPES } from '~/flash';
import { TYPE_USER } from '~/graphql_shared/constants';
import { convertToGraphQLId, getIdFromGraphQLId } from '~/graphql_shared/utils';
import { ITEM_TYPE } from '~/groups/constants';
import CsvImportExportButtons from '~/issuable/components/csv_import_export_buttons.vue';
import IssuableByEmail from '~/issuable/components/issuable_by_email.vue';
import axios from '~/lib/utils/axios_utils';
import { scrollUp } from '~/lib/utils/scroll_utils';
import { getParameterByName, joinPaths } from '~/lib/utils/url_utility';
import {
DEFAULT_NONE_ANY,
OPERATOR_IS_ONLY,
TOKEN_TITLE_ASSIGNEE,
TOKEN_TITLE_AUTHOR,
TOKEN_TITLE_CONFIDENTIAL,
TOKEN_TITLE_LABEL,
TOKEN_TITLE_MILESTONE,
TOKEN_TITLE_MY_REACTION,
TOKEN_TITLE_RELEASE,
TOKEN_TITLE_TYPE,
} from '~/vue_shared/components/filtered_search_bar/constants';
import IssuableList from '~/vue_shared/issuable/list/components/issuable_list_root.vue';
import { IssuableListTabs, IssuableStates } from '~/vue_shared/issuable/list/constants';
import {
@ -40,7 +54,13 @@ import {
TOKEN_TYPE_TYPE,
UPDATED_DESC,
urlSortParams,
} from '~/issues/list/constants';
} from '../constants';
import eventHub from '../eventhub';
import reorderIssuesMutation from '../queries/reorder_issues.mutation.graphql';
import searchLabelsQuery from '../queries/search_labels.query.graphql';
import searchMilestonesQuery from '../queries/search_milestones.query.graphql';
import searchUsersQuery from '../queries/search_users.query.graphql';
import setSortPreferenceMutation from '../queries/set_sort_preference.mutation.graphql';
import {
convertToApiParams,
convertToSearchQuery,
@ -50,28 +70,7 @@ import {
getInitialPageParams,
getSortKey,
getSortOptions,
} from '~/issues/list/utils';
import axios from '~/lib/utils/axios_utils';
import { scrollUp } from '~/lib/utils/scroll_utils';
import { getParameterByName, joinPaths } from '~/lib/utils/url_utility';
import {
DEFAULT_NONE_ANY,
OPERATOR_IS_ONLY,
TOKEN_TITLE_ASSIGNEE,
TOKEN_TITLE_AUTHOR,
TOKEN_TITLE_CONFIDENTIAL,
TOKEN_TITLE_LABEL,
TOKEN_TITLE_MILESTONE,
TOKEN_TITLE_MY_REACTION,
TOKEN_TITLE_RELEASE,
TOKEN_TITLE_TYPE,
} from '~/vue_shared/components/filtered_search_bar/constants';
import eventHub from '../eventhub';
import reorderIssuesMutation from '../queries/reorder_issues.mutation.graphql';
import setSortPreferenceMutation from '../queries/set_sort_preference.mutation.graphql';
import searchLabelsQuery from '../queries/search_labels.query.graphql';
import searchMilestonesQuery from '../queries/search_milestones.query.graphql';
import searchUsersQuery from '../queries/search_users.query.graphql';
} from '../utils';
import NewIssueDropdown from './new_issue_dropdown.vue';
const AuthorToken = () =>
@ -103,77 +102,31 @@ export default {
directives: {
GlTooltip: GlTooltipDirective,
},
inject: {
autocompleteAwardEmojisPath: {
default: '',
},
calendarPath: {
default: '',
},
canBulkUpdate: {
default: false,
},
emptyStateSvgPath: {
default: '',
},
exportCsvPath: {
default: '',
},
fullPath: {
default: '',
},
hasAnyIssues: {
default: false,
},
hasAnyProjects: {
default: false,
},
hasBlockedIssuesFeature: {
default: false,
},
hasIssueWeightsFeature: {
default: false,
},
hasMultipleIssueAssigneesFeature: {
default: false,
},
initialEmail: {
default: '',
},
initialSort: {
default: '',
},
isAnonymousSearchDisabled: {
default: false,
},
isIssueRepositioningDisabled: {
default: false,
},
isProject: {
default: false,
},
isSignedIn: {
default: false,
},
jiraIntegrationPath: {
default: '',
},
newIssuePath: {
default: '',
},
releasesPath: {
default: '',
},
rssPath: {
default: '',
},
showNewIssueLink: {
default: false,
},
signInPath: {
default: '',
},
},
inject: [
'autocompleteAwardEmojisPath',
'calendarPath',
'canBulkUpdate',
'emptyStateSvgPath',
'exportCsvPath',
'fullPath',
'hasAnyIssues',
'hasAnyProjects',
'hasBlockedIssuesFeature',
'hasIssueWeightsFeature',
'hasMultipleIssueAssigneesFeature',
'initialEmail',
'initialSort',
'isAnonymousSearchDisabled',
'isIssueRepositioningDisabled',
'isProject',
'isSignedIn',
'jiraIntegrationPath',
'newIssuePath',
'releasesPath',
'rssPath',
'showNewIssueLink',
'signInPath',
],
props: {
eeSearchTokens: {
type: Array,
@ -349,6 +302,7 @@ export default {
token: MilestoneToken,
fetchMilestones: this.fetchMilestones,
recentSuggestionsStorageKey: `${this.fullPath}-issues-recent-tokens-milestone`,
shouldSkipSort: true,
},
{
type: TOKEN_TYPE_LABEL,
@ -414,7 +368,7 @@ export default {
tokens.sort((a, b) => a.title.localeCompare(b.title));
return orderBy(tokens, ['title']);
return tokens;
},
showPaginationControls() {
return this.issues.length > 0 && (this.pageInfo.hasNextPage || this.pageInfo.hasPreviousPage);

View file

@ -7,10 +7,10 @@ import {
GlSearchBoxByType,
} from '@gitlab/ui';
import createFlash from '~/flash';
import searchProjectsQuery from '~/issues/list/queries/search_projects.query.graphql';
import { DASH_SCOPE, joinPaths } from '~/lib/utils/url_utility';
import { __, sprintf } from '~/locale';
import { DEBOUNCE_DELAY } from '~/vue_shared/components/filtered_search_bar/constants';
import searchProjectsQuery from '../queries/search_projects.query.graphql';
export default {
i18n: {

View file

@ -3,7 +3,12 @@
query searchMilestones($fullPath: ID!, $search: String, $isProject: Boolean = false) {
group(fullPath: $fullPath) @skip(if: $isProject) {
id
milestones(searchTitle: $search, includeAncestors: true, includeDescendants: true) {
milestones(
searchTitle: $search
includeAncestors: true
includeDescendants: true
sort: EXPIRED_LAST_DUE_DATE_ASC
) {
nodes {
...Milestone
}
@ -11,7 +16,7 @@ query searchMilestones($fullPath: ID!, $search: String, $isProject: Boolean = fa
}
project(fullPath: $fullPath) @include(if: $isProject) {
id
milestones(searchTitle: $search, includeAncestors: true) {
milestones(searchTitle: $search, includeAncestors: true, sort: EXPIRED_LAST_DUE_DATE_ASC) {
nodes {
...Milestone
}

View file

@ -1,3 +1,9 @@
import { isPositiveInteger } from '~/lib/utils/number_utils';
import { __ } from '~/locale';
import {
FILTERED_SEARCH_TERM,
OPERATOR_IS_NOT,
} from '~/vue_shared/components/filtered_search_bar/constants';
import {
API_PARAM,
BLOCKING_ISSUES_ASC,
@ -36,13 +42,7 @@ import {
urlSortParams,
WEIGHT_ASC,
WEIGHT_DESC,
} from '~/issues/list/constants';
import { isPositiveInteger } from '~/lib/utils/number_utils';
import { __ } from '~/locale';
import {
FILTERED_SEARCH_TERM,
OPERATOR_IS_NOT,
} from '~/vue_shared/components/filtered_search_bar/constants';
} from './constants';
export const getInitialPageParams = (sortKey) =>
sortKey === RELATIVE_POSITION_ASC ? largePageSizeParams : defaultPageSizeParams;

View file

@ -43,8 +43,8 @@ export default {
class="gl-ml-3"
size="small"
:icon="glFeatures.restructuredMrWidget ? undefined : 'comment-next'"
:variant="glFeatures.restructuredMrWidget && 'confirm'"
:category="glFeatures.restructuredMrWidget && 'secondary'"
:variant="glFeatures.restructuredMrWidget ? 'confirm' : 'default'"
:category="glFeatures.restructuredMrWidget ? 'secondary' : 'primary'"
@click="jumpToFirstUnresolvedDiscussion"
>
{{ s__('mrWidget|Jump to first unresolved thread') }}

View file

@ -130,10 +130,6 @@
border-radius: $border-radius-base;
}
.empty-variables {
padding: 20px 0;
}
.warning-title {
color: $gray-900;
}

View file

@ -1,6 +1,6 @@
import { mount } from '@vue/test-utils';
import Vue, { nextTick } from 'vue';
import Vue from 'vue';
import Vuex from 'vuex';
import { mountExtended } from 'helpers/vue_test_utils_helper';
import CiVariableTable from '~/ci_variable_list/components/ci_variable_table.vue';
import createStore from '~/ci_variable_list/store';
import mockData from '../services/mock_data';
@ -14,15 +14,15 @@ describe('Ci variable table', () => {
const createComponent = () => {
store = createStore();
jest.spyOn(store, 'dispatch').mockImplementation();
wrapper = mount(CiVariableTable, {
wrapper = mountExtended(CiVariableTable, {
attachTo: document.body,
store,
});
};
const findRevealButton = () => wrapper.find({ ref: 'secret-value-reveal-button' });
const findEditButton = () => wrapper.find({ ref: 'edit-ci-variable' });
const findEmptyVariablesPlaceholder = () => wrapper.find({ ref: 'empty-variables' });
const findRevealButton = () => wrapper.findByText('Reveal values');
const findEditButton = () => wrapper.findByLabelText('Edit');
const findEmptyVariablesPlaceholder = () => wrapper.findByText('There are no variables yet.');
beforeEach(() => {
createComponent();
@ -36,18 +36,36 @@ describe('Ci variable table', () => {
expect(store.dispatch).toHaveBeenCalledWith('fetchVariables');
});
describe('Renders correct data', () => {
it('displays empty message when variables are not present', () => {
describe('When table is empty', () => {
beforeEach(() => {
store.state.variables = [];
});
it('displays empty message', () => {
expect(findEmptyVariablesPlaceholder().exists()).toBe(true);
});
it('displays correct amount of variables present and no empty message', async () => {
store.state.variables = mockData.mockVariables;
it('hides the reveal button', () => {
expect(findRevealButton().exists()).toBe(false);
});
});
await nextTick();
expect(wrapper.findAll('.js-ci-variable-row').length).toBe(1);
describe('When table has variables', () => {
beforeEach(() => {
store.state.variables = mockData.mockVariables;
});
it('does not display the empty message', () => {
expect(findEmptyVariablesPlaceholder().exists()).toBe(false);
});
it('displays the reveal button', () => {
expect(findRevealButton().exists()).toBe(true);
});
it('displays the correct amount of variables', async () => {
expect(wrapper.findAll('.js-ci-variable-row')).toHaveLength(1);
});
});
describe('Table click actions', () => {

View file

@ -62,6 +62,7 @@ describe('CE IssuesListApp component', () => {
Vue.use(VueApollo);
const defaultProvide = {
autocompleteAwardEmojisPath: 'autocomplete/award/emojis/path',
calendarPath: 'calendar/path',
canBulkUpdate: false,
emptyStateSvgPath: 'empty-state.svg',
@ -73,10 +74,16 @@ describe('CE IssuesListApp component', () => {
hasIssuableHealthStatusFeature: true,
hasIssueWeightsFeature: true,
hasIterationsFeature: true,
hasMultipleIssueAssigneesFeature: true,
initialEmail: 'email@example.com',
initialSort: CREATED_DESC,
isAnonymousSearchDisabled: false,
isIssueRepositioningDisabled: false,
isProject: true,
isSignedIn: true,
jiraIntegrationPath: 'jira/integration/path',
newIssuePath: 'new/issue/path',
releasesPath: 'releases/path',
rssPath: 'rss/path',
showNewIssueLink: true,
signInPath: 'sign/in/path',