diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2021-08-19 09:08:42 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2021-08-19 09:08:42 +0000 |
commit | b76ae638462ab0f673e5915986070518dd3f9ad3 (patch) | |
tree | bdab0533383b52873be0ec0eb4d3c66598ff8b91 /app/assets/javascripts/vue_shared/components/dropdown | |
parent | 434373eabe7b4be9593d18a585fb763f1e5f1a6f (diff) | |
download | gitlab-ce-b76ae638462ab0f673e5915986070518dd3f9ad3.tar.gz |
Add latest changes from gitlab-org/gitlab@14-2-stable-eev14.2.0-rc42
Diffstat (limited to 'app/assets/javascripts/vue_shared/components/dropdown')
2 files changed, 192 insertions, 0 deletions
diff --git a/app/assets/javascripts/vue_shared/components/dropdown/dropdown_widget/dropdown_widget.stories.js b/app/assets/javascripts/vue_shared/components/dropdown/dropdown_widget/dropdown_widget.stories.js new file mode 100644 index 00000000000..eeed5e9dc3a --- /dev/null +++ b/app/assets/javascripts/vue_shared/components/dropdown/dropdown_widget/dropdown_widget.stories.js @@ -0,0 +1,27 @@ +/* eslint-disable @gitlab/require-i18n-strings */ + +import { __ } from '~/locale'; +import DropdownWidget from './dropdown_widget.vue'; + +export default { + component: DropdownWidget, + title: 'vue_shared/components/dropdown/dropdown_widget/dropdown_widget', +}; + +const Template = (args, { argTypes }) => ({ + components: { DropdownWidget }, + props: Object.keys(argTypes), + template: '<dropdown-widget v-bind="$props" v-on="$props" />', +}); + +export const Default = Template.bind({}); +Default.args = { + options: [ + { id: 'gid://gitlab/Milestone/-1', title: __('Any Milestone') }, + { id: 'gid://gitlab/Milestone/0', title: __('No Milestone') }, + { id: 'gid://gitlab/Milestone/-2', title: __('Upcoming') }, + { id: 'gid://gitlab/Milestone/-3', title: __('Started') }, + ], + selectText: 'Select', + searchText: 'Search', +}; diff --git a/app/assets/javascripts/vue_shared/components/dropdown/dropdown_widget/dropdown_widget.vue b/app/assets/javascripts/vue_shared/components/dropdown/dropdown_widget/dropdown_widget.vue new file mode 100644 index 00000000000..7859ef85dd8 --- /dev/null +++ b/app/assets/javascripts/vue_shared/components/dropdown/dropdown_widget/dropdown_widget.vue @@ -0,0 +1,165 @@ +<script> +import { + GlLoadingIcon, + GlDropdown, + GlDropdownForm, + GlDropdownDivider, + GlDropdownItem, + GlSearchBoxByType, +} from '@gitlab/ui'; +import { __ } from '~/locale'; + +export default { + components: { + GlLoadingIcon, + GlDropdown, + GlDropdownForm, + GlDropdownDivider, + GlDropdownItem, + GlSearchBoxByType, + }, + props: { + selectText: { + type: String, + required: false, + default: __('Select'), + }, + searchText: { + type: String, + required: false, + default: __('Search'), + }, + presetOptions: { + type: Array, + required: false, + default: () => [], + }, + options: { + type: Array, + required: false, + default: () => [], + }, + isLoading: { + type: Boolean, + required: false, + default: false, + }, + selected: { + type: Object, + required: false, + default: () => {}, + }, + searchTerm: { + type: String, + required: false, + default: '', + }, + }, + computed: { + isSearchEmpty() { + return this.searchTerm === '' && !this.isLoading; + }, + noOptionsFound() { + return !this.isSearchEmpty && this.options.length === 0; + }, + }, + methods: { + selectOption(option) { + this.$emit('set-option', option || null); + }, + isSelected(option) { + return ( + this.selected && + ((option.name && this.selected.name === option.name) || + (option.title && this.selected.title === option.title)) + ); + }, + showDropdown() { + this.$refs.dropdown.show(); + }, + setFocus() { + this.$refs.search.focusInput(); + }, + setSearchTerm(search) { + this.$emit('set-search', search); + }, + avatarUrl(option) { + return option.avatar_url || option.avatarUrl || null; + }, + secondaryText(option) { + // TODO: this has some knowledge of the context where the component is used. We could later rework it. + return option.username || null; + }, + }, + i18n: { + noMatchingResults: __('No matching results'), + }, +}; +</script> + +<template> + <gl-dropdown + ref="dropdown" + :text="selectText" + lazy + menu-class="gl-w-full!" + class="gl-w-full" + v-on="$listeners" + @shown="setFocus" + > + <template #header> + <gl-search-box-by-type + ref="search" + :value="searchTerm" + :placeholder="searchText" + class="js-dropdown-input-field" + @input="setSearchTerm" + /> + </template> + <gl-dropdown-form class="gl-relative gl-min-h-7"> + <gl-loading-icon + v-if="isLoading" + size="md" + class="gl-absolute gl-left-0 gl-top-0 gl-right-0" + /> + <template v-else> + <template v-if="isSearchEmpty && presetOptions.length > 0"> + <gl-dropdown-item + v-for="option in presetOptions" + :key="option.id" + :is-checked="isSelected(option)" + :is-check-centered="true" + :is-check-item="true" + @click="selectOption(option)" + > + <slot name="preset-item" :item="option"> + {{ option.title }} + </slot> + </gl-dropdown-item> + <gl-dropdown-divider /> + </template> + <gl-dropdown-item + v-for="option in options" + :key="option.id" + :is-checked="isSelected(option)" + :is-check-centered="true" + :is-check-item="true" + :avatar-url="avatarUrl(option)" + :secondary-text="secondaryText(option)" + data-testid="unselected-option" + @click="selectOption(option)" + > + <slot name="item" :item="option"> + {{ option.title }} + </slot> + </gl-dropdown-item> + <gl-dropdown-item v-if="noOptionsFound" class="gl-pl-6!"> + {{ $options.i18n.noMatchingResults }} + </gl-dropdown-item> + </template> + </gl-dropdown-form> + <template #footer> + <slot name="footer"></slot> + </template> + </gl-dropdown> +</template> |