summaryrefslogtreecommitdiff
path: root/app/assets/javascripts
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-09-15 18:09:43 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2020-09-15 18:09:43 +0000
commitf784f7d3b19fe80834240bde23d1300accb01118 (patch)
tree2acd4a74cbcde10c5b70c356b146af78362862d1 /app/assets/javascripts
parent06c127aa72cff78235426341081837cff0b6f78b (diff)
downloadgitlab-ce-f784f7d3b19fe80834240bde23d1300accb01118.tar.gz
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app/assets/javascripts')
-rw-r--r--app/assets/javascripts/ide/components/new_dropdown/modal.vue12
-rw-r--r--app/assets/javascripts/integrations/edit/components/integration_form.vue44
-rw-r--r--app/assets/javascripts/integrations/edit/index.js8
-rw-r--r--app/assets/javascripts/integrations/edit/store/actions.js2
-rw-r--r--app/assets/javascripts/integrations/edit/store/getters.js2
-rw-r--r--app/assets/javascripts/integrations/edit/store/mutation_types.js2
-rw-r--r--app/assets/javascripts/integrations/edit/store/mutations.js6
-rw-r--r--app/assets/javascripts/integrations/edit/store/state.js2
-rw-r--r--app/assets/javascripts/integrations/integration_settings_form.js114
-rw-r--r--app/assets/javascripts/lib/utils/axios_startup_calls.js76
-rw-r--r--app/assets/javascripts/packages/details/store/getters.js4
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/deployment/deployment_view_button.vue77
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/review_app_link.vue13
-rw-r--r--app/assets/javascripts/vue_shared/components/filtered_search_dropdown.vue153
14 files changed, 209 insertions, 306 deletions
diff --git a/app/assets/javascripts/ide/components/new_dropdown/modal.vue b/app/assets/javascripts/ide/components/new_dropdown/modal.vue
index 44986c8c575..528475849de 100644
--- a/app/assets/javascripts/ide/components/new_dropdown/modal.vue
+++ b/app/assets/javascripts/ide/components/new_dropdown/modal.vue
@@ -1,6 +1,6 @@
<script>
import { mapActions, mapState, mapGetters } from 'vuex';
-import { GlModal } from '@gitlab/ui';
+import { GlModal, GlButton } from '@gitlab/ui';
import { deprecatedCreateFlash as flash } from '~/flash';
import { __, sprintf, s__ } from '~/locale';
import { modalTypes } from '../../constants';
@@ -9,6 +9,7 @@ import { trimPathComponents, getPathParent } from '../../utils';
export default {
components: {
GlModal,
+ GlButton,
},
data() {
return {
@@ -156,13 +157,14 @@ export default {
/>
<ul v-if="isCreatingNewFile" class="file-templates gl-mt-3 list-inline qa-template-list">
<li v-for="(template, index) in templateTypes" :key="index" class="list-inline-item">
- <button
- type="button"
- class="btn btn-missing p-1 pr-2 pl-2"
+ <gl-button
+ variant="dashed"
+ category="secondary"
+ class="p-1 pr-2 pl-2"
@click="createFromTemplate(template)"
>
{{ template.name }}
- </button>
+ </gl-button>
</li>
</ul>
</div>
diff --git a/app/assets/javascripts/integrations/edit/components/integration_form.vue b/app/assets/javascripts/integrations/edit/components/integration_form.vue
index f74818ac87c..17f910a9d75 100644
--- a/app/assets/javascripts/integrations/edit/components/integration_form.vue
+++ b/app/assets/javascripts/integrations/edit/components/integration_form.vue
@@ -1,6 +1,8 @@
<script>
import { mapState, mapActions, mapGetters } from 'vuex';
+import { GlButton } from '@gitlab/ui';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
+import eventHub from '../event_hub';
import OverrideDropdown from './override_dropdown.vue';
import ActiveCheckbox from './active_checkbox.vue';
@@ -18,11 +20,15 @@ export default {
JiraIssuesFields,
TriggerFields,
DynamicField,
+ GlButton,
},
mixins: [glFeatureFlagsMixin()],
computed: {
- ...mapGetters(['currentKey', 'propsSource']),
- ...mapState(['adminState', 'override']),
+ ...mapGetters(['currentKey', 'propsSource', 'isSavingOrTesting']),
+ ...mapState(['adminState', 'override', 'isSaving', 'isTesting']),
+ isEditable() {
+ return this.propsSource.editable;
+ },
isJira() {
return this.propsSource.type === 'jira';
},
@@ -31,7 +37,15 @@ export default {
},
},
methods: {
- ...mapActions(['setOverride']),
+ ...mapActions(['setOverride', 'setIsSaving', 'setIsTesting']),
+ onSaveClick() {
+ this.setIsSaving(true);
+ eventHub.$emit('saveIntegration');
+ },
+ onTestClick() {
+ this.setIsTesting(true);
+ eventHub.$emit('testIntegration');
+ },
},
};
</script>
@@ -67,5 +81,29 @@ export default {
:key="`${currentKey}-jira-issues-fields`"
v-bind="propsSource.jiraIssuesProps"
/>
+ <div v-if="isEditable" class="footer-block row-content-block">
+ <gl-button
+ category="primary"
+ variant="success"
+ type="submit"
+ :loading="isSaving"
+ :disabled="isSavingOrTesting"
+ data-qa-selector="save_changes_button"
+ @click.prevent="onSaveClick"
+ >
+ {{ __('Save changes') }}
+ </gl-button>
+ <gl-button
+ v-if="propsSource.canTest"
+ :loading="isTesting"
+ :disabled="isSavingOrTesting"
+ :href="propsSource.testPath"
+ @click.prevent="onTestClick"
+ >
+ {{ __('Test settings') }}
+ </gl-button>
+
+ <gl-button class="btn-cancel" :href="propsSource.cancelPath">{{ __('Cancel') }}</gl-button>
+ </div>
</div>
</template>
diff --git a/app/assets/javascripts/integrations/edit/index.js b/app/assets/javascripts/integrations/edit/index.js
index 2ee25bcb1e7..5413226078d 100644
--- a/app/assets/javascripts/integrations/edit/index.js
+++ b/app/assets/javascripts/integrations/edit/index.js
@@ -24,11 +24,15 @@ function parseDatasetToProps(data) {
fields,
inheritFromId,
integrationLevel,
+ cancelPath,
+ testPath,
...booleanAttributes
} = data;
const {
showActive,
activated,
+ editable,
+ canTest,
commitEvents,
mergeRequestEvents,
enableComments,
@@ -41,6 +45,10 @@ function parseDatasetToProps(data) {
initialActivated: activated,
showActive,
type,
+ cancelPath,
+ editable,
+ canTest,
+ testPath,
triggerFieldsProps: {
initialTriggerCommit: commitEvents,
initialTriggerMergeRequest: mergeRequestEvents,
diff --git a/app/assets/javascripts/integrations/edit/store/actions.js b/app/assets/javascripts/integrations/edit/store/actions.js
index 1a1873979c9..199c9074ead 100644
--- a/app/assets/javascripts/integrations/edit/store/actions.js
+++ b/app/assets/javascripts/integrations/edit/store/actions.js
@@ -1,3 +1,5 @@
import * as types from './mutation_types';
export const setOverride = ({ commit }, override) => commit(types.SET_OVERRIDE, override);
+export const setIsSaving = ({ commit }, isSaving) => commit(types.SET_IS_SAVING, isSaving);
+export const setIsTesting = ({ commit }, isTesting) => commit(types.SET_IS_TESTING, isTesting);
diff --git a/app/assets/javascripts/integrations/edit/store/getters.js b/app/assets/javascripts/integrations/edit/store/getters.js
index b68bd668980..4b494c30845 100644
--- a/app/assets/javascripts/integrations/edit/store/getters.js
+++ b/app/assets/javascripts/integrations/edit/store/getters.js
@@ -1,5 +1,7 @@
export const isInheriting = state => (state.adminState === null ? false : !state.override);
+export const isSavingOrTesting = state => state.isSaving || state.isTesting;
+
export const propsSource = (state, getters) =>
getters.isInheriting ? state.adminState : state.customState;
diff --git a/app/assets/javascripts/integrations/edit/store/mutation_types.js b/app/assets/javascripts/integrations/edit/store/mutation_types.js
index dfba3c6d640..0dae8ea079e 100644
--- a/app/assets/javascripts/integrations/edit/store/mutation_types.js
+++ b/app/assets/javascripts/integrations/edit/store/mutation_types.js
@@ -1 +1,3 @@
export const SET_OVERRIDE = 'SET_OVERRIDE';
+export const SET_IS_SAVING = 'SET_IS_SAVING';
+export const SET_IS_TESTING = 'SET_IS_TESTING';
diff --git a/app/assets/javascripts/integrations/edit/store/mutations.js b/app/assets/javascripts/integrations/edit/store/mutations.js
index 8757d415197..8ac3c476f9e 100644
--- a/app/assets/javascripts/integrations/edit/store/mutations.js
+++ b/app/assets/javascripts/integrations/edit/store/mutations.js
@@ -4,4 +4,10 @@ export default {
[types.SET_OVERRIDE](state, override) {
state.override = override;
},
+ [types.SET_IS_SAVING](state, isSaving) {
+ state.isSaving = isSaving;
+ },
+ [types.SET_IS_TESTING](state, isTesting) {
+ state.isTesting = isTesting;
+ },
};
diff --git a/app/assets/javascripts/integrations/edit/store/state.js b/app/assets/javascripts/integrations/edit/store/state.js
index 95c1a2be500..7f1cd5c6e4d 100644
--- a/app/assets/javascripts/integrations/edit/store/state.js
+++ b/app/assets/javascripts/integrations/edit/store/state.js
@@ -5,5 +5,7 @@ export default ({ adminState = null, customState = {} } = {}) => {
override,
adminState,
customState,
+ isSaving: false,
+ isTesting: false,
};
};
diff --git a/app/assets/javascripts/integrations/integration_settings_form.js b/app/assets/javascripts/integrations/integration_settings_form.js
index 1135065b06c..529358a5f0b 100644
--- a/app/assets/javascripts/integrations/integration_settings_form.js
+++ b/app/assets/javascripts/integrations/integration_settings_form.js
@@ -1,7 +1,7 @@
import $ from 'jquery';
import axios from '../lib/utils/axios_utils';
-import { deprecatedCreateFlash as flash } from '../flash';
-import { __ } from '~/locale';
+import { __, s__ } from '~/locale';
+import toast from '~/vue_shared/plugins/global_toast';
import initForm from './edit';
import eventHub from './edit/event_hub';
@@ -10,65 +10,63 @@ export default class IntegrationSettingsForm {
this.$form = $(formSelector);
this.formActive = false;
+ this.vue = null;
+
// Form Metadata
- this.canTestService = this.$form.data('canTest');
this.testEndPoint = this.$form.data('testUrl');
-
- // Form Child Elements
- this.$submitBtn = this.$form.find('button[type="submit"]');
- this.$submitBtnLoader = this.$submitBtn.find('.js-btn-spinner');
- this.$submitBtnLabel = this.$submitBtn.find('.js-btn-label');
}
init() {
// Init Vue component
- initForm(
+ this.vue = initForm(
document.querySelector('.js-vue-integration-settings'),
document.querySelector('.js-vue-admin-integration-settings'),
);
eventHub.$on('toggle', active => {
this.formActive = active;
- this.handleServiceToggle();
+ this.toggleServiceState();
+ });
+ eventHub.$on('testIntegration', () => {
+ this.testIntegration();
+ });
+ eventHub.$on('saveIntegration', () => {
+ this.saveIntegration();
});
-
- // Bind Event Listeners
- this.$submitBtn.on('click', e => this.handleSettingsSave(e));
}
- handleSettingsSave(e) {
- // Check if Service is marked active, as if not marked active,
- // We can skip testing it and directly go ahead to allow form to
- // be submitted
- if (!this.formActive) {
- return;
+ saveIntegration() {
+ // Service was marked active so now we check;
+ // 1) If form contents are valid
+ // 2) If this service can be saved
+ // If both conditions are true, we override form submission
+ // and save the service using provided configuration.
+ if (this.$form.get(0).checkValidity()) {
+ this.$form.submit();
+ } else {
+ eventHub.$emit('validateForm');
+ this.vue.$store.dispatch('setIsSaving', false);
}
+ }
+ testIntegration() {
// Service was marked active so now we check;
// 1) If form contents are valid
// 2) If this service can be tested
// If both conditions are true, we override form submission
// and test the service using provided configuration.
if (this.$form.get(0).checkValidity()) {
- if (this.canTestService) {
- e.preventDefault();
- // eslint-disable-next-line no-jquery/no-serialize
- this.testSettings(this.$form.serialize());
- }
+ // eslint-disable-next-line no-jquery/no-serialize
+ this.testSettings(this.$form.serialize());
} else {
- e.preventDefault();
eventHub.$emit('validateForm');
+ this.vue.$store.dispatch('setIsTesting', false);
}
}
- handleServiceToggle() {
- this.toggleServiceState();
- }
-
/**
* Change Form's validation enforcement based on service status (active/inactive)
*/
toggleServiceState() {
- this.toggleSubmitBtnLabel();
if (this.formActive) {
this.$form.removeAttr('novalidate');
} else if (!this.$form.attr('novalidate')) {
@@ -77,67 +75,23 @@ export default class IntegrationSettingsForm {
}
/**
- * Toggle Submit button label based on Integration status and ability to test service
- */
- toggleSubmitBtnLabel() {
- let btnLabel = __('Save changes');
-
- if (this.formActive && this.canTestService) {
- btnLabel = __('Test settings and save changes');
- }
-
- this.$submitBtnLabel.text(btnLabel);
- }
-
- /**
- * Toggle Submit button state based on provided boolean value of `saveTestActive`
- * When enabled, it does two things, and reverts back when disabled
- *
- * 1. It shows load spinner on submit button
- * 2. Makes submit button disabled
- */
- toggleSubmitBtnState(saveTestActive) {
- if (saveTestActive) {
- this.$submitBtn.disable();
- this.$submitBtnLoader.removeClass('hidden');
- } else {
- this.$submitBtn.enable();
- this.$submitBtnLoader.addClass('hidden');
- }
- }
-
- /**
* Test Integration config
*/
testSettings(formData) {
- this.toggleSubmitBtnState(true);
-
return axios
.put(this.testEndPoint, formData)
.then(({ data }) => {
if (data.error) {
- let flashActions;
-
- if (data.test_failed) {
- flashActions = {
- title: __('Save anyway'),
- clickHandler: e => {
- e.preventDefault();
- this.$form.submit();
- },
- };
- }
-
- flash(`${data.message} ${data.service_response}`, 'alert', document, flashActions);
+ toast(`${data.message} ${data.service_response}`);
} else {
- this.$form.submit();
+ toast(s__('Integrations|Connection successful.'));
}
-
- this.toggleSubmitBtnState(false);
})
.catch(() => {
- flash(__('Something went wrong on our end.'));
- this.toggleSubmitBtnState(false);
+ toast(__('Something went wrong on our end.'));
+ })
+ .finally(() => {
+ this.vue.$store.dispatch('setIsTesting', false);
});
}
}
diff --git a/app/assets/javascripts/lib/utils/axios_startup_calls.js b/app/assets/javascripts/lib/utils/axios_startup_calls.js
index 642bd948b61..7e2665b910c 100644
--- a/app/assets/javascripts/lib/utils/axios_startup_calls.js
+++ b/app/assets/javascripts/lib/utils/axios_startup_calls.js
@@ -10,6 +10,32 @@ const getFullUrl = req => {
return mergeUrlParams(req.params || {}, url);
};
+const handleStartupCall = async ({ fetchCall }, req) => {
+ const res = await fetchCall;
+ if (!res.ok) {
+ throw new Error(res.statusText);
+ }
+
+ const fetchHeaders = {};
+ res.headers.forEach((val, key) => {
+ fetchHeaders[key] = val;
+ });
+
+ const data = await res.clone().json();
+
+ Object.assign(req, {
+ adapter: () =>
+ Promise.resolve({
+ data,
+ status: res.status,
+ statusText: res.statusText,
+ headers: fetchHeaders,
+ config: req,
+ request: req,
+ }),
+ });
+};
+
const setupAxiosStartupCalls = axios => {
const { startup_calls: startupCalls } = window.gl || {};
@@ -17,38 +43,28 @@ const setupAxiosStartupCalls = axios => {
return;
}
- // TODO: To save performance of future axios calls, we can
- // remove this interceptor once the "startupCalls" have been loaded
- axios.interceptors.request.use(req => {
+ const remainingCalls = new Map(Object.entries(startupCalls));
+
+ const interceptor = axios.interceptors.request.use(async req => {
const fullUrl = getFullUrl(req);
- const existing = startupCalls[fullUrl];
-
- if (existing) {
- // eslint-disable-next-line no-param-reassign
- req.adapter = () =>
- existing.fetchCall.then(res => {
- const fetchHeaders = {};
- res.headers.forEach((val, key) => {
- fetchHeaders[key] = val;
- });
-
- // We can delete it as it anyhow should only be called once
- delete startupCalls[fullUrl];
-
- // eslint-disable-next-line promise/no-nesting
- return res
- .clone()
- .json()
- .then(data => ({
- data,
- status: res.status,
- statusText: res.statusText,
- headers: fetchHeaders,
- config: req,
- request: req,
- }));
- });
+ const startupCall = remainingCalls.get(fullUrl);
+
+ if (!startupCall?.fetchCall) {
+ return req;
+ }
+
+ try {
+ await handleStartupCall(startupCall, req);
+ } catch (e) {
+ // eslint-disable-next-line no-console
+ console.warn(`[gitlab] Something went wrong with the startup call for "${fullUrl}"`, e);
+ }
+
+ remainingCalls.delete(fullUrl);
+
+ if (remainingCalls.size === 0) {
+ axios.interceptors.request.eject(interceptor);
}
return req;
diff --git a/app/assets/javascripts/packages/details/store/getters.js b/app/assets/javascripts/packages/details/store/getters.js
index d1814d506ad..ede6d39bde7 100644
--- a/app/assets/javascripts/packages/details/store/getters.js
+++ b/app/assets/javascripts/packages/details/store/getters.js
@@ -84,10 +84,10 @@ export const npmSetupCommand = ({ packageEntity, npmPath }) => (type = NpmManage
const scope = packageEntity.name.substring(0, packageEntity.name.indexOf('/'));
if (type === NpmManager.NPM) {
- return `echo ${scope}:registry=${npmPath} >> .npmrc`;
+ return `echo ${scope}:registry=${npmPath}/ >> .npmrc`;
}
- return `echo \\"${scope}:registry\\" \\"${npmPath}\\" >> .yarnrc`;
+ return `echo \\"${scope}:registry\\" \\"${npmPath}/\\" >> .yarnrc`;
};
export const nugetInstallationCommand = ({ packageEntity }) =>
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/deployment/deployment_view_button.vue b/app/assets/javascripts/vue_merge_request_widget/components/deployment/deployment_view_button.vue
index b12250d1d1c..157d6d60290 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/deployment/deployment_view_button.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/deployment/deployment_view_button.vue
@@ -1,17 +1,23 @@
<script>
-import { GlLink } from '@gitlab/ui';
-import FilteredSearchDropdown from '~/vue_shared/components/filtered_search_dropdown.vue';
+import { GlButtonGroup, GlDropdown, GlDropdownItem, GlLink, GlSearchBoxByType } from '@gitlab/ui';
+import autofocusonshow from '~/vue_shared/directives/autofocusonshow';
import ReviewAppLink from '../review_app_link.vue';
export default {
name: 'DeploymentViewButton',
components: {
- FilteredSearchDropdown,
+ GlButtonGroup,
+ GlDropdown,
+ GlDropdownItem,
GlLink,
+ GlSearchBoxByType,
ReviewAppLink,
VisualReviewAppLink: () =>
import('ee_component/vue_merge_request_widget/components/visual_review_app_link.vue'),
},
+ directives: {
+ autofocusonshow,
+ },
props: {
appButtonText: {
type: Object,
@@ -37,6 +43,9 @@ export default {
}),
},
},
+ data() {
+ return { searchTerm: '' };
+ },
computed: {
deploymentExternalUrl() {
if (this.deployment.changes && this.deployment.changes.length === 1) {
@@ -47,44 +56,52 @@ export default {
shouldRenderDropdown() {
return this.deployment.changes && this.deployment.changes.length > 1;
},
+ filteredChanges() {
+ return this.deployment?.changes?.filter(change => change.path.includes(this.searchTerm));
+ },
},
};
</script>
-
<template>
<span>
- <filtered-search-dropdown
- v-if="shouldRenderDropdown"
- class="js-mr-wigdet-deployment-dropdown inline"
- :items="deployment.changes"
- :main-action-link="deploymentExternalUrl"
- filter-key="path"
- >
- <template #mainAction="{ className }">
- <review-app-link
- :display="appButtonText"
- :link="deploymentExternalUrl"
- :css-class="`deploy-link js-deploy-url inline ${className}`"
+ <gl-button-group v-if="shouldRenderDropdown" size="small">
+ <review-app-link
+ :display="appButtonText"
+ :link="deploymentExternalUrl"
+ size="small"
+ css-class="deploy-link js-deploy-url inline"
+ />
+ <gl-dropdown size="small" class="js-mr-wigdet-deployment-dropdown">
+ <gl-search-box-by-type
+ v-model.trim="searchTerm"
+ v-autofocusonshow
+ autofocus
+ class="gl-m-3"
/>
- </template>
-
- <template #result="{ result }">
- <gl-link
- :href="result.external_url"
- target="_blank"
- rel="noopener noreferrer nofollow"
- class="js-deploy-url-menu-item menu-item"
+ <gl-dropdown-item
+ v-for="change in filteredChanges"
+ :key="change.path"
+ class="js-filtered-dropdown-result"
>
- <strong class="str-truncated-100 gl-mb-0 d-block">{{ result.path }}</strong>
-
- <p class="text-secondary str-truncated-100 gl-mb-0 d-block">{{ result.external_url }}</p>
- </gl-link>
- </template>
- </filtered-search-dropdown>
+ <gl-link
+ :href="change.external_url"
+ target="_blank"
+ rel="noopener noreferrer nofollow"
+ class="js-deploy-url-menu-item menu-item"
+ >
+ <strong class="str-truncated-100 gl-mb-0 gl-display-block">{{ change.path }}</strong>
+ <p class="text-secondary str-truncated-100 gl-mb-0 d-block">
+ {{ change.external_url }}
+ </p>
+ </gl-link>
+ </gl-dropdown-item>
+ </gl-dropdown>
+ </gl-button-group>
<review-app-link
v-else
:display="appButtonText"
:link="deploymentExternalUrl"
+ size="small"
css-class="js-deploy-url deploy-link btn btn-default btn-sm inline"
/>
<visual-review-app-link
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/review_app_link.vue b/app/assets/javascripts/vue_merge_request_widget/components/review_app_link.vue
index 16ac1e7fbb1..ebd2b5cd22d 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/review_app_link.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/review_app_link.vue
@@ -1,8 +1,9 @@
<script>
-import { GlTooltipDirective, GlIcon } from '@gitlab/ui';
+import { GlButton, GlTooltipDirective, GlIcon } from '@gitlab/ui';
export default {
components: {
+ GlButton,
GlIcon,
},
directives: {
@@ -21,14 +22,20 @@ export default {
type: String,
required: true,
},
+ size: {
+ type: String,
+ required: false,
+ default: 'medium',
+ },
},
};
</script>
<template>
- <a
+ <gl-button
v-gl-tooltip
:title="display.tooltip"
:href="link"
+ :size="size"
target="_blank"
rel="noopener noreferrer nofollow"
:class="cssClass"
@@ -36,5 +43,5 @@ export default {
data-track-label="review_app"
>
{{ display.text }} <gl-icon class="fgray" name="external-link" />
- </a>
+ </gl-button>
</template>
diff --git a/app/assets/javascripts/vue_shared/components/filtered_search_dropdown.vue b/app/assets/javascripts/vue_shared/components/filtered_search_dropdown.vue
deleted file mode 100644
index ee2687ace0e..00000000000
--- a/app/assets/javascripts/vue_shared/components/filtered_search_dropdown.vue
+++ /dev/null
@@ -1,153 +0,0 @@
-<script>
-import $ from 'jquery';
-import { GlDeprecatedButton, GlIcon } from '@gitlab/ui';
-import { __ } from '~/locale';
-/**
- * Renders a split dropdown with
- * an input that allows to search through the given
- * array of options.
- *
- * When there are no results and `showCreateMode` is true
- * it renders a create button with the value typed.
- */
-export default {
- name: 'FilteredSearchDropdown',
- components: {
- GlIcon,
- GlDeprecatedButton,
- },
- props: {
- title: {
- type: String,
- required: false,
- default: '',
- },
- buttonType: {
- required: false,
- validator: value =>
- ['primary', 'default', 'secondary', 'success', 'info', 'warning', 'danger'].indexOf(
- value,
- ) !== -1,
- default: 'default',
- },
- size: {
- required: false,
- type: String,
- default: 'sm',
- },
- items: {
- type: Array,
- required: true,
- },
- visibleItems: {
- type: Number,
- required: false,
- default: 5,
- },
- filterKey: {
- type: String,
- required: true,
- },
- showCreateMode: {
- type: Boolean,
- required: false,
- default: false,
- },
- createButtonText: {
- type: String,
- required: false,
- default: __('Create'),
- },
- },
- data() {
- return {
- filter: '',
- };
- },
- computed: {
- className() {
- return `btn btn-${this.buttonType} btn-${this.size}`;
- },
- filteredResults() {
- if (this.filter !== '') {
- return this.items.filter(
- item =>
- item[this.filterKey] &&
- item[this.filterKey].toLowerCase().includes(this.filter.toLowerCase()),
- );
- }
-
- return this.items.slice(0, this.visibleItems);
- },
- computedCreateButtonText() {
- return `${this.createButtonText} ${this.filter}`;
- },
- shouldRenderCreateButton() {
- return this.showCreateMode && this.filteredResults.length === 0 && this.filter !== '';
- },
- },
- mounted() {
- /**
- * Resets the filter every time the user closes the dropdown
- */
- $(this.$el)
- .on('shown.bs.dropdown', () => {
- this.$nextTick(() => this.$refs.searchInput.focus());
- })
- .on('hidden.bs.dropdown', () => {
- this.filter = '';
- });
- },
-};
-</script>
-<template>
- <div class="dropdown">
- <div class="btn-group">
- <slot name="mainAction" :class-name="className">
- <button type="button" :class="className">{{ title }}</button>
- </slot>
-
- <button
- type="button"
- :class="className"
- class="dropdown-toggle dropdown-toggle-split"
- data-toggle="dropdown"
- aria-haspopup="true"
- aria-expanded="false"
- :aria-label="__('Expand dropdown')"
- >
- <gl-icon name="angle-down" :size="12" />
- </button>
- <div class="dropdown-menu dropdown-menu-right">
- <div class="dropdown-input">
- <input
- ref="searchInput"
- v-model="filter"
- type="search"
- :placeholder="__('Filter')"
- class="js-filtered-dropdown-input dropdown-input-field"
- />
- <gl-icon class="dropdown-input-search" name="search" />
- </div>
-
- <div class="dropdown-content">
- <ul>
- <li v-for="(result, i) in filteredResults" :key="i" class="js-filtered-dropdown-result">
- <slot name="result" :result="result">{{ result[filterKey] }}</slot>
- </li>
- </ul>
- </div>
-
- <div v-if="shouldRenderCreateButton" class="dropdown-footer">
- <slot name="footer" :filter="filter">
- <gl-deprecated-button
- class="js-dropdown-create-button btn-transparent"
- @click="$emit('createItem', filter)"
- >{{ computedCreateButtonText }}</gl-deprecated-button
- >
- </slot>
- </div>
- </div>
- </div>
- </div>
-</template>