diff --git a/apps/dialtone-documentation/docs/components/avatar.md b/apps/dialtone-documentation/docs/components/avatar.md index cb011ef279..0cde67071e 100644 --- a/apps/dialtone-documentation/docs/components/avatar.md +++ b/apps/dialtone-documentation/docs/components/avatar.md @@ -9,18 +9,22 @@ figma_url: https://www.figma.com/file/2adf7JhZOncRyjYiy2joil/DT-Core%3A-Componen --- -
- - - -
+
+ + + + + +
## Usage -The Avatar component is designed to prioritize different sources for content display. It will sequentially check for the availability of an image source (`image-src`) or an icon name (`icon-name`). If both properties are not provided, the avatar will extract and display initials from the full name (`full-name`). The resulting initials are extracted using the following logic: +The Avatar component is designed to prioritize different sources for content display. It will sequentially check for the availability of an image source (`image-src`) or content through the icon slot. If both are not provided, the avatar will extract and display initials from the full name (`full-name`). The resulting initials are extracted using the following logic: * If the string contains two or more words, the result will be the first character of the first and last word capitalized. E.g.: `full-name: "Jaqueline Nackos"` will result in: `JN`. @@ -45,7 +49,11 @@ The Avatar component is designed to prioritize different sources for content dis - + + + Icon When no username can be associated with the Avatar. @@ -79,7 +87,11 @@ The Avatar component is designed to prioritize different sources for content dis ### Icon - + + + @@ -150,7 +164,11 @@ vueCode='
- + + +
@@ -183,11 +201,31 @@ htmlCode=' ' vueCode=' - - - - - + + + + + + + + + + + + + + + ' /> @@ -273,7 +311,11 @@ vueCode='
- + + +
@@ -295,7 +337,11 @@ htmlCode=' ' vueCode=' - + + + ' /> @@ -306,14 +352,22 @@ If you need to create a clickable avatar you can set the clickable prop. This wi
- + + +
@@ -355,6 +409,8 @@ to [WCAG](https://www.w3.org/WAI/tutorials/images/decorative) references for your specific usage. diff --git a/packages/dialtone-vue2/components/avatar/avatar.test.js b/packages/dialtone-vue2/components/avatar/avatar.test.js index 771aa2508d..96f3026e06 100644 --- a/packages/dialtone-vue2/components/avatar/avatar.test.js +++ b/packages/dialtone-vue2/components/avatar/avatar.test.js @@ -1,4 +1,5 @@ import { createLocalVue, mount } from '@vue/test-utils'; +import { DtIconUser } from '@dialpad/dialtone-icons/vue2'; import DtAvatar from './avatar.vue'; import { AVATAR_KIND_MODIFIERS, AVATAR_SIZE_MODIFIERS } from './avatar_constants'; @@ -9,6 +10,7 @@ const MOCK_INITIALS = 'JN'; const MOCK_SIZE = 'lg'; const MOCK_GROUP = 25; const MOCK_CUSTOM_CLASS = 'my-custom-class'; +const MOCK_ICON_SLOT = ''; let MOCK_ELEMENT = null; const baseProps = { @@ -20,23 +22,28 @@ const baseListeners = {}; let mockProps = {}; let mockListeners = {}; const testContext = {}; +let mockSlots = {}; describe('DtAvatar Tests', () => { let wrapper; let image; let count; let presence; + let iconWrapper; const updateWrapper = () => { wrapper = mount(DtAvatar, { propsData: { ...baseProps, ...mockProps }, listeners: { ...baseListeners, ...mockListeners }, localVue: testContext.localVue, + slots: { ...mockSlots }, + components: { DtIconUser }, }); image = wrapper.find('[data-qa="dt-avatar-image"]'); count = wrapper.find('[data-qa="dt-avatar-count"]'); presence = wrapper.find('[data-qa="dt-presence"]'); + iconWrapper = wrapper.find('[data-qa="dt-avatar-icon"]'); }; beforeAll(() => { @@ -50,6 +57,7 @@ describe('DtAvatar Tests', () => { afterEach(() => { mockProps = {}; mockListeners = {}; + mockSlots = {}; }); describe('Presentation Tests', () => { @@ -83,19 +91,23 @@ describe('DtAvatar Tests', () => { }); }); - describe('When the iconName is provided', () => { + describe('When the icon slot is provided', () => { beforeEach(() => { - mockProps = { iconName: 'accessibility' }; + mockSlots = { icon: MOCK_ICON_SLOT }; updateWrapper(); }); it('icon should exist', () => { - expect(wrapper.find('svg').exists()).toBeTruthy(); + expect(iconWrapper.exists()).toBeTruthy(); }); it('should have correct class', () => { - expect(wrapper.find('svg').classes(AVATAR_KIND_MODIFIERS.icon)).toBe(true); + expect(iconWrapper.classes(AVATAR_KIND_MODIFIERS.icon)).toBe(true); + }); + + it('should render the custom icon', () => { + expect(iconWrapper.findComponent(DtIconUser).exists()).toBe(true); }); }); diff --git a/packages/dialtone-vue2/components/avatar/avatar.vue b/packages/dialtone-vue2/components/avatar/avatar.vue index 2913e9d65d..af1b388dc8 100644 --- a/packages/dialtone-vue2/components/avatar/avatar.vue +++ b/packages/dialtone-vue2/components/avatar/avatar.vue @@ -22,14 +22,19 @@ :src="imageSrc" :alt="imageAlt" > - + :aria-label="clickable ? iconAriaLabel : ''" + :data-qa="iconDataQa" + :role="clickable ? 'button' : ''" + > + + +
- +

import { getUniqueString, getRandomElement } from '@/common/utils'; import { DtPresence } from '../presence'; -import { DtIcon } from '@/components/icon'; import { AVATAR_KIND_MODIFIERS, AVATAR_SIZE_MODIFIERS, @@ -84,19 +88,16 @@ import { AVATAR_GROUP_VALIDATOR, AVATAR_ICON_SIZES, } from './avatar_constants'; -import { getIconNames } from '@/common/storybook_utils.js'; import { ICON_SIZE_MODIFIERS } from '@/components/icon/icon_constants.js'; import { extractInitialsFromName } from './utils'; -const ICONS_LIST = getIconNames(); - /** * An avatar is a visual representation of a user or object. * @see https://dialtone.dialpad.com/components/avatar.html */ export default { name: 'DtAvatar', - components: { DtPresence, DtIcon }, + components: { DtPresence }, inheritAttrs: false, @@ -194,14 +195,6 @@ export default { validator: (group) => AVATAR_GROUP_VALIDATOR(group), }, - /** - * The icon that overlays the avatar - */ - overlayIcon: { - type: String, - default: '', - }, - /** * The text that overlays the avatar */ @@ -236,15 +229,6 @@ export default { default: undefined, }, - /** - * Icon name to be displayed on the avatar - */ - iconName: { - type: String, - default: undefined, - validator: (name) => ICONS_LIST.includes(name), - }, - /** * Icon size to be displayed on the avatar * @values 100, 200, 300, 400, 500, 600, 700, 800 @@ -305,8 +289,16 @@ export default { }, computed: { - isNotIconType () { - return !this.iconName; + isIconType () { + return this.$scopedSlots.icon && this.$scopedSlots.icon(); + }, + + hasOverlayIcon () { + return !!this.$slots.overlayIcon; + }, + + iconDataQa () { + return 'dt-avatar-icon'; }, avatarClasses () { @@ -316,7 +308,7 @@ export default { this.avatarClass, { 'd-avatar--group': this.showGroup, - [`d-avatar--color-${this.getColor()}`]: this.isNotIconType, + [`d-avatar--color-${this.getColor()}`]: !this.isIconType, 'd-avatar--clickable': this.clickable, }, ]; @@ -326,6 +318,7 @@ export default { return [ 'd-avatar__overlay', this.overlayClass, + { 'd-avatar__overlay-icon': this.hasOverlayIcon }, ]; }, diff --git a/packages/dialtone-vue2/components/avatar/avatar_variants.story.vue b/packages/dialtone-vue2/components/avatar/avatar_variants.story.vue index b2ac1db739..cc07088d20 100644 --- a/packages/dialtone-vue2/components/avatar/avatar_variants.story.vue +++ b/packages/dialtone-vue2/components/avatar/avatar_variants.story.vue @@ -35,8 +35,11 @@ :seed="$attrs.seed" :size="size" full-name="Avatar Icon" - icon-name="user" - /> + > + +

@@ -64,7 +67,11 @@ :image-src="$attrs.imageSrc" :image-alt="$attrs.imageAlt" overlay-icon="hear" - /> + > + + + > + + + > + + diff --git a/packages/dialtone-vue2/recipes/item_layout/contact_info/contact_info_variants.story.vue b/packages/dialtone-vue2/recipes/item_layout/contact_info/contact_info_variants.story.vue index feb71be5cc..0b743ce3a3 100644 --- a/packages/dialtone-vue2/recipes/item_layout/contact_info/contact_info_variants.story.vue +++ b/packages/dialtone-vue2/recipes/item_layout/contact_info/contact_info_variants.story.vue @@ -6,8 +6,16 @@

+