@webitel/ui-sdk 2.0.1-0 → 2.0.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webitel/ui-sdk",
3
- "version": "2.0.1-0",
3
+ "version": "2.0.3-1",
4
4
  "private": false,
5
5
  "scripts": {
6
6
  "serve-lib": "vue-cli-service serve",
@@ -1,18 +1,18 @@
1
1
  <template>
2
2
  <div
3
- class="wt-select"
4
3
  :class="{
5
4
  'wt-select--disabled': disabled,
6
5
  'wt-select--invalid': invalid,
7
6
  'wt-select--clearable': clearable,
8
7
  'wt-select--loading': isLoading,
9
8
  }"
9
+ class="wt-select"
10
10
  >
11
11
  <wt-label
12
- class="wt-select__label"
13
12
  v-if="hasLabel"
14
13
  :disabled="disabled"
15
14
  :invalid="invalid"
15
+ class="wt-select__label"
16
16
  v-bind="labelProps"
17
17
  >
18
18
  <!-- @slot Custom input label -->
@@ -20,19 +20,19 @@
20
20
  </wt-label>
21
21
  <vue-multiselect
22
22
  ref="vue-multiselect"
23
- class="wt-select__select"
24
- v-bind="$attrs"
25
- :value="selectValue"
26
- :options="selectOptions"
27
- :placeholder="placeholder || label"
28
- :limit="1"
23
+ :allow-empty="allowEmpty"
24
+ :disabled="disabled"
25
+ :internal-search="!searchMethod"
29
26
  :label="optionLabel"
30
- :track-by="trackBy"
27
+ :limit="1"
31
28
  :limit-text="(count) => `+${count}`"
32
29
  :loading="false"
33
- :internal-search="!searchMethod"
34
- :disabled="disabled"
35
- :allow-empty="allowEmpty"
30
+ :options="selectOptions"
31
+ :placeholder="placeholder || label"
32
+ :track-by="trackBy"
33
+ :value="selectValue"
34
+ class="wt-select__select"
35
+ v-bind="$attrs"
36
36
  v-on="listeners"
37
37
  >
38
38
 
@@ -47,7 +47,7 @@
47
47
  <template slot="singleLabel" slot-scope="{ option }">
48
48
  <slot name="singleLabel" v-bind="{ option, optionLabel }">
49
49
  <span class="multiselect__single-label">
50
- {{ $te(option.locale) ? $t(option.locale) : (option[optionLabel] || option) }}
50
+ {{ getOptionLabel({ option, optionLabel }) }}
51
51
  </span>
52
52
  </slot>
53
53
  </template>
@@ -56,7 +56,7 @@
56
56
  <template slot="option" slot-scope="{ option }">
57
57
  <slot name="option" v-bind="{ option, optionLabel }">
58
58
  <span>
59
- {{ $te(option.locale) ? $t(option.locale) : (option[optionLabel] || option) }}
59
+ {{ getOptionLabel({ option, optionLabel }) }}
60
60
  </span>
61
61
  </slot>
62
62
  </template>
@@ -65,9 +65,9 @@
65
65
  <template slot="caret" slot-scope="{ toggle }">
66
66
  <!-- @mousedown.native.prevent.stop="toggle": https://github.com/shentao/vue-multiselect/issues/1204#issuecomment-615114727 -->
67
67
  <wt-icon-btn
68
+ :disabled="disabled"
68
69
  class="wt-select__arrow-caret"
69
70
  icon="arrow-down"
70
- :disabled="disabled"
71
71
  @mousedown.native.prevent.stop="toggle"
72
72
  ></wt-icon-btn>
73
73
  </template>
@@ -76,21 +76,21 @@
76
76
  <template slot="clear" slot-scope="{}">
77
77
  <wt-icon-btn
78
78
  v-if="clearable"
79
- class="wt-select__clear"
80
79
  :class="{ 'hidden': !isValue }"
81
80
  :disabled="disabled"
81
+ class="wt-select__clear"
82
82
  icon="close"
83
83
  @click="clearValue"
84
84
  ></wt-icon-btn>
85
85
  </template>
86
86
 
87
87
  <template slot="beforeList">
88
- <div class="wt-select__loading-wrapper" v-show="isLoading">
88
+ <div v-show="isLoading" class="wt-select__loading-wrapper">
89
89
  <wt-loader size="sm"></wt-loader>
90
90
  </div>
91
91
  </template>
92
92
 
93
- <template slot="afterList" v-if="showIntersectionObserver">
93
+ <template v-if="showIntersectionObserver" slot="afterList">
94
94
  <div v-observe-visibility="handleAfterListIntersect"></div>
95
95
  </template>
96
96
  </vue-multiselect>
@@ -105,9 +105,9 @@
105
105
  <script>
106
106
  import VueMultiselect from 'vue-multiselect';
107
107
  import { ObserveVisibility } from 'vue-observe-visibility';
108
- import isEmpty from '../../../scripts/isEmpty';
109
- import debounce from '../../../scripts/debounce';
110
108
  import validationMixin from '../../../mixins/validationMixin/validationMixin';
109
+ import debounce from '../../../scripts/debounce';
110
+ import isEmpty from '../../../scripts/isEmpty';
111
111
 
112
112
  export default {
113
113
  name: 'wt-select',
@@ -225,6 +225,13 @@ export default {
225
225
  },
226
226
 
227
227
  methods: {
228
+ getOptionLabel({ option, optionLabel }) {
229
+ if (option.locale) {
230
+ if (Array.isArray(option.locale)) return this.$tc(...option.locale);
231
+ return this.$t(option.locale);
232
+ }
233
+ return option[optionLabel] || option;
234
+ },
228
235
  handleSearchChange(search) {
229
236
  if (!this.isApiMode) return;
230
237
  this.isLoading = true;
@@ -294,28 +301,28 @@ export default {
294
301
 
295
302
  .wt-select__arrow-caret {
296
303
  position: absolute;
304
+ z-index: var(--select-caret-z-index);
297
305
  top: var(--select-caret-top-pos);
298
306
  right: var(--select-caret-right-pos);
299
- transform: rotate(0);
300
307
  transition: var(--transition);
301
- z-index: var(--select-caret-z-index);
308
+ transform: rotate(0);
302
309
  }
303
310
 
304
311
  ::v-deep .wt-select__clear {
305
312
  position: absolute;
313
+ z-index: var(--select-clear-z-index);
306
314
  top: var(--select-clear-top-pos);
307
315
  right: var(--select-clear-right-pos);
308
- transform: rotate(0);
309
316
  transition: var(--transition);
310
- z-index: var(--select-clear-z-index);
317
+ transform: rotate(0);
311
318
  }
312
319
 
313
320
  ::v-deep {
314
321
  .multiselect__tags {
315
322
  display: flex;
316
323
  align-items: center;
317
- min-height: var(--select-tags-min-height); // reset original 20px style
318
324
  box-sizing: border-box;
325
+ min-height: var(--select-tags-min-height); // reset original 20px style
319
326
  padding: var(--select-tags-padding);
320
327
  color: var(--form-input-color);
321
328
  border: var(--select-tags-border);
@@ -327,8 +334,8 @@ export default {
327
334
  @extend %typo-body-1;
328
335
  @include wt-placeholder;
329
336
  min-height: var(--select-input-min-height); // reset original 20px style
330
- padding: 0;
331
337
  margin: 0;
338
+ padding: 0;
332
339
  background: transparent;
333
340
  }
334
341
 
@@ -340,34 +347,34 @@ export default {
340
347
  @extend %typo-body-1;
341
348
  @extend .wt-chip;
342
349
  position: absolute;
350
+ z-index: var(--select-chip-z-index);
343
351
  right: var(--select-chip-right-pos);
344
352
  min-height: var(--select-input-min-height); // reset original 20px style
345
353
  margin: 0;
346
354
  font-weight: normal;
347
- z-index: var(--select-chip-z-index);
348
355
 
349
356
  /* these before-after elements are fixing issue when chip is visually overflown by
350
357
  selected option name (yep, chip has z-index, but its background has 0.3 opacity)*/
351
358
  &:before {
352
- content: '';
353
359
  position: absolute;
360
+ z-index: -2; // below chip itself
354
361
  top: 0;
355
362
  right: 0;
356
363
  bottom: 0;
357
364
  left: 0;
358
- z-index: -2; // below chip itself
365
+ content: '';
359
366
  background: var(--main-color);
360
367
  }
361
368
 
362
369
  &:after {
363
370
  @extend .wt-chip;
364
- content: '';
365
371
  position: absolute;
372
+ z-index: -1; // below chip itself
366
373
  top: 0;
367
374
  right: 0;
368
375
  bottom: 0;
369
376
  left: 0;
370
- z-index: -1; // below chip itself
377
+ content: '';
371
378
 
372
379
  }
373
380
  }
@@ -378,33 +385,33 @@ export default {
378
385
 
379
386
  .multiselect__custom-tag, .multiselect__single-label {
380
387
  @extend %typo-body-1;
381
- color: var(--form-input-color);
388
+ display: block;
382
389
 
383
390
  // text overflow 3 dots
384
- display: block;
385
- white-space: nowrap;
386
391
  overflow: hidden;
392
+ white-space: nowrap;
387
393
  text-overflow: ellipsis;
394
+ color: var(--form-input-color);
388
395
  }
389
396
 
390
397
  .multiselect__placeholder {
391
398
  @extend %wt-placeholder;
392
- padding: 0;
399
+ overflow: hidden;
393
400
  margin: 0;
394
401
 
395
402
  // text overflow 3 dots
403
+ padding: 0;
396
404
  white-space: nowrap;
397
- overflow: hidden;
398
405
  text-overflow: ellipsis;
399
406
  }
400
407
  }
401
408
 
402
409
  .multiselect__content-wrapper {
403
410
  @extend %wt-distant-scrollbar;
411
+ transition: var(--transition);
404
412
  border: var(--select-tags-border);
405
413
  border-color: var(--form-border-color);
406
414
  border-radius: var(--border-radius);
407
- transition: var(--transition);
408
415
 
409
416
  .multiselect__option {
410
417
  @extend %typo-body-1;
@@ -435,15 +442,15 @@ export default {
435
442
 
436
443
  .wt-select__loading-wrapper {
437
444
  position: sticky;
438
- width: 100%;
439
- height: 300px; // max dropdown panel height
445
+ z-index: 1;
440
446
  top: 0;
441
447
  left: 0;
442
448
  display: flex;
443
449
  align-items: center;
444
450
  justify-content: center;
451
+ width: 100%;
452
+ height: 300px; // max dropdown panel height
445
453
  backdrop-filter: blur(4px);
446
- z-index: 1;
447
454
  }
448
455
  }
449
456
 
@@ -513,8 +520,8 @@ export default {
513
520
  pointer-events: none;
514
521
 
515
522
  .multiselect--disabled {
516
- background: none;
517
523
  opacity: 1;
524
+ background: none;
518
525
  }
519
526
 
520
527
  .wt-select__arrow-caret ::v-deep .wt-icon__icon,
@@ -525,8 +532,8 @@ export default {
525
532
  .multiselect {
526
533
  ::v-deep {
527
534
  .multiselect__tags {
528
- background: var(--form-border--disabled-color);
529
535
  border-color: var(--form-border--disabled-color);
536
+ background: var(--form-border--disabled-color);
530
537
 
531
538
  .multiselect__placeholder {
532
539
  @extend %wt-placeholder--disabled;
@@ -33,6 +33,8 @@ export default {
33
33
  ru: 'Русский',
34
34
  ua: 'Українська',
35
35
  },
36
+ from: 'From',
37
+ to: 'To',
36
38
  },
37
39
  // date-related texts
38
40
  date: {
@@ -33,6 +33,8 @@ export default {
33
33
  ru: 'Русский',
34
34
  ua: 'Українська',
35
35
  },
36
+ from: 'От',
37
+ to: 'До',
36
38
  },
37
39
  // date-related texts
38
40
  date: {
@@ -33,6 +33,8 @@ export default {
33
33
  ru: 'Русский',
34
34
  ua: 'Українська',
35
35
  },
36
+ from: 'Від',
37
+ to: 'До',
36
38
  },
37
39
  // date-related texts
38
40
  date: {
@@ -0,0 +1,49 @@
1
+ import { shallowMount, createLocalVue } from '@vue/test-utils';
2
+ import Vuex from 'vuex';
3
+ import VueRouter from 'vue-router';
4
+ import DatetimeFilter from '../filter-datetime.vue';
5
+ import BaseFilterSchema from '../../classes/BaseFilterSchema';
6
+ import baseFilterMixin from '../../mixins/baseFilterMixin/baseFilterMixin';
7
+
8
+ const localVue = createLocalVue();
9
+ localVue.use(Vuex);
10
+ localVue.use(VueRouter);
11
+ const router = new VueRouter();
12
+
13
+ describe('DatetimeFilter Filter', () => {
14
+ const namespace = 'jest';
15
+ const filterQuery = 'jest';
16
+ const filterSchema = new BaseFilterSchema();
17
+ const store = new Vuex.Store({
18
+ modules: {
19
+ [namespace]: {
20
+ namespaced: true,
21
+ state: {
22
+ [filterQuery]: filterSchema,
23
+ },
24
+ },
25
+ },
26
+ });
27
+
28
+ const mountOptions = {
29
+ localVue,
30
+ store,
31
+ router,
32
+ propsData: {
33
+ namespace,
34
+ filterQuery,
35
+ },
36
+ };
37
+ it('renders a component', () => {
38
+ const wrapper = shallowMount(DatetimeFilter, mountOptions);
39
+ expect(wrapper.exists()).toBe(true);
40
+ });
41
+ it('initial restoreValue() triggers setValue() method', async () => {
42
+ const value = Date.now();
43
+ await router.replace({ query: { [filterQuery]: value } });
44
+ const setValueMock = jest.fn();
45
+ jest.spyOn(baseFilterMixin.methods, 'setValue').mockImplementationOnce(setValueMock);
46
+ shallowMount(DatetimeFilter, mountOptions);
47
+ expect(setValueMock).toHaveBeenCalledWith({ filter: filterQuery, value });
48
+ });
49
+ });
@@ -0,0 +1,65 @@
1
+ import { createLocalVue, shallowMount } from '@vue/test-utils';
2
+ import VueRouter from 'vue-router';
3
+ import Vuex from 'vuex';
4
+ import BaseFilterSchema from '../../classes/BaseFilterSchema';
5
+ import baseFilterMixin from '../../mixins/baseFilterMixin/baseFilterMixin';
6
+ import FilterFromTo from '../filter-from-to.vue';
7
+
8
+ const localVue = createLocalVue();
9
+ localVue.use(Vuex);
10
+ localVue.use(VueRouter);
11
+ const router = new VueRouter();
12
+
13
+ describe('FilterFromTo Filter', () => {
14
+ const namespace = 'jest';
15
+ const filterQuery = 'jest';
16
+ const filterSchema = new BaseFilterSchema();
17
+ const store = new Vuex.Store({
18
+ modules: {
19
+ [namespace]: {
20
+ namespaced: true,
21
+ state: {
22
+ [filterQuery]: filterSchema,
23
+ },
24
+ },
25
+ },
26
+ });
27
+
28
+ const mountOptions = {
29
+ localVue,
30
+ store,
31
+ router,
32
+ propsData: {
33
+ namespace,
34
+ filterQuery,
35
+ },
36
+ };
37
+ it('renders a component', () => {
38
+ const wrapper = shallowMount(FilterFromTo, mountOptions);
39
+ expect(wrapper.exists()).toBe(true);
40
+ });
41
+ it('initial restoreValue() triggers setValue() method: From case', async () => {
42
+ const value = 10;
43
+ await router.replace({ query: { [`${filterQuery}From`]: value } });
44
+ const setValueMock = jest.fn();
45
+ jest.spyOn(baseFilterMixin.methods, 'setValue')
46
+ .mockImplementationOnce(setValueMock);
47
+ shallowMount(FilterFromTo, mountOptions);
48
+ expect(setValueMock).toHaveBeenCalledWith({
49
+ filter: filterQuery,
50
+ value: { to: undefined, from: value },
51
+ });
52
+ });
53
+ // it('initial restoreValue() triggers setValue() method: To case', async () => {
54
+ // const value = 10;
55
+ // await router.replace({ query: { [`${filterQuery}To`]: value } });
56
+ // const setValueMock = jest.fn();
57
+ // jest.spyOn(baseFilterMixin.methods, 'setValue')
58
+ // .mockImplementationOnce(setValueMock);
59
+ // shallowMount(FilterFromTo, mountOptions);
60
+ // expect(setValueMock).toHaveBeenCalledWith({
61
+ // filter: filterQuery,
62
+ // value: { to: value, from: 0 },
63
+ // });
64
+ // });
65
+ });
@@ -0,0 +1,39 @@
1
+ <template>
2
+ <wt-datetimepicker
3
+ :value="filterSchema.value"
4
+ :label="label"
5
+ @change="handleChange"
6
+ ></wt-datetimepicker>
7
+ </template>
8
+
9
+ <script>
10
+ import baseFilterMixin from '../mixins/baseFilterMixin/baseFilterMixin';
11
+
12
+ export default {
13
+ name: 'filter-from',
14
+ mixins: [baseFilterMixin],
15
+ props: {
16
+ filterQuery: {
17
+ type: String,
18
+ required: true,
19
+ },
20
+ label: {
21
+ type: String,
22
+ },
23
+ },
24
+
25
+ methods: {
26
+ handleChange(value) {
27
+ this.setValue({ filter: this.filterQuery, value });
28
+ this.setValueToQuery({ filterQuery: this.filterQuery, value });
29
+ },
30
+ restoreValue(value) {
31
+ this.setValue({ filter: this.filterQuery, value: +value });
32
+ },
33
+ },
34
+ };
35
+ </script>
36
+
37
+ <style scoped>
38
+
39
+ </style>
@@ -0,0 +1,142 @@
1
+ <template>
2
+ <div class="filter-from-to">
3
+ <wt-label>{{ label }}</wt-label>
4
+ <div class="filter-from-to__inputs-wrapper">
5
+ <div class="filter-from-to__input-wrapper">
6
+ <wt-label
7
+ class="filter-from-to__input-label"
8
+ for="filter-from-to-from"
9
+ >{{ $t('reusable.from') }}
10
+ </wt-label>
11
+ <wt-input
12
+ class="filter-from-to-input"
13
+ name="filter-from-to-from"
14
+ :value="filterSchema.value.from"
15
+ type="number"
16
+ :number-min="0"
17
+ @input="setFrom"
18
+ ></wt-input>
19
+ </div>
20
+ <div class="filter-from-to__input-wrapper">
21
+ <wt-label
22
+ class="filter-from-to__input-label"
23
+ for="filter-from-to-to"
24
+ >{{ $t('reusable.to') }}
25
+ </wt-label>
26
+ <wt-input
27
+ class="filter-from-to-input"
28
+ name="filter-from-to-to"
29
+ :value="filterSchema.value.to"
30
+ type="number"
31
+ :number-min="0"
32
+ @input="setTo"
33
+ ></wt-input>
34
+ </div>
35
+ </div>
36
+ </div>
37
+ </template>
38
+
39
+ <script>
40
+ import debounce from '../../../scripts/debounce';
41
+ import baseFilterMixin from '../mixins/baseFilterMixin/baseFilterMixin';
42
+
43
+ export default {
44
+ name: 'filter-from-to',
45
+ mixins: [baseFilterMixin],
46
+ props: {
47
+ filterQuery: {
48
+ type: String,
49
+ required: true,
50
+ },
51
+ label: {
52
+ type: String,
53
+ },
54
+ },
55
+ methods: {
56
+ restore() {
57
+ this.restoreFrom();
58
+ this.restoreTo();
59
+ },
60
+
61
+ restoreFrom() {
62
+ const from = 0;
63
+ const queryValue = this.$route.query[`${this.filterQuery}From`];
64
+ // this.value.from = +queryValue || from;
65
+ const value = { from: +queryValue || from, to: this.value.to };
66
+ this.setValue({ filter: this.filterQuery, value });
67
+ },
68
+
69
+ restoreTo() {
70
+ const to = null;
71
+ const queryValue = this.$route.query[`${this.filterQuery}To`];
72
+ // this.value.to = +queryValue || to;
73
+ const value = { from: this.value.from, to: +queryValue || to };
74
+ console.info(value);
75
+ this.setValue({ filter: this.filterQuery, value });
76
+ },
77
+
78
+ setFrom(value) {
79
+ this.setValueToQuery({
80
+ filterQuery: `${this.filterQuery}From`,
81
+ value,
82
+ });
83
+ this.restoreFrom();
84
+ },
85
+
86
+ setTo(value) {
87
+ this.setValueToQuery({
88
+ filterQuery: `${this.filterQuery}To`,
89
+ value,
90
+ });
91
+ this.restoreTo();
92
+ },
93
+ },
94
+
95
+ created() {
96
+ this.setFrom = debounce(this.setFrom);
97
+ this.setTo = debounce(this.setTo);
98
+ },
99
+ };
100
+ </script>
101
+
102
+ <style lang="scss" scoped>
103
+ .filter-from-to {
104
+
105
+ & > .wt-label {
106
+ margin-bottom: 10px;
107
+ }
108
+
109
+ &:hover > .wt-label {
110
+ color: var(--form-label--hover-color);
111
+ }
112
+
113
+ &:focus-within > .wt-label {
114
+ color: var(--form-label--active-color);
115
+ }
116
+ }
117
+
118
+ .filter-from-to__inputs-wrapper,
119
+ .filter-from-to__input-wrapper {
120
+ display: flex;
121
+ align-items: center;
122
+ }
123
+
124
+ .filter-from-to__input-label {
125
+ @extend %typo-subtitle-1;
126
+ margin-right: 5px;
127
+ }
128
+
129
+ .filter-from-to__input-wrapper {
130
+ &:focus-within .wt-label {
131
+ color: var(--form-label--active-color)
132
+ }
133
+
134
+ .filter-from-to-input {
135
+ width: 70px;
136
+ }
137
+
138
+ &:first-child {
139
+ margin-right: 10px;
140
+ }
141
+ }
142
+ </style>