@webitel/ui-sdk 3.1.79 → 3.1.81

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": "3.1.79",
3
+ "version": "3.1.81",
4
4
  "private": false,
5
5
  "scripts": {
6
6
  "serve-lib": "vue-cli-service serve",
@@ -1,4 +1,5 @@
1
- import { shallowMount } from '@vue/test-utils';
1
+ import { mount } from '@vue/test-utils';
2
+ import { ref } from 'vue';
2
3
  import AuditForm from '../audit-form.vue';
3
4
  import { generateQuestionSchema } from '../../schemas/AuditFormQuestionSchema';
4
5
 
@@ -8,11 +9,11 @@ import {
8
9
 
9
10
  jest.mock('../../composables/useDestroyableSortable');
10
11
 
11
- useDestroyableSortable.mockImplementation(() => ({}));
12
+ useDestroyableSortable.mockImplementation(() => ({ reloadSortable: ref(false) }));
12
13
 
13
14
  describe('AuditForm', () => {
14
- it('renders a component', () => {
15
- const wrapper = shallowMount(AuditForm, {
15
+ it('renders a component', async () => {
16
+ const wrapper = mount(AuditForm, {
16
17
  props: {
17
18
  mode: '',
18
19
  questions: [],
@@ -20,47 +21,51 @@ describe('AuditForm', () => {
20
21
  });
21
22
  expect(wrapper.isVisible()).toBe(true);
22
23
  });
23
- it('add question button triggers update question event', () => {
24
- const wrapper = shallowMount(AuditForm, {
24
+ it('add question button triggers update question event', async () => {
25
+ const wrapper = mount(AuditForm, {
25
26
  props: {
26
27
  mode: 'create',
27
28
  questions: [],
28
29
  },
29
30
  });
30
- wrapper.findComponent('.audit-form__add-button').vm.$emit('click');
31
+ await wrapper.findComponent('.audit-form__add-button').vm.$emit('click');
32
+ await wrapper.vm.$nextTick();
33
+ await wrapper.vm.$nextTick();
34
+ await wrapper.vm.$nextTick();
35
+ console.info(wrapper.html());
31
36
  expect(wrapper.emitted()['update:questions'][0][0])
32
37
  .toEqual([generateQuestionSchema({ required: true })]);
33
38
  });
34
- it('delete event from child question emits update without passed question', () => {
35
- const wrapper = shallowMount(AuditForm, {
39
+ it('delete event from child question emits update without passed question', async () => {
40
+ const wrapper = mount(AuditForm, {
36
41
  props: {
37
42
  mode: 'create',
38
43
  questions: [generateQuestionSchema()],
39
44
  },
40
45
  });
41
- wrapper.findComponent({ name: 'audit-form-question' }).vm.$emit('delete', { key: 0 });
46
+ await wrapper.findComponent({ name: 'audit-form-question' }).vm.$emit('delete', { key: 0 });
42
47
  expect(wrapper.emitted()['update:questions'][0][0])
43
48
  .toEqual([]);
44
49
  });
45
- it('copy event from child question emits update with duplicated questions', () => {
50
+ it('copy event from child question emits update with duplicated questions', async () => {
46
51
  const question = generateQuestionSchema();
47
- const wrapper = shallowMount(AuditForm, {
52
+ const wrapper = mount(AuditForm, {
48
53
  props: {
49
54
  mode: 'create',
50
55
  questions: [question],
51
56
  },
52
57
  });
53
- wrapper.findComponent({ name: 'audit-form-question' }).vm.$emit('copy', { question, key: 0 });
58
+ await wrapper.findComponent({ name: 'audit-form-question' }).vm.$emit('copy', { question, key: 0 });
54
59
  expect(wrapper.emitted()['update:questions'][0][0])
55
60
  .toEqual([question, question]);
56
61
  });
57
- it('initializes result depending on passed questions', () => {
58
- const wrapper = shallowMount(AuditForm, {
62
+ it('initializes result depending on passed questions',() => {
63
+ const wrapper = mount(AuditForm, {
59
64
  props: {
60
65
  mode: 'fill',
61
66
  questions: [generateQuestionSchema(), generateQuestionSchema(), generateQuestionSchema()],
62
67
  },
63
68
  });
64
- expect(wrapper.emitted()['update:result'][0][0]).toEqual([null, null, null]);
69
+ expect(wrapper.emitted()['update:result'][0][0]).toEqual([{}, {}, {}]);
65
70
  });
66
71
  });
@@ -2,7 +2,7 @@
2
2
  <article
3
3
  class="audit-form-question-read"
4
4
  :class="{
5
- 'audit-form-question-read--filled': result && result.score != null,
5
+ 'audit-form-question-read--filled': isResult && result.score != null,
6
6
  'audit-form-question-read--readonly': readonly,
7
7
  }"
8
8
  @keyup.enter="emit('activate')"
@@ -17,7 +17,7 @@
17
17
  >{{ question.question }}</p>
18
18
  <wt-icon
19
19
  class="audit-form-question-read__drag-icon"
20
- v-if="!disableDragging"
20
+ v-if="!disableDragging && !first"
21
21
  icon="move"
22
22
  color="secondary"
23
23
  ></wt-icon>
@@ -37,6 +37,7 @@
37
37
  <script setup>
38
38
  import { computed } from 'vue';
39
39
  import { EngineAuditQuestionType } from 'webitel-sdk';
40
+ import isEmpty from '../../../scripts/isEmpty';
40
41
  import AuditFormQuestionOptions from './questions/options/audit-form-question-options.vue';
41
42
  import AuditFormQuestionScore from './questions/score/audit-form-question-score.vue';
42
43
  import WtIcon from '../../../components/atoms/wt-icon/wt-icon.vue';
@@ -58,6 +59,10 @@ const props = defineProps({
58
59
  type: Boolean,
59
60
  default: false,
60
61
  },
62
+ first: {
63
+ type: Boolean,
64
+ default: false,
65
+ },
61
66
  });
62
67
 
63
68
  const emit = defineEmits([
@@ -70,6 +75,8 @@ const QuestionTypeComponent = computed(() => {
70
75
  if (props.question.type === EngineAuditQuestionType.Score) return AuditFormQuestionScore;
71
76
  return null;
72
77
  });
78
+
79
+ const isResult = computed(() => !isEmpty(props.result));
73
80
  </script>
74
81
 
75
82
  <style lang="scss" scoped>
@@ -5,7 +5,7 @@
5
5
  :class="[
6
6
  `audit-form-question--mode-${mode}`,
7
7
  {
8
- 'audit-form-question--answered': !!result,
8
+ 'audit-form-question--answered': isResult,
9
9
  },
10
10
  ]"
11
11
  :disable-dragging="mode === 'fill'"
@@ -27,6 +27,7 @@
27
27
  import { useVuelidate } from '@vuelidate/core';
28
28
  import { required } from '@vuelidate/validators';
29
29
  import { computed, onMounted, ref, toRefs } from 'vue';
30
+ import isEmpty from '../../../scripts/isEmpty';
30
31
  import vClickaway from '../../../directives/clickaway/clickaway';
31
32
  import QuestionRead from './audit-form-question-read-wrapper.vue';
32
33
  import QuestionWrite from './audit-form-question-write-wrapper.vue';
@@ -90,6 +91,8 @@ const component = computed(() => {
90
91
  return QuestionRead;
91
92
  });
92
93
 
94
+ const isResult = computed(() => !isEmpty(props.result));
95
+
93
96
  function saveQuestion() {
94
97
  state.value = QuestionState.SAVED;
95
98
  }
@@ -99,10 +102,11 @@ function activateQuestion() {
99
102
  state.value = QuestionState.EDIT;
100
103
  }
101
104
 
105
+ defineExpose({ activateQuestion });
106
+
102
107
  // initialize validations
103
108
  onMounted(() => {
104
109
  v$.value.$touch();
105
- if (props.first) activateQuestion();
106
110
  });
107
111
  </script>
108
112
 
@@ -7,12 +7,14 @@
7
7
  >
8
8
  <audit-form-question
9
9
  v-for="(question, key) of questions"
10
+ ref="auditQuestions"
10
11
  :key="key"
11
12
  :question="question"
12
13
  :result="(result && result[key]) ? result[key] : null"
13
14
  :mode="mode"
14
15
  :first="key === 0"
15
16
  :readonly="readonly"
17
+ :class="{'audit-form-question--sort-ignore': key === 0}"
16
18
  @copy="copyQuestion({ question, key })"
17
19
  @delete="deleteQuestion({ question, key})"
18
20
  @update:question="handleQuestionUpdate({ key, value: $event })"
@@ -33,6 +35,7 @@
33
35
  import cloneDeep from 'lodash/cloneDeep';
34
36
  import {
35
37
  watch, watchEffect, ref, computed, onMounted,
38
+ reactive, nextTick,
36
39
  } from 'vue';
37
40
  import { useVuelidate } from '@vuelidate/core';
38
41
  import { useDestroyableSortable } from '../composables/useDestroyableSortable';
@@ -70,13 +73,17 @@ const emit = defineEmits([
70
73
  const v$ = useVuelidate();
71
74
 
72
75
  const isInvalidForm = computed(() => !!v$.value.$errors.length);
76
+ const auditQuestions = ref(null);
77
+ const isQuestionAdded = reactive({ value: false, index: null });
73
78
 
74
- function addQuestion({ index, question } = {}) {
79
+ async function addQuestion({ index, question } = {}) {
75
80
  const questions = [...props.questions];
76
81
  const newQuestion = question || generateQuestionSchema();
77
82
  if (index != null) questions.splice(index, 0, newQuestion);
78
83
  else questions.push(newQuestion);
79
- emit('update:questions', questions);
84
+ await emit('update:questions', questions);
85
+ isQuestionAdded.value = true;
86
+ isQuestionAdded.index = index || 'last';
80
87
  }
81
88
 
82
89
  function handleQuestionUpdate({ key, value }) {
@@ -98,6 +105,7 @@ function deleteQuestion({ key }) {
98
105
  }
99
106
 
100
107
  function changeQuestionsOrder({ oldIndex, newIndex }) {
108
+ if (newIndex === 0) return;
101
109
  const questions = [...props.questions];
102
110
  const [el] = questions.splice(oldIndex, 1);
103
111
  questions.splice(newIndex, 0, el);
@@ -111,14 +119,28 @@ function handleResultUpdate({ key, value }) {
111
119
  }
112
120
 
113
121
  function initResult() {
114
- const result = props.questions.map(() => null);
122
+ const result = props.questions.map(() => ({}));
115
123
  emit('update:result', result);
116
124
  }
117
125
 
118
126
  function initQuestions() {
119
127
  if (props.mode === 'create' && !props.questions.length) {
120
128
  addQuestion({ question: generateQuestionSchema({ required: true }) });
121
- }
129
+ } else if (props.questions.length) auditQuestions.value.at(0).activateQuestion();
130
+ }
131
+
132
+ // https://my.webitel.com/browse/WTEL-3451, https://my.webitel.com/browse/WTEL-3436
133
+ // at new question added, activate this question
134
+ async function atQuestionAdded() {
135
+ // wait for new question to render
136
+ await nextTick();
137
+ const index = isQuestionAdded.index && isQuestionAdded.index === 'last'
138
+ ? -1
139
+ : isQuestionAdded.index;
140
+ auditQuestions.value.at(index).activateQuestion();
141
+
142
+ isQuestionAdded.value = false;
143
+ isQuestionAdded.index = null;
122
144
  }
123
145
 
124
146
  const sortableWrapper = ref(null);
@@ -126,6 +148,7 @@ const sortableWrapper = ref(null);
126
148
  const { reloadSortable } = useDestroyableSortable(sortableWrapper, {
127
149
  handle: '.audit-form-question-read__drag-icon',
128
150
  disabled: props.mode !== 'create',
151
+ filter: '.audit-form-question--sort-ignore',
129
152
  onEnd: ({ newIndex, oldIndex }) => {
130
153
  if (newIndex === oldIndex) return;
131
154
  changeQuestionsOrder({ oldIndex, newIndex });
@@ -134,6 +157,10 @@ const { reloadSortable } = useDestroyableSortable(sortableWrapper, {
134
157
 
135
158
  watch(v$, () => emit('update:validation', { invalid: isInvalidForm.value, v$: v$.value }));
136
159
  watchEffect(initResult);
160
+ watch(() => props.questions, () => {
161
+ if (!isQuestionAdded.value) return;
162
+ atQuestionAdded();
163
+ });
137
164
 
138
165
  onMounted(() => {
139
166
  initQuestions();
@@ -34,7 +34,7 @@
34
34
  :key="value"
35
35
  :label="`${value}`"
36
36
  :value="value"
37
- :selected="result ? result.score : result"
37
+ :selected="isResult ? result.score : null"
38
38
  @input="emit('change:result', { score: value })"
39
39
  ></wt-radio>
40
40
  </div>
@@ -45,6 +45,7 @@
45
45
  import { useVuelidate } from '@vuelidate/core';
46
46
  import { maxValue, minValue, required } from '@vuelidate/validators';
47
47
  import { computed, onMounted, toRefs } from 'vue';
48
+ import isEmpty from '../../../../../scripts/isEmpty';
48
49
  import updateObject from '../../../../../scripts/updateObject';
49
50
  import WtRadio from '../../../../../components/molecules/wt-radio/wt-radio.vue';
50
51
  import WtInput from '../../../../../components/molecules/wt-input/wt-input.vue';
@@ -103,6 +104,8 @@ const scoreRange = computed(() => {
103
104
  return result;
104
105
  });
105
106
 
107
+ const isResult = computed(() => !isEmpty(props.result));
108
+
106
109
  function updateQuestion({ path, value }) {
107
110
  emit('change:question', updateObject({ obj: props.question, path, value }));
108
111
  }