diff options
Diffstat (limited to 'app/assets/javascripts/feature_flags/components')
8 files changed, 41 insertions, 544 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 77e40039b43..d86e13ce722 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 @@ -196,6 +196,7 @@ export default { /> <gl-loading-icon v-if="isRotating" + size="sm" class="gl-absolute gl-align-self-center gl-right-5 gl-mr-7" /> 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 e7f4b51c964..dde021b67be 100644 --- a/app/assets/javascripts/feature_flags/components/edit_feature_flag.vue +++ b/app/assets/javascripts/feature_flags/components/edit_feature_flag.vue @@ -1,10 +1,8 @@ <script> import { GlAlert, GlLoadingIcon, GlToggle } from '@gitlab/ui'; import { mapState, mapActions } from 'vuex'; -import axios from '~/lib/utils/axios_utils'; import { sprintf, s__ } from '~/locale'; import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; -import { LEGACY_FLAG } from '../constants'; import FeatureFlagForm from './form.vue'; export default { @@ -15,59 +13,29 @@ export default { FeatureFlagForm, }, mixins: [glFeatureFlagMixin()], - inject: { - showUserCallout: {}, - userCalloutId: { - default: '', - }, - userCalloutsPath: { - default: '', - }, - }, - data() { - return { - userShouldSeeNewFlagAlert: this.showUserCallout, - }; - }, - translations: { - legacyReadOnlyFlagAlert: s__( - 'FeatureFlags|GitLab is moving to a new way of managing feature flags. This feature flag is read-only, and it will be removed in 14.0. Please create a new feature flag.', - ), - }, computed: { ...mapState([ 'path', 'error', 'name', 'description', - 'scopes', 'strategies', 'isLoading', 'hasError', 'iid', 'active', - 'version', ]), title() { return this.iid ? `^${this.iid} ${this.name}` : sprintf(s__('Edit %{name}'), { name: this.name }); }, - deprecated() { - return this.version === LEGACY_FLAG; - }, }, created() { return this.fetchFeatureFlag(); }, methods: { ...mapActions(['updateFeatureFlag', 'fetchFeatureFlag', 'toggleActive']), - dismissNewVersionFlagAlert() { - this.userShouldSeeNewFlagAlert = false; - axios.post(this.userCalloutsPath, { - feature_name: this.userCalloutId, - }); - }, }, }; </script> @@ -76,9 +44,6 @@ export default { <gl-loading-icon v-if="isLoading" size="xl" class="gl-mt-7" /> <template v-else-if="!isLoading && !hasError"> - <gl-alert v-if="deprecated" variant="warning" :dismissible="false" class="gl-my-5">{{ - $options.translations.legacyReadOnlyFlagAlert - }}</gl-alert> <div class="gl-display-flex gl-align-items-center gl-mb-4 gl-mt-4"> <gl-toggle :value="active" @@ -100,12 +65,10 @@ export default { <feature-flag-form :name="name" :description="description" - :scopes="scopes" :strategies="strategies" :cancel-path="path" :submit-text="__('Save changes')" :active="active" - :version="version" @handleSubmit="(data) => updateFeatureFlag(data)" /> </template> diff --git a/app/assets/javascripts/feature_flags/components/feature_flags.vue b/app/assets/javascripts/feature_flags/components/feature_flags.vue index d08e8d2b3a1..53909dcf42e 100644 --- a/app/assets/javascripts/feature_flags/components/feature_flags.vue +++ b/app/assets/javascripts/feature_flags/components/feature_flags.vue @@ -3,11 +3,8 @@ import { GlAlert, GlBadge, GlButton, GlModalDirective, GlSprintf } from '@gitlab import { isEmpty } from 'lodash'; import { mapState, mapActions } from 'vuex'; -import { - buildUrlWithCurrentLocation, - getParameterByName, - historyPushState, -} from '~/lib/utils/common_utils'; +import { buildUrlWithCurrentLocation, historyPushState } from '~/lib/utils/common_utils'; +import { getParameterByName } from '~/lib/utils/url_utility'; import TablePagination from '~/vue_shared/components/pagination/table_pagination.vue'; import ConfigureFeatureFlagsModal from './configure_feature_flags_modal.vue'; import EmptyState from './empty_state.vue'; diff --git a/app/assets/javascripts/feature_flags/components/feature_flags_table.vue b/app/assets/javascripts/feature_flags/components/feature_flags_table.vue index 9220077af71..cfd838bf5a1 100644 --- a/app/assets/javascripts/feature_flags/components/feature_flags_table.vue +++ b/app/assets/javascripts/feature_flags/components/feature_flags_table.vue @@ -1,8 +1,7 @@ <script> -import { GlBadge, GlButton, GlTooltipDirective, GlModal, GlToggle, GlIcon } from '@gitlab/ui'; +import { GlBadge, GlButton, GlTooltipDirective, GlModal, GlToggle } from '@gitlab/ui'; import { __, s__, sprintf } from '~/locale'; import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; -import { ROLLOUT_STRATEGY_PERCENT_ROLLOUT, NEW_VERSION_FLAG, LEGACY_FLAG } from '../constants'; import { labelForStrategy } from '../utils'; export default { @@ -14,7 +13,6 @@ export default { components: { GlBadge, GlButton, - GlIcon, GlModal, GlToggle, }, @@ -35,13 +33,7 @@ export default { deleteFeatureFlagName: null, }; }, - translations: { - legacyFlagReadOnlyAlert: s__('FeatureFlags|Flag is read-only'), - }, computed: { - permissions() { - return this.glFeatures.featureFlagPermissions; - }, modalTitle() { return sprintf(s__('FeatureFlags|Delete %{name}?'), { name: this.deleteFeatureFlagName, @@ -57,12 +49,6 @@ export default { }, }, methods: { - isLegacyFlag(flag) { - return flag.version !== NEW_VERSION_FLAG; - }, - statusToggleDisabled(flag) { - return flag.version === LEGACY_FLAG; - }, scopeTooltipText(scope) { return !scope.active ? sprintf(s__('FeatureFlags|Inactive flag for %{scope}'), { @@ -70,22 +56,6 @@ export default { }) : ''; }, - badgeText(scope) { - const displayName = - scope.environmentScope === '*' - ? s__('FeatureFlags|* (All environments)') - : scope.environmentScope; - - const displayPercentage = - scope.rolloutStrategy === ROLLOUT_STRATEGY_PERCENT_ROLLOUT - ? `: ${scope.rolloutPercentage}%` - : ''; - - return `${displayName}${displayPercentage}`; - }, - badgeVariant(scope) { - return scope.active ? 'info' : 'muted'; - }, strategyBadgeText(strategy) { return labelForStrategy(strategy); }, @@ -142,7 +112,6 @@ export default { <gl-toggle v-if="featureFlag.update_path" :value="featureFlag.active" - :disabled="statusToggleDisabled(featureFlag)" :label="$options.i18n.toggleLabel" label-position="hidden" data-testid="feature-flag-status-toggle" @@ -169,12 +138,6 @@ export default { <div class="feature-flag-name text-monospace text-truncate"> {{ featureFlag.name }} </div> - <gl-icon - v-if="isLegacyFlag(featureFlag)" - v-gl-tooltip.hover="$options.translations.legacyFlagReadOnlyAlert" - class="gl-ml-3" - name="information-o" - /> </div> <div class="feature-flag-description text-secondary text-truncate"> {{ featureFlag.description }} @@ -189,27 +152,14 @@ export default { <div class="table-mobile-content d-flex flex-wrap justify-content-end justify-content-md-start js-feature-flag-environments" > - <template v-if="isLegacyFlag(featureFlag)"> - <gl-badge - v-for="scope in featureFlag.scopes" - :key="scope.id" - v-gl-tooltip.hover="scopeTooltipText(scope)" - :variant="badgeVariant(scope)" - :data-qa-selector="`feature-flag-scope-${badgeVariant(scope)}-badge`" - class="gl-mr-3 gl-mt-2" - >{{ badgeText(scope) }}</gl-badge - > - </template> - <template v-else> - <gl-badge - v-for="strategy in featureFlag.strategies" - :key="strategy.id" - data-testid="strategy-badge" - variant="info" - class="gl-mr-3 gl-mt-2 gl-white-space-normal gl-text-left gl-px-5" - >{{ strategyBadgeText(strategy) }}</gl-badge - > - </template> + <gl-badge + v-for="strategy in featureFlag.strategies" + :key="strategy.id" + data-testid="strategy-badge" + variant="info" + class="gl-mr-3 gl-mt-2 gl-white-space-normal gl-text-left gl-px-5" + >{{ strategyBadgeText(strategy) }}</gl-badge + > </div> </div> diff --git a/app/assets/javascripts/feature_flags/components/form.vue b/app/assets/javascripts/feature_flags/components/form.vue index 67ddceaf080..f7ad2c1f106 100644 --- a/app/assets/javascripts/feature_flags/components/form.vue +++ b/app/assets/javascripts/feature_flags/components/form.vue @@ -1,16 +1,6 @@ <script> -import { - GlButton, - GlBadge, - GlTooltip, - GlTooltipDirective, - GlFormTextarea, - GlFormCheckbox, - GlSprintf, - GlIcon, - GlToggle, -} from '@gitlab/ui'; -import { memoize, isString, cloneDeep, isNumber, uniqueId } from 'lodash'; +import { GlButton } from '@gitlab/ui'; +import { memoize, cloneDeep, isNumber, uniqueId } from 'lodash'; import Vue from 'vue'; import { s__ } from '~/locale'; import RelatedIssuesRoot from '~/related_issues/components/related_issues_root.vue'; @@ -20,12 +10,8 @@ import { ROLLOUT_STRATEGY_PERCENT_ROLLOUT, ROLLOUT_STRATEGY_USER_ID, ALL_ENVIRONMENTS_NAME, - INTERNAL_ID_PREFIX, NEW_VERSION_FLAG, - LEGACY_FLAG, } from '../constants'; -import { createNewEnvironmentScope } from '../store/helpers'; -import EnvironmentsDropdown from './environments_dropdown.vue'; import Strategy from './strategy.vue'; export default { @@ -35,20 +21,9 @@ export default { }, components: { GlButton, - GlBadge, - GlFormTextarea, - GlFormCheckbox, - GlTooltip, - GlSprintf, - GlIcon, - GlToggle, - EnvironmentsDropdown, Strategy, RelatedIssuesRoot, }, - directives: { - GlTooltip: GlTooltipDirective, - }, mixins: [featureFlagsMixin()], inject: { featureFlagIssuesEndpoint: { @@ -71,11 +46,6 @@ export default { required: false, default: '', }, - scopes: { - type: Array, - required: false, - default: () => [], - }, cancelPath: { type: String, required: true, @@ -89,11 +59,6 @@ export default { required: false, default: () => [], }, - version: { - type: String, - required: false, - default: LEGACY_FLAG, - }, }, translations: { allEnvironmentsText: s__('FeatureFlags|* (All Environments)'), @@ -120,35 +85,18 @@ export default { formName: this.name, formDescription: this.description, - // operate on a clone to avoid mutating props - formScopes: this.scopes.map((s) => ({ ...s })), formStrategies: cloneDeep(this.strategies), newScope: '', }; }, computed: { - filteredScopes() { - return this.formScopes.filter((scope) => !scope.shouldBeDestroyed); - }, filteredStrategies() { return this.formStrategies.filter((s) => !s.shouldBeDestroyed); }, - canUpdateFlag() { - return !this.permissionsFlag || (this.formScopes || []).every((scope) => scope.canUpdate); - }, - permissionsFlag() { - return this.glFeatures.featureFlagPermissions; - }, - supportsStrategies() { - return this.version === NEW_VERSION_FLAG; - }, showRelatedIssues() { return this.featureFlagIssuesEndpoint.length > 0; }, - readOnly() { - return this.version === LEGACY_FLAG; - }, }, methods: { keyFor(strategy) { @@ -174,37 +122,6 @@ export default { isAllEnvironment(name) { return name === ALL_ENVIRONMENTS_NAME; }, - - /** - * When the user clicks the remove button we delete the scope - * - * If the scope has an ID, we need to add the `shouldBeDestroyed` flag. - * If the scope does *not* have an ID, we can just remove it. - * - * This flag will be used when submitting the data to the backend - * to determine which records to delete (via a "_destroy" property). - * - * @param {Object} scope - */ - removeScope(scope) { - if (isString(scope.id) && scope.id.startsWith(INTERNAL_ID_PREFIX)) { - this.formScopes = this.formScopes.filter((s) => s !== scope); - } else { - Vue.set(scope, 'shouldBeDestroyed', true); - } - }, - - /** - * Creates a new scope and adds it to the list of scopes - * - * @param overrides An object whose properties will - * be used override the default scope options - */ - createNewScope(overrides) { - this.formScopes.push(createNewEnvironmentScope(overrides, this.permissionsFlag)); - this.newScope = ''; - }, - /** * When the user clicks the submit button * it triggers an event with the form data @@ -214,61 +131,16 @@ export default { name: this.formName, description: this.formDescription, active: this.active, - version: this.version, + version: NEW_VERSION_FLAG, + strategies: this.formStrategies, }; - if (this.version === LEGACY_FLAG) { - flag.scopes = this.formScopes; - } else { - flag.strategies = this.formStrategies; - } - this.$emit('handleSubmit', flag); }, - canUpdateScope(scope) { - return !this.permissionsFlag || scope.canUpdate; - }, - isRolloutPercentageInvalid: memoize(function isRolloutPercentageInvalid(percentage) { return !this.$options.rolloutPercentageRegex.test(percentage); }), - - /** - * Generates a unique ID for the strategy based on the v-for index - * - * @param index The index of the strategy - */ - rolloutStrategyId(index) { - return `rollout-strategy-${index}`; - }, - - /** - * Generates a unique ID for the percentage based on the v-for index - * - * @param index The index of the percentage - */ - rolloutPercentageId(index) { - return `rollout-percentage-${index}`; - }, - rolloutUserId(index) { - return `rollout-user-id-${index}`; - }, - - shouldDisplayIncludeUserIds(scope) { - return ![ROLLOUT_STRATEGY_ALL_USERS, ROLLOUT_STRATEGY_USER_ID].includes( - scope.rolloutStrategy, - ); - }, - shouldDisplayUserIds(scope) { - return scope.rolloutStrategy === ROLLOUT_STRATEGY_USER_ID || scope.shouldIncludeUserIds; - }, - onStrategyChange(index) { - const scope = this.filteredScopes[index]; - scope.shouldIncludeUserIds = - scope.rolloutUserIds.length > 0 && - scope.rolloutStrategy === ROLLOUT_STRATEGY_PERCENT_ROLLOUT; - }, onFormStrategyChange(strategy, index) { Object.assign(this.filteredStrategies[index], strategy); }, @@ -281,12 +153,7 @@ export default { <div class="row"> <div class="form-group col-md-4"> <label for="feature-flag-name" class="label-bold">{{ s__('FeatureFlags|Name') }} *</label> - <input - id="feature-flag-name" - v-model="formName" - :disabled="!canUpdateFlag" - class="form-control" - /> + <input id="feature-flag-name" v-model="formName" class="form-control" /> </div> </div> @@ -298,7 +165,6 @@ export default { <textarea id="feature-flag-description" v-model="formDescription" - :disabled="!canUpdateFlag" class="form-control" rows="4" ></textarea> @@ -312,277 +178,35 @@ export default { :show-categorized-issues="false" /> - <template v-if="supportsStrategies"> - <div class="row"> - <div class="col-md-12"> - <h4>{{ s__('FeatureFlags|Strategies') }}</h4> - <div class="flex align-items-baseline justify-content-between"> - <p class="mr-3">{{ $options.translations.newHelpText }}</p> - <gl-button variant="confirm" category="secondary" @click="addStrategy"> - {{ s__('FeatureFlags|Add strategy') }} - </gl-button> - </div> - </div> - </div> - <div v-if="filteredStrategies.length > 0" data-testid="feature-flag-strategies"> - <strategy - v-for="(strategy, index) in filteredStrategies" - :key="keyFor(strategy)" - :strategy="strategy" - :index="index" - @change="onFormStrategyChange($event, index)" - @delete="deleteStrategy(strategy)" - /> - </div> - <div v-else class="flex justify-content-center border-top py-4 w-100"> - <span>{{ $options.translations.noStrategiesText }}</span> - </div> - </template> - - <div v-else class="row"> - <div class="form-group col-md-12"> - <h4>{{ s__('FeatureFlags|Target environments') }}</h4> - <gl-sprintf :message="$options.translations.helpText"> - <template #code="{ content }"> - <code>{{ content }}</code> - </template> - <template #bold="{ content }"> - <b>{{ content }}</b> - </template> - </gl-sprintf> - - <div class="js-scopes-table gl-mt-3"> - <div class="gl-responsive-table-row table-row-header" role="row"> - <div class="table-section section-30" role="columnheader"> - {{ s__('FeatureFlags|Environment Spec') }} - </div> - <div class="table-section section-20 text-center" role="columnheader"> - {{ s__('FeatureFlags|Status') }} - </div> - <div class="table-section section-40" role="columnheader"> - {{ s__('FeatureFlags|Rollout Strategy') }} - </div> - </div> - - <div - v-for="(scope, index) in filteredScopes" - :key="scope.id" - ref="scopeRow" - class="gl-responsive-table-row" - role="row" - > - <div class="table-section section-30" role="gridcell"> - <div class="table-mobile-header" role="rowheader"> - {{ s__('FeatureFlags|Environment Spec') }} - </div> - <div - class="table-mobile-content gl-display-flex gl-align-items-center gl-justify-content-start" - > - <p v-if="isAllEnvironment(scope.environmentScope)" class="js-scope-all pl-3"> - {{ $options.translations.allEnvironmentsText }} - </p> - - <environments-dropdown - v-else - class="col-12" - :value="scope.environmentScope" - :disabled="!canUpdateScope(scope) || scope.environmentScope !== ''" - @selectEnvironment="(env) => (scope.environmentScope = env)" - @createClicked="(env) => (scope.environmentScope = env)" - @clearInput="(env) => (scope.environmentScope = '')" - /> - - <gl-badge v-if="permissionsFlag && scope.protected" variant="success"> - {{ s__('FeatureFlags|Protected') }} - </gl-badge> - </div> - </div> - - <div class="table-section section-20 text-center" role="gridcell"> - <div class="table-mobile-header" role="rowheader"> - {{ $options.i18n.statusLabel }} - </div> - <div class="table-mobile-content gl-display-flex gl-justify-content-center"> - <gl-toggle - :value="scope.active" - :disabled="!active || !canUpdateScope(scope)" - :label="$options.i18n.statusLabel" - label-position="hidden" - @change="(status) => (scope.active = status)" - /> - </div> - </div> - - <div class="table-section section-40" role="gridcell"> - <div class="table-mobile-header" role="rowheader"> - {{ s__('FeatureFlags|Rollout Strategy') }} - </div> - <div class="table-mobile-content js-rollout-strategy form-inline"> - <label class="sr-only" :for="rolloutStrategyId(index)"> - {{ s__('FeatureFlags|Rollout Strategy') }} - </label> - <div class="select-wrapper col-12 col-md-8 p-0"> - <select - :id="rolloutStrategyId(index)" - v-model="scope.rolloutStrategy" - :disabled="!scope.active" - class="form-control select-control w-100 js-rollout-strategy" - @change="onStrategyChange(index)" - > - <option :value="$options.ROLLOUT_STRATEGY_ALL_USERS"> - {{ s__('FeatureFlags|All users') }} - </option> - <option :value="$options.ROLLOUT_STRATEGY_PERCENT_ROLLOUT"> - {{ s__('FeatureFlags|Percent rollout (logged in users)') }} - </option> - <option :value="$options.ROLLOUT_STRATEGY_USER_ID"> - {{ s__('FeatureFlags|User IDs') }} - </option> - </select> - <gl-icon - name="chevron-down" - class="gl-absolute gl-top-3 gl-right-3 gl-text-gray-500" - :size="16" - /> - </div> - - <div - v-if="scope.rolloutStrategy === $options.ROLLOUT_STRATEGY_PERCENT_ROLLOUT" - class="d-flex-center mt-2 mt-md-0 ml-md-2" - > - <label class="sr-only" :for="rolloutPercentageId(index)"> - {{ s__('FeatureFlags|Rollout Percentage') }} - </label> - <div class="gl-w-9"> - <input - :id="rolloutPercentageId(index)" - v-model="scope.rolloutPercentage" - :disabled="!scope.active" - :class="{ - 'is-invalid': isRolloutPercentageInvalid(scope.rolloutPercentage), - }" - type="number" - min="0" - max="100" - :pattern="$options.rolloutPercentageRegex.source" - class="rollout-percentage js-rollout-percentage form-control text-right w-100" - /> - </div> - <gl-tooltip - v-if="isRolloutPercentageInvalid(scope.rolloutPercentage)" - :target="rolloutPercentageId(index)" - > - {{ - s__( - 'FeatureFlags|Percent rollout must be an integer number between 0 and 100', - ) - }} - </gl-tooltip> - <span class="ml-1">%</span> - </div> - <div class="d-flex flex-column align-items-start mt-2 w-100"> - <gl-form-checkbox - v-if="shouldDisplayIncludeUserIds(scope)" - v-model="scope.shouldIncludeUserIds" - >{{ s__('FeatureFlags|Include additional user IDs') }}</gl-form-checkbox - > - <template v-if="shouldDisplayUserIds(scope)"> - <label :for="rolloutUserId(index)" class="mb-2"> - {{ s__('FeatureFlags|User IDs') }} - </label> - <gl-form-textarea - :id="rolloutUserId(index)" - v-model="scope.rolloutUserIds" - class="w-100" - /> - </template> - </div> - </div> - </div> - - <div class="table-section section-10 text-right" role="gridcell"> - <div class="table-mobile-header" role="rowheader"> - {{ s__('FeatureFlags|Remove') }} - </div> - <div class="table-mobile-content"> - <gl-button - v-if="!isAllEnvironment(scope.environmentScope) && canUpdateScope(scope)" - v-gl-tooltip - :title="$options.i18n.removeLabel" - :aria-label="$options.i18n.removeLabel" - class="js-delete-scope btn-transparent pr-3 pl-3" - icon="clear" - data-testid="feature-flag-delete" - @click="removeScope(scope)" - /> - </div> - </div> - </div> - - <div class="gl-responsive-table-row" role="row" data-testid="add-new-scope"> - <div class="table-section section-30" role="gridcell"> - <div class="table-mobile-header" role="rowheader"> - {{ s__('FeatureFlags|Environment Spec') }} - </div> - <div class="table-mobile-content"> - <environments-dropdown - class="js-new-scope-name col-12" - :value="newScope" - @selectEnvironment="(env) => createNewScope({ environmentScope: env })" - @createClicked="(env) => createNewScope({ environmentScope: env })" - /> - </div> - </div> - - <div class="table-section section-20 text-center" role="gridcell"> - <div class="table-mobile-header" role="rowheader"> - {{ $options.i18n.statusLabel }} - </div> - <div class="table-mobile-content gl-display-flex gl-justify-content-center"> - <gl-toggle - :disabled="!active" - :label="$options.i18n.statusLabel" - label-position="hidden" - :value="false" - @change="createNewScope({ active: true })" - /> - </div> - </div> - - <div class="table-section section-40" role="gridcell"> - <div class="table-mobile-header" role="rowheader"> - {{ s__('FeatureFlags|Rollout Strategy') }} - </div> - <div class="table-mobile-content js-rollout-strategy form-inline"> - <label class="sr-only" for="new-rollout-strategy-placeholder">{{ - s__('FeatureFlags|Rollout Strategy') - }}</label> - <div class="select-wrapper col-12 col-md-8 p-0"> - <select - id="new-rollout-strategy-placeholder" - disabled - class="form-control select-control w-100" - > - <option>{{ s__('FeatureFlags|All users') }}</option> - </select> - <gl-icon - name="chevron-down" - class="gl-absolute gl-top-3 gl-right-3 gl-text-gray-500" - :size="16" - /> - </div> - </div> - </div> - </div> + <div class="row"> + <div class="col-md-12"> + <h4>{{ s__('FeatureFlags|Strategies') }}</h4> + <div class="flex align-items-baseline justify-content-between"> + <p class="mr-3">{{ $options.translations.newHelpText }}</p> + <gl-button variant="confirm" category="secondary" @click="addStrategy"> + {{ s__('FeatureFlags|Add strategy') }} + </gl-button> </div> </div> </div> + <div v-if="filteredStrategies.length > 0" data-testid="feature-flag-strategies"> + <strategy + v-for="(strategy, index) in filteredStrategies" + :key="keyFor(strategy)" + :strategy="strategy" + :index="index" + @change="onFormStrategyChange($event, index)" + @delete="deleteStrategy(strategy)" + /> + </div> + <div v-else class="flex justify-content-center border-top py-4 w-100"> + <span>{{ $options.translations.noStrategiesText }}</span> + </div> </fieldset> <div class="form-actions"> <gl-button ref="submitButton" - :disabled="readOnly" type="button" variant="confirm" class="js-ff-submit col-xs-12" diff --git a/app/assets/javascripts/feature_flags/components/new_environments_dropdown.vue b/app/assets/javascripts/feature_flags/components/new_environments_dropdown.vue index c59e3178b09..5575c6567b5 100644 --- a/app/assets/javascripts/feature_flags/components/new_environments_dropdown.vue +++ b/app/assets/javascripts/feature_flags/components/new_environments_dropdown.vue @@ -80,7 +80,7 @@ export default { @focus="fetchEnvironments" @keyup="fetchEnvironments" /> - <gl-loading-icon v-if="isLoading" /> + <gl-loading-icon v-if="isLoading" size="sm" /> <gl-dropdown-item v-for="environment in results" v-else-if="results.length" diff --git a/app/assets/javascripts/feature_flags/components/new_feature_flag.vue b/app/assets/javascripts/feature_flags/components/new_feature_flag.vue index 19be57f9d27..865c1e677cd 100644 --- a/app/assets/javascripts/feature_flags/components/new_feature_flag.vue +++ b/app/assets/javascripts/feature_flags/components/new_feature_flag.vue @@ -1,10 +1,8 @@ <script> import { GlAlert } from '@gitlab/ui'; import { mapState, mapActions } from 'vuex'; -import axios from '~/lib/utils/axios_utils'; import featureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; -import { NEW_VERSION_FLAG, ROLLOUT_STRATEGY_ALL_USERS } from '../constants'; -import { createNewEnvironmentScope } from '../store/helpers'; +import { ROLLOUT_STRATEGY_ALL_USERS } from '../constants'; import FeatureFlagForm from './form.vue'; export default { @@ -13,48 +11,14 @@ export default { GlAlert, }, mixins: [featureFlagsMixin()], - inject: { - showUserCallout: {}, - userCalloutId: { - default: '', - }, - userCalloutsPath: { - default: '', - }, - }, - data() { - return { - userShouldSeeNewFlagAlert: this.showUserCallout, - }; - }, computed: { ...mapState(['error', 'path']), - scopes() { - return [ - createNewEnvironmentScope( - { - environmentScope: '*', - active: true, - }, - this.glFeatures.featureFlagsPermissions, - ), - ]; - }, - version() { - return NEW_VERSION_FLAG; - }, strategies() { return [{ name: ROLLOUT_STRATEGY_ALL_USERS, parameters: {}, scopes: [] }]; }, }, methods: { ...mapActions(['createFeatureFlag']), - dismissNewVersionFlagAlert() { - this.userShouldSeeNewFlagAlert = false; - axios.post(this.userCalloutsPath, { - feature_name: this.userCalloutId, - }); - }, }, }; </script> @@ -69,9 +33,7 @@ export default { <feature-flag-form :cancel-path="path" :submit-text="s__('FeatureFlags|Create feature flag')" - :scopes="scopes" :strategies="strategies" - :version="version" @handleSubmit="(data) => createFeatureFlag(data)" /> </div> 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 45fc37da747..9dbffe75f6b 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 @@ -76,7 +76,7 @@ export default { @focus="fetchUserLists" @keyup="fetchUserLists" /> - <gl-loading-icon v-if="isLoading" /> + <gl-loading-icon v-if="isLoading" size="sm" /> <gl-dropdown-item v-for="list in userLists" :key="list.id" |