Skip to content

Instantly share code, notes, and snippets.

@vicainelli
Last active August 27, 2021 10:44
Show Gist options
  • Save vicainelli/ab6e34a91616a8f9e597bccb24ec8e81 to your computer and use it in GitHub Desktop.
Save vicainelli/ab6e34a91616a8f9e597bccb24ec8e81 to your computer and use it in GitHub Desktop.
Toast Component
// tailwind.config.js
module.exports = {
purge: [],
darkMode: false, // or 'media' or 'class'
theme: {
extend: {},
},
variants: {
extend: {
boxShadow: {
toast: '0px 8px 16px 0px #40404014',
},
transitionTimingFunction: {
'in-toast': 'cubic-bezier(0.7, 0, 0.84, 0)',
'out-toast': 'cubic-bezier(0.16, 1, 0.3, 1)',
'in-out-toast': 'cubic-bezier(0.87, 0, 0.13, 1)',
},
},
},
plugins: [],
}
import { mount } from '@cypress/vue';
import Toast from '@/components/toast';
import '@/assets/css/style.css';
const toastTemplate = {
template: `
<div class="w-screen h-screen bg-grey-200">
<div class="text-center pt-8">
<button @click="$refs.toast.success({ title: 'New action, pushing the previous one', description: 'Description for the action, containing links' })">Success</button>
<button @click="$refs.toast.error({ title: 'Action failed', description: 'Sorry, your action didn’t come through' })">Error</button>
<button @click="$refs.toast.info({ title: 'Action performed', description: 'Description for the action that spans up to two lines, due to extensive text' })">Info</button>
</div>
<Toast ref="toast" />
</div>`,
components: { Toast },
};
describe('Toast UI', () => {
it('should have correct notifications', () => {
mount(toastTemplate);
cy.get('button').contains(/success/i).click();
cy.get('div').contains(/New action, pushing the previous one/i).should('be.visible');
cy.get('button').contains(/error/i).click();
cy.get('button').contains(/info/i).click();
});
});
import Toast from '@/components/toast';
export default {
title: 'Components / Toast',
excludeStories: /.*Data$/,
};
export const Default = () => ({
components: { Toast },
template: `
<div>
<div class="text-center pt-8">
<button @click="$refs.toast.success({ title: 'New action, pushing the previous one', description: 'Description for the action, containing links' })">Success</button>
<button @click="$refs.toast.error({ title: 'Action failed', description: 'Sorry, your action didn’t come through' })">Error</button>
<button @click="$refs.toast.info({ title: 'Action performed', description: 'Description for the action that spans up to two lines, due to extensive text' })">Info</button>
</div>
<Toast ref="toast" />
</div>
`,
});
<template>
<div class="fixed z-40 w-full bottom-0 left-0">
<transition-group
tag="div"
enter-active-class="transition ease-out-toast duration-200"
leave-active-class="transition ease-in-toast duration-200 absolute w-full"
enter-class="transform translate-y-3 opacity-0"
enter-to-class="transform translate-y-0 opacity-100"
leave-class="transform translate-y-0 opacity-100"
leave-to-class="transform translate-y-1/4 opacity-0"
move-class="ease-in-out-toast duration-200"
class="grid grid-cols-1 gap-5 p-5"
>
<div v-for="event in events" :key="event.id">
<div
:data-testid="`toast_${event.id}`"
class="font-sans w-full max-w-col-4 bg-white shadow-toast rounded-lg p-4 flex justify-between items-start"
>
<div class="w-full flex-1">
<div>
<div class="w-full">
<div>
<div class="flex items-center justify-between">
<h3
class="font-bold text-xs text-grey-700"
:class="[
{
'text-green-500': event.type && event.type === 'success',
'text-red-500': event.type && event.type === 'error',
},
]"
>
{{ event.content.title }}
</h3>
<button @click="remove(event)">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-x"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg>
</button>
</div>
</div>
<p v-if="event.content.description !== ''" class="text-xs text-grey-700">
{{ event.content.description }}
</p>
</div>
</div>
</div>
</div>
</div>
</transition-group>
</div>
</template>
<script>
export default {
name: 'v-toast',
props: {
duration: {
type: Number,
default: 5000,
},
},
data() {
return {
events: [],
};
},
methods: {
success(content) {
this.add(content, 'success');
},
info(content) {
this.add(content, 'info');
},
error(content) {
this.add(content, 'error');
},
add(content, type = null) {
const event = {
id: Math.random()
.toString(36),
content,
type,
};
this.events.push(event);
this.setTimer(event);
},
remove(event) {
this.events = this.events.filter((e) => e.id !== event.id);
},
setTimer(event) {
setTimeout(() => {
this.remove(event);
}, this.duration);
},
},
};
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment