@pequity/squirrel 4.1.2 → 5.0.0

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 (29) hide show
  1. package/dist/cjs/p-drawer.js +2 -6
  2. package/dist/cjs/p-inline-date-picker.js +6 -4
  3. package/dist/cjs/p-loading.js +3 -3
  4. package/dist/es/p-drawer.js +2 -6
  5. package/dist/es/p-inline-date-picker.js +6 -4
  6. package/dist/es/p-loading.js +4 -4
  7. package/dist/squirrel/components/p-action-bar/p-action-bar.vue.d.ts +0 -1
  8. package/dist/squirrel/components/p-card/p-card.vue.d.ts +14 -6
  9. package/dist/squirrel/components/p-checkbox/p-checkbox.vue.d.ts +12 -4
  10. package/dist/squirrel/components/p-drawer/p-drawer.vue.d.ts +1 -2
  11. package/dist/squirrel/components/p-dropdown-select/p-dropdown-select.vue.d.ts +119 -21
  12. package/dist/squirrel/components/p-info-icon/p-info-icon.vue.d.ts +9 -3
  13. package/dist/squirrel/components/p-inline-date-picker/p-inline-date-picker.vue.d.ts +14 -1
  14. package/dist/squirrel/components/p-link/p-link.vue.d.ts +15 -9
  15. package/dist/squirrel/components/p-modal/p-modal.vue.d.ts +17 -8
  16. package/dist/squirrel/components/p-pagination-info/p-pagination-info.vue.d.ts +9 -3
  17. package/dist/squirrel/components/p-select/p-select.vue.d.ts +13 -6
  18. package/dist/squirrel/components/p-select-btn/p-select-btn.vue.d.ts +21 -15
  19. package/dist/squirrel/components/p-select-list/p-select-list.vue.d.ts +118 -21
  20. package/dist/squirrel/components/p-table/p-table.vue.d.ts +23 -14
  21. package/dist/squirrel/components/p-table-td/p-table-td.vue.d.ts +19 -12
  22. package/dist/style.css +34 -34
  23. package/package.json +8 -8
  24. package/squirrel/components/p-drawer/p-drawer.vue +4 -7
  25. package/squirrel/components/p-inline-date-picker/p-inline-date-picker.spec.js +176 -0
  26. package/squirrel/components/p-inline-date-picker/p-inline-date-picker.vue +7 -7
  27. package/squirrel/components/p-input-number/p-input-number.spec.js +0 -1
  28. package/squirrel/components/p-loading/p-loading.spec.js +36 -25
  29. package/squirrel/components/p-loading/p-loading.vue +22 -15
@@ -0,0 +1,176 @@
1
+ import PInlineDatePicker from '@squirrel/components/p-inline-date-picker/p-inline-date-picker.vue';
2
+ import { createWrapperFor } from '@tests/jest.helpers';
3
+
4
+ const createWrapper = (props) => {
5
+ return createWrapperFor(PInlineDatePicker, {
6
+ props,
7
+ global: {
8
+ stubs: {
9
+ DatePicker: {
10
+ name: 'DatePicker',
11
+ template: '<div class="date-picker-stub"></div>',
12
+ props: ['modelValue', 'selectAttribute', 'minDate', 'maxDate', 'masks', 'timezone', 'style'],
13
+ },
14
+ },
15
+ },
16
+ });
17
+ };
18
+
19
+ describe('PInlineDatePicker.vue', () => {
20
+ it('renders a datepicker', () => {
21
+ const wrapper = createWrapper();
22
+
23
+ const datePicker = wrapper.findComponent({ name: 'DatePicker' });
24
+
25
+ expect(datePicker.props()).toEqual({
26
+ modelValue: null,
27
+ selectAttribute: { highlight: { class: 'bg-primary', contentClass: 'text-white' } },
28
+ minDate: null,
29
+ maxDate: null,
30
+ masks: { input: 'DD-MMM-YYYY', data: 'YYYY-MM-DD' },
31
+ style: {},
32
+ timezone: '',
33
+ });
34
+ expect(wrapper.find('label').exists()).toBe(false);
35
+ expect(wrapper.find('div.text-xs.text-on-error.mt-1').isVisible()).toBe(false);
36
+ });
37
+
38
+ it('passes all props to the datepicker', () => {
39
+ const wrapper = createWrapper({
40
+ modelValue: '2024-05-19',
41
+ minDate: new Date('2024-05-01'),
42
+ maxDate: new Date('2024-05-31'),
43
+ timezone: 'UTC',
44
+ });
45
+
46
+ const datePicker = wrapper.findComponent({ name: 'DatePicker' });
47
+
48
+ expect(datePicker.props().modelValue).toEqual(new Date('2024-05-19'));
49
+ expect(datePicker.props().minDate).toEqual(new Date('2024-05-01'));
50
+ expect(datePicker.props().maxDate).toEqual(new Date('2024-05-31'));
51
+ expect(datePicker.props().timezone).toBe('UTC');
52
+ });
53
+
54
+ it('renders a label when the label prop is set', () => {
55
+ const wrapper = createWrapper({ label: 'test datepicker' });
56
+
57
+ expect(wrapper.find('label').text()).toBe('test datepicker');
58
+ });
59
+
60
+ it('hides the component when the hidden prop is set', () => {
61
+ const wrapper = createWrapper({ hidden: true });
62
+
63
+ expect(wrapper.classes()).toContain('hidden');
64
+ });
65
+
66
+ it('adds the required class when the required prop is set', () => {
67
+ const wrapper = createWrapper({ label: 'test datepicker', required: true });
68
+
69
+ expect(wrapper.find('label').classes()).toEqual([
70
+ 'block',
71
+ 'mb-1',
72
+ 'font-bold',
73
+ 'text-sm',
74
+ "after:content-['_*']",
75
+ 'after:text-on-error',
76
+ ]);
77
+
78
+ expect(wrapper.props().required).toBe(true);
79
+ });
80
+
81
+ 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:
83
+ // [Vue warn]: Component emitted event "update:view" but it is neither declared in the emits option nor as an "onUpdate:view" prop.
84
+ const warnSpy = jest.spyOn(console, 'warn').mockImplementation(() => {});
85
+
86
+ const testFn = jest.fn();
87
+
88
+ const wrapper = createWrapperFor({
89
+ template: `<PInlineDatePicker v-model="dateStrVal" @update:view="testEvent" />`,
90
+ components: { PInlineDatePicker },
91
+ data() {
92
+ return {
93
+ dateStrVal: '2024-09-01',
94
+ };
95
+ },
96
+ methods: {
97
+ testEvent() {
98
+ testFn();
99
+ },
100
+ },
101
+ });
102
+
103
+ const datePicker = wrapper.findComponent({ name: 'DatePicker' });
104
+
105
+ datePicker.vm.$emit('update:view');
106
+
107
+ expect(testFn).toHaveBeenCalled();
108
+
109
+ warnSpy.mockRestore();
110
+ });
111
+
112
+ it('sets the disabled state correctly', () => {
113
+ const wrapper = createWrapper({ label: 'test datepicker', disabled: true });
114
+
115
+ const datePicker = wrapper.findComponent({ name: 'DatePicker' });
116
+
117
+ expect(['pointer-events-none', 'opacity-70'].every((c) => datePicker.classes().includes(c))).toBe(true);
118
+ });
119
+
120
+ it(`updates the value when a day is clicked`, async () => {
121
+ const wrapper = createWrapperFor({
122
+ template: `<PInlineDatePicker v-model="dateStrVal" />`,
123
+ components: { PInlineDatePicker },
124
+ data() {
125
+ return {
126
+ dateStrVal: '2024-09-01',
127
+ };
128
+ },
129
+ });
130
+
131
+ const datePicker = wrapper.findComponent({ name: 'DatePicker' });
132
+
133
+ datePicker.vm.$emit('dayclick', { id: '2024-09-02' });
134
+
135
+ 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({ name: 'DatePicker' });
142
+ datePicker.vm.move = jest.fn();
143
+
144
+ expect(datePicker.props().modelValue).toEqual(new Date('2024-09-01'));
145
+
146
+ await wrapper.setProps({ modelValue: '2024-09-02' });
147
+
148
+ expect(datePicker.vm.move).toHaveBeenCalled();
149
+ expect(datePicker.props().modelValue).toEqual(new Date('2024-09-02'));
150
+ });
151
+
152
+ it('sets the error state correctly', () => {
153
+ const wrapper = createWrapper({
154
+ label: 'test datepicker',
155
+ errorMsg: 'datepicker has error',
156
+ });
157
+
158
+ expect(wrapper.attributes()['data-has-error']).toBeDefined();
159
+ const datePicker = wrapper.findComponent({ name: 'DatePicker' });
160
+
161
+ expect(datePicker.props().style).toEqual({
162
+ border: '1px solid var(--color-on-error)',
163
+ });
164
+ const errorDiv = wrapper.find('div.text-xs.text-on-error.mt-1');
165
+ expect(errorDiv.isVisible()).toBe(true);
166
+ expect(errorDiv.text()).toBe('datepicker has error');
167
+ });
168
+
169
+ it('emits null when date is invalid', async () => {
170
+ const wrapper = createWrapper({ label: 'test datepicker', modelValue: 'not a date' });
171
+
172
+ await wrapper.vm.$nextTick();
173
+
174
+ expect(wrapper.emitted()['update:modelValue'][0][0]).toBe(null);
175
+ });
176
+ });
@@ -25,13 +25,13 @@
25
25
  <script lang="ts">
26
26
  import dayjs from 'dayjs';
27
27
  import inputClassesMixin from '@squirrel/utils/inputClassesMixin';
28
+ import { type AttributeConfig } from 'v-calendar/dist/types/src/utils/attribute';
28
29
  import { DatePicker } from 'v-calendar';
29
30
  import { type PropType, type StyleValue, defineComponent } from 'vue';
30
31
 
31
- // The type of the select attribute is defined in node_modules/v-calendar/dist/types/src/utils/attribute.d.ts
32
- // but there was no way to import it, so as a workaround we cast the selectAttribute as `any`.
33
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
34
- const selectAttribute = { highlight: { class: 'bg-primary', contentClass: 'text-white' } } as any;
32
+ const selectAttribute: AttributeConfig = {
33
+ highlight: { class: 'bg-primary', contentClass: 'text-white' },
34
+ };
35
35
 
36
36
  const DEFAULT_MASKS = {
37
37
  // The mask for the input
@@ -112,7 +112,7 @@ export default defineComponent({
112
112
  },
113
113
  watch: {
114
114
  modelValue: {
115
- handler(nV) {
115
+ async handler(nV) {
116
116
  const date = dayjs(nV, this.masks.data).toDate();
117
117
 
118
118
  if (nV && date.toString() === 'Invalid Date') {
@@ -123,8 +123,8 @@ export default defineComponent({
123
123
  this.innerValue = nV ? dayjs(nV, this.masks.data).toDate() : null;
124
124
  const datepicker = this.$refs.datepicker as { move: (val: unknown) => void };
125
125
 
126
- if (datepicker && this.innerValue) {
127
- datepicker.move(this.innerValue);
126
+ if (datepicker && typeof datepicker.move === 'function' && this.innerValue) {
127
+ await datepicker.move(this.innerValue);
128
128
  }
129
129
  },
130
130
  immediate: true,
@@ -5,7 +5,6 @@ const baseClasses = () => [
5
5
  'text-night',
6
6
  'w-full',
7
7
  'bg-surface',
8
-
9
8
  'focus:outline-none',
10
9
  'border-0',
11
10
  'ring-1',
@@ -53,6 +53,7 @@ const createWrapper = (options) => {
53
53
  {
54
54
  global: {
55
55
  stubs: {
56
+ Teleport: true,
56
57
  PSkeletonLoader: true,
57
58
  },
58
59
  },
@@ -232,45 +233,55 @@ describe('PLoading.vue', () => {
232
233
  template: `<div>{{ modelValue }}</div>`,
233
234
  });
234
235
 
235
- const wrapper = createWrapperFor({
236
- template: `
236
+ const wrapper = createWrapperFor(
237
+ {
238
+ template: `
237
239
  <button class="request-1-sec-component-props" @click="fireRequestComponentProps"></button>
238
240
  <button class="update-props" @click="updateProps"></button>
239
241
  `,
240
- components: { TestComponent },
241
- setup() {
242
- const componentProps = ref({ modelValue: 0 });
243
- const { loadingShow, loadingHide } = usePLoading();
244
-
245
- const fireRequestComponentProps = async () => {
246
- const id = `component-props`;
247
- componentProps.value.modelValue = 10;
248
- loadingShow({ id, content: TestComponent, props: componentProps });
249
- await new Promise((resolve) => setTimeout(resolve, 1000));
250
- loadingHide(id);
251
- };
252
-
253
- const updateProps = () => {
254
- componentProps.value.modelValue = 20;
255
- };
256
-
257
- return { loadingShow, loadingHide, fireRequestComponentProps, updateProps };
242
+ components: { TestComponent },
243
+ setup() {
244
+ const componentProps = ref({ modelValue: 0 });
245
+ const { loadingShow, loadingHide } = usePLoading();
246
+
247
+ const fireRequestComponentProps = async () => {
248
+ const id = `component-props`;
249
+ componentProps.value.modelValue = 10;
250
+ loadingShow({ id, content: TestComponent, props: componentProps });
251
+ await new Promise((resolve) => setTimeout(resolve, 1000));
252
+ loadingHide(id);
253
+ };
254
+
255
+ const updateProps = () => {
256
+ componentProps.value.modelValue = 20;
257
+ };
258
+
259
+ return { loadingShow, loadingHide, fireRequestComponentProps, updateProps, componentProps };
260
+ },
258
261
  },
259
- });
262
+ {
263
+ global: {
264
+ stubs: {
265
+ Teleport: true,
266
+ },
267
+ },
268
+ }
269
+ );
260
270
 
261
271
  await wrapper.find('.request-1-sec-component-props').trigger('click');
262
272
 
263
273
  jest.advanceTimersByTime(300);
264
274
  await waitNT(appWrapper.vm);
265
- const testComponent = appWrapper.findComponent(TestComponent);
275
+ const getTestComponent = () => appWrapper.findComponent(TestComponent);
276
+
266
277
  expect(appWrapper.find('[aria-busy]').exists()).toBe(true);
267
- expect(testComponent.exists()).toBe(true);
268
- expect(testComponent.props().modelValue).toBe(10);
278
+ expect(getTestComponent().exists()).toBe(true);
279
+ expect(getTestComponent().props().modelValue).toBe(10);
269
280
 
270
281
  jest.advanceTimersByTime(300);
271
282
  await waitNT(appWrapper.vm);
272
283
  await wrapper.find('.update-props').trigger('click');
273
- expect(testComponent.props().modelValue).toBe(20);
284
+ expect(getTestComponent().props().modelValue).toBe(20);
274
285
 
275
286
  jest.advanceTimersByTime(500);
276
287
  await waitNT(appWrapper.vm);
@@ -1,23 +1,30 @@
1
1
  <template>
2
- <Transition name="pm-backdrop-transition" enter-active-class="fadeInDown" leave-active-class="fadeOutUp">
3
- <div v-if="show" class="fixed left-0 top-0 z-[120] flex w-full justify-center" aria-live="polite" aria-busy="true">
2
+ <Teleport to="body">
3
+ <Transition name="pm-backdrop-transition" enter-active-class="fadeInDown" leave-active-class="fadeOutUp">
4
4
  <div
5
- :style="{ width: `${width}px`, height: `${height}px` }"
6
- class="overflow-hidden rounded-b border-x border-b border-p-gray-30 bg-p-blue-10 shadow-sm transition-all duration-300"
5
+ v-if="show"
6
+ class="fixed left-0 top-0 z-[120] flex w-full justify-center"
7
+ aria-live="polite"
8
+ aria-busy="true"
7
9
  >
8
- <Transition enter-from-class="opacity-0" enter-active-class="transition duration-500">
9
- <Component :is="content" v-if="isComponent(content)" v-bind="componentProps" />
10
- <div v-else :class="textDivClass">{{ content }}</div>
11
- </Transition>
10
+ <div
11
+ :style="{ width: `${width}px`, height: `${height}px` }"
12
+ class="overflow-hidden rounded-b border-x border-b border-p-gray-30 bg-p-blue-10 shadow-sm transition-all duration-300"
13
+ >
14
+ <Transition enter-from-class="opacity-0" enter-active-class="transition duration-500">
15
+ <Component :is="content" v-if="isComponent(content)" v-bind="componentProps" />
16
+ <div v-else :class="textDivClass">{{ content }}</div>
17
+ </Transition>
18
+ </div>
19
+ </div>
20
+ </Transition>
21
+ <div v-if="content" class="invisible fixed">
22
+ <div ref="dimsReference">
23
+ <Component :is="content" v-if="isComponent(content)" v-bind="componentProps" />
24
+ <div v-else :class="textDivClass">{{ content }}</div>
12
25
  </div>
13
26
  </div>
14
- </Transition>
15
- <div v-if="content" class="invisible fixed">
16
- <div ref="dimsReference">
17
- <Component :is="content" v-if="isComponent(content)" v-bind="componentProps" />
18
- <div v-else :class="textDivClass">{{ content }}</div>
19
- </div>
20
- </div>
27
+ </Teleport>
21
28
  </template>
22
29
 
23
30
  <script setup lang="ts">