@htlkg/components 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/dist/composables/index.js +388 -0
  2. package/dist/composables/index.js.map +1 -0
  3. package/package.json +41 -0
  4. package/src/composables/index.ts +6 -0
  5. package/src/composables/useForm.test.ts +229 -0
  6. package/src/composables/useForm.ts +130 -0
  7. package/src/composables/useFormValidation.test.ts +189 -0
  8. package/src/composables/useFormValidation.ts +83 -0
  9. package/src/composables/useModal.property.test.ts +164 -0
  10. package/src/composables/useModal.ts +43 -0
  11. package/src/composables/useNotifications.test.ts +166 -0
  12. package/src/composables/useNotifications.ts +81 -0
  13. package/src/composables/useTable.property.test.ts +198 -0
  14. package/src/composables/useTable.ts +134 -0
  15. package/src/composables/useTabs.property.test.ts +247 -0
  16. package/src/composables/useTabs.ts +101 -0
  17. package/src/data/Chart.demo.vue +340 -0
  18. package/src/data/Chart.md +525 -0
  19. package/src/data/Chart.vue +133 -0
  20. package/src/data/DataList.md +80 -0
  21. package/src/data/DataList.test.ts +69 -0
  22. package/src/data/DataList.vue +46 -0
  23. package/src/data/SearchableSelect.md +107 -0
  24. package/src/data/SearchableSelect.vue +124 -0
  25. package/src/data/Table.demo.vue +296 -0
  26. package/src/data/Table.md +588 -0
  27. package/src/data/Table.property.test.ts +548 -0
  28. package/src/data/Table.test.ts +562 -0
  29. package/src/data/Table.unit.test.ts +544 -0
  30. package/src/data/Table.vue +321 -0
  31. package/src/data/index.ts +5 -0
  32. package/src/domain/BrandCard.md +81 -0
  33. package/src/domain/BrandCard.vue +63 -0
  34. package/src/domain/BrandSelector.md +84 -0
  35. package/src/domain/BrandSelector.vue +65 -0
  36. package/src/domain/ProductBadge.md +60 -0
  37. package/src/domain/ProductBadge.vue +47 -0
  38. package/src/domain/UserAvatar.md +84 -0
  39. package/src/domain/UserAvatar.vue +60 -0
  40. package/src/domain/domain-components.property.test.ts +449 -0
  41. package/src/domain/index.ts +4 -0
  42. package/src/forms/DateRange.demo.vue +273 -0
  43. package/src/forms/DateRange.md +337 -0
  44. package/src/forms/DateRange.vue +110 -0
  45. package/src/forms/JsonSchemaForm.demo.vue +549 -0
  46. package/src/forms/JsonSchemaForm.md +112 -0
  47. package/src/forms/JsonSchemaForm.property.test.ts +817 -0
  48. package/src/forms/JsonSchemaForm.test.ts +601 -0
  49. package/src/forms/JsonSchemaForm.unit.test.ts +801 -0
  50. package/src/forms/JsonSchemaForm.vue +615 -0
  51. package/src/forms/index.ts +3 -0
  52. package/src/index.ts +17 -0
  53. package/src/navigation/Breadcrumbs.demo.vue +142 -0
  54. package/src/navigation/Breadcrumbs.md +102 -0
  55. package/src/navigation/Breadcrumbs.test.ts +69 -0
  56. package/src/navigation/Breadcrumbs.vue +58 -0
  57. package/src/navigation/Stepper.demo.vue +337 -0
  58. package/src/navigation/Stepper.md +174 -0
  59. package/src/navigation/Stepper.vue +146 -0
  60. package/src/navigation/Tabs.demo.vue +293 -0
  61. package/src/navigation/Tabs.md +163 -0
  62. package/src/navigation/Tabs.test.ts +176 -0
  63. package/src/navigation/Tabs.vue +104 -0
  64. package/src/navigation/index.ts +5 -0
  65. package/src/overlays/Alert.demo.vue +377 -0
  66. package/src/overlays/Alert.md +248 -0
  67. package/src/overlays/Alert.test.ts +166 -0
  68. package/src/overlays/Alert.vue +70 -0
  69. package/src/overlays/Drawer.md +140 -0
  70. package/src/overlays/Drawer.test.ts +92 -0
  71. package/src/overlays/Drawer.vue +76 -0
  72. package/src/overlays/Modal.demo.vue +149 -0
  73. package/src/overlays/Modal.md +385 -0
  74. package/src/overlays/Modal.test.ts +128 -0
  75. package/src/overlays/Modal.vue +86 -0
  76. package/src/overlays/Notification.md +150 -0
  77. package/src/overlays/Notification.test.ts +96 -0
  78. package/src/overlays/Notification.vue +58 -0
  79. package/src/overlays/index.ts +4 -0
@@ -0,0 +1,248 @@
1
+ # Alert Component
2
+
3
+ A stateful Vue wrapper around `@hotelinking/ui`'s `uiAlert` component. Provides visual alerts for information, success, warnings, and errors with optional actions.
4
+
5
+ ## Features
6
+
7
+ - **Four alert types**: info, success, warning, danger
8
+ - **v-model support**: Two-way binding for show/hide state
9
+ - **Action buttons**: Optional clickable actions with custom events
10
+ - **Loading state**: Skeleton loading indicator
11
+ - **Rich content**: Slot support for custom HTML content
12
+ - **Exposed methods**: Programmatic show/hide/toggle control
13
+
14
+ ## Import
15
+
16
+ ```typescript
17
+ import { Alert } from '@htlkg/components';
18
+ // or
19
+ import { Alert } from '@htlkg/components/overlays';
20
+ ```
21
+
22
+ ## Props
23
+
24
+ | Prop | Type | Default | Description |
25
+ |------|------|---------|-------------|
26
+ | `title` | `string` | required | Alert title |
27
+ | `type` | `'info' \| 'success' \| 'warning' \| 'danger'` | `'info'` | Alert type (affects colors and icon) |
28
+ | `actions` | `Array<{name: string, event: string}>` | `[]` | Action buttons to display |
29
+ | `loading` | `boolean` | `false` | Show loading skeleton |
30
+ | `show` | `boolean` | `true` | Control visibility (v-model) |
31
+
32
+ ## Events
33
+
34
+ | Event | Payload | Description |
35
+ |-------|---------|-------------|
36
+ | `update:show` | `boolean` | Emitted when visibility changes (v-model) |
37
+ | `alertEvent` | `string` | Emitted when action button is clicked (event value) |
38
+ | `close` | - | Emitted when alert is hidden |
39
+
40
+ ## Slots
41
+
42
+ | Slot | Description |
43
+ |------|-------------|
44
+ | `default` | Alert content (supports HTML) |
45
+
46
+ ## Exposed Methods
47
+
48
+ | Method | Description |
49
+ |--------|-------------|
50
+ | `show()` | Show the alert |
51
+ | `hide()` | Hide the alert |
52
+ | `toggle()` | Toggle alert visibility |
53
+
54
+ ## Usage Examples
55
+
56
+ ### Basic Info Alert
57
+
58
+ ```vue
59
+ <script setup>
60
+ import { ref } from 'vue';
61
+ import { Alert } from '@htlkg/components';
62
+
63
+ const showAlert = ref(true);
64
+ </script>
65
+
66
+ <template>
67
+ <Alert
68
+ v-model:show="showAlert"
69
+ title="System Update"
70
+ type="info"
71
+ >
72
+ We've updated our Design System to version 1.0.
73
+ </Alert>
74
+ </template>
75
+ ```
76
+
77
+ ### Success Alert
78
+
79
+ ```vue
80
+ <template>
81
+ <Alert
82
+ title="Operation Completed"
83
+ type="success"
84
+ >
85
+ The reservation has been created successfully.
86
+ </Alert>
87
+ </template>
88
+ ```
89
+
90
+ ### Warning Alert with Actions
91
+
92
+ ```vue
93
+ <script setup>
94
+ import { ref } from 'vue';
95
+ import { Alert } from '@htlkg/components';
96
+
97
+ const showWarning = ref(true);
98
+
99
+ const actions = [
100
+ { name: 'Confirm', event: 'confirm' },
101
+ { name: 'Cancel', event: 'cancel' }
102
+ ];
103
+
104
+ const handleAction = (event) => {
105
+ if (event === 'confirm') {
106
+ // Proceed with action
107
+ showWarning.value = false;
108
+ } else if (event === 'cancel') {
109
+ showWarning.value = false;
110
+ }
111
+ };
112
+ </script>
113
+
114
+ <template>
115
+ <Alert
116
+ v-model:show="showWarning"
117
+ title="Confirm Deletion"
118
+ type="warning"
119
+ :actions="actions"
120
+ @alertEvent="handleAction"
121
+ >
122
+ This action cannot be undone. Are you sure?
123
+ </Alert>
124
+ </template>
125
+ ```
126
+
127
+ ### Danger Alert
128
+
129
+ ```vue
130
+ <template>
131
+ <Alert
132
+ title="Error Processing Request"
133
+ type="danger"
134
+ >
135
+ Could not connect to the server. Please try again later.
136
+ </Alert>
137
+ </template>
138
+ ```
139
+
140
+ ### Loading State
141
+
142
+ ```vue
143
+ <script setup>
144
+ import { ref, onMounted } from 'vue';
145
+ import { Alert } from '@htlkg/components';
146
+
147
+ const isLoading = ref(true);
148
+
149
+ onMounted(async () => {
150
+ await fetchData();
151
+ isLoading.value = false;
152
+ });
153
+ </script>
154
+
155
+ <template>
156
+ <Alert
157
+ title="Loading Information..."
158
+ type="info"
159
+ :loading="isLoading"
160
+ >
161
+ Preparing system update
162
+ </Alert>
163
+ </template>
164
+ ```
165
+
166
+ ### Rich Content with HTML
167
+
168
+ ```vue
169
+ <template>
170
+ <Alert
171
+ title="New Features Available"
172
+ type="success"
173
+ :actions="[
174
+ { name: 'Explore', event: 'explore' },
175
+ { name: 'Later', event: 'later' }
176
+ ]"
177
+ >
178
+ <p class="mb-2">
179
+ <strong>Update 1.0:</strong> We've added new features:
180
+ </p>
181
+ <ul class="list-disc list-inside space-y-1">
182
+ <li>Improved pricing calendar</li>
183
+ <li>New notification system</li>
184
+ <li>Performance optimization</li>
185
+ </ul>
186
+ </Alert>
187
+ </template>
188
+ ```
189
+
190
+ ### Programmatic Control
191
+
192
+ ```vue
193
+ <script setup>
194
+ import { ref } from 'vue';
195
+ import { Alert } from '@htlkg/components';
196
+
197
+ const alertRef = ref();
198
+
199
+ const showAlert = () => alertRef.value?.show();
200
+ const hideAlert = () => alertRef.value?.hide();
201
+ const toggleAlert = () => alertRef.value?.toggle();
202
+ </script>
203
+
204
+ <template>
205
+ <div>
206
+ <button @click="showAlert">Show</button>
207
+ <button @click="hideAlert">Hide</button>
208
+ <button @click="toggleAlert">Toggle</button>
209
+
210
+ <Alert
211
+ ref="alertRef"
212
+ title="Controlled Alert"
213
+ type="info"
214
+ >
215
+ This alert is controlled programmatically.
216
+ </Alert>
217
+ </div>
218
+ </template>
219
+ ```
220
+
221
+ ## Alert Types
222
+
223
+ | Type | Color Scheme | Icon | Use Case |
224
+ |------|--------------|------|----------|
225
+ | `info` | Lime | ℹ️ Information | General information, updates |
226
+ | `success` | Green | ✅ Check | Successful operations, confirmations |
227
+ | `warning` | Yellow | ⚠️ Warning | Important warnings, confirmations needed |
228
+ | `danger` | Red | ❌ Error | Errors, critical issues |
229
+
230
+ ## Design System Integration
231
+
232
+ This component wraps `@hotelinking/ui`'s `uiAlert` component, providing:
233
+
234
+ - Reactive state management with Vue 3 Composition API
235
+ - v-model support for show/hide state
236
+ - Exposed methods for programmatic control
237
+ - Event handling for action buttons
238
+ - Consistent API with other overlay components (Modal, Notification, Drawer)
239
+
240
+ ## Related Components
241
+
242
+ - **Notification**: For toast-style notifications
243
+ - **Modal**: For dialog overlays
244
+ - **Drawer**: For side panel overlays
245
+
246
+ ## Demo
247
+
248
+ See the [Alert demo page](/components/alert) for interactive examples.
@@ -0,0 +1,166 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { mount } from '@vue/test-utils';
3
+ import Alert from './Alert.vue';
4
+
5
+ describe('Alert Component', () => {
6
+ it('renders with basic props', () => {
7
+ const wrapper = mount(Alert, {
8
+ props: {
9
+ title: 'Test Alert',
10
+ type: 'info'
11
+ }
12
+ });
13
+
14
+ expect(wrapper.exists()).toBe(true);
15
+ });
16
+
17
+ it('renders different alert types', () => {
18
+ const types = ['info', 'success', 'warning', 'danger'] as const;
19
+
20
+ types.forEach(type => {
21
+ const wrapper = mount(Alert, {
22
+ props: {
23
+ title: 'Test Alert',
24
+ type
25
+ }
26
+ });
27
+
28
+ expect(wrapper.exists()).toBe(true);
29
+ });
30
+ });
31
+
32
+ it('renders with actions', () => {
33
+ const actions = [
34
+ { name: 'Confirm', event: 'confirm' },
35
+ { name: 'Cancel', event: 'cancel' }
36
+ ];
37
+
38
+ const wrapper = mount(Alert, {
39
+ props: {
40
+ title: 'Test Alert',
41
+ type: 'warning',
42
+ actions
43
+ }
44
+ });
45
+
46
+ expect(wrapper.exists()).toBe(true);
47
+ expect(wrapper.props('actions')).toEqual(actions);
48
+ });
49
+
50
+ it('emits alertEvent when action is triggered', async () => {
51
+ const wrapper = mount(Alert, {
52
+ props: {
53
+ title: 'Test Alert',
54
+ type: 'info',
55
+ actions: [{ name: 'OK', event: 'ok' }]
56
+ }
57
+ });
58
+
59
+ const component = wrapper.vm as any;
60
+ if (component.handleAlertEvent) {
61
+ component.handleAlertEvent('ok');
62
+ expect(wrapper.emitted('alertEvent')).toBeTruthy();
63
+ expect(wrapper.emitted('alertEvent')?.[0]).toEqual(['ok']);
64
+ }
65
+ });
66
+
67
+ it('renders with show prop', () => {
68
+ const wrapper = mount(Alert, {
69
+ props: {
70
+ title: 'Test Alert',
71
+ type: 'info',
72
+ show: true
73
+ }
74
+ });
75
+
76
+ expect(wrapper.exists()).toBe(true);
77
+ expect(wrapper.props('show')).toBe(true);
78
+ });
79
+
80
+ it('hides when show is false', () => {
81
+ const wrapper = mount(Alert, {
82
+ props: {
83
+ title: 'Test Alert',
84
+ type: 'info',
85
+ show: false
86
+ }
87
+ });
88
+
89
+ expect(wrapper.find('div').exists()).toBe(false);
90
+ });
91
+
92
+ it('supports v-model for show state', async () => {
93
+ const wrapper = mount(Alert, {
94
+ props: {
95
+ title: 'Test Alert',
96
+ type: 'info',
97
+ show: false,
98
+ 'onUpdate:show': (value: boolean) => wrapper.setProps({ show: value })
99
+ }
100
+ });
101
+
102
+ const component = wrapper.vm as any;
103
+ component.show();
104
+
105
+ await wrapper.vm.$nextTick();
106
+ expect(wrapper.emitted('update:show')).toBeTruthy();
107
+ });
108
+
109
+ it('exposes show, hide, and toggle methods', () => {
110
+ const wrapper = mount(Alert, {
111
+ props: {
112
+ title: 'Test Alert',
113
+ type: 'info'
114
+ }
115
+ });
116
+
117
+ const component = wrapper.vm as any;
118
+ expect(component.show).toBeDefined();
119
+ expect(component.hide).toBeDefined();
120
+ expect(component.toggle).toBeDefined();
121
+ });
122
+
123
+ it('renders with loading state', () => {
124
+ const wrapper = mount(Alert, {
125
+ props: {
126
+ title: 'Loading Alert',
127
+ type: 'info',
128
+ loading: true
129
+ }
130
+ });
131
+
132
+ expect(wrapper.exists()).toBe(true);
133
+ expect(wrapper.props('loading')).toBe(true);
134
+ });
135
+
136
+ it('renders slot content', () => {
137
+ const wrapper = mount(Alert, {
138
+ props: {
139
+ title: 'Test Alert',
140
+ type: 'info'
141
+ },
142
+ slots: {
143
+ default: '<p>Custom alert content</p>'
144
+ }
145
+ });
146
+
147
+ expect(wrapper.html()).toContain('Custom alert content');
148
+ });
149
+
150
+ it('emits close event when hidden', async () => {
151
+ const wrapper = mount(Alert, {
152
+ props: {
153
+ title: 'Test Alert',
154
+ type: 'info',
155
+ show: true
156
+ }
157
+ });
158
+
159
+ const component = wrapper.vm as any;
160
+ component.hide();
161
+
162
+ await wrapper.vm.$nextTick();
163
+ expect(wrapper.emitted('close')).toBeTruthy();
164
+ expect(wrapper.emitted('update:show')).toBeTruthy();
165
+ });
166
+ });
@@ -0,0 +1,70 @@
1
+ <script setup lang="ts">
2
+ import { computed } from 'vue';
3
+ import { uiAlert, type UiAlertInterface } from '@hotelinking/ui';
4
+
5
+ interface Props {
6
+ title: string;
7
+ type?: UiAlertInterface['type'];
8
+ actions?: UiAlertInterface['actions'];
9
+ loading?: boolean;
10
+ show?: boolean;
11
+ }
12
+
13
+ const props = withDefaults(defineProps<Props>(), {
14
+ type: 'info',
15
+ actions: () => [],
16
+ loading: false,
17
+ show: true
18
+ });
19
+
20
+ const emit = defineEmits<{
21
+ 'update:show': [value: boolean];
22
+ 'alertEvent': [event: string];
23
+ 'close': [];
24
+ }>();
25
+
26
+ // Internal state synced with v-model
27
+ const isVisible = computed({
28
+ get: () => props.show,
29
+ set: (value: boolean) => {
30
+ emit('update:show', value);
31
+ if (!value) {
32
+ emit('close');
33
+ }
34
+ }
35
+ });
36
+
37
+ // Convert to uiAlert format
38
+ const alertConfig = computed<UiAlertInterface>(() => ({
39
+ title: props.title,
40
+ type: props.type,
41
+ actions: props.actions,
42
+ loading: props.loading
43
+ }));
44
+
45
+ // Handle alert action events from uiAlert
46
+ function handleAlertEvent(event: string) {
47
+ emit('alertEvent', event);
48
+ }
49
+
50
+ // Expose methods for parent components
51
+ defineExpose({
52
+ show: () => { isVisible.value = true; },
53
+ hide: () => { isVisible.value = false; },
54
+ toggle: () => { isVisible.value = !isVisible.value; }
55
+ });
56
+ </script>
57
+
58
+ <template>
59
+ <div v-if="isVisible">
60
+ <uiAlert
61
+ :title="alertConfig.title"
62
+ :type="alertConfig.type"
63
+ :actions="alertConfig.actions"
64
+ :loading="alertConfig.loading"
65
+ @alert-event="handleAlertEvent"
66
+ >
67
+ <slot />
68
+ </uiAlert>
69
+ </div>
70
+ </template>
@@ -0,0 +1,140 @@
1
+ # Drawer Component
2
+
3
+ A side panel overlay component for displaying additional content without leaving the current page.
4
+
5
+ ## Features
6
+
7
+ - **Four positions**: Left, right, top, bottom
8
+ - **v-model support**: Two-way binding for open/close state
9
+ - **Overlay backdrop**: Darkens background when open
10
+ - **Exposed methods**: Programmatic open/close/toggle control
11
+ - **Responsive**: Adapts to mobile screens
12
+
13
+ ## Import
14
+
15
+ ```typescript
16
+ import { Drawer } from '@htlkg/components';
17
+ // or
18
+ import { Drawer } from '@htlkg/components/overlays';
19
+ ```
20
+
21
+ ## Props
22
+
23
+ | Prop | Type | Default | Description |
24
+ |------|------|---------|-------------|
25
+ | `open` | `boolean` | `false` | Control visibility (v-model) |
26
+ | `position` | `'left' \| 'right' \| 'top' \| 'bottom'` | `'right'` | Drawer position |
27
+ | `title` | `string` | `''` | Drawer title |
28
+
29
+ ## Events
30
+
31
+ | Event | Payload | Description |
32
+ |-------|---------|-------------|
33
+ | `update:open` | `boolean` | Emitted when visibility changes |
34
+ | `close` | - | Emitted when drawer is closed |
35
+
36
+ ## Slots
37
+
38
+ | Slot | Description |
39
+ |------|-------------|
40
+ | `default` | Drawer content |
41
+
42
+ ## Exposed Methods
43
+
44
+ | Method | Description |
45
+ |--------|-------------|
46
+ | `open()` | Open the drawer |
47
+ | `close()` | Close the drawer |
48
+ | `toggle()` | Toggle drawer visibility |
49
+
50
+ ## Usage Examples
51
+
52
+ ### Basic Drawer
53
+
54
+ ```vue
55
+ <script setup>
56
+ import { ref } from 'vue';
57
+ import { Drawer } from '@htlkg/components';
58
+
59
+ const isOpen = ref(false);
60
+ </script>
61
+
62
+ <template>
63
+ <button @click="isOpen = true">Open Drawer</button>
64
+
65
+ <Drawer
66
+ v-model:open="isOpen"
67
+ title="Settings"
68
+ >
69
+ <div class="space-y-4">
70
+ <h3>User Settings</h3>
71
+ <p>Configure your preferences here.</p>
72
+ </div>
73
+ </Drawer>
74
+ </template>
75
+ ```
76
+
77
+ ### Left Drawer
78
+
79
+ ```vue
80
+ <template>
81
+ <Drawer
82
+ v-model:open="isOpen"
83
+ position="left"
84
+ title="Navigation"
85
+ >
86
+ <nav>
87
+ <ul>
88
+ <li><a href="/dashboard">Dashboard</a></li>
89
+ <li><a href="/users">Users</a></li>
90
+ <li><a href="/settings">Settings</a></li>
91
+ </ul>
92
+ </nav>
93
+ </Drawer>
94
+ </template>
95
+ ```
96
+
97
+ ### Filter Drawer
98
+
99
+ ```vue
100
+ <script setup>
101
+ import { ref } from 'vue';
102
+ import { Drawer } from '@htlkg/components';
103
+
104
+ const showFilters = ref(false);
105
+ const filters = ref({
106
+ status: '',
107
+ dateRange: ''
108
+ });
109
+ </script>
110
+
111
+ <template>
112
+ <button @click="showFilters = true">Filters</button>
113
+
114
+ <Drawer
115
+ v-model:open="showFilters"
116
+ title="Filter Options"
117
+ position="right"
118
+ >
119
+ <form class="space-y-4">
120
+ <div>
121
+ <label>Status</label>
122
+ <select v-model="filters.status">
123
+ <option value="">All</option>
124
+ <option value="active">Active</option>
125
+ <option value="inactive">Inactive</option>
126
+ </select>
127
+ </div>
128
+ <div>
129
+ <label>Date Range</label>
130
+ <input v-model="filters.dateRange" type="date" />
131
+ </div>
132
+ <button type="submit">Apply Filters</button>
133
+ </form>
134
+ </Drawer>
135
+ </template>
136
+ ```
137
+
138
+ ## Demo
139
+
140
+ See the [Drawer demo page](/components/drawer) for interactive examples.
@@ -0,0 +1,92 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { mount } from '@vue/test-utils';
3
+ import Drawer from './Drawer.vue';
4
+
5
+ describe('Drawer Component', () => {
6
+ it('renders with basic props', () => {
7
+ const wrapper = mount(Drawer, {
8
+ props: {
9
+ open: true,
10
+ title: 'Test Drawer'
11
+ }
12
+ });
13
+
14
+ expect(wrapper.exists()).toBe(true);
15
+ });
16
+
17
+ it('supports v-model for open state', async () => {
18
+ const wrapper = mount(Drawer, {
19
+ props: {
20
+ open: false,
21
+ 'onUpdate:open': (value: boolean) => wrapper.setProps({ open: value })
22
+ }
23
+ });
24
+
25
+ expect(wrapper.props('open')).toBe(false);
26
+
27
+ const component = wrapper.vm as any;
28
+ if (component.open) {
29
+ component.open();
30
+ await wrapper.vm.$nextTick();
31
+ expect(wrapper.emitted('update:open')).toBeTruthy();
32
+ }
33
+ });
34
+
35
+ it('emits close event when drawer is closed', async () => {
36
+ const wrapper = mount(Drawer, {
37
+ props: {
38
+ open: true
39
+ }
40
+ });
41
+
42
+ const component = wrapper.vm as any;
43
+ if (component.close) {
44
+ component.close();
45
+ await wrapper.vm.$nextTick();
46
+ expect(wrapper.emitted('update:open')).toBeTruthy();
47
+ expect(wrapper.emitted('close')).toBeTruthy();
48
+ }
49
+ });
50
+
51
+ it('exposes open and close methods', () => {
52
+ const wrapper = mount(Drawer, {
53
+ props: {
54
+ open: false
55
+ }
56
+ });
57
+
58
+ const component = wrapper.vm as any;
59
+ if (component.open && component.close) {
60
+ expect(typeof component.open).toBe('function');
61
+ expect(typeof component.close).toBe('function');
62
+ }
63
+ });
64
+
65
+ it('renders with different positions', () => {
66
+ const positions = ['left', 'right', 'top', 'bottom'];
67
+
68
+ positions.forEach(position => {
69
+ const wrapper = mount(Drawer, {
70
+ props: {
71
+ open: true,
72
+ position: position as any
73
+ }
74
+ });
75
+
76
+ expect(wrapper.exists()).toBe(true);
77
+ });
78
+ });
79
+
80
+ it('renders slot content', () => {
81
+ const wrapper = mount(Drawer, {
82
+ props: {
83
+ open: true
84
+ },
85
+ slots: {
86
+ default: '<div class="drawer-content">Custom Content</div>'
87
+ }
88
+ });
89
+
90
+ expect(wrapper.html()).toContain('drawer-content');
91
+ });
92
+ });