@weni/unnnic-system 3.2.9-alpha.6 → 3.2.9-alpha.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/CHANGELOG.md +10 -0
- package/dist/components/DataTable/index.vue.d.ts +7 -0
- package/dist/components/DataTable/index.vue.d.ts.map +1 -1
- package/dist/{es-1bd57600.js → es-d6005598.js} +1 -1
- package/dist/{index-4cd729ce.js → index-35640dc9.js} +1457 -1456
- package/dist/{pt-br-59f06c05.js → pt-br-89c2c09d.js} +1 -1
- package/dist/style.css +1 -1
- package/dist/unnnic.js +1 -1
- package/dist/unnnic.umd.cjs +32 -32
- package/package.json +1 -1
- package/src/components/Alert/__tests__/__snapshots__/Alert.spec.js.snap +11 -7
- package/src/components/Alert/__tests__/__snapshots__/Version1dot1.spec.js.snap +2 -2
- package/src/components/DataTable/index.vue +25 -10
- package/src/components/Select/SelectOption.vue +8 -0
- package/src/components/Select/__tests__/Select.spec.js +2 -2
- package/src/components/Select/index.vue +61 -5
- package/src/stories/DataTable.stories.js +60 -0
package/package.json
CHANGED
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
|
2
2
|
|
|
3
3
|
exports[`UnnnicAlert.vue > matches the snapshot 1`] = `
|
|
4
|
-
"<
|
|
5
|
-
<
|
|
6
|
-
<
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
4
|
+
"<transition-stub data-v-c3231c18="" name="toast-slide" appear="true" persisted="false" css="true" icon="alert-icon" position="top-right" closetext="Close">
|
|
5
|
+
<aside data-v-c3231c18="" class="unnnic-toast unnnic-toast--informational" role="status" aria-live="polite" data-testid="unnnic-toast">
|
|
6
|
+
<section data-v-c3231c18="" class="unnnic-toast__content">
|
|
7
|
+
<header data-v-c3231c18="" class="unnnic-toast__header"><span data-v-26446d8e="" data-v-c3231c18="" class="material-symbols-rounded unnnic-icon-scheme--blue-500 unnnic-icon-size--ant" data-testid="toast-type-icon" translate="no">info</span>
|
|
8
|
+
<h3 data-v-c3231c18="" class="unnnic-toast__title">Test Alert</h3><span data-v-26446d8e="" data-v-c3231c18="" class="material-symbols-rounded unnnic-icon-scheme--neutral-dark unnnic-icon-size--ant unnnic--clickable unnnic-toast__close" data-testid="toast-close-button" translate="no">close</span>
|
|
9
|
+
</header>
|
|
10
|
+
<!--v-if-->
|
|
11
|
+
</section>
|
|
12
|
+
<!--v-if-->
|
|
13
|
+
</aside>
|
|
14
|
+
</transition-stub>"
|
|
11
15
|
`;
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
|
2
2
|
|
|
3
3
|
exports[`Alert.vue > matches snapshot 1`] = `
|
|
4
|
-
"<div data-v-fb94f284="" class="alert-container">
|
|
4
|
+
"<div data-v-fb94f284="" class="alert-container" version="1.0" linkhref="https://example.com" linktarget="_blank" linktext="Learn more">
|
|
5
5
|
<div data-v-fb94f284="" class="alert alert--scheme-aux-green">
|
|
6
6
|
<div data-v-fb94f284="" class="alert__progress"></div>
|
|
7
|
-
<div data-v-fb94f284="" class="alert__text">Test Alert</div
|
|
7
|
+
<div data-v-fb94f284="" class="alert__text">Test Alert</div>
|
|
8
8
|
<div data-v-fb94f284="" class="alert__close">
|
|
9
9
|
<unnnic-icon-stub data-v-fb94f284="" filled="false" next="false" icon="close-1" clickable="false" size="sm" scheme="neutral-white"></unnnic-icon-stub>
|
|
10
10
|
</div>
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
{
|
|
17
17
|
'unnnic-data-table__header-cell--clickable': header.isSortable,
|
|
18
18
|
'unnnic-data-table__header-cell--sorting':
|
|
19
|
-
|
|
19
|
+
sortState.header === header.title && sortState.order !== '',
|
|
20
20
|
},
|
|
21
21
|
]"
|
|
22
22
|
@click.stop="handleClickHeader(header)"
|
|
@@ -31,12 +31,12 @@
|
|
|
31
31
|
</template>
|
|
32
32
|
<template v-if="header.isSortable">
|
|
33
33
|
<IconArrowsDefault
|
|
34
|
-
v-if="
|
|
34
|
+
v-if="sortState.header !== header.title"
|
|
35
35
|
class="order-default-icon"
|
|
36
36
|
data-testid="arrow-default-icon"
|
|
37
37
|
/>
|
|
38
38
|
<Icon
|
|
39
|
-
v-else-if="
|
|
39
|
+
v-else-if="sortState.order === 'asc'"
|
|
40
40
|
clickable
|
|
41
41
|
size="ant"
|
|
42
42
|
:icon="'switch_left'"
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
data-testid="arrow-asc-icon"
|
|
45
45
|
/>
|
|
46
46
|
<Icon
|
|
47
|
-
v-else-if="
|
|
47
|
+
v-else-if="sortState.order === 'desc'"
|
|
48
48
|
clickable
|
|
49
49
|
size="ant"
|
|
50
50
|
:icon="'switch_left'"
|
|
@@ -183,6 +183,12 @@ type DataTableItem = {
|
|
|
183
183
|
[key: string]: any;
|
|
184
184
|
};
|
|
185
185
|
|
|
186
|
+
type SortState = {
|
|
187
|
+
header: string;
|
|
188
|
+
itemKey: string;
|
|
189
|
+
order: string;
|
|
190
|
+
};
|
|
191
|
+
|
|
186
192
|
interface Props {
|
|
187
193
|
headers: DataTableHeader[];
|
|
188
194
|
items: DataTableItem[];
|
|
@@ -198,6 +204,7 @@ interface Props {
|
|
|
198
204
|
pageTotal?: number;
|
|
199
205
|
pageInterval?: number;
|
|
200
206
|
locale?: string;
|
|
207
|
+
sort?: SortState;
|
|
201
208
|
}
|
|
202
209
|
|
|
203
210
|
defineOptions({
|
|
@@ -225,6 +232,7 @@ const props = withDefaults(defineProps<Props>(), {
|
|
|
225
232
|
pageTotal: 0,
|
|
226
233
|
pageInterval: 5,
|
|
227
234
|
locale: 'en',
|
|
235
|
+
sort: undefined,
|
|
228
236
|
});
|
|
229
237
|
|
|
230
238
|
const defaultTranslations = {
|
|
@@ -251,12 +259,16 @@ const headersItemsKeys: ComputedRef<string[]> = computed(() => {
|
|
|
251
259
|
return props.headers.map((header) => header.itemKey);
|
|
252
260
|
});
|
|
253
261
|
|
|
254
|
-
const
|
|
262
|
+
const internalSort = ref({
|
|
255
263
|
header: '',
|
|
256
264
|
itemKey: '',
|
|
257
265
|
order: '',
|
|
258
266
|
});
|
|
259
267
|
|
|
268
|
+
const sortState = computed(() => {
|
|
269
|
+
return props.sort !== undefined ? props.sort : internalSort.value;
|
|
270
|
+
});
|
|
271
|
+
|
|
260
272
|
const getHeaderColumnSize = (header: DataTableHeader): string => {
|
|
261
273
|
return typeof header.size === 'number'
|
|
262
274
|
? `${header.size || 1}fr`
|
|
@@ -271,9 +283,12 @@ const gridTemplateColumns: ComputedRef<string> = computed(() => {
|
|
|
271
283
|
return columnSizes.join(' ');
|
|
272
284
|
});
|
|
273
285
|
|
|
274
|
-
const handleSort = (header:
|
|
275
|
-
sort
|
|
276
|
-
|
|
286
|
+
const handleSort = (header: SortState, order: string) => {
|
|
287
|
+
if (props.sort === undefined) {
|
|
288
|
+
internalSort.value = { ...header, order };
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
emit('update:sort', { ...header, order });
|
|
277
292
|
};
|
|
278
293
|
|
|
279
294
|
const handleClickHeader = (header: DataTableHeader) => {
|
|
@@ -286,9 +301,9 @@ const handleClickHeader = (header: DataTableHeader) => {
|
|
|
286
301
|
};
|
|
287
302
|
|
|
288
303
|
const nextSort =
|
|
289
|
-
header.title !==
|
|
304
|
+
header.title !== sortState.value.header
|
|
290
305
|
? 'asc'
|
|
291
|
-
: nextSortOrderMapper[
|
|
306
|
+
: nextSortOrderMapper[sortState.value.order];
|
|
292
307
|
|
|
293
308
|
handleSort(
|
|
294
309
|
nextSort === ''
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
{
|
|
6
6
|
'unnnic-select-option--disabled': props.disabled,
|
|
7
7
|
'unnnic-select-option--active': props.active,
|
|
8
|
+
'unnnic-select-option--focused': props.focused,
|
|
8
9
|
},
|
|
9
10
|
]"
|
|
10
11
|
>
|
|
@@ -21,11 +22,13 @@ interface SelectOptionProps {
|
|
|
21
22
|
label: string;
|
|
22
23
|
disabled?: boolean;
|
|
23
24
|
active?: boolean;
|
|
25
|
+
focused?: boolean;
|
|
24
26
|
}
|
|
25
27
|
|
|
26
28
|
const props = withDefaults(defineProps<SelectOptionProps>(), {
|
|
27
29
|
disabled: false,
|
|
28
30
|
active: false,
|
|
31
|
+
focused: false,
|
|
29
32
|
});
|
|
30
33
|
</script>
|
|
31
34
|
|
|
@@ -43,6 +46,11 @@ const props = withDefaults(defineProps<SelectOptionProps>(), {
|
|
|
43
46
|
padding: $unnnic-space-2 $unnnic-space-4;
|
|
44
47
|
font: $unnnic-font-emphasis;
|
|
45
48
|
|
|
49
|
+
&:hover:not(&--active):not(&--disabled),
|
|
50
|
+
&--focused {
|
|
51
|
+
background-color: $unnnic-color-bg-soft;
|
|
52
|
+
}
|
|
53
|
+
|
|
46
54
|
&--active {
|
|
47
55
|
background-color: $unnnic-color-bg-active;
|
|
48
56
|
color: $unnnic-color-fg-inverted;
|
|
@@ -237,13 +237,13 @@ describe('UnnnicSelect.vue', () => {
|
|
|
237
237
|
describe('computed properties', () => {
|
|
238
238
|
test('calculatedMaxHeight returns correct value', () => {
|
|
239
239
|
const maxHeight = wrapper.vm.calculatedMaxHeight;
|
|
240
|
-
expect(maxHeight).toBe('
|
|
240
|
+
expect(maxHeight).toBe('235px');
|
|
241
241
|
});
|
|
242
242
|
|
|
243
243
|
test('calculatedMaxHeight includes search height when enabled', async () => {
|
|
244
244
|
await wrapper.setProps({ enableSearch: true });
|
|
245
245
|
const maxHeight = wrapper.vm.calculatedMaxHeight;
|
|
246
|
-
expect(maxHeight).toBe('
|
|
246
|
+
expect(maxHeight).toBe('289px');
|
|
247
247
|
});
|
|
248
248
|
|
|
249
249
|
test('calculatedMaxHeight returns unset when no options', async () => {
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div
|
|
2
|
+
<div
|
|
3
|
+
class="unnnic-select"
|
|
4
|
+
@keydown="handleKeyDown"
|
|
5
|
+
>
|
|
3
6
|
<UnnnicPopover
|
|
4
7
|
v-model="openPopover"
|
|
5
8
|
:popoverBalloonProps="{ maxHeight: calculatedMaxHeight }"
|
|
@@ -30,12 +33,14 @@
|
|
|
30
33
|
@update:modelValue="handleSearch"
|
|
31
34
|
/>
|
|
32
35
|
<UnnnicSelectOption
|
|
33
|
-
v-for="option in filteredOptions"
|
|
36
|
+
v-for="(option, index) in filteredOptions"
|
|
34
37
|
:key="option[props.itemValue]"
|
|
38
|
+
:data-option-index="index"
|
|
35
39
|
:label="option[props.itemLabel]"
|
|
36
40
|
:active="
|
|
37
41
|
option[props.itemValue] === selectedItem?.[props.itemValue]
|
|
38
42
|
"
|
|
43
|
+
:focused="focusedOptionIndex === index"
|
|
39
44
|
:disabled="option.disabled"
|
|
40
45
|
@click="handleSelectOption(option)"
|
|
41
46
|
/>
|
|
@@ -46,7 +51,7 @@
|
|
|
46
51
|
</template>
|
|
47
52
|
|
|
48
53
|
<script setup lang="ts">
|
|
49
|
-
import { computed, ref, watch } from 'vue';
|
|
54
|
+
import { computed, ref, watch, nextTick } from 'vue';
|
|
50
55
|
import UnnnicInput from '../Input/Input.vue';
|
|
51
56
|
import UnnnicPopover from '../Popover/index.vue';
|
|
52
57
|
import UnnnicSelectOption from './SelectOption.vue';
|
|
@@ -102,13 +107,64 @@ const openPopover = ref(false);
|
|
|
102
107
|
watch(openPopover, () => {
|
|
103
108
|
if (!openPopover.value) {
|
|
104
109
|
handleSearch('');
|
|
110
|
+
} else {
|
|
111
|
+
focusedOptionIndex.value = -1;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (openPopover.value && props.modelValue) {
|
|
115
|
+
const selectedOptionIndex = props.options.findIndex(
|
|
116
|
+
(option) =>
|
|
117
|
+
option[props.itemValue] === selectedItem.value[props.itemValue],
|
|
118
|
+
);
|
|
119
|
+
scrollToOption(selectedOptionIndex, 'instant', 'center');
|
|
105
120
|
}
|
|
106
121
|
});
|
|
107
122
|
|
|
123
|
+
const handleKeyDown = (event) => {
|
|
124
|
+
const { key } = event;
|
|
125
|
+
const validKeys = ['ArrowUp', 'ArrowDown', 'Enter'];
|
|
126
|
+
if (validKeys.includes(key)) {
|
|
127
|
+
event.preventDefault();
|
|
128
|
+
if (key === 'ArrowUp') {
|
|
129
|
+
if (focusedOptionIndex.value === 0) return;
|
|
130
|
+
focusedOptionIndex.value--;
|
|
131
|
+
scrollToOption(focusedOptionIndex.value);
|
|
132
|
+
}
|
|
133
|
+
if (key === 'ArrowDown') {
|
|
134
|
+
if (focusedOptionIndex.value === filteredOptions.value.length - 1) return;
|
|
135
|
+
focusedOptionIndex.value++;
|
|
136
|
+
scrollToOption(focusedOptionIndex.value);
|
|
137
|
+
}
|
|
138
|
+
if (key === 'Enter') {
|
|
139
|
+
handleSelectOption(filteredOptions.value[focusedOptionIndex.value]);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
const focusedOptionIndex = ref<number>(-1);
|
|
145
|
+
|
|
146
|
+
const scrollToOption = (
|
|
147
|
+
index: number,
|
|
148
|
+
behavior: 'smooth' | 'instant' = 'smooth',
|
|
149
|
+
block: 'center' | 'start' | 'end' | 'nearest' = 'center',
|
|
150
|
+
) => {
|
|
151
|
+
nextTick(() => {
|
|
152
|
+
const option = document.querySelector(`[data-option-index="${index}"]`);
|
|
153
|
+
if (option) {
|
|
154
|
+
option.scrollIntoView({ behavior, block });
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
};
|
|
158
|
+
|
|
108
159
|
const calculatedMaxHeight = computed(() => {
|
|
109
160
|
if (!props.options || props.options.length === 0) return 'unset';
|
|
110
|
-
const
|
|
111
|
-
|
|
161
|
+
const popoverPadding = 32;
|
|
162
|
+
const popoverGap = 4;
|
|
163
|
+
// 37 = 21px (height) + 16px (padding)
|
|
164
|
+
const fieldsHeight = 37 * props.optionsLines;
|
|
165
|
+
const size =
|
|
166
|
+
fieldsHeight + popoverPadding + (popoverGap * props.optionsLines - 2);
|
|
167
|
+
return `${props.enableSearch ? size + 54 : size}px`;
|
|
112
168
|
});
|
|
113
169
|
|
|
114
170
|
const selectedItem = computed(() => {
|
|
@@ -330,3 +330,63 @@ export const Loading = {
|
|
|
330
330
|
isLoading: true,
|
|
331
331
|
},
|
|
332
332
|
};
|
|
333
|
+
|
|
334
|
+
export const ControlledSort = {
|
|
335
|
+
args: { headers, items },
|
|
336
|
+
render: (args) => ({
|
|
337
|
+
components: {
|
|
338
|
+
UnnnicDataTable,
|
|
339
|
+
},
|
|
340
|
+
setup() {
|
|
341
|
+
let sortState = {
|
|
342
|
+
header: 'ID',
|
|
343
|
+
itemKey: 'id',
|
|
344
|
+
order: 'asc',
|
|
345
|
+
};
|
|
346
|
+
|
|
347
|
+
const handleSort = ({ order, header, itemKey }) => {
|
|
348
|
+
action('update:sort')({ order, header, itemKey });
|
|
349
|
+
sortState = { header, itemKey, order };
|
|
350
|
+
|
|
351
|
+
if (order === 'asc') {
|
|
352
|
+
args.items = [...args.items].sort((a, b) => {
|
|
353
|
+
if (itemKey === 'id') return a.id - b.id;
|
|
354
|
+
return a[itemKey] > b[itemKey] ? 1 : -1;
|
|
355
|
+
});
|
|
356
|
+
} else if (order === 'desc') {
|
|
357
|
+
args.items = [...args.items].sort((a, b) => {
|
|
358
|
+
if (itemKey === 'id') return b.id - a.id;
|
|
359
|
+
return a[itemKey] < b[itemKey] ? 1 : -1;
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
};
|
|
363
|
+
|
|
364
|
+
const updatePage = (page) => {
|
|
365
|
+
action('update:page')(page);
|
|
366
|
+
args.page = page;
|
|
367
|
+
};
|
|
368
|
+
|
|
369
|
+
const itemClick = (item) => {
|
|
370
|
+
action('itemClick')(item);
|
|
371
|
+
};
|
|
372
|
+
|
|
373
|
+
return { args, sortState, handleSort, updatePage, itemClick };
|
|
374
|
+
},
|
|
375
|
+
template: `
|
|
376
|
+
<div>
|
|
377
|
+
<UnnnicDataTable
|
|
378
|
+
v-bind="args"
|
|
379
|
+
:headers="args.headers"
|
|
380
|
+
:items="args.items"
|
|
381
|
+
:pageTotal="125"
|
|
382
|
+
:pageInterval="5"
|
|
383
|
+
v-model:sort="sortState"
|
|
384
|
+
@update:sort="handleSort"
|
|
385
|
+
@update:page="updatePage"
|
|
386
|
+
@itemClick="itemClick"
|
|
387
|
+
>
|
|
388
|
+
</UnnnicDataTable>
|
|
389
|
+
</div>
|
|
390
|
+
`,
|
|
391
|
+
}),
|
|
392
|
+
};
|