This repository has been archived by the owner on Sep 30, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: introduces Notification component (#207)
- Loading branch information
Showing
8 changed files
with
386 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
// https://docs.cypress.io/api/introduction/api.html | ||
|
||
describe('notification', () => { | ||
beforeEach(() => { | ||
cy.visit('/notification'); | ||
}); | ||
|
||
it('toggles through button', () => { | ||
cy.get('[data-cy="content"]').should('not.exist'); | ||
cy.get('[data-cy="visibility-toggle"]').click(); | ||
cy.get('[data-cy="content"]').should('exist'); | ||
cy.get('[data-cy="content"]').should('contain.text', 'This is a notification'); | ||
cy.get('[data-cy="visibility-toggle"]').click(); | ||
cy.get('[data-cy="content"]').should('not.exist'); | ||
}); | ||
|
||
it('dismisses by click', () => { | ||
cy.get('[data-cy="content"]').should('not.exist'); | ||
cy.get('[data-cy="visibility-toggle"]').click(); | ||
cy.get('[data-cy="content"]').should('exist'); | ||
cy.get('[data-cy="content"]').click(); | ||
cy.get('[data-cy="content"]').should('not.exist'); | ||
}); | ||
|
||
it('does not dismisses by click if timeout is negative', () => { | ||
cy.get('[data-cy="timeout"]').type('-1'); | ||
cy.get('[data-cy="content"]').should('not.exist'); | ||
cy.get('[data-cy="visibility-toggle"]').click(); | ||
cy.get('[data-cy="content"]').should('exist'); | ||
cy.get('[data-cy="content"]').click(); | ||
cy.get('[data-cy="content"]').should('exist'); | ||
}); | ||
|
||
it('auto dismisses on timeout', () => { | ||
cy.get('[data-cy="timeout"]').type('100'); | ||
cy.get('[data-cy="content"]').should('not.exist'); | ||
cy.get('[data-cy="visibility-toggle"]').click(); | ||
cy.get('[data-cy="content"]').should('exist'); | ||
cy.get('[data-cy="content"]').should('not.exist'); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
<script setup lang='ts'> | ||
import { | ||
RuiButton, | ||
RuiMenuSelect, | ||
RuiNotification, | ||
RuiTextField, | ||
} from '@rotki/ui-library-compat'; | ||
import { ref } from 'vue'; | ||
const visible = ref(false); | ||
const timeout = ref(0); | ||
const theme = ref<'light' | 'dark'>(); | ||
const options = ['light', 'dark']; | ||
</script> | ||
|
||
<template> | ||
<div> | ||
<h2 | ||
class="text-h4 mb-6" | ||
data-cy="notification" | ||
> | ||
Notification | ||
</h2> | ||
<div> | ||
<RuiButton | ||
data-cy="visibility-toggle" | ||
@click="visible = !visible" | ||
> | ||
{{ visible ? 'Hide' : 'Show' }} | ||
</RuiButton> | ||
<RuiTextField | ||
v-model="timeout" | ||
type="number" | ||
variant="outlined" | ||
label="timeout" | ||
class="mt-4" | ||
data-cy="timeout" | ||
/> | ||
<RuiMenuSelect | ||
v-model="theme" | ||
:options="options" | ||
label="theme" | ||
variant="outlined" | ||
clearable | ||
data-cy="menu" | ||
/> | ||
</div> | ||
|
||
<RuiNotification | ||
v-model="visible" | ||
:timeout="timeout" | ||
:theme="theme" | ||
> | ||
<div | ||
class="m-4" | ||
data-cy="content" | ||
> | ||
This is a notification | ||
</div> | ||
</RuiNotification> | ||
</div> | ||
</template> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
103 changes: 103 additions & 0 deletions
103
src/components/overlays/notification/Notification.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
import Vue from 'vue'; | ||
import { mount } from '@vue/test-utils'; | ||
import { describe, expect, it, vi } from 'vitest'; | ||
import { TeleportPlugin } from '@/components/overlays/teleport-container'; | ||
import Notification from '@/components/overlays/notification/Notification.vue'; | ||
|
||
Vue.use(TeleportPlugin); | ||
|
||
function createWrapper(options?: any) { | ||
return mount(Notification, { | ||
...options, | ||
scopedSlots: { | ||
default: `<div id="content">Notification</div>`, | ||
}, | ||
stubs: { | ||
Transition: true, | ||
}, | ||
}); | ||
} | ||
|
||
describe('notification', () => { | ||
it('renders properly', async () => { | ||
const wrapper = createWrapper({ | ||
propsData: { | ||
timeout: 0, | ||
value: true, | ||
}, | ||
}); | ||
|
||
await nextTick(); | ||
const notification = document.body.querySelector('#content') as HTMLDivElement; | ||
expect(notification).toBeTruthy(); | ||
wrapper.destroy(); | ||
}); | ||
|
||
it('does not render if value is false', async () => { | ||
const wrapper = createWrapper({ | ||
propsData: { | ||
timeout: 0, | ||
value: false, | ||
}, | ||
}); | ||
|
||
await nextTick(); | ||
const notification = document.body.querySelector('#content') as HTMLDivElement; | ||
expect(notification).toBeFalsy(); | ||
wrapper.destroy(); | ||
}); | ||
|
||
it('closes on click', async () => { | ||
const wrapper = createWrapper({ | ||
propsData: { | ||
timeout: 0, | ||
value: true, | ||
}, | ||
}); | ||
|
||
await nextTick(); | ||
const notification = document.body.querySelector('#content') as HTMLDivElement; | ||
expect(notification).toBeTruthy(); | ||
notification.click(); | ||
await nextTick(); | ||
expect(wrapper.emitted()).toHaveProperty('input', [[false]]); | ||
wrapper.destroy(); | ||
}); | ||
|
||
it('does not close on click if timeout is negative', async () => { | ||
const wrapper = createWrapper({ | ||
propsData: { | ||
timeout: -1, | ||
value: true, | ||
}, | ||
}); | ||
|
||
await nextTick(); | ||
const notification = document.body.querySelector('#content') as HTMLDivElement; | ||
expect(notification).toBeTruthy(); | ||
notification.click(); | ||
await nextTick(); | ||
expect(wrapper.emitted()).toEqual({}); | ||
wrapper.destroy(); | ||
}); | ||
|
||
it('closes automatically after timeout', async () => { | ||
vi.useFakeTimers(); | ||
const wrapper = createWrapper({ | ||
propsData: { | ||
timeout: 5000, | ||
value: true, | ||
}, | ||
}); | ||
|
||
await nextTick(); | ||
vi.advanceTimersByTime(3000); | ||
const notification = document.body.querySelector('#content') as HTMLDivElement; | ||
expect(notification).toBeTruthy(); | ||
expect(wrapper.emitted()).toEqual({}); | ||
vi.advanceTimersByTime(2000); | ||
expect(wrapper.emitted()).toHaveProperty('input', [[false]]); | ||
wrapper.destroy(); | ||
vi.useRealTimers(); | ||
}); | ||
}); |
90 changes: 90 additions & 0 deletions
90
src/components/overlays/notification/Notification.stories.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
import Button from '@/components/buttons/button/Button.vue'; | ||
import Card from '@/components/cards/Card.vue'; | ||
import Notification, { type NotificationProps } from '@/components/overlays/notification/Notification.vue'; | ||
import type { Meta, StoryFn, StoryObj } from '@storybook/vue'; | ||
|
||
const render: StoryFn<NotificationProps> = args => ({ | ||
components: { Button, Card, Notification }, | ||
setup() { | ||
const value = computed({ | ||
get() { | ||
return args.value; | ||
}, | ||
set(val) { | ||
args.value = val; | ||
}, | ||
}); | ||
|
||
return { args, value }; | ||
}, | ||
template: ` | ||
<div> | ||
<Button @click="value = !value"> Click </Button> | ||
<Notification v-bind="args" v-model='value'> | ||
<Card>I am a notification</Card> | ||
</Notification> | ||
</div> | ||
`, | ||
}); | ||
|
||
const meta: Meta<NotificationProps> = { | ||
args: {}, | ||
argTypes: { | ||
theme: { | ||
control: 'select', | ||
options: ['light', 'dark'], | ||
}, | ||
timeout: { control: 'number' }, | ||
width: { control: 'text' }, | ||
}, | ||
component: Notification, | ||
parameters: { | ||
docs: { | ||
controls: { exclude: ['default'] }, | ||
}, | ||
}, | ||
render, | ||
tags: ['autodocs'], | ||
title: 'Components/Overlays/Notification', | ||
}; | ||
|
||
type Story = StoryObj<NotificationProps>; | ||
|
||
export const Default: Story = { | ||
args: { | ||
timeout: 0, | ||
value: false, | ||
}, | ||
}; | ||
|
||
export const NonPersistent: Story = { | ||
args: { | ||
timeout: 0, | ||
value: false, | ||
}, | ||
}; | ||
|
||
export const Light: Story = { | ||
args: { | ||
theme: 'light', | ||
timeout: 0, | ||
value: false, | ||
}, | ||
}; | ||
|
||
export const Dark: Story = { | ||
args: { | ||
theme: 'dark', | ||
timeout: 0, | ||
value: false, | ||
}, | ||
}; | ||
|
||
export const Persistent: Story = { | ||
args: { | ||
timeout: -1, | ||
value: false, | ||
}, | ||
}; | ||
|
||
export default meta; |
Oops, something went wrong.