feat(ui) : prop required + aria-required + astérisque sur Select/SelectCheckbox/Upload/RichText
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -19,6 +19,7 @@ type InputRichTextProps = {
|
||||
groupClass?: string
|
||||
labelClass?: string
|
||||
editorClass?: string
|
||||
required?: boolean
|
||||
}
|
||||
|
||||
const InputRichTextForTest = InputRichText as DefineComponent<InputRichTextProps>
|
||||
@@ -162,4 +163,16 @@ describe('MalioInputRichText', () => {
|
||||
expect(html).toContain('Mon titre')
|
||||
expect(html).toContain('Un paragraphe.')
|
||||
})
|
||||
|
||||
it('affiche l\'astérisque quand required est vrai', async () => {
|
||||
const wrapper = await mountComponent({label: 'Champ', required: true})
|
||||
|
||||
expect(wrapper.find('[data-test="required-mark"]').exists()).toBe(true)
|
||||
})
|
||||
|
||||
it('n\'affiche pas l\'astérisque par défaut', async () => {
|
||||
const wrapper = await mountComponent({label: 'Champ'})
|
||||
|
||||
expect(wrapper.find('[data-test="required-mark"]').exists()).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
:for="editorId"
|
||||
:class="mergedLabelClass"
|
||||
>
|
||||
{{ label }}
|
||||
{{ label }}<MalioRequiredMark v-if="required" />
|
||||
</label>
|
||||
|
||||
<!-- Mode lecture seule (rendu uniquement) -->
|
||||
@@ -22,6 +22,7 @@
|
||||
v-else
|
||||
:id="editorId"
|
||||
:class="mergedEditorWrapperClass"
|
||||
:aria-required="required || undefined"
|
||||
@click="focusEditor"
|
||||
>
|
||||
<div
|
||||
@@ -202,6 +203,7 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, onBeforeUnmount, onMounted, ref, shallowRef, useId, watch } from 'vue'
|
||||
import { Icon as IconifyIcon } from '@iconify/vue'
|
||||
import MalioRequiredMark from '../shared/RequiredMark.vue'
|
||||
import { Editor, EditorContent } from '@tiptap/vue-3'
|
||||
import StarterKit from '@tiptap/starter-kit'
|
||||
import Placeholder from '@tiptap/extension-placeholder'
|
||||
@@ -232,6 +234,7 @@ const props = withDefaults(
|
||||
groupClass?: string
|
||||
labelClass?: string
|
||||
editorClass?: string
|
||||
required?: boolean
|
||||
}>(),
|
||||
{
|
||||
id: '',
|
||||
@@ -249,6 +252,7 @@ const props = withDefaults(
|
||||
groupClass: '',
|
||||
labelClass: '',
|
||||
editorClass: '',
|
||||
required: false,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ type InputUploadProps = {
|
||||
success?: string
|
||||
displayIcon?: boolean
|
||||
accept?: string
|
||||
required?: boolean
|
||||
}
|
||||
|
||||
const InputUploadForTest = InputUpload as DefineComponent<InputUploadProps>
|
||||
@@ -186,4 +187,16 @@ describe('MalioInputUpload', () => {
|
||||
|
||||
expect(wrapper.get('[data-test="icon"]').classes()).toContain('text-black')
|
||||
})
|
||||
|
||||
it('affiche l\'astérisque quand required est vrai', () => {
|
||||
const wrapper = mountComponent({label: 'Champ', required: true})
|
||||
|
||||
expect(wrapper.find('[data-test="required-mark"]').exists()).toBe(true)
|
||||
})
|
||||
|
||||
it('n\'affiche pas l\'astérisque par défaut', () => {
|
||||
const wrapper = mountComponent({label: 'Champ'})
|
||||
|
||||
expect(wrapper.find('[data-test="required-mark"]').exists()).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
:accept="accept"
|
||||
class="hidden"
|
||||
:disabled="disabled"
|
||||
:required="required"
|
||||
@change="onFileChange"
|
||||
>
|
||||
|
||||
@@ -33,7 +34,7 @@
|
||||
:for="inputId"
|
||||
:class="mergedLabelClass"
|
||||
>
|
||||
{{ label }}
|
||||
{{ label }}<MalioRequiredMark v-if="required" />
|
||||
</label>
|
||||
|
||||
<IconifyIcon
|
||||
@@ -70,6 +71,7 @@
|
||||
import {computed, ref, useAttrs, useId} from 'vue'
|
||||
import { Icon as IconifyIcon } from '@iconify/vue'
|
||||
import {twMerge} from 'tailwind-merge'
|
||||
import MalioRequiredMark from '../shared/RequiredMark.vue'
|
||||
|
||||
defineOptions({name: 'MalioInputUpload', inheritAttrs: false})
|
||||
|
||||
@@ -87,6 +89,7 @@ const props = withDefaults(
|
||||
success?: string
|
||||
displayIcon?: boolean
|
||||
accept?: string
|
||||
required?: boolean
|
||||
}>(),
|
||||
{
|
||||
id: '',
|
||||
@@ -101,6 +104,7 @@ const props = withDefaults(
|
||||
success: '',
|
||||
displayIcon: true,
|
||||
accept: '',
|
||||
required: false,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ type SelectProps = {
|
||||
textLabel?: string
|
||||
rounded?: string
|
||||
disabled?: boolean
|
||||
required?: boolean
|
||||
}
|
||||
|
||||
const SelectForTest = Select as DefineComponent<SelectProps>
|
||||
@@ -260,6 +261,22 @@ describe('MalioSelect', () => {
|
||||
expect(wrapper.get('[data-test="chevron"]').classes()).toContain('text-m-success')
|
||||
})
|
||||
|
||||
it('affiche l\'astérisque quand required est vrai', () => {
|
||||
const wrapper = mount(SelectForTest, {
|
||||
props: {modelValue: null, label: 'Champ', required: true},
|
||||
})
|
||||
|
||||
expect(wrapper.find('[data-test="required-mark"]').exists()).toBe(true)
|
||||
})
|
||||
|
||||
it('n\'affiche pas l\'astérisque par défaut', () => {
|
||||
const wrapper = mount(SelectForTest, {
|
||||
props: {modelValue: null, label: 'Champ'},
|
||||
})
|
||||
|
||||
expect(wrapper.find('[data-test="required-mark"]').exists()).toBe(false)
|
||||
})
|
||||
|
||||
it('keeps the bottom border allocation when open downward (transparent, not zero)', async () => {
|
||||
const wrapper = mount(SelectForTest, {
|
||||
props: {modelValue: null, options},
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
:aria-controls="listboxId"
|
||||
:aria-invalid="hasError"
|
||||
:aria-describedby="describedBy"
|
||||
:aria-required="required || undefined"
|
||||
:disabled="disabled"
|
||||
@click="toggle"
|
||||
>
|
||||
@@ -59,7 +60,7 @@
|
||||
]"
|
||||
:style="labelTransformStyle"
|
||||
>
|
||||
{{ label }}
|
||||
{{ label }}<MalioRequiredMark v-if="required" />
|
||||
</label>
|
||||
|
||||
<span
|
||||
@@ -171,6 +172,7 @@
|
||||
import {computed, onBeforeUnmount, onMounted, ref, useId, nextTick} from 'vue'
|
||||
import {Icon as IconifyIcon} from '@iconify/vue'
|
||||
import {twMerge} from 'tailwind-merge'
|
||||
import MalioRequiredMark from '../shared/RequiredMark.vue'
|
||||
|
||||
defineOptions({name: 'MalioSelect', inheritAttrs: false})
|
||||
|
||||
@@ -193,6 +195,7 @@ const props = withDefaults(defineProps<{
|
||||
disabled?: boolean
|
||||
groupClass?: string
|
||||
noOptionsText?: string
|
||||
required?: boolean
|
||||
}>(), {
|
||||
options: () => [],
|
||||
emptyOptionLabel: '',
|
||||
@@ -207,6 +210,7 @@ const props = withDefaults(defineProps<{
|
||||
disabled: false,
|
||||
groupClass: '',
|
||||
noOptionsText: 'Aucune option disponible',
|
||||
required: false,
|
||||
})
|
||||
|
||||
const emit = defineEmits<{
|
||||
|
||||
@@ -25,6 +25,7 @@ type SelectCheckboxProps = {
|
||||
selectAllLabel?: string
|
||||
disabled?: boolean
|
||||
groupClass?: string
|
||||
required?: boolean
|
||||
}
|
||||
|
||||
const SelectCheckboxForTest = SelectCheckbox as DefineComponent<SelectCheckboxProps>
|
||||
@@ -235,6 +236,22 @@ describe('MalioSelectCheckbox', () => {
|
||||
expect(wrapper.get('[data-test="chevron"]').classes()).toContain('text-m-success')
|
||||
})
|
||||
|
||||
it('affiche l\'astérisque quand required est vrai', () => {
|
||||
const wrapper = mount(SelectCheckboxForTest, {
|
||||
props: {modelValue: [], label: 'Champ', required: true},
|
||||
})
|
||||
|
||||
expect(wrapper.find('[data-test="required-mark"]').exists()).toBe(true)
|
||||
})
|
||||
|
||||
it('n\'affiche pas l\'astérisque par défaut', () => {
|
||||
const wrapper = mount(SelectCheckboxForTest, {
|
||||
props: {modelValue: [], label: 'Champ'},
|
||||
})
|
||||
|
||||
expect(wrapper.find('[data-test="required-mark"]').exists()).toBe(false)
|
||||
})
|
||||
|
||||
it('keeps the bottom border allocation when open downward (transparent, not zero)', async () => {
|
||||
const wrapper = mount(SelectCheckboxForTest, {
|
||||
props: {modelValue: [], options},
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
:aria-controls="listboxId"
|
||||
:aria-invalid="hasError"
|
||||
:aria-describedby="describedBy"
|
||||
:aria-required="required || undefined"
|
||||
:disabled="disabled"
|
||||
@click="toggle"
|
||||
>
|
||||
@@ -59,7 +60,7 @@
|
||||
]"
|
||||
:style="labelTransformStyle"
|
||||
>
|
||||
{{ label }}
|
||||
{{ label }}<MalioRequiredMark v-if="required" />
|
||||
</label>
|
||||
|
||||
<div
|
||||
@@ -221,6 +222,7 @@ import {computed, onBeforeUnmount, onMounted, ref, useId, nextTick} from 'vue'
|
||||
import {Icon as IconifyIcon} from '@iconify/vue'
|
||||
import {twMerge} from 'tailwind-merge'
|
||||
import Checkbox from '../checkbox/Checkbox.vue'
|
||||
import MalioRequiredMark from '../shared/RequiredMark.vue'
|
||||
|
||||
defineOptions({name: 'MalioSelectCheckbox', inheritAttrs: false})
|
||||
|
||||
@@ -246,6 +248,7 @@ const props = withDefaults(defineProps<{
|
||||
disabled?: boolean
|
||||
groupClass?: string
|
||||
noOptionsText?: string
|
||||
required?: boolean
|
||||
}>(), {
|
||||
options: () => [],
|
||||
emptyOptionLabel: '',
|
||||
@@ -263,6 +266,7 @@ const props = withDefaults(defineProps<{
|
||||
disabled: false,
|
||||
groupClass: '',
|
||||
noOptionsText: 'Aucune option disponible',
|
||||
required: false,
|
||||
})
|
||||
|
||||
const emit = defineEmits<{
|
||||
|
||||
Reference in New Issue
Block a user