diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-11-19 08:27:35 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-11-19 08:27:35 +0000 |
commit | 7e9c479f7de77702622631cff2628a9c8dcbc627 (patch) | |
tree | c8f718a08e110ad7e1894510980d2155a6549197 /app/assets/javascripts/feature_flags | |
parent | e852b0ae16db4052c1c567d9efa4facc81146e88 (diff) | |
download | gitlab-ce-7e9c479f7de77702622631cff2628a9c8dcbc627.tar.gz |
Add latest changes from gitlab-org/gitlab@13-6-stable-eev13.6.0-rc42
Diffstat (limited to 'app/assets/javascripts/feature_flags')
19 files changed, 171 insertions, 83 deletions
diff --git a/app/assets/javascripts/feature_flags/components/configure_feature_flags_modal.vue b/app/assets/javascripts/feature_flags/components/configure_feature_flags_modal.vue index 686399843dd..bf47d7cf7c0 100644 --- a/app/assets/javascripts/feature_flags/components/configure_feature_flags_modal.vue +++ b/app/assets/javascripts/feature_flags/components/configure_feature_flags_modal.vue @@ -2,6 +2,7 @@ import { GlFormGroup, GlFormInput, + GlFormInputGroup, GlModal, GlTooltipDirective, GlLoadingIcon, @@ -17,6 +18,7 @@ export default { components: { GlFormGroup, GlFormInput, + GlFormInputGroup, GlModal, ModalCopyButton, GlIcon, @@ -167,63 +169,47 @@ export default { </template> </gl-sprintf> </callout> - <div class="form-group"> - <label for="api_url" class="label-bold">{{ $options.translations.apiUrlLabelText }}</label> - <div class="input-group"> - <input - id="api_url" - :value="unleashApiUrl" - readonly - class="form-control" - type="text" - name="api_url" - /> - <span class="input-group-append"> + <gl-form-group :label="$options.translations.apiUrlLabelText" label-for="api-url"> + <gl-form-input-group id="api-url" :value="unleashApiUrl" readonly type="text" name="api-url"> + <template #append> <modal-copy-button :text="unleashApiUrl" :title="$options.translations.apiUrlCopyText" :modal-id="modalId" - class="input-group-text" /> - </span> - </div> - </div> - <div class="form-group"> - <label for="instance_id" class="label-bold">{{ - $options.translations.instanceIdLabelText - }}</label> - <div class="input-group"> - <input + </template> + </gl-form-input-group> + </gl-form-group> + <gl-form-group :label="$options.translations.instanceIdLabelText" label-for="instance_id"> + <gl-form-input-group> + <gl-form-input id="instance_id" :value="instanceId" - class="form-control" type="text" name="instance_id" readonly :disabled="isRotating" /> - <gl-loading-icon v-if="isRotating" - class="position-absolute align-self-center instance-id-loading-icon" + class="gl-absolute gl-align-self-center gl-right-5 gl-mr-7" /> - <div class="input-group-append"> + <template #append> <modal-copy-button :text="instanceId" :title="$options.translations.instanceIdCopyText" :modal-id="modalId" :disabled="isRotating" - class="input-group-text" /> - </div> - </div> - </div> + </template> + </gl-form-input-group> + </gl-form-group> <div v-if="hasRotateError" - class="text-danger d-flex align-items-center font-weight-normal mb-2" + class="gl-text-red-500 gl-display-flex gl-align-items-center gl-font-weight-normal gl-mb-3" > - <gl-icon name="warning" class="mr-1" /> + <gl-icon name="warning" class="gl-mr-2" /> <span>{{ $options.translations.instanceIdRegenerateError }}</span> </div> <callout diff --git a/app/assets/javascripts/feature_flags/components/edit_feature_flag.vue b/app/assets/javascripts/feature_flags/components/edit_feature_flag.vue index 26b18f9bf5a..9ec65bb0b43 100644 --- a/app/assets/javascripts/feature_flags/components/edit_feature_flag.vue +++ b/app/assets/javascripts/feature_flags/components/edit_feature_flag.vue @@ -103,7 +103,7 @@ export default { > {{ $options.translations.newFlagAlert }} </gl-alert> - <gl-loading-icon v-if="isLoading" /> + <gl-loading-icon v-if="isLoading" size="xl" class="gl-mt-7" /> <template v-else-if="!isLoading && !hasError"> <gl-alert v-if="deprecatedAndEditable" variant="warning" :dismissible="false" class="gl-my-5"> diff --git a/app/assets/javascripts/feature_flags/components/feature_flags.vue b/app/assets/javascripts/feature_flags/components/feature_flags.vue index eb7046a3d9b..340cf68793f 100644 --- a/app/assets/javascripts/feature_flags/components/feature_flags.vue +++ b/app/assets/javascripts/feature_flags/components/feature_flags.vue @@ -278,7 +278,7 @@ export default { /> </feature-flags-tab> <template #tabs-end> - <div + <li class="gl-display-none gl-display-md-flex gl-align-items-center gl-flex-fill-1 gl-justify-content-end" > <gl-button @@ -313,7 +313,7 @@ export default { > {{ s__('FeatureFlags|New feature flag') }} </gl-button> - </div> + </li> </template> </gl-tabs> </div> diff --git a/app/assets/javascripts/feature_flags/components/form.vue b/app/assets/javascripts/feature_flags/components/form.vue index 3c1944d91bd..36ebf893486 100644 --- a/app/assets/javascripts/feature_flags/components/form.vue +++ b/app/assets/javascripts/feature_flags/components/form.vue @@ -3,7 +3,7 @@ import Vue from 'vue'; import { memoize, isString, cloneDeep, isNumber, uniqueId } from 'lodash'; import { GlButton, - GlDeprecatedBadge as GlBadge, + GlBadge, GlTooltip, GlTooltipDirective, GlFormTextarea, @@ -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)" /> @@ -488,7 +472,9 @@ export default { :target="rolloutPercentageId(index)" > {{ - s__('FeatureFlags|Percent rollout must be a whole number between 0 and 100') + s__( + 'FeatureFlags|Percent rollout must be an integer number between 0 and 100', + ) }} </gl-tooltip> <span class="ml-1">%</span> diff --git a/app/assets/javascripts/feature_flags/components/strategies/flexible_rollout.vue b/app/assets/javascripts/feature_flags/components/strategies/flexible_rollout.vue index 020a0d43096..4daf8b4e6bf 100644 --- a/app/assets/javascripts/feature_flags/components/strategies/flexible_rollout.vue +++ b/app/assets/javascripts/feature_flags/components/strategies/flexible_rollout.vue @@ -17,8 +17,8 @@ export default { }, }, i18n: { - percentageDescription: __('Enter a whole number between 0 and 100'), - percentageInvalid: __('Percent rollout must be a whole number between 0 and 100'), + percentageDescription: __('Enter an integer number number between 0 and 100'), + percentageInvalid: __('Percent rollout must be an integer number between 0 and 100'), percentageLabel: __('Percentage'), stickinessDescription: __('Consistency guarantee method'), stickinessLabel: __('Based on'), 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..6a57e9a8759 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|No user list selected'), }, 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.translations.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/components/strategies/percent_rollout.vue b/app/assets/javascripts/feature_flags/components/strategies/percent_rollout.vue index d262769c891..91e1b85d66e 100644 --- a/app/assets/javascripts/feature_flags/components/strategies/percent_rollout.vue +++ b/app/assets/javascripts/feature_flags/components/strategies/percent_rollout.vue @@ -16,9 +16,9 @@ export default { }, }, i18n: { - rolloutPercentageDescription: __('Enter a whole number between 0 and 100'), + rolloutPercentageDescription: __('Enter an integer number between 0 and 100'), rolloutPercentageInvalid: s__( - 'FeatureFlags|Percent rollout must be a whole number between 0 and 100', + 'FeatureFlags|Percent rollout must be an integer number between 0 and 100', ), rolloutPercentageLabel: s__('FeatureFlag|Percentage'), }, 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), + }, }); |