Commit 4d89e6a2 authored by GitLab Bot's avatar GitLab Bot

Automatic merge of gitlab-org/gitlab master

parents 3658a9b8 01eafdbc
<script>
import { mapState } from 'vuex';
import { GlAlert } from '@gitlab/ui';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
export default {
name: 'JiraConnectApp',
components: {
GlAlert,
},
mixins: [glFeatureFlagsMixin()],
computed: {
...mapState(['errorMessage']),
......@@ -16,6 +20,12 @@ export default {
<template>
<div>
<gl-alert v-if="errorMessage" class="gl-mb-6" variant="danger" :dismissible="false">
{{ errorMessage }}
</gl-alert>
<h1>GitLab for Jira Configuration</h1>
<div v-if="showNewUi">
<h3 data-testid="new-jira-connect-ui-heading">{{ s__('Integrations|Linked namespaces') }}</h3>
</div>
......
......@@ -23,11 +23,9 @@ const initJiraFormHandlers = () => {
};
const reqFailed = (res, fallbackErrorMessage) => {
const { responseJSON: { error = fallbackErrorMessage } = {} } = res || {};
const { error = fallbackErrorMessage } = res || {};
store.commit(SET_ERROR_MESSAGE, error);
// eslint-disable-next-line no-alert
alert(error);
};
if (typeof AP.getLocation === 'function') {
......
......@@ -44,13 +44,21 @@ export default {
<template>
<div data-testid="ci-lint-value">
<pre v-if="scripts.beforeScript.show" data-testid="ci-lint-before-script">{{
scripts.beforeScript.content
}}</pre>
<pre v-if="scripts.script.show" data-testid="ci-lint-script">{{ scripts.script.content }}</pre>
<pre v-if="scripts.afterScript.show" data-testid="ci-lint-after-script">{{
scripts.afterScript.content
<pre
v-if="scripts.beforeScript.show"
class="gl-white-space-pre-wrap"
data-testid="ci-lint-before-script"
>{{ scripts.beforeScript.content }}</pre
>
<pre v-if="scripts.script.show" class="gl-white-space-pre-wrap" data-testid="ci-lint-script">{{
scripts.script.content
}}</pre>
<pre
v-if="scripts.afterScript.show"
class="gl-white-space-pre-wrap"
data-testid="ci-lint-after-script"
>{{ scripts.afterScript.content }}</pre
>
<ul class="gl-list-style-none gl-pl-0 gl-mb-0">
<li v-if="tagList">
......
......@@ -256,7 +256,7 @@ export default {
return {
...filter,
value: {
data: stripQuotes(valueString),
data: typeof valueString === 'string' ? stripQuotes(valueString) : valueString,
operator: filter.value.operator,
},
};
......
@import 'mixins_and_variables_and_functions';
// We should only import styles that we actually use.
// @import '@gitlab/ui/src/scss/gitlab_ui';
/**
NOTE: We should only import styles that we actually use.
Ex:
@import '@gitlab/ui/src/scss/gitlab_ui';
*/
@import '@gitlab/ui/src/scss/bootstrap';
@import 'bootstrap-vue/src/index';
@import '@gitlab/ui/src/scss/utilities';
@import '@gitlab/ui/src/components/base/alert/alert';
$atlaskit-border-color: #dfe1e6;
......
......@@ -9,10 +9,9 @@
= link_to _('Sign in to GitLab'), jira_connect_users_path, target: '_blank', rel: 'noopener noreferrer', class: 'js-jira-connect-sign-in'
.jira-connect-app
- if current_user.blank? && @subscriptions.empty?
%h1
GitLab for Jira Configuration
- if current_user.blank? && @subscriptions.empty?
%h2.heading-with-border Sign in to GitLab.com to get started.
.gl-mt-5
......
---
title: Improve error messages when adding namespaces in Jira Connect App
merge_request: 48651
author:
type: changed
......@@ -67,8 +67,9 @@ You can also filter epics in the Roadmap view by:
- Author
- Label
- Milestone
- Confidentiality of epics
![roadmap date range in weeks](img/roadmap_filters_v13_7.png)
![roadmap date range in weeks](img/roadmap_filters_v13_8.png)
Roadmaps can also be [visualized inside an epic](../epics/index.md#roadmap-in-epics).
......
......@@ -6,6 +6,7 @@ import {
GlDropdown,
GlDropdownItem,
GlDropdownDivider,
GlFilteredSearchToken,
} from '@gitlab/ui';
import { __ } from '~/locale';
......@@ -132,10 +133,23 @@ export default {
});
},
},
{
type: 'confidential',
icon: 'eye-slash',
title: __('Confidential'),
unique: true,
token: GlFilteredSearchToken,
operators: [{ value: '=', description: __('is'), default: 'true' }],
options: [
{ icon: 'eye-slash', value: true, title: __('Yes') },
{ icon: 'eye', value: false, title: __('No') },
],
},
];
},
getFilteredSearchValue() {
const { authorUsername, labelName, milestoneTitle, search } = this.filterParams || {};
const { authorUsername, labelName, milestoneTitle, confidential, search } =
this.filterParams || {};
const filteredSearchValue = [];
if (authorUsername) {
......@@ -161,6 +175,13 @@ export default {
);
}
if (confidential !== undefined) {
filteredSearchValue.push({
type: 'confidential',
value: { data: confidential },
});
}
if (search) {
filteredSearchValue.push(search);
}
......@@ -169,7 +190,8 @@ export default {
},
updateUrl() {
const queryParams = urlParamsToObject(window.location.search);
const { authorUsername, labelName, milestoneTitle, search } = this.filterParams || {};
const { authorUsername, labelName, milestoneTitle, confidential, search } =
this.filterParams || {};
queryParams.state = this.epicsState;
queryParams.sort = this.sortedBy;
......@@ -191,6 +213,12 @@ export default {
queryParams['label_name[]'] = labelName;
}
if (confidential !== undefined) {
queryParams.confidential = confidential;
} else {
delete queryParams.confidential;
}
if (search) {
queryParams.search = search;
} else {
......@@ -229,6 +257,9 @@ export default {
case 'label_name':
labels.push(filter.value.data);
break;
case 'confidential':
filterParams.confidential = filter.value.data;
break;
default:
break;
}
......
......@@ -9,6 +9,7 @@ fragment BaseEpic on Epic {
dueDate
hasChildren
hasParent
confidential
descendantWeightSum {
closedIssues
openedIssues
......
......@@ -9,6 +9,7 @@ query epicChildEpics(
$dueDate: Time
$labelName: [String!] = []
$authorUsername: String = ""
$confidential: Boolean
$search: String = ""
) {
group(fullPath: $fullPath) {
......@@ -25,6 +26,7 @@ query epicChildEpics(
endDate: $dueDate
labelName: $labelName
authorUsername: $authorUsername
confidential: $confidential
search: $search
) {
edges {
......
......@@ -9,6 +9,7 @@ query groupEpics(
$labelName: [String!] = []
$authorUsername: String = ""
$milestoneTitle: String = ""
$confidential: Boolean
$search: String = ""
) {
group(fullPath: $fullPath) {
......@@ -22,6 +23,7 @@ query groupEpics(
labelName: $labelName
authorUsername: $authorUsername
milestoneTitle: $milestoneTitle
confidential: $confidential
search: $search
) {
edges {
......
......@@ -57,11 +57,17 @@ export default () => {
supportedPresetTypes.indexOf(dataset.presetType) > -1
? dataset.presetType
: PRESET_TYPES.MONTHS;
const filterParams = Object.assign(
convertObjectPropsToCamelCase(urlParamsToObject(window.location.search.substring(1)), {
const rawFilterParams = urlParamsToObject(window.location.search.substring(1));
const filterParams = {
...convertObjectPropsToCamelCase(rawFilterParams, {
dropKeys: ['scope', 'utf8', 'state', 'sort', 'layout'], // These keys are unsupported/unnecessary
}),
);
// We shall put parsed value of `confidential` only
// when it is defined.
...(rawFilterParams.confidential && {
confidential: parseBoolean(rawFilterParams.confidential),
}),
};
const timeframe = getTimeframeForPreset(
presetType,
window.innerWidth - el.offsetLeft - EPIC_DETAILS_CELL_WIDTH,
......
---
title: Add confidential filter token in Roadmap
merge_request: 51196
author:
type: added
import { GlSegmentedControl, GlDropdown, GlDropdownItem } from '@gitlab/ui';
import { GlSegmentedControl, GlDropdown, GlDropdownItem, GlFilteredSearchToken } from '@gitlab/ui';
import { shallowMount, createLocalVue } from '@vue/test-utils';
import Vuex from 'vuex';
......@@ -89,6 +89,7 @@ describe('RoadmapFilters', () => {
authorUsername: 'root',
labelName: ['Bug'],
milestoneTitle: '4.0',
confidential: true,
});
wrapper.vm.$store.dispatch('setSortedBy', 'end_date_asc');
......@@ -97,7 +98,7 @@ describe('RoadmapFilters', () => {
wrapper.vm.updateUrl();
expect(global.window.location.href).toBe(
`${TEST_HOST}/?state=${EPICS_STATES.CLOSED}&sort=end_date_asc&author_username=root&milestone_title=4.0&label_name%5B%5D=Bug`,
`${TEST_HOST}/?state=${EPICS_STATES.CLOSED}&sort=end_date_asc&author_username=root&milestone_title=4.0&label_name%5B%5D=Bug&confidential=true`,
);
});
});
......@@ -143,10 +144,18 @@ describe('RoadmapFilters', () => {
type: 'author_username',
value: { data: 'root' },
},
{
type: 'milestone_title',
value: { data: '4.0' },
},
{
type: 'label_name',
value: { data: 'Bug' },
},
{
type: 'confidential',
value: { data: true },
},
];
let filteredSearchBar;
......@@ -192,6 +201,18 @@ describe('RoadmapFilters', () => {
operators: [{ value: '=', description: 'is', default: 'true' }],
fetchMilestones: expect.any(Function),
},
{
type: 'confidential',
icon: 'eye-slash',
title: 'Confidential',
unique: true,
token: GlFilteredSearchToken,
operators: [{ value: '=', description: 'is', default: 'true' }],
options: [
{ icon: 'eye-slash', value: true, title: 'Yes' },
{ icon: 'eye', value: false, title: 'No' },
],
},
]);
});
......@@ -220,6 +241,8 @@ describe('RoadmapFilters', () => {
wrapper.vm.$store.dispatch('setFilterParams', {
authorUsername: 'root',
labelName: ['Bug'],
milestoneTitle: '4.0',
confidential: true,
});
await wrapper.vm.$nextTick();
......@@ -241,6 +264,8 @@ describe('RoadmapFilters', () => {
expect(wrapper.vm.setFilterParams).toHaveBeenCalledWith({
authorUsername: 'root',
labelName: ['Bug'],
milestoneTitle: '4.0',
confidential: true,
});
expect(wrapper.vm.fetchEpics).toHaveBeenCalled();
expect(wrapper.vm.updateUrl).toHaveBeenCalled();
......
import Vue from 'vue';
import Vuex from 'vuex';
import { shallowMount } from '@vue/test-utils';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import { GlAlert } from '@gitlab/ui';
import JiraConnectApp from '~/jira_connect/components/app.vue';
import createStore from '~/jira_connect/store';
import { SET_ERROR_MESSAGE } from '~/jira_connect/store/mutation_types';
Vue.use(Vuex);
describe('JiraConnectApp', () => {
let wrapper;
let store;
const findAlert = () => wrapper.findComponent(GlAlert);
const findHeader = () => wrapper.findByTestId('new-jira-connect-ui-heading');
const findHeaderText = () => findHeader().text();
const createComponent = (options = {}) => {
store = createStore();
wrapper = extendedWrapper(
shallowMount(JiraConnectApp, {
store,
provide: {
glFeatures: { newJiraConnectUi: true },
},
......@@ -43,5 +55,26 @@ describe('JiraConnectApp', () => {
expect(findHeader().exists()).toBe(false);
});
});
it.each`
errorMessage | errorShouldRender
${'Test error'} | ${true}
${''} | ${false}
${undefined} | ${false}
`(
'renders correct alert when errorMessage is `$errorMessage`',
async ({ errorMessage, errorShouldRender }) => {
createComponent();
store.commit(SET_ERROR_MESSAGE, errorMessage);
await wrapper.vm.$nextTick();
expect(findAlert().exists()).toBe(errorShouldRender);
if (errorShouldRender) {
expect(findAlert().isVisible()).toBe(errorShouldRender);
expect(findAlert().html()).toContain(errorMessage);
}
},
);
});
});
......@@ -25,6 +25,7 @@ import {
tokenValueLabel,
tokenValueMilestone,
tokenValueMembership,
tokenValueConfidential,
} from './mock_data';
jest.mock('~/vue_shared/components/filtered_search_bar/filtered_search_utils', () => ({
......@@ -227,12 +228,13 @@ describe('FilteredSearchBarRoot', () => {
});
describe('removeQuotesEnclosure', () => {
const mockFilters = [tokenValueAuthor, tokenValueLabel, 'foo'];
const mockFilters = [tokenValueAuthor, tokenValueLabel, tokenValueConfidential, 'foo'];
it('returns filter array with unescaped strings for values which have spaces', () => {
expect(wrapper.vm.removeQuotesEnclosure(mockFilters)).toEqual([
tokenValueAuthor,
tokenValueLabel,
tokenValueConfidential,
'foo',
]);
});
......
......@@ -155,6 +155,14 @@ export const tokenValueMembership = {
},
};
export const tokenValueConfidential = {
type: 'confidential',
value: {
operator: '=',
data: true,
},
};
export const tokenValuePlain = {
type: 'filtered-search-term',
value: { data: 'foo' },
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment