@pequity/squirrel 8.2.1 → 8.3.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.
@@ -46,10 +46,14 @@ const _sfc_main = /* @__PURE__ */ vue.defineComponent({
46
46
  type: [String, Object, Array],
47
47
  default: "fixed bottom-0 left-0 right-0 top-0 flex flex-col items-center justify-center overflow-y-auto overflow-x-hidden outline-none"
48
48
  },
49
- modalClass: {
49
+ modalBaseClass: {
50
50
  type: [String, Object, Array],
51
51
  default: "pm relative flex flex-col rounded-2xl pb-6 cursor-default bg-surface shadow-xl"
52
52
  },
53
+ modalClass: {
54
+ type: [String, Object, Array],
55
+ default: ""
56
+ },
53
57
  modalStyle: {
54
58
  type: [String, Object, Array],
55
59
  default: ""
@@ -103,18 +107,10 @@ const _sfc_main = /* @__PURE__ */ vue.defineComponent({
103
107
  default: "500px"
104
108
  }
105
109
  },
106
- emits: [
107
- "before-open",
108
- "opening",
109
- "opened",
110
- "before-close",
111
- "closing",
112
- "closed",
113
- "update:modelValue"
114
- ],
110
+ emits: ["before-open", "opening", "opened", "before-close", "closing", "closed", "update:modelValue", "click:overlay"],
115
111
  setup(__props, { emit: __emit }) {
116
112
  vue.useCssVars((_ctx) => ({
117
- "9a05239e": __props.maxWidth
113
+ "29225c6f": __props.maxWidth
118
114
  }));
119
115
  let animatingZIndex = 0;
120
116
  const emit = __emit;
@@ -162,8 +158,9 @@ const _sfc_main = /* @__PURE__ */ vue.defineComponent({
162
158
  props.name && modal.hide(props.name);
163
159
  }
164
160
  };
165
- const clickOutside = (e) => {
161
+ const overlayClick = (e) => {
166
162
  if (e.target === pmWrapper.value) {
163
+ emit("click:overlay", e);
167
164
  close();
168
165
  }
169
166
  };
@@ -310,13 +307,13 @@ const _sfc_main = /* @__PURE__ */ vue.defineComponent({
310
307
  "aria-modal": "true",
311
308
  "aria-describedby": `${id.value}-content`,
312
309
  "aria-labelledby": `${id.value}-title`,
313
- onClick: _cache[0] || (_cache[0] = ($event) => clickOutside($event)),
310
+ onClick: _cache[0] || (_cache[0] = ($event) => overlayClick($event)),
314
311
  onKeydown: _cache[1] || (_cache[1] = ($event) => keydown($event))
315
312
  }, [
316
313
  vue.createElementVNode("div", {
317
314
  ref: "pm",
318
315
  "data-pm-id": id.value,
319
- class: vue.normalizeClass(__props.modalClass),
316
+ class: vue.normalizeClass([__props.modalBaseClass, __props.modalClass]),
320
317
  style: vue.normalizeStyle(__props.modalStyle)
321
318
  }, [
322
319
  vue.renderSlot(_ctx.$slots, "title-wrapper", {}, () => [
@@ -371,5 +368,5 @@ const _sfc_main = /* @__PURE__ */ vue.defineComponent({
371
368
  };
372
369
  }
373
370
  });
374
- const pModal = /* @__PURE__ */ _pluginVue_exportHelper._export_sfc(_sfc_main, [["__scopeId", "data-v-c3379c94"]]);
371
+ const pModal = /* @__PURE__ */ _pluginVue_exportHelper._export_sfc(_sfc_main, [["__scopeId", "data-v-a511ac01"]]);
375
372
  module.exports = pModal;
@@ -45,10 +45,14 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
45
45
  type: [String, Object, Array],
46
46
  default: "fixed bottom-0 left-0 right-0 top-0 flex flex-col items-center justify-center overflow-y-auto overflow-x-hidden outline-none"
47
47
  },
48
- modalClass: {
48
+ modalBaseClass: {
49
49
  type: [String, Object, Array],
50
50
  default: "pm relative flex flex-col rounded-2xl pb-6 cursor-default bg-surface shadow-xl"
51
51
  },
52
+ modalClass: {
53
+ type: [String, Object, Array],
54
+ default: ""
55
+ },
52
56
  modalStyle: {
53
57
  type: [String, Object, Array],
54
58
  default: ""
@@ -102,18 +106,10 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
102
106
  default: "500px"
103
107
  }
104
108
  },
105
- emits: [
106
- "before-open",
107
- "opening",
108
- "opened",
109
- "before-close",
110
- "closing",
111
- "closed",
112
- "update:modelValue"
113
- ],
109
+ emits: ["before-open", "opening", "opened", "before-close", "closing", "closed", "update:modelValue", "click:overlay"],
114
110
  setup(__props, { emit: __emit }) {
115
111
  useCssVars((_ctx) => ({
116
- "9a05239e": __props.maxWidth
112
+ "29225c6f": __props.maxWidth
117
113
  }));
118
114
  let animatingZIndex = 0;
119
115
  const emit = __emit;
@@ -161,8 +157,9 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
161
157
  props.name && modal.hide(props.name);
162
158
  }
163
159
  };
164
- const clickOutside = (e) => {
160
+ const overlayClick = (e) => {
165
161
  if (e.target === pmWrapper.value) {
162
+ emit("click:overlay", e);
166
163
  close();
167
164
  }
168
165
  };
@@ -309,13 +306,13 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
309
306
  "aria-modal": "true",
310
307
  "aria-describedby": `${id.value}-content`,
311
308
  "aria-labelledby": `${id.value}-title`,
312
- onClick: _cache[0] || (_cache[0] = ($event) => clickOutside($event)),
309
+ onClick: _cache[0] || (_cache[0] = ($event) => overlayClick($event)),
313
310
  onKeydown: _cache[1] || (_cache[1] = ($event) => keydown($event))
314
311
  }, [
315
312
  createElementVNode("div", {
316
313
  ref: "pm",
317
314
  "data-pm-id": id.value,
318
- class: normalizeClass(__props.modalClass),
315
+ class: normalizeClass([__props.modalBaseClass, __props.modalClass]),
319
316
  style: normalizeStyle(__props.modalStyle)
320
317
  }, [
321
318
  renderSlot(_ctx.$slots, "title-wrapper", {}, () => [
@@ -370,7 +367,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
370
367
  };
371
368
  }
372
369
  });
373
- const pModal = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-c3379c94"]]);
370
+ const pModal = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-a511ac01"]]);
374
371
  export {
375
372
  pModal as default
376
373
  };
@@ -32,6 +32,10 @@ declare const __VLS_component: import("vue").DefineComponent<import("vue").Extra
32
32
  type: PropType<StyleValue>;
33
33
  default: string;
34
34
  };
35
+ modalBaseClass: {
36
+ type: PropType<StyleValue>;
37
+ default: string;
38
+ };
35
39
  modalClass: {
36
40
  type: PropType<StyleValue>;
37
41
  default: string;
@@ -89,13 +93,14 @@ declare const __VLS_component: import("vue").DefineComponent<import("vue").Extra
89
93
  default: string;
90
94
  };
91
95
  }>, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
92
- "update:modelValue": (...args: any[]) => void;
93
- closed: (...args: any[]) => void;
94
- "before-open": (...args: any[]) => void;
95
- opening: (...args: any[]) => void;
96
- opened: (...args: any[]) => void;
97
- "before-close": (...args: any[]) => void;
98
- closing: (...args: any[]) => void;
96
+ "update:modelValue": (args_0: boolean) => any;
97
+ closed: () => any;
98
+ "before-open": () => any;
99
+ opening: () => any;
100
+ opened: () => any;
101
+ "before-close": () => any;
102
+ closing: () => any;
103
+ "click:overlay": (args_0: MouseEvent) => any;
99
104
  }, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
100
105
  name: {
101
106
  type: StringConstructor;
@@ -117,6 +122,10 @@ declare const __VLS_component: import("vue").DefineComponent<import("vue").Extra
117
122
  type: PropType<StyleValue>;
118
123
  default: string;
119
124
  };
125
+ modalBaseClass: {
126
+ type: PropType<StyleValue>;
127
+ default: string;
128
+ };
120
129
  modalClass: {
121
130
  type: PropType<StyleValue>;
122
131
  default: string;
@@ -174,13 +183,14 @@ declare const __VLS_component: import("vue").DefineComponent<import("vue").Extra
174
183
  default: string;
175
184
  };
176
185
  }>> & Readonly<{
177
- "onUpdate:modelValue"?: ((...args: any[]) => any) | undefined;
178
- onClosed?: ((...args: any[]) => any) | undefined;
179
- "onBefore-open"?: ((...args: any[]) => any) | undefined;
180
- onOpening?: ((...args: any[]) => any) | undefined;
181
- onOpened?: ((...args: any[]) => any) | undefined;
182
- "onBefore-close"?: ((...args: any[]) => any) | undefined;
183
- onClosing?: ((...args: any[]) => any) | undefined;
186
+ "onUpdate:modelValue"?: ((args_0: boolean) => any) | undefined;
187
+ onClosed?: (() => any) | undefined;
188
+ "onBefore-open"?: (() => any) | undefined;
189
+ onOpening?: (() => any) | undefined;
190
+ onOpened?: (() => any) | undefined;
191
+ "onBefore-close"?: (() => any) | undefined;
192
+ onClosing?: (() => any) | undefined;
193
+ "onClick:overlay"?: ((args_0: MouseEvent) => any) | undefined;
184
194
  }>, {
185
195
  name: string;
186
196
  title: string;
@@ -196,6 +206,7 @@ declare const __VLS_component: import("vue").DefineComponent<import("vue").Extra
196
206
  baseZindex: number;
197
207
  bgClass: StyleValue;
198
208
  wrapperClass: StyleValue;
209
+ modalBaseClass: StyleValue;
199
210
  modalClass: StyleValue;
200
211
  modalStyle: StyleValue;
201
212
  bgInClass: string;
package/dist/squirrel.css CHANGED
@@ -295,33 +295,33 @@ to {
295
295
  opacity: 0;
296
296
  transform: translate3d(0, -100%, 0);
297
297
  }
298
- }.pm[data-v-c3379c94] {
298
+ }.pm[data-v-a511ac01] {
299
299
  width: calc(100% - 32px);
300
300
  min-width: 110px;
301
- max-width: var(--9a05239e);
301
+ max-width: var(--29225c6f);
302
302
  max-height: calc(100vh - 32px);
303
303
  }
304
- .fadeIn[data-v-c3379c94] {
304
+ .fadeIn[data-v-a511ac01] {
305
305
  animation-duration: 0.4s;
306
- animation-name: fadeInFrames-c3379c94;
306
+ animation-name: fadeInFrames-a511ac01;
307
307
  animation-fill-mode: both;
308
308
  }
309
- .fadeOut[data-v-c3379c94] {
309
+ .fadeOut[data-v-a511ac01] {
310
310
  animation-duration: 0.2s;
311
- animation-name: fadeOutFrames-c3379c94;
311
+ animation-name: fadeOutFrames-a511ac01;
312
312
  animation-fill-mode: both;
313
313
  }
314
- .slideInTop[data-v-c3379c94] {
314
+ .slideInTop[data-v-a511ac01] {
315
315
  animation-duration: 0.4s;
316
- animation-name: fadeInFrames-c3379c94,slideInTopFrames-c3379c94;
316
+ animation-name: fadeInFrames-a511ac01,slideInTopFrames-a511ac01;
317
317
  animation-fill-mode: both;
318
318
  }
319
- .slideOutTop[data-v-c3379c94] {
319
+ .slideOutTop[data-v-a511ac01] {
320
320
  animation-duration: 0.2s;
321
- animation-name: fadeOutFrames-c3379c94,slideOutTopFrames-c3379c94;
321
+ animation-name: fadeOutFrames-a511ac01,slideOutTopFrames-a511ac01;
322
322
  animation-fill-mode: both;
323
323
  }
324
- @keyframes slideInTopFrames-c3379c94 {
324
+ @keyframes slideInTopFrames-a511ac01 {
325
325
  from {
326
326
  transform: translate(0, -12px);
327
327
  animation-timing-function: cubic-bezier(0, 0, 0, 1);
@@ -330,7 +330,7 @@ to {
330
330
  transform: translate(0, 0);
331
331
  }
332
332
  }
333
- @keyframes slideOutTopFrames-c3379c94 {
333
+ @keyframes slideOutTopFrames-a511ac01 {
334
334
  from {
335
335
  transform: translate(0, 0);
336
336
  animation-timing-function: cubic-bezier(0.33, 0, 0.67, 1);
@@ -339,7 +339,7 @@ to {
339
339
  transform: translate(0, -12px);
340
340
  }
341
341
  }
342
- @keyframes fadeInFrames-c3379c94 {
342
+ @keyframes fadeInFrames-a511ac01 {
343
343
  from {
344
344
  opacity: 0;
345
345
  animation-timing-function: cubic-bezier(0, 0, 1, 1);
@@ -348,7 +348,7 @@ to {
348
348
  opacity: 1;
349
349
  }
350
350
  }
351
- @keyframes fadeOutFrames-c3379c94 {
351
+ @keyframes fadeOutFrames-a511ac01 {
352
352
  from {
353
353
  opacity: 1;
354
354
  animation-timing-function: cubic-bezier(0.33, 0, 0.67, 1);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@pequity/squirrel",
3
3
  "description": "Squirrel component library",
4
- "version": "8.2.1",
4
+ "version": "8.3.1",
5
5
  "packageManager": "pnpm@10.6.4",
6
6
  "type": "module",
7
7
  "scripts": {
@@ -53,29 +53,29 @@
53
53
  "@playwright/test": "^1.52.0",
54
54
  "@semantic-release/changelog": "^6.0.3",
55
55
  "@semantic-release/git": "^10.0.1",
56
- "@storybook/addon-a11y": "^8.6.12",
57
- "@storybook/addon-actions": "^8.6.12",
58
- "@storybook/addon-essentials": "^8.6.12",
59
- "@storybook/addon-interactions": "^8.6.12",
60
- "@storybook/addon-links": "^8.6.12",
61
- "@storybook/blocks": "^8.6.12",
62
- "@storybook/manager-api": "^8.6.12",
63
- "@storybook/test": "^8.6.12",
56
+ "@storybook/addon-a11y": "^8.6.14",
57
+ "@storybook/addon-actions": "^8.6.14",
58
+ "@storybook/addon-essentials": "^8.6.14",
59
+ "@storybook/addon-interactions": "^8.6.14",
60
+ "@storybook/addon-links": "^8.6.14",
61
+ "@storybook/blocks": "^8.6.14",
62
+ "@storybook/manager-api": "^8.6.14",
63
+ "@storybook/test": "^8.6.14",
64
64
  "@storybook/test-runner": "^0.22.0",
65
- "@storybook/theming": "^8.6.12",
66
- "@storybook/vue3": "^8.6.12",
67
- "@storybook/vue3-vite": "^8.6.12",
65
+ "@storybook/theming": "^8.6.14",
66
+ "@storybook/vue3": "^8.6.14",
67
+ "@storybook/vue3-vite": "^8.6.14",
68
68
  "@tanstack/vue-virtual": "3.13.8",
69
69
  "@types/jsdom": "^21.1.7",
70
70
  "@types/lodash-es": "^4.17.12",
71
- "@types/node": "^22.15.18",
71
+ "@types/node": "^22.15.19",
72
72
  "@vitejs/plugin-vue": "^5.2.4",
73
73
  "@vitest/coverage-v8": "^3.1.3",
74
- "@vue/compiler-sfc": "3.5.13",
74
+ "@vue/compiler-sfc": "3.5.14",
75
75
  "@vue/test-utils": "^2.4.6",
76
76
  "@vuepic/vue-datepicker": "11.0.2",
77
77
  "autoprefixer": "^10.4.21",
78
- "eslint": "^9.26.0",
78
+ "eslint": "^9.27.0",
79
79
  "eslint-plugin-storybook": "^0.12.0",
80
80
  "floating-vue": "5.2.2",
81
81
  "glob": "^11.0.2",
@@ -90,15 +90,15 @@
90
90
  "prettier-plugin-tailwindcss": "^0.6.11",
91
91
  "resolve-tspaths": "^0.8.23",
92
92
  "rimraf": "^6.0.1",
93
- "sass": "^1.88.0",
94
- "semantic-release": "^24.2.3",
95
- "storybook": "^8.6.12",
93
+ "sass": "^1.89.0",
94
+ "semantic-release": "^24.2.4",
95
+ "storybook": "^8.6.14",
96
96
  "svgo": "^3.3.2",
97
97
  "tailwindcss": "^3.4.17",
98
98
  "typescript": "5.8.3",
99
99
  "vite": "^6.3.5",
100
100
  "vitest": "^3.1.3",
101
- "vue": "3.5.13",
101
+ "vue": "3.5.14",
102
102
  "vue-currency-input": "3.2.1",
103
103
  "vue-router": "4.5.1",
104
104
  "vue-toastification": "2.0.0-rc.5",
@@ -1,4 +1,5 @@
1
1
  import PDrawer from '@squirrel/components/p-drawer/p-drawer.vue';
2
+ import { ref } from 'vue';
2
3
 
3
4
  export default {
4
5
  title: 'Components/PDrawer',
@@ -27,7 +28,9 @@ export const Default = {
27
28
  return {
28
29
  components: { PDrawer },
29
30
  setup() {
30
- return { argsWithoutSlots };
31
+ const show = ref(false);
32
+
33
+ return { argsWithoutSlots, show };
31
34
  },
32
35
  template: `
33
36
  <div>
@@ -39,11 +42,6 @@ export const Default = {
39
42
  </template>
40
43
  </PDrawer>
41
44
  </div>`,
42
- data() {
43
- return {
44
- show: false,
45
- };
46
- },
47
45
  };
48
46
  },
49
47
  args: {
@@ -1,4 +1,5 @@
1
1
  import PDropdown from '@squirrel/components/p-dropdown/p-dropdown.vue';
2
+ import { ref } from 'vue';
2
3
 
3
4
  export default {
4
5
  title: 'Components/PDropdown',
@@ -66,7 +67,10 @@ export const DynamicReference = {
66
67
  render: (args) => ({
67
68
  components: { PDropdown },
68
69
  setup() {
69
- return { args };
70
+ const show1 = ref(false);
71
+ const show1Reference = ref(null);
72
+
73
+ return { args, show1, show1Reference };
70
74
  },
71
75
  template: `
72
76
  <div>
@@ -85,12 +89,6 @@ export const DynamicReference = {
85
89
  </template>
86
90
  </PDropdown>
87
91
  </div>`,
88
- data() {
89
- return {
90
- show1: false,
91
- show1Reference: null,
92
- };
93
- },
94
92
  methods: {
95
93
  showDyn(e) {
96
94
  this.show1Reference = e.target;
@@ -134,7 +134,9 @@ export const WithSlots = {
134
134
  render: (args) => ({
135
135
  components: { PDropdownSelect },
136
136
  setup() {
137
- return { args };
137
+ const selected = ref(null);
138
+
139
+ return { args, selected };
138
140
  },
139
141
  template: `
140
142
  <div>
@@ -160,11 +162,6 @@ export const WithSlots = {
160
162
  </div>
161
163
  </div>
162
164
  `,
163
- data() {
164
- return {
165
- selected: null,
166
- };
167
- },
168
165
  }),
169
166
  argTypes: {
170
167
  ...fieldArgTypes,
@@ -188,7 +185,11 @@ export const Sizes = {
188
185
  render: (args) => ({
189
186
  components: { PDropdownSelect },
190
187
  setup() {
191
- return { args };
188
+ const selectedSm = ref(null);
189
+ const selectedMd = ref(null);
190
+ const selectedLg = ref(null);
191
+
192
+ return { args, selectedSm, selectedMd, selectedLg };
192
193
  },
193
194
  template: `
194
195
  <div>
@@ -214,13 +215,6 @@ export const Sizes = {
214
215
  />
215
216
  </div>
216
217
  `,
217
- data() {
218
- return {
219
- selectedSm: null,
220
- selectedMd: null,
221
- selectedLg: null,
222
- };
223
- },
224
218
  }),
225
219
  argTypes: {
226
220
  ...fieldArgTypes,
@@ -1,5 +1,6 @@
1
1
  import { fieldArgTypes } from '@root/stories/common/field';
2
2
  import PInputPercent from '@squirrel/components/p-input-percent/p-input-percent.vue';
3
+ import { ref } from 'vue';
3
4
 
4
5
  export default {
5
6
  title: 'Components/PInputPercent',
@@ -26,7 +27,9 @@ export const Default = {
26
27
  render: (args) => ({
27
28
  components: { PInputPercent },
28
29
  setup() {
29
- return { args };
30
+ const value = ref(0.12);
31
+
32
+ return { args, value };
30
33
  },
31
34
  template: `
32
35
  <div>
@@ -34,11 +37,6 @@ export const Default = {
34
37
  Model value: {{ value }}
35
38
  </div>
36
39
  `,
37
- data() {
38
- return {
39
- value: 0.12,
40
- };
41
- },
42
40
  }),
43
41
  args: {
44
42
  size: 'md',
@@ -50,7 +48,9 @@ export const Sizes = {
50
48
  render: (args) => ({
51
49
  components: { PInputPercent },
52
50
  setup() {
53
- return { args };
51
+ const value = ref(0.23);
52
+
53
+ return { args, value };
54
54
  },
55
55
  template: `
56
56
  <div>
@@ -60,11 +60,6 @@ export const Sizes = {
60
60
  <div class="mt-6">{{ value }}</div>
61
61
  </div>
62
62
  `,
63
- data() {
64
- return {
65
- value: 0.23,
66
- };
67
- },
68
63
  }),
69
64
  argTypes: {
70
65
  ...fieldArgTypes,
@@ -78,4 +78,36 @@ describe('Modal basic functionality', () => {
78
78
 
79
79
  wrapper.unmount();
80
80
  });
81
+
82
+ it('sets the correct base modal class', async () => {
83
+ const wrapper = createWrapperContainer();
84
+
85
+ await wrapper.setData({ showModal: true });
86
+
87
+ expect(wrapper.find('[data-pm-id]').classes()).toEqual(
88
+ 'pm relative flex flex-col rounded-2xl pb-6 cursor-default bg-surface shadow-xl'.split(' ')
89
+ );
90
+
91
+ wrapper.unmount();
92
+ });
93
+
94
+ it('passes the modalBaseClass prop to the modal', async () => {
95
+ const wrapper = createWrapperContainer({ modalBaseClass: 'custom-class' });
96
+
97
+ await wrapper.setData({ showModal: true });
98
+
99
+ expect(wrapper.find('[data-pm-id]').classes()).toEqual(['custom-class']);
100
+
101
+ wrapper.unmount();
102
+ });
103
+
104
+ it('passes the modalClass prop to the modal', async () => {
105
+ const wrapper = createWrapperContainer({ modalClass: 'h-full' });
106
+
107
+ await wrapper.setData({ showModal: true });
108
+
109
+ expect(wrapper.find('[data-pm-id]').classes()).toContain('h-full');
110
+
111
+ wrapper.unmount();
112
+ });
81
113
  });
@@ -2,17 +2,35 @@ import PModal from '@squirrel/components/p-modal/p-modal.vue';
2
2
  import { sleep, waitRAF } from '@tests/vitest.helpers';
3
3
  import { mount } from '@vue/test-utils';
4
4
 
5
- const createWrapper = () => {
6
- return mount(PModal, {
5
+ const createWrapperContainer = (componentArgs) => {
6
+ const args = componentArgs || {};
7
+
8
+ args.appendTo = '#modal-host';
9
+
10
+ const wrapperContainer = {
11
+ components: {
12
+ PModal,
13
+ },
14
+ data() {
15
+ return {
16
+ showModal: false,
17
+ args,
18
+ };
19
+ },
20
+ template: `
21
+ <button @click="showModal = !showModal">Toggle modal</button>
22
+ <div id="modal-host"></div>
23
+ <PModal v-model="showModal" v-bind="args"><p>Modal content goes here...</p></PModal>
24
+ `,
25
+ };
26
+
27
+ return mount(wrapperContainer, {
7
28
  attachTo: document.body,
8
29
  global: {
9
30
  stubs: {
10
31
  transition: false,
11
32
  },
12
33
  },
13
- slots: {
14
- default: '<p>Modal content goes here...</p>',
15
- },
16
34
  });
17
35
  };
18
36
 
@@ -22,24 +40,39 @@ describe('Modal events', () => {
22
40
  });
23
41
 
24
42
  it.each(['before-open', 'opening', 'opened'])('emits a %s event when opening', async (eventName) => {
25
- const wrapper = createWrapper();
43
+ const wrapper = createWrapperContainer();
44
+ const modal = wrapper.findComponent(PModal);
26
45
 
27
- await wrapper.setProps({ modelValue: true });
46
+ await wrapper.find('button').trigger('click');
28
47
  await waitRAF();
29
48
  await sleep(200);
30
- expect(wrapper.emitted()[eventName]).toBeTruthy();
49
+ expect(modal.emitted()[eventName]).toBeTruthy();
31
50
  wrapper.unmount();
32
51
  });
33
52
 
34
53
  it.each(['before-close', 'closing', 'closed'])('emits a %s event when closing', async (eventName) => {
35
- const wrapper = createWrapper();
54
+ const wrapper = createWrapperContainer();
55
+ const modal = wrapper.findComponent(PModal);
36
56
 
37
- await wrapper.setProps({ modelValue: true });
57
+ await wrapper.find('button').trigger('click');
38
58
  await waitRAF();
39
- await wrapper.setProps({ modelValue: false });
59
+ await wrapper.find('button').trigger('click');
40
60
  await waitRAF();
41
61
  await sleep(200);
42
- expect(wrapper.emitted()[eventName]).toBeTruthy();
62
+ expect(modal.emitted()[eventName]).toBeTruthy();
63
+ wrapper.unmount();
64
+ });
65
+
66
+ it('emits a click:overlay event when clicking on the overlay', async () => {
67
+ const wrapper = createWrapperContainer();
68
+ const modal = wrapper.findComponent(PModal);
69
+
70
+ await wrapper.find('button').trigger('click');
71
+ await waitRAF();
72
+
73
+ await wrapper.find('[role="dialog"]').trigger('click');
74
+
75
+ expect(modal.emitted('click:overlay')).toBeTruthy();
43
76
  wrapper.unmount();
44
77
  });
45
78
  });
@@ -65,3 +65,25 @@ export const Default = {
65
65
  `,
66
66
  },
67
67
  };
68
+
69
+ export const FullHeight = {
70
+ render: (args) => ({
71
+ components: { PModal, PBtn },
72
+ setup() {
73
+ const modal = usePModal();
74
+
75
+ return { args, modal };
76
+ },
77
+ template: `
78
+ <div>
79
+ <PBtn @click="modal.show('full-height-modal')">Open full height modal</PBtn>
80
+ <PModal v-bind="args">${args.default}</PModal>
81
+ </div>`,
82
+ }),
83
+ args: {
84
+ name: 'full-height-modal',
85
+ title: 'Full height modal',
86
+ modalClass: 'h-full',
87
+ default: Default.args.default,
88
+ },
89
+ };
@@ -26,10 +26,10 @@
26
26
  aria-modal="true"
27
27
  :aria-describedby="`${id}-content`"
28
28
  :aria-labelledby="`${id}-title`"
29
- @click="clickOutside($event)"
29
+ @click="overlayClick($event)"
30
30
  @keydown="keydown($event)"
31
31
  >
32
- <div ref="pm" :data-pm-id="id" :class="modalClass" :style="modalStyle">
32
+ <div ref="pm" :data-pm-id="id" :class="[modalBaseClass, modalClass]" :style="modalStyle">
33
33
  <slot name="title-wrapper">
34
34
  <div class="flex pb-4 pl-8 pr-4 pt-4">
35
35
  <h3 v-if="title" :id="`${id}-title`" class="mr-auto pt-4 text-xl font-semibold">
@@ -94,15 +94,16 @@ defineOptions({
94
94
  name: 'PModal',
95
95
  });
96
96
 
97
- const emit = defineEmits([
98
- 'before-open',
99
- 'opening',
100
- 'opened',
101
- 'before-close',
102
- 'closing',
103
- 'closed',
104
- 'update:modelValue',
105
- ]);
97
+ const emit = defineEmits<{
98
+ 'before-open': [];
99
+ opening: [];
100
+ opened: [];
101
+ 'before-close': [];
102
+ closing: [];
103
+ closed: [];
104
+ 'update:modelValue': [boolean];
105
+ 'click:overlay': [MouseEvent];
106
+ }>();
106
107
 
107
108
  const props = defineProps({
108
109
  name: {
@@ -126,10 +127,14 @@ const props = defineProps({
126
127
  default:
127
128
  'fixed bottom-0 left-0 right-0 top-0 flex flex-col items-center justify-center overflow-y-auto overflow-x-hidden outline-none',
128
129
  },
129
- modalClass: {
130
+ modalBaseClass: {
130
131
  type: [String, Object, Array] as PropType<StyleValue>,
131
132
  default: 'pm relative flex flex-col rounded-2xl pb-6 cursor-default bg-surface shadow-xl',
132
133
  },
134
+ modalClass: {
135
+ type: [String, Object, Array] as PropType<StyleValue>,
136
+ default: '',
137
+ },
133
138
  modalStyle: {
134
139
  type: [String, Object, Array] as PropType<StyleValue>,
135
140
  default: '',
@@ -234,8 +239,9 @@ const close = () => {
234
239
  }
235
240
  };
236
241
 
237
- const clickOutside = (e: MouseEvent) => {
242
+ const overlayClick = (e: MouseEvent) => {
238
243
  if (e.target === pmWrapper.value) {
244
+ emit('click:overlay', e);
239
245
  close();
240
246
  }
241
247
  };
@@ -1,4 +1,5 @@
1
1
  import PPagination from '@squirrel/components/p-pagination/p-pagination.vue';
2
+ import { ref } from 'vue';
2
3
 
3
4
  export default {
4
5
  title: 'Components/PPagination',
@@ -7,7 +8,9 @@ export default {
7
8
  render: (args) => ({
8
9
  components: { PPagination },
9
10
  setup() {
10
- return { args };
11
+ const selected = ref(1);
12
+
13
+ return { args, selected };
11
14
  },
12
15
  template: `
13
16
  <div>
@@ -17,11 +20,6 @@ export default {
17
20
  Selected: {{ selected }}
18
21
  </div>
19
22
  `,
20
- data() {
21
- return {
22
- selected: 1,
23
- };
24
- },
25
23
  }),
26
24
  parameters: {
27
25
  docs: {
@@ -1,13 +1,13 @@
1
1
  import { fieldArgTypes } from '@root/stories/common/field';
2
2
  import PSelect from '@squirrel/components/p-select/p-select.vue';
3
3
 
4
- const selectItems = Object.freeze([
4
+ const selectItems = [
5
5
  { value: 1, text: 'Aleksandr Chappel' },
6
6
  { value: 2, text: 'Van Deyes' },
7
7
  { value: 3, text: 'Meris Hardman and a very long text here' },
8
8
  { value: 4, text: 'Chick Catto' },
9
9
  { value: 5, text: "Alys O'Flynn" },
10
- ]);
10
+ ];
11
11
 
12
12
  export default {
13
13
  title: 'Components/PSelect',
@@ -1,14 +1,15 @@
1
1
  import { fieldArgTypes } from '@root/stories/common/field';
2
2
  import PSelectBtn from '@squirrel/components/p-select-btn/p-select-btn.vue';
3
3
  import { expect, userEvent, within } from '@storybook/test';
4
+ import { ref } from 'vue';
4
5
 
5
- const selectItems = Object.freeze([
6
+ const selectItems = [
6
7
  { value: 1, text: 'Aleksandr Chappel', tooltip: 'lorem ipsum text 1' },
7
8
  { value: 2, text: 'Van Deyes', tooltip: 'lorem ipsum text 2' },
8
9
  { value: 3, text: 'Meris Hardman', tooltip: 'lorem ipsum text 3' },
9
10
  { value: 4, text: 'Chick Catto', tooltip: 'lorem ipsum text 4' },
10
11
  { value: 5, text: "Alys O'Flynn", tooltip: 'lorem ipsum text 5' },
11
- ]);
12
+ ];
12
13
 
13
14
  export default {
14
15
  title: 'Components/PSelectBtn',
@@ -17,7 +18,9 @@ export default {
17
18
  render: (args) => ({
18
19
  components: { PSelectBtn },
19
20
  setup() {
20
- return { args };
21
+ const selected = ref(1);
22
+
23
+ return { args, selected };
21
24
  },
22
25
  template: `
23
26
  <div>
@@ -27,11 +30,6 @@ export default {
27
30
  Selected: {{ selected }}
28
31
  </div>
29
32
  `,
30
- data() {
31
- return {
32
- selected: 1,
33
- };
34
- },
35
33
  }),
36
34
  argTypes: {
37
35
  size: fieldArgTypes.size,
@@ -86,7 +84,9 @@ export const Sizes = {
86
84
  render: (args) => ({
87
85
  components: { PSelectBtn },
88
86
  setup() {
89
- return { args };
87
+ const selected = ref(1);
88
+
89
+ return { args, selected };
90
90
  },
91
91
  template: `
92
92
  <div>
@@ -101,11 +101,6 @@ export const Sizes = {
101
101
  </div>
102
102
  </div>
103
103
  `,
104
- data() {
105
- return {
106
- selected: 1,
107
- };
108
- },
109
104
  }),
110
105
  argTypes: {
111
106
  modelValue: {
@@ -135,7 +130,9 @@ export const WithTooltip = {
135
130
  render: (args) => ({
136
131
  components: { PSelectBtn },
137
132
  setup() {
138
- return { args };
133
+ const selected = ref(1);
134
+
135
+ return { args, selected };
139
136
  },
140
137
  template: `
141
138
  <div>
@@ -144,11 +141,6 @@ export const WithTooltip = {
144
141
  </div>
145
142
  </div>
146
143
  `,
147
- data() {
148
- return {
149
- selected: 1,
150
- };
151
- },
152
144
  }),
153
145
  argTypes: {
154
146
  modelValue: {
@@ -218,7 +210,9 @@ export const MultipleSelected = {
218
210
  render: (args) => ({
219
211
  components: { PSelectBtn },
220
212
  setup() {
221
- return { args };
213
+ const selected = ref([{ value: 2, text: 'Van Deyes', tooltip: 'lorem ipsum text 2' }]);
214
+
215
+ return { args, selected };
222
216
  },
223
217
  template: `
224
218
  <div>
@@ -228,11 +222,6 @@ export const MultipleSelected = {
228
222
  Selected: {{ selected }}
229
223
  </div>
230
224
  `,
231
- data() {
232
- return {
233
- selected: [{ value: 2, text: 'Van Deyes', tooltip: 'lorem ipsum text 2' }],
234
- };
235
- },
236
225
  }),
237
226
  args: {
238
227
  items: selectItems,
@@ -2,16 +2,17 @@ import { fieldArgTypes } from '@root/stories/common/field';
2
2
  import { getCSSTransitionDuration, sleep } from '@root/stories/common/helpers';
3
3
  import PSelectPill from '@squirrel/components/p-select-pill/p-select-pill.vue';
4
4
  import { expect, userEvent, within } from '@storybook/test';
5
+ import { ref } from 'vue';
5
6
 
6
7
  const ACTIVE_CLASS = 'text-p-purple-60';
7
8
 
8
- const selectItems = Object.freeze([
9
+ const selectItems = [
9
10
  { value: 1, text: 'Aleksandr Chappel' },
10
11
  { value: 2, text: 'Van Deyes' },
11
12
  { value: 3, text: 'Meris Hardman' },
12
13
  { value: 4, text: 'Chick Catto' },
13
14
  { value: 5, text: "Alys O'Flynn" },
14
- ]);
15
+ ];
15
16
 
16
17
  const selectItemsSubtext = selectItems.map((item, i) => ({ ...item, subtext: `text${i + 1}` }));
17
18
 
@@ -22,7 +23,9 @@ export default {
22
23
  render: (args) => ({
23
24
  components: { PSelectPill },
24
25
  setup() {
25
- return { args };
26
+ const selected = ref(1);
27
+
28
+ return { args, selected };
26
29
  },
27
30
  template: `
28
31
  <div>
@@ -32,11 +35,6 @@ export default {
32
35
  Selected: {{ selected }}
33
36
  </div>
34
37
  `,
35
- data() {
36
- return {
37
- selected: 1,
38
- };
39
- },
40
38
  }),
41
39
  argTypes: {
42
40
  size: fieldArgTypes.size,
@@ -98,7 +96,9 @@ export const Sizes = {
98
96
  render: (args) => ({
99
97
  components: { PSelectPill },
100
98
  setup() {
101
- return { args };
99
+ const selected = ref(1);
100
+
101
+ return { args, selected };
102
102
  },
103
103
  template: `
104
104
  <div>
@@ -113,11 +113,6 @@ export const Sizes = {
113
113
  </div>
114
114
  </div>
115
115
  `,
116
- data() {
117
- return {
118
- selected: 1,
119
- };
120
- },
121
116
  }),
122
117
  argTypes: {
123
118
  modelValue: {
@@ -147,7 +142,9 @@ export const Subtext = {
147
142
  render: (args) => ({
148
143
  components: { PSelectPill },
149
144
  setup() {
150
- return { args };
145
+ const selected = ref(1);
146
+
147
+ return { args, selected };
151
148
  },
152
149
  template: `
153
150
  <div>
@@ -156,11 +153,6 @@ export const Subtext = {
156
153
  </div>
157
154
  </div>
158
155
  `,
159
- data() {
160
- return {
161
- selected: 1,
162
- };
163
- },
164
156
  }),
165
157
  argTypes: {
166
158
  modelValue: {
@@ -1,5 +1,6 @@
1
1
  import { SORTING_TYPES } from '@squirrel/components/p-table-sort/p-table-sort.config';
2
2
  import PTableSort from '@squirrel/components/p-table-sort/p-table-sort.vue';
3
+ import { ref } from 'vue';
3
4
 
4
5
  export default {
5
6
  title: 'Components/PTableSort',
@@ -37,17 +38,14 @@ export const WithVModel = {
37
38
  render: (args) => ({
38
39
  components: { PTableSort },
39
40
  setup() {
40
- return { args };
41
+ const selected = ref(SORTING_TYPES.NO_SORTING);
42
+
43
+ return { args, selected };
41
44
  },
42
45
  template: `
43
46
  <div>
44
47
  <PTableSort v-model="selected" v-bind="args" />
45
48
  <div class="mt-2">Selected: {{ selected }}</div>
46
49
  </div>`,
47
- data() {
48
- return {
49
- selected: SORTING_TYPES.NO_SORTING,
50
- };
51
- },
52
50
  }),
53
51
  };