Skip to content
Snippets Groups Projects
Verified Commit b63717d2 authored by Brandon Labuschagne's avatar Brandon Labuschagne :red_circle: Committed by GitLab
Browse files

Merge branch 'branch-rule-details' into 'master'

parents 7b8fd0f3 6b3507fc
No related branches found
No related tags found
2 merge requests!162233Draft: Script to update Topology Service Gem,!161965Migrate branch rule details cards to crud components
Pipeline #1406280119 passed
<script>
// eslint-disable-next-line no-restricted-imports
import { mapActions } from 'vuex';
import {
GlSprintf,
GlLink,
GlLoadingIcon,
GlCard,
GlButton,
GlModal,
GlModalDirective,
} from '@gitlab/ui';
import { GlSprintf, GlLink, GlLoadingIcon, GlButton, GlModal, GlModalDirective } from '@gitlab/ui';
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import { sprintf, n__, s__ } from '~/locale';
import PageHeading from '~/vue_shared/components/page_heading.vue';
import CrudComponent from '~/vue_shared/components/crud_component.vue';
import {
getParameterByName,
mergeUrlParams,
......@@ -58,11 +52,12 @@ export default {
GlSprintf,
GlLink,
GlLoadingIcon,
GlCard,
GlModal,
GlButton,
BranchRuleModal,
RuleDrawer,
PageHeading,
CrudComponent,
},
mixins: [glFeatureFlagsMixin()],
inject: {
......@@ -343,29 +338,28 @@ export default {
<template>
<div>
<div class="gl-flex gl-items-center gl-justify-between">
<h1 class="h3 gl-mb-5">{{ $options.i18n.pageTitle }}</h1>
<gl-button
v-if="glFeatures.editBranchRules && branchRule"
v-gl-modal="$options.deleteModalId"
data-testid="delete-rule-button"
category="secondary"
variant="danger"
:disabled="$apollo.loading"
>{{ $options.i18n.deleteRule }}
</gl-button>
</div>
<page-heading :heading="$options.i18n.pageTitle">
<template #actions>
<gl-button
v-if="glFeatures.editBranchRules && branchRule"
v-gl-modal="$options.deleteModalId"
data-testid="delete-rule-button"
category="secondary"
variant="danger"
:disabled="$apollo.loading"
>{{ $options.i18n.deleteRule }}
</gl-button>
</template>
</page-heading>
<gl-loading-icon v-if="$apollo.loading" size="lg" />
<div v-else-if="!branchRule && !isPredefinedRule">{{ $options.i18n.noData }}</div>
<div v-else>
<gl-card
class="gl-new-card"
header-class="gl-new-card-header"
body-class="gl-new-card-body gl-p-5"
<crud-component
:title="$options.i18n.ruleTarget"
class="gl-mt-3"
data-testid="rule-target-card"
>
<template #header>
<strong>{{ $options.i18n.ruleTarget }}</strong>
<template #actions>
<gl-button
v-if="glFeatures.editBranchRules && !isPredefinedRule"
v-gl-modal="$options.editModalId"
......@@ -383,10 +377,10 @@ export default {
<p v-if="matchingBranchesCount" class="gl-mb-0 gl-mt-3">
<gl-link :href="matchingBranchesLinkHref">{{ matchingBranchesLinkTitle }}</gl-link>
</p>
</gl-card>
</crud-component>
<section v-if="!isPredefinedRule">
<h2 class="h4 gl-mb-1 gl-mt-5">{{ $options.i18n.protectBranchTitle }}</h2>
<h2 class="h4 gl-mb-1 gl-mt-6">{{ $options.i18n.protectBranchTitle }}</h2>
<gl-sprintf :message="$options.i18n.protectBranchDescription">
<template #link="{ content }">
<gl-link :href="$options.protectedBranchesHelpDocLink">
......@@ -440,6 +434,7 @@ export default {
<!-- Force push -->
<protection-toggle
v-if="hasPushAccessLevelSet"
class="gl-mt-6"
data-testid="force-push-content"
data-test-id-prefix="force-push"
:is-protected="branchProtection.allowForcePush"
......@@ -469,7 +464,7 @@ export default {
<!-- Approvals -->
<template v-if="showApprovers">
<h2 class="h4 gl-mb-1 gl-mt-5">{{ $options.i18n.approvalsTitle }}</h2>
<h2 class="h4 gl-mb-1 gl-mt-6">{{ $options.i18n.approvalsTitle }}</h2>
<gl-sprintf :message="$options.i18n.approvalsDescription">
<template #link="{ content }">
<gl-link :href="$options.approvalsHelpDocLink">
......@@ -493,7 +488,7 @@ export default {
<!-- Status checks -->
<template v-if="showStatusChecks">
<h2 class="h4 gl-mb-1 gl-mt-5">{{ $options.i18n.statusChecksTitle }}</h2>
<h2 class="h4 gl-mb-1 gl-mt-6">{{ $options.i18n.statusChecksTitle }}</h2>
<gl-sprintf :message="$options.i18n.statusChecksDescription">
<template #link="{ content }">
<gl-link :href="$options.statusChecksHelpDocLink">
......
<script>
import { GlCard, GlLink, GlButton } from '@gitlab/ui';
import { GlLink, GlButton } from '@gitlab/ui';
import { s__ } from '~/locale';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import CrudComponent from '~/vue_shared/components/crud_component.vue';
import ProtectionRow from './protection_row.vue';
export const i18n = {
......@@ -13,7 +14,7 @@ export const i18n = {
export default {
name: 'ProtectionDetail',
i18n,
components: { GlCard, GlLink, GlButton, ProtectionRow },
components: { GlLink, GlButton, ProtectionRow, CrudComponent },
mixins: [glFeatureFlagsMixin()],
props: {
header: {
......@@ -90,29 +91,25 @@ export default {
</script>
<template>
<gl-card
class="gl-new-card gl-mb-5"
header-class="gl-new-card-header gl-flex-wrap"
body-class="gl-new-card-body gl-px-5 gl-pt-4"
>
<template #header>
<strong>{{ header }}</strong>
<crud-component :title="header" class="gl-mt-3">
<template #description>
<p v-if="showHelpText" class="gl-mb-0 gl-basis-full gl-text-subtle">
{{ helpText }}
</p>
</template>
<template #actions>
<gl-button
v-if="glFeatures.editBranchRules && isEditAvailable"
size="small"
data-testid="edit-rule-button"
@click="$emit('edit')"
>{{ __('Edit') }}</gl-button
>
>{{ __('Edit') }}
</gl-button>
<gl-link v-else :href="headerLinkHref">{{ headerLinkTitle }}</gl-link>
<p v-if="showHelpText" class="gl-mb-0 gl-basis-full gl-text-secondary">
{{ helpText }}
</p></template
>
<p v-if="showEmptyState" class="gl-text-secondary" data-testid="protection-empty-state">
</template>
<span v-if="showEmptyState" class="gl-text-subtle" data-testid="protection-empty-state">
{{ emptyStateCopy }}
</p>
</span>
<!-- Roles -->
<protection-row v-if="roles.length" :title="$options.i18n.rolesTitle" :access-levels="roles" />
......@@ -135,5 +132,5 @@ export default {
:status-check-url="statusCheck.externalUrl"
:hmac="statusCheck.hmac"
/>
</gl-card>
</crud-component>
</template>
......@@ -148,18 +148,20 @@ export default {
</template>
<template #header>
<gl-button
variant="confirm"
:disabled="!isRuleUpdated"
:loading="isLoading"
data-testid="save-allowed-to-merge"
@click="editRule()"
>
{{ __('Save changes') }}
</gl-button>
<gl-button variant="confirm" category="secondary" @click="$emit('close')">
{{ __('Cancel') }}
</gl-button>
<div class="gl-flex gl-gap-3">
<gl-button
variant="confirm"
:disabled="!isRuleUpdated"
:loading="isLoading"
data-testid="save-allowed-to-merge"
@click="editRule()"
>
{{ __('Save changes') }}
</gl-button>
<gl-button variant="confirm" category="secondary" @click="$emit('close')">
{{ __('Cancel') }}
</gl-button>
</div>
</template>
<template #default>
<gl-form-group class="gl-border-none">
......
......@@ -136,7 +136,7 @@ export default {
<code>{{ searchQuery }}</code>
</template>
</gl-collapsible-listbox>
<span data-testid="help-text" class="gl-text-secondary">
<div data-testid="help-text" class="gl-text-subtle gl-mt-2">
<gl-sprintf :message="formDescriptionText">
<template #link="{ content }">
<gl-link :href="$options.wildcardsHelpDocLink">
......@@ -144,7 +144,7 @@ export default {
</gl-link>
</template>
</gl-sprintf>
</span>
</div>
</gl-form-group>
</gl-modal>
</template>
<script>
import { GlCard, GlIcon, GlCollapsibleListbox, GlSearchBoxByType } from '@gitlab/ui';
import { GlCollapsibleListbox, GlSearchBoxByType } from '@gitlab/ui';
import { parseBoolean, convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
import CrudComponent from '~/vue_shared/components/crud_component.vue';
import { createAlert } from '~/alert';
import { __, sprintf } from '~/locale';
import {
......@@ -21,10 +22,9 @@ export default {
name: 'ListSelector',
i18n: I18N,
components: {
GlCard,
GlIcon,
GlSearchBoxByType,
GlCollapsibleListbox,
CrudComponent,
},
props: {
type: {
......@@ -174,16 +174,7 @@ export default {
</script>
<template>
<gl-card header-class="gl-new-card-header gl-border-none" body-class="gl-card-footer">
<template #header
><strong data-testid="list-selector-title"
>{{ config.title }}
<span class="gl-ml-3 gl-text-gray-700"
><gl-icon :name="config.icon" /> {{ selectedItems.length }}</span
></strong
></template
>
<crud-component :title="config.title" :count="selectedItems.length" :icon="config.icon">
<div class="gl-flex gl-gap-3" :class="{ 'gl-mb-4': selectedItems.length }">
<gl-collapsible-listbox
ref="results"
......@@ -232,6 +223,6 @@ export default {
/>
</div>
<div v-else class="gl-mt-5 gl-text-secondary">{{ emptyPlaceholder }}</div>
</gl-card>
<div v-else class="gl-mt-5 gl-text-subtle">{{ emptyPlaceholder }}</div>
</crud-component>
</template>
......@@ -16,6 +16,8 @@ import Protection from '~/projects/settings/branch_rules/components/view/protect
import ProtectionToggle from '~/projects/settings/branch_rules/components/view/protection_toggle.vue';
import BranchRuleModal from '~/projects/settings/components/branch_rule_modal.vue';
import getProtectableBranches from '~/projects/settings/graphql/queries/protectable_branches.query.graphql';
import PageHeading from '~/vue_shared/components/page_heading.vue';
import CrudComponent from '~/vue_shared/components/crud_component.vue';
import {
I18N,
......@@ -101,6 +103,7 @@ describe('View branch rules', () => {
RuleDrawer,
GlCard: stubComponent(GlCard, { template: RENDER_ALL_SLOTS_TEMPLATE }),
GlModal: stubComponent(GlModal, { template: RENDER_ALL_SLOTS_TEMPLATE }),
CrudComponent,
},
mocks: {
$toast: {
......@@ -127,7 +130,6 @@ describe('View branch rules', () => {
const findAllowedToPush = () => wrapper.findByTestId('allowed-to-push-content');
const findAllowForcePushToggle = () => wrapper.findByTestId('force-push-content');
const findApprovalsTitle = () => wrapper.findByText(I18N.approvalsTitle);
const findpageTitle = () => wrapper.findByText(I18N.pageTitle);
const findStatusChecksTitle = () => wrapper.findByText(I18N.statusChecksTitle);
const findDeleteRuleButton = () => wrapper.findByTestId('delete-rule-button');
const findEditRuleNameButton = () => wrapper.findByTestId('edit-rule-name-button');
......@@ -146,7 +148,9 @@ describe('View branch rules', () => {
);
it('renders page title', () => {
expect(findpageTitle().exists()).toBe(true);
const pageTitle = wrapper.findComponent(PageHeading).props('heading');
expect(pageTitle).toBe('Branch rule details');
});
it('gets the branch param from url and renders it in the view', () => {
......
import { GlCard, GlLink } from '@gitlab/ui';
import { GlLink } from '@gitlab/ui';
import CrudComponent from '~/vue_shared/components/crud_component.vue';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import Protection, { i18n } from '~/projects/settings/branch_rules/components/view/protection.vue';
import ProtectionRow from '~/projects/settings/branch_rules/components/view/protection_row.vue';
......@@ -16,22 +17,22 @@ describe('Branch rule protection', () => {
emptyStateCopy: 'Nothing to show',
...props,
},
stubs: { GlCard },
stubs: { CrudComponent },
provide: { glFeatures },
});
};
beforeEach(() => createComponent());
const findCard = () => wrapper.findComponent(GlCard);
const findCrudComponent = () => wrapper.findComponent(CrudComponent);
const findHeader = () => wrapper.findByText(protectionPropsMock.header);
const findLink = () => wrapper.findComponent(GlLink);
const findProtectionRows = () => wrapper.findAllComponents(ProtectionRow);
const findEmptyState = () => wrapper.findByTestId('protection-empty-state');
const findEditButton = () => wrapper.findByTestId('edit-rule-button');
it('renders a card component', () => {
expect(findCard().exists()).toBe(true);
it('renders a crud component', () => {
expect(findCrudComponent().exists()).toBe(true);
});
it('renders a header', () => {
......@@ -47,7 +48,7 @@ describe('Branch rule protection', () => {
it('renders a help text when provided', () => {
createComponent({ editBranchRules: true }, { helpText: 'Help text' });
expect(findCard().text()).toContain('Help text');
expect(findCrudComponent().text()).toContain('Help text');
});
it('renders a protection row for roles', () => {
......
import Vue from 'vue';
import VueApollo from 'vue-apollo';
import { GlCard, GlIcon, GlCollapsibleListbox, GlSearchBoxByType } from '@gitlab/ui';
import { GlIcon, GlCollapsibleListbox, GlSearchBoxByType } from '@gitlab/ui';
import CrudComponent from '~/vue_shared/components/crud_component.vue';
import Api from '~/api';
import RestApi from '~/rest_api';
import { createAlert } from '~/alert';
......@@ -63,13 +64,14 @@ describe('List Selector spec', () => {
propsData: {
...props,
},
stubs: { CrudComponent },
});
await waitForPromises();
};
const findCard = () => wrapper.findComponent(GlCard);
const findTitle = () => findCard().find('[data-testid="list-selector-title"]');
const findCrudComponent = () => wrapper.findComponent(CrudComponent);
const findTitle = () => findCrudComponent().props('title');
const findIcon = () => wrapper.findComponent(GlIcon);
const findAllListBoxComponents = () => wrapper.findAllComponents(GlCollapsibleListbox);
const findSearchResultsDropdown = () => findAllListBoxComponents().at(0);
......@@ -96,13 +98,12 @@ describe('List Selector spec', () => {
describe('Users type', () => {
beforeEach(() => createComponent(USERS_MOCK_PROPS));
it('renders a Card component', () => {
expect(findCard().exists()).toBe(true);
it('renders a crud component', () => {
expect(findCrudComponent().exists()).toBe(true);
});
it('renders a correct title', () => {
expect(findTitle().exists()).toBe(true);
expect(findTitle().text()).toContain('Users');
expect(findTitle()).toContain('Users');
});
it('renders the correct icon', () => {
......@@ -132,8 +133,8 @@ describe('List Selector spec', () => {
beforeEach(() => createComponent({ ...USERS_MOCK_PROPS, selectedItems }));
it('renders a heading with the total selected items', () => {
expect(findTitle().text()).toContain('Users');
expect(findTitle().text()).toContain('1');
expect(findTitle()).toContain('Users');
expect(findCrudComponent().props('count')).toBe(1);
});
it('renders a user component for each selected item', () => {
......@@ -158,8 +159,7 @@ describe('List Selector spec', () => {
const search = 'foo';
it('renders a correct title', () => {
expect(findTitle().exists()).toBe(true);
expect(findTitle().text()).toContain('Groups');
expect(findTitle()).toContain('Groups');
});
it('renders the correct icon', () => {
......@@ -296,8 +296,8 @@ describe('List Selector spec', () => {
beforeEach(() => createComponent({ ...GROUPS_MOCK_PROPS, selectedItems }));
it('renders a heading with the total selected items', () => {
expect(findTitle().text()).toContain('Groups');
expect(findTitle().text()).toContain('1');
expect(findTitle()).toContain('Groups');
expect(findCrudComponent().props('count')).toBe(1);
});
it('renders a group component for each selected item', () => {
......@@ -321,8 +321,7 @@ describe('List Selector spec', () => {
beforeEach(() => createComponent(DEPLOY_KEYS_MOCK_PROPS));
it('renders a correct title', () => {
expect(findTitle().exists()).toBe(true);
expect(findTitle().text()).toContain('Deploy keys');
expect(findTitle()).toContain('Deploy keys');
});
it('renders the correct icon', () => {
......@@ -335,8 +334,8 @@ describe('List Selector spec', () => {
beforeEach(() => createComponent({ ...DEPLOY_KEYS_MOCK_PROPS, selectedItems }));
it('renders a heading with the total selected items', () => {
expect(findTitle().text()).toContain('Deploy keys');
expect(findTitle().text()).toContain('1');
expect(findTitle()).toContain('Deploy keys');
expect(findCrudComponent().props('count')).toBe(1);
});
it('renders a deploy key component for each selected item', () => {
......@@ -363,7 +362,7 @@ describe('List Selector spec', () => {
beforeEach(() => createComponent(PROJECTS_MOCK_PROPS));
it('renders a correct title', () => {
expect(findTitle().text()).toContain('Projects');
expect(findTitle()).toContain('Projects');
});
it('renders the correct icon', () => {
......@@ -405,8 +404,8 @@ describe('List Selector spec', () => {
beforeEach(() => createComponent({ ...GROUPS_MOCK_PROPS, selectedItems }));
it('renders a heading with the total selected items', () => {
expect(findTitle().text()).toContain('Groups');
expect(findTitle().text()).toContain('1');
expect(findTitle()).toContain('Groups');
expect(findCrudComponent().props('count')).toBe(1);
});
it('renders a group component for each selected item', () => {
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment