feat: Update agent and team multi-select with new multi-select dropdown (#2516)

* feat: Update agent/team multiselect styles

* Component name fixes

* Adds key control for our multiselect dropdown and component name spell fix

* Minor fixes

* Review fixes

* Minor fixes

* Minor fixes

* Review fixes

Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
Co-authored-by: Nithin David Thomas <1277421+nithindavid@users.noreply.github.com>
This commit is contained in:
Sivin Varghese 2021-07-29 17:44:37 +05:30 committed by GitHub
parent 672e5874fb
commit f2b5e328bb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 117 additions and 41 deletions

View file

@ -91,6 +91,23 @@
}, },
"SEARCH": { "SEARCH": {
"NO_RESULTS": "No results found." "NO_RESULTS": "No results found."
},
"MULTI_SELECTOR": {
"PLACEHOLDER": "None",
"TITLE": {
"AGENT": "Select agent",
"TEAM": "Select team"
},
"SEARCH": {
"NO_RESULTS": {
"AGENT": "No agents found",
"TEAM": "No teams found"
},
"PLACEHOLDER": {
"AGENT": "Search agents",
"TEAM": "Search teams"
}
}
} }
} }
} }

View file

@ -24,27 +24,21 @@
</woot-button> </woot-button>
</template> </template>
</contact-details-item> </contact-details-item>
<multiselect <multiselect-dropdown
v-model="assignedAgent"
:options="agentsList" :options="agentsList"
label="name" :selected-item="assignedAgent"
track-by="id" :multiselector-title="$t('AGENT_MGMT.MULTI_SELECTOR.TITLE.AGENT')"
deselect-label="" :multiselector-placeholder="
select-label="" $t('AGENT_MGMT.MULTI_SELECTOR.PLACEHOLDER')
selected-label="" "
:placeholder="$t('CONVERSATION_SIDEBAR.SELECT.PLACEHOLDER')" :no-search-result="
:allow-empty="true" $t('AGENT_MGMT.MULTI_SELECTOR.SEARCH.NO_RESULTS.AGENT')
> "
<template slot="option" slot-scope="props"> :input-placeholder="
<div class="option__desc"> $t('AGENT_MGMT.MULTI_SELECTOR.SEARCH.PLACEHOLDER.AGENT')
<availability-status-badge "
:status="props.option.availability_status" @click="onClickAssignAgent"
/> />
<span class="option__title">{{ props.option.name }}</span>
</div>
</template>
<span slot="noResult">{{ $t('AGENT_MGMT.SEARCH.NO_RESULTS') }}</span>
</multiselect>
</div> </div>
<div class="multiselect-wrap--small"> <div class="multiselect-wrap--small">
<contact-details-item <contact-details-item
@ -52,19 +46,21 @@
icon="ion-ios-people" icon="ion-ios-people"
emoji="🎢" emoji="🎢"
/> />
<multiselect <multiselect-dropdown
v-model="assignedTeam"
:options="teamsList" :options="teamsList"
label="name" :selected-item="assignedTeam"
track-by="id" :multiselector-title="$t('AGENT_MGMT.MULTI_SELECTOR.TITLE.TEAM')"
deselect-label="" :multiselector-placeholder="
select-label="" $t('AGENT_MGMT.MULTI_SELECTOR.PLACEHOLDER')
selected-label="" "
:placeholder="$t('CONVERSATION_SIDEBAR.SELECT.PLACEHOLDER')" :no-search-result="
:allow-empty="true" $t('AGENT_MGMT.MULTI_SELECTOR.SEARCH.NO_RESULTS.TEAM')
> "
<span slot="noResult">{{ $t('AGENT_MGMT.SEARCH.NO_RESULTS') }}</span> :input-placeholder="
</multiselect> $t('AGENT_MGMT.MULTI_SELECTOR.SEARCH.PLACEHOLDER.TEAM')
"
@click="onClickAssignTeam"
/>
</div> </div>
</div> </div>
<conversation-labels :conversation-id="conversationId" /> <conversation-labels :conversation-id="conversationId" />
@ -138,7 +134,7 @@ import ContactDetailsItem from './ContactDetailsItem.vue';
import ContactInfo from './contact/ContactInfo'; import ContactInfo from './contact/ContactInfo';
import ConversationLabels from './labels/LabelBox.vue'; import ConversationLabels from './labels/LabelBox.vue';
import ContactCustomAttributes from './ContactCustomAttributes'; import ContactCustomAttributes from './ContactCustomAttributes';
import AvailabilityStatusBadge from 'dashboard/components/widgets/conversation/AvailabilityStatusBadge.vue'; import MultiselectDropdown from 'shared/components/ui/MultiselectDropdown.vue';
import flag from 'country-code-emoji'; import flag from 'country-code-emoji';
@ -149,7 +145,7 @@ export default {
ContactDetailsItem, ContactDetailsItem,
ContactInfo, ContactInfo,
ConversationLabels, ConversationLabels,
AvailabilityStatusBadge, MultiselectDropdown,
}, },
mixins: [alertMixin, agentMixin], mixins: [alertMixin, agentMixin],
props: { props: {
@ -330,6 +326,21 @@ export default {
}; };
this.assignedAgent = selfAssign; this.assignedAgent = selfAssign;
}, },
onClickAssignAgent(selectedItem) {
if (this.assignedAgent && this.assignedAgent.id === selectedItem.id) {
this.assignedAgent = null;
} else {
this.assignedAgent = selectedItem;
}
},
onClickAssignTeam(selectedItemTeam) {
if (this.assignedTeam && this.assignedTeam.id === selectedItemTeam.id) {
this.assignedTeam = null;
} else {
this.assignedTeam = selectedItemTeam;
}
},
}, },
}; };
</script> </script>

View file

@ -1,5 +1,5 @@
import { action } from '@storybook/addon-actions'; import { action } from '@storybook/addon-actions';
import Dropdown from './MutiselectDropdown'; import Dropdown from './MultiselectDropdown';
export default { export default {
title: 'Components/Dropdown/Multiselect Dropdown', title: 'Components/Dropdown/Multiselect Dropdown',

View file

@ -1,5 +1,9 @@
<template> <template>
<div v-on-clickaway="onCloseDropdown" class="selector-wrap"> <div
v-on-clickaway="onCloseDropdown"
class="selector-wrap"
@keyup.esc="onCloseDropdown"
>
<woot-button <woot-button
variant="hollow" variant="hollow"
color-scheme="secondary" color-scheme="secondary"

View file

@ -11,14 +11,19 @@
/> />
</div> </div>
<div class="list-scroll-container"> <div class="list-scroll-container">
<div class="dropdown-list"> <div
ref="multiselectDropdown"
class="multiselect-dropdown--list"
@keyup.up="onArrowUp"
@keyup.down="onArrowDown"
>
<woot-dropdown-menu> <woot-dropdown-menu>
<woot-dropdown-item <woot-dropdown-item
v-for="option in filteredOptions" v-for="option in filteredOptions"
:key="option.id" :key="option.id"
> >
<woot-button <woot-button
class="dropdown-item" class="multiselect-dropdown--item"
variant="clear" variant="clear"
:class="{ :class="{
active: option.id === (selectedItem && selectedItem.id), active: option.id === (selectedItem && selectedItem.id),
@ -102,17 +107,50 @@ export default {
return this.filteredOptions.length === 0 && this.search !== ''; return this.filteredOptions.length === 0 && this.search !== '';
}, },
}, },
mounted() { mounted() {
this.focusInput(); this.focusInput();
}, },
methods: { methods: {
onclick(option) { onclick(option) {
this.$emit('click', option); this.$emit('click', option);
}, },
focusInput() { focusInput() {
this.$refs.searchbar.focus(); this.$refs.searchbar.focus();
}, },
onArrowUp() {
const allDropdownItems = this.$refs.multiselectDropdown.querySelectorAll(
'.dropdown .multiselect-dropdown--item'
);
const focusedElement = this.$refs.multiselectDropdown.querySelector(
'.dropdown .multiselect-dropdown--item:focus'
);
const activeElementIndex = [...allDropdownItems].indexOf(focusedElement);
const lastElementIndex = allDropdownItems.length - 1;
if (activeElementIndex >= 1) {
allDropdownItems[activeElementIndex - 1].focus();
} else {
allDropdownItems[lastElementIndex].focus();
}
},
onArrowDown() {
const allDropdownItems = this.$refs.multiselectDropdown.querySelectorAll(
'.dropdown .multiselect-dropdown--item'
);
const focusedElement = this.$refs.multiselectDropdown.querySelector(
'.dropdown .multiselect-dropdown--item:focus'
);
const activeElementIndex = [...allDropdownItems].indexOf(focusedElement);
const lastElementIndex = allDropdownItems.length - 1;
if (activeElementIndex === lastElementIndex) {
allDropdownItems[0].focus();
} else {
allDropdownItems[activeElementIndex + 1].focus();
}
},
}, },
}; };
</script> </script>
@ -153,18 +191,24 @@ export default {
overflow: auto; overflow: auto;
} }
.dropdown-list { .multiselect-dropdown--list {
width: 100%; width: 100%;
max-height: 12rem; max-height: 12rem;
} }
.dropdown-item { .multiselect-dropdown--item {
justify-content: space-between; justify-content: space-between;
width: 100%; width: 100%;
&.active { &.active {
background-color: var(--w-50);
color: var(--w-900);
font-weight: var(--font-weight-bold); font-weight: var(--font-weight-bold);
} }
&:focus {
background-color: var(--color-background);
}
} }
.user-wrap { .user-wrap {