summaryrefslogtreecommitdiff
path: root/app/assets
diff options
context:
space:
mode:
Diffstat (limited to 'app/assets')
-rw-r--r--app/assets/javascripts/api.js6
-rw-r--r--app/assets/javascripts/ci_lint/components/ci_lint.vue5
-rw-r--r--app/assets/javascripts/feature_flags/components/form.vue16
-rw-r--r--app/assets/javascripts/feature_flags/components/strategies/gitlab_user_list.vue73
-rw-r--r--app/assets/javascripts/feature_flags/edit.js2
-rw-r--r--app/assets/javascripts/feature_flags/new.js2
-rw-r--r--app/assets/javascripts/feature_flags/store/edit/index.js4
-rw-r--r--app/assets/javascripts/feature_flags/store/gitlab_user_list/actions.js17
-rw-r--r--app/assets/javascripts/feature_flags/store/gitlab_user_list/getters.js11
-rw-r--r--app/assets/javascripts/feature_flags/store/gitlab_user_list/index.js12
-rw-r--r--app/assets/javascripts/feature_flags/store/gitlab_user_list/mutation_types.js5
-rw-r--r--app/assets/javascripts/feature_flags/store/gitlab_user_list/mutations.js19
-rw-r--r--app/assets/javascripts/feature_flags/store/gitlab_user_list/state.js9
-rw-r--r--app/assets/javascripts/feature_flags/store/gitlab_user_list/status.js6
-rw-r--r--app/assets/javascripts/feature_flags/store/helpers.js4
-rw-r--r--app/assets/javascripts/feature_flags/store/new/index.js4
-rw-r--r--app/assets/javascripts/filtered_search/add_extra_tokens_for_merge_requests.js68
17 files changed, 186 insertions, 77 deletions
diff --git a/app/assets/javascripts/api.js b/app/assets/javascripts/api.js
index 63b75cdb734..0c6acbf4e45 100644
--- a/app/assets/javascripts/api.js
+++ b/app/assets/javascripts/api.js
@@ -737,6 +737,12 @@ const Api = {
return axios.get(url, { params: { page } });
},
+ searchFeatureFlagUserLists(id, search) {
+ const url = Api.buildUrl(this.featureFlagUserLists).replace(':id', id);
+
+ return axios.get(url, { params: { search } });
+ },
+
createFeatureFlagUserList(id, list) {
const url = Api.buildUrl(this.featureFlagUserLists).replace(':id', id);
diff --git a/app/assets/javascripts/ci_lint/components/ci_lint.vue b/app/assets/javascripts/ci_lint/components/ci_lint.vue
index 2532f4b86d2..cae82ea316c 100644
--- a/app/assets/javascripts/ci_lint/components/ci_lint.vue
+++ b/app/assets/javascripts/ci_lint/components/ci_lint.vue
@@ -27,6 +27,7 @@ export default {
data() {
return {
content: '',
+ loading: false,
valid: false,
errors: null,
warnings: null,
@@ -44,6 +45,7 @@ export default {
},
methods: {
async lint() {
+ this.loading = true;
try {
const {
data: {
@@ -62,6 +64,8 @@ export default {
} catch (error) {
this.apiError = error;
this.isErrorDismissed = false;
+ } finally {
+ this.loading = false;
}
},
clear() {
@@ -93,6 +97,7 @@ export default {
<div class="gl-display-flex gl-align-items-center">
<gl-button
class="gl-mr-4"
+ :loading="loading"
category="primary"
variant="success"
data-testid="ci-lint-validate"
diff --git a/app/assets/javascripts/feature_flags/components/form.vue b/app/assets/javascripts/feature_flags/components/form.vue
index 3e2505b460a..36ebf893486 100644
--- a/app/assets/javascripts/feature_flags/components/form.vue
+++ b/app/assets/javascripts/feature_flags/components/form.vue
@@ -11,10 +11,8 @@ import {
GlSprintf,
GlIcon,
} from '@gitlab/ui';
-import Api from '~/api';
import RelatedIssuesRoot from '~/related_issues/components/related_issues_root.vue';
import { s__ } from '~/locale';
-import { deprecatedCreateFlash as flash, FLASH_TYPES } from '~/flash';
import featureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import ToggleButton from '~/vue_shared/components/toggle_button.vue';
import EnvironmentsDropdown from './environments_dropdown.vue';
@@ -89,7 +87,6 @@ export default {
},
},
inject: {
- projectId: {},
featureFlagIssuesEndpoint: {
default: '',
},
@@ -124,7 +121,6 @@ export default {
formStrategies: cloneDeep(this.strategies),
newScope: '',
- userLists: [],
};
},
computed: {
@@ -155,17 +151,6 @@ export default {
);
},
},
- mounted() {
- if (this.supportsStrategies) {
- Api.fetchFeatureFlagUserLists(this.projectId)
- .then(({ data }) => {
- this.userLists = data;
- })
- .catch(() => {
- flash(s__('FeatureFlags|There was an error retrieving user lists'), FLASH_TYPES.WARNING);
- });
- }
- },
methods: {
keyFor(strategy) {
if (strategy.id) {
@@ -346,7 +331,6 @@ export default {
:key="keyFor(strategy)"
:strategy="strategy"
:index="index"
- :user-lists="userLists"
@change="onFormStrategyChange($event, index)"
@delete="deleteStrategy(strategy)"
/>
diff --git a/app/assets/javascripts/feature_flags/components/strategies/gitlab_user_list.vue b/app/assets/javascripts/feature_flags/components/strategies/gitlab_user_list.vue
index ec97e8b1350..273bd47cfc1 100644
--- a/app/assets/javascripts/feature_flags/components/strategies/gitlab_user_list.vue
+++ b/app/assets/javascripts/feature_flags/components/strategies/gitlab_user_list.vue
@@ -1,11 +1,20 @@
<script>
-import { GlFormSelect } from '@gitlab/ui';
+import { debounce } from 'lodash';
+import { createNamespacedHelpers } from 'vuex';
+import { GlDropdown, GlDropdownItem, GlLoadingIcon, GlSearchBoxByType } from '@gitlab/ui';
import { s__ } from '~/locale';
import ParameterFormGroup from './parameter_form_group.vue';
+const { mapActions, mapGetters, mapState } = createNamespacedHelpers('userLists');
+
+const { fetchUserLists, setFilter } = mapActions(['fetchUserLists', 'setFilter']);
+
export default {
components: {
- GlFormSelect,
+ GlDropdown,
+ GlDropdownItem,
+ GlLoadingIcon,
+ GlSearchBoxByType,
ParameterFormGroup,
},
props: {
@@ -13,34 +22,40 @@ export default {
required: true,
type: Object,
},
- userLists: {
- required: false,
- type: Array,
- default: () => [],
- },
},
translations: {
- rolloutUserListLabel: s__('FeatureFlag|List'),
+ rolloutUserListLabel: s__('FeatureFlag|User List'),
rolloutUserListDescription: s__('FeatureFlag|Select a user list'),
rolloutUserListNoListError: s__('FeatureFlag|There are no configured user lists'),
+ defaultDropdownText: s__('FeatureFlags|Select a user list'),
},
computed: {
- userListOptions() {
- return this.userLists.map(({ name, id }) => ({ value: id, text: name }));
- },
- hasUserLists() {
- return this.userListOptions.length > 0;
- },
+ ...mapGetters(['hasUserLists', 'isLoading', 'hasError', 'userListOptions']),
+ ...mapState(['filter', 'userLists']),
userListId() {
- return this.strategy?.userListId ?? '';
+ return this.strategy?.userList?.id ?? '';
},
+ dropdownText() {
+ return this.strategy?.userList?.name ?? this.$options.defaultDropdownText;
+ },
+ },
+ mounted() {
+ fetchUserLists.apply(this);
},
methods: {
+ setFilter: debounce(setFilter, 250),
+ fetchUserLists: debounce(fetchUserLists, 250),
onUserListChange(list) {
this.$emit('change', {
- userListId: list,
+ userList: list,
});
},
+ isSelectedUserList({ id }) {
+ return id === this.userListId;
+ },
+ setFocus() {
+ this.$refs.searchBox.focusInput();
+ },
},
};
</script>
@@ -52,12 +67,26 @@ export default {
:description="hasUserLists ? $options.translations.rolloutUserListDescription : ''"
>
<template #default="{ inputId }">
- <gl-form-select
- :id="inputId"
- :value="userListId"
- :options="userListOptions"
- @change="onUserListChange"
- />
+ <gl-dropdown :id="inputId" :text="dropdownText" @shown="setFocus">
+ <gl-search-box-by-type
+ ref="searchBox"
+ class="gl-m-3"
+ :value="filter"
+ @input="setFilter"
+ @focus="fetchUserLists"
+ @keyup="fetchUserLists"
+ />
+ <gl-loading-icon v-if="isLoading" />
+ <gl-dropdown-item
+ v-for="list in userLists"
+ :key="list.id"
+ :is-checked="isSelectedUserList(list)"
+ is-check-item
+ @click="onUserListChange(list)"
+ >
+ {{ list.name }}
+ </gl-dropdown-item>
+ </gl-dropdown>
</template>
</parameter-form-group>
</template>
diff --git a/app/assets/javascripts/feature_flags/edit.js b/app/assets/javascripts/feature_flags/edit.js
index b4d2111acf3..05a9bbce654 100644
--- a/app/assets/javascripts/feature_flags/edit.js
+++ b/app/assets/javascripts/feature_flags/edit.js
@@ -22,7 +22,7 @@ export default () => {
} = el.dataset;
return new Vue({
- store: createStore({ endpoint, path: featureFlagsPath }),
+ store: createStore({ endpoint, projectId, path: featureFlagsPath }),
el,
provide: {
environmentsScopeDocsPath,
diff --git a/app/assets/javascripts/feature_flags/new.js b/app/assets/javascripts/feature_flags/new.js
index a1efbd87ec4..8e18213cc03 100644
--- a/app/assets/javascripts/feature_flags/new.js
+++ b/app/assets/javascripts/feature_flags/new.js
@@ -22,7 +22,7 @@ export default () => {
return new Vue({
el,
- store: createStore({ endpoint, path: featureFlagsPath }),
+ store: createStore({ endpoint, projectId, path: featureFlagsPath }),
provide: {
environmentsScopeDocsPath,
strategyTypeDocsPagePath,
diff --git a/app/assets/javascripts/feature_flags/store/edit/index.js b/app/assets/javascripts/feature_flags/store/edit/index.js
index f737e0517fc..81edc791924 100644
--- a/app/assets/javascripts/feature_flags/store/edit/index.js
+++ b/app/assets/javascripts/feature_flags/store/edit/index.js
@@ -1,4 +1,5 @@
import Vuex from 'vuex';
+import userLists from '../gitlab_user_list';
import state from './state';
import * as actions from './actions';
import mutations from './mutations';
@@ -8,4 +9,7 @@ export default data =>
actions,
mutations,
state: state(data),
+ modules: {
+ userLists: userLists(data),
+ },
});
diff --git a/app/assets/javascripts/feature_flags/store/gitlab_user_list/actions.js b/app/assets/javascripts/feature_flags/store/gitlab_user_list/actions.js
new file mode 100644
index 00000000000..d4587713fed
--- /dev/null
+++ b/app/assets/javascripts/feature_flags/store/gitlab_user_list/actions.js
@@ -0,0 +1,17 @@
+import Api from '~/api';
+import * as types from './mutation_types';
+
+const getErrorMessages = error => [].concat(error?.response?.data?.message ?? error.message);
+
+export const fetchUserLists = ({ commit, state: { filter, projectId } }) => {
+ commit(types.FETCH_USER_LISTS);
+
+ return Api.searchFeatureFlagUserLists(projectId, filter)
+ .then(({ data }) => commit(types.RECEIVE_USER_LISTS_SUCCESS, data))
+ .catch(error => commit(types.RECEIVE_USER_LISTS_ERROR, getErrorMessages(error)));
+};
+
+export const setFilter = ({ commit, dispatch }, filter) => {
+ commit(types.SET_FILTER, filter);
+ return dispatch('fetchUserLists');
+};
diff --git a/app/assets/javascripts/feature_flags/store/gitlab_user_list/getters.js b/app/assets/javascripts/feature_flags/store/gitlab_user_list/getters.js
new file mode 100644
index 00000000000..164b0980120
--- /dev/null
+++ b/app/assets/javascripts/feature_flags/store/gitlab_user_list/getters.js
@@ -0,0 +1,11 @@
+import statuses from './status';
+
+export const userListOptions = ({ userLists }) =>
+ userLists.map(({ name, id }) => ({ value: id, text: name }));
+
+export const hasUserLists = ({ userLists, status }) =>
+ [statuses.START, statuses.LOADING].indexOf(status) > -1 || userLists.length > 0;
+
+export const isLoading = ({ status }) => status === statuses.LOADING;
+
+export const hasError = ({ status }) => status === statuses.ERROR;
diff --git a/app/assets/javascripts/feature_flags/store/gitlab_user_list/index.js b/app/assets/javascripts/feature_flags/store/gitlab_user_list/index.js
new file mode 100644
index 00000000000..d25b574981f
--- /dev/null
+++ b/app/assets/javascripts/feature_flags/store/gitlab_user_list/index.js
@@ -0,0 +1,12 @@
+import state from './state';
+import mutations from './mutations';
+import * as actions from './actions';
+import * as getters from './getters';
+
+export default data => ({
+ state: state(data),
+ actions,
+ getters,
+ mutations,
+ namespaced: true,
+});
diff --git a/app/assets/javascripts/feature_flags/store/gitlab_user_list/mutation_types.js b/app/assets/javascripts/feature_flags/store/gitlab_user_list/mutation_types.js
new file mode 100644
index 00000000000..0fe12f06785
--- /dev/null
+++ b/app/assets/javascripts/feature_flags/store/gitlab_user_list/mutation_types.js
@@ -0,0 +1,5 @@
+export const FETCH_USER_LISTS = 'FETCH_USER_LISTS';
+export const RECEIVE_USER_LISTS_SUCCESS = 'RECEIVE_USER_LISTS_SUCCESS';
+export const RECEIVE_USER_LISTS_ERROR = 'RECEIVE_USER_LISTS_ERROR';
+
+export const SET_FILTER = 'SET_FILTER';
diff --git a/app/assets/javascripts/feature_flags/store/gitlab_user_list/mutations.js b/app/assets/javascripts/feature_flags/store/gitlab_user_list/mutations.js
new file mode 100644
index 00000000000..bd7c6f68009
--- /dev/null
+++ b/app/assets/javascripts/feature_flags/store/gitlab_user_list/mutations.js
@@ -0,0 +1,19 @@
+import statuses from './status';
+import * as types from './mutation_types';
+
+export default {
+ [types.FETCH_USER_LISTS](state) {
+ state.status = statuses.LOADING;
+ },
+ [types.RECEIVE_USER_LISTS_SUCCESS](state, lists) {
+ state.userLists = lists;
+ state.status = statuses.IDLE;
+ },
+ [types.RECEIVE_USER_LISTS_ERROR](state, error) {
+ state.error = error;
+ state.status = statuses.ERROR;
+ },
+ [types.SET_FILTER](state, filter) {
+ state.filter = filter;
+ },
+};
diff --git a/app/assets/javascripts/feature_flags/store/gitlab_user_list/state.js b/app/assets/javascripts/feature_flags/store/gitlab_user_list/state.js
new file mode 100644
index 00000000000..2664ec794fc
--- /dev/null
+++ b/app/assets/javascripts/feature_flags/store/gitlab_user_list/state.js
@@ -0,0 +1,9 @@
+import statuses from './status';
+
+export default ({ projectId }) => ({
+ projectId,
+ userLists: [],
+ filter: '',
+ status: statuses.START,
+ error: '',
+});
diff --git a/app/assets/javascripts/feature_flags/store/gitlab_user_list/status.js b/app/assets/javascripts/feature_flags/store/gitlab_user_list/status.js
new file mode 100644
index 00000000000..67f153eb58e
--- /dev/null
+++ b/app/assets/javascripts/feature_flags/store/gitlab_user_list/status.js
@@ -0,0 +1,6 @@
+export default {
+ START: 'START',
+ LOADING: 'LOADING',
+ IDLE: 'IDLE',
+ ERROR: 'ERROR',
+};
diff --git a/app/assets/javascripts/feature_flags/store/helpers.js b/app/assets/javascripts/feature_flags/store/helpers.js
index db6da815abf..d42e5c504db 100644
--- a/app/assets/javascripts/feature_flags/store/helpers.js
+++ b/app/assets/javascripts/feature_flags/store/helpers.js
@@ -174,7 +174,7 @@ export const mapStrategiesToViewModel = strategiesFromRails =>
id: s.id,
name: s.name,
parameters: mapStrategiesParametersToViewModel(s.parameters),
- userListId: s.user_list?.id,
+ userList: s.user_list,
// eslint-disable-next-line no-underscore-dangle
shouldBeDestroyed: Boolean(s._destroy),
scopes: mapStrategyScopesToView(s.scopes),
@@ -197,7 +197,7 @@ const mapStrategyToRails = strategy => {
};
if (strategy.name === ROLLOUT_STRATEGY_GITLAB_USER_LIST) {
- mappedStrategy.user_list_id = strategy.userListId;
+ mappedStrategy.user_list_id = strategy.userList.id;
}
return mappedStrategy;
};
diff --git a/app/assets/javascripts/feature_flags/store/new/index.js b/app/assets/javascripts/feature_flags/store/new/index.js
index f737e0517fc..81edc791924 100644
--- a/app/assets/javascripts/feature_flags/store/new/index.js
+++ b/app/assets/javascripts/feature_flags/store/new/index.js
@@ -1,4 +1,5 @@
import Vuex from 'vuex';
+import userLists from '../gitlab_user_list';
import state from './state';
import * as actions from './actions';
import mutations from './mutations';
@@ -8,4 +9,7 @@ export default data =>
actions,
mutations,
state: state(data),
+ modules: {
+ userLists: userLists(data),
+ },
});
diff --git a/app/assets/javascripts/filtered_search/add_extra_tokens_for_merge_requests.js b/app/assets/javascripts/filtered_search/add_extra_tokens_for_merge_requests.js
index 6f5aef54cef..7d4df25816b 100644
--- a/app/assets/javascripts/filtered_search/add_extra_tokens_for_merge_requests.js
+++ b/app/assets/javascripts/filtered_search/add_extra_tokens_for_merge_requests.js
@@ -96,43 +96,41 @@ export default (IssuableTokenKeys, disableTargetBranchFilter = false) => {
IssuableTokenKeys.tokenKeysWithAlternative.splice(tokenPosition, 0, ...[approvedBy.token]);
IssuableTokenKeys.conditions.push(...approvedBy.condition);
- if (gon?.features?.deploymentFilters) {
- const environmentToken = {
- formattedKey: __('Environment'),
- key: 'environment',
- type: 'string',
- param: '',
- symbol: '',
- icon: 'cloud-gear',
- tag: 'environment',
- };
+ const environmentToken = {
+ formattedKey: __('Environment'),
+ key: 'environment',
+ type: 'string',
+ param: '',
+ symbol: '',
+ icon: 'cloud-gear',
+ tag: 'environment',
+ };
- const deployedBeforeToken = {
- formattedKey: __('Deployed-before'),
- key: 'deployed-before',
- type: 'string',
- param: '',
- symbol: '',
- icon: 'clock',
- tag: 'deployed_before',
- };
+ const deployedBeforeToken = {
+ formattedKey: __('Deployed-before'),
+ key: 'deployed-before',
+ type: 'string',
+ param: '',
+ symbol: '',
+ icon: 'clock',
+ tag: 'deployed_before',
+ };
- const deployedAfterToken = {
- formattedKey: __('Deployed-after'),
- key: 'deployed-after',
- type: 'string',
- param: '',
- symbol: '',
- icon: 'clock',
- tag: 'deployed_after',
- };
+ const deployedAfterToken = {
+ formattedKey: __('Deployed-after'),
+ key: 'deployed-after',
+ type: 'string',
+ param: '',
+ symbol: '',
+ icon: 'clock',
+ tag: 'deployed_after',
+ };
- IssuableTokenKeys.tokenKeys.push(environmentToken, deployedBeforeToken, deployedAfterToken);
+ IssuableTokenKeys.tokenKeys.push(environmentToken, deployedBeforeToken, deployedAfterToken);
- IssuableTokenKeys.tokenKeysWithAlternative.push(
- environmentToken,
- deployedBeforeToken,
- deployedAfterToken,
- );
- }
+ IssuableTokenKeys.tokenKeysWithAlternative.push(
+ environmentToken,
+ deployedBeforeToken,
+ deployedAfterToken,
+ );
};