Merge branch 'alert' into 'main'
Alert See merge request cellule-financiere-pmo/design-system/visua-vue!11
This commit is contained in:
commit
06e61bd809
|
@ -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/),
|
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).
|
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
|
## [1.0.10] - 2025-07-27
|
||||||
### Added
|
### Added
|
||||||
- File compoenent
|
- File compoenent
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
# visua-vue
|
# visua-vue
|
||||||
|
|
||||||
**Current version: v1.0.10**
|
**Current version: v1.0.11**
|
4
package-lock.json
generated
4
package-lock.json
generated
|
@ -1,12 +1,12 @@
|
||||||
{
|
{
|
||||||
"name": "@cellule-financiere-pmo/visua-vue",
|
"name": "@cellule-financiere-pmo/visua-vue",
|
||||||
"version": "1.0.10",
|
"version": "1.0.11",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "@cellule-financiere-pmo/visua-vue",
|
"name": "@cellule-financiere-pmo/visua-vue",
|
||||||
"version": "1.0.10",
|
"version": "1.0.11",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@cellule-financiere-pmo/visua": "1.1.3",
|
"@cellule-financiere-pmo/visua": "1.1.3",
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@cellule-financiere-pmo/visua-vue",
|
"name": "@cellule-financiere-pmo/visua-vue",
|
||||||
"version": "1.0.10",
|
"version": "1.0.11",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
|
|
|
@ -9,7 +9,8 @@
|
||||||
// import VSelectView from '../template/VSelectView.vue';
|
// import VSelectView from '../template/VSelectView.vue';
|
||||||
// import VProgressBarView from '../template/VProgressBarView.vue';
|
// import VProgressBarView from '../template/VProgressBarView.vue';
|
||||||
// import VMessageView from '../template/VMessageView.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';
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
@ -24,5 +25,6 @@ import VFileUploadView from '../template/VFileUploadView.vue'
|
||||||
<VSelectView/> -->
|
<VSelectView/> -->
|
||||||
<!-- <VProgressBarView/> -->
|
<!-- <VProgressBarView/> -->
|
||||||
<!-- <VMessageView/> -->
|
<!-- <VMessageView/> -->
|
||||||
<VFileUploadView/>
|
<!-- <VFileUploadView/> -->
|
||||||
|
<VAlertView/>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -15,3 +15,4 @@
|
||||||
@import './primevue-style/progressbar.css';
|
@import './primevue-style/progressbar.css';
|
||||||
@import './primevue-style/fileupload.css';
|
@import './primevue-style/fileupload.css';
|
||||||
@import './primevue-style/scrollpanel.css';
|
@import './primevue-style/scrollpanel.css';
|
||||||
|
@import './primevue-style/toast.css';
|
||||||
|
|
37
src/assets/style/primevue-style/toast.css
Normal file
37
src/assets/style/primevue-style/toast.css
Normal file
|
@ -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);
|
||||||
|
}
|
62
src/components/alert/IVAlert.type.ts
Normal file
62
src/components/alert/IVAlert.type.ts
Normal file
|
@ -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;
|
||||||
|
}
|
106
src/components/alert/VAlert.vue
Normal file
106
src/components/alert/VAlert.vue
Normal file
|
@ -0,0 +1,106 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import Toast from 'primevue/toast';
|
||||||
|
import VButton from '../button/VButton.vue';
|
||||||
|
import type { ToastProps } from 'primevue/toast';
|
||||||
|
import styles from '@visua/typography.module.css';
|
||||||
|
|
||||||
|
const props = withDefaults(defineProps<ToastProps>(), {
|
||||||
|
group: undefined,
|
||||||
|
position: 'bottom-center',
|
||||||
|
breakpoints: undefined
|
||||||
|
});
|
||||||
|
|
||||||
|
const emit = defineEmits([
|
||||||
|
'close',
|
||||||
|
'life-end'
|
||||||
|
]);
|
||||||
|
|
||||||
|
const getIconColor = (type?: string) => {
|
||||||
|
switch (type) {
|
||||||
|
case 'error': return 'var(--text-default-error)';
|
||||||
|
case 'warn' : return 'var(--text-default-warning)';
|
||||||
|
case 'success' : return 'var(--text-default-success)';
|
||||||
|
case 'info': return 'var(--text-default-info)';
|
||||||
|
default:
|
||||||
|
return 'var(--text-default-success)';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const getIconClass = (type?: string) => {
|
||||||
|
switch (type) {
|
||||||
|
case 'error': return 'ri-spam-fill';
|
||||||
|
case 'warn' : return 'ri-alert-fill';
|
||||||
|
case 'success' : return 'ri-checkbox-circle-fill';
|
||||||
|
case 'info': return 'ri-information-fill';
|
||||||
|
default:
|
||||||
|
return 'ri-information-fill';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Toast
|
||||||
|
:position=props.position
|
||||||
|
:group="props.group"
|
||||||
|
:breakpoints="props.breakpoints"
|
||||||
|
class="p-toast"
|
||||||
|
role="alert"
|
||||||
|
aria-live="assertive"
|
||||||
|
aria-atomic="true"
|
||||||
|
@close="emit('close', $event)"
|
||||||
|
@life-end="emit('life-end', $event)"
|
||||||
|
>
|
||||||
|
<template #container="{message, closeCallback}">
|
||||||
|
<div class="header">
|
||||||
|
<i
|
||||||
|
style="font-size: var(--p-message-icon-lg-size);"
|
||||||
|
:class="getIconClass(message.severity)"
|
||||||
|
:style="{color: getIconColor(message.severity)}"
|
||||||
|
/>
|
||||||
|
<span
|
||||||
|
:class="[styles['text-body-MD-standard-text-Medium']]"
|
||||||
|
:style="{color: getIconColor(message.severity)}"
|
||||||
|
style="width: 100%;"
|
||||||
|
>
|
||||||
|
{{ message.summary }}
|
||||||
|
</span>
|
||||||
|
<VButton
|
||||||
|
title="Fermer le message"
|
||||||
|
tertiary
|
||||||
|
no-outline
|
||||||
|
size="sm"
|
||||||
|
icon-only
|
||||||
|
aria-label="Fermer"
|
||||||
|
icon="ri-close-line"
|
||||||
|
type="button"
|
||||||
|
@click="closeCallback"
|
||||||
|
style="height: 2rem;"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-if="!!message.detail"
|
||||||
|
:class="['content', styles['text-body-MD-standard-text-Regular']]"
|
||||||
|
>
|
||||||
|
<span>{{ message.detail }}</span>
|
||||||
|
<slot name="footer"/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</Toast>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="css" scoped>
|
||||||
|
.header{
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
gap: 0.25rem;
|
||||||
|
padding: var(--p-toast-text-gap);
|
||||||
|
color: var(--text-title-grey);
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content{
|
||||||
|
padding: 0rem 0.825rem 0.5rem 0.825rem;
|
||||||
|
color: var(--text-default-grey);
|
||||||
|
}
|
||||||
|
</style>
|
24
src/components/composable/useAlert.ts
Normal file
24
src/components/composable/useAlert.ts
Normal file
|
@ -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}
|
||||||
|
}
|
|
@ -2,9 +2,11 @@ import './assets/main.css'
|
||||||
import { createApp } from 'vue'
|
import { createApp } from 'vue'
|
||||||
import App from './App.vue'
|
import App from './App.vue'
|
||||||
import primeVue from 'primevue/config'
|
import primeVue from 'primevue/config'
|
||||||
|
import ToastService from 'primevue/toastservice'
|
||||||
|
|
||||||
const app = createApp(App)
|
const app = createApp(App)
|
||||||
|
|
||||||
app.use(primeVue)
|
app.use(primeVue)
|
||||||
|
app.use(ToastService)
|
||||||
|
|
||||||
app.mount('#app')
|
app.mount('#app')
|
||||||
|
|
60
test/VAlert.spec.ts
Normal file
60
test/VAlert.spec.ts
Normal file
|
@ -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<typeof mount>;
|
||||||
|
|
||||||
|
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');
|
||||||
|
});
|
||||||
|
});
|
Loading…
Reference in New Issue
Block a user