Commit 230856ff authored by Vitaly Slobodin's avatar Vitaly Slobodin

Merge branch '321994-replace-select2select-with-gldropdown' into 'master'

Replace Select2Select with GlDropdown

See merge request gitlab-org/gitlab!61708
parents 6bf32ad8 fea7562f
<script>
import { GlDropdown, GlSearchBoxByType } from '@gitlab/ui';
export default {
components: {
GlDropdown,
GlSearchBoxByType,
},
inheritAttrs: false,
props: {
namespaces: {
type: Array,
required: true,
},
},
data() {
return { searchTerm: '' };
},
computed: {
filteredNamespaces() {
return this.namespaces.filter((ns) =>
ns.toLowerCase().includes(this.searchTerm.toLowerCase()),
);
},
},
};
</script>
<template>
<gl-dropdown
toggle-class="gl-rounded-top-right-none! gl-rounded-bottom-right-none!"
class="import-entities-namespace-dropdown gl-h-7 gl-flex-fill-1"
data-qa-selector="target_namespace_selector_dropdown"
v-bind="$attrs"
>
<template #header>
<gl-search-box-by-type v-model.trim="searchTerm" />
</template>
<slot :namespaces="filteredNamespaces"></slot>
</gl-dropdown>
</template>
<script>
import {
GlButton,
GlDropdown,
GlDropdownDivider,
GlDropdownItem,
GlDropdownSectionHeader,
......@@ -11,6 +10,7 @@ import {
} from '@gitlab/ui';
import { joinPaths } from '~/lib/utils/url_utility';
import { s__ } from '~/locale';
import ImportGroupDropdown from '../../components/group_dropdown.vue';
import ImportStatus from '../../components/import_status.vue';
import { STATUSES } from '../../constants';
import addValidationErrorMutation from '../graphql/mutations/add_validation_error.mutation.graphql';
......@@ -22,8 +22,8 @@ const DEBOUNCE_INTERVAL = 300;
export default {
components: {
ImportStatus,
ImportGroupDropdown,
GlButton,
GlDropdown,
GlDropdownDivider,
GlDropdownItem,
GlDropdownSectionHeader,
......@@ -83,6 +83,10 @@ export default {
},
computed: {
availableNamespaceNames() {
return this.availableNamespaces.map((ns) => ns.full_path);
},
importTarget() {
return this.group.import_target;
},
......@@ -153,9 +157,11 @@ export default {
disabled: isAlreadyImported,
}"
>
<gl-dropdown
<import-group-dropdown
#default="{ namespaces }"
:text="importTarget.target_namespace"
:disabled="isAlreadyImported"
:namespaces="availableNamespaceNames"
toggle-class="gl-rounded-top-right-none! gl-rounded-bottom-right-none!"
class="import-entities-namespace-dropdown gl-h-7 gl-flex-grow-1"
data-qa-selector="target_namespace_selector_dropdown"
......@@ -163,22 +169,22 @@ export default {
<gl-dropdown-item @click="$emit('update-target-namespace', '')">{{
s__('BulkImport|No parent')
}}</gl-dropdown-item>
<template v-if="availableNamespaces.length">
<template v-if="namespaces.length">
<gl-dropdown-divider />
<gl-dropdown-section-header>
{{ s__('BulkImport|Existing groups') }}
</gl-dropdown-section-header>
<gl-dropdown-item
v-for="ns in availableNamespaces"
:key="ns.full_path"
v-for="ns in namespaces"
:key="ns"
data-qa-selector="target_group_dropdown_item"
:data-qa-group-name="ns.full_path"
@click="$emit('update-target-namespace', ns.full_path)"
:data-qa-group-name="ns"
@click="$emit('update-target-namespace', ns)"
>
{{ ns.full_path }}
{{ ns }}
</gl-dropdown-item>
</template>
</gl-dropdown>
</import-group-dropdown>
<div
class="import-entities-target-select-separator gl-h-7 gl-px-3 gl-display-flex gl-align-items-center gl-border-solid gl-border-0 gl-border-t-1 gl-border-b-1"
>
......
......@@ -47,18 +47,7 @@ export default {
},
availableNamespaces() {
const serializedNamespaces = this.namespaces.map(({ fullPath }) => ({
id: fullPath,
text: fullPath,
}));
return [
{ text: __('Groups'), children: serializedNamespaces },
{
text: __('Users'),
children: [{ id: this.defaultTargetNamespace, text: this.defaultTargetNamespace }],
},
];
return this.namespaces.map(({ fullPath }) => fullPath);
},
importAllButtonText() {
......@@ -179,6 +168,7 @@ export default {
:key="repo.importSource.providerLink"
:repo="repo"
:available-namespaces="availableNamespaces"
:user-namespace="defaultTargetNamespace"
/>
</template>
</tbody>
......
<script>
import { GlIcon, GlBadge, GlFormInput, GlButton, GlLink } from '@gitlab/ui';
import {
GlIcon,
GlBadge,
GlFormInput,
GlButton,
GlLink,
GlDropdownItem,
GlDropdownDivider,
GlDropdownSectionHeader,
} from '@gitlab/ui';
import { mapState, mapGetters, mapActions } from 'vuex';
import { __ } from '~/locale';
import Select2Select from '~/vue_shared/components/select2_select.vue';
import ImportGroupDropdown from '../../components/group_dropdown.vue';
import ImportStatus from '../../components/import_status.vue';
import { STATUSES } from '../../constants';
import { isProjectImportable, isIncompatible, getImportStatus } from '../utils';
......@@ -10,10 +19,13 @@ import { isProjectImportable, isIncompatible, getImportStatus } from '../utils';
export default {
name: 'ProviderRepoTableRow',
components: {
Select2Select,
ImportGroupDropdown,
ImportStatus,
GlFormInput,
GlButton,
GlDropdownItem,
GlDropdownDivider,
GlDropdownSectionHeader,
GlIcon,
GlBadge,
GlLink,
......@@ -23,6 +35,10 @@ export default {
type: Object,
required: true,
},
userNamespace: {
type: String,
required: true,
},
availableNamespaces: {
type: Array,
required: true,
......@@ -61,22 +77,6 @@ export default {
return this.ciCdOnly ? __('Connect') : __('Import');
},
select2Options() {
return {
data: this.availableNamespaces,
containerCssClass: 'import-namespace-select qa-project-namespace-select gl-w-auto',
};
},
targetNamespaceSelect: {
get() {
return this.importTarget.targetNamespace;
},
set(value) {
this.updateImportTarget({ targetNamespace: value });
},
},
newNameInput: {
get() {
return this.importTarget.newName;
......@@ -118,7 +118,29 @@ export default {
<template v-if="repo.importSource.target">{{ repo.importSource.target }}</template>
<template v-else-if="isImportNotStarted">
<div class="import-entities-target-select gl-display-flex gl-align-items-stretch gl-w-full">
<select2-select v-model="targetNamespaceSelect" :options="select2Options" />
<import-group-dropdown
#default="{ namespaces }"
:text="importTarget.targetNamespace"
:namespaces="availableNamespaces"
>
<template v-if="namespaces.length">
<gl-dropdown-section-header>{{ __('Groups') }}</gl-dropdown-section-header>
<gl-dropdown-item
v-for="ns in namespaces"
:key="ns"
data-qa-selector="target_group_dropdown_item"
:data-qa-group-name="ns"
@click="updateImportTarget({ targetNamespace: ns })"
>
{{ ns }}
</gl-dropdown-item>
<gl-dropdown-divider />
</template>
<gl-dropdown-section-header>{{ __('Users') }}</gl-dropdown-section-header>
<gl-dropdown-item @click="updateImportTarget({ targetNamespace: ns })">{{
userNamespace
}}</gl-dropdown-item>
</import-group-dropdown>
<div
class="import-entities-target-select-separator gl-px-3 gl-display-flex gl-align-items-center gl-border-solid gl-border-0 gl-border-t-1 gl-border-b-1"
>
......
......@@ -38,7 +38,7 @@ export function initStoreFromElement(element) {
export function initPropsFromElement(element) {
return {
providerTitle: element.dataset.providerTitle,
providerTitle: element.dataset.provider,
filterable: parseBoolean(element.dataset.filterable),
paginatable: parseBoolean(element.dataset.paginatable),
};
......
<script>
import $ from 'jquery';
import 'select2';
import { loadCSSFile } from '~/lib/utils/css_utils';
export default {
// False positive i18n lint: https://gitlab.com/gitlab-org/frontend/eslint-plugin-i18n/issues/26
// eslint-disable-next-line @gitlab/require-i18n-strings
name: 'Select2Select',
props: {
options: {
type: Object,
required: false,
default: () => ({}),
},
value: {
type: String,
required: false,
default: '',
},
},
watch: {
value() {
$(this.$refs.dropdownInput).val(this.value).trigger('change');
},
},
mounted() {
loadCSSFile(gon.select2_css_path)
.then(() => {
$(this.$refs.dropdownInput)
.val(this.value)
.select2(this.options)
.on('change', (event) => this.$emit('input', event.target.value));
})
.catch(() => {});
},
beforeDestroy() {
$(this.$refs.dropdownInput).select2('destroy');
},
};
</script>
<template>
<input ref="dropdownInput" type="hidden" />
</template>
......@@ -10,12 +10,15 @@ module QA
view "app/assets/javascripts/import_entities/import_groups/components/import_table_row.vue" do
element :import_item
element :target_namespace_selector_dropdown
element :target_group_dropdown_item
element :import_status_indicator
element :import_group_button
end
view "app/assets/javascripts/import_entities/components/group_dropdown.vue" do
element :target_namespace_selector_dropdown
end
# Import source group in to target group
#
# @param [String] source_group_name
......
......@@ -14,13 +14,16 @@ module QA
view 'app/assets/javascripts/import_entities/import_projects/components/provider_repo_table_row.vue' do
element :project_import_row
element :project_namespace_select
element :project_path_field
element :import_button
element :project_path_content
element :go_to_project_button
end
view "app/assets/javascripts/import_entities/components/group_dropdown.vue" do
element :target_namespace_selector_dropdown
end
def add_personal_access_token(personal_access_token)
# If for some reasons this process is retried, user cannot re-enter github token in the same group
# In this case skip this step and proceed to import project row
......@@ -59,10 +62,9 @@ module QA
def choose_test_namespace(full_path)
within_repo_path(full_path) do
click_element :project_namespace_select
within_element(:target_namespace_selector_dropdown) { click_button(class: 'dropdown-toggle') }
click_element(:target_group_dropdown_item, group_name: Runtime::Namespace.path)
end
search_and_select(Runtime::Namespace.path)
end
def set_path(full_path, name)
......
import { GlSearchBoxByType, GlDropdown } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import { nextTick } from 'vue';
import GroupDropdown from '~/import_entities/components/group_dropdown.vue';
describe('Import entities group dropdown component', () => {
let wrapper;
let namespacesTracker;
const createComponent = (propsData) => {
namespacesTracker = jest.fn();
wrapper = shallowMount(GroupDropdown, {
scopedSlots: {
default: namespacesTracker,
},
stubs: { GlDropdown },
propsData,
});
};
afterEach(() => {
wrapper.destroy();
});
it('passes namespaces from props to default slot', () => {
const namespaces = ['ns1', 'ns2'];
createComponent({ namespaces });
expect(namespacesTracker).toHaveBeenCalledWith({ namespaces });
});
it('filters namespaces based on user input', async () => {
const namespaces = ['match1', 'some unrelated', 'match2'];
createComponent({ namespaces });
namespacesTracker.mockReset();
wrapper.find(GlSearchBoxByType).vm.$emit('input', 'match');
await nextTick();
expect(namespacesTracker).toHaveBeenCalledWith({ namespaces: ['match1', 'match2'] });
});
});
import { GlButton, GlDropdown, GlDropdownItem, GlLink, GlFormInput } from '@gitlab/ui';
import { GlButton, GlDropdownItem, GlLink, GlFormInput } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import Vue, { nextTick } from 'vue';
import VueApollo from 'vue-apollo';
import createMockApollo from 'helpers/mock_apollo_helper';
import ImportGroupDropdown from '~/import_entities/components/group_dropdown.vue';
import { STATUSES } from '~/import_entities/constants';
import ImportTableRow from '~/import_entities/import_groups/components/import_table_row.vue';
import addValidationErrorMutation from '~/import_entities/import_groups/graphql/mutations/add_validation_error.mutation.graphql';
......@@ -41,7 +42,7 @@ describe('import table row', () => {
};
const findImportButton = () => findByText(GlButton, 'Import');
const findNameInput = () => wrapper.find(GlFormInput);
const findNamespaceDropdown = () => wrapper.find(GlDropdown);
const findNamespaceDropdown = () => wrapper.find(ImportGroupDropdown);
const createComponent = (props) => {
apolloProvider = createMockApollo([
......@@ -65,6 +66,7 @@ describe('import table row', () => {
wrapper = shallowMount(ImportTableRow, {
apolloProvider,
stubs: { ImportGroupDropdown },
propsData: {
availableNamespaces: availableNamespacesFixture,
groupPathRegex: /.*/,
......
......@@ -11,6 +11,8 @@ import state from '~/import_entities/import_projects/store/state';
describe('ImportProjectsTable', () => {
let wrapper;
const USER_NAMESPACE = 'root';
const findFilterField = () =>
wrapper
.findAllComponents(GlFormInput)
......@@ -48,7 +50,7 @@ describe('ImportProjectsTable', () => {
localVue.use(Vuex);
const store = new Vuex.Store({
state: { ...state(), ...initialState },
state: { ...state(), defaultTargetNamespace: USER_NAMESPACE, ...initialState },
getters: {
...getters,
...customGetters,
......
import { GlBadge, GlButton } from '@gitlab/ui';
import { GlBadge, GlButton, GlDropdown } from '@gitlab/ui';
import { createLocalVue, shallowMount } from '@vue/test-utils';
import { nextTick } from 'vue';
import Vuex from 'vuex';
import { STATUSES } from '~/import_entities//constants';
import ImportGroupDropdown from '~/import_entities/components/group_dropdown.vue';
import ImportStatus from '~/import_entities/components/import_status.vue';
import ProviderRepoTableRow from '~/import_entities/import_projects/components/provider_repo_table_row.vue';
import Select2Select from '~/vue_shared/components/select2_select.vue';
describe('ProviderRepoTableRow', () => {
let wrapper;
......@@ -16,10 +16,8 @@ describe('ProviderRepoTableRow', () => {
newName: 'newName',
};
const availableNamespaces = [
{ text: 'Groups', children: [{ id: 'test', text: 'test' }] },
{ text: 'Users', children: [{ id: 'root', text: 'root' }] },
];
const availableNamespaces = ['test'];
const userNamespace = 'root';
function initStore(initialState) {
const store = new Vuex.Store({
......@@ -48,7 +46,7 @@ describe('ProviderRepoTableRow', () => {
wrapper = shallowMount(ProviderRepoTableRow, {
localVue,
store,
propsData: { availableNamespaces, ...props },
propsData: { availableNamespaces, userNamespace, ...props },
});
}
......@@ -81,9 +79,8 @@ describe('ProviderRepoTableRow', () => {
expect(wrapper.find(ImportStatus).props().status).toBe(STATUSES.NONE);
});
it('renders a select2 namespace select', () => {
expect(wrapper.find(Select2Select).exists()).toBe(true);
expect(wrapper.find(Select2Select).props().options.data).toBe(availableNamespaces);
it('renders a group namespace select', () => {
expect(wrapper.find(ImportGroupDropdown).props().namespaces).toBe(availableNamespaces);
});
it('renders import button', () => {
......@@ -133,7 +130,7 @@ describe('ProviderRepoTableRow', () => {
});
it('does not renders a namespace select', () => {
expect(wrapper.find(Select2Select).exists()).toBe(false);
expect(wrapper.find(GlDropdown).exists()).toBe(false);
});
it('does not render import button', () => {
......
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