Skip to content

Commit

Permalink
fix: update snackbar behavior -> remove stackable behavior
Browse files Browse the repository at this point in the history
  • Loading branch information
Mike Lebeau committed Oct 20, 2023
1 parent 6a6eeae commit ab24d57
Show file tree
Hide file tree
Showing 6 changed files with 78 additions and 44 deletions.
70 changes: 28 additions & 42 deletions packages/components/snackbar/src/notify.ts
Original file line number Diff line number Diff line change
@@ -1,65 +1,51 @@
import { createVNode, render, type AppContext, type VNode } from 'vue'
import {
createVNode,
render,
type AppContext,
type VNode,
ref,
type Ref,
} from 'vue'
import Snackbar from './snackbar.vue'
import type { PuikSnackbarOptions } from './snackbar'

const notifications: VNode[] = []

const GAP = 16

let seed = 1
const currentNotification: Ref<VNode | null> = ref(null)
const PUIK_SNACKBAR_ID = 'puik-snackbar-id'

const notify = (
options: PuikSnackbarOptions,
context: AppContext | null = null
) => {
const id = `puik-snackbar_${seed++}`
const customOnClose = options.onClose
let offset = options.offset || 32
const offset = options.offset || 32

notifications.forEach(({ el }) => {
offset += (el?.offsetHeight || 0) + GAP
})
const appendTo: HTMLElement | null = document.body
const documentBody: HTMLElement | null = document.body

const props = {
...options,
offset,
id,
onClose: () => close(id, customOnClose),
id: PUIK_SNACKBAR_ID,
onClose: () => {
currentNotification.value = null
return customOnClose
},
}

const notification = createVNode(Snackbar, props)
notification.appContext = context ?? notify._context
const container = document.createElement('div')

notification.props!.onDestroy = () => render(null, container)

render(notification, container)
notifications.push(notification)
appendTo.appendChild(container.firstElementChild!)

const close = (id: string, customClose?: () => void) => {
const idx = notifications.findIndex(({ props }) => props?.id === id)
if (idx === -1) return
const newNotification = createVNode(Snackbar, props)
newNotification.appContext = context ?? notify._context

const { el } = notifications[idx]
if (!el) return

customClose?.()

const removedHeight = el?.offsetHeight

notifications.splice(idx, 1)
const len = notifications.length
const container = document.createElement('div')

if (len < 1) return
newNotification.props!.onDestroy = () => render(null, container)
render(newNotification, container)
documentBody.appendChild(container.firstElementChild!)

for (let i = idx; i < len; i++) {
const { el, component } = notifications[i]
const pos = parseInt(el?.style.bottom, 10) - removedHeight - GAP
component!.props.offset = pos
}
if (currentNotification.value) {
const curNot = document.getElementById(PUIK_SNACKBAR_ID)
curNot?.remove()
}

currentNotification.value = newNotification
}

notify._context = null
Expand Down
6 changes: 6 additions & 0 deletions packages/components/snackbar/src/snackbar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export interface PuikSnackbarOptions {
action?: SnackbarAction
duration?: number
offset?: number
hasCloseButton?: boolean
onClose?: () => void
}

Expand Down Expand Up @@ -49,6 +50,11 @@ export const snackbarProps = buildProps({
required: false,
default: 0,
},
hasCloseButton: {
type: Boolean,
required: false,
default: true,
},
onClose: {
type: Function,
required: false,
Expand Down
4 changes: 4 additions & 0 deletions packages/components/snackbar/src/snackbar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
>
<div
v-show="visible"
:id="props.id"
class="puik-snackbar"
:class="`puik-snackbar--${variant}`"
:style="position"
Expand All @@ -24,6 +25,7 @@
{{ action.label }}
</button>
<button
v-if="hasCloseButton"
class="puik-snackbar__close-button"
:aria-label="t('puik.snackbar.closeBtnLabel')"
@click="close"
Expand All @@ -40,9 +42,11 @@ import { useTimeoutFn, useEventListener } from '@vueuse/core'
import { useLocale } from '@puik/hooks'
import { snackbarProps } from './snackbar'
import type { CSSProperties } from 'vue'
defineOptions({
name: 'PuikSnackbar',
})
const { t } = useLocale()
let timer: (() => void) | undefined
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ interface PuikSnackbarOptions {
action?: SnackbarAction
duration?: number
offset?: number
hasCloseButton?: boolean
onClose?: () => void
}
```
Expand Down
31 changes: 31 additions & 0 deletions packages/components/snackbar/stories/snackbar.stories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,18 @@ export default {
},
},
},
isClosable: {
description: 'Display a close button or not',
control: 'boolean',
table: {
type: {
summary: 'boolean',
},
defaultValue: {
summary: false,
},
},
},
onClose: {
description: 'Set the function to call when the snackbar has been closed',
table: {
Expand Down Expand Up @@ -105,6 +117,25 @@ export const Default = {
},
}

const HasNoCloseButtonTemplate: StoryFn = (args: Args) => ({
components: {
PuikButton,
},
setup() {
const open = () => PuikSnackbar(args as PuikSnackbarOptions)
return { args, open }
},
template: `<puik-button @click="open">Display Snackbar</puik-button>`,
})

export const HasNoCloseButton = {
render: HasNoCloseButtonTemplate,
args: {
text: 'New category added.',
hasCloseButton: false,
},
}

const WithoutActionTemplate: StoryFn = () => ({
components: {
PuikButton,
Expand Down
10 changes: 8 additions & 2 deletions packages/components/snackbar/test/snackbar.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { mount } from '@vue/test-utils'
import { describe, it, expect, vi } from 'vitest'
import { faker } from '@faker-js/faker'
import PuikSnackbar from '../src/snackbar.vue'
import type { SnackbarOptions } from '../src/snackbar'
import type { PuikSnackbarOptions } from '../src/snackbar'
import type { MountingOptions, VueWrapper } from '@vue/test-utils'
describe('Snackbar tests', () => {
let wrapper: VueWrapper<any>
Expand All @@ -13,7 +13,7 @@ describe('Snackbar tests', () => {
const findCloseButton = () => wrapper.find('.puik-snackbar__close-button')

const factory = (
propsData: SnackbarOptions,
propsData: PuikSnackbarOptions,
options: MountingOptions<any> = {}
) => {
wrapper = mount(PuikSnackbar, {
Expand All @@ -39,6 +39,12 @@ describe('Snackbar tests', () => {
expect(findAction().exists()).toBeFalsy()
})

it('should be a snackbar without close button', () => {
const text = faker.lorem.sentence()
factory({ text, hasCloseButton: false })
expect(findCloseButton().exists()).toBeFalsy()
})

it('should be a default snackbar with action', async () => {
const text = faker.lorem.sentence()
const label = faker.lorem.word()
Expand Down

0 comments on commit ab24d57

Please sign in to comment.