Cette section documente tous les composants réutilisables de l'application, organisés par catégorie. Chaque composant est conçu pour être modulaire, réutilisable et facilement personnalisable.
components/
└── feature/
├── ComponentName.vue # Le composant principal
├── ComponentName.ts # Logique TypeScript
├── ComponentName.spec.ts # Tests
└── index.ts # Export du composant
EquipmentCard.vue)<template>
<div class="component-name">
<!-- Contenu du composant -->
</div>
</template>
<script setup lang="ts">
// Imports
import { ref } from 'vue';
// Props
defineProps({
/** Description de la prop */
title: {
type: String,
required: true,
validator: (value: string) => value.length > 0
}
});
// Événements
const emit = defineEmits<{
(e: 'update:modelValue', value: string): void;
(e: 'submit'): void;
}>();
// Logique du composant
const count = ref(0);
</script>
<styles scoped>
.component-name {
/* Styles spécifiques au composant */
}
</styles>
scopedv-if au lieu de v-show pour les éléments rarement affichésv-memo<template>
<div class="component-name">
<slot />
</div>
</template>
<script setup lang="ts">
// Imports
import { ref } from 'vue';
// Props
const props = defineProps({
/**
* Titre du composant
* @required
*/
title: {
type: String,
required: true
},
/**
* État actif
* @default false
*/
active: {
type: Boolean,
default: false
}
});
// Événements
const emit = defineEmits<{
/** Émis lors du clic */
(e: 'click', event: MouseEvent): void;
/** Émis lors de la mise à jour de la valeur */
(e: 'update:modelValue', value: string): void;
}>();
// Logique du composant
const count = ref(0);
// Méthodes exposées
defineExpose({
/** Réinitialise le compteur */
reset: () => {
count.value = 0;
}
});
</script>
<style scoped>
.component-name {
/* Styles de base */
padding: 1rem;
border: 1px solid #e2e8f0;
border-radius: 0.375rem;
/* Styles conditionnels */
&[data-active] {
border-color: #3b82f6;
background-color: #eff6ff;
}
}
</style>
// tests/components/__tests__/ComponentName.spec.ts
import { mount } from '@vue/test-utils';
import { describe, it, expect } from 'vitest';
import ComponentName from '../ComponentName.vue';
describe('ComponentName', () => {
it('affiche le titre', () => {
const wrapper = mount(ComponentName, {
props: {
title: 'Mon Titre'
}
});
expect(wrapper.text()).toContain('Mon Titre');
});
it('émet un événement au clic', async () => {
const wrapper = mount(ComponentName);
await wrapper.trigger('click');
expect(wrapper.emitted('click')).toHaveLength(1);
});
});