Merge branch 'modal' into 'main'
Modal See merge request cellule-financiere-pmo/design-system/visua-vue!12
This commit is contained in:
commit
88f6ee8fef
|
@ -5,6 +5,12 @@ 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.12] - 2025-07-28
|
||||||
|
### Added
|
||||||
|
- Modal compoenent
|
||||||
|
- Confirm modal component
|
||||||
|
- useConfirmModal composable
|
||||||
|
|
||||||
## [1.0.11] - 2025-07-27
|
## [1.0.11] - 2025-07-27
|
||||||
### Added
|
### Added
|
||||||
- Alert compoenent
|
- Alert compoenent
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
# visua-vue
|
# visua-vue
|
||||||
|
|
||||||
**Current version: v1.0.11**
|
**Current version: v1.0.12**
|
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.11",
|
"version": "1.0.12",
|
||||||
"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.11",
|
"version": "1.0.12",
|
||||||
"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.11",
|
"version": "1.0.12",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
|
|
|
@ -10,7 +10,9 @@
|
||||||
// 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';
|
// import VAlertView from '../template/VAlertView.vue';
|
||||||
|
// import VModalView from '../template/VModalView.vue';
|
||||||
|
import VConfirmModalView from '../template/VConfirmModalView.vue';
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
@ -26,5 +28,7 @@ import VAlertView from '../template/VAlertView.vue';
|
||||||
<!-- <VProgressBarView/> -->
|
<!-- <VProgressBarView/> -->
|
||||||
<!-- <VMessageView/> -->
|
<!-- <VMessageView/> -->
|
||||||
<!-- <VFileUploadView/> -->
|
<!-- <VFileUploadView/> -->
|
||||||
<VAlertView/>
|
<!-- <VAlertView/> -->
|
||||||
|
<!-- <VModalView/> -->
|
||||||
|
<VConfirmModalView/>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -16,3 +16,5 @@
|
||||||
@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';
|
@import './primevue-style/toast.css';
|
||||||
|
@import './primevue-style/dialog.css';
|
||||||
|
@import './primevue-style/confirmdialog.css';
|
||||||
|
|
5
src/assets/style/primevue-style/confirmdialog.css
Normal file
5
src/assets/style/primevue-style/confirmdialog.css
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
:root {
|
||||||
|
--p-confirmdialog-content-gap: 1rem;
|
||||||
|
--p-confirmdialog-icon-size: var(--titles-H6-XXS-size);
|
||||||
|
--p-confirmdialog-icon-color: var(--text-title-grey);
|
||||||
|
}
|
14
src/assets/style/primevue-style/dialog.css
Normal file
14
src/assets/style/primevue-style/dialog.css
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
:root{
|
||||||
|
--p-dialog-background: var(--background-lifted-grey);
|
||||||
|
--p-dialog-border-color: var(--border-default-grey);
|
||||||
|
--p-dialog-color: var(--text-title-grey);
|
||||||
|
--p-dialog-border-radius: 0px;
|
||||||
|
--p-dialog-shadow: var(--shadow);
|
||||||
|
--p-dialog-header-padding: 1rem;
|
||||||
|
--p-dialog-header-gap: 0.5rem;
|
||||||
|
--p-dialog-title-font-size: var(--titles-H4-SM-size);
|
||||||
|
--p-dialog-title-font-weight: var( --titles-H4-SM-weight);
|
||||||
|
--p-dialog-content-padding: 0 1rem 1rem 1rem;
|
||||||
|
--p-dialog-footer-padding: 1rem;
|
||||||
|
--p-dialog-footer-gap: 1rem;
|
||||||
|
}
|
42
src/components/composable/useConfirmModal.ts
Normal file
42
src/components/composable/useConfirmModal.ts
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
import type { ConfirmationOptions } from "primevue/confirmationoptions";
|
||||||
|
import { useConfirm } from "primevue";
|
||||||
|
import VButton from "../button/VButton.vue";
|
||||||
|
|
||||||
|
export function useConfirmModal() {
|
||||||
|
const confirm = useConfirm();
|
||||||
|
|
||||||
|
const showConfirmModal = ({
|
||||||
|
acceptProps = VButton,
|
||||||
|
rejectProps = VButton,
|
||||||
|
group = '',
|
||||||
|
header = '',
|
||||||
|
message = '',
|
||||||
|
icon = '',
|
||||||
|
accept = Function,
|
||||||
|
reject = Function,
|
||||||
|
onHide = Function,
|
||||||
|
onShow = Function,
|
||||||
|
modal = true,
|
||||||
|
blockScroll = true,
|
||||||
|
position = 'center',
|
||||||
|
appendTo = 'body',
|
||||||
|
}: ConfirmationOptions) => {
|
||||||
|
confirm.require({
|
||||||
|
group,
|
||||||
|
header,
|
||||||
|
message,
|
||||||
|
icon,
|
||||||
|
rejectProps,
|
||||||
|
acceptProps,
|
||||||
|
accept,
|
||||||
|
reject,
|
||||||
|
onHide,
|
||||||
|
onShow,
|
||||||
|
modal,
|
||||||
|
blockScroll,
|
||||||
|
position,
|
||||||
|
appendTo,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return {showConfirmModal}
|
||||||
|
}
|
205
src/components/modal/IVModal.type.ts
Normal file
205
src/components/modal/IVModal.type.ts
Normal file
|
@ -0,0 +1,205 @@
|
||||||
|
import type { HTMLAttributes } from 'vue';
|
||||||
|
import type IVButton from '../button/IVButton.type';
|
||||||
|
import { type DialogBreakpoints } from 'primevue';
|
||||||
|
import type { HintedString } from '@primevue/core';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface representing the properties for a Modal component.
|
||||||
|
*/
|
||||||
|
export interface IModal {
|
||||||
|
/**
|
||||||
|
* Optional unique identifier for the modal element.
|
||||||
|
*/
|
||||||
|
modalId?: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flag indicating whether the modal is currently open.
|
||||||
|
*/
|
||||||
|
opened?: boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List of actions (buttons) displayed in the modal.
|
||||||
|
*/
|
||||||
|
actions?: IVButton[]
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines if the modal should behave like an alert dialog.
|
||||||
|
*/
|
||||||
|
isAlert?: boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reference to the originating element that triggered the modal,
|
||||||
|
* used to return focus when the modal is closed.
|
||||||
|
*/
|
||||||
|
origin?: { focus: () => void }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Title displayed at the top of the modal.
|
||||||
|
*/
|
||||||
|
title: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Optional icon name or path to render next to the title.
|
||||||
|
*/
|
||||||
|
icon?: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines the modal size: 'sm' = small, 'md' = medium, 'lg' = large.
|
||||||
|
*/
|
||||||
|
size?: 'sm' | 'md' | 'lg'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Accessible label (aria-label) for the close button.
|
||||||
|
*/
|
||||||
|
closeButtonLabel?: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tooltip (title attribute) shown when hovering over the close button.
|
||||||
|
*/
|
||||||
|
closeButtonTitle?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines valid properties in Dialog component.
|
||||||
|
*/
|
||||||
|
export interface DialogProps {
|
||||||
|
/**
|
||||||
|
* Title content of the dialog.
|
||||||
|
*/
|
||||||
|
header?: string | undefined;
|
||||||
|
/**
|
||||||
|
* Footer content of the dialog.
|
||||||
|
*/
|
||||||
|
footer?: string | undefined;
|
||||||
|
/**
|
||||||
|
* Specifies the visibility of the dialog.
|
||||||
|
* @defaultValue false
|
||||||
|
*/
|
||||||
|
visible?: boolean | undefined;
|
||||||
|
/**
|
||||||
|
* Defines if background should be blocked when dialog is displayed.
|
||||||
|
* @defaultValue false
|
||||||
|
*/
|
||||||
|
modal?: boolean | undefined;
|
||||||
|
/**
|
||||||
|
* Style of the content section.
|
||||||
|
*/
|
||||||
|
contentStyle?: unknown;
|
||||||
|
/**
|
||||||
|
* Style class of the content section.
|
||||||
|
*/
|
||||||
|
contentClass?: unknown;
|
||||||
|
/**
|
||||||
|
* Used to pass all properties of the HTMLDivElement to the overlay Dialog inside the component.
|
||||||
|
*/
|
||||||
|
contentProps?: HTMLAttributes | undefined;
|
||||||
|
/**
|
||||||
|
* Adds a close icon to the header to hide the dialog.
|
||||||
|
* @defaultValue true
|
||||||
|
*/
|
||||||
|
closable?: boolean | undefined;
|
||||||
|
/**
|
||||||
|
* Specifies if clicking the modal background should hide the dialog.
|
||||||
|
* @defaultValue false
|
||||||
|
*/
|
||||||
|
dismissableMask?: boolean | undefined;
|
||||||
|
/**
|
||||||
|
* Specifies if pressing escape key should hide the dialog.
|
||||||
|
* @defaultValue true
|
||||||
|
*/
|
||||||
|
closeOnEscape?: boolean | undefined;
|
||||||
|
/**
|
||||||
|
* Whether to show the header or not.
|
||||||
|
* @defaultValue true
|
||||||
|
*/
|
||||||
|
showHeader?: boolean | undefined;
|
||||||
|
/**
|
||||||
|
* Whether background scroll should be blocked when dialog is visible.
|
||||||
|
* @defaultValue false
|
||||||
|
*/
|
||||||
|
blockScroll?: boolean | undefined;
|
||||||
|
/**
|
||||||
|
* Base zIndex value to use in layering.
|
||||||
|
* @defaultValue 0
|
||||||
|
*/
|
||||||
|
baseZIndex?: number | undefined;
|
||||||
|
/**
|
||||||
|
* Whether to automatically manage layering.
|
||||||
|
* @defaultValue true
|
||||||
|
*/
|
||||||
|
autoZIndex?: boolean | undefined;
|
||||||
|
/**
|
||||||
|
* Position of the dialog.
|
||||||
|
* @defaultValue center
|
||||||
|
*/
|
||||||
|
position?: HintedString<'center' | 'top' | 'bottom' | 'left' | 'right' | 'topleft' | 'topright' | 'bottomleft' | 'bottomright'> | undefined;
|
||||||
|
/**
|
||||||
|
* Whether the dialog can be displayed full screen.
|
||||||
|
* @defaultValue false
|
||||||
|
*/
|
||||||
|
maximizable?: boolean | undefined;
|
||||||
|
/**
|
||||||
|
* Object literal to define widths per screen size.
|
||||||
|
*/
|
||||||
|
breakpoints?: DialogBreakpoints;
|
||||||
|
/**
|
||||||
|
* Enables dragging to change the position using header.
|
||||||
|
* @defaultValue true
|
||||||
|
*/
|
||||||
|
draggable?: boolean | undefined;
|
||||||
|
/**
|
||||||
|
* Keeps dialog in the viewport when dragging.
|
||||||
|
* @defaultValue true
|
||||||
|
*/
|
||||||
|
keepInViewport?: boolean | undefined;
|
||||||
|
/**
|
||||||
|
* Minimum value for the left coordinate of dialog in dragging.
|
||||||
|
* @defaultValue 0.
|
||||||
|
*/
|
||||||
|
minX?: number | undefined;
|
||||||
|
/**
|
||||||
|
* Minimum value for the top coordinate of dialog in dragging.
|
||||||
|
* @defaultValue 0
|
||||||
|
*/
|
||||||
|
minY?: number | undefined;
|
||||||
|
/**
|
||||||
|
* A valid query selector or an HTMLElement to specify where the dialog gets attached.
|
||||||
|
* @defaultValue body
|
||||||
|
*/
|
||||||
|
appendTo?: HintedString<'body' | 'self'> | undefined | HTMLElement;
|
||||||
|
/**
|
||||||
|
* Icon to display in the dialog close button.
|
||||||
|
*/
|
||||||
|
closeIcon?: string | undefined;
|
||||||
|
/**
|
||||||
|
* Icon to display in the dialog maximize button when dialog is not maximized.
|
||||||
|
*/
|
||||||
|
maximizeIcon?: string | undefined;
|
||||||
|
/**
|
||||||
|
* Icon to display in the dialog maximize button when dialog is minimized.
|
||||||
|
*/
|
||||||
|
minimizeIcon?: string | undefined;
|
||||||
|
/**
|
||||||
|
* Used to pass all properties of the ButtonProps to the Button component.
|
||||||
|
* @type {ButtonProps}
|
||||||
|
* @defaultValue { severity: 'secondary', rounded: true, text: true }
|
||||||
|
*/
|
||||||
|
closeButtonProps?: object | undefined;
|
||||||
|
/**
|
||||||
|
* Used to pass all properties of the ButtonProps to the Button component.
|
||||||
|
* @type {ButtonProps}
|
||||||
|
* @defaultValue { severity: 'secondary', rounded: true, text: true }
|
||||||
|
*/
|
||||||
|
maximizeButtonProps?: object | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extended Modal interface that includes configuration for responsive breakpoints.
|
||||||
|
*/
|
||||||
|
export default interface IVModal extends Partial<Omit<IModal, 'opened'>>, Partial<DialogProps> {
|
||||||
|
/**
|
||||||
|
* Breakpoints used to control responsive modal behavior.
|
||||||
|
* Compatible with PrimeVue Dialog breakpoints format.
|
||||||
|
*/
|
||||||
|
breakpoints?: DialogBreakpoints
|
||||||
|
}
|
68
src/components/modal/VConfirmModal.vue
Normal file
68
src/components/modal/VConfirmModal.vue
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import ConfirmDialog from 'primevue/confirmdialog';
|
||||||
|
import type { ConfirmDialogProps } from 'primevue/confirmdialog';
|
||||||
|
import VButtonGroup from '../button/VButtonGroup.vue';
|
||||||
|
import styles from '@visua/typography.module.css';
|
||||||
|
|
||||||
|
const props = withDefaults(defineProps<ConfirmDialogProps>(), {
|
||||||
|
group: '',
|
||||||
|
draggable: true,
|
||||||
|
breakpoints: undefined,
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<ConfirmDialog
|
||||||
|
:group="props.group"
|
||||||
|
:draggable="props.draggable"
|
||||||
|
:breakpoints="props.breakpoints"
|
||||||
|
role="alert"
|
||||||
|
>
|
||||||
|
<template #container="slotProps">
|
||||||
|
<div class="container">
|
||||||
|
<div
|
||||||
|
class='header'
|
||||||
|
:class="[styles['titles-H6-XXS']]"
|
||||||
|
:style="('danger' in slotProps.message.acceptProps) ? {color: 'var(--text-default-error)'} : {color: 'var(--text-default-warning)'}"
|
||||||
|
>
|
||||||
|
<i :class="slotProps.message.icon" style="font-weight:lighter;"></i>
|
||||||
|
<span style="width: 100%;">{{ slotProps.message.header }}</span>
|
||||||
|
</div>
|
||||||
|
<span>{{ slotProps.message.message }}</span>
|
||||||
|
<VButtonGroup
|
||||||
|
:title="slotProps.message.group"
|
||||||
|
inline-layout-when="always"
|
||||||
|
size="sm"
|
||||||
|
align="right"
|
||||||
|
:buttons="[
|
||||||
|
{
|
||||||
|
...slotProps.message.acceptProps,
|
||||||
|
onclick: slotProps.acceptCallback
|
||||||
|
},
|
||||||
|
{
|
||||||
|
...slotProps.message.rejectProps,
|
||||||
|
onclick: slotProps.rejectCallback
|
||||||
|
}
|
||||||
|
]"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</ConfirmDialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="css" scoped>
|
||||||
|
.container{
|
||||||
|
padding: 1rem;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header{
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
gap: var(--p-dialog-header-gap);
|
||||||
|
align-items: baseline;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
137
src/components/modal/VModal.vue
Normal file
137
src/components/modal/VModal.vue
Normal file
|
@ -0,0 +1,137 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import Dialog from 'primevue/dialog';
|
||||||
|
import type IVModal from './IVModal.type';
|
||||||
|
import { useId, computed, watch, ref } from 'vue';
|
||||||
|
import VButtonGroup from '../button/VButtonGroup.vue';
|
||||||
|
import VButton from '../button/VButton.vue';
|
||||||
|
import styles from '@visua/typography.module.css';
|
||||||
|
|
||||||
|
const props = withDefaults(defineProps<IVModal>(), {
|
||||||
|
modalId: () => useId(),
|
||||||
|
actions: () => [],
|
||||||
|
icon: undefined,
|
||||||
|
size: 'md',
|
||||||
|
closeButtonLabel: 'Fermer',
|
||||||
|
closeButtonTitle: 'Fermer la fenêtre modale',
|
||||||
|
title: undefined,
|
||||||
|
visible: false,
|
||||||
|
isAlert: false,
|
||||||
|
breakpoints: undefined,
|
||||||
|
modal: true,
|
||||||
|
dismissableMask: false,
|
||||||
|
blockScroll: true,
|
||||||
|
position: 'center',
|
||||||
|
maximizable: false,
|
||||||
|
draggable: true,
|
||||||
|
showHeader: true,
|
||||||
|
closeOnEscape: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
const role = computed(() => {
|
||||||
|
return props.isAlert ? 'alertdialog' : 'dialog'
|
||||||
|
})
|
||||||
|
|
||||||
|
const modalSize = computed(() => {
|
||||||
|
switch (props.size) {
|
||||||
|
case 'lg':
|
||||||
|
return `width: 49.5rem;`
|
||||||
|
case 'md':
|
||||||
|
return `width: 36.75rem;`
|
||||||
|
case 'sm':
|
||||||
|
return `width: 24rem;`
|
||||||
|
default:
|
||||||
|
return `width: 36.75rem;`
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const emit = defineEmits([
|
||||||
|
'update:visible',
|
||||||
|
'hide',
|
||||||
|
'after-hide',
|
||||||
|
'show',
|
||||||
|
'maximize',
|
||||||
|
'unmaximize',
|
||||||
|
'dragstart',
|
||||||
|
'dragend'
|
||||||
|
]);
|
||||||
|
|
||||||
|
const localVisible = ref(props.visible);
|
||||||
|
watch(() => props.visible, (newVal) => {
|
||||||
|
if(localVisible.value !== newVal){
|
||||||
|
localVisible.value = newVal;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
watch(localVisible, (newVal) => {
|
||||||
|
if(props.visible !== newVal){
|
||||||
|
emit('update:visible', newVal);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Dialog
|
||||||
|
:header="props.title"
|
||||||
|
id="modal"
|
||||||
|
:modal="props.modal"
|
||||||
|
:dismissable-mask="props.dismissableMask"
|
||||||
|
:role="role"
|
||||||
|
:aria-model="true"
|
||||||
|
:aria-labelledby="`modal-${props.modalId}-dialog`"
|
||||||
|
ref="modal"
|
||||||
|
:style="modalSize"
|
||||||
|
:breakpoints="props.breakpoints"
|
||||||
|
:block-scroll="props.blockScroll"
|
||||||
|
:position="props.position"
|
||||||
|
:maximizable="props.maximizable"
|
||||||
|
:draggable="props.draggable"
|
||||||
|
:show-header="props.showHeader"
|
||||||
|
:close-on-escape="props.closeOnEscape"
|
||||||
|
v-model:visible="localVisible"
|
||||||
|
@update:visible="emit('update:visible', $event)"
|
||||||
|
@after-hide="emit('after-hide', $event)"
|
||||||
|
@dragend="emit('dragend', $event)"
|
||||||
|
@dragstart="emit('dragstart', $event)"
|
||||||
|
@hide="emit('hide', $event)"
|
||||||
|
@maximize="emit('maximize', $event)"
|
||||||
|
@unmaximize="emit('unmaximize', $event)"
|
||||||
|
@show="emit('show', $event)"
|
||||||
|
>
|
||||||
|
<template #header>
|
||||||
|
<slot name="header" v-if="props.icon !== undefined">
|
||||||
|
<span :class="[styles['titles-H6-XXS']]">
|
||||||
|
<i :class="props.icon"></i>
|
||||||
|
{{ props.title }}
|
||||||
|
</span>
|
||||||
|
</slot>
|
||||||
|
</template>
|
||||||
|
<slot name="content"/>
|
||||||
|
<template #footer>
|
||||||
|
<slot name="footer">
|
||||||
|
<VButtonGroup
|
||||||
|
v-if="props.actions?.length"
|
||||||
|
inline-layout-when="always"
|
||||||
|
align="right"
|
||||||
|
:buttons="props.actions"
|
||||||
|
size="sm"
|
||||||
|
title="groupe de boutons"
|
||||||
|
/>
|
||||||
|
</slot>
|
||||||
|
</template>
|
||||||
|
<template #closebutton="{closeCallback}">
|
||||||
|
<VButton
|
||||||
|
tertiary
|
||||||
|
noOutline
|
||||||
|
size="sm"
|
||||||
|
icon="ri-close-line"
|
||||||
|
iconRight
|
||||||
|
@click="closeCallback"
|
||||||
|
:label="closeButtonLabel"
|
||||||
|
:title="closeButtonTitle"
|
||||||
|
ref="closeBtn"
|
||||||
|
aria-controls="modal-1"
|
||||||
|
type="button"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</Dialog>
|
||||||
|
</template>
|
|
@ -3,10 +3,12 @@ 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'
|
import ToastService from 'primevue/toastservice'
|
||||||
|
import ConfirmationService from 'primevue/confirmationservice'
|
||||||
|
|
||||||
const app = createApp(App)
|
const app = createApp(App)
|
||||||
|
|
||||||
app.use(primeVue)
|
app.use(primeVue)
|
||||||
app.use(ToastService)
|
app.use(ToastService)
|
||||||
|
app.use(ConfirmationService)
|
||||||
|
|
||||||
app.mount('#app')
|
app.mount('#app')
|
||||||
|
|
69
test/VModal.spec.ts
Normal file
69
test/VModal.spec.ts
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||||
|
import { mount } from '@vue/test-utils';
|
||||||
|
import { nextTick } from 'vue';
|
||||||
|
import VModal from '../src/components/modal/VModal.vue';
|
||||||
|
import type IVButton from '../src/components/button/IVButton.type';
|
||||||
|
|
||||||
|
describe('VModal.vue', () => {
|
||||||
|
let actions: IVButton[];
|
||||||
|
let visible: boolean;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
visible = true;
|
||||||
|
actions = [
|
||||||
|
{
|
||||||
|
label: 'Bouton primaire',
|
||||||
|
onClick: vi.fn(),
|
||||||
|
title: 'primaire',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Bouton secondaire',
|
||||||
|
secondary: true,
|
||||||
|
onClick: vi.fn(),
|
||||||
|
title: 'secondaire',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Bouton tertiaire',
|
||||||
|
tertiary: true,
|
||||||
|
onClick: vi.fn(),
|
||||||
|
title: 'tertiaire',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
});
|
||||||
|
|
||||||
|
it('emits update:visible when internal visibility changes', async () => {
|
||||||
|
const wrapper = mount(VModal, {
|
||||||
|
props: {
|
||||||
|
visible: true,
|
||||||
|
actions,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Simulation d’un changement interne
|
||||||
|
await wrapper.vm.$emit('update:visible', false);
|
||||||
|
await nextTick();
|
||||||
|
|
||||||
|
const emitted = wrapper.emitted('update:visible');
|
||||||
|
expect(emitted).toBeTruthy();
|
||||||
|
expect(emitted?.[0]).toEqual([false]);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('emits lifecycle events (show/hide)', async () => {
|
||||||
|
const wrapper = mount(VModal, {
|
||||||
|
props: {
|
||||||
|
visible,
|
||||||
|
actions,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
wrapper.vm.$emit('show');
|
||||||
|
wrapper.vm.$emit('hide');
|
||||||
|
|
||||||
|
await nextTick();
|
||||||
|
|
||||||
|
expect(wrapper.emitted('show')).toBeTruthy();
|
||||||
|
expect(wrapper.emitted('hide')).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
Loading…
Reference in New Issue
Block a user