Skip to content

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

vue
<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.

vue
<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.

vue
<template>
  <VBsbShare :share="['copy']" :share-options="{ text: 'Copy this important information!' }" />
</template>

<script setup>
import VBsbShare from '@/components/VBsbShare.vue'
</script>

API

Props

PropTypeDefaultDescription
shareArray<'twitter', 'facebook', 'linkedin', 'whatsapp', 'copy'>['twitter', 'facebook', 'linkedin', 'whatsapp', 'copy']Specifies the social media platforms to display as sharing options.
windowFeaturesObject (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).
shareOptionsObject (of type ShareOptions){ text: '', url: '', number: '' }Defines options related to the shared content, such as the text, URL, and (for WhatsApp) a phone number.
useNativeBehaviorBooleanfalseDetermines whether to use the native behavior of the social share platform (when applicable).
variant'flat' | 'text' | 'elevated' | 'tonal' | 'outlined' | 'plain'undefinedDefines the style of the Vuetify buttons (e.g., flat, outlined, elevated).
density'default' | 'comfortable' | 'compact'undefinedControls the density (size and spacing) of the buttons (default, comfortable, compact).
colorStringundefinedSpecifies 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

ts
export type Share = 'twitter' | 'facebook' | 'linkedin' | 'whatsapp' | 'copy'

This type defines the available social sharing platforms.

WindowFeatures

ts
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

ts
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

  1. Install Dependencies
ps
npm install vue-socials
  1. Create component and unit test.
Component
vue
<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
ts
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)
  })
})