Share
Overview
The VBsbShare
component is a versatile social sharing button group that integrates various social media platforms such as Twitter, Facebook, LinkedIn, WhatsApp, and a copy-to-clipboard functionality. This component allows users to share content across different social networks with ease, offering customizable options for appearance, behavior, and supported platforms.
Usage Examples
Basic Example
<template>
<VBsbShare
:share="['twitter', 'facebook', 'linkedin', 'whatsapp', 'copy']"
:share-options="{ text: 'Check out this amazing content!', url: 'http://example.com' }"
:window-features="{ width: 600, height: 400, top: 100, left: 100 }"
/>
</template>
<script setup>
import VBsbShare from '@/components/VBsbShare.vue'
</script>
Customized Button Styles Example
You can customize the appearance of the buttons by providing different values for variant
, density
, and color
props.
<template>
<VBsbShare
:share="['twitter', 'facebook']"
:share-options="{ text: 'Learn more about our services', url: 'http://example.com' }"
:variant="'outlined'"
:density="'compact'"
:color="'primary'"
/>
</template>
<script setup>
import VBsbShare from '@/components/VBsbShare.vue'
</script>
Copy-to-Clipboard Only Example
You can enable only the copy-to-clipboard functionality without social sharing buttons by adjusting the share
prop.
<template>
<VBsbShare :share="['copy']" :share-options="{ text: 'Copy this important information!' }" />
</template>
<script setup>
import VBsbShare from '@/components/VBsbShare.vue'
</script>
API
Props
Prop | Type | Default | Description |
---|---|---|---|
share | Array<'twitter', 'facebook', 'linkedin', 'whatsapp', 'copy'> | ['twitter', 'facebook', 'linkedin', 'whatsapp', 'copy'] | Specifies the social media platforms to display as sharing options. |
windowFeatures | Object (of type WindowFeatures ) | { width: 600, height: 400, top: 100, left: 100 } | Specifies the features of the popup window when sharing (width, height, top, left position). |
shareOptions | Object (of type ShareOptions ) | { text: '', url: '', number: '' } | Defines options related to the shared content, such as the text, URL, and (for WhatsApp) a phone number. |
useNativeBehavior | Boolean | false | Determines whether to use the native behavior of the social share platform (when applicable). |
variant | 'flat' | 'text' | 'elevated' | 'tonal' | 'outlined' | 'plain' | undefined | Defines the style of the Vuetify buttons (e.g., flat, outlined, elevated). |
density | 'default' | 'comfortable' | 'compact' | undefined | Controls the density (size and spacing) of the buttons (default, comfortable, compact). |
color | String | undefined | Specifies the color of the buttons, which corresponds to Vuetify’s color schemes (e.g., primary, secondary, etc.). |
Emits
The component does not emit any custom events.
Exposes
The component does not expose any methods or variables to parent components.
Types
Share
export type Share = 'twitter' | 'facebook' | 'linkedin' | 'whatsapp' | 'copy'
This type defines the available social sharing platforms.
WindowFeatures
export type WindowFeatures = {
width: number
height: number
top: number
left: number
}
This type defines the attributes for controlling the pop-up window features when sharing on social media.
ShareOptions
export type ShareOptions = {
text?: string // Text to share
url: string // URL to share
via?: string // Optional: account to credit (for Twitter)
hashtags?: string[] // Optional: hashtags to include (for Twitter)
number: string // Optional: phone number (for WhatsApp)
quote?: string // Optional: quote to include (for Facebook)
}
This type defines the options related to the shared content, including text, URL, and platform-specific details (e.g., via
for Twitter, number
for WhatsApp).
Source
- Install Dependencies
npm install vue-socials
- Create component and unit test.
Component
<template>
<v-defaults-provider
:defaults="{ VBtn: { variant: props.variant, density: props.density, color: props.color } }"
>
<STwitter
v-if="props.share.includes('twitter')"
:window-features="windowFeatures"
:useNativeBehavior="useNativeBehavior"
:share-options="shareOptions"
>
<v-btn data-cy="v-bsb-share-twitter" icon="$mdiTwitter" />
</STwitter>
<SFacebook
v-if="props.share.includes('facebook')"
:window-features="windowFeatures"
:useNativeBehavior="useNativeBehavior"
:share-options="shareOptions"
>
<v-btn data-cy="v-bsb-share-facebook" icon="$mdiFacebook" />
</SFacebook>
<SLinkedIn
v-if="props.share.includes('linkedin')"
:window-features="windowFeatures"
:useNativeBehavior="useNativeBehavior"
:share-options="shareOptions"
>
<v-btn data-cy="v-bsb-share-linkedin" icon="$mdiLinkedin" />
</SLinkedIn>
<SWhatsApp
v-if="props.share.includes('whatsapp')"
:window-features="windowFeatures"
:useNativeBehavior="useNativeBehavior"
:share-options="shareOptions"
>
<v-btn data-cy="v-bsb-share-whatsapp" icon="$mdiWhatsapp" />
</SWhatsApp>
<v-btn
v-if="props.share.includes('copy')"
data-cy="v-bsb-share-copy"
icon="$mdiContentCopy"
@click="copyToClipboard"
/>
</v-defaults-provider>
</template>
<script setup lang="ts">
import { STwitter, SFacebook, SLinkedIn, SWhatsApp } from 'vue-socials'
export type Share = 'twitter' | 'facebook' | 'linkedin' | 'whatsapp' | 'copy'
export type WindowFeatures = {
width: number
height: number
top: number
left: number
}
export type ShareOptions = {
text?: string
url: string
via?: string
hashtags?: string[]
number: string
quote?: string
}
const props = defineProps({
share: {
type: Array as PropType<Share[]>,
default: () => ['twitter', 'facebook', 'linkedin', 'whatsapp', 'copy'],
},
windowFeatures: {
type: Object as PropType<WindowFeatures>,
default: () => ({ url: '' }),
},
shareOptions: {
type: Object as PropType<ShareOptions>,
default: () => ({ number: '' }),
},
useNativeBehavior: {
type: Boolean,
default: false,
},
variant: {
type: String as PropType<
'flat' | 'text' | 'elevated' | 'tonal' | 'outlined' | 'plain' | undefined
>,
default: undefined,
},
density: {
type: String as PropType<'default' | 'comfortable' | 'compact' | undefined>,
default: undefined,
},
color: {
type: String as PropType<string | undefined>,
default: undefined,
},
})
const copyToClipboard = () => {
if (navigator.clipboard) {
navigator.clipboard
.writeText(props.shareOptions.text || '')
.then(() => console.log('Copied to clipboard'))
.catch((err) => console.error('Error copying to clipboard', err))
} else {
const el = document.createElement('textarea')
el.value = props.shareOptions.text || ''
document.body.appendChild(el)
el.select()
document.execCommand('copy')
document.body.removeChild(el)
}
}
</script>
Test
import { mount, VueWrapper } from '@vue/test-utils'
import { describe, it, expect, beforeEach, vi } from 'vitest'
import VBsbShare from '../VBsbShare.vue'
import vuetify from '../../plugins/vuetify'
vi.mock('navigator.clipboard', () => ({
writeText: vi.fn(() => Promise.resolve()),
}))
describe('VBsbShare.vue', () => {
let wrapper: VueWrapper
beforeEach(() => {
wrapper = mount(VBsbShare, {
global: {
plugins: [vuetify],
},
props: {
share: ['twitter', 'facebook', 'linkedin', 'whatsapp', 'copy'],
windowFeatures: { width: 600, height: 400, top: 100, left: 100 },
shareOptions: { text: 'Share this text', url: 'http://example.com', number: '123' },
useNativeBehavior: false,
variant: 'flat',
density: 'default',
color: 'primary',
},
})
})
it('renders the correct number of share buttons', () => {
const buttons = wrapper.findAll('button')
expect(buttons.length).toBe(5)
})
it('renders the Twitter share button', () => {
const twitterBtn = wrapper.find('button[data-cy="v-bsb-share-twitter"]')
expect(twitterBtn.exists()).toBe(true)
})
it('renders the Facebook share button', () => {
const facebookBtn = wrapper.find('button[data-cy="v-bsb-share-facebook"]')
expect(facebookBtn.exists()).toBe(true)
})
it('renders the LinkedIn share button', () => {
const linkedInBtn = wrapper.find('button[data-cy="v-bsb-share-linkedin"]')
expect(linkedInBtn.exists()).toBe(true)
})
it('renders the WhatsApp share button', () => {
const whatsappBtn = wrapper.find('button[data-cy="v-bsb-share-whatsapp"]')
expect(whatsappBtn.exists()).toBe(true)
})
it('renders the Copy button', () => {
const copyBtn = wrapper.find('button[data-cy="v-bsb-share-copy"]')
expect(copyBtn.exists()).toBe(true)
})
it('does not render social buttons when they are not in the share prop', async () => {
await wrapper.setProps({ share: ['copy'] })
const twitterBtn = wrapper.find('button[data-cy="v-bsb-share-twitter"]')
const copyBtn = wrapper.find('button[data-cy="v-bsb-share-copy"]')
expect(twitterBtn.exists()).toBe(false)
expect(copyBtn.exists()).toBe(true)
})
})