Commit 764ef548 authored by Natalia Tepluhina's avatar Natalia Tepluhina

Merge branch '332260-refactor-dropdownbutton-dropdowncontents-to-gldropdown' into 'master'

Refactor labels dropdown to GlDropdown

See merge request gitlab-org/gitlab!69263
parents 7641233f e51b3eed
......@@ -259,6 +259,7 @@ export function mountSidebarLabels() {
initiallySelectedLabels: JSON.parse(el.dataset.selectedLabels),
variant: DropdownVariant.Sidebar,
canUpdate: parseBoolean(el.dataset.canEdit),
isClassicSidebar: true,
},
render: (createElement) => createElement(SidebarLabels),
});
......
<script>
import { GlButton, GlIcon } from '@gitlab/ui';
import { mapActions, mapGetters } from 'vuex';
export default {
components: {
GlButton,
GlIcon,
},
computed: {
...mapGetters([
'dropdownButtonText',
'isDropdownVariantStandalone',
'isDropdownVariantEmbedded',
]),
},
methods: {
...mapActions(['toggleDropdownContents']),
handleButtonClick(e) {
if (this.isDropdownVariantStandalone || this.isDropdownVariantEmbedded) {
this.toggleDropdownContents();
}
if (this.isDropdownVariantStandalone) {
e.stopPropagation();
}
},
},
};
</script>
<template>
<gl-button
class="labels-select-dropdown-button js-dropdown-button w-100 text-left"
@click="handleButtonClick"
>
<span class="dropdown-toggle-text gl-pointer-events-none flex-fill">
{{ dropdownButtonText }}
</span>
<gl-icon name="chevron-down" class="gl-pointer-events-none float-right" />
</gl-button>
</template>
<script>
import { GlButton } from '@gitlab/ui';
import { GlButton, GlDropdown, GlDropdownItem, GlLink } from '@gitlab/ui';
import { mapActions, mapGetters, mapState } from 'vuex';
import DropdownContentsCreateView from './dropdown_contents_create_view.vue';
......@@ -10,13 +10,12 @@ export default {
DropdownContentsLabelsView,
DropdownContentsCreateView,
GlButton,
GlDropdown,
GlDropdownItem,
GlLink,
},
inject: ['allowLabelCreate', 'labelsManagePath'],
props: {
renderOnTop: {
type: Boolean,
required: false,
default: false,
},
labelsCreateTitle: {
type: String,
required: true,
......@@ -44,67 +43,90 @@ export default {
},
computed: {
...mapState(['showDropdownContentsCreateView']),
...mapGetters(['isDropdownVariantSidebar', 'isDropdownVariantEmbedded']),
...mapGetters(['dropdownButtonText', 'isDropdownVariantSidebar', 'isDropdownVariantEmbedded']),
dropdownContentsView() {
if (this.showDropdownContentsCreateView) {
return 'dropdown-contents-create-view';
}
return 'dropdown-contents-labels-view';
},
directionStyle() {
const bottom = this.isDropdownVariantSidebar ? '3rem' : '2rem';
return this.renderOnTop ? { bottom } : {};
},
dropdownTitle() {
return this.showDropdownContentsCreateView ? this.labelsCreateTitle : this.labelsListTitle;
},
showDropdownFooter() {
return (
!this.showDropdownContentsCreateView &&
(this.isDropdownVariantSidebar || this.isDropdownVariantEmbedded)
);
},
},
methods: {
...mapActions(['toggleDropdownContentsCreateView']),
showDropdown() {
this.$refs.dropdown.show();
},
toggleDropdownContent() {
this.toggleDropdownContentsCreateView();
// Required to recalculate dropdown position as its size changes
this.$refs.dropdown.$refs.dropdown.$_popper.scheduleUpdate();
},
},
};
</script>
<template>
<div
class="labels-select-dropdown-contents gl-w-full gl-my-2 gl-py-3 gl-rounded-base gl-absolute"
<gl-dropdown
ref="dropdown"
:text="dropdownButtonText"
class="gl-w-full gl-mt-2"
data-qa-selector="labels_dropdown_content"
:style="directionStyle"
>
<div
v-if="isDropdownVariantSidebar || isDropdownVariantEmbedded"
class="dropdown-title gl-display-flex gl-align-items-center gl-pt-0 gl-pb-3!"
data-testid="dropdown-title"
>
<gl-button
v-if="showDropdownContentsCreateView"
:aria-label="__('Go back')"
variant="link"
size="small"
class="js-btn-back dropdown-header-button p-0"
icon="arrow-left"
@click.stop="toggleDropdownContentsCreateView"
/>
<span class="flex-grow-1">{{ dropdownTitle }}</span>
<gl-button
:aria-label="__('Close')"
variant="link"
size="small"
class="dropdown-header-button gl-p-0!"
icon="close"
@click="$emit('closeDropdown')"
/>
</div>
<template #header>
<div
v-if="isDropdownVariantSidebar || isDropdownVariantEmbedded"
class="dropdown-title gl-display-flex gl-align-items-center gl-pt-0 gl-pb-3!"
>
<gl-button
v-if="showDropdownContentsCreateView"
:aria-label="__('Go back')"
variant="link"
size="small"
class="js-btn-back dropdown-header-button gl-p-0"
icon="arrow-left"
data-testid="go-back-button"
@click.stop="toggleDropdownContent"
/>
<span class="gl-flex-grow-1">{{ dropdownTitle }}</span>
<gl-button
:aria-label="__('Close')"
variant="link"
size="small"
class="dropdown-header-button gl-p-0!"
icon="close"
@click="$emit('closeDropdown')"
/>
</div>
</template>
<component
:is="dropdownContentsView"
:selected-labels="selectedLabels"
:allow-multiselect="allowMultiselect"
:labels-list-title="labelsListTitle"
:footer-create-label-title="footerCreateLabelTitle"
:footer-manage-label-title="footerManageLabelTitle"
@hideCreateView="toggleDropdownContentsCreateView"
@setLabels="$emit('setLabels', $event)"
@toggleDropdownContentsCreateView="toggleDropdownContentsCreateView"
/>
</div>
<template #footer>
<div v-if="showDropdownFooter" data-testid="dropdown-footer">
<gl-dropdown-item
v-if="allowLabelCreate"
data-testid="create-label-button"
@click.native.capture.stop="toggleDropdownContent"
>
{{ footerCreateLabelTitle }}
</gl-dropdown-item>
<gl-dropdown-item :href="labelsManagePath">
{{ footerManageLabelTitle }}
</gl-dropdown-item>
</div>
</template>
</gl-dropdown>
</template>
<script>
import { GlLoadingIcon, GlSearchBoxByType, GlLink } from '@gitlab/ui';
import { GlDropdownForm, GlDropdownItem, GlLoadingIcon, GlSearchBoxByType } from '@gitlab/ui';
import fuzzaldrinPlus from 'fuzzaldrin-plus';
import { debounce } from 'lodash';
import createFlash from '~/flash';
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import { DEFAULT_DEBOUNCE_AND_THROTTLE_MS } from '~/lib/utils/constants';
import { UP_KEY_CODE, DOWN_KEY_CODE, ENTER_KEY_CODE, ESC_KEY_CODE } from '~/lib/utils/keycodes';
import { __ } from '~/locale';
import { DropdownVariant } from './constants';
import projectLabelsQuery from './graphql/project_labels.query.graphql';
import LabelItem from './label_item.vue';
export default {
components: {
GlDropdownForm,
GlDropdownItem,
GlLoadingIcon,
GlSearchBoxByType,
GlLink,
LabelItem,
},
inject: ['projectPath', 'allowLabelCreate', 'labelsManagePath', 'variant'],
inject: ['projectPath'],
props: {
selectedLabels: {
type: Array,
......@@ -28,18 +27,6 @@ export default {
type: Boolean,
required: true,
},
labelsListTitle: {
type: String,
required: true,
},
footerCreateLabelTitle: {
type: String,
required: true,
},
footerManageLabelTitle: {
type: String,
required: true,
},
},
data() {
return {
......@@ -74,12 +61,6 @@ export default {
},
},
computed: {
isDropdownVariantSidebar() {
return this.variant === DropdownVariant.Sidebar;
},
isDropdownVariantEmbedded() {
return this.variant === DropdownVariant.Embedded;
},
labelsFetchInProgress() {
return this.$apollo.queries.labels.loading;
},
......@@ -150,37 +131,10 @@ export default {
});
}
},
/**
* This method enables keyboard navigation support for
* the dropdown.
*/
handleKeyDown(e) {
if (e.keyCode === UP_KEY_CODE && this.currentHighlightItem > 0) {
this.currentHighlightItem -= 1;
} else if (
e.keyCode === DOWN_KEY_CODE &&
this.currentHighlightItem < this.visibleLabels.length - 1
) {
this.currentHighlightItem += 1;
} else if (e.keyCode === ENTER_KEY_CODE && this.currentHighlightItem > -1) {
this.updateSelectedLabels(this.visibleLabels[this.currentHighlightItem]);
this.searchKey = '';
} else if (e.keyCode === ESC_KEY_CODE) {
this.$emit('setLabels', this.localSelectedLabels);
}
if (e.keyCode !== ESC_KEY_CODE) {
// Scroll the list only after highlighting
// styles are rendered completely.
this.$nextTick(() => {
this.scrollIntoViewIfNeeded();
});
}
},
handleLabelClick(label) {
this.updateSelectedLabels(label);
if (!this.allowMultiselect) {
this.$emit('setLabels', this.localSelectedLabels);
this.$emit('closeDropdown', this.localSelectedLabels);
}
},
setSearchKey(value) {
......@@ -191,69 +145,42 @@ export default {
</script>
<template>
<div
class="labels-select-contents-list js-labels-list"
data-testid="dropdown-wrapper"
@keydown="handleKeyDown"
>
<div class="dropdown-input" @click.stop="() => {}">
<gl-search-box-by-type
ref="searchInput"
:value="searchKey"
:disabled="labelsFetchInProgress"
data-qa-selector="dropdown_input_field"
data-testid="dropdown-input-field"
@input="debouncedSearchKeyUpdate"
/>
</div>
<div ref="labelsListContainer" class="dropdown-content" data-testid="dropdown-content">
<gl-dropdown-form class="labels-select-contents-list js-labels-list">
<gl-search-box-by-type
ref="searchInput"
:value="searchKey"
:disabled="labelsFetchInProgress"
data-qa-selector="dropdown_input_field"
data-testid="dropdown-input-field"
@input="debouncedSearchKeyUpdate"
/>
<div ref="labelsListContainer" data-testid="dropdown-content">
<gl-loading-icon
v-if="labelsFetchInProgress"
class="labels-fetch-loading gl-align-items-center gl-w-full gl-h-full"
size="md"
/>
<ul v-else class="list-unstyled gl-mb-0 gl-word-break-word" data-testid="labels-list">
<label-item
<template v-else>
<gl-dropdown-item
v-for="(label, index) in visibleLabels"
:key="label.id"
:label="label"
:is-label-set="isLabelSelected(label)"
:highlight="index === currentHighlightItem"
@clickLabel="handleLabelClick(label)"
/>
<li
data-testid="labels-list"
@click.native.capture.stop="handleLabelClick(label)"
>
<label-item
:label="label"
:is-label-set="isLabelSelected(label)"
:highlight="index === currentHighlightItem"
/>
</gl-dropdown-item>
<gl-dropdown-item
v-show="showNoMatchingResultsMessage"
class="gl-p-3 gl-text-center"
data-testid="no-results"
>
{{ __('No matching results') }}
</li>
</ul>
</div>
<div
v-if="isDropdownVariantSidebar || isDropdownVariantEmbedded"
class="dropdown-footer"
data-testid="dropdown-footer"
>
<ul class="list-unstyled">
<li v-if="allowLabelCreate">
<gl-link
class="gl-display-flex gl-flex-direction-row gl-w-full gl-overflow-break-word label-item"
data-testid="create-label-button"
@click.stop="$emit('toggleDropdownContentsCreateView')"
>
{{ footerCreateLabelTitle }}
</gl-link>
</li>
<li>
<gl-link
:href="labelsManagePath"
class="gl-display-flex gl-flex-direction-row gl-w-full gl-overflow-break-word label-item"
>
{{ footerManageLabelTitle }}
</gl-link>
</li>
</ul>
</gl-dropdown-item>
</template>
</div>
</div>
</gl-dropdown-form>
</template>
......@@ -18,7 +18,7 @@ export default {
default: false,
},
},
render(h, { props, listeners }) {
render(h, { props }) {
const { label, highlight, isLabelSet } = props;
const labelColorBox = h('span', {
......@@ -53,18 +53,7 @@ export default {
const labelTitle = h('span', label.title);
const labelLink = h(
GlLink,
{
class: 'gl-display-flex gl-align-items-center label-item gl-text-black-normal',
on: {
click: () => {
listeners.clickLabel(label);
},
},
},
[noIcon, checkedIcon, labelColorBox, labelTitle],
);
const labelLink = h(GlLink, [noIcon, checkedIcon, labelColorBox, labelTitle]);
return h(
'li',
......
<script>
import Vue from 'vue';
import Vuex, { mapState, mapActions, mapGetters } from 'vuex';
import { isInViewport } from '~/lib/utils/common_utils';
import Vuex, { mapActions, mapGetters } from 'vuex';
import { __ } from '~/locale';
import SidebarEditableItem from '~/sidebar/components/sidebar_editable_item.vue';
import { DropdownVariant } from './constants';
import DropdownButton from './dropdown_button.vue';
import DropdownContents from './dropdown_contents.vue';
import DropdownValue from './dropdown_value.vue';
import DropdownValueCollapsed from './dropdown_value_collapsed.vue';
......@@ -18,7 +16,6 @@ export default {
store: new Vuex.Store(labelsSelectModule()),
components: {
DropdownValue,
DropdownButton,
DropdownContents,
DropdownValueCollapsed,
SidebarEditableItem,
......@@ -137,7 +134,6 @@ export default {
},
},
computed: {
...mapState(['showDropdownContents']),
...mapGetters([
'isDropdownVariantSidebar',
'isDropdownVariantStandalone',
......@@ -150,9 +146,6 @@ export default {
selectedLabels,
});
},
showDropdownContents(showDropdownContents) {
this.setContentIsOnViewport(showDropdownContents);
},
isEditing(newVal) {
if (newVal) {
this.toggleDropdownContents();
......@@ -190,11 +183,9 @@ export default {
handleCollapsedValueClick() {
this.$emit('toggleCollapse');
},
setContentIsOnViewport() {
showDropdown() {
this.$nextTick(() => {
if (this.$refs.dropdownContents) {
this.contentIsOnViewport = isInViewport(this.$refs.dropdownContents.$el);
}
this.$refs.dropdownContents.showDropdown();
});
},
},
......@@ -219,8 +210,7 @@ export default {
ref="editable"
:title="__('Labels')"
:loading="labelsSelectInProgress"
@open="setContentIsOnViewport"
@close="contentIsOnViewport = true"
@open="showDropdown"
>
<template #collapsed>
<dropdown-value
......@@ -248,7 +238,6 @@ export default {
>
<slot></slot>
</dropdown-value>
<dropdown-button />
<dropdown-contents
v-if="edit"
ref="dropdownContents"
......@@ -256,7 +245,6 @@ export default {
:labels-list-title="labelsListTitle"
:footer-create-label-title="footerCreateLabelTitle"
:footer-manage-label-title="footerManageLabelTitle"
:render-on-top="!contentIsOnViewport"
:labels-create-title="labelsCreateTitle"
:selected-labels="selectedLabels"
@closeDropdown="collapseDropdown"
......
......@@ -15,6 +15,7 @@ RSpec.describe "Issues > User edits issue", :js do
context 'with authorized user' do
before do
stub_feature_flags(labels_widget: false)
project.add_developer(user)
project_with_milestones.add_developer(user)
sign_in(user)
......
......@@ -17,6 +17,7 @@ RSpec.describe 'Labels Hierarchy', :js do
let!(:project_label_1) { create(:label, project: project_1, title: 'Label_4') }
before do
stub_feature_flags(labels_widget: false)
grandparent.add_owner(user)
sign_in(user)
......
import { GlIcon, GlButton } from '@gitlab/ui';
import { shallowMount, createLocalVue } from '@vue/test-utils';
import Vuex from 'vuex';
import DropdownButton from '~/vue_shared/components/sidebar/labels_select_widget/dropdown_button.vue';
import labelSelectModule from '~/vue_shared/components/sidebar/labels_select_widget/store';
import { mockConfig } from './mock_data';
let store;
const localVue = createLocalVue();
localVue.use(Vuex);
const createComponent = (initialState = mockConfig) => {
store = new Vuex.Store(labelSelectModule());
store.dispatch('setInitialState', initialState);
return shallowMount(DropdownButton, {
localVue,
store,
});
};
describe('DropdownButton', () => {
let wrapper;
beforeEach(() => {
wrapper = createComponent();
});
afterEach(() => {
wrapper.destroy();
});
const findDropdownButton = () => wrapper.find(GlButton);
const findDropdownText = () => wrapper.find('.dropdown-toggle-text');
const findDropdownIcon = () => wrapper.find(GlIcon);
describe('methods', () => {
describe('handleButtonClick', () => {
it.each`
variant | expectPropagationStopped
${'standalone'} | ${true}
${'embedded'} | ${false}
`(
'toggles dropdown content and handles event propagation when `state.variant` is "$variant"',
({ variant, expectPropagationStopped }) => {
const event = { stopPropagation: jest.fn() };
wrapper = createComponent({ ...mockConfig, variant });
findDropdownButton().vm.$emit('click', event);
expect(store.state.showDropdownContents).toBe(true);
expect(event.stopPropagation).toHaveBeenCalledTimes(expectPropagationStopped ? 1 : 0);
},
);
});
});
describe('template', () => {
it('renders component container element', () => {
expect(wrapper.find(GlButton).element).toBe(wrapper.element);
});
it('renders default button text element', () => {
const dropdownTextEl = findDropdownText();
expect(dropdownTextEl.exists()).toBe(true);
expect(dropdownTextEl.text()).toBe('Label');
});
it('renders provided button text element', () => {
store.state.dropdownButtonText = 'Custom label';
const dropdownTextEl = findDropdownText();
return wrapper.vm.$nextTick().then(() => {
expect(dropdownTextEl.text()).toBe('Custom label');
});
});
it('renders chevron icon element', () => {
const iconEl = findDropdownIcon();
expect(iconEl.exists()).toBe(true);
expect(iconEl.props('name')).toBe('chevron-down');
});
});
});
......@@ -45,8 +45,6 @@ describe('DropdownContentsLabelsView', () => {
provide: {
projectPath: 'test',
iid: 1,
allowLabelCreate: true,
labelsManagePath: '/gitlab-org/my-project/-/labels',
variant: DropdownVariant.Sidebar,
...injected,
},
......@@ -69,10 +67,7 @@ describe('DropdownContentsLabelsView', () => {
const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
const findLabelsList = () => wrapper.find('[data-testid="labels-list"]');
const findDropdownWrapper = () => wrapper.find('[data-testid="dropdown-wrapper"]');
const findDropdownFooter = () => wrapper.find('[data-testid="dropdown-footer"]');
const findNoResultsMessage = () => wrapper.find('[data-testid="no-results"]');
const findCreateLabelButton = () => wrapper.find('[data-testid="create-label-button"]');
describe('when loading labels', () => {
it('renders disabled search input field', async () => {
......@@ -109,34 +104,6 @@ describe('DropdownContentsLabelsView', () => {
expect(findLabelsList().exists()).toBe(true);
expect(findLabels()).toHaveLength(2);
});
it('changes highlighted label correctly on pressing down button', async () => {
expect(findLabels().at(0).attributes('highlight')).toBeUndefined();
await findDropdownWrapper().trigger('keydown.down');
expect(findLabels().at(0).attributes('highlight')).toBe('true');
await findDropdownWrapper().trigger('keydown.down');
expect(findLabels().at(1).attributes('highlight')).toBe('true');
expect(findLabels().at(0).attributes('highlight')).toBeUndefined();
});
it('changes highlighted label correctly on pressing up button', async () => {
await findDropdownWrapper().trigger('keydown.down');
await findDropdownWrapper().trigger('keydown.down');
expect(findLabels().at(1).attributes('highlight')).toBe('true');
await findDropdownWrapper().trigger('keydown.up');
expect(findLabels().at(0).attributes('highlight')).toBe('true');
});
it('changes label selected state when Enter is pressed', async () => {
expect(findLabels().at(0).attributes('islabelset')).toBeUndefined();
await findDropdownWrapper().trigger('keydown.down');
await findDropdownWrapper().trigger('keydown.enter');
expect(findLabels().at(0).attributes('islabelset')).toBe('true');
});
});
it('when search returns 0 results', async () => {
......@@ -164,44 +131,4 @@ describe('DropdownContentsLabelsView', () => {
await waitForPromises();
expect(createFlash).toHaveBeenCalled();
});
it('does not render footer on standalone dropdown', () => {
createComponent({ injected: { variant: DropdownVariant.Standalone } });
expect(findDropdownFooter().exists()).toBe(false);
});
it('renders footer on sidebar dropdown', () => {
createComponent();
expect(findDropdownFooter().exists()).toBe(true);
});
it('renders footer on embedded dropdown', () => {
createComponent({ injected: { variant: DropdownVariant.Embedded } });
expect(findDropdownFooter().exists()).toBe(true);
});
it('does not render create label button if `allowLabelCreate` is false', () => {
createComponent({ injected: { allowLabelCreate: false } });
expect(findCreateLabelButton().exists()).toBe(false);
});
describe('when `allowLabelCreate` is true', () => {
beforeEach(() => {
createComponent();
});
it('renders create label button', () => {
expect(findCreateLabelButton().exists()).toBe(true);
});
it('emits `toggleDropdownContentsCreateView` event on create label button click', () => {
findCreateLabelButton().vm.$emit('click', new MouseEvent('click'));
expect(wrapper.emitted('toggleDropdownContentsCreateView')).toEqual([[]]);
});
});
});
import { shallowMount, createLocalVue } from '@vue/test-utils';
import { GlDropdown } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import Vue from 'vue';
import Vuex from 'vuex';
import { DropdownVariant } from '~/vue_shared/components/sidebar/labels_select_widget/constants';
import DropdownContents from '~/vue_shared/components/sidebar/labels_select_widget/dropdown_contents.vue';
import DropdownContentsCreateView from '~/vue_shared/components/sidebar/labels_select_widget/dropdown_contents_create_view.vue';
import DropdownContentsLabelsView from '~/vue_shared/components/sidebar/labels_select_widget/dropdown_contents_labels_view.vue';
import labelsSelectModule from '~/vue_shared/components/sidebar/labels_select_widget/store';
import { mockConfig, mockLabels } from './mock_data';
const localVue = createLocalVue();
localVue.use(Vuex);
const createComponent = (initialState = mockConfig, defaultProps = {}) => {
const store = new Vuex.Store(labelsSelectModule());
store.dispatch('setInitialState', initialState);
return shallowMount(DropdownContents, {
propsData: {
...defaultProps,
labelsCreateTitle: 'test',
selectedLabels: mockLabels,
allowMultiselect: true,
labelsListTitle: 'Assign labels',
footerCreateLabelTitle: 'create',
footerManageLabelTitle: 'manage',
},
localVue,
store,
});
};
Vue.use(Vuex);
describe('DropdownContent', () => {
let wrapper;
const createComponent = ({
initialState = mockConfig,
defaultProps = {},
injected = {},
} = {}) => {
const store = new Vuex.Store(labelsSelectModule());
store.dispatch('setInitialState', initialState);
wrapper = shallowMount(DropdownContents, {
propsData: {
...defaultProps,
labelsCreateTitle: 'test',
selectedLabels: mockLabels,
allowMultiselect: true,
labelsListTitle: 'Assign labels',
footerCreateLabelTitle: 'create',
footerManageLabelTitle: 'manage',
},
provide: {
allowLabelCreate: true,
labelsManagePath: 'foo/bar',
...injected,
},
store,
stubs: {
GlDropdown,
},
});
};
beforeEach(() => {
wrapper = createComponent();
createComponent();
});
afterEach(() => {
wrapper.destroy();
});
describe('computed', () => {
describe('dropdownContentsView', () => {
it('returns string "dropdown-contents-create-view" when `showDropdownContentsCreateView` prop is `true`', () => {
wrapper.vm.$store.dispatch('toggleDropdownContentsCreateView');
const findDropdownFooter = () => wrapper.find('[data-testid="dropdown-footer"]');
const findCreateLabelButton = () => wrapper.find('[data-testid="create-label-button"]');
const findGoBackButton = () => wrapper.find('[data-testid="go-back-button"]');
expect(wrapper.vm.dropdownContentsView).toBe('dropdown-contents-create-view');
});
describe('Create view', () => {
beforeEach(() => {
wrapper.vm.$store.dispatch('toggleDropdownContentsCreateView');
});
it('returns string "dropdown-contents-labels-view" when `showDropdownContentsCreateView` prop is `false`', () => {
expect(wrapper.vm.dropdownContentsView).toBe('dropdown-contents-labels-view');
});
it('renders create view when `showDropdownContentsCreateView` prop is `true`', () => {
expect(wrapper.findComponent(DropdownContentsCreateView).exists()).toBe(true);
});
it('does not render footer', () => {
expect(findDropdownFooter().exists()).toBe(false);
});
it('does not render create label button', () => {
expect(findCreateLabelButton().exists()).toBe(false);
});
it('renders go back button', () => {
expect(findGoBackButton().exists()).toBe(true);
});
});
describe('template', () => {
it('renders component container element with class `labels-select-dropdown-contents` and no styles', () => {
expect(wrapper.attributes('class')).toContain('labels-select-dropdown-contents');
expect(wrapper.attributes('style')).toBeUndefined();
describe('Labels view', () => {
it('renders labels view when `showDropdownContentsCreateView` when `showDropdownContentsCreateView` prop is `false`', () => {
expect(wrapper.findComponent(DropdownContentsLabelsView).exists()).toBe(true);
});
it('renders footer on sidebar dropdown', () => {
expect(findDropdownFooter().exists()).toBe(true);
});
it('does not render footer on standalone dropdown', () => {
createComponent({ initialState: { ...mockConfig, variant: DropdownVariant.Standalone } });
expect(findDropdownFooter().exists()).toBe(false);
});
it('renders footer on embedded dropdown', () => {
createComponent({ initialState: { ...mockConfig, variant: DropdownVariant.Embedded } });
expect(findDropdownFooter().exists()).toBe(true);
});
describe('when `renderOnTop` is true', () => {
it.each`
variant | expected
${DropdownVariant.Sidebar} | ${'bottom: 3rem'}
${DropdownVariant.Standalone} | ${'bottom: 2rem'}
${DropdownVariant.Embedded} | ${'bottom: 2rem'}
`('renders upward for $variant variant', ({ variant, expected }) => {
wrapper = createComponent({ ...mockConfig, variant }, { renderOnTop: true });
it('does not render go back button', () => {
expect(findGoBackButton().exists()).toBe(false);
});
it('does not render create label button if `allowLabelCreate` is false', () => {
createComponent({ injected: { allowLabelCreate: false } });
expect(wrapper.attributes('style')).toContain(expected);
expect(findCreateLabelButton().exists()).toBe(false);
});
describe('when `allowLabelCreate` is true', () => {
beforeEach(() => {
createComponent();
});
it('renders create label button', () => {
expect(findCreateLabelButton().exists()).toBe(true);
});
it('triggers `toggleDropdownContent` method on create label button click', () => {
jest.spyOn(wrapper.vm, 'toggleDropdownContent').mockImplementation(() => {});
findCreateLabelButton().trigger('click');
expect(wrapper.vm.toggleDropdownContent).toHaveBeenCalled();
});
});
});
describe('template', () => {
it('renders component container element with classes `gl-w-full gl-mt-2` and no styles', () => {
expect(wrapper.attributes('class')).toContain('gl-w-full gl-mt-2');
expect(wrapper.attributes('style')).toBeUndefined();
});
});
});
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment