@pequity/squirrel 11.0.2 → 11.0.3

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.
@@ -79,7 +79,6 @@ describe('PTable.vue', () => {
79
79
 
80
80
  cols.forEach((col) => {
81
81
  const th = wrapper.find(`th[data-col-id="${col.id}"]`);
82
- const containerDiv = th.find('div.relative');
83
82
  const headerCell = th.find('div.header-cell-stub');
84
83
  const showFilterIcon = col.filterable || col.sortable;
85
84
 
@@ -90,12 +89,15 @@ describe('PTable.vue', () => {
90
89
  expect(headerCell.classes()).toContain('header-cell-class');
91
90
  expect(headerCell.attributes()['filter-active']).toBe(col.filterActive.toString());
92
91
  expect(headerCell.attributes()['show-filter-icon']).toBe(showFilterIcon.toString());
92
+ });
93
93
 
94
- if (!col.borderColor) {
95
- ['border-b', 'border-p-gray-30'].forEach((className) => {
96
- expect(containerDiv.classes()).toContain(className);
97
- });
98
- }
94
+ // Check border classes only for columns without custom borderColor
95
+ const colsWithoutBorder = cols.filter((col) => !col.borderColor);
96
+ colsWithoutBorder.forEach((col) => {
97
+ const th = wrapper.find(`th[data-col-id="${col.id}"]`);
98
+ const containerDiv = th.find('div.relative');
99
+ expect(containerDiv.classes()).toContain('border-b');
100
+ expect(containerDiv.classes()).toContain('border-p-gray-30');
99
101
  });
100
102
 
101
103
  data.forEach((row, i) => {
@@ -132,7 +134,6 @@ describe('PTable.vue', () => {
132
134
 
133
135
  cols.forEach((col) => {
134
136
  const th = wrapper.find(`th[data-col-id="${col.id}"]`);
135
- const containerDiv = th.find('div.relative');
136
137
  const headerCell = th.find('div.header-cell-stub');
137
138
  const showFilterIcon = col.filterable || col.sortable;
138
139
 
@@ -143,12 +144,15 @@ describe('PTable.vue', () => {
143
144
  expect(headerCell.classes()).toContain('header-cell-class');
144
145
  expect(headerCell.attributes()['filter-active']).toBe(col.filterActive.toString());
145
146
  expect(headerCell.attributes()['show-filter-icon']).toBe(showFilterIcon.toString());
147
+ });
146
148
 
147
- if (!col.borderColor) {
148
- ['border-b', 'border-p-gray-30'].forEach((className) => {
149
- expect(containerDiv.classes()).toContain(className);
150
- });
151
- }
149
+ // Check border classes only for columns without custom borderColor
150
+ const colsWithoutBorder = cols.filter((col) => !col.borderColor);
151
+ colsWithoutBorder.forEach((col) => {
152
+ const th = wrapper.find(`th[data-col-id="${col.id}"]`);
153
+ const containerDiv = th.find('div.relative');
154
+ expect(containerDiv.classes()).toContain('border-b');
155
+ expect(containerDiv.classes()).toContain('border-p-gray-30');
152
156
  });
153
157
 
154
158
  data.forEach((row, i) => {
@@ -185,7 +189,6 @@ describe('PTable.vue', () => {
185
189
 
186
190
  cols.forEach((col) => {
187
191
  const th = wrapper.find(`th[data-col-id="${col.id}"]`);
188
- const containerDiv = th.find('div.relative');
189
192
  const headerCell = th.find('div.header-cell-stub');
190
193
  const showFilterIcon = col.filterable || col.sortable;
191
194
 
@@ -196,12 +199,15 @@ describe('PTable.vue', () => {
196
199
  expect(headerCell.classes()).toContain('header-cell-class');
197
200
  expect(headerCell.attributes()['filter-active']).toBe(col.filterActive.toString());
198
201
  expect(headerCell.attributes()['show-filter-icon']).toBe(showFilterIcon.toString());
202
+ });
199
203
 
200
- if (!col.borderColor) {
201
- ['border-b', 'border-p-gray-30'].forEach((className) => {
202
- expect(containerDiv.classes()).toContain(className);
203
- });
204
- }
204
+ // Check border classes only for columns without custom borderColor
205
+ const colsWithoutBorder = cols.filter((col) => !col.borderColor);
206
+ colsWithoutBorder.forEach((col) => {
207
+ const th = wrapper.find(`th[data-col-id="${col.id}"]`);
208
+ const containerDiv = th.find('div.relative');
209
+ expect(containerDiv.classes()).toContain('border-b');
210
+ expect(containerDiv.classes()).toContain('border-p-gray-30');
205
211
  });
206
212
 
207
213
  data.forEach((row, i) => {
@@ -212,12 +218,13 @@ describe('PTable.vue', () => {
212
218
 
213
219
  cols.forEach((col, j) => {
214
220
  const innerDiv = tds[j].find('div');
215
- if (col.name === 'Third column') {
216
- expect(innerDiv.text()).toBe('Constant Cell Content');
217
- } else {
218
- expect(innerDiv.text()).toBe(row[col.name]);
219
- expect(innerDiv.classes()).toContain('py-2');
220
- }
221
+ const isThirdColumn = col.name === 'Third column';
222
+ const expectedText = isThirdColumn ? 'Constant Cell Content' : row[col.name];
223
+ expect(innerDiv.text()).toBe(expectedText);
224
+
225
+ // Only check py-2 class for non-third-column cells
226
+ const hasPy2Class = innerDiv.classes().includes('py-2');
227
+ expect(hasPy2Class).toBe(!isThirdColumn);
221
228
  });
222
229
  });
223
230
  });
@@ -242,7 +249,6 @@ describe('PTable.vue', () => {
242
249
 
243
250
  cols.forEach((col) => {
244
251
  const th = wrapper.find(`th[data-col-id="${col.id}"]`);
245
- const containerDiv = th.find('div.relative');
246
252
  const headerCell = th.find('div.header-cell-stub');
247
253
  const showFilterIcon = col.filterable || col.sortable;
248
254
 
@@ -254,15 +260,18 @@ describe('PTable.vue', () => {
254
260
  expect(headerCell.attributes()['filter-active']).toBe(col.filterActive.toString());
255
261
  expect(headerCell.attributes()['show-filter-icon']).toBe(showFilterIcon.toString());
256
262
 
257
- if (col.name === 'Second column') {
258
- expect(th.find('div.prepend').exists()).toBe(true);
259
- }
263
+ // Check prepend div for second column
264
+ const isSecondColumn = col.name === 'Second column';
265
+ expect(th.find('div.prepend').exists()).toBe(isSecondColumn);
266
+ });
260
267
 
261
- if (!col.borderColor) {
262
- ['border-b', 'border-p-gray-30'].forEach((className) => {
263
- expect(containerDiv.classes()).toContain(className);
264
- });
265
- }
268
+ // Check border classes only for columns without custom borderColor
269
+ const colsWithoutBorder = cols.filter((col) => !col.borderColor);
270
+ colsWithoutBorder.forEach((col) => {
271
+ const th = wrapper.find(`th[data-col-id="${col.id}"]`);
272
+ const containerDiv = th.find('div.relative');
273
+ expect(containerDiv.classes()).toContain('border-b');
274
+ expect(containerDiv.classes()).toContain('border-p-gray-30');
266
275
  });
267
276
  });
268
277
 
@@ -276,13 +285,12 @@ describe('PTable.vue', () => {
276
285
  const th = wrapper.find(`th[data-col-id="${col.id}"]`);
277
286
  const containerDiv = th.find('div.relative');
278
287
 
279
- if (i === 0 || i === cols.length - 1) {
280
- ['th-shadow', 'px-4'].forEach((className) => {
281
- expect(containerDiv.classes()).toContain(className);
282
- });
283
- } else {
284
- expect(containerDiv.classes()).toContain('px-2');
285
- }
288
+ const isFirstOrLast = i === 0 || i === cols.length - 1;
289
+
290
+ // Check classes based on position
291
+ expect(containerDiv.classes().includes('th-shadow')).toBe(isFirstOrLast);
292
+ expect(containerDiv.classes().includes('px-4')).toBe(isFirstOrLast);
293
+ expect(containerDiv.classes().includes('px-2')).toBe(!isFirstOrLast);
286
294
  });
287
295
  });
288
296
 
@@ -399,9 +407,8 @@ describe('PTable.vue', () => {
399
407
  // Check subheader classes include th-shadow for fixed columns
400
408
  cols.forEach((col, i) => {
401
409
  const subheaderDiv = wrapper.find(`th[data-col-id="${col.id}"] > div:last-child`);
402
- if (i === 0 || i === cols.length - 1) {
403
- expect(subheaderDiv.classes()).toContain('th-shadow');
404
- }
410
+ const isFirstOrLast = i === 0 || i === cols.length - 1;
411
+ expect(subheaderDiv.classes().includes('th-shadow')).toBe(isFirstOrLast);
405
412
  });
406
413
  });
407
414
 
@@ -104,14 +104,16 @@ describe('PTabsPills.vue', () => {
104
104
 
105
105
  const buttons = await wrapper.findAll('button');
106
106
 
107
- buttons.forEach((button, i) => {
108
- if (i === 2) {
109
- expect(button.element.disabled).toBe(true);
110
- expect(button.classes()).toContain('text-p-gray-30');
111
- } else {
112
- expect(button.element.disabled).toBe(false);
113
- expect(button.classes()).not.toContain('text-p-gray-30');
114
- }
107
+ // Test the disabled button
108
+ const disabledButton = buttons[2];
109
+ expect(disabledButton.element.disabled).toBe(true);
110
+ expect(disabledButton.classes()).toContain('text-p-gray-30');
111
+
112
+ // Test the enabled buttons
113
+ const enabledButtons = buttons.filter((_, i) => i !== 2);
114
+ enabledButtons.forEach((button) => {
115
+ expect(button.element.disabled).toBe(false);
116
+ expect(button.classes()).not.toContain('text-p-gray-30');
115
117
  });
116
118
  });
117
119
  });
@@ -0,0 +1,47 @@
1
+ {
2
+ "squirrel": {
3
+ "close": "Schließen",
4
+ "action_bar_clear_all": "Alle löschen",
5
+ "select_list_items": "{count} Element | {count} Elemente",
6
+ "select_list_select_all": "Alle auswählen",
7
+ "select_list_select_all_filtered": "Alle gefilterten auswählen",
8
+ "select_list_clear_all": "Alle löschen",
9
+ "select_list_no_items_found": "Keine Elemente gefunden",
10
+ "dropdown_select_aria_label": "Dropdown-Auswahl",
11
+ "dropdown_select_remove_item": "Element entfernen",
12
+ "dropdown_select_clear_selection": "Auswahl löschen",
13
+ "dropdown_select_all_options_selected": "Alle Optionen ausgewählt",
14
+ "dropdown_select_options": "Option | Optionen",
15
+ "dropdown_select_selected": "ausgewählt",
16
+ "dropdown_select_items": "@:squirrel.select_list_items",
17
+ "dropdown_select_select_all": "@:squirrel.select_list_select_all",
18
+ "dropdown_select_select_all_filtered": "@:squirrel.select_list_select_all_filtered",
19
+ "dropdown_select_clear_all": "@:squirrel.select_list_clear_all",
20
+ "dropdown_select_add": "Hinzufügen",
21
+ "dropdown_select_no_items_found_type_to_add": "Keine Elemente gefunden. Tippen Sie zum Hinzufügen",
22
+ "dropdown_select_no_items_found": "@:squirrel.select_list_no_items_found",
23
+ "file_upload_dropzone": "Ablagebereich",
24
+ "file_upload_drag_or_select": "Ziehen oder {select}",
25
+ "file_upload_drop": "{fileWord} ablegen",
26
+ "file_upload_max": "Max. {count}",
27
+ "file_upload_one": "Eine",
28
+ "file_upload_files": "Datei | Dateien",
29
+ "file_upload_select": "{fileWord} auswählen",
30
+ "file_upload_with_size_less_than": "mit einer Größe unter {maxSize} | mit einer Größe unter {maxSize} jeweils",
31
+ "file_upload_max_files_exceeded": "Sie können maximal {count} {fileWord} hochladen.",
32
+ "file_upload_files_not_allowed": "{extension}-Dateien sind nicht erlaubt.",
33
+ "file_upload_file_size_exceeded": "Die Dateigröße von {fileName} überschreitet {maxSize}.",
34
+ "input_search_press_enter_to_search": "Drücken Sie die Eingabetaste zum Suchen",
35
+ "input_search_clear_search_input": "Sucheingabe löschen",
36
+ "pagination_go_to_previous_page": "zur vorherigen Seite gehen",
37
+ "pagination_go_to_page": "zu Seite {page} gehen",
38
+ "pagination_go_to_next_page": "zur nächsten Seite gehen",
39
+ "pagination_info_showing_results": "Zeige {from} bis {to} von {count} Ergebnissen",
40
+ "pagination_info_no_results_found": "Keine Ergebnisse gefunden",
41
+ "table_sort_sort": "SORTIEREN",
42
+ "table_sort_clear": "Löschen",
43
+ "table_sort_sort_ascending": "Aufsteigend sortieren",
44
+ "table_sort_sort_descending": "Absteigend sortieren",
45
+ "tabs_pills_aria_label": "Tab-Pillen"
46
+ }
47
+ }
@@ -0,0 +1,47 @@
1
+ {
2
+ "squirrel": {
3
+ "close": "Cerrar",
4
+ "action_bar_clear_all": "Borrar todo",
5
+ "select_list_items": "{count} elemento | {count} elementos",
6
+ "select_list_select_all": "Seleccionar todo",
7
+ "select_list_select_all_filtered": "Seleccionar todo (filtrado)",
8
+ "select_list_clear_all": "Borrar todo",
9
+ "select_list_no_items_found": "No se encontraron elementos",
10
+ "dropdown_select_aria_label": "Selección desplegable",
11
+ "dropdown_select_remove_item": "Eliminar elemento",
12
+ "dropdown_select_clear_selection": "Borrar selección",
13
+ "dropdown_select_all_options_selected": "Todas las opciones seleccionadas",
14
+ "dropdown_select_options": "opción | opciones",
15
+ "dropdown_select_selected": "seleccionado",
16
+ "dropdown_select_items": "@:squirrel.select_list_items",
17
+ "dropdown_select_select_all": "@:squirrel.select_list_select_all",
18
+ "dropdown_select_select_all_filtered": "@:squirrel.select_list_select_all_filtered",
19
+ "dropdown_select_clear_all": "@:squirrel.select_list_clear_all",
20
+ "dropdown_select_add": "Agregar",
21
+ "dropdown_select_no_items_found_type_to_add": "No se encontraron elementos. Escriba para agregar",
22
+ "dropdown_select_no_items_found": "@:squirrel.select_list_no_items_found",
23
+ "file_upload_dropzone": "zona de carga",
24
+ "file_upload_drag_or_select": "Arrastre o {select}",
25
+ "file_upload_drop": "Soltar {fileWord}",
26
+ "file_upload_max": "Máx. {count}",
27
+ "file_upload_one": "Uno",
28
+ "file_upload_files": "archivo | archivos",
29
+ "file_upload_select": "seleccionar {fileWord}",
30
+ "file_upload_with_size_less_than": "con tamaño menor que {maxSize} | con tamaño menor que {maxSize} cada uno",
31
+ "file_upload_max_files_exceeded": "Solo puede cargar un máximo de {count} {fileWord}.",
32
+ "file_upload_files_not_allowed": "Los archivos {extension} no están permitidos.",
33
+ "file_upload_file_size_exceeded": "El tamaño del archivo {fileName} excede {maxSize}.",
34
+ "input_search_press_enter_to_search": "Presione Enter para buscar",
35
+ "input_search_clear_search_input": "Borrar entrada de búsqueda",
36
+ "pagination_go_to_previous_page": "ir a la página anterior",
37
+ "pagination_go_to_page": "ir a la página {page}",
38
+ "pagination_go_to_next_page": "ir a la página siguiente",
39
+ "pagination_info_showing_results": "Mostrando {from} a {to} de {count} resultados",
40
+ "pagination_info_no_results_found": "No se encontraron resultados",
41
+ "table_sort_sort": "ORDENAR",
42
+ "table_sort_clear": "Borrar",
43
+ "table_sort_sort_ascending": "Ordenar ascendente",
44
+ "table_sort_sort_descending": "Ordenar descendente",
45
+ "tabs_pills_aria_label": "Pestañas de píldoras"
46
+ }
47
+ }
@@ -1,4 +1,6 @@
1
+ import deDE from '@squirrel/locales/de-DE.json';
1
2
  import enUS from '@squirrel/locales/en-US.json';
3
+ import esES from '@squirrel/locales/es-ES.json';
2
4
  import frFR from '@squirrel/locales/fr-FR.json';
3
5
  import { SquirrelPlugin } from '@squirrel/plugin/index';
4
6
  import { type App, createApp, nextTick, ref } from 'vue';
@@ -64,12 +66,30 @@ describe('SquirrelPlugin', () => {
64
66
  expect(mockI18n.global.mergeLocaleMessage).toHaveBeenCalledWith('fr-FR', frFR);
65
67
  });
66
68
 
67
- it('should not merge messages for unsupported locales', async () => {
69
+ it('should merge de-DE messages when locale is de-DE', async () => {
70
+ mockI18n = createMockI18n('de-DE', { 'de-DE': {} });
71
+ app.use(SquirrelPlugin, mockI18n);
72
+
73
+ await nextTick();
74
+
75
+ expect(mockI18n.global.mergeLocaleMessage).toHaveBeenCalledWith('de-DE', deDE);
76
+ });
77
+
78
+ it('should merge es-ES messages when locale is es-ES', async () => {
68
79
  mockI18n = createMockI18n('es-ES', { 'es-ES': {} });
69
80
  app.use(SquirrelPlugin, mockI18n);
70
81
 
71
82
  await nextTick();
72
83
 
84
+ expect(mockI18n.global.mergeLocaleMessage).toHaveBeenCalledWith('es-ES', esES);
85
+ });
86
+
87
+ it('should not merge messages for unsupported locales', async () => {
88
+ mockI18n = createMockI18n('pt-BR', { 'pt-BR': {} });
89
+ app.use(SquirrelPlugin, mockI18n);
90
+
91
+ await nextTick();
92
+
73
93
  expect(mockI18n.global.mergeLocaleMessage).not.toHaveBeenCalled();
74
94
  });
75
95
 
@@ -105,9 +125,9 @@ describe('SquirrelPlugin', () => {
105
125
  });
106
126
 
107
127
  it('should stop watching when all Squirrel locales are available', async () => {
108
- // Create mock with both locales available initially
109
- mockI18n = createMockI18n('en-US', { 'en-US': {}, 'fr-FR': {} });
110
- mockI18n.global.availableLocales = ['en-US', 'fr-FR'];
128
+ // Create mock with all locales available initially
129
+ mockI18n = createMockI18n('en-US', { 'de-DE': {}, 'en-US': {}, 'es-ES': {}, 'fr-FR': {} });
130
+ mockI18n.global.availableLocales = ['de-DE', 'en-US', 'es-ES', 'fr-FR'];
111
131
 
112
132
  app.use(SquirrelPlugin, mockI18n);
113
133
 
@@ -1,4 +1,6 @@
1
+ import deDE from '@squirrel/locales/de-DE.json';
1
2
  import enUS from '@squirrel/locales/en-US.json';
3
+ import esES from '@squirrel/locales/es-ES.json';
2
4
  import frFR from '@squirrel/locales/fr-FR.json';
3
5
  import { type App, nextTick, type Plugin, watchEffect } from 'vue';
4
6
 
@@ -12,7 +14,9 @@ type I18nInstance = {
12
14
  };
13
15
 
14
16
  const squirrelMessages = {
17
+ 'de-DE': deDE,
15
18
  'en-US': enUS,
19
+ 'es-ES': esES,
16
20
  'fr-FR': frFR,
17
21
  } as const;
18
22
 
@@ -1,10 +1,12 @@
1
1
  import { getDateFnsLocale } from '@squirrel/utils/dateLocale';
2
- import { enUS, fr } from 'date-fns/locale';
2
+ import { de, enUS, es, fr } from 'date-fns/locale';
3
3
 
4
4
  describe('getDateFnsLocale', () => {
5
5
  it('should return correct locale for valid codes', () => {
6
- expect(getDateFnsLocale('fr-FR')).toBe(fr);
6
+ expect(getDateFnsLocale('de-DE')).toBe(de);
7
7
  expect(getDateFnsLocale('en-US')).toBe(enUS);
8
+ expect(getDateFnsLocale('es-ES')).toBe(es);
9
+ expect(getDateFnsLocale('fr-FR')).toBe(fr);
8
10
  });
9
11
 
10
12
  it('should return enUS as fallback for invalid locale codes', () => {
@@ -14,6 +16,8 @@ describe('getDateFnsLocale', () => {
14
16
  });
15
17
 
16
18
  it('should be case-sensitive', () => {
19
+ expect(getDateFnsLocale('DE-DE')).toBe(enUS);
20
+ expect(getDateFnsLocale('de-de')).toBe(enUS);
17
21
  expect(getDateFnsLocale('FR-FR')).toBe(enUS);
18
22
  expect(getDateFnsLocale('fr-fr')).toBe(enUS);
19
23
  });
@@ -1,16 +1,20 @@
1
1
  import type { Locale } from 'date-fns';
2
+ import { de } from 'date-fns/locale/de';
2
3
  import { enUS } from 'date-fns/locale/en-US';
4
+ import { es } from 'date-fns/locale/es';
3
5
  import { fr } from 'date-fns/locale/fr';
4
6
 
5
7
  /**
6
8
  * Maps vue-i18n locale codes to date-fns Locale objects.
7
9
  *
8
- * @param localeCode - The locale code from vue-i18n (e.g., 'en-US', 'fr-FR')
10
+ * @param localeCode - The locale code from vue-i18n (e.g., 'en-US', 'de-DE', 'es-ES', 'fr-FR')
9
11
  * @returns The corresponding date-fns Locale object
10
12
  */
11
13
  const localeMap = {
12
- 'fr-FR': fr,
14
+ 'de-DE': de,
13
15
  'en-US': enUS,
16
+ 'es-ES': es,
17
+ 'fr-FR': fr,
14
18
  } as const;
15
19
 
16
20
  const isValidLocaleCode = (code: string): code is keyof typeof localeMap => code in localeMap;