@webitel/ui-sdk 24.6.0 → 24.6.2

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.
Files changed (33) hide show
  1. package/dist/ui-sdk.css +1 -1
  2. package/dist/ui-sdk.mjs +547 -532
  3. package/dist/ui-sdk.umd.js +1 -1
  4. package/package.json +4 -6
  5. package/src/components/wt-table/wt-table.vue +41 -6
  6. package/src/components/wt-table-column-select/wt-table-column-select.vue +1 -1
  7. package/src/modules/Filters/classes/BaseFilterSchema.js +140 -7
  8. package/src/modules/Filters/components/__tests__/filter-pagination.spec.js +94 -0
  9. package/src/modules/Filters/components/__tests__/filter-table-fields.spec.js +71 -0
  10. package/src/modules/Filters/components/filter-pagination.vue +8 -8
  11. package/src/modules/Filters/components/filter-table-fields.vue +4 -11
  12. package/src/modules/Filters/composables/useTableFilters.js +2 -3
  13. package/src/modules/Filters/enums/FilterEvent.enum.js +6 -0
  14. package/src/modules/Filters/scripts/getters/index.js +9 -0
  15. package/src/modules/Filters/scripts/getters/localStorageGetter.js +17 -0
  16. package/src/modules/Filters/scripts/getters/queryGetter.js +10 -0
  17. package/src/modules/Filters/scripts/getters/valueGetter.js +9 -0
  18. package/src/modules/Filters/scripts/restores/index.js +7 -0
  19. package/src/modules/Filters/scripts/restores/localStorageRestore.js +7 -0
  20. package/src/modules/Filters/scripts/restores/queryRestore.js +7 -0
  21. package/src/modules/Filters/scripts/setters/index.js +9 -0
  22. package/src/modules/Filters/scripts/setters/localStorageSetter.js +16 -0
  23. package/src/modules/Filters/scripts/setters/querySetter.js +41 -0
  24. package/src/modules/Filters/scripts/setters/valueSetter.js +6 -0
  25. package/src/modules/Filters/scripts/utils/changeRouteQuery.js +17 -0
  26. package/src/modules/Filters/store/FiltersStoreModule.js +88 -139
  27. package/src/modules/Filters/store/__tests__/FiltersStoreModule.spec.js +157 -0
  28. package/src/modules/Notifications/store/__tests__/NotificationsStoreModule.actions.spec.js +1 -1
  29. package/src/modules/TableStoreModule/composables/useTableStore.js +13 -9
  30. package/src/modules/TableStoreModule/store/TableStoreModule.js +124 -106
  31. package/src/modules/TableStoreModule/store/__tests__/TableStoreModule.spec.js +241 -0
  32. package/src/modules/Filters/restores/filterFieldsRestore.js +0 -9
  33. package/src/modules/Filters/restores/readme.md +0 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webitel/ui-sdk",
3
- "version": "24.6.0",
3
+ "version": "24.6.2",
4
4
  "private": false,
5
5
  "scripts": {
6
6
  "dev": "vite",
@@ -40,7 +40,7 @@
40
40
  "@vuelidate/validators": "^2.0.4",
41
41
  "@vuepic/vue-datepicker": "^4.4.0",
42
42
  "@vueuse/components": "^10.7.2",
43
- "axios": "^1.6.8",
43
+ "axios": "^1.7.1",
44
44
  "clipboard-copy": "^4.0.1",
45
45
  "csv-stringify": "^5.5.3",
46
46
  "deep-copy": "^1.4.2",
@@ -51,6 +51,7 @@
51
51
  "jszip": "^3.5.0",
52
52
  "jszip-utils": "^0.1.0",
53
53
  "lodash": "^4.17.21",
54
+ "mitt": "^3.0.1",
54
55
  "plyr": "3.6.3",
55
56
  "query-string": "^8.1.0",
56
57
  "sortablejs": "^1.15.0",
@@ -70,7 +71,7 @@
70
71
  "eslint": "^8.53.0",
71
72
  "eslint-plugin-vue": "^9.18.1",
72
73
  "globby": "^14.0.0",
73
- "happy-dom": "^12.9.1",
74
+ "happy-dom": "^14.11.0",
74
75
  "markdown-it": "^13.0.2",
75
76
  "markdown-table": "^3.0.3",
76
77
  "postcss": "^8.4.31",
@@ -86,8 +87,5 @@
86
87
  "vitest": "^1.4.0",
87
88
  "vue": "^3.4.15",
88
89
  "vuex": "^4.1.0"
89
- },
90
- "overrides": {
91
- "axios": "^1.6.8"
92
90
  }
93
91
  }
@@ -63,8 +63,8 @@
63
63
  class="wt-table__td wt-table__td--checkbox"
64
64
  >
65
65
  <wt-checkbox
66
- :selected="row._isSelected"
67
- @change="row._isSelected = !row._isSelected"
66
+ :selected="_selected.includes(row)"
67
+ @change="handleSelection(row, $event)"
68
68
  />
69
69
  </td>
70
70
 
@@ -147,17 +147,30 @@ export default {
147
147
  type: Boolean,
148
148
  default: true,
149
149
  },
150
+ selected: {
151
+ type: Array,
152
+ // no default! because we need to know if it's passed or not
153
+ },
150
154
  gridActions: {
151
155
  type: Boolean,
152
156
  default: true,
153
157
  },
154
158
  },
159
+ emits: [
160
+ 'sort',
161
+ 'update:selected',
162
+ ],
155
163
 
156
164
  data: () => ({}),
157
165
 
158
166
  computed: {
167
+ _selected() {
168
+ // _isSelected for backwards compatibility
169
+ return this.selected || this.data.filter((item) => item._isSelected);
170
+ },
171
+
159
172
  isAllSelected() {
160
- return this.data.every((item) => item._isSelected);
173
+ return this._selected.length === this.data.length && this.data.length > 0;
161
174
  },
162
175
 
163
176
  dataHeaders() {
@@ -206,11 +219,33 @@ export default {
206
219
  return this.sortable && sort !== undefined;
207
220
  },
208
221
  selectAll() {
209
- const { isAllSelected } = this;
210
- // eslint-disable-next-line no-param-reassign,no-return-assign
211
- this.data.forEach((item) => item._isSelected = !isAllSelected);
222
+ if (this.selected) {
223
+ if (this.isAllSelected) {
224
+ this.$emit('update:selected', []);
225
+ } else {
226
+ this.$emit('update:selected', [...this.data]);
227
+ }
228
+ } else {
229
+ // for backwards compatibility
230
+ this.data.forEach((item) => {
231
+ item._isSelected = !this.isAllSelected;
232
+ });
233
+ }
212
234
  },
235
+ handleSelection(row, remove) {
236
+ if (this.selected) {
237
+ if (remove) {
238
+ this.$emit('update:selected', this._selected.filter((item) => item !== row));
239
+ } else {
240
+ this.$emit('update:selected', [...this._selected, row]);
241
+ }
242
+ } else {
243
+ // for backwards compatibility
244
+ row._isSelected = !row._isSelected;
245
+ }
246
+ }
213
247
  },
248
+
214
249
  };
215
250
  </script>
216
251
 
@@ -10,7 +10,7 @@
10
10
  {{ $t('webitelUI.tableColumnSelect.title') }}
11
11
  </wt-tooltip>
12
12
  <wt-popup
13
- v-if="isColumnSelectPopup"
13
+ :shown="isColumnSelectPopup"
14
14
  class="wt-table-column-select__popup"
15
15
  @close="close"
16
16
  >
@@ -1,13 +1,146 @@
1
+ import { valueSetter, querySetter, localStorageSetter } from '../scripts/setters';
2
+ import { queryRestore, localStorageRestore } from '../scripts/restores';
3
+ import { valueGetter, queryGetter, localStorageGetter } from '../scripts/getters';
4
+
5
+ const convertGetterArray = (context) => (router) => (getters) => {
6
+ const availableGetters = ['value', 'query', 'localStorage'];
7
+
8
+ getters.forEach((getter) => {
9
+ if (!availableGetters.includes(getter)) throw new Error(`Unknown getter: ${getter}`);
10
+ });
11
+
12
+ const getter = () => {
13
+ if (getters.includes('value')) {
14
+ const value = valueGetter(context)();
15
+ if (value) return value;
16
+ }
17
+ if (getters.includes('query')) {
18
+ const value = queryGetter(context)(router)();
19
+ if (value) return value;
20
+ }
21
+ if (getters.includes('localStorage')) {
22
+ const value = localStorageGetter(context)();
23
+ if (value) return value;
24
+ }
25
+
26
+ console.error(`No value found for ${context.name} filter!`);
27
+
28
+ return undefined;
29
+ };
30
+
31
+ return getter;
32
+ };
33
+
34
+ const convertSetterArray = (context) => (router) => (setters) => {
35
+ const availableSetters = ['value', 'query', 'localStorage'];
36
+ setters.forEach((setter) => {
37
+ if (!availableSetters.includes(setter)) throw new Error(`Unknown setter: ${setter}`);
38
+ });
39
+
40
+ const setter = async (value) => {
41
+ if (setters.includes('value')) valueSetter(context)(value);
42
+ if (setters.includes('query')) await querySetter(context)(router)(value);
43
+ if (setters.includes('localStorage')) localStorageSetter(context)(value);
44
+
45
+ return context;
46
+ };
47
+
48
+ return setter;
49
+ };
50
+
51
+ const convertRestoreArray = (context) => (router) => (restores) => {
52
+ const availableRestores = ['query', 'localStorage'];
53
+
54
+ restores.forEach((restore) => {
55
+ if (!availableRestores.includes(restore)) throw new Error(`Unknown restore: ${restore}`);
56
+ });
57
+
58
+ const restore = () => {
59
+ if (restores.includes('query')) {
60
+ const restoredValue = queryRestore(context)(router)();
61
+ if (restoredValue) return restoredValue;
62
+ }
63
+
64
+ if (restores.includes('localStorage')) {
65
+ const restoredValue = localStorageRestore(context)();
66
+ if (restoredValue) return restoredValue;
67
+ }
68
+ };
69
+
70
+ return restore;
71
+ };
72
+
1
73
  export default class BaseFilterSchema {
2
74
  constructor({
3
- value = '',
4
- defaultValue = '',
75
+ name,
76
+ value,
77
+ get,
78
+ set,
5
79
  restore,
6
- localStorageKey,
80
+ router, // is required for query get/set, if getters/setters are passed as strings
81
+ ...rest
7
82
  } = {}) {
8
- this.value = value;
9
- this.defaultValue = defaultValue;
10
- if (restore) this.restore = restore;
11
- if (localStorageKey) this.localStorageKey = localStorageKey;
83
+ if (!name) throw new Error('Filter name is required');
84
+ if (value === undefined) throw new Error(`Filter value is required: ${name}`);
85
+
86
+ if (!get) throw new Error(`Filter getter is required: ${name}`);
87
+ if (!set) throw new Error(`Filter setter is required: ${name}`);
88
+ if (!restore) throw new Error(`Filter restore is required: ${name}`);
89
+
90
+ Object.assign(this, {
91
+ name,
92
+ value,
93
+ get,
94
+ set,
95
+ restore,
96
+ }, rest);
97
+
98
+ this.setupGetters(get, { router });
99
+ this.setupSetters(set, { router });
100
+ this.setupRestores(restore, { router });
101
+
102
+ return this;
103
+ }
104
+
105
+ setupGetters(getters, { router }) {
106
+ let getter;
107
+
108
+ if (Array.isArray(getters)) {
109
+ getter = convertGetterArray(this)(router)(getters);
110
+ } else if (typeof getters === 'function') {
111
+ getter = getters(this);
112
+ } else {
113
+ throw new Error('Getter should be a function or an array of available getters');
114
+ }
115
+
116
+ this.get = getter;
117
+ }
118
+
119
+ setupSetters(setters, { router }) {
120
+ let setter;
121
+
122
+ if (Array.isArray(setters)) {
123
+ setter = convertSetterArray(this)(router)(setters);
124
+ } else if (typeof setters === 'function') {
125
+ setter = setters(this);
126
+ } else {
127
+ throw new Error('Setter should be a function or an array of available setters');
128
+ }
129
+
130
+ this.set = setter;
131
+ }
132
+
133
+ setupRestores(restores, { router }) {
134
+ let restore;
135
+
136
+ if (Array.isArray(restores)) {
137
+ restore = convertRestoreArray(this)(router)(restores);
138
+ } else if (typeof restores === 'function') {
139
+ restore = restores(this);
140
+ } else {
141
+ throw new Error('Restore should be a function or an array of available restores');
142
+ }
143
+
144
+ this.restore = restore;
12
145
  }
13
146
  }
@@ -0,0 +1,94 @@
1
+ import { mount } from '@vue/test-utils';
2
+ import { createStore } from 'vuex';
3
+ import FiltersStoreModule from '../../store/FiltersStoreModule';
4
+ import FilterPagination from '../filter-pagination.vue';
5
+
6
+ const filtersSchema = [
7
+ {
8
+ name: 'page',
9
+ value: 1,
10
+ get: ['value'],
11
+ set: ['value'],
12
+ restore: () => {},
13
+ },
14
+ {
15
+ name: 'size',
16
+ value: 11,
17
+ get: ['value'],
18
+ set: ['value'],
19
+ restore: () => {},
20
+ },
21
+ ];
22
+
23
+ describe('FilterPagination', () => {
24
+ it('renders a component', () => {
25
+ const store = createStore({
26
+ modules: {
27
+ filters: new FiltersStoreModule().addFilter(filtersSchema).getModule(),
28
+ },
29
+ });
30
+
31
+ const wrapper = mount(FilterPagination, {
32
+ props: {
33
+ namespace: 'filters',
34
+ },
35
+ global: {
36
+ plugins: [store],
37
+ },
38
+ });
39
+ expect(wrapper.exists()).toBe(true);
40
+ });
41
+
42
+ it('size change dispatches SET_FILTER action', async () => {
43
+ const store = createStore({
44
+ modules: {
45
+ filters: new FiltersStoreModule().addFilter(filtersSchema).getModule(),
46
+ },
47
+ });
48
+
49
+ const wrapper = mount(FilterPagination, {
50
+ shallow: true,
51
+ props: {
52
+ namespace: 'filters',
53
+ },
54
+ global: {
55
+ plugins: [store],
56
+ stubs: {
57
+ WtPagination: false,
58
+ },
59
+ },
60
+ });
61
+
62
+ const pagination = wrapper.findComponent('.wt-pagination');
63
+ pagination.vm.$emit('input', 321);
64
+ pagination.vm.$emit('change');
65
+ await wrapper.vm.$nextTick();
66
+ expect(store.getters['filters/GET_FILTER']('size')).toBe(321);
67
+ });
68
+
69
+ it('page change dispatches SET_FILTER action', async () => {
70
+ const store = createStore({
71
+ modules: {
72
+ filters: new FiltersStoreModule().addFilter(filtersSchema).getModule(),
73
+ },
74
+ });
75
+
76
+ const wrapper = mount(FilterPagination, {
77
+ shallow: true,
78
+ props: {
79
+ namespace: 'filters',
80
+ },
81
+ global: {
82
+ plugins: [store],
83
+ stubs: {
84
+ WtPagination: false,
85
+ },
86
+ },
87
+ });
88
+
89
+ const pagination = wrapper.findComponent('.wt-pagination');
90
+ pagination.vm.$emit('next');
91
+ await wrapper.vm.$nextTick();
92
+ expect(store.getters['filters/GET_FILTER']('page')).toBe(2);
93
+ });
94
+ });
@@ -0,0 +1,71 @@
1
+ import { createStore } from 'vuex';
2
+ import { mount } from '@vue/test-utils';
3
+ import FiltersStoreModule from '../../store/FiltersStoreModule';
4
+ import FilterTableFields from '../filter-table-fields.vue';
5
+
6
+ const filterSchema = {
7
+ name: 'fields',
8
+ value: ['field2'],
9
+ get: ['value'],
10
+ set: ['value'],
11
+ restore: () => {},
12
+ };
13
+
14
+ describe('FilterTableFields', () => {
15
+ it('renders a component', () => {
16
+ const store = createStore({
17
+ modules: {
18
+ filters: new FiltersStoreModule().addFilter(filterSchema).getModule(),
19
+ },
20
+ });
21
+
22
+ const headers = [
23
+ { value: 'f1', field: 'field1', show: false },
24
+ { value: 'f2', field: 'field2', show: true },
25
+ ];
26
+
27
+ const wrapper = mount(FilterTableFields, {
28
+ props: {
29
+ namespace: 'filters',
30
+ headers,
31
+ },
32
+ global: {
33
+ plugins: [store],
34
+ },
35
+ });
36
+ expect(wrapper.exists()).toBe(true);
37
+ });
38
+
39
+ it('fields change dispatches SET_FILTER action', async () => {
40
+ const store = createStore({
41
+ modules: {
42
+ filters: new FiltersStoreModule().addFilter(filterSchema).getModule(),
43
+ },
44
+ });
45
+
46
+ const headers = [
47
+ { value: 'f1', field: 'field1', show: false },
48
+ { value: 'f2', field: 'field2', show: true },
49
+ ];
50
+
51
+ const wrapper = mount(FilterTableFields, {
52
+ shallow: true,
53
+ props: {
54
+ namespace: 'filters',
55
+ headers,
56
+ },
57
+ global: {
58
+ plugins: [store],
59
+ },
60
+ });
61
+
62
+ wrapper.findComponent({ name: 'WtTableColumnSelect' }).vm.$emit('change', [
63
+ { value: 'f1', field: 'field1', show: true },
64
+ { value: 'f2', field: 'field2', show: false },
65
+ ]);
66
+
67
+ await wrapper.vm.$nextTick();
68
+
69
+ expect(store.getters['filters/GET_FILTER']('fields')).toEqual(['f1']);
70
+ });
71
+ });
@@ -34,26 +34,26 @@ const store = useStore();
34
34
 
35
35
  const localSize = ref(0);
36
36
 
37
- const page = computed(() => getNamespacedState(store.state, props.namespace)[pageFilterName].value);
38
- const size = computed(() => getNamespacedState(store.state, props.namespace)[sizeFilterName].value);
37
+ const page = computed(() => store.getters[`${props.namespace}/GET_FILTER`](pageFilterName));
38
+ const size = computed(() => store.getters[`${props.namespace}/GET_FILTER`](sizeFilterName));
39
39
 
40
40
  function setFilter(payload) {
41
41
  return store.dispatch(`${props.namespace}/SET_FILTER`, payload);
42
42
  }
43
43
 
44
44
  function setPage(value) {
45
- return setFilter({ value, filter: pageFilterName });
45
+ return setFilter({ value, name: pageFilterName });
46
46
  }
47
47
 
48
48
  function setSize(value) {
49
49
  if (value === size.value) return;
50
- // eslint-disable-next-line consistent-return
51
- return setFilter({ value, filter: sizeFilterName });
50
+ return setFilter({ value, name: sizeFilterName });
52
51
  }
53
52
 
54
- watch(size, () => {
55
- localSize.value = size.value;
56
- }, { immediate: true });
53
+ // TF?
54
+ // watch(size, () => {
55
+ // localSize.value = size.value;
56
+ // }, { immediate: true });
57
57
  </script>
58
58
 
59
59
  <style lang="scss" scoped>
@@ -22,35 +22,28 @@ const props = defineProps({
22
22
  },
23
23
  staticHeaders: {
24
24
  type: Array,
25
+ default: () => [],
25
26
  },
26
27
  });
27
28
 
28
- const emit = defineEmits(
29
+ const emit = defineEmits([
29
30
  'change',
30
- );
31
+ ]);
31
32
 
32
- // const storedProp = 'value';
33
33
  const filterQuery = 'fields';
34
34
 
35
35
  const store = useStore();
36
36
 
37
- const filterSchema = computed(() => getNamespacedState(store.state, props.namespace)[filterQuery]);
38
-
39
37
  function setValue(payload) {
40
38
  return store.dispatch(`${props.namespace}/SET_FILTER`, payload);
41
39
  }
42
40
 
43
- function setToLocalStorage({ value }) {
44
- localStorage.setItem(filterSchema.value.localStorageKey, value);
45
- }
46
-
47
41
  function handleChange(headers) {
48
42
  const value = headers.filter((item) => item.show).map(({ value }) => value);
49
43
  const params = {
50
- filter: filterQuery,
44
+ name: filterQuery,
51
45
  value,
52
46
  };
53
- setToLocalStorage(params);
54
47
  setValue(params);
55
48
  emit('change', headers);
56
49
  }
@@ -9,9 +9,8 @@ export const useTableFilters = (namespace) => {
9
9
  return store.dispatch(`${filtersNamespace}/RESTORE_FILTERS`, payload);
10
10
  }
11
11
 
12
- restoreFilters();
13
-
14
12
  return {
15
- filtersNamespace,
13
+ namespace: filtersNamespace,
14
+ restoreFilters,
16
15
  };
17
16
  };
@@ -0,0 +1,6 @@
1
+ const FilterEvent = Object.freeze({
2
+ FILTER_SET: 'FILTER_SET',
3
+ RESTORED: 'RESTORED',
4
+ });
5
+
6
+ export default FilterEvent;
@@ -0,0 +1,9 @@
1
+ import localStorageGetter from './localStorageGetter';
2
+ import queryGetter from './queryGetter';
3
+ import valueGetter from './valueGetter';
4
+
5
+ export {
6
+ localStorageGetter,
7
+ queryGetter,
8
+ valueGetter,
9
+ };
@@ -0,0 +1,17 @@
1
+ // context === filter "this"
2
+ const localStorageGetter = (context) => () => {
3
+ if (!context) throw new Error('Filter context is required for localStorageGetter!');
4
+
5
+ const key = context.localStorageKey;
6
+
7
+ if (!key) throw new Error(`Please provide "localStorageKey" for ${context.name} filter!`);
8
+
9
+ const value = localStorage.getItem(key);
10
+ if (!value) return null;
11
+
12
+ const splitted = value.split(',');
13
+ if (splitted.length === 1) return splitted[0];
14
+ return splitted;
15
+ }
16
+
17
+ export default localStorageGetter;
@@ -0,0 +1,10 @@
1
+
2
+ // context === filter "this"
3
+ const queryGetter = (context) => (router) => () => {
4
+ if (!router) throw new Error('Router is required for queryGetter!');
5
+ if (!router) throw new Error('Router is required for queryGetter!');
6
+
7
+ return router.currentRoute.value.query[context.name];
8
+ };
9
+
10
+ export default queryGetter;
@@ -0,0 +1,9 @@
1
+ // context === filter "this"
2
+ const valueGetter = (context) => () => {
3
+ const { value, storedProp, multiple } = context;
4
+ if (multiple) return value.map((item) => item[storedProp]); // if arr, map
5
+ if (storedProp) return value[storedProp]; // if object and has specific prop, return this prop
6
+ return value; // else return val
7
+ }
8
+
9
+ export default valueGetter;
@@ -0,0 +1,7 @@
1
+ import localStorageRestore from './localStorageRestore';
2
+ import queryRestore from './queryRestore';
3
+
4
+ export {
5
+ localStorageRestore,
6
+ queryRestore,
7
+ };
@@ -0,0 +1,7 @@
1
+ /**
2
+ * restore copies getter logic
3
+ */
4
+
5
+ import localStorageGetter from '../getters/localStorageGetter';
6
+
7
+ export default localStorageGetter;
@@ -0,0 +1,7 @@
1
+ /**
2
+ * restore copies getter logic
3
+ */
4
+
5
+ import queryGetter from '../getters/queryGetter';
6
+
7
+ export default queryGetter;
@@ -0,0 +1,9 @@
1
+ import localStorageSetter from './localStorageSetter';
2
+ import querySetter from './querySetter';
3
+ import valueSetter from './valueSetter';
4
+
5
+ export {
6
+ localStorageSetter,
7
+ querySetter,
8
+ valueSetter,
9
+ };
@@ -0,0 +1,16 @@
1
+ // context === filter "this"
2
+
3
+ const localStorageSetter = (context) => (rawValue) => {
4
+ if (!context) throw new Error('Filter context is required for localStorageSetter!');
5
+
6
+ const key = context.localStorageKey;
7
+
8
+ if (!key) throw new Error(`Please provide "localStorageKey" for ${context.name} filter!`);
9
+
10
+ const value = Array.isArray(rawValue) ? rawValue.join(',') : rawValue;
11
+ localStorage.setItem(key, value);
12
+
13
+ return context;
14
+ }
15
+
16
+ export default localStorageSetter;