@weni/unnnic-system 2.18.1 → 2.19.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@weni/unnnic-system",
3
- "version": "2.18.1",
3
+ "version": "2.19.0",
4
4
  "type": "commonjs",
5
5
  "files": [
6
6
  "dist",
@@ -1,5 +1,3 @@
1
1
  // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2
2
 
3
- exports[`ChartFunnelFourRows.vue > matches snapshot 1`] = `"<div class="chart-funnel-base-row unnnic-chart-funnel-three-rows"></div>"`;
4
-
5
3
  exports[`ChartFunnelThreeRows.vue > matches snapshot 1`] = `"<div class="chart-funnel-base-row unnnic-chart-funnel-three-rows"></div>"`;
@@ -13,10 +13,39 @@
13
13
  <th
14
14
  v-for="(cell, index) of headers"
15
15
  :key="cell.content + index"
16
- class="unnnic-table-next__header-cell"
16
+ :class="{
17
+ 'unnnic-table-next__header-cell': true,
18
+ 'unnnic-table-next__header-cell--clickable': cell.isSortable,
19
+ 'unnnic-table-next__header-cell--sorting':
20
+ sort.header === cell.content && sort.order !== '',
21
+ }"
17
22
  data-testid="header-cell"
23
+ @click.stop="handleClickHeader(cell)"
18
24
  >
19
25
  {{ cell.content }}
26
+ <template v-if="cell.isSortable">
27
+ <IconArrowsDefault
28
+ v-if="sort.header !== cell.content"
29
+ class="order-default-icon"
30
+ data-testid="arrow-default-icon"
31
+ />
32
+ <Icon
33
+ v-else-if="sort.order === 'asc'"
34
+ clickable
35
+ size="ant"
36
+ :icon="'switch_left'"
37
+ style="transform: rotate(-90deg)"
38
+ data-testid="arrow-asc-icon"
39
+ />
40
+ <Icon
41
+ v-else-if="sort.order === 'desc'"
42
+ clickable
43
+ size="ant"
44
+ :icon="'switch_left'"
45
+ style="transform: rotate(90deg)"
46
+ data-testid="arrow-desc-icon"
47
+ />
48
+ </template>
20
49
  </th>
21
50
  </tr>
22
51
  </thead>
@@ -117,6 +146,8 @@ import { validateHeaders, validateRows } from './validation';
117
146
  import TableBodyCell from './TableBodyCell.vue';
118
147
  import TablePagination from './TablePagination.vue';
119
148
  import UnnnicI18n from '../../mixins/i18n';
149
+ import Icon from '../Icon.vue';
150
+ import IconArrowsDefault from '../icons/iconArrowsDefault.vue';
120
151
 
121
152
  export default {
122
153
  name: 'UnnnicTableNext',
@@ -124,6 +155,8 @@ export default {
124
155
  components: {
125
156
  TableBodyCell,
126
157
  TablePagination,
158
+ Icon,
159
+ IconArrowsDefault,
127
160
  },
128
161
 
129
162
  mixins: [UnnnicI18n],
@@ -191,7 +224,7 @@ export default {
191
224
  },
192
225
  },
193
226
 
194
- emits: ['update:pagination', 'row-click'],
227
+ emits: ['update:pagination', 'row-click', 'sort'],
195
228
 
196
229
  data() {
197
230
  return {
@@ -202,6 +235,7 @@ export default {
202
235
  es: 'No hay resultados coincidentes',
203
236
  },
204
237
  },
238
+ sort: { header: '', order: '' },
205
239
  };
206
240
  },
207
241
 
@@ -226,6 +260,28 @@ export default {
226
260
  return this.hideHeaders || !this.headers.length;
227
261
  },
228
262
  },
263
+ methods: {
264
+ handleClickHeader(header) {
265
+ if (!header.isSortable) return;
266
+
267
+ const nextSortOrderMapper = {
268
+ asc: 'desc',
269
+ desc: '',
270
+ '': 'asc',
271
+ };
272
+
273
+ const nextSort =
274
+ header.content !== this.sort.header
275
+ ? 'asc'
276
+ : nextSortOrderMapper[this.sort.order];
277
+
278
+ this.handleSort(nextSort === '' ? '' : header.content, nextSort);
279
+ },
280
+ handleSort(key, order) {
281
+ this.sort = { header: key, order };
282
+ this.$emit('sort', this.sort);
283
+ },
284
+ },
229
285
  };
230
286
  </script>
231
287
 
@@ -260,6 +316,8 @@ $tableBorder: $unnnic-border-width-thinner solid $unnnic-color-neutral-soft;
260
316
 
261
317
  font-weight: $unnnic-font-weight-bold;
262
318
 
319
+ display: flex;
320
+
263
321
  &:first-of-type {
264
322
  border-radius: $unnnic-border-radius-sm 0 0 0;
265
323
  }
@@ -271,6 +329,17 @@ $tableBorder: $unnnic-border-width-thinner solid $unnnic-color-neutral-soft;
271
329
  &:last-of-type {
272
330
  border-radius: 0 $unnnic-border-radius-sm 0 0;
273
331
  }
332
+
333
+ &--sorting {
334
+ background-color: $unnnic-color-neutral-soft;
335
+ }
336
+
337
+ &--clickable {
338
+ &:hover {
339
+ cursor: pointer;
340
+ background-color: $unnnic-color-neutral-soft;
341
+ }
342
+ }
274
343
  }
275
344
  }
276
345
 
@@ -1,5 +1,5 @@
1
1
  import { mount } from '@vue/test-utils';
2
- import { describe, it, expect, beforeEach } from 'vitest';
2
+ import { describe, it, expect, beforeEach, vi } from 'vitest';
3
3
  import TableNext from '../TableNext.vue';
4
4
  import TablePagination from '../TablePagination.vue';
5
5
 
@@ -88,6 +88,51 @@ describe('TableNext.vue', () => {
88
88
  });
89
89
  });
90
90
 
91
+ describe('Header sorting', () => {
92
+ it('render sort icon', async () => {
93
+ wrapper = mount(TableNext, {
94
+ props: {
95
+ headers: [
96
+ { content: 'ID', size: 1, isSortable: true },
97
+ { content: 'Name', size: 'auto' },
98
+ { content: 'Age', size: 2 },
99
+ ],
100
+ rows,
101
+ },
102
+ });
103
+ expect(wrapper.find('[data-testid="arrow-default-icon"]').exists()).toBe(
104
+ true,
105
+ );
106
+ });
107
+ it('change sorting and emit sort event', async () => {
108
+ wrapper = mount(TableNext, {
109
+ props: {
110
+ headers: [
111
+ { content: 'ID', size: 1, isSortable: true },
112
+ { content: 'Name', size: 'auto' },
113
+ { content: 'Age', size: 2 },
114
+ ],
115
+ rows,
116
+ },
117
+ });
118
+ const handlerSortSpy = vi.spyOn(wrapper.vm, 'handleSort');
119
+
120
+ const [headerCell, unsortableHeaderCell] = wrapper.findAll(
121
+ '[data-testid="header-cell"]',
122
+ );
123
+
124
+ await unsortableHeaderCell.trigger('click');
125
+ expect(handlerSortSpy).not.toHaveBeenCalled();
126
+
127
+ await headerCell.trigger('click');
128
+ expect(handlerSortSpy).toHaveBeenCalledWith('ID', 'asc');
129
+ await headerCell.trigger('click');
130
+ expect(handlerSortSpy).toHaveBeenCalledWith('ID', 'desc');
131
+ await headerCell.trigger('click');
132
+ expect(handlerSortSpy).toHaveBeenCalledWith('', '');
133
+ });
134
+ });
135
+
91
136
  describe('Row rendering and behaviors', () => {
92
137
  it('renders rows correctly', () => {
93
138
  const bodyRows = wrapper.findAll('[data-testid="body-row"]');
@@ -4,8 +4,12 @@ exports[`TableNext.vue > matches the snapshot 1`] = `
4
4
  "<table data-v-2143c794="" class="unnnic-table-next">
5
5
  <thead data-v-2143c794="" class="unnnic-table-next__header" data-testid="header">
6
6
  <tr data-v-2143c794="" class="unnnic-table-next__header-row" data-testid="header-row" style="grid-template-columns: 1fr 2fr;">
7
- <th data-v-2143c794="" class="unnnic-table-next__header-cell" data-testid="header-cell">Header 1</th>
8
- <th data-v-2143c794="" class="unnnic-table-next__header-cell" data-testid="header-cell">Header 2</th>
7
+ <th data-v-2143c794="" class="unnnic-table-next__header-cell" data-testid="header-cell">Header 1
8
+ <!--v-if-->
9
+ </th>
10
+ <th data-v-2143c794="" class="unnnic-table-next__header-cell" data-testid="header-cell">Header 2
11
+ <!--v-if-->
12
+ </th>
9
13
  </tr>
10
14
  </thead>
11
15
  <tbody data-v-2143c794="" class="unnnic-table-next__body" data-testid="body">
@@ -0,0 +1,16 @@
1
+ <template>
2
+ <svg
3
+ xmlns="http://www.w3.org/2000/svg"
4
+ width="20"
5
+ height="20"
6
+ viewBox="0 0 20 20"
7
+ fill="none"
8
+ >
9
+ <path
10
+ fill-rule="evenodd"
11
+ clip-rule="evenodd"
12
+ d="M6.52329 7.62271C6.49402 7.70173 6.47939 7.77475 6.47939 7.84175C6.47939 8.01402 6.53572 8.16813 6.64838 8.30407C6.7609 8.44001 6.92389 8.50798 7.13734 8.50798L12.8628 8.50798C13.0762 8.50798 13.2392 8.44001 13.3517 8.30407C13.4644 8.16813 13.5207 8.01402 13.5207 7.84175C13.5207 7.77475 13.5061 7.70173 13.4768 7.62271C13.4477 7.54355 13.4012 7.46936 13.3374 7.40015L10.5064 4.20619C10.4407 4.13255 10.3648 4.07978 10.2788 4.04787C10.1928 4.01596 10.0999 4 10 4C9.90023 4 9.80731 4.01596 9.72129 4.04787C9.63528 4.07978 9.55941 4.13255 9.49369 4.20619L6.66267 7.40015C6.59888 7.46936 6.55242 7.54355 6.52329 7.62271ZM13.4767 12.3773C13.506 12.2983 13.5206 12.2253 13.5206 12.1583C13.5206 11.986 13.4643 11.8319 13.3516 11.6959C13.2391 11.56 13.0761 11.492 12.8627 11.492H7.13726C6.92381 11.492 6.76082 11.56 6.6483 11.6959C6.53564 11.8319 6.47931 11.986 6.47931 12.1583C6.47931 12.2253 6.49394 12.2983 6.52321 12.3773C6.55235 12.4565 6.5988 12.5306 6.66259 12.5998L9.49362 15.7938C9.55933 15.8674 9.6352 15.9202 9.72122 15.9521C9.80723 15.984 9.90015 16 9.99997 16C10.0998 16 10.1927 15.984 10.2787 15.9521C10.3647 15.9202 10.4406 15.8674 10.5063 15.7938L13.3373 12.5999C13.4011 12.5306 13.4476 12.4565 13.4767 12.3773Z"
13
+ fill="#D0D3D9"
14
+ />
15
+ </svg>
16
+ </template>
@@ -51,7 +51,12 @@ export default {
51
51
  UnnnicTableNext,
52
52
  },
53
53
  setup() {
54
- return { args };
54
+ const sort = ({ order }) => {
55
+ if (order === 'asc') return;
56
+ if (order === 'desc') args.rows === args.rows.reverse();
57
+ else args.rows === args.rows.reverse();
58
+ };
59
+ return { args, sort };
55
60
  },
56
61
  data() {
57
62
  return {
@@ -61,6 +66,7 @@ export default {
61
66
  {
62
67
  content: 'ID',
63
68
  size: 'auto',
69
+ isSortable: true,
64
70
  },
65
71
  {
66
72
  content: 'Name',
@@ -88,6 +94,7 @@ export default {
88
94
  v-model:pagination="pagination"
89
95
  :paginationTotal="125"
90
96
  :paginationInterval="5"
97
+ @sort="sort"
91
98
  />
92
99
  `,
93
100
  }),