@webitel/ui-sdk 2.0.32-2 → 2.1.0-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.
- package/dist/ui-sdk.common.js +267 -250
- package/dist/ui-sdk.common.js.map +1 -1
- package/dist/ui-sdk.css +1 -1
- package/dist/ui-sdk.umd.js +267 -250
- package/dist/ui-sdk.umd.js.map +1 -1
- package/dist/ui-sdk.umd.min.js +4 -4
- package/dist/ui-sdk.umd.min.js.map +1 -1
- package/package.json +1 -2
- package/src/components/molecules/wt-label/mixins/labelUsageMixin.js +21 -0
- package/src/components/molecules/wt-select/__tests__/WtSelect.spec.js +0 -26
- package/src/components/molecules/wt-select/mixins/__tests__/multiselectMixin.spec.js +32 -0
- package/src/components/molecules/wt-select/mixins/multiselectMixin.js +147 -0
- package/src/components/molecules/wt-select/wt-select.vue +106 -392
- package/src/components/molecules/wt-tags-input/__tests__/WtTagsInput.spec.js +15 -18
- package/src/components/molecules/wt-tags-input/wt-tags-input.vue +108 -211
- package/src/css/components/atoms/wt-chip/_variables.scss +1 -1
- package/src/css/components/molecules/wt-select/_multiselect.scss +194 -0
- package/src/css/components/molecules/wt-select/_variables.scss +3 -50
- package/src/locale/en/en.js +0 -1
- package/src/locale/ru/ru.js +0 -1
- package/src/locale/ua/ua.js +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webitel/ui-sdk",
|
|
3
|
-
"version": "2.0
|
|
3
|
+
"version": "2.1.0-1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"scripts": {
|
|
6
6
|
"serve-lib": "vue-cli-service serve",
|
|
@@ -25,7 +25,6 @@
|
|
|
25
25
|
"src/store/*"
|
|
26
26
|
],
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@johmun/vue-tags-input": "^2.1.0",
|
|
29
28
|
"core-js": "^3.6.5",
|
|
30
29
|
"csv-stringify": "^5.5.3",
|
|
31
30
|
"deep-copy": "^1.4.2",
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export default {
|
|
2
|
+
props: {
|
|
3
|
+
label: {
|
|
4
|
+
type: String,
|
|
5
|
+
default: '',
|
|
6
|
+
},
|
|
7
|
+
labelProps: {
|
|
8
|
+
type: Object,
|
|
9
|
+
description: 'Object with props, passed down to wt-label as props',
|
|
10
|
+
},
|
|
11
|
+
},
|
|
12
|
+
computed: {
|
|
13
|
+
hasLabel() {
|
|
14
|
+
return !!(this.label || this.$slots.label);
|
|
15
|
+
},
|
|
16
|
+
|
|
17
|
+
requiredLabel() {
|
|
18
|
+
return this.required ? `${this.label}*` : this.label;
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
};
|
|
@@ -24,30 +24,4 @@ describe('WtSelect', () => {
|
|
|
24
24
|
});
|
|
25
25
|
expect(wrapper.find('.wt-label').text()).toBe(label);
|
|
26
26
|
});
|
|
27
|
-
|
|
28
|
-
it('Correctly computes internal search options', () => {
|
|
29
|
-
const options = ['1', '2'];
|
|
30
|
-
const wrapper = shallowMount(WtSelect, {
|
|
31
|
-
stubs: {
|
|
32
|
-
WtLabel,
|
|
33
|
-
WtIcon: true,
|
|
34
|
-
},
|
|
35
|
-
propsData: { options },
|
|
36
|
-
});
|
|
37
|
-
expect(wrapper.vm.selectOptions).toBe(options);
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
it('Correctly computes internal search options', async () => {
|
|
41
|
-
const options = ['1', '2'];
|
|
42
|
-
const searchOptions = ['1'];
|
|
43
|
-
const wrapper = shallowMount(WtSelect, {
|
|
44
|
-
stubs: {
|
|
45
|
-
WtLabel,
|
|
46
|
-
WtIcon: true,
|
|
47
|
-
},
|
|
48
|
-
propsData: { options, searchMethod: () => ({ items: searchOptions }) },
|
|
49
|
-
});
|
|
50
|
-
await wrapper.vm.$nextTick();
|
|
51
|
-
expect(wrapper.vm.selectOptions).toBe(searchOptions);
|
|
52
|
-
});
|
|
53
27
|
});
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { shallowMount } from '@vue/test-utils';
|
|
2
|
+
import MultiselectMixin from '../multiselectMixin';
|
|
3
|
+
|
|
4
|
+
const Component = {
|
|
5
|
+
mixins: [MultiselectMixin],
|
|
6
|
+
render() {},
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
describe('MultiselectMixin', () => {
|
|
10
|
+
it('renders a component', () => {
|
|
11
|
+
const wrapper = shallowMount(Component);
|
|
12
|
+
expect(wrapper.exists()).toBe(true);
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it('Correctly computes internal search options', () => {
|
|
16
|
+
const options = ['1', '2'];
|
|
17
|
+
const wrapper = shallowMount(Component, {
|
|
18
|
+
propsData: { options },
|
|
19
|
+
});
|
|
20
|
+
expect(wrapper.vm.selectOptions).toBe(options);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('Correctly computes internal search options: search case', async () => {
|
|
24
|
+
const options = ['1', '2'];
|
|
25
|
+
const searchOptions = ['1'];
|
|
26
|
+
const wrapper = shallowMount(Component, {
|
|
27
|
+
propsData: { options, searchMethod: () => ({ items: searchOptions }) },
|
|
28
|
+
});
|
|
29
|
+
await wrapper.vm.$nextTick();
|
|
30
|
+
expect(wrapper.vm.selectOptions).toBe(searchOptions);
|
|
31
|
+
});
|
|
32
|
+
});
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import VueMultiselect from 'vue-multiselect';
|
|
2
|
+
import { ObserveVisibility } from 'vue-observe-visibility';
|
|
3
|
+
import validationMixin
|
|
4
|
+
from '../../../../mixins/validationMixin/validationMixin';
|
|
5
|
+
import debounce from '../../../../scripts/debounce';
|
|
6
|
+
import isEmpty from '../../../../scripts/isEmpty';
|
|
7
|
+
import labelUsageMixin from '../../wt-label/mixins/labelUsageMixin';
|
|
8
|
+
|
|
9
|
+
export default {
|
|
10
|
+
mixins: [validationMixin, labelUsageMixin],
|
|
11
|
+
directives: { ObserveVisibility },
|
|
12
|
+
components: {
|
|
13
|
+
VueMultiselect,
|
|
14
|
+
},
|
|
15
|
+
props: {
|
|
16
|
+
options: {
|
|
17
|
+
type: Array,
|
|
18
|
+
default: () => [],
|
|
19
|
+
},
|
|
20
|
+
|
|
21
|
+
placeholder: {
|
|
22
|
+
type: String,
|
|
23
|
+
default: '',
|
|
24
|
+
},
|
|
25
|
+
|
|
26
|
+
trackBy: {
|
|
27
|
+
type: String,
|
|
28
|
+
default: 'id',
|
|
29
|
+
},
|
|
30
|
+
|
|
31
|
+
optionLabel: {
|
|
32
|
+
type: String,
|
|
33
|
+
},
|
|
34
|
+
|
|
35
|
+
searchMethod: {
|
|
36
|
+
type: Function,
|
|
37
|
+
},
|
|
38
|
+
|
|
39
|
+
disabled: {
|
|
40
|
+
type: Boolean,
|
|
41
|
+
default: false,
|
|
42
|
+
},
|
|
43
|
+
|
|
44
|
+
required: {
|
|
45
|
+
type: Boolean,
|
|
46
|
+
default: false,
|
|
47
|
+
},
|
|
48
|
+
|
|
49
|
+
allowEmpty: {
|
|
50
|
+
type: Boolean,
|
|
51
|
+
default: false,
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
data: () => ({
|
|
55
|
+
apiOptions: [],
|
|
56
|
+
isLoading: false,
|
|
57
|
+
|
|
58
|
+
searchParams: {
|
|
59
|
+
search: '',
|
|
60
|
+
page: 1,
|
|
61
|
+
},
|
|
62
|
+
searchHasNext: true,
|
|
63
|
+
}),
|
|
64
|
+
computed: {
|
|
65
|
+
isApiMode() {
|
|
66
|
+
return !!this.searchMethod;
|
|
67
|
+
},
|
|
68
|
+
showIntersectionObserver() {
|
|
69
|
+
return this.isApiMode && !this.isLoading && this.apiOptions.length;
|
|
70
|
+
},
|
|
71
|
+
selectValue() {
|
|
72
|
+
// vue-multiselect doesn't show placeholder if value is empty object
|
|
73
|
+
return this.isValue ? this.value : '';
|
|
74
|
+
},
|
|
75
|
+
|
|
76
|
+
selectOptions() {
|
|
77
|
+
return this.isApiMode ? this.apiOptions : this.options;
|
|
78
|
+
},
|
|
79
|
+
|
|
80
|
+
isValue() {
|
|
81
|
+
return !isEmpty(this.value);
|
|
82
|
+
},
|
|
83
|
+
listeners() {
|
|
84
|
+
return {
|
|
85
|
+
...this.$listeners,
|
|
86
|
+
input: this.input,
|
|
87
|
+
close: this.close,
|
|
88
|
+
tag: this.tag,
|
|
89
|
+
'search-change': (search) => {
|
|
90
|
+
this.$emit('search-change', search);
|
|
91
|
+
this.handleSearchChange(search);
|
|
92
|
+
},
|
|
93
|
+
};
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
|
|
97
|
+
methods: {
|
|
98
|
+
getOptionLabel({ option, optionLabel }) {
|
|
99
|
+
const defaultOptionLabel = 'name';
|
|
100
|
+
|
|
101
|
+
if (optionLabel) return option[optionLabel];
|
|
102
|
+
if (option.locale) {
|
|
103
|
+
if (Array.isArray(option.locale)) return this.$tc(...option.locale);
|
|
104
|
+
return this.$t(option.locale);
|
|
105
|
+
}
|
|
106
|
+
return option[defaultOptionLabel] || option;
|
|
107
|
+
},
|
|
108
|
+
handleSearchChange(search) {
|
|
109
|
+
if (!this.isApiMode) return;
|
|
110
|
+
this.isLoading = true;
|
|
111
|
+
this.searchParams.page = 1;
|
|
112
|
+
this.searchParams.search = search;
|
|
113
|
+
this.fetchOptions();
|
|
114
|
+
},
|
|
115
|
+
|
|
116
|
+
handleAfterListIntersect(isVisible) {
|
|
117
|
+
if (isVisible && this.searchHasNext) {
|
|
118
|
+
this.isLoading = true;
|
|
119
|
+
this.searchParams.page += 1;
|
|
120
|
+
this.fetchOptions();
|
|
121
|
+
}
|
|
122
|
+
},
|
|
123
|
+
|
|
124
|
+
async fetchOptions({ search, page } = this.searchParams) {
|
|
125
|
+
if (!this.isApiMode) return;
|
|
126
|
+
const { items, next } = await this.searchMethod({ search, page });
|
|
127
|
+
this.apiOptions = this.searchParams.page === 1 ? items : this.apiOptions.concat(items);
|
|
128
|
+
this.searchHasNext = next;
|
|
129
|
+
this.isLoading = false;
|
|
130
|
+
},
|
|
131
|
+
|
|
132
|
+
input(value) {
|
|
133
|
+
this.$emit('input', value);
|
|
134
|
+
},
|
|
135
|
+
|
|
136
|
+
close() {
|
|
137
|
+
this.$emit('closed');
|
|
138
|
+
},
|
|
139
|
+
|
|
140
|
+
tag() {},
|
|
141
|
+
},
|
|
142
|
+
|
|
143
|
+
created() {
|
|
144
|
+
this.fetchOptions();
|
|
145
|
+
this.fetchOptions = debounce(this.fetchOptions, 500);
|
|
146
|
+
},
|
|
147
|
+
};
|