diff --git a/CHANGELOG.md b/CHANGELOG.md index d87b97f..ce9fa83 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [1.0.11] - 2025-07-27 +### Added +- Alert compoenent +- useAlert composable + ## [1.0.10] - 2025-07-27 ### Added - File compoenent diff --git a/README.md b/README.md index 86e0390..7c32bf8 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,3 @@ # visua-vue -**Current version: v1.0.10** \ No newline at end of file +**Current version: v1.0.11** \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 707beef..f60c67f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@cellule-financiere-pmo/visua-vue", - "version": "1.0.10", + "version": "1.0.11", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@cellule-financiere-pmo/visua-vue", - "version": "1.0.10", + "version": "1.0.11", "license": "ISC", "dependencies": { "@cellule-financiere-pmo/visua": "1.1.3", diff --git a/package.json b/package.json index e0a863f..249783b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@cellule-financiere-pmo/visua-vue", - "version": "1.0.10", + "version": "1.0.11", "type": "module", "scripts": { "dev": "vite", diff --git a/src/App.vue b/src/App.vue index 1f2a29d..0fec676 100644 --- a/src/App.vue +++ b/src/App.vue @@ -9,7 +9,8 @@ // import VSelectView from '../template/VSelectView.vue'; // import VProgressBarView from '../template/VProgressBarView.vue'; // import VMessageView from '../template/VMessageView.vue'; -import VFileUploadView from '../template/VFileUploadView.vue' +// import VFileUploadView from '../template/VFileUploadView.vue'; +import VAlertView from '../template/VAlertView.vue'; @@ -24,5 +25,6 @@ import VFileUploadView from '../template/VFileUploadView.vue' --> - + + diff --git a/src/assets/style/primevue-configuration.css b/src/assets/style/primevue-configuration.css index 8120b3b..9a185a9 100644 --- a/src/assets/style/primevue-configuration.css +++ b/src/assets/style/primevue-configuration.css @@ -15,3 +15,4 @@ @import './primevue-style/progressbar.css'; @import './primevue-style/fileupload.css'; @import './primevue-style/scrollpanel.css'; +@import './primevue-style/toast.css'; diff --git a/src/assets/style/primevue-style/toast.css b/src/assets/style/primevue-style/toast.css new file mode 100644 index 0000000..38b3fcd --- /dev/null +++ b/src/assets/style/primevue-style/toast.css @@ -0,0 +1,37 @@ +:root{ + --p-toast-width: 25rem; + --p-toast-border-radius: 0px; + --p-toast-border-width: var(--large-border-width); + --p-toast-icon-size: 1.5rem; + --p-toast-content-padding: 0.25rem; + --p-toast-content-gap: 0.5rem; + --p-toast-text-gap: 0.5rem; + --p-toast-summary-font-weight: var( --text-body-MD-standard-text-weight); + --p-toast-summary-font-size: var(--text-body-MD-standard-text-size); + --p-toast-detail-font-weight: var( --text-body-MD-standard-text-Regular-weight); + --p-toast-detail-font-size: var(--text-body-MD-standard-text-Regular-size); + /* info */ + --p-toast-info-background: var(--background-contrast-info); + --p-toast-info-border-color: var(--border-plain-info); + --p-toast-info-color: var(--text-default-info); + --p-toast-info-detail-color: var(--input-color); + --p-toast-info-shadow: var(--shadow); + /* success */ + --p-toast-success-background: var(--background-contrast-success); + --p-toast-success-border-color: var(--border-plain-success); + --p-toast-success-color: var(--text-default-success); + --p-toast-success-detail-color: var(--input-color); + --p-toast-success-shadow: var(--shadow); + /* warn */ + --p-toast-warn-background: var(--background-contrast-warning); + --p-toast-warn-border-color: var(--border-plain-warning); + --p-toast-warn-color: var(--text-default-warning); + --p-toast-warn-detail-color: var(--input-color); + --p-toast-warn-shadow: var(--shadow); + /* error */ + --p-toast-error-background: var(--background-contrast-error); + --p-toast-error-border-color: var(--border-plain-error); + --p-toast-error-color: var(--text-default-error); + --p-toast-error-detail-color: var(--input-color); + --p-toast-error-shadow: var(--shadow); +} diff --git a/src/components/alert/IVAlert.type.ts b/src/components/alert/IVAlert.type.ts new file mode 100644 index 0000000..02bbfa5 --- /dev/null +++ b/src/components/alert/IVAlert.type.ts @@ -0,0 +1,62 @@ +/** + * Interface representing the properties of an Alert component. + */ +export default interface IVAlert { + /** + * Determines if the alert should be visible. + * @default false + */ + alert?: boolean; + + /** + * Indicates whether the alert has been closed. + * @default false + */ + closed?: boolean; + + /** + * Specifies if the alert can be closed by the user. + * @default false + */ + closeable?: boolean; + + /** + * Unique identifier for the alert instance. + */ + id?: string; + + /** + * Title displayed at the top of the alert. + */ + title?: string; + + /** + * Detailed description or message content of the alert. + */ + description?: string; + + /** + * Determines if a smaller variant of the alert should be displayed. + * @default false + */ + small?: boolean; + + /** + * Type of alert, affecting its visual style and icon. + * - `"success"`: Indicates a successful or positive action. + * - `"error"`: Indicates an error or critical issue. + * - `"info"`: Provides general information. + * - `"warn"`: Indicates a warning or potential issue. + */ + type?: "success" | "error" | "info" | "warn" | undefined; + + /** + * Label for the close button, useful for accessibility. + */ + closeButtonLabel?: string; + + /** + * Time in milliseconds after which the alert automatically disappears. + */ + lifeTime?: number; +} diff --git a/src/components/alert/VAlert.vue b/src/components/alert/VAlert.vue new file mode 100644 index 0000000..f43a43f --- /dev/null +++ b/src/components/alert/VAlert.vue @@ -0,0 +1,106 @@ + + + + + diff --git a/src/components/composable/useAlert.ts b/src/components/composable/useAlert.ts new file mode 100644 index 0000000..af7be43 --- /dev/null +++ b/src/components/composable/useAlert.ts @@ -0,0 +1,24 @@ +import { useToast } from "primevue/usetoast"; +import type IVAlert from "@/components/alert/IVAlert.type"; + +export function useAlert() { + const toast = useToast(); + + const showAlert = ({ + title = '', + description = '', + type = 'info', + closeable = true, + lifeTime, + }: IVAlert) => { + toast.add({ + severity: type, + summary: title, + detail: description, + life: lifeTime, + closable: closeable, + }) + } + + return { showAlert} +} diff --git a/src/main.ts b/src/main.ts index 2e0a866..0197873 100644 --- a/src/main.ts +++ b/src/main.ts @@ -2,9 +2,11 @@ import './assets/main.css' import { createApp } from 'vue' import App from './App.vue' import primeVue from 'primevue/config' +import ToastService from 'primevue/toastservice' const app = createApp(App) app.use(primeVue) +app.use(ToastService) app.mount('#app') diff --git a/test/VAlert.spec.ts b/test/VAlert.spec.ts new file mode 100644 index 0000000..4704dcf --- /dev/null +++ b/test/VAlert.spec.ts @@ -0,0 +1,60 @@ +import { mount, flushPromises } from '@vue/test-utils'; +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import VAlert from '../src/components/alert/VAlert.vue'; +import Toast from 'primevue/toast'; +import ToastService from 'primevue/toastservice'; + +vi.mock('primevue/toastservice', () => ({ + default: { + install: () => {}, + add: vi.fn() + } +})); + +describe('VAlert.vue', () => { + let wrapper: ReturnType; + + beforeEach(() => { + const container = document.createElement('div'); + container.id = 'app'; + document.body.appendChild(container); + + wrapper = mount(VAlert, { + attachTo: '#app', + global: { + components: { Toast }, + plugins: [ToastService] + }, + props: { + position: 'top-right' + } + }); + }); + + // 💡 Helper pour injecter un toast et attendre le rendu + const renderToast = async (payload: { severity: string; summary: string; detail: string; life: number }) => { + wrapper.vm.$toast?.add(payload); + await new Promise(resolve => setTimeout(resolve, 50)); + await flushPromises(); + }; + + it('renders the Toast container via teleport', async () => { + await renderToast({ + severity: 'success', + summary: 'Test Title', + detail: 'Test description', + life: 3000 + }); + + const toast = document.querySelector('.p-toast'); + expect(toast).toBeTruthy(); + }); + + it('responds to "close" and "life-end" events', async () => { + await wrapper.vm.$emit('close', { id: 'test-alert' }); + await wrapper.vm.$emit('life-end', { id: 'test-alert' }); + + expect(wrapper.emitted()).toHaveProperty('close'); + expect(wrapper.emitted()).toHaveProperty('life-end'); + }); +});