Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
b475a8f853
commit
4ea8f39241
9 changed files with 129 additions and 146 deletions
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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: {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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') }}
|
||||
|
|
|
@ -130,10 +130,6 @@
|
|||
border-radius: $border-radius-base;
|
||||
}
|
||||
|
||||
.empty-variables {
|
||||
padding: 20px 0;
|
||||
}
|
||||
|
||||
.warning-title {
|
||||
color: $gray-900;
|
||||
}
|
||||
|
|
|
@ -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', () => {
|
||||
|
|
|
@ -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',
|
||||
|
|
Loading…
Reference in a new issue