Skip to content

Commit

Permalink
feat(b-avatar): add size classes for sm and lg sizes (closes #5592)…
Browse files Browse the repository at this point in the history
… (#5768)

* feat(b-avatar): add size classes for `sm` and `lg`

* Update avatar.js

* Update avatar.js

* Update avatar.js

* Update avatar-group.spec.js
  • Loading branch information
jacobmllr95 authored Sep 13, 2020
1 parent 7ec2205 commit 942bf31
Show file tree
Hide file tree
Showing 5 changed files with 85 additions and 39 deletions.
8 changes: 8 additions & 0 deletions src/_variables.scss
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,14 @@ $b-popover-bg-level: $alert-bg-level !default;
$b-popover-border-level: $alert-border-level !default;
$b-popover-color-level: $alert-color-level !default;

// --- Avatar ---

$b-avatar-size: 2.5rem !default;
$b-avatar-size-sm: 1.5rem !default;
$b-avatar-size-lg: 3.5rem !default;
$b-avatar-font-size-scale: 0.4 !default;
$b-avatar-badge-font-size-scale: $b-avatar-font-size-scale * 0.7 !default;

// --- Skeleton ---

$b-skeleton-background-color: rgba(0, 0, 0, 0.12) !default;
Expand Down
28 changes: 28 additions & 0 deletions src/components/avatar/_avatar.scss
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
align-items: center;
justify-content: center;
vertical-align: middle;
width: $b-avatar-size;
height: $b-avatar-size;
font-size: inherit;
font-weight: 400;
line-height: 1;
Expand Down Expand Up @@ -100,6 +102,32 @@
}
}

.b-avatar-sm {
width: $b-avatar-size-sm;
height: $b-avatar-size-sm;

.b-avatar-text {
font-size: calc(#{$b-avatar-size-sm * $b-avatar-font-size-scale});
}

.b-avatar-badge {
font-size: calc(#{$b-avatar-size-sm * $b-avatar-badge-font-size-scale});
}
}

.b-avatar-lg {
width: $b-avatar-size-lg;
height: $b-avatar-size-lg;

.b-avatar-text {
font-size: calc(#{$b-avatar-size-lg * $b-avatar-font-size-scale});
}

.b-avatar-badge {
font-size: calc(#{$b-avatar-size-lg * $b-avatar-badge-font-size-scale});
}
}

.b-avatar-group {
.b-avatar-group-inner {
display: flex;
Expand Down
16 changes: 16 additions & 0 deletions src/components/avatar/avatar-group.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,20 @@ describe('avatar-group', () => {

wrapper.destroy()
})

it('overlap props work', async () => {
const wrapper = mount(BAvatarGroup, {
propsData: {
overlap: 0.65
}
})

expect(wrapper.vm).toBeDefined()
await waitNT(wrapper.vm)

expect(wrapper.vm.overlap).toBe(0.65)
expect(wrapper.vm.overlapScale).toBe(0.325)

wrapper.destroy()
})
})
53 changes: 21 additions & 32 deletions src/components/avatar/avatar.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import Vue from '../../utils/vue'
import { getComponentConfig } from '../../utils/config'
import { isNumber, isString, isUndefinedOrNull } from '../../utils/inspect'
import { isNumber, isString } from '../../utils/inspect'
import { toFloat } from '../../utils/number'
import { omit } from '../../utils/object'
import { pluckProps } from '../../utils/props'
Expand All @@ -15,17 +15,13 @@ import normalizeSlotMixin from '../../mixins/normalize-slot'
const NAME = 'BAvatar'
const CLASS_NAME = 'b-avatar'

const SIZES = ['sm', null, 'lg']

const RX_NUMBER = /^[0-9]*\.?[0-9]+$/

const FONT_SIZE_SCALE = 0.4
const BADGE_FONT_SIZE_SCALE = FONT_SIZE_SCALE * 0.7

const DEFAULT_SIZES = {
sm: '1.5em',
md: '2.5em',
lg: '3.5em'
}

// --- Props ---
const linkProps = omit(BLinkProps, ['active', 'event', 'routerTag'])

Expand Down Expand Up @@ -99,18 +95,10 @@ const props = {

// --- Utility methods ---
export const computeSize = value => {
// Default to `md` size when `null`, or parse to
// number when value is a float-like string
value =
isUndefinedOrNull(value) || value === ''
? 'md'
: isString(value) && RX_NUMBER.test(value)
? toFloat(value, 0)
: value
// Parse to number when value is a float-like string
value = isString(value) && RX_NUMBER.test(value) ? toFloat(value, 0) : value
// Convert all numbers to pixel values
// Handle default sizes when `sm`, `md` or `lg`
// Or use value as is
return isNumber(value) ? `${value}px` : DEFAULT_SIZES[value] || value
return isNumber(value) ? `${value}px` : value || null
}

// --- Main component ---
Expand All @@ -130,36 +118,35 @@ export const BAvatar = /*#__PURE__*/ Vue.extend({
computed: {
computedSize() {
// Always use the avatar group size
return computeSize(this.bvAvatarGroup ? this.bvAvatarGroup.size : this.size)
const { bvAvatarGroup } = this
return computeSize(bvAvatarGroup ? bvAvatarGroup.size : this.size)
},
computedVariant() {
// Prefer avatar-group variant if provided
const avatarGroup = this.bvAvatarGroup
return avatarGroup && avatarGroup.variant ? avatarGroup.variant : this.variant
const { bvAvatarGroup } = this
return bvAvatarGroup && bvAvatarGroup.variant ? bvAvatarGroup.variant : this.variant
},
computedRounded() {
const avatarGroup = this.bvAvatarGroup
const square = avatarGroup && avatarGroup.square ? true : this.square
const rounded = avatarGroup && avatarGroup.rounded ? avatarGroup.rounded : this.rounded
const { bvAvatarGroup } = this
const square = bvAvatarGroup && bvAvatarGroup.square ? true : this.square
const rounded = bvAvatarGroup && bvAvatarGroup.rounded ? bvAvatarGroup.rounded : this.rounded
return square ? '0' : rounded === '' ? true : rounded || 'circle'
},
fontStyle() {
let fontSize = this.computedSize
fontSize = fontSize ? `calc(${fontSize} * ${FONT_SIZE_SCALE})` : null
const { computedSize: size } = this
const fontSize = SIZES.indexOf(size) === -1 ? `calc(${size} * ${FONT_SIZE_SCALE})` : null
return fontSize ? { fontSize } : {}
},
marginStyle() {
const avatarGroup = this.bvAvatarGroup
const overlapScale = avatarGroup ? avatarGroup.overlapScale : 0
const size = this.computedSize
const { computedSize: size, bvAvatarGroup } = this
const overlapScale = bvAvatarGroup ? bvAvatarGroup.overlapScale : 0
const value = size && overlapScale ? `calc(${size} * -${overlapScale})` : null
return value ? { marginLeft: value, marginRight: value } : {}
},
badgeStyle() {
const { computedSize: size, badgeTop, badgeLeft, badgeOffset } = this
const offset = badgeOffset || '0px'
return {
fontSize: size ? `calc(${size} * ${BADGE_FONT_SIZE_SCALE} )` : null,
fontSize: SIZES.indexOf(size) === -1 ? `calc(${size} * ${BADGE_FONT_SIZE_SCALE} )` : null,
top: badgeTop ? offset : null,
bottom: badgeTop ? null : offset,
left: badgeLeft ? offset : null,
Expand Down Expand Up @@ -246,6 +233,8 @@ export const BAvatar = /*#__PURE__*/ Vue.extend({
const componentData = {
staticClass: CLASS_NAME,
class: {
// Apply size class
[`${CLASS_NAME}-${size}`]: size && SIZES.indexOf(size) !== -1,
// We use badge styles for theme variants when not rendering `BButton`
[`badge-${variant}`]: !button && variant,
// Rounding/Square
Expand All @@ -254,7 +243,7 @@ export const BAvatar = /*#__PURE__*/ Vue.extend({
// Other classes
disabled
},
style: { width: size, height: size, ...marginStyle },
style: { ...marginStyle, width: size, height: size },
attrs: { 'aria-label': ariaLabel || null },
props: button ? { variant, disabled, type } : link ? pluckProps(linkProps, this) : {},
on: button || link ? { click: this.onClick } : {}
Expand Down
19 changes: 12 additions & 7 deletions src/components/avatar/avatar.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -180,19 +180,22 @@ describe('avatar', () => {

it('`size` prop should work as expected', async () => {
const wrapper1 = mount(BAvatar)
expect(wrapper1.attributes('style')).toEqual('width: 2.5em; height: 2.5em;')
expect(wrapper1.attributes('style')).toEqual(undefined)
wrapper1.destroy()

const wrapper2 = mount(BAvatar, { propsData: { size: 'sm' } })
expect(wrapper2.attributes('style')).toEqual('width: 1.5em; height: 1.5em;')
expect(wrapper2.attributes('style')).toEqual(undefined)
expect(wrapper2.classes()).toContain('b-avatar-sm')
wrapper2.destroy()

const wrapper3 = mount(BAvatar, { propsData: { size: 'md' } })
expect(wrapper3.attributes('style')).toEqual('width: 2.5em; height: 2.5em;')
expect(wrapper3.attributes('style')).toEqual(undefined)
expect(wrapper3.classes()).not.toContain('b-avatar-md')
wrapper3.destroy()

const wrapper4 = mount(BAvatar, { propsData: { size: 'lg' } })
expect(wrapper4.attributes('style')).toEqual('width: 3.5em; height: 3.5em;')
expect(wrapper4.attributes('style')).toEqual(undefined)
expect(wrapper4.classes()).toContain('b-avatar-lg')
wrapper4.destroy()

const wrapper5 = mount(BAvatar, { propsData: { size: 20 } })
Expand Down Expand Up @@ -255,7 +258,8 @@ describe('avatar', () => {
expect(wrapper1.element.tagName).toBe('SPAN')
expect(wrapper1.classes()).toContain('b-avatar')
expect(wrapper1.classes()).toContain('badge-secondary')
expect(wrapper1.attributes('style')).toContain('width: 2.5em; height: 2.5em;')
// Uses avatar group size (default)
expect(wrapper1.attributes('style')).toBe(undefined)

wrapper1.destroy()

Expand All @@ -272,7 +276,8 @@ describe('avatar', () => {
expect(wrapper2.classes()).toContain('b-avatar')
expect(wrapper2.classes()).toContain('badge-danger')
expect(wrapper2.classes()).not.toContain('badge-secondary')
expect(wrapper2.attributes('style')).toContain('width: 2.5em; height: 2.5em;')
// Uses avatar group size (default)
expect(wrapper2.attributes('style')).toBe(undefined)

wrapper2.destroy()
})
Expand All @@ -293,7 +298,7 @@ describe('avatar', () => {
expect(wrapper1.classes()).toContain('b-avatar')
expect(wrapper1.classes()).toContain('badge-secondary')
// Uses avatar group size (default)
expect(wrapper1.attributes('style')).toContain('width: 2.5em; height: 2.5em;')
expect(wrapper1.attributes('style')).toBe(undefined)

wrapper1.destroy()

Expand Down

0 comments on commit 942bf31

Please sign in to comment.