Commit 99d03dd8 authored by Rajat Jain's avatar Rajat Jain

Autocomplete confidential only epics & issues

Passing `confidential_only=true` as a query param in the
autocomplete endpoints. Enabling the backend to only fetch
confidential epics and confidential issues.
parent 42f861cc
...@@ -2,6 +2,8 @@ ...@@ -2,6 +2,8 @@
import { GlFormGroup, GlFormRadioGroup, GlLoadingIcon } from '@gitlab/ui'; import { GlFormGroup, GlFormRadioGroup, GlLoadingIcon } from '@gitlab/ui';
import { __ } from '~/locale'; import { __ } from '~/locale';
import RelatedIssuableInput from './related_issuable_input.vue'; import RelatedIssuableInput from './related_issuable_input.vue';
import { mergeUrlParams } from '~/lib/utils/url_utility';
import { import {
issuableTypesMap, issuableTypesMap,
itemAddFailureTypesMap, itemAddFailureTypesMap,
...@@ -67,6 +69,11 @@ export default { ...@@ -67,6 +69,11 @@ export default {
required: false, required: false,
default: '', default: '',
}, },
confidential: {
type: Boolean,
required: false,
default: false,
},
}, },
data() { data() {
return { return {
...@@ -102,6 +109,21 @@ export default { ...@@ -102,6 +109,21 @@ export default {
// Only other failure is MAX_NUMBER_OF_CHILD_EPICS at the moment // Only other failure is MAX_NUMBER_OF_CHILD_EPICS at the moment
return addRelatedItemErrorMap[this.itemAddFailureType]; return addRelatedItemErrorMap[this.itemAddFailureType];
}, },
transformedAutocompleteSources() {
if (!this.confidential) {
return this.autoCompleteSources;
}
if (!this.autoCompleteSources?.issues || !this.autoCompleteSources?.epics) {
return this.autoCompleteSources;
}
return {
...this.autoCompleteSources,
issues: mergeUrlParams({ confidential_only: true }, this.autoCompleteSources.issues),
epics: mergeUrlParams({ confidential_only: true }, this.autoCompleteSources.epics),
};
},
}, },
methods: { methods: {
onPendingIssuableRemoveRequest(params) { onPendingIssuableRemoveRequest(params) {
...@@ -149,11 +171,12 @@ export default { ...@@ -149,11 +171,12 @@ export default {
<related-issuable-input <related-issuable-input
ref="relatedIssuableInput" ref="relatedIssuableInput"
input-id="add-related-issues-form-input" input-id="add-related-issues-form-input"
:confidential="confidential"
:focus-on-mount="true" :focus-on-mount="true"
:references="pendingReferences" :references="pendingReferences"
:path-id-separator="pathIdSeparator" :path-id-separator="pathIdSeparator"
:input-value="inputValue" :input-value="inputValue"
:auto-complete-sources="autoCompleteSources" :auto-complete-sources="transformedAutocompleteSources"
:auto-complete-options="{ issues: true, epics: true }" :auto-complete-options="{ issues: true, epics: true }"
:issuable-type="issuableType" :issuable-type="issuableType"
@pendingIssuableRemoveRequest="onPendingIssuableRemoveRequest" @pendingIssuableRemoveRequest="onPendingIssuableRemoveRequest"
......
...@@ -2,7 +2,12 @@ ...@@ -2,7 +2,12 @@
import $ from 'jquery'; import $ from 'jquery';
import GfmAutoComplete from 'ee_else_ce/gfm_auto_complete'; import GfmAutoComplete from 'ee_else_ce/gfm_auto_complete';
import issueToken from './issue_token.vue'; import issueToken from './issue_token.vue';
import { autoCompleteTextMap, inputPlaceholderTextMap, issuableTypesMap } from '../constants'; import {
autoCompleteTextMap,
inputPlaceholderConfidentialTextMap,
inputPlaceholderTextMap,
issuableTypesMap,
} from '../constants';
const SPACE_FACTOR = 1; const SPACE_FACTOR = 1;
...@@ -51,6 +56,11 @@ export default { ...@@ -51,6 +56,11 @@ export default {
required: false, required: false,
default: issuableTypesMap.ISSUE, default: issuableTypesMap.ISSUE,
}, },
confidential: {
type: Boolean,
required: false,
default: false,
},
}, },
data() { data() {
return { return {
...@@ -61,9 +71,12 @@ export default { ...@@ -61,9 +71,12 @@ export default {
}, },
computed: { computed: {
inputPlaceholder() { inputPlaceholder() {
const { issuableType, allowAutoComplete } = this; const { issuableType, allowAutoComplete, confidential } = this;
const inputPlaceholderMapping = confidential
? inputPlaceholderConfidentialTextMap
: inputPlaceholderTextMap;
const allowAutoCompleteText = autoCompleteTextMap[allowAutoComplete][issuableType]; const allowAutoCompleteText = autoCompleteTextMap[allowAutoComplete][issuableType];
return `${inputPlaceholderTextMap[issuableType]}${allowAutoCompleteText}`; return `${inputPlaceholderMapping[issuableType]}${allowAutoCompleteText}`;
}, },
allowAutoComplete() { allowAutoComplete() {
return Object.keys(this.autoCompleteSources).length > 0; return Object.keys(this.autoCompleteSources).length > 0;
......
...@@ -37,6 +37,12 @@ export const inputPlaceholderTextMap = { ...@@ -37,6 +37,12 @@ export const inputPlaceholderTextMap = {
[issuableTypesMap.MERGE_REQUEST]: __('Enter merge request URLs'), [issuableTypesMap.MERGE_REQUEST]: __('Enter merge request URLs'),
}; };
export const inputPlaceholderConfidentialTextMap = {
[issuableTypesMap.ISSUE]: __('Paste confidential issue link'),
[issuableTypesMap.EPIC]: __('Paste confidential epic link'),
[issuableTypesMap.MERGE_REQUEST]: __('Enter merge request URLs'),
};
export const relatedIssuesRemoveErrorMap = { export const relatedIssuesRemoveErrorMap = {
[issuableTypesMap.ISSUE]: __('An error occurred while removing issues.'), [issuableTypesMap.ISSUE]: __('An error occurred while removing issues.'),
[issuableTypesMap.EPIC]: __('An error occurred while removing epics.'), [issuableTypesMap.EPIC]: __('An error occurred while removing epics.'),
......
<script> <script>
import { mapState } from 'vuex';
import { GlDeprecatedButton, GlLoadingIcon } from '@gitlab/ui'; import { GlDeprecatedButton, GlLoadingIcon } from '@gitlab/ui';
import { __ } from '~/locale'; import { __ } from '~/locale';
...@@ -21,6 +23,7 @@ export default { ...@@ -21,6 +23,7 @@ export default {
}; };
}, },
computed: { computed: {
...mapState(['parentItem']),
isSubmitButtonDisabled() { isSubmitButtonDisabled() {
return this.inputValue.length === 0 || this.isSubmitting; return this.inputValue.length === 0 || this.isSubmitting;
}, },
...@@ -51,7 +54,9 @@ export default { ...@@ -51,7 +54,9 @@ export default {
<input <input
ref="input" ref="input"
v-model="inputValue" v-model="inputValue"
:placeholder="__('New epic title')" :placeholder="
parentItem.confidential ? __('New confidential epic title ') : __('New epic title')
"
type="text" type="text"
class="form-control" class="form-control"
@keyup.escape.exact="onFormCancel" @keyup.escape.exact="onFormCancel"
......
...@@ -33,7 +33,7 @@ export default { ...@@ -33,7 +33,7 @@ export default {
}; };
}, },
computed: { computed: {
...mapState(['projectsFetchInProgress', 'itemCreateInProgress', 'projects']), ...mapState(['projectsFetchInProgress', 'itemCreateInProgress', 'projects', 'parentItem']),
dropdownToggleText() { dropdownToggleText() {
if (this.selectedProject) { if (this.selectedProject) {
return this.selectedProject.name_with_namespace; return this.selectedProject.name_with_namespace;
...@@ -121,7 +121,9 @@ export default { ...@@ -121,7 +121,9 @@ export default {
<gl-form-input <gl-form-input
ref="titleInput" ref="titleInput"
v-model.trim="title" v-model.trim="title"
:placeholder="__('New issue title')" :placeholder="
parentItem.confidential ? __('New confidential issue title') : __('New issue title')
"
autofocus autofocus
/> />
</div> </div>
......
...@@ -167,6 +167,7 @@ export default { ...@@ -167,6 +167,7 @@ export default {
:has-error="itemAddFailure" :has-error="itemAddFailure"
:item-add-failure-type="itemAddFailureType" :item-add-failure-type="itemAddFailureType"
:item-add-failure-message="itemAddFailureMessage" :item-add-failure-message="itemAddFailureMessage"
:confidential="parentItem.confidential"
@pendingIssuableRemoveRequest="handlePendingItemRemove" @pendingIssuableRemoveRequest="handlePendingItemRemove"
@addIssuableFormInput="handleAddItemFormInput" @addIssuableFormInput="handleAddItemFormInput"
@addIssuableFormBlur="handleAddItemFormBlur" @addIssuableFormBlur="handleAddItemFormBlur"
......
...@@ -43,6 +43,7 @@ export default () => { ...@@ -43,6 +43,7 @@ export default () => {
id, id,
iid: Number(iid), iid: Number(iid),
title: initialData.initialTitleText, title: initialData.initialTitleText,
confidential: initialData.confidential,
reference: `${initialData.fullPath}${initialData.issuableRef}`, reference: `${initialData.fullPath}${initialData.issuableRef}`,
userPermissions: { userPermissions: {
adminEpic: initialData.canAdmin, adminEpic: initialData.canAdmin,
......
---
title: Autocomplete confidential only epics & issues
merge_request: 36687
author:
type: changed
...@@ -32,6 +32,17 @@ const findRadioInput = (inputs, value) => inputs.filter(input => input.element.v ...@@ -32,6 +32,17 @@ const findRadioInput = (inputs, value) => inputs.filter(input => input.element.v
const findRadioInputs = wrapper => wrapper.findAll('[name="linked-issue-type-radio"]'); const findRadioInputs = wrapper => wrapper.findAll('[name="linked-issue-type-radio"]');
const constructWrapper = props => {
return shallowMount(AddIssuableForm, {
propsData: {
inputValue: '',
pendingReferences: [],
pathIdSeparator,
...props,
},
});
};
describe('AddIssuableForm', () => { describe('AddIssuableForm', () => {
let wrapper; let wrapper;
...@@ -243,4 +254,40 @@ describe('AddIssuableForm', () => { ...@@ -243,4 +254,40 @@ describe('AddIssuableForm', () => {
}); });
}); });
}); });
describe('computed', () => {
describe('transformedAutocompleteSources', () => {
const autoCompleteSources = {
issues: 'http://localhost/autocomplete/issues',
epics: 'http://localhost/autocomplete/epics',
};
it('returns autocomplete object', () => {
wrapper = constructWrapper({
autoCompleteSources,
});
expect(wrapper.vm.transformedAutocompleteSources).toBe(autoCompleteSources);
wrapper = constructWrapper({
autoCompleteSources,
confidential: false,
});
expect(wrapper.vm.transformedAutocompleteSources).toBe(autoCompleteSources);
});
it('returns autocomplete sources with query `confidential_only`, when it is confidential', () => {
wrapper = constructWrapper({
autoCompleteSources,
confidential: true,
});
const actualSources = wrapper.vm.transformedAutocompleteSources;
expect(actualSources.epics).toContain('?confidential_only=true');
expect(actualSources.issues).toContain('?confidential_only=true');
});
});
});
}); });
import { shallowMount } from '@vue/test-utils'; import { createLocalVue, shallowMount } from '@vue/test-utils';
import Vuex from 'vuex';
import { GlDeprecatedButton } from '@gitlab/ui'; import { GlDeprecatedButton } from '@gitlab/ui';
import CreateEpicForm from 'ee/related_items_tree/components/create_epic_form.vue'; import CreateEpicForm from 'ee/related_items_tree/components/create_epic_form.vue';
import createDefaultStore from 'ee/related_items_tree/store';
import { mockInitialConfig, mockParentItem } from '../mock_data';
const localVue = createLocalVue();
localVue.use(Vuex);
const createComponent = (isSubmitting = false) => {
const store = createDefaultStore();
store.dispatch('setInitialConfig', mockInitialConfig);
store.dispatch('setInitialParentItem', mockParentItem);
const createComponent = (isSubmitting = false) => return shallowMount(CreateEpicForm, {
shallowMount(CreateEpicForm, { localVue,
store,
propsData: { propsData: {
isSubmitting, isSubmitting,
}, },
}); });
};
describe('RelatedItemsTree', () => { describe('RelatedItemsTree', () => {
describe('CreateEpicForm', () => { describe('CreateEpicForm', () => {
......
...@@ -15551,6 +15551,12 @@ msgstr "" ...@@ -15551,6 +15551,12 @@ msgstr ""
msgid "New changes were added. %{linkStart}Reload the page to review them%{linkEnd}" msgid "New changes were added. %{linkStart}Reload the page to review them%{linkEnd}"
msgstr "" msgstr ""
msgid "New confidential epic title "
msgstr ""
msgid "New confidential issue title"
msgstr ""
msgid "New deploy key" msgid "New deploy key"
msgstr "" msgstr ""
...@@ -16831,6 +16837,12 @@ msgstr "" ...@@ -16831,6 +16837,12 @@ msgstr ""
msgid "Paste a machine public key here. Read more about how to generate it %{link_start}here%{link_end}" msgid "Paste a machine public key here. Read more about how to generate it %{link_start}here%{link_end}"
msgstr "" msgstr ""
msgid "Paste confidential epic link"
msgstr ""
msgid "Paste confidential issue link"
msgstr ""
msgid "Paste epic link" msgid "Paste epic link"
msgstr "" msgstr ""
......
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