Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(@vtmn/svelte, @vtmn/react, vtmn/vue): use VtmnAlert as a component #1464

Merged
merged 5 commits into from
Sep 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,14 @@ const OverviewTemplate: Story = (args) => {

export const Overview = OverviewTemplate.bind({});
Overview.args = {};

const AlertItemTemplate: Story = (args) => {
return (
<div>
<VtmnAlert {...args} timeout={0} />
</div>
);
};

export const AlertItem = AlertItemTemplate.bind({});
AlertItem.args = {};
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
<script>
import { Meta, Template, Story } from '@storybook/addon-svelte-csf';
import { vtmnAlertStore, VtmnAlert, VtmnButton } from '@vtmn/svelte';
import {
vtmnAlertStore,
VtmnAlert,
VtmnButton,
VtmnAlertItem,
} from '@vtmn/svelte';
import { parameters } from '@vtmn/showcase-core/csf/components/overlays/alert.csf';
import README from '@vtmn/svelte/src/components/overlays/VtmnAlert/README.md';

const argTypes = {
title: {
type: { name: 'string', required: true },
type: { name: 'string' },
description: 'Title of the modal',
defaultValue: 'This is the title of the alert',
control: { type: 'text' },
Expand All @@ -26,6 +31,14 @@
type: 'boolean',
},
},
ariaLabelCloseButton: {
type: { name: 'string', required: false },
description: 'Aria label displayed for the close button',
defaultValue: 'Close alert',
control: {
type: 'text',
},
},
variant: {
type: { name: 'boolean', required: false },
description: 'Variant of the alert',
Expand Down Expand Up @@ -56,6 +69,7 @@
on:click={() => {
vtmnAlertStore.send({
...args,
ariaLabelCloseButton: 'Close alert',
'aria-labelledby': 'Storybook',
'aria-describedby': args.variant,
});
Expand All @@ -65,3 +79,19 @@
</Template>

<Story name="Overview" />

<Story
name="Alert item"
args={{
title: undefined,
description: undefined,
timeout: 0,
ariaLabelCloseButton: 'Close alert',
}}
let:args
>
<VtmnAlertItem {...args}>
<svelte:fragment slot="title">Slot title</svelte:fragment>
<svelte:fragment slot="description">Slot description</svelte:fragment>
</VtmnAlertItem>
</Story>
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,18 @@ const Template = (args) => ({

export const Overview = Template.bind({});
Overview.args = {};

const AlertItemTemplate = (args) => ({
components: { VtmnAlert },
setup() {
return {
args,
};
},
template: `<VtmnAlert v-bind="args" />`,
});

export const AlertItem = AlertItemTemplate.bind({});
AlertItem.args = {
timeout: 0,
};
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,17 @@ export interface VtmnAlertProps
*/
variant: VtmnAlertVariant;

/**
* Aria label applied on the close button
*/
ariaLabelCloseButton: string;

/**
* time (ms) before the alert disappears
* Set to 0 to keep the alert visible
*/
timeout: number;

/**
* The alert callback close function
* @type {function}
Expand All @@ -33,6 +44,8 @@ export const VtmnAlert = ({
title,
message,
onClose,
ariaLabelCloseButton = 'Close alert',
timeout = 8000,
className,
}: VtmnAlertProps) => {
return (
Expand All @@ -42,7 +55,7 @@ export const VtmnAlert = ({
className={clsx(
'vtmn-alert',
`vtmn-alert_variant--${variant}`,
'show',
timeout > 0 && 'show',
className,
)}
>
Expand All @@ -54,7 +67,7 @@ export const VtmnAlert = ({
size="small"
variant="ghost-reversed"
iconAlone="close-line"
aria-label="Close toast"
aria-label={ariaLabelCloseButton}
onClick={onClose}
></VtmnButton>
)}
Expand Down
1 change: 1 addition & 0 deletions packages/sources/svelte/src/components/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export { default as VtmnTabsItem } from './navigation/VtmnTabsItem/VtmnTabsItem.

// Overlays
export { default as VtmnAlert } from './overlays/VtmnAlert/VtmnAlert.svelte';
export { default as VtmnAlertItem } from './overlays/VtmnAlert/VtmnAlertItem.svelte';
export { vtmnAlertStore } from './overlays/VtmnAlert/vtmnAlertStore';
export { default as VtmnModal } from './overlays/VtmnModal/VtmnModal.svelte';
export { default as VtmnPopover } from './overlays/VtmnPopover/VtmnPopover.svelte';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,13 @@

/**
* @type {string} title of the alert
* Can also be set with a slot="title"
*/
export let title;
export let title = undefined;

/**
* @type {string} description of the alert
* Can also be set with a slot="description"
*/
export let description = undefined;

Expand All @@ -27,8 +29,15 @@

/**
* @type {number} time (ms) before the alert disappears
* Can't be above 8000ms.
* Set to 0 to keep the alert visible
*/
export let timeout;
export let timeout = 8000;

/**
* @type {string} aria label on the button
*/
export let ariaLabelCloseButton = 'Close alert';

let className = undefined;
/**
Expand All @@ -39,35 +48,44 @@
const dispatch = createEventDispatcher();
const closeHandler = () => dispatch('close');
const _clearTimeout = () => timeoutId && clearTimeout(timeoutId);
const _setTimeout = () => (timeoutId = setTimeout(closeHandler, timeout));
const _setTimeout = () =>
(timeoutId = timeout && setTimeout(closeHandler, timeout));
onDestroy(_clearTimeout);
onMount(_setTimeout);
$: componentClass = cn(
'vtmn-alert',
'show',
timeout > 0 && 'show',
variant && `vtmn-alert_variant--${variant}`,
className,
);
</script>

<div class={componentClass} role="alert" tabindex="-1" {...$$restProps}>
<div class="vtmn-alert_content" role="document">
<div id="alert-title" class="vtmn-alert_content-title">
{title}
<div class="vtmn-alert_content-title">
{#if $$slots.title}
<slot name="title" />
{:else}
{title}
{/if}
{#if withCloseButton}
<VtmnButton
aria-label="Close alert"
aria-label={ariaLabelCloseButton}
variant="ghost-reversed"
size="small"
iconAlone="close-line"
on:click={closeHandler}
/>
{/if}
</div>
{#if description}
<p id="alert-text" class="vtmn-alert_content-description">
{description}
</p>
{#if description || $$slots.description}
<span class="vtmn-alert_content-description">
{#if $$slots.description}
<slot name="description" />
{:else}
{description}
{/if}
</span>
{/if}
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<script>
import VtmnAlertItem from '../VtmnAlertItem.svelte';
</script>

<VtmnAlertItem {...$$restProps} on:close>
<svelte:fragment slot="title">Slot title</svelte:fragment>
<svelte:fragment slot="description">Slot description</svelte:fragment>
</VtmnAlertItem>
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import '@testing-library/jest-dom';
import { fireEvent, render, waitFor } from '@testing-library/svelte';
import VtmnAlertItem from '../VtmnAlertItem.svelte';
import VtmnAlertItemWithSlot from './VtmnAlertItemWithSlots.test.svelte';

const timeout = 5000;

Expand Down Expand Up @@ -138,6 +139,7 @@ describe('VtmnAlertItem', () => {
const { getByLabelText } = render(VtmnAlertItem, {
title: 'Alert unit-test',
timeout,
ariaLabelCloseButton: 'Close alert',
withCloseButton: true,
});
expect(getByLabelText('Close alert')).toBeVisible();
Expand All @@ -147,6 +149,7 @@ describe('VtmnAlertItem', () => {
const { getByLabelText, component } = render(VtmnAlertItem, {
title: 'Alert unit-test',
timeout,
ariaLabelCloseButton: 'Close alert',
withCloseButton: true,
});
await expectedCloseOnElement(getByLabelText('Close alert'), component, 1);
Expand All @@ -165,4 +168,10 @@ describe('VtmnAlertItem', () => {
timeout: 100,
});
});

test('Should display the component with the slots', async () => {
const { getByText } = render(VtmnAlertItemWithSlot, {});
expect(getByText('Slot title')).toBeVisible();
expect(getByText('Slot description')).toBeVisible();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,14 @@ class VtmnAlertStore {
this._alerts = writable([]);
}

send({ variant, title, description, withCloseButton, ...attributes }) {
send({
variant,
title,
description,
withCloseButton,
ariaLabelCloseButton,
...attributes
}) {
this._alerts.update((state) => [
...state,
{
Expand All @@ -15,6 +22,7 @@ class VtmnAlertStore {
title,
description,
withCloseButton,
ariaLabelCloseButton,
id: `vtmn-alert-${uuid()}`,
},
]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ export default /*#__PURE__*/ defineComponent({
type: Boolean as PropType<boolean>,
default: false,
},
ariaLabelCloseButton: {
type: String as PropType<string>,
default: 'Close alert'
},
timeout: {
type: Number as PropType<number>,
default: 8000,
Expand All @@ -45,7 +49,7 @@ export default /*#__PURE__*/ defineComponent({
return {
classes: computed(() => ({
'vtmn-alert': true,
show: true,
show: props.timeout > 0,
[`vtmn-alert_variant--${props.variant}`]: props.variant,
})),
handleClose,
Expand All @@ -57,18 +61,18 @@ export default /*#__PURE__*/ defineComponent({
<template>
<div :class="classes" role="alert" tabindex="-1" v-bind="$attrs">
<div class="vtmn-alert_content" role="document">
<div id="alert-title" class="vtmn-alert_content-title">
<div class="vtmn-alert_content-title">
{{ title }}
<VtmnButton
v-if="withCloseButton"
iconAlone="close-line"
variant="ghost-reversed"
size="small"
aria-label="close"
:aria-label="ariaLabelCloseButton"
@click.prevent="handleClose"
/>
</div>
<p v-if="message" id="alert-text" class="vtmn-alert_content-description">
<p v-if="message" class="vtmn-alert_content-description">
{{ message }}
</p>
</div>
Expand Down
Loading