@webitel/ui-sdk 24.4.39 → 24.6.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.
Files changed (46) hide show
  1. package/dist/ui-sdk.css +1 -1
  2. package/dist/ui-sdk.mjs +3439 -3353
  3. package/dist/ui-sdk.umd.js +11 -11
  4. package/package.json +4 -6
  5. package/src/components/transitions/wt-expand-transition.vue +63 -4
  6. package/src/components/wt-button/wt-button.vue +42 -6
  7. package/src/components/wt-expansion-panel/wt-expansion-panel.vue +0 -1
  8. package/src/components/wt-item-link/wt-item-link.vue +39 -9
  9. package/src/components/wt-player/wt-player.vue +8 -20
  10. package/src/components/wt-popup/__tests__/WtPopup.spec.js +5 -9
  11. package/src/components/wt-popup/_variables.scss +5 -0
  12. package/src/components/wt-popup/wt-popup.vue +88 -39
  13. package/src/components/wt-table/wt-table.vue +41 -6
  14. package/src/locale/en/en.js +0 -1
  15. package/src/locale/ru/ru.js +0 -1
  16. package/src/locale/ua/ua.js +0 -1
  17. package/src/modules/AuditForm/components/questions/options/__tests__/audit-form-question-options-write-row.spec.js +1 -1
  18. package/src/modules/AuditForm/components/questions/options/audit-form-question-options-write-row.vue +3 -15
  19. package/src/modules/AuditForm/components/questions/score/audit-form-question-score.vue +3 -6
  20. package/src/modules/Filters/classes/BaseFilterSchema.js +140 -7
  21. package/src/modules/Filters/components/__tests__/filter-pagination.spec.js +94 -0
  22. package/src/modules/Filters/components/__tests__/filter-table-fields.spec.js +71 -0
  23. package/src/modules/Filters/components/filter-pagination.vue +8 -8
  24. package/src/modules/Filters/components/filter-table-fields.vue +4 -11
  25. package/src/modules/Filters/composables/useTableFilters.js +2 -3
  26. package/src/modules/Filters/enums/FilterEvent.enum.js +6 -0
  27. package/src/modules/Filters/scripts/getters/index.js +9 -0
  28. package/src/modules/Filters/scripts/getters/localStorageGetter.js +17 -0
  29. package/src/modules/Filters/scripts/getters/queryGetter.js +10 -0
  30. package/src/modules/Filters/scripts/getters/valueGetter.js +9 -0
  31. package/src/modules/Filters/scripts/restores/index.js +7 -0
  32. package/src/modules/Filters/scripts/restores/localStorageRestore.js +7 -0
  33. package/src/modules/Filters/scripts/restores/queryRestore.js +7 -0
  34. package/src/modules/Filters/scripts/setters/index.js +9 -0
  35. package/src/modules/Filters/scripts/setters/localStorageSetter.js +16 -0
  36. package/src/modules/Filters/scripts/setters/querySetter.js +41 -0
  37. package/src/modules/Filters/scripts/setters/valueSetter.js +6 -0
  38. package/src/modules/Filters/scripts/utils/changeRouteQuery.js +17 -0
  39. package/src/modules/Filters/store/FiltersStoreModule.js +88 -139
  40. package/src/modules/Filters/store/__tests__/FiltersStoreModule.spec.js +157 -0
  41. package/src/modules/Notifications/store/__tests__/NotificationsStoreModule.actions.spec.js +1 -1
  42. package/src/modules/TableStoreModule/composables/useTableStore.js +13 -9
  43. package/src/modules/TableStoreModule/store/TableStoreModule.js +124 -106
  44. package/src/modules/TableStoreModule/store/__tests__/TableStoreModule.spec.js +241 -0
  45. package/src/modules/Filters/restores/filterFieldsRestore.js +0 -9
  46. package/src/modules/Filters/restores/readme.md +0 -1
@@ -1,181 +1,130 @@
1
- import deepEqual from 'deep-equal';
2
- import debounce from '../../../scripts/debounce';
1
+ import mitt from 'mitt';
3
2
  import isEmpty from '../../../scripts/isEmpty';
4
3
  import BaseStoreModule from '../../../store/BaseStoreModules/BaseStoreModule';
5
-
6
- const SET_FILTER_SOURCE = Object.freeze({
7
- RESTORE: 'restore', // when filter is restored at session start
8
- DEFAULT: 'default',
9
- });
4
+ import BaseFilterSchema from '../classes/BaseFilterSchema';
5
+ import FilterEvent from '../enums/FilterEvent.enum';
10
6
 
11
7
  export default class FiltersStoreModule extends BaseStoreModule {
8
+ state = {
9
+ _emitter: mitt(),
10
+ };
11
+
12
12
  getters = {
13
- ROUTER: () => console.error('setup ROUTER getter for filters store'),
14
- TABLE_NAMESPACE: () => console.error('setup TABLE_NAMESPACE getter for filters store'),
13
+ _STATE_FILTER_NAMES: (state) => Object.keys(state)
14
+ .filter((key) => !key.startsWith('_')),
15
15
 
16
16
  // get value of specific filter
17
- GET_FILTER: (state) => (filter) => {
18
- if (!state[filter]) return null;
19
- const { value, storedProp, multiple } = state[filter];
20
- if (multiple) return value.map((item) => item[storedProp]); // if arr, map
21
- if (storedProp) return value[storedProp]; // if object and has specific prop, return this prop
22
- return value; // else return val
17
+ GET_FILTER: (state) => (filterName) => {
18
+ const filter = state[filterName];
19
+ if (!filter) throw new Error(`Unknown filter: ${filterName}`);
20
+
21
+ return filter.get();
23
22
  },
24
23
 
25
24
  // get all filters values
26
- GET_FILTERS: (state, getters) => Object.keys(state)
27
- .reduce((filters, filterKey) => {
28
- const filterValue = getters.GET_FILTER(filterKey);
25
+ GET_FILTERS: (state, getters) => getters._STATE_FILTER_NAMES
26
+ .reduce((values, filterName) => {
27
+ const filterValue = getters.GET_FILTER(filterName);
29
28
  return isEmpty(filterValue) ? filters : {
30
- ...filters,
31
- [filterKey]: filterValue,
29
+ ...values,
30
+ [filterName]: filterValue,
32
31
  };
33
32
  }, {}),
34
-
35
- // get value of filter from query string by query key
36
- GET_VALUE_FROM_QUERY: (state, getters) => ({ filterQuery }) => (
37
- getters.ROUTER.currentRoute.value.query[filterQuery]
38
- ),
39
33
  };
40
34
 
41
35
  actions = {
42
36
  SET_FILTER: async (context, {
43
- filter,
37
+ name,
44
38
  value,
45
- source = SET_FILTER_SOURCE.DEFAULT,
46
39
  silent = false, // if true, don't call ON_FILTER_SET event
47
40
  }) => {
48
- const { multiple, defaultValue } = context.state[filter];
41
+ const filter = context.state[name];
49
42
 
50
- let newValue = value;
43
+ await filter.set(value);
51
44
 
52
- if (value) {
53
- if (multiple && !Array.isArray(value)) newValue = [value];
54
- } else if (value === null || value === undefined) {
55
- newValue = defaultValue;
56
- }
57
-
58
- await context.dispatch('SET_VALUE_TO_QUERY', {
59
- filterQuery: filter,
60
- value,
61
- storedProp: context.state[filter].storedProp,
45
+ if (!silent) await context.dispatch('EMIT', {
46
+ event: FilterEvent.FILTER_SET,
47
+ payload: {
48
+ name,
49
+ value: context.getters.GET_FILTER(name),
50
+ },
62
51
  });
63
- context.commit('SET_FILTER', { filter, value: newValue });
64
-
65
- if (source !== SET_FILTER_SOURCE.RESTORE && filter !== 'page') {
66
- await context.dispatch('SET_FILTER', {
67
- filter: 'page',
68
- value: 1,
69
- silent: true,
70
- });
71
- }
72
-
73
- if (!silent) await context.dispatch('ON_FILTER_SET', { filter, value });
74
52
  },
75
- SET_VALUE_TO_QUERY: async (
76
- context,
77
- { filterQuery, value, storedProp = 'id' },
78
- ) => {
79
- let newValue = '';
80
- if (Array.isArray(value)) {
81
- if (value.length && typeof value[0] !== 'object') {
82
- newValue = value;
83
- } else {
84
- newValue = value.map((item) => item[storedProp]);
85
- }
86
- } else if (typeof value === 'object' && value !== null) {
87
- newValue = value[storedProp];
88
- } else {
89
- newValue = value;
90
- }
91
53
 
92
- return context.dispatch('CHANGE_ROUTE_QUERY', ({
93
- value: newValue,
94
- filterQuery,
95
- }));
96
- },
54
+ RESTORE_FILTER: async (context, { name }) => {
55
+ const filter = context.state[name];
56
+ const value = filter.restore();
97
57
 
98
- CHANGE_ROUTE_QUERY: (context, { value, filterQuery }) => {
99
- if (deepEqual(context.getters.GET_VALUE_FROM_QUERY({ filterQuery }), value)) return;
100
- const query = { ...context.getters.ROUTER.currentRoute.value.query };
101
- query[filterQuery] = value;
102
- // eslint-disable-next-line consistent-return
103
- return context.getters.ROUTER.replace({
104
- name: context.getters.ROUTER.currentRoute.value.name,
105
- query,
106
- });
107
- },
108
-
109
- RESTORE_FILTER: async (context, { filter }) => {
110
- const query = context.getters.GET_VALUE_FROM_QUERY({ filterQuery: filter });
111
- // note: restore fn may still return value even if there's no query value
112
- // from localStorage, for instance
113
- const value = context.state[filter].restore
114
- ? context.state[filter].restore({ query, defaultValue: context.state[filter].defaultValue })
115
- : query;
116
58
  if (value) {
117
- if (filter
118
- === 'sort') await context.dispatch('RESTORE_SORT', { value });
119
- else if (filter
120
- === 'fields') await context.dispatch('RESTORE_FIELDS', { value });
121
59
  await context.dispatch('SET_FILTER', ({
122
- filter,
60
+ name,
123
61
  value,
124
- source: SET_FILTER_SOURCE.RESTORE,
62
+ silent: true,
125
63
  }));
126
- return true;
127
64
  }
128
- return false;
129
65
  },
130
66
 
131
67
  RESTORE_FILTERS: async (context) => {
132
- const restores = [];
133
- for (const filter of Object.keys(context.state)) {
134
- // collect restores in order to check if there were actually any restores
135
- // empty restore returns "false"
136
- const result = await context.dispatch('RESTORE_FILTER', { filter });
137
- restores.push(result);
138
- }
139
-
140
- // if no filters to restore, call LOAD_LIST manually
141
- if (restores.every((restore) => restore === false)) {
142
- context.dispatch('LOAD_DATA_LIST');
143
- }
144
- },
68
+ await Promise.allSettled(context.getters._STATE_FILTER_NAMES.map((name) => {
69
+ return context.dispatch('RESTORE_FILTER', { name });
70
+ }));
145
71
 
146
- RESET_FILTERS: (context) => Promise.allSettled(Object.keys(context.state)
147
- .map((filter) => {
148
- return context.dispatch('SET_FILTER', {
149
- filter,
150
- value: context.state[filter].defaultValue,
72
+ return context.dispatch('EMIT', {
73
+ event: FilterEvent.RESTORED,
74
+ payload: context.getters.GET_FILTERS,
151
75
  });
152
- })),
76
+ },
153
77
 
154
- ON_FILTER_SET: (
155
- context,
156
- { filter, value },
157
- ) => context.dispatch('LOAD_DATA_LIST'),
78
+ RESET_FILTERS: (context) => Promise.allSettled(
79
+ context.getters._FILTER_NAMES.map((name) => {
80
+ const filter = context.state[name];
158
81
 
159
- LOAD_DATA_LIST: (context) => context.dispatch(
160
- `${context.getters.TABLE_NAMESPACE}/LOAD_DATA_LIST`,
161
- { filters: context.getters.GET_FILTERS },
162
- { root: true },
163
- ),
164
- RESTORE_SORT: (context, { value }) => context.dispatch(
165
- `${context.getters.TABLE_NAMESPACE}/RESTORE_SORT_FROM_FILTER`,
166
- { value },
167
- { root: true },
168
- ),
169
- RESTORE_FIELDS: (context, { value }) => context.dispatch(
170
- `${context.getters.TABLE_NAMESPACE}/RESTORE_FIELDS_FROM_FILTER`,
171
- { value },
172
- { root: true },
82
+ return context.dispatch('SET_FILTER', {
83
+ filter,
84
+ value: filter.defaultValue,
85
+ });
86
+ }),
173
87
  ),
174
- };
175
88
 
176
- mutations = {
177
- SET_FILTER: (state, { filter, value }) => {
178
- state[filter].value = value;
89
+ SUBSCRIBE: (context, { event, callback }) => {
90
+ const subscribe = () => context.state._emitter.on(event, callback);
91
+ if (Array.isArray(event)) event.forEach((e) => subscribe(e, callback));
92
+ else subscribe(event, callback);
93
+ },
94
+
95
+ FLUSH_SUBSCRIBERS: (context) => {
96
+ return context.state._emitter.off('*');
97
+ },
98
+
99
+ EMIT: async (context, { event, payload }) => {
100
+ return new Promise(async (resolve, reject) => {
101
+ const listeners = context.state._emitter.all.get(event);
102
+
103
+ if (!listeners) {
104
+ console.info(`No listeners for ${event} event`);
105
+ resolve();
106
+ }
107
+
108
+ try {
109
+ for (const listener of listeners) {
110
+ await listener({ event, payload });
111
+ }
112
+ resolve();
113
+ } catch (err) {
114
+ reject(err);
115
+ }
116
+ });
179
117
  },
180
118
  };
181
- }
119
+
120
+ addFilter(filter) {
121
+ const setup = (filter) => {
122
+ this.state[filter.name] = new BaseFilterSchema(filter);
123
+ };
124
+
125
+ if (Array.isArray(filter)) filter.forEach((f) => setup(f));
126
+ else setup(filter);
127
+
128
+ return this;
129
+ }
130
+ };
@@ -0,0 +1,157 @@
1
+ import { createRouter, createWebHistory } from 'vue-router';
2
+ import { createStore } from 'vuex';
3
+ import { valueGetter } from '../../scripts/getters';
4
+ import { queryRestore } from '../../scripts/restores';
5
+ import { querySetter, valueSetter } from '../../scripts/setters';
6
+ import FiltersStoreModule from '../FiltersStoreModule';
7
+
8
+ describe('FiltersStoreModule', () => {
9
+ it('get/sets primitive type filter', async () => {
10
+ const filters = new FiltersStoreModule().addFilter([
11
+ {
12
+ name: 'vi',
13
+ value: 1,
14
+ defaultValue: 1,
15
+ get: ['value'],
16
+ set: ['value'],
17
+ restore: [],
18
+ },
19
+ ]).getModule();
20
+
21
+ const store = createStore({
22
+ modules: {
23
+ filters,
24
+ },
25
+ });
26
+
27
+ const newValue = 2;
28
+
29
+ await store.dispatch('filters/SET_FILTER', {
30
+ name: 'vi',
31
+ value: newValue,
32
+ });
33
+
34
+ expect(store.getters['filters/GET_FILTER']('vi')).toBe(newValue);
35
+ });
36
+
37
+ it('sets/restores primitive type filter', async () => {
38
+ const localStorageKey = 'vivivi';
39
+
40
+ const filters = new FiltersStoreModule()
41
+ .addFilter({
42
+ name: 'vi',
43
+ value: 1,
44
+ localStorageKey,
45
+ get: ['value'],
46
+ set: ['value', 'localStorage'],
47
+ restore: ['localStorage'],
48
+ })
49
+ .getModule();
50
+
51
+ const store = createStore({
52
+ modules: {
53
+ filters,
54
+ },
55
+ });
56
+
57
+ await store.dispatch('filters/SET_FILTER', {
58
+ name: 'vi',
59
+ value: '2',
60
+ });
61
+
62
+ expect(localStorage.getItem(localStorageKey)).toBe('2');
63
+
64
+ await store.dispatch('filters/RESTORE_FILTER', { name: 'vi' });
65
+
66
+ expect(store.getters['filters/GET_FILTER']('vi')).toBe('2');
67
+ });
68
+
69
+ it('sets/restores primitive value from query using getters/setters as functions', async () => {
70
+ const router = createRouter({
71
+ history: createWebHistory(),
72
+ routes: [
73
+ {
74
+ name: 'home',
75
+ path: '/home',
76
+ component: {},
77
+ },
78
+ ],
79
+ });
80
+
81
+ await router.push({ name: 'home' });
82
+
83
+ const filters = new FiltersStoreModule()
84
+ .addFilter({
85
+ name: 'vi',
86
+ value: 1,
87
+ get: (context) => () => {
88
+ return valueGetter(context)();
89
+ },
90
+ set: (context) => async (v) => {
91
+ valueSetter(context)(v);
92
+ await querySetter(context)(router)(v);
93
+ return context;
94
+ },
95
+ restore: (context) => () => {
96
+ return queryRestore(context)(router)();
97
+ },
98
+ })
99
+ .getModule();
100
+
101
+ const store = createStore({
102
+ modules: {
103
+ filters,
104
+ },
105
+ });
106
+
107
+ await store.dispatch('filters/SET_FILTER', {
108
+ name: 'vi',
109
+ value: '2',
110
+ });
111
+
112
+ await store.dispatch('filters/RESTORE_FILTER', { name: 'vi' });
113
+
114
+ expect(store.getters['filters/GET_FILTER']('vi')).toBe('2');
115
+ });
116
+
117
+ it('sets/restores primitive value from query using getters/setters as strings', async () => {
118
+ const router = createRouter({
119
+ history: createWebHistory(),
120
+ routes: [
121
+ {
122
+ name: 'home',
123
+ path: '/home',
124
+ component: {},
125
+ },
126
+ ],
127
+ });
128
+
129
+ await router.push({ name: 'home' });
130
+
131
+ const filters = new FiltersStoreModule()
132
+ .addFilter({
133
+ name: 'vi',
134
+ value: 1,
135
+ get: ['value'],
136
+ set: ['value', 'query'],
137
+ restore: ['query'],
138
+ router,
139
+ })
140
+ .getModule();
141
+
142
+ const store = createStore({
143
+ modules: {
144
+ filters,
145
+ },
146
+ });
147
+
148
+ await store.dispatch('filters/SET_FILTER', {
149
+ name: 'vi',
150
+ value: '2',
151
+ });
152
+
153
+ await store.dispatch('filters/RESTORE_FILTER', { name: 'vi' });
154
+
155
+ expect(store.getters['filters/GET_FILTER']('vi')).toBe('2');
156
+ });
157
+ });
@@ -69,7 +69,7 @@ describe('features/notifications store: actions', () => {
69
69
  it('_SUBSCRIBE_TAB_CLOSING action commits SET_CURRENT_TAB_ID mutation', async () => {
70
70
  await notificationsModule.actions._SUBSCRIBE_TAB_CLOSING(context);
71
71
  window.dispatchEvent(new Event('storage'));
72
- expect(context.commit).toHaveBeenCalledWith('SET_CURRENT_TAB_ID', null);
72
+ expect(context.commit).toHaveBeenCalledWith('SET_CURRENT_TAB_ID', 'null');
73
73
  });
74
74
 
75
75
  it('PLAY_SOUND action commits SET_CURRENTLY_PLAYING mutation with sound', async () => {
@@ -10,6 +10,8 @@ export const useTableStore = (namespace) => {
10
10
 
11
11
  const dataList = computed(() => getNamespacedState(store.state, tableNamespace).dataList);
12
12
 
13
+ const selected = computed(() => getNamespacedState(store.state, tableNamespace).selected);
14
+
13
15
  const isLoading = computed(() => getNamespacedState(store.state, tableNamespace).isLoading);
14
16
 
15
17
  const headers = computed(() => getNamespacedState(store.state, tableNamespace).headers);
@@ -17,33 +19,35 @@ export const useTableStore = (namespace) => {
17
19
  const isNext = computed(() => getNamespacedState(store.state, tableNamespace).isNextPage);
18
20
 
19
21
  const error = computed(() => getNamespacedState(store.state, tableNamespace).errors);
20
- async function loadData(payload) {
22
+
23
+ function loadData(payload) {
21
24
  return store.dispatch(`${tableNamespace}/LOAD_DATA_LIST`, payload);
22
25
  }
23
26
 
24
- async function patchProperty(payload) {
27
+ function patchProperty(payload) {
25
28
  return store.dispatch(`${tableNamespace}/PATCH_ITEM_PROPERTY`, payload);
26
29
  }
27
30
 
28
- async function deleteData(payload) {
31
+ function deleteData(payload) {
29
32
  return store.dispatch(`${tableNamespace}/DELETE`, payload);
30
33
  }
31
34
 
32
- async function sort(...params) {
35
+ function sort([header, nextSortOrder]) {
33
36
  return store.dispatch(`${tableNamespace}/SORT`, {
34
- header: params[0],
35
- nextSortOrder: params[1],
37
+ header,
38
+ nextSortOrder,
36
39
  });
37
40
  }
38
41
 
39
- async function setHeaders(payload) {
40
- return store.dispatch(`${tableNamespace}/SET_HEADERS`, payload);
42
+ function setSelected(payload) {
43
+ return store.dispatch(`${tableNamespace}/SET_SELECTED`, payload);
41
44
  }
42
45
 
43
46
  return {
44
47
  namespace: tableNamespace,
45
48
 
46
49
  dataList,
50
+ selected,
47
51
  isLoading,
48
52
  headers,
49
53
  isNext,
@@ -53,6 +57,6 @@ export const useTableStore = (namespace) => {
53
57
  patchProperty,
54
58
  deleteData,
55
59
  sort,
56
- setHeaders,
60
+ setSelected,
57
61
  };
58
62
  };