diff --git a/CHANGELOG.md b/CHANGELOG.md
index caf21dd..ae4a917 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,15 @@ 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.8] - 2025-07-23
+### Added
+- ProgressBar component
+
+## [1.0.7] - 2025-07-23
+### Added
+- Select component
+- Badge component
+
## [1.0.6] - 2025-07-23
### Added
- Checkbox component
diff --git a/README.md b/README.md
index 6872d61..11390d3 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,3 @@
# visua-vue
-**Current version: v1.0.6**
\ No newline at end of file
+**Current version: v1.0.8**
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
index eb36326..f5dab22 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "@cellule-financiere-pmo/visua-vue",
- "version": "1.0.6",
+ "version": "1.0.8",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@cellule-financiere-pmo/visua-vue",
- "version": "1.0.6",
+ "version": "1.0.8",
"license": "ISC",
"dependencies": {
"@cellule-financiere-pmo/visua": "1.1.3",
diff --git a/package.json b/package.json
index 4db8cd9..4f3f114 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "@cellule-financiere-pmo/visua-vue",
- "version": "1.0.6",
+ "version": "1.0.8",
"type": "module",
"scripts": {
"dev": "vite",
diff --git a/src/App.vue b/src/App.vue
index e582a0d..9c3f67f 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -4,7 +4,10 @@
// import VLinkView from '../template/VLinkView.vue';
// import VAccordionView from '../template/VAccordionView.vue';
// import VInputView from '../template/VInputView.vue';
-import VCheckboxView from '../template/VCheckboxView.vue';
+// import VCheckboxView from '../template/VCheckboxView.vue';
+// import VBadgeView from '../template/VBadgeView.vue';
+// import VSelectView from '../template/VSelectView.vue';
+import VProgressBarView from '../template/VProgressBarView.vue';
@@ -14,5 +17,8 @@ import VCheckboxView from '../template/VCheckboxView.vue';
-
+
+
+
+
diff --git a/src/assets/style/primevue-configuration.css b/src/assets/style/primevue-configuration.css
index c381491..a92e270 100644
--- a/src/assets/style/primevue-configuration.css
+++ b/src/assets/style/primevue-configuration.css
@@ -8,3 +8,8 @@
@import './primevue-style/various.css';
@import './primevue-style/form.css';
@import './primevue-style/checkbox.css';
+@import './primevue-style/tag.css';
+@import './primevue-style/select.css';
+@import './primevue-style/overlay.css';
+@import './primevue-style/iconfield.css';
+@import './primevue-style/progressbar.css';
diff --git a/src/assets/style/primevue-style/iconfield.css b/src/assets/style/primevue-style/iconfield.css
new file mode 100644
index 0000000..d9f6df5
--- /dev/null
+++ b/src/assets/style/primevue-style/iconfield.css
@@ -0,0 +1,3 @@
+:root{
+ --p-iconfield-icon-color: var(--input-color);
+}
diff --git a/src/assets/style/primevue-style/overlay.css b/src/assets/style/primevue-style/overlay.css
new file mode 100644
index 0000000..3850fc9
--- /dev/null
+++ b/src/assets/style/primevue-style/overlay.css
@@ -0,0 +1,17 @@
+:root{
+ --p-overlay-modal-background: var(--background-lifted-grey);
+ --p-overlay-modal-border-color: var(--border-default-grey);
+ --p-overlay-modal-color: var(--text-default-grey);
+ --p-overlay-popover-background: var(--background-lifted-grey);
+ --p-overlay-popover-border-color: var(--border-default-grey);
+ --p-overlay-popover-color: var(--text-default-grey);
+ --p-overlay-navigation-shadow: var(--shadow);
+ --p-overlay-modal-border-radius: 0px;
+ --p-overlay-modal-padding: 1.25rem;
+ --p-overlay-modal-shadow: var(--shadow);
+ --p-overlay-popover-border-radius: 0px;
+ --p-overlay-popover-padding: 0.75rem;
+ --p-overlay-popover-shadow: var(--shadow);
+ --p-overlay-select-border-radius: 0px;
+ --p-overlay-select-shadow: var(--shadow);
+}
diff --git a/src/assets/style/primevue-style/progressbar.css b/src/assets/style/primevue-style/progressbar.css
new file mode 100644
index 0000000..e39e63b
--- /dev/null
+++ b/src/assets/style/primevue-style/progressbar.css
@@ -0,0 +1,9 @@
+:root{
+ --p-progressbar-background: var(--background-contrast-grey);
+ --p-progressbar-height: 1rem;
+ --p-progressbar-border-radius: 0.75rem;
+ --p-progressbar-value-background: var(--background-active-blue-france);
+ --p-progressbar-label-color: var(--text-inverted-grey);
+ --p-progressbar-label-font-size: var(--text-body-XS-mention-text-Medium-size);
+ --p-progressbar-label-font-weight: var(--text-body-XS-mention-text-Medium-weight);
+}
diff --git a/src/assets/style/primevue-style/select.css b/src/assets/style/primevue-style/select.css
new file mode 100644
index 0000000..1f271dd
--- /dev/null
+++ b/src/assets/style/primevue-style/select.css
@@ -0,0 +1,47 @@
+:root{
+ --p-select-background: var(--input-background);
+ --p-select-disabled-background: var(--input-disabled-background);
+ --p-select-border-color: var(--input-border-color);
+ --p-select-hover-border-color: var(--input-border-color);
+ --p-select-color: var(--input-color);
+ --p-select-disabled-color: var(--input-disabled-color);
+ --p-select-placeholder-color: var(--input-color);
+ --p-select-padding-x: var(--input-padding-x);
+ --p-select-padding-y: var(--input-padding-y);
+ --p-select-border-radius: var(--input-border-raduis);
+ --p-select-dropdown-width: 2rem;
+ --p-select-dropdown-color: var(--input-color);
+ --p-select-overlay-background: var(--input-background);
+ --p-select-overlay-border-color: var(--input-border-color);
+ --p-select-overlay-border-radius: 0px 0px 0.25rem 0.25rem;
+ --p-select-overlay-color: var(--input-color);
+ --p-select-overlay-shadow: var(--shadow);
+ --p-select-list-padding: 0.25rem;
+ --p-select-list-gap: 0.125rem;
+ --p-select-list-header-padding: 0.5rem 1rem;
+ /* options */
+ --p-select-option-selected-background: var(--background-active-blue-france);
+ --p-select-option-selected-focus-background: var(--background-active-blue-france);
+ --p-select-option-color: var(--input-color);
+ --p-select-option-focus-color: var(--input-color);
+ --p-select-option-selected-color: var(--text-inverted-blue-france);
+ --p-select-option-padding: 0.5rem 0.75rem;
+ --p-select-option-border-radius: 0px;
+ --p-select-option-group-background: var(--input-background);
+ --p-select-option-group-color: var(--input-color);
+ --p-select-option-group-font-weight: var(--text-body-MD-standard-text-Regular-weight);
+ --p-select-option-group-padding: 0.25rem;
+ --p-select-clear-icon-color: var(--input-color);
+ --p-select-checkmark-color: var(--input-color);
+ --p-select-checkmark-gutter-start: 0.125rem;
+ --p-select-checkmark-gutter-end: 0.125rem;
+ --p-select-empty-message-padding: 0.25rem;
+ /* focus */
+ --p-select-focus-border-color: var(--input-border-color);
+ --p-select-focus-ring-width: var(--focus-width);
+ --p-select-focus-ring-style: var(--focus-style);
+ --p-select-focus-ring-color: var(--focus-color);
+ --p-select-focus-ring-offset: var(--focus-offset);
+ --p-select-option-focus-background: var(--background-transparent-active);
+ --p-select-option-selected-focus-color: var(--text-inverted-blue-france);
+}
diff --git a/src/assets/style/primevue-style/tag.css b/src/assets/style/primevue-style/tag.css
new file mode 100644
index 0000000..8ca750c
--- /dev/null
+++ b/src/assets/style/primevue-style/tag.css
@@ -0,0 +1,23 @@
+:root {
+ --p-tag-icon-size: var(--text-body-MD-standard-text-Regular-size);
+ --p-tag-font-size: var( --text-body-SM-detail-text-Maj-size);
+ --p-tag-font-weight: var( --text-body-SM-detail-text-Maj-weight);
+ --p-tag-padding: 0.25rem 0.5rem;
+ --p-tag-gap: 0.25rem;
+ --p-tag-border-radius: 0.25rem;
+ /* --p-tag-rounded-border-radius:
+ --p-tag-contrast-background: */
+ --p-tag-contrast-color: var(--p-surface-0);
+ --p-tag-danger-background: var(--background-contrast-error);
+ --p-tag-danger-color: var(--text-default-error);
+ --p-tag-warn-background: var(--background-contrast-warning);
+ --p-tag-warn-color: var(--text-default-warning);
+ --p-tag-info-background: var(--background-contrast-info);
+ --p-tag-info-color: var(--text-default-info);
+ --p-tag-success-background: var(--background-contrast-success);
+ --p-tag-success-color: var(--text-default-success);
+ --p-tag-secondary-background: var(--illustration-color-950-tournesol-default);
+ --p-tag-secondary-color: var(--illustration-color-sun-tournesol-default);
+ --p-tag-primary-background: var(--illustration-color-950-glycine-default);
+ --p-tag-primary-color: var(--illustration-color-sun-glycine-default);
+}
diff --git a/src/components/badge/IVBadge.type.ts b/src/components/badge/IVBadge.type.ts
new file mode 100644
index 0000000..b97777e
--- /dev/null
+++ b/src/components/badge/IVBadge.type.ts
@@ -0,0 +1,38 @@
+/**
+ * Interface representing the properties of a Badge component.
+ */
+export default interface IVBadge {
+ /**
+ * The text label displayed inside the badge.
+ */
+ label: string;
+
+ /**
+ * The type of badge, which determines its color and icon.
+ * - `success`: Indicates a positive or successful status.
+ * - `error`: Indicates an error or negative status.
+ * - `new`: Highlights something new or recently added.
+ * - `info`: Provides informational context.
+ * - `warning`: Warns the user about a potential issue.
+ * - **undefined**: Defaults to a neutral badge style.
+ */
+ type?: 'success' | 'error' | 'new' | 'info' | 'warning' | undefined;
+
+ /**
+ * If true, the badge will be displayed without an icon.
+ * Defaults to false.
+ */
+ noIcon?: boolean;
+
+ /**
+ * If true, the badge will be rendered in a smaller size.
+ * Useful for compact UI elements.
+ */
+ small?: boolean;
+
+ /**
+ * Optional maximum width for the badge.
+ * Accepts any valid CSS width value (e.g., '100px', '10rem', '50%').
+ */
+ maxWidth?: string;
+}
diff --git a/src/components/badge/VBadge.vue b/src/components/badge/VBadge.vue
new file mode 100644
index 0000000..e474e5d
--- /dev/null
+++ b/src/components/badge/VBadge.vue
@@ -0,0 +1,71 @@
+
+
+
+
+
+ {{ props.label }}
+
+
+
+
+
diff --git a/src/components/progressbar/IVProgressBar.type.ts b/src/components/progressbar/IVProgressBar.type.ts
new file mode 100644
index 0000000..67afc37
--- /dev/null
+++ b/src/components/progressbar/IVProgressBar.type.ts
@@ -0,0 +1,30 @@
+/**
+ * Interface representing the properties of a ProgressBar component.
+ */
+export default interface IVProgressBar {
+ /**
+ * Whether to display the numeric value of the progress.
+ * If true, the value will be shown alongside the progress bar.
+ * @default false
+ */
+ showValue?: boolean;
+
+ /**
+ * The current progress value, typically between 0 and 100.
+ */
+ value: number;
+
+ /**
+ * If true, the progress bar will be in indeterminate mode,
+ * indicating ongoing activity without a specific completion percentage.
+ * @default false
+ */
+ indeterminate?: boolean;
+
+ /**
+ * If true, renders a smaller version of the progress bar.
+ * Useful for compact UI layouts.
+ * @default false
+ */
+ small?: boolean;
+}
diff --git a/src/components/progressbar/VProgressBar.vue b/src/components/progressbar/VProgressBar.vue
new file mode 100644
index 0000000..efce040
--- /dev/null
+++ b/src/components/progressbar/VProgressBar.vue
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/components/select/IVSelect.type.ts b/src/components/select/IVSelect.type.ts
new file mode 100644
index 0000000..3be1034
--- /dev/null
+++ b/src/components/select/IVSelect.type.ts
@@ -0,0 +1,351 @@
+import type { HintedString } from '@primevue/core';
+import type { VirtualScrollerProps } from 'primevue/virtualscroller';
+
+/**
+ * Represents the props for a custom VSelect component.
+ */
+export type option = string | number | null | undefined;
+
+export interface ISelect {
+ /**
+ * Whether the select field is required.
+ */
+ required?: boolean;
+
+ /**
+ * Whether the select field is disabled.
+ */
+ disabled?: boolean;
+
+ /**
+ * The unique ID for the select element.
+ */
+ selectId?: string;
+
+ /**
+ * The name attribute for the select element.
+ */
+ name?: string;
+
+ /**
+ * Optional hint text displayed below the select field.
+ */
+ hint?: string;
+
+ /**
+ * The currently selected value.
+ */
+ modelValue?: option;
+
+ /**
+ * The label displayed above the select field.
+ */
+ label?: string;
+
+ /**
+ * The list of options to display in the dropdown.
+ * Can be an array of strings, numbers, or objects.
+ */
+ options?: Array<
+ string | number | Record | {
+ value: unknown;
+ text: string;
+ disabled: boolean;
+ }
+ >;
+
+ /**
+ * Defines how to extract the label from an option.
+ * Can be a string key or a function.
+ */
+ optionLabel?: string | ((option: unknown) => string);
+
+ /**
+ * Defines how to extract the value from an option.
+ * Can be a string key or a function.
+ */
+ optionValue?: string | ((option: unknown) => unknown);
+
+ /**
+ * Message displayed when the selection is valid.
+ */
+ successMessage?: string;
+
+ /**
+ * Message displayed when the selection is invalid.
+ */
+ errorMessage?: string;
+
+ /**
+ * Text displayed when no option is selected.
+ */
+ defaultUnselectedText?: string;
+}
+
+export interface SelectProps {
+ /**
+ * Value of the component.
+ */
+ modelValue?: unknown;
+ /**
+ * The default value for the input when not controlled by `modelValue`.
+ */
+ defaultValue?: unknown;
+ /**
+ * The name attribute for the element, typically used in form submissions.
+ */
+ name?: string | undefined;
+ /**
+ * An array of select items to display as the available options.
+ */
+ options?: unknown[];
+ /**
+ * Property name or getter function to use as the label of an option.
+ */
+ optionLabel?: string | ((data: unknown) => string) | undefined;
+ /**
+ * Property name or getter function to use as the value of an option, defaults to the option itself when not defined.
+ */
+ optionValue?: string | ((data: unknown) => unknown) | undefined;
+ /**
+ * Property name or getter function to use as the disabled flag of an option, defaults to false when not defined.
+ */
+ optionDisabled?: string | ((data: unknown) => boolean) | undefined;
+ /**
+ * Property name or getter function to use as the label of an option group.
+ */
+ optionGroupLabel?: string | ((data: unknown) => string) | undefined;
+ /**
+ * Property name or getter function that refers to the children options of option group.
+ */
+ optionGroupChildren?: string | ((data: unknown) => unknown[]) | undefined;
+ /**
+ * Height of the viewport, a scrollbar is defined if height of list exceeds this value.
+ * @defaultValue 14rem
+ */
+ scrollHeight?: string | undefined;
+ /**
+ * When specified, displays a filter input at header.
+ * @defaultValue false
+ */
+ filter?: boolean | undefined;
+ /**
+ * Placeholder text to show when filter input is empty.
+ */
+ filterPlaceholder?: string | undefined;
+ /**
+ * Locale to use in filtering. The default locale is the host environment's current locale.
+ */
+ filterLocale?: string | undefined;
+ /**
+ * Defines the filtering algorithm to use when searching the options.
+ * @defaultValue contains
+ */
+ filterMatchMode?: HintedString<'contains' | 'startsWith' | 'endsWith'> | undefined;
+ /**
+ * Fields used when filtering the options, defaults to optionLabel.
+ */
+ filterFields?: string[] | undefined;
+ /**
+ * When present, custom value instead of predefined options can be entered using the editable input field.
+ * @defaultValue false
+ */
+ editable?: boolean | undefined;
+ /**
+ * Default text to display when no option is selected.
+ */
+ placeholder?: string | undefined;
+ /**
+ * Defines the size of the component.
+ */
+ size?: HintedString<'small' | 'large'> | undefined;
+ /**
+ * When present, it specifies that the component should have invalid state style.
+ * @defaultValue false
+ */
+ invalid?: boolean | undefined;
+ /**
+ * When present, it specifies that the component should be disabled.
+ * @defaultValue false
+ */
+ disabled?: boolean | undefined;
+ /**
+ * Specifies the input variant of the component.
+ * @defaultValue null
+ */
+ variant?: HintedString<'outlined' | 'filled'> | undefined | null;
+ /**
+ * A property to uniquely identify an option.
+ */
+ dataKey?: string | undefined;
+ /**
+ * When enabled, a clear icon is displayed to clear the value.
+ * @defaultValue false
+ */
+ showClear?: boolean | undefined;
+ /**
+ * Spans 100% width of the container when enabled.
+ * @defaultValue null
+ */
+ fluid?: boolean | undefined;
+ /**
+ * @deprecated since v4.0. Use 'labelId' instead.
+ * Identifier of the underlying input element.
+ */
+ inputId?: string | undefined;
+ /**
+ * @deprecated since v4.0. Use 'labelStyle' instead.
+ * Inline style of the input field.
+ */
+ inputStyle?: object | undefined;
+ /**
+ * @deprecated since v4.0. Use 'labelClass' instead.
+ * Style class of the input field.
+ */
+ inputClass?: string | object | undefined;
+ /**
+ * Identifier of the underlying label element.
+ */
+ labelId?: string | undefined;
+ /**
+ * Inline style of the label field.
+ */
+ labelStyle?: object | undefined;
+ /**
+ * Style class of the label field.
+ */
+ labelClass?: string | object | undefined;
+ /**
+ * @deprecated since v4.0. Use 'overlayStyle' instead.
+ * Inline style of the overlay panel.
+ */
+ panelStyle?: object | undefined;
+ /**
+ * @deprecated since v4.0. Use 'overlayClass' instead.
+ * Style class of the overlay panel.
+ */
+ panelClass?: string | object | undefined;
+ /**
+ * Inline style of the overlay.
+ */
+ overlayStyle?: object | undefined;
+ /**
+ * Style class of the overlay.
+ */
+ overlayClass?: string | object | undefined;
+ /**
+ * A valid query selector or an HTMLElement to specify where the overlay gets attached.
+ * @defaultValue body
+ */
+ appendTo?: HintedString<'body' | 'self'> | undefined | HTMLElement;
+ /**
+ * Whether the select is in loading state.
+ * @defaultValue false
+ */
+ loading?: boolean | undefined;
+ /**
+ * Icon to display in clear button.
+ */
+ clearIcon?: string | undefined;
+ /**
+ * Icon to display in the select.
+ */
+ dropdownIcon?: string | undefined;
+ /**
+ * Icon to display in filter input.
+ */
+ filterIcon?: string | undefined;
+ /**
+ * Icon to display in loading state.
+ */
+ loadingIcon?: string | undefined;
+ /**
+ * Clears the filter value when hiding the select.
+ * @defaultValue false
+ */
+ resetFilterOnHide?: boolean;
+ /**
+ * Clears the filter value when clicking on the clear icon.
+ * @defaultValue false
+ */
+ resetFilterOnClear?: boolean;
+ /**
+ * Whether to use the virtualScroller feature. The properties of VirtualScroller component can be used like an object in it.
+ */
+ virtualScrollerOptions?: VirtualScrollerProps;
+ /**
+ * Whether to focus on the first visible or selected element when the overlay panel is shown.
+ * @defaultValue false
+ */
+ autoOptionFocus?: boolean | undefined;
+ /**
+ * Whether to focus on the filter element when the overlay panel is shown.
+ * @defaultValue false
+ */
+ autoFilterFocus?: boolean | undefined;
+ /**
+ * When enabled, the focused option is selected.
+ * @defaultValue false
+ */
+ selectOnFocus?: boolean | undefined;
+ /**
+ * When enabled, the focus is placed on the hovered option.
+ * @defaultValue true
+ */
+ focusOnHover?: boolean | undefined;
+ /**
+ * Whether the selected option will be add highlight class.
+ * @defaultValue true
+ */
+ highlightOnSelect?: boolean | undefined;
+ /**
+ * Whether the selected option will be shown with a check mark.
+ * @defaultValue false
+ */
+ checkmark?: boolean | undefined;
+ /**
+ * Text to be displayed in hidden accessible field when filtering returns any results. Defaults to value from PrimeVue locale configuration.
+ * @defaultValue '{0} results are available'
+ */
+ filterMessage?: string | undefined;
+ /**
+ * Text to be displayed in hidden accessible field when options are selected. Defaults to value from PrimeVue locale configuration.
+ * @defaultValue '{0} items selected'
+ */
+ selectionMessage?: string | undefined;
+ /**
+ * Text to be displayed in hidden accessible field when any option is not selected. Defaults to value from PrimeVue locale configuration.
+ * @defaultValue No selected item
+ */
+ emptySelectionMessage?: string | undefined;
+ /**
+ * Text to display when filtering does not return any results. Defaults to value from PrimeVue locale configuration.
+ * @defaultValue No results found
+ */
+ emptyFilterMessage?: string | undefined;
+ /**
+ * Text to display when there are no options available. Defaults to value from PrimeVue locale configuration.
+ * @defaultValue No available options
+ */
+ emptyMessage?: string | undefined;
+ /**
+ * Index of the element in tabbing order.
+ */
+ tabindex?: number | string | undefined;
+ /**
+ * Defines a string value that labels an interactive element.
+ */
+ ariaLabel?: string | undefined;
+ /**
+ * Identifier of the underlying input element.
+ */
+ ariaLabelledby?: string | undefined;
+ /**
+ * Form control object, typically used for handling validation and form state.
+ */
+ formControl?: Record | undefined;
+}
+
+export default interface IVSelect extends Partial>, Partial {
+ optionTemplate?: boolean
+}
diff --git a/src/components/select/VSelect.vue b/src/components/select/VSelect.vue
new file mode 100644
index 0000000..4bad988
--- /dev/null
+++ b/src/components/select/VSelect.vue
@@ -0,0 +1,186 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/test/VProgressBar.spec.ts b/test/VProgressBar.spec.ts
new file mode 100644
index 0000000..3d87af4
--- /dev/null
+++ b/test/VProgressBar.spec.ts
@@ -0,0 +1,57 @@
+import { describe, it, expect } from 'vitest';
+import { mount } from '@vue/test-utils';
+import VProgressBar from '../src/components/progressbar/VProgressBar.vue';
+
+describe('VProgressBar', () => {
+ it('renders with default props', () => {
+ const wrapper = mount(VProgressBar);
+ const progressBar = wrapper.findComponent({ name: 'ProgressBar' });
+
+ expect(progressBar.exists()).toBe(true);
+ expect(progressBar.props('value')).toBe(0);
+ expect(progressBar.props('mode')).toBe('determinate');
+ expect(progressBar.props('showValue')).toBe(false);
+ });
+
+ it('renders with custom value', () => {
+ const wrapper = mount(VProgressBar, {
+ props: { value: 75 }
+ });
+
+ expect(wrapper.findComponent({ name: 'ProgressBar' }).props('value')).toBe(75);
+ });
+
+ it('renders in indeterminate mode', () => {
+ const wrapper = mount(VProgressBar, {
+ props: { indeterminate: true, value: 75 }
+ });
+
+ expect(wrapper.findComponent({ name: 'ProgressBar' }).props('mode')).toBe('indeterminate');
+ });
+
+ it('hides value when small is true even if showValue is true', () => {
+ const wrapper = mount(VProgressBar, {
+ props: { showValue: true, small: true, value: 25 }
+ });
+
+ expect(wrapper.findComponent({ name: 'ProgressBar' }).props('showValue')).toBe(false);
+ });
+
+ it('applies small class when small is true', () => {
+ const wrapper = mount(VProgressBar, {
+ props: { small: true, value: 15 }
+ });
+
+ expect(wrapper.find('.p-progressbar').classes()).toContain('small');
+ });
+
+ it('renders slot content', () => {
+ const wrapper = mount(VProgressBar, {
+ props: { value: 43, showValue: true },
+ slots: {
+ default: 'Valeur: 43/100'
+ }
+ });
+ expect(wrapper.html()).toContain('Valeur: 43/100');
+ });
+});
diff --git a/test/VSelect.spec.ts b/test/VSelect.spec.ts
new file mode 100644
index 0000000..3e0c8ca
--- /dev/null
+++ b/test/VSelect.spec.ts
@@ -0,0 +1,101 @@
+import { describe, it, expect, vi } from 'vitest';
+import { mount } from '@vue/test-utils';
+import VSelect from '../src/components/select/VSelect.vue';
+import Select from 'primevue/select';
+
+// Mock global matchMedia
+Object.defineProperty(window, 'matchMedia', {
+ writable: true,
+ value: vi.fn().mockImplementation((query) => ({
+ matches: false,
+ media: query,
+ onchange: null,
+ addListener: vi.fn(), // deprecated
+ removeListener: vi.fn(), // deprecated
+ addEventListener: vi.fn(),
+ removeEventListener: vi.fn(),
+ dispatchEvent: vi.fn(),
+ })),
+});
+
+
+const globalConfig = {
+ global: {
+ components: {
+ Checkbox: Select
+ },
+ mocks: {
+ $primevue: {
+ config: {}
+ }
+ },
+ stubs: {
+ VLabel: false,
+ VHint: true,
+ }
+ }
+}
+
+describe('VSelect.vue', () => {
+ const options = [
+ { value: 'apple', text: 'Apple' },
+ { value: 'banana', text: 'Banana' },
+ { value: 'cherry', text: 'Cherry' }
+ ];
+
+ it('renders with basic props', () => {
+ const wrapper = mount(VSelect, {
+ props: {
+ label: 'Fruits',
+ options,
+ modelValue: null
+ },
+ global: globalConfig.global
+ });
+
+ const label = wrapper.findComponent({ name: 'VLabel' });
+ expect(label.exists()).toBe(true);
+ expect(label.props('label')).toBe('Fruits');
+
+ });
+
+ it('emits update:modelValue and change when value is selected', async () => {
+ const wrapper = mount(VSelect, {
+ props: {
+ options,
+ modelValue: null
+ },
+ global: globalConfig.global
+ });
+
+ // Simule la sélection d'une option
+ wrapper.vm.$emit('update:modelValue', 'banana');
+ wrapper.vm.$emit('change', { value: 'banana' });
+
+ const emittedUpdate = wrapper.emitted('update:modelValue');
+ const emittedChange = wrapper.emitted('change');
+
+ expect(emittedUpdate).toBeTruthy();
+ expect(emittedUpdate![0]).toEqual(['banana']);
+
+ expect(emittedChange).toBeTruthy();
+ expect(emittedChange![0]).toEqual([{ value: 'banana' }]);
+ });
+
+ it('reacts to external modelValue changes', async () => {
+ const wrapper = mount(VSelect, {
+ props: {
+ options,
+ modelValue: 'apple'
+ },
+ global: globalConfig.global
+ });
+
+ await wrapper.setProps({ modelValue: 'cherry' });
+ expect(wrapper.emitted('update:modelValue')).toBeFalsy();
+
+ const select = wrapper.findComponent({ name: 'Select' });
+ expect(select.props('modelValue')).toBe('cherry');
+
+ });
+});