diff --git a/CHANGELOG.md b/CHANGELOG.md index 08334de..caf21dd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,14 @@ 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.6] - 2025-07-23 +### Added +- Checkbox component + +### Fixed +- VLabel `required-tip` slot +- VHint UI style + ## [1.0.5] - 2025-07-22 ### Added - Input component diff --git a/README.md b/README.md index 1e65271..6872d61 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,3 @@ # visua-vue -**Current version: v1.0.5** \ No newline at end of file +**Current version: v1.0.6** \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 696bb58..eb36326 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@cellule-financiere-pmo/visua-vue", - "version": "1.0.5", + "version": "1.0.6", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@cellule-financiere-pmo/visua-vue", - "version": "1.0.5", + "version": "1.0.6", "license": "ISC", "dependencies": { "@cellule-financiere-pmo/visua": "1.1.3", diff --git a/package.json b/package.json index e69ad57..4db8cd9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@cellule-financiere-pmo/visua-vue", - "version": "1.0.5", + "version": "1.0.6", "type": "module", "scripts": { "dev": "vite", diff --git a/src/App.vue b/src/App.vue index 42b178a..e582a0d 100644 --- a/src/App.vue +++ b/src/App.vue @@ -3,7 +3,8 @@ // import VButtonGroupView from '../template/VButtonGroupView.vue'; // import VLinkView from '../template/VLinkView.vue'; // import VAccordionView from '../template/VAccordionView.vue'; -import VInputView from '../template/VInputView.vue'; +// import VInputView from '../template/VInputView.vue'; +import VCheckboxView from '../template/VCheckboxView.vue'; @@ -12,5 +13,6 @@ import VInputView from '../template/VInputView.vue'; - + + diff --git a/src/assets/style/primevue-configuration.css b/src/assets/style/primevue-configuration.css index 1ae4ed8..c381491 100644 --- a/src/assets/style/primevue-configuration.css +++ b/src/assets/style/primevue-configuration.css @@ -7,3 +7,4 @@ @import './primevue-style/divider.css'; @import './primevue-style/various.css'; @import './primevue-style/form.css'; +@import './primevue-style/checkbox.css'; diff --git a/src/assets/style/primevue-style/checkbox.css b/src/assets/style/primevue-style/checkbox.css new file mode 100644 index 0000000..635532a --- /dev/null +++ b/src/assets/style/primevue-style/checkbox.css @@ -0,0 +1,32 @@ +:root{ + /* size */ + --p-checkbox-width: 1.5rem; + --p-checkbox-height: 1.5rem; + --p-checkbox-sm-width: 1rem; + --p-checkbox-sm-height: 1rem; + /* border */ + --p-checkbox-border-radius: 0px; + --p-checkbox-border-color: var(--border-action-high-blue-france); + --p-checkbox-hover-border-color: var(--border-action-high-blue-france); + /* background */ + --p-checkbox-background: var(--background-default-grey); + /* icon */ + --p-checkbox-icon-size: 1rem; + --p-checkbox-icon-sm-size: 1rem; + --p-checkbox-icon-color: var(--background-default-grey); + /* focus */ + --p-checkbox-checked-focus-border-color: var(--border-action-high-blue-france); + --p-checkbox-focus-border-color: var(--border-action-high-blue-france); + --p-checkbox-focus-ring-color: var(--focus-color); + --p-checkbox-focus-ring-width: var(--focus-width); + --p-checkbox-focus-ring-style: var(--focus-style); + --p-checkbox-focus-ring-offset: var(--focus-offset); + /* checked */ + --p-checkbox-checked-border-color: var(--border-action-high-blue-france); + --p-checkbox-checked-background: var(--border-active-blue-france); + --p-checkbox-icon-checked-color: var(--background-default-grey); + /* checked:hover */ + --p-checkbox-icon-checked-hover-color: var(--background-default-grey); + --p-checkbox-checked-hover-border-color: var(--border-action-high-blue-france); + --p-checkbox-checked-hover-background: var(--border-active-blue-france); +} diff --git a/src/components/checkbox/IVCheckbox.type.ts b/src/components/checkbox/IVCheckbox.type.ts new file mode 100644 index 0000000..ed4f537 --- /dev/null +++ b/src/components/checkbox/IVCheckbox.type.ts @@ -0,0 +1,89 @@ +/** + * Interface representing the props for a CheckBox Vue component. + */ +export default interface IVCheckBox { + /** + * The unique identifier for the checkbox element. + */ + id?: string; + + /** + * The name attribute for the checkbox input. + */ + name?: string; + + /** + * Indicates whether the checkbox is required in a form. + */ + required?: boolean; + + /** + * The value associated with the checkbox. + */ + value?: unknown; + + /** + * Whether the checkbox is initially checked. + */ + checked?: boolean; + + /** + * The bound value of the checkbox, can be a boolean or an array of values. + */ + modelValue: Array | boolean; + + /** + * If true, renders the checkbox in a smaller size. + */ + small?: boolean; + + /** + * If true, displays the checkbox inline with other elements. + */ + inline?: boolean; + + /** + * If true, the checkbox is read-only and cannot be changed by the user. + */ + readonly?: boolean; + + /** + * Opacity level applied when the checkbox is read-only. + */ + readonlyOpacity?: number; + + /** + * The label text displayed next to the checkbox. + */ + label?: string; + + /** + * The error message shown when validation fails. + */ + errorMessage?: string; + + /** + * The message shown when the checkbox input is valid. + */ + validMessage?: string; + + /** + * Additional hint or helper text displayed below the checkbox. + */ + hint?: string; + + /** + * If true, disables the checkbox input. + */ + disabled?: boolean; + + /** + * Visual style of the checkbox, can be 'normal', 'error', or 'success'. + */ + type?: 'normal' | 'error' | 'success'; + + /** + * If true, the checkbox operates in binary mode (boolean value). + */ + binary?: boolean; +} diff --git a/src/components/checkbox/VCheckbox.vue b/src/components/checkbox/VCheckbox.vue new file mode 100644 index 0000000..9992c58 --- /dev/null +++ b/src/components/checkbox/VCheckbox.vue @@ -0,0 +1,172 @@ + + + + + diff --git a/src/components/hint/VHint.vue b/src/components/hint/VHint.vue index 455181a..1a3b039 100644 --- a/src/components/hint/VHint.vue +++ b/src/components/hint/VHint.vue @@ -39,21 +39,28 @@ const severity = computed(() => { {{ props.title }} + diff --git a/src/components/label/VLabel.vue b/src/components/label/VLabel.vue index ae1cd1e..10a1144 100644 --- a/src/components/label/VLabel.vue +++ b/src/components/label/VLabel.vue @@ -24,12 +24,11 @@ const props = withDefaults(defineProps(), { :aria-disabled="props.disabled" > {{ props.label }} - - * - + (), { .success {color: var(--text-default-success);} .error {color: var(--text-default-error);} .disabled {color: var(--text-disabled-grey);} -.required {color: var(--minor-red-marianne);} +.required { + color: var(--minor-red-marianne); + display: inline; +} diff --git a/test/VCheckbox.spec.ts b/test/VCheckbox.spec.ts new file mode 100644 index 0000000..acce989 --- /dev/null +++ b/test/VCheckbox.spec.ts @@ -0,0 +1,98 @@ +import { describe, it, expect } from 'vitest' +import { mount } from '@vue/test-utils' +import VCheckbox from '../src/components/checkbox/VCheckbox.vue' +import PrimeCheckbox from 'primevue/checkbox' + +const globalConfig = { + global: { + components: { + Checkbox: PrimeCheckbox + }, + mocks: { + $primevue: { + config: {} + } + }, + stubs: { + VLabel: true, + VHint: true, + } + } +} + +describe('VCheckbox.vue', () => { + it('adds value to modelValue array when checkbox is clicked (multiple mode)', async () => { + const wrapper = mount(VCheckbox, { + props: { + label: 'Fruit', + modelValue: [], + value: 'pomme' + }, + global: globalConfig.global + }); + + const input = wrapper.find('input[type="checkbox"]'); + await input.setValue(true); + + const emitted = wrapper.emitted('update:modelValue'); + expect(emitted).toBeTruthy(); + expect(emitted?.[0]?.[0]).toContain('pomme'); + }); + + it('removes value from modelValue array when checkbox is unchecked (multiple mode)', async () => { + const wrapper = mount(VCheckbox, { + props: { + label: 'Fruit', + modelValue: ['pomme'], + value: 'pomme' + }, + global: globalConfig.global + }); + + const input = wrapper.find('input[type="checkbox"]'); + await input.setValue(false); + + const emitted = wrapper.emitted('update:modelValue'); + expect(emitted).toBeTruthy(); + expect(emitted?.[0]?.[0]).not.toContain('pomme'); + }); + + it('emits change, focus and blur events', async () => { + const wrapper = mount(VCheckbox, { + props: { + label: 'Test Checkbox', + modelValue: false + }, + global: globalConfig.global + }); + const input = wrapper.find('input[type="checkbox"]'); + await input.trigger('focus'); + await input.trigger('blur'); + await input.trigger('change'); + expect(wrapper.emitted('focus')).toBeTruthy(); + expect(wrapper.emitted('blur')).toBeTruthy(); + expect(wrapper.emitted('change')).toBeTruthy(); + }); + + it('applies error and success classes based on props', () => { + const errorWrapper = mount(VCheckbox, { + props: { + label: 'Error Checkbox', + modelValue: false, + errorMessage: 'Error occurred' + }, + global: globalConfig.global + }); + expect(errorWrapper.find('.p-checkbox.error').exists()).toBe(true); + + const successWrapper = mount(VCheckbox, { + props: { + label: 'Success Checkbox', + modelValue: true, + validMessage: 'Valid input' + }, + global: globalConfig.global + }); + expect(successWrapper.find('.p-checkbox.success').exists()).toBe(true); + }); +});