@pequity/squirrel 5.4.8 → 5.4.9

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.
@@ -1,17 +1,12 @@
1
1
  import PInlineDatePicker from '@squirrel/components/p-inline-date-picker/p-inline-date-picker.vue';
2
2
  import { createWrapperFor } from '@tests/vitest.helpers';
3
- import { DatePicker } from 'v-calendar';
4
3
 
5
4
  const createWrapper = (props) => {
6
5
  return createWrapperFor(PInlineDatePicker, {
7
6
  props,
8
7
  global: {
9
8
  stubs: {
10
- DatePicker: {
11
- name: 'DatePicker',
12
- template: '<div class="date-picker-stub"></div>',
13
- props: ['modelValue', 'selectAttribute', 'minDate', 'maxDate', 'masks', 'timezone', 'style'],
14
- },
9
+ VueDatePicker: true,
15
10
  },
16
11
  },
17
12
  });
@@ -21,15 +16,13 @@ describe('PInlineDatePicker.vue', () => {
21
16
  it('renders a datepicker', () => {
22
17
  const wrapper = createWrapper();
23
18
 
24
- const datePicker = wrapper.findComponent(DatePicker);
19
+ const datePicker = wrapper.findComponent({ name: 'VueDatePicker' });
25
20
 
26
21
  expect(datePicker.props()).toMatchObject({
27
- modelValue: null,
28
- selectAttribute: { highlight: { class: 'bg-primary', contentClass: 'text-white' } },
22
+ modelValue: '',
29
23
  minDate: null,
30
24
  maxDate: null,
31
- masks: { input: 'DD-MMM-YYYY', data: 'YYYY-MM-DD' },
32
- timezone: '',
25
+ timezone: null,
33
26
  });
34
27
 
35
28
  expect(wrapper.find('label').exists()).toBe(false);
@@ -44,9 +37,9 @@ describe('PInlineDatePicker.vue', () => {
44
37
  timezone: 'UTC',
45
38
  });
46
39
 
47
- const datePicker = wrapper.findComponent(DatePicker);
40
+ const datePicker = wrapper.findComponent({ name: 'VueDatePicker' });
48
41
 
49
- expect(datePicker.props().modelValue).toEqual(new Date('2024-05-19'));
42
+ expect(datePicker.props().modelValue).toBe('2024-05-19');
50
43
  expect(datePicker.props().minDate).toEqual(new Date('2024-05-01'));
51
44
  expect(datePicker.props().maxDate).toEqual(new Date('2024-05-31'));
52
45
  expect(datePicker.props().timezone).toBe('UTC');
@@ -78,8 +71,9 @@ describe('PInlineDatePicker.vue', () => {
78
71
 
79
72
  expect(wrapper.props().required).toBe(true);
80
73
  });
74
+
81
75
  it('passes listeners to the datepicker', () => {
82
- // Since DatePicker emits are not defined on PInlineDatePicker we need to Spy on console.warn and mock the implementation to suppress the warning:
76
+ // Since VueDatePicker emits are not defined on PInlineDatePicker we need to Spy on console.warn and mock the implementation to suppress the warning:
83
77
  // [Vue warn]: Component emitted event "update:view" but it is neither declared in the emits option nor as an "onUpdate:view" prop.
84
78
  const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
85
79
 
@@ -100,7 +94,7 @@ describe('PInlineDatePicker.vue', () => {
100
94
  },
101
95
  });
102
96
 
103
- const datePicker = wrapper.findComponent(DatePicker);
97
+ const datePicker = wrapper.findComponent({ name: 'VueDatePicker' });
104
98
 
105
99
  datePicker.vm.$emit('update:view');
106
100
 
@@ -112,12 +106,16 @@ describe('PInlineDatePicker.vue', () => {
112
106
  it('sets the disabled state correctly', () => {
113
107
  const wrapper = createWrapper({ label: 'test datepicker', disabled: true });
114
108
 
115
- const datePickerDiv = wrapper.find('.vc-container');
109
+ const datePicker = wrapper.findComponent({ name: 'VueDatePicker' });
116
110
 
117
- expect(['pointer-events-none', 'opacity-70'].every((c) => datePickerDiv.classes().includes(c))).toBe(true);
111
+ expect(datePicker.props().disabled).toBe(true);
118
112
  });
119
113
 
120
- it(`updates the value when a day is clicked`, async () => {
114
+ it('updates the value bound with v-model', async () => {
115
+ // Since VueDatePicker emits are not defined on PInlineDatePicker we need to Spy on console.warn and mock the implementation to suppress the warning:
116
+ // [Vue warn]: Component emitted event "update:modelValue" but it is neither declared in the emits option nor as an "onUpdate:view" prop.
117
+ const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
118
+
121
119
  const wrapper = createWrapperFor({
122
120
  template: `<PInlineDatePicker v-model="dateStrVal" />`,
123
121
  components: { PInlineDatePicker },
@@ -128,25 +126,13 @@ describe('PInlineDatePicker.vue', () => {
128
126
  },
129
127
  });
130
128
 
131
- const datePicker = wrapper.findComponent(DatePicker);
129
+ const datePicker = wrapper.findComponent({ name: 'VueDatePicker' });
132
130
 
133
- datePicker.vm.$emit('dayclick', { id: '2024-09-02' });
131
+ datePicker.vm.$emit('update:modelValue', '2024-09-02');
134
132
 
135
133
  expect(wrapper.vm.dateStrVal).toBe('2024-09-02');
136
- });
137
-
138
- it(`updates the datepicker value when modelValue is changed`, async () => {
139
- const wrapper = createWrapper({ modelValue: '2024-09-01' });
140
-
141
- const datePicker = wrapper.findComponent(DatePicker);
142
- datePicker.vm.move = vi.fn();
143
-
144
- expect(datePicker.props().modelValue).toEqual(new Date('2024-09-01'));
145
-
146
- await wrapper.setProps({ modelValue: '2024-09-02' });
147
134
 
148
- expect(datePicker.vm.move).toHaveBeenCalled();
149
- expect(datePicker.props().modelValue).toEqual(new Date('2024-09-02'));
135
+ warnSpy.mockRestore();
150
136
  });
151
137
 
152
138
  it('sets the error state correctly', () => {
@@ -155,20 +141,10 @@ describe('PInlineDatePicker.vue', () => {
155
141
  errorMsg: 'datepicker has error',
156
142
  });
157
143
 
158
- expect(wrapper.attributes()['data-has-error']).toBeDefined();
159
- const datePickerDiv = wrapper.find('.vc-container');
160
-
161
- expect(datePickerDiv.element.style.border).toBe('1px solid #f0453c');
162
144
  const errorDiv = wrapper.find('div.text-xs.text-on-error.mt-1');
145
+
146
+ expect(wrapper.attributes()['data-has-error']).toBeDefined();
163
147
  expect(errorDiv.isVisible()).toBe(true);
164
148
  expect(errorDiv.text()).toBe('datepicker has error');
165
149
  });
166
-
167
- it('emits null when date is invalid', async () => {
168
- const wrapper = createWrapper({ label: 'test datepicker', modelValue: 'not a date' });
169
-
170
- await wrapper.vm.$nextTick();
171
-
172
- expect(wrapper.emitted()['update:modelValue'][0][0]).toBe(null);
173
- });
174
150
  });
@@ -22,8 +22,8 @@ export default {
22
22
  docs: {
23
23
  description: {
24
24
  component: `Allows users to select a date by choosing a date from the calendar.
25
- This component uses \`<v-date-picker>\` from [V-Calendar](https://vcalendar.io/) internally.
26
- So please take a look also there at their extensive [documentation](https://vcalendar.io/api/v2.0/).`,
25
+ This component uses \`VueDatePicker\` from [@vuepic/vue-datepicker](https://vue3datepicker.com) internally.
26
+ So please take a look also there at their extensive [documentation](https://vue3datepicker.com/).`,
27
27
  },
28
28
  },
29
29
  },
@@ -5,123 +5,54 @@
5
5
  {{ label }}
6
6
  </label>
7
7
  </slot>
8
- <DatePicker
9
- ref="datepickerRef"
10
- :class="{ 'pointer-events-none opacity-70': $attrs.disabled }"
11
- :model-value="innerValue"
12
- :select-attribute="selectAttribute"
13
- :min-date="minDate || null"
14
- :max-date="maxDate || null"
15
- :masks="masks"
16
- :style="styleDatepicker"
17
- :timezone="timezone"
18
- v-bind="listeners"
19
- @dayclick="dayclick"
20
- />
8
+ <VueDatePicker v-model="model" v-bind="datePickerProps" />
21
9
  <div v-show="errorMsg" :class="errorMsgClasses">{{ errorMsg }}</div>
22
10
  </div>
23
11
  </template>
24
12
 
25
13
  <script setup lang="ts">
26
14
  import { useInputClasses } from '@squirrel/composables/useInputClasses';
27
- import dayjs from 'dayjs';
28
- import { DatePicker } from 'v-calendar';
29
- import { type AttributeConfig } from 'v-calendar/dist/types/src/utils/attribute.d';
30
- import { computed, ref, type StyleValue, useAttrs, watch } from 'vue';
15
+ import VueDatePicker, { type VueDatePickerProps } from '@vuepic/vue-datepicker';
16
+ import { computed, type StyleValue, useAttrs } from 'vue';
31
17
 
32
18
  defineOptions({
33
19
  name: 'PInlineDatePicker',
34
20
  inheritAttrs: false,
35
21
  });
36
22
 
37
- const selectAttribute: AttributeConfig = {
38
- highlight: { class: 'bg-primary', contentClass: 'text-white' },
39
- };
40
-
41
- const DEFAULT_MASKS = {
42
- // The mask for the input
43
- input: 'DD-MMM-YYYY',
44
- // The mask for the model value
45
- data: 'YYYY-MM-DD',
46
- };
47
-
48
23
  type Props = {
49
- modelValue?: string | null;
50
24
  label?: string;
51
25
  errorMsg?: string;
52
26
  required?: boolean;
53
- minDate?: Date | null;
54
- maxDate?: Date | null;
55
- timezone?: string;
56
- };
27
+ } & VueDatePickerProps;
57
28
 
58
29
  const props = withDefaults(defineProps<Props>(), {
59
- modelValue: '',
60
30
  label: '',
61
31
  errorMsg: '',
62
32
  required: false,
63
- minDate: null,
64
- maxDate: null,
65
- timezone: '',
33
+ inline: true,
34
+ autoApply: true,
35
+ enableTimePicker: false,
36
+ modelType: 'yyyy-MM-dd',
37
+ hideOffsetDates: true,
38
+ weekStart: 0,
66
39
  });
67
40
 
68
- const emit = defineEmits<{
69
- 'update:modelValue': [value: string | null];
70
- }>();
41
+ const model = defineModel<Date | string | null>({ default: '' });
71
42
 
72
43
  // Data
73
- const { labelClasses, errorMsgClasses } = useInputClasses(props);
74
44
  const attrs = useAttrs();
75
- // innerValue is a Date object
76
- const innerValue = ref<Date | null>(null);
77
- const datepickerRef = ref<InstanceType<typeof DatePicker> | null>(null);
45
+ const { labelClasses, errorMsgClasses } = useInputClasses(props);
78
46
 
79
47
  // Computed
80
- const masks = computed(() => {
81
- return Object.assign(DEFAULT_MASKS, attrs.masks);
82
- });
48
+ const datePickerProps = computed(() => {
49
+ const { modelValue: _, ...propsWithoutModelValue } = props;
50
+ const { class: classes, style, ...attrsWithoutClassAndStyle } = attrs;
83
51
 
84
- const listeners = computed(() => {
85
- return Object.keys(attrs).reduce((acc, curr) => {
86
- if (curr.startsWith('on')) {
87
- return { ...acc, [curr]: attrs[curr] };
88
- } else {
89
- return acc;
90
- }
91
- }, {});
52
+ return { ...propsWithoutModelValue, ...attrsWithoutClassAndStyle };
92
53
  });
93
54
 
94
55
  const style = computed(() => {
95
56
  return attrs.style as StyleValue;
96
57
  });
97
-
98
- const styleDatepicker = computed(() => {
99
- return props.errorMsg ? { border: '1px solid #f0453c' } : {};
100
- });
101
-
102
- // Methods
103
- const dayclick = (e: { id: string }) => {
104
- emit('update:modelValue', e.id);
105
- };
106
-
107
- // Watch
108
- watch(
109
- () => props.modelValue,
110
- async (nV) => {
111
- const date = dayjs(nV, masks.value.data).toDate();
112
-
113
- if (nV && date.toString() === 'Invalid Date') {
114
- emit('update:modelValue', null);
115
- return;
116
- }
117
-
118
- innerValue.value = nV ? dayjs(nV, masks.value.data).toDate() : null;
119
- const datepicker = datepickerRef.value as { move: (val: unknown) => void };
120
-
121
- if (datepicker && typeof datepicker.move === 'function' && innerValue.value) {
122
- await datepicker.move(innerValue.value);
123
- }
124
- },
125
- { immediate: true }
126
- );
127
58
  </script>