@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.
- package/README.md +1 -1
- package/dist/cjs/chunks/p-date-picker.js +143 -85
- package/dist/cjs/chunks/p-inline-date-picker.js +117 -67
- package/dist/es/chunks/p-date-picker.js +144 -86
- package/dist/es/chunks/p-inline-date-picker.js +118 -68
- package/dist/squirrel/components/p-date-picker/p-date-picker.vue.d.ts +27 -31
- package/dist/squirrel/components/p-drawer/p-drawer.vue.d.ts +2 -2
- package/dist/squirrel/components/p-dropdown-select/p-dropdown-select.vue.d.ts +1 -1
- package/dist/squirrel/components/p-inline-date-picker/p-inline-date-picker.vue.d.ts +46 -0
- package/dist/squirrel/components/p-modal/p-modal.vue.d.ts +3 -3
- package/dist/style.css +37 -928
- package/package.json +7 -9
- package/squirrel/assets/squirrel.css +45 -6
- package/squirrel/components/p-date-picker/p-date-picker.spec.js +292 -0
- package/squirrel/components/p-date-picker/p-date-picker.stories.js +2 -2
- package/squirrel/components/p-date-picker/p-date-picker.vue +45 -90
- package/squirrel/components/p-inline-date-picker/p-inline-date-picker.spec.js +21 -45
- package/squirrel/components/p-inline-date-picker/p-inline-date-picker.stories.js +2 -2
- package/squirrel/components/p-inline-date-picker/p-inline-date-picker.vue +16 -85
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pequity/squirrel",
|
|
3
3
|
"description": "Squirrel component library",
|
|
4
|
-
"version": "5.4.
|
|
4
|
+
"version": "5.4.9",
|
|
5
5
|
"packageManager": "pnpm@9.12.3",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"scripts": {
|
|
@@ -38,12 +38,11 @@
|
|
|
38
38
|
"main": "./dist/cjs/index.js",
|
|
39
39
|
"module": "./dist/es/index.js",
|
|
40
40
|
"peerDependencies": {
|
|
41
|
-
"@popperjs/core": "^2.11.8",
|
|
42
41
|
"@tanstack/vue-virtual": "^3.8.3",
|
|
42
|
+
"@vuepic/vue-datepicker": "^9.0.3",
|
|
43
43
|
"dayjs": "^1.11.12",
|
|
44
44
|
"floating-vue": "^5.2.2",
|
|
45
45
|
"lodash-es": "^4.17.21",
|
|
46
|
-
"v-calendar": "^3.1.2",
|
|
47
46
|
"vue": "^3.4.33",
|
|
48
47
|
"vue-currency-input": "^3.1.0",
|
|
49
48
|
"vue-router": "^4.4.0",
|
|
@@ -54,7 +53,6 @@
|
|
|
54
53
|
"@commitlint/config-conventional": "^19.5.0",
|
|
55
54
|
"@pequity/eslint-config": "^1.0.1",
|
|
56
55
|
"@playwright/test": "^1.48.2",
|
|
57
|
-
"@popperjs/core": "2.11.8",
|
|
58
56
|
"@semantic-release/changelog": "^6.0.3",
|
|
59
57
|
"@semantic-release/git": "^10.0.1",
|
|
60
58
|
"@storybook/addon-a11y": "^8.4.2",
|
|
@@ -69,14 +67,15 @@
|
|
|
69
67
|
"@storybook/theming": "^8.4.2",
|
|
70
68
|
"@storybook/vue3": "^8.4.2",
|
|
71
69
|
"@storybook/vue3-vite": "^8.4.2",
|
|
72
|
-
"@tanstack/vue-virtual": "3.10.
|
|
70
|
+
"@tanstack/vue-virtual": "3.10.9",
|
|
73
71
|
"@types/jsdom": "^21.1.7",
|
|
74
72
|
"@types/lodash-es": "^4.17.12",
|
|
75
73
|
"@types/node": "^22.9.0",
|
|
76
|
-
"@vitejs/plugin-vue": "^5.1.
|
|
74
|
+
"@vitejs/plugin-vue": "^5.1.5",
|
|
77
75
|
"@vitest/coverage-v8": "^2.1.4",
|
|
78
76
|
"@vue/compiler-sfc": "3.5.12",
|
|
79
77
|
"@vue/test-utils": "^2.4.6",
|
|
78
|
+
"@vuepic/vue-datepicker": "^10.0.0",
|
|
80
79
|
"autoprefixer": "^10.4.20",
|
|
81
80
|
"dayjs": "1.11.13",
|
|
82
81
|
"eslint": "^9.14.0",
|
|
@@ -89,7 +88,7 @@
|
|
|
89
88
|
"lint-staged": "^15.2.10",
|
|
90
89
|
"lodash-es": "4.17.21",
|
|
91
90
|
"make-coverage-badge": "^1.2.0",
|
|
92
|
-
"postcss": "^8.4.
|
|
91
|
+
"postcss": "^8.4.49",
|
|
93
92
|
"prettier": "^3.3.3",
|
|
94
93
|
"prettier-plugin-tailwindcss": "^0.6.8",
|
|
95
94
|
"resolve-tspaths": "^0.8.22",
|
|
@@ -100,8 +99,7 @@
|
|
|
100
99
|
"svgo": "^3.3.2",
|
|
101
100
|
"tailwindcss": "^3.4.14",
|
|
102
101
|
"typescript": "5.6.3",
|
|
103
|
-
"
|
|
104
|
-
"vite": "^5.4.10",
|
|
102
|
+
"vite": "^5.4.11",
|
|
105
103
|
"vitest": "^2.1.4",
|
|
106
104
|
"vue": "3.5.12",
|
|
107
105
|
"vue-currency-input": "3.1.0",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
@import 'floating-vue/dist/style.css';
|
|
2
2
|
@import 'vue-toastification/dist/index.css';
|
|
3
|
-
@import '
|
|
3
|
+
@import '@vuepic/vue-datepicker/dist/main.css';
|
|
4
4
|
|
|
5
5
|
:root {
|
|
6
6
|
/* Design system colors */
|
|
@@ -76,16 +76,55 @@
|
|
|
76
76
|
overflow-wrap: break-word;
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
-
/* Datepicker - v-calendar */
|
|
80
|
-
.vc-container.vc-blue {
|
|
81
|
-
font-family: Inter;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
79
|
/* Skeleton fade */
|
|
85
80
|
.skeleton-fade {
|
|
86
81
|
mask-image: linear-gradient(rgba(0, 0, 0, 1), transparent);
|
|
87
82
|
}
|
|
88
83
|
|
|
84
|
+
/* Date picker - Vue datepicker */
|
|
85
|
+
:root {
|
|
86
|
+
--dp-font-family: 'Inter', 'Helvetica', 'Arial', 'sans-serif';
|
|
87
|
+
--dp-cell-border-radius: 50%;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
.dp__theme_light {
|
|
91
|
+
--dp-background-color: rgb(var(--color-surface));
|
|
92
|
+
--dp-text-color: rgb(var(--color-on-surface));
|
|
93
|
+
--dp-hover-color: rgb(var(--color-p-blue-10));
|
|
94
|
+
--dp-hover-text-color: rgb(var(--color-p-gray-100));
|
|
95
|
+
--dp-hover-icon-color: rgb(var(--color-p-gray-40));
|
|
96
|
+
--dp-primary-color: rgb(var(--color-primary));
|
|
97
|
+
--dp-primary-disabled-color: rgb(var(--color-p-blue-40));
|
|
98
|
+
--dp-primary-text-color: rgb(var(--color-surface));
|
|
99
|
+
--dp-secondary-color: rgb(var(--color-p-gray-30));
|
|
100
|
+
--dp-border-color: rgb(var(--color-p-gray-30));
|
|
101
|
+
--dp-menu-border-color: rgb(var(--color-p-gray-30));
|
|
102
|
+
--dp-border-color-hover: rgb(var(--color-p-gray-30));
|
|
103
|
+
--dp-border-color-focus: rgb(var(--color-p-gray-30));
|
|
104
|
+
--dp-disabled-color: rgb(var(--color-p-blue-10));
|
|
105
|
+
--dp-scroll-bar-background: rgb(var(--color-p-blue-10));
|
|
106
|
+
--dp-scroll-bar-color: rgb(var(--color-p-gray-40));
|
|
107
|
+
--dp-success-color: rgb(var(--color-on-success));
|
|
108
|
+
--dp-success-color-disabled: rgb(var(--color-p-green-40));
|
|
109
|
+
--dp-icon-color: rgb(var(--color-p-gray-40));
|
|
110
|
+
--dp-danger-color: rgb(var(--color-on-error));
|
|
111
|
+
--dp-marker-color: rgb(var(--color-on-error));
|
|
112
|
+
--dp-tooltip-color: rgb(var(--color-p-blue-10));
|
|
113
|
+
--dp-disabled-color-text: rgb(var(--color-p-gray-40));
|
|
114
|
+
--dp-highlight-color: rgb(var(--color-p-blue-15));
|
|
115
|
+
--dp-range-between-dates-background-color: var(--dp-hover-color, rgb(var(--color-p-blue-10)));
|
|
116
|
+
--dp-range-between-dates-text-color: var(--dp-hover-text-color, rgb(var(--color-p-gray-100)));
|
|
117
|
+
--dp-range-between-border-color: var(--dp-hover-color, rgb(var(--color-p-blue-10)));
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
.dp__theme_light .dp__today {
|
|
121
|
+
border: 1px solid rgb(var(--color-p-blue-40));
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
[data-has-error='true'] .dp__theme_light {
|
|
125
|
+
border-color: var(--dp-danger-color, rgb(var(--color-on-error)));
|
|
126
|
+
}
|
|
127
|
+
|
|
89
128
|
/* Toasts - vue-toastification */
|
|
90
129
|
.Vue-Toastification__toast {
|
|
91
130
|
font-family: Inter;
|
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
import PDatePicker from '@squirrel/components/p-date-picker/p-date-picker.vue';
|
|
2
|
+
import { createWrapperFor } from '@tests/vitest.helpers';
|
|
3
|
+
|
|
4
|
+
const createWrapper = (props) => {
|
|
5
|
+
return createWrapperFor(PDatePicker, {
|
|
6
|
+
props,
|
|
7
|
+
});
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
describe('PDatePicker.vue', () => {
|
|
11
|
+
it('renders a datepicker', () => {
|
|
12
|
+
const wrapper = createWrapper();
|
|
13
|
+
|
|
14
|
+
const datePicker = wrapper.findComponent({ name: 'VueDatePicker' });
|
|
15
|
+
|
|
16
|
+
expect(datePicker.props()).toMatchObject({
|
|
17
|
+
modelValue: '',
|
|
18
|
+
minDate: null,
|
|
19
|
+
maxDate: null,
|
|
20
|
+
timezone: null,
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
expect(wrapper.find('label').exists()).toBe(false);
|
|
24
|
+
expect(wrapper.find('div.text-xs.text-on-error.mt-1').isVisible()).toBe(false);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('passes all props to the datepicker', () => {
|
|
28
|
+
const wrapper = createWrapper({
|
|
29
|
+
modelValue: '2024-05-19',
|
|
30
|
+
minDate: new Date('2024-05-01'),
|
|
31
|
+
maxDate: new Date('2024-05-31'),
|
|
32
|
+
timezone: 'UTC',
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
const datePicker = wrapper.findComponent({ name: 'VueDatePicker' });
|
|
36
|
+
|
|
37
|
+
expect(datePicker.props().modelValue).toBe('2024-05-19');
|
|
38
|
+
expect(datePicker.props().minDate).toEqual(new Date('2024-05-01'));
|
|
39
|
+
expect(datePicker.props().maxDate).toEqual(new Date('2024-05-31'));
|
|
40
|
+
expect(datePicker.props().timezone).toBe('UTC');
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('renders a label when the label prop is set', () => {
|
|
44
|
+
const wrapper = createWrapper({ label: 'test datepicker' });
|
|
45
|
+
|
|
46
|
+
const pInput = wrapper.findComponent({ name: 'PInput' });
|
|
47
|
+
|
|
48
|
+
expect(pInput.props().label).toBe('test datepicker');
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('hides the component when the hidden prop is set', () => {
|
|
52
|
+
const wrapper = createWrapper({ hidden: true });
|
|
53
|
+
|
|
54
|
+
expect(wrapper.classes()).toContain('hidden');
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('adds the required class when the required prop is set', () => {
|
|
58
|
+
const wrapper = createWrapper({ label: 'test datepicker', required: true });
|
|
59
|
+
|
|
60
|
+
const pInput = wrapper.findComponent({ name: 'PInput' });
|
|
61
|
+
|
|
62
|
+
expect(pInput.props().required).toBe(true);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('passes listeners to the input', async () => {
|
|
66
|
+
const testFn = vi.fn();
|
|
67
|
+
|
|
68
|
+
const wrapper = createWrapperFor({
|
|
69
|
+
template: `<PDatePicker v-model="dateStrVal" @input="testEvent" />`,
|
|
70
|
+
components: { PDatePicker },
|
|
71
|
+
data() {
|
|
72
|
+
return {
|
|
73
|
+
dateStrVal: '2024-09-01',
|
|
74
|
+
};
|
|
75
|
+
},
|
|
76
|
+
methods: {
|
|
77
|
+
testEvent() {
|
|
78
|
+
testFn();
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
const input = wrapper.find('input');
|
|
84
|
+
|
|
85
|
+
await input.trigger('input');
|
|
86
|
+
|
|
87
|
+
expect(testFn).toHaveBeenCalled();
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it('sets the disabled state correctly', () => {
|
|
91
|
+
const wrapper = createWrapperFor(PDatePicker, { props: { label: 'test datepicker', disabled: true } });
|
|
92
|
+
|
|
93
|
+
const datePicker = wrapper.findComponent({ name: 'VueDatePicker' });
|
|
94
|
+
const input = wrapper.find('input');
|
|
95
|
+
|
|
96
|
+
expect(datePicker.props().disabled).toBe(true);
|
|
97
|
+
expect(input.element.disabled).toBe(true);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it('updates the value bound with v-model', async () => {
|
|
101
|
+
// Since VueDatePicker emits are not defined on PDatePicker we need to Spy on console.warn and mock the implementation to suppress the warning:
|
|
102
|
+
// [Vue warn]: Component emitted event "update:modelValue" but it is neither declared in the emits option nor as an "onUpdate:view" prop.
|
|
103
|
+
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
|
104
|
+
|
|
105
|
+
const wrapper = createWrapperFor({
|
|
106
|
+
template: `<PDatePicker v-model="dateStrVal" />`,
|
|
107
|
+
components: { PDatePicker },
|
|
108
|
+
data() {
|
|
109
|
+
return {
|
|
110
|
+
dateStrVal: '2024-09-01',
|
|
111
|
+
};
|
|
112
|
+
},
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
const datePicker = wrapper.findComponent({ name: 'VueDatePicker' });
|
|
116
|
+
|
|
117
|
+
datePicker.vm.$emit('update:modelValue', '2024-09-02');
|
|
118
|
+
|
|
119
|
+
expect(wrapper.vm.dateStrVal).toBe('2024-09-02');
|
|
120
|
+
|
|
121
|
+
warnSpy.mockRestore();
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
it('sets the error state correctly', () => {
|
|
125
|
+
const wrapper = createWrapper({
|
|
126
|
+
label: 'test datepicker',
|
|
127
|
+
errorMsg: 'datepicker has error',
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
const pInput = wrapper.findComponent({ name: 'PInput' });
|
|
131
|
+
|
|
132
|
+
expect(pInput.props().errorMsg).toBe('datepicker has error');
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
it('input listens to the tab event', async () => {
|
|
136
|
+
const testFn = vi.fn();
|
|
137
|
+
|
|
138
|
+
const wrapper = createWrapperFor({
|
|
139
|
+
template: `<PDatePicker v-model="dateStrVal" @keydown.tab="testEvent"><input type="text" /></PDatePicker>`,
|
|
140
|
+
components: { PDatePicker },
|
|
141
|
+
data() {
|
|
142
|
+
return {
|
|
143
|
+
dateStrVal: '2024-09-01',
|
|
144
|
+
};
|
|
145
|
+
},
|
|
146
|
+
methods: {
|
|
147
|
+
testEvent() {
|
|
148
|
+
testFn();
|
|
149
|
+
},
|
|
150
|
+
},
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
const input = wrapper.find('input');
|
|
154
|
+
|
|
155
|
+
await input.trigger('keydown.tab');
|
|
156
|
+
|
|
157
|
+
expect(testFn).toHaveBeenCalled();
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
it('clears the datepicker value when the input gets cleared', async () => {
|
|
161
|
+
// Since VueDatePicker emits are not defined on PDatePicker we need to Spy on console.warn and mock the implementation to suppress the warning:
|
|
162
|
+
// [Vue warn]: Component emitted event "update:modelValue" but it is neither declared in the emits option nor as an "onUpdate:view" prop.
|
|
163
|
+
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
|
164
|
+
|
|
165
|
+
const wrapper = createWrapperFor({
|
|
166
|
+
template: `<PDatePicker v-model="dateStrVal" />`,
|
|
167
|
+
components: { PDatePicker },
|
|
168
|
+
data() {
|
|
169
|
+
return {
|
|
170
|
+
dateStrVal: '2024-09-01',
|
|
171
|
+
};
|
|
172
|
+
},
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
const datePicker = wrapper.findComponent({ name: 'VueDatePicker' });
|
|
176
|
+
|
|
177
|
+
await datePicker.vm.$emit('update:modelValue', '');
|
|
178
|
+
|
|
179
|
+
expect(wrapper.vm.dateStrVal).toBe('');
|
|
180
|
+
|
|
181
|
+
warnSpy.mockRestore();
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
it('does not clear the datepicker value when the input has a value', async () => {
|
|
185
|
+
// Since VueDatePicker emits are not defined on PDatePicker we need to Spy on console.warn and mock the implementation to suppress the warning:
|
|
186
|
+
// [Vue warn]: Component emitted event "update:modelValue" but it is neither declared in the emits option nor as an "onUpdate:view" prop.
|
|
187
|
+
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
|
188
|
+
|
|
189
|
+
const wrapper = createWrapperFor({
|
|
190
|
+
template: `<PDatePicker v-model="dateStrVal" />`,
|
|
191
|
+
components: { PDatePicker },
|
|
192
|
+
data() {
|
|
193
|
+
return {
|
|
194
|
+
dateStrVal: '2024-09-01',
|
|
195
|
+
};
|
|
196
|
+
},
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
const datePicker = wrapper.findComponent({ name: 'VueDatePicker' });
|
|
200
|
+
|
|
201
|
+
await datePicker.vm.$emit('update:modelValue', '2024-09-02');
|
|
202
|
+
|
|
203
|
+
expect(wrapper.vm.dateStrVal).toBe('2024-09-02');
|
|
204
|
+
|
|
205
|
+
warnSpy.mockRestore();
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
it('calls onInput, onEnter, and onTab when respective events occur', async () => {
|
|
209
|
+
// Mock the scoped slot functions
|
|
210
|
+
const onInput = vi.fn();
|
|
211
|
+
const onEnter = vi.fn();
|
|
212
|
+
const onTab = vi.fn();
|
|
213
|
+
const onFocus = vi.fn();
|
|
214
|
+
const onBlur = vi.fn();
|
|
215
|
+
const onClear = vi.fn();
|
|
216
|
+
|
|
217
|
+
// Stub VueDatePicker and pass the scoped slot props to the PInput component
|
|
218
|
+
const wrapper = createWrapperFor(PDatePicker, {
|
|
219
|
+
global: {
|
|
220
|
+
stubs: {
|
|
221
|
+
VueDatePicker: {
|
|
222
|
+
template: `
|
|
223
|
+
<div>
|
|
224
|
+
<slot name="dp-input"
|
|
225
|
+
:value="value"
|
|
226
|
+
:onInput="onInput"
|
|
227
|
+
:onEnter="onEnter"
|
|
228
|
+
:onTab="onTab"
|
|
229
|
+
:onFocus="onFocus"
|
|
230
|
+
:onBlur="onBlur"
|
|
231
|
+
:onClear="onClear"
|
|
232
|
+
/>
|
|
233
|
+
</div>
|
|
234
|
+
`,
|
|
235
|
+
data() {
|
|
236
|
+
return {
|
|
237
|
+
value: '',
|
|
238
|
+
};
|
|
239
|
+
},
|
|
240
|
+
methods: {
|
|
241
|
+
onInput,
|
|
242
|
+
onEnter,
|
|
243
|
+
onTab,
|
|
244
|
+
onFocus,
|
|
245
|
+
onBlur,
|
|
246
|
+
onClear,
|
|
247
|
+
},
|
|
248
|
+
},
|
|
249
|
+
},
|
|
250
|
+
},
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
const input = wrapper.find('input');
|
|
254
|
+
|
|
255
|
+
// Simulate the input and keyboard events
|
|
256
|
+
await input.setValue('2023-10-21');
|
|
257
|
+
await input.trigger('input');
|
|
258
|
+
await input.trigger('keydown.enter');
|
|
259
|
+
await input.trigger('keydown.tab');
|
|
260
|
+
|
|
261
|
+
// Simulate focus and blur events
|
|
262
|
+
await input.trigger('focus');
|
|
263
|
+
await input.trigger('blur');
|
|
264
|
+
|
|
265
|
+
// Simulate clearing the input (which should trigger onClear)
|
|
266
|
+
await input.setValue('');
|
|
267
|
+
await input.trigger('input');
|
|
268
|
+
|
|
269
|
+
expect(onInput).toHaveBeenCalled();
|
|
270
|
+
expect(onEnter).toHaveBeenCalled();
|
|
271
|
+
expect(onTab).toHaveBeenCalled();
|
|
272
|
+
expect(onFocus).toHaveBeenCalled();
|
|
273
|
+
expect(onBlur).toHaveBeenCalled();
|
|
274
|
+
expect(onClear).toHaveBeenCalled();
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
it('returns the placeholder if it is provided in props', async () => {
|
|
278
|
+
const wrapper = createWrapperFor(PDatePicker, { props: { placeholder: 'Select a date', format: 'dd-MMM-yyyy' } });
|
|
279
|
+
|
|
280
|
+
const input = wrapper.find('input');
|
|
281
|
+
|
|
282
|
+
expect(input.element.placeholder).toBe('Select a date');
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
it('returns the format if no placeholder is provided', async () => {
|
|
286
|
+
const wrapper = createWrapperFor(PDatePicker, { props: { placeholder: '', format: 'dd-MMM-yyyy' } });
|
|
287
|
+
|
|
288
|
+
const input = wrapper.find('input');
|
|
289
|
+
|
|
290
|
+
expect(input.element.placeholder).toBe('dd-MMM-yyyy');
|
|
291
|
+
});
|
|
292
|
+
});
|
|
@@ -15,8 +15,8 @@ export default {
|
|
|
15
15
|
docs: {
|
|
16
16
|
description: {
|
|
17
17
|
component: `Allows users to enter a date either through text input, or by choosing a date from the calendar.
|
|
18
|
-
This component uses \`
|
|
19
|
-
So please take a look also there at their extensive [documentation](https://
|
|
18
|
+
This component uses \`VueDatePicker\` from [@vuepic/vue-datepicker](https://vue3datepicker.com) internally.
|
|
19
|
+
So please take a look also there at their extensive [documentation](https://vue3datepicker.com/).`,
|
|
20
20
|
},
|
|
21
21
|
},
|
|
22
22
|
},
|
|
@@ -1,126 +1,81 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<
|
|
3
|
-
<
|
|
4
|
-
<
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
:timezone="timezone"
|
|
16
|
-
>
|
|
17
|
-
<template #default="{ inputValue, inputEvents }">
|
|
18
|
-
<PInput
|
|
19
|
-
:value="inputValue"
|
|
20
|
-
v-bind="attrsWithoutClassAndStyle"
|
|
21
|
-
:placeholder="displayPlaceholder"
|
|
22
|
-
v-on="inputEvents"
|
|
23
|
-
/>
|
|
24
|
-
</template>
|
|
25
|
-
</DatePicker>
|
|
26
|
-
<div v-show="errorMsg" :class="errorMsgClasses">{{ errorMsg }}</div>
|
|
27
|
-
</div>
|
|
2
|
+
<VueDatePicker v-model="model" :class="[{ hidden: $attrs.hidden }, $attrs.class]" v-bind="datePickerProps">
|
|
3
|
+
<template #dp-input="{ value, onInput, onEnter, onTab, onFocus, onBlur, onClear }">
|
|
4
|
+
<PInput
|
|
5
|
+
:model-value="value"
|
|
6
|
+
v-bind="inputPropsAndAttrs"
|
|
7
|
+
@input="handleInput($event, onInput, onClear)"
|
|
8
|
+
@keydown.enter="onEnter($event)"
|
|
9
|
+
@keydown.tab="onTab($event)"
|
|
10
|
+
@focus="onFocus"
|
|
11
|
+
@blur="onBlur"
|
|
12
|
+
/>
|
|
13
|
+
</template>
|
|
14
|
+
</VueDatePicker>
|
|
28
15
|
</template>
|
|
29
16
|
|
|
30
17
|
<script setup lang="ts">
|
|
31
18
|
import PInput from '@squirrel/components/p-input/p-input.vue';
|
|
32
|
-
import {
|
|
33
|
-
import
|
|
34
|
-
import { isDate, isString } from 'lodash-es';
|
|
35
|
-
import { DatePicker } from 'v-calendar';
|
|
36
|
-
import { computed, nextTick, ref, type StyleValue, useAttrs, watch } from 'vue';
|
|
19
|
+
import VueDatePicker, { type VueDatePickerProps } from '@vuepic/vue-datepicker';
|
|
20
|
+
import { computed, useAttrs } from 'vue';
|
|
37
21
|
|
|
38
22
|
defineOptions({
|
|
39
23
|
name: 'PDatePicker',
|
|
40
24
|
inheritAttrs: false,
|
|
41
25
|
});
|
|
42
26
|
|
|
43
|
-
// The type of the select attribute is defined in node_modules/v-calendar/dist/types/src/utils/attribute.d.ts
|
|
44
|
-
// but there was no way to import it, so as a workaround we cast the selectAttribute as `any`.
|
|
45
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
46
|
-
const selectAttribute = { highlight: { class: 'bg-primary', contentClass: 'text-white' } } as any;
|
|
47
|
-
|
|
48
|
-
const DEFAULT_MASKS = {
|
|
49
|
-
// The mask for the input
|
|
50
|
-
input: 'DD-MMM-YYYY',
|
|
51
|
-
// The mask for the model value
|
|
52
|
-
data: 'YYYY-MM-DD',
|
|
53
|
-
};
|
|
54
|
-
|
|
55
27
|
type Props = {
|
|
56
|
-
modelValue?: string | null;
|
|
57
28
|
label?: string;
|
|
58
29
|
errorMsg?: string;
|
|
59
30
|
required?: boolean;
|
|
60
|
-
|
|
61
|
-
maxDate?: Date | null;
|
|
62
|
-
timezone?: string;
|
|
63
|
-
};
|
|
31
|
+
} & VueDatePickerProps;
|
|
64
32
|
|
|
65
33
|
const props = withDefaults(defineProps<Props>(), {
|
|
66
|
-
modelValue: '',
|
|
67
34
|
label: '',
|
|
68
35
|
errorMsg: '',
|
|
69
36
|
required: false,
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
37
|
+
inline: false,
|
|
38
|
+
autoApply: true,
|
|
39
|
+
enableTimePicker: false,
|
|
40
|
+
modelType: 'yyyy-MM-dd',
|
|
41
|
+
hideOffsetDates: true,
|
|
42
|
+
weekStart: 0,
|
|
43
|
+
textInput: true,
|
|
44
|
+
format: 'dd-MMM-yyyy',
|
|
73
45
|
});
|
|
74
46
|
|
|
75
|
-
const
|
|
76
|
-
'update:modelValue': [value: string | null];
|
|
77
|
-
}>();
|
|
47
|
+
const model = defineModel<Date | string | null>({ default: '' });
|
|
78
48
|
|
|
79
49
|
// Data
|
|
80
|
-
const { labelClasses, errorMsgClasses } = useInputClasses(props);
|
|
81
50
|
const attrs = useAttrs();
|
|
82
|
-
// innerValue is a Date object
|
|
83
|
-
const innerValue = ref<Date | null>(null);
|
|
84
|
-
const stopWatch = ref(false);
|
|
85
51
|
|
|
86
52
|
// Computed
|
|
87
|
-
const
|
|
88
|
-
|
|
89
|
-
});
|
|
53
|
+
const datePickerProps = computed(() => {
|
|
54
|
+
const { modelValue: _, ...propsWithoutModelValue } = props;
|
|
90
55
|
|
|
91
|
-
|
|
92
|
-
return isString(attrs.placeholder) ? attrs.placeholder : masks.value.input;
|
|
56
|
+
return propsWithoutModelValue;
|
|
93
57
|
});
|
|
94
58
|
|
|
95
|
-
const
|
|
96
|
-
const { class: classes, style, ...
|
|
59
|
+
const inputPropsAndAttrs = computed(() => {
|
|
60
|
+
const { class: classes, style, ...res } = attrs;
|
|
97
61
|
|
|
98
|
-
|
|
99
|
-
|
|
62
|
+
res.label = props.label;
|
|
63
|
+
res.errorMsg = props.errorMsg;
|
|
64
|
+
res.required = props.required;
|
|
65
|
+
res.disabled = props.disabled;
|
|
66
|
+
res.placeholder = props.placeholder ? props.placeholder : props.format;
|
|
100
67
|
|
|
101
|
-
|
|
102
|
-
return attrs.style as StyleValue;
|
|
68
|
+
return res;
|
|
103
69
|
});
|
|
104
70
|
|
|
105
|
-
//
|
|
106
|
-
|
|
107
|
-
(
|
|
108
|
-
|
|
109
|
-
if (!stopWatch.value) {
|
|
110
|
-
innerValue.value = nV ? dayjs(nV, masks.value.data).toDate() : null;
|
|
111
|
-
}
|
|
112
|
-
},
|
|
113
|
-
{ immediate: true }
|
|
114
|
-
);
|
|
71
|
+
// Methods
|
|
72
|
+
const handleInput = (e: Event, onInputFn: (e: Event) => void, onClearFn: (e: Event) => void) => {
|
|
73
|
+
if (e.target instanceof HTMLInputElement && e.target.value === '') {
|
|
74
|
+
onClearFn(e);
|
|
115
75
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
emit('update:modelValue', toEmit);
|
|
122
|
-
nextTick(() => {
|
|
123
|
-
stopWatch.value = false;
|
|
124
|
-
});
|
|
125
|
-
});
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return onInputFn(e);
|
|
80
|
+
};
|
|
126
81
|
</script>
|