@farm-investimentos/front-mfe-components 15.6.0 → 15.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@farm-investimentos/front-mfe-components",
3
- "version": "15.6.0",
3
+ "version": "15.6.2",
4
4
  "author": "farm investimentos",
5
5
  "private": false,
6
6
  "main": "./dist/front-mfe-components.common.js",
@@ -12,6 +12,11 @@
12
12
  position: relative;
13
13
  cursor: pointer;
14
14
  }
15
+
16
+ &--plain {
17
+ cursor: default;
18
+ pointer-events: none;
19
+ }
15
20
  }
16
21
 
17
22
  &__content-title {
@@ -39,10 +39,10 @@ export const ColorIcons = () => ({
39
39
  <farm-row>
40
40
  <farm-col cols="12" md="4" v-for="color in colors" :key="'color_' + color">
41
41
  <h4 style="margin:15px">{{ color }}</h4>
42
- <farm-collapsible
42
+ <farm-collapsible
43
43
  style="margin-top:15px"
44
- icon="clipboard"
45
- title="color icon"
44
+ icon="clipboard"
45
+ title="color icon"
46
46
  :colorIcon="color"
47
47
  >
48
48
  </farm-collapsible>
@@ -101,14 +101,14 @@ export const ColorsChips = () => ({
101
101
  <farm-row>
102
102
  <farm-col cols="12" md="4" v-for="color in colors" :key="'color_' + color">
103
103
  <h4 style="margin:15px">{{ color }}</h4>
104
- <farm-collapsible
104
+ <farm-collapsible
105
105
  style="margin-top:15px"
106
106
  v-for="variation in variations"
107
- icon="plus"
107
+ icon="plus"
108
108
  textChip="chip"
109
- title="color chip"
109
+ title="color chip"
110
110
  :key="color + '_' + variation"
111
- :showChip="true"
111
+ :showChip="true"
112
112
  :colorChip="color"
113
113
  :variation="variation"
114
114
  >
@@ -128,12 +128,12 @@ export const ColorsOutlinedChips = () => ({
128
128
  <farm-row>
129
129
  <farm-col cols="12" md="4" v-for="color in colors" :key="'color_' + color">
130
130
  <h4 style="margin:15px">{{ color }}</h4>
131
- <farm-collapsible
131
+ <farm-collapsible
132
132
  style="margin-top:15px"
133
133
  v-for="variation in variations"
134
- icon="plus"
134
+ icon="plus"
135
135
  textChip="chip"
136
- title="color chip"
136
+ title="color chip"
137
137
  :key="color + '_' + variation"
138
138
  :showChip="true"
139
139
  :outlined="true"
@@ -161,14 +161,14 @@ export const Custom = () => ({
161
161
  <farm-col md="6" justify="end"
162
162
  align="end">
163
163
  <farm-btn
164
-
164
+
165
165
  >
166
166
  custom
167
167
  </farm-btn>
168
168
  </farm-col>
169
169
  </farm-row>
170
170
  </template>
171
- collapsible content
171
+ collapsible content
172
172
  </farm-collapsible>
173
173
  `,
174
174
  });
@@ -212,7 +212,7 @@ export const CustomHeaderContent = () => ({
212
212
  </farm-col>
213
213
  </farm-row>
214
214
  </template>
215
-
215
+
216
216
  <farm-bodytext >collapsible content</farm-bodytext>
217
217
  </farm-collapsible>
218
218
  `,
@@ -295,7 +295,7 @@ export const CustomHeaderAndBodyContent = () => ({
295
295
  </farm-row>
296
296
  </div>
297
297
  </template>
298
-
298
+
299
299
  <farm-row extraDecrease style="background-color:#f5f5f5;" class="mt-5 px-2">
300
300
  <farm-col class="collapsible-stories-class-with-line">
301
301
  <farm-caption variation="semiBold"
@@ -328,6 +328,51 @@ export const CustomHeaderAndBodyContent = () => ({
328
328
  `,
329
329
  });
330
330
 
331
+ export const Plain = () => ({
332
+ template: `
333
+ <farm-collapsible title="" custom-header plain>
334
+ <template #header-content>
335
+ <farm-row>
336
+ <farm-col md="12">
337
+ <farm-bodytext color="success">initial text</farm-bodytext>
338
+ </farm-col>
339
+ </farm-row>
340
+
341
+ <farm-row class="pt-4">
342
+ <farm-col class="collapsible-stories-class-with-line">
343
+ <farm-caption variation="semiBold"
344
+ >text here</farm-caption
345
+ >
346
+ 123455
347
+ </farm-col>
348
+ <farm-col class="collapsible-stories-class-with-line">
349
+ <farm-caption variation="semiBold"
350
+ >label here</farm-caption
351
+ >
352
+
353
+ <farm-caption variation="medium" class="my-1">R$ 1000</farm-caption>
354
+ </farm-col>
355
+
356
+ <farm-col class="collapsible-stories-class-with-line">
357
+ <farm-caption variation="semiBold">LABEL HERE</farm-caption>
358
+
359
+ <farm-caption variation="medium" class="my-1">R$ 1000</farm-caption>
360
+ </farm-col>
361
+
362
+ <farm-col>
363
+ <farm-caption variation="semiBold"
364
+ >Label here (text here)</farm-caption
365
+ >
366
+ <farm-caption variation="medium" class="my-1">value here</farm-caption>
367
+ </farm-col>
368
+ </farm-row>
369
+ </template>
370
+
371
+ <farm-bodytext >collapsible content</farm-bodytext>
372
+ </farm-collapsible>
373
+ `,
374
+ });
375
+
331
376
  Primary.storyName = 'Basic';
332
377
  Title.storyName = 'Title';
333
378
  Icon.storyName = 'Icon';
@@ -54,10 +54,11 @@
54
54
  <div
55
55
  v-if="customHeader"
56
56
  class="collapsible__header--custom"
57
+ :class="plain ? 'collapsible__header--plain' : ''"
57
58
  @click="onToggleCollapsible(status)"
58
59
  >
59
60
  <slot name="header-content"></slot>
60
- <div class="collapsible__content-right--custom">
61
+ <div v-if="!plain" class="collapsible__content-right--custom">
61
62
  <div class="collapsible__icon collapsible__icon--arrow">
62
63
  <farm-icon size="md" color="primary">
63
64
  {{ arrowIcon }}
@@ -125,12 +126,19 @@ export default defineComponent({
125
126
  default: '',
126
127
  },
127
128
  /**
128
- * has butotn
129
+ * has button
129
130
  */
130
131
  hasButton: {
131
132
  type: Boolean,
132
133
  default: false,
133
134
  },
135
+ /**
136
+ * plain layout
137
+ */
138
+ plain: {
139
+ type: Boolean,
140
+ default: false,
141
+ },
134
142
  /**
135
143
  * export button disabled toggle
136
144
  */
@@ -20,10 +20,16 @@ const styles = {
20
20
  vForm: {
21
21
  maxWidth: '480px',
22
22
  },
23
+ dynamicField: {
24
+ display: 'flex',
25
+ },
23
26
  footer: {
24
27
  display: 'flex',
25
28
  justifyContent: 'end',
26
29
  },
30
+ footerMargin: {
31
+ marginTop: '16px',
32
+ },
27
33
  };
28
34
 
29
35
  export const Primary = () => ({
@@ -141,6 +147,79 @@ export const Secondary = () => ({
141
147
  `,
142
148
  });
143
149
 
150
+ export const WithDynamicFields = () => ({
151
+ data() {
152
+ return {
153
+ form: {
154
+ document: null,
155
+ name: null,
156
+ checkbox: null,
157
+ birthDate: '',
158
+ selectId: null,
159
+ rangeDate: [],
160
+ dynamics: [],
161
+ },
162
+ validForm: true,
163
+ rules: {
164
+ required: value => !!value || 'Campo obrigatório',
165
+ checked: value => !!value || 'Deve estar marcado',
166
+ },
167
+ items: [
168
+ { value: null, text: '' },
169
+ { value: 1, text: 'label 1' },
170
+ { value: 2, text: 'label 2' },
171
+ ],
172
+ styles,
173
+ };
174
+ },
175
+ methods: {
176
+ addDynamic() {
177
+ const dynamic = {
178
+ name2: '',
179
+ selectId2: null,
180
+ };
181
+ this.form.dynamics.push({ ...dynamic });
182
+ this.$refs.form.restart();
183
+ },
184
+ removeDynamic(index) {
185
+ this.form.dynamics.splice(index, 1);
186
+ this.$refs.form.restart();
187
+ },
188
+ },
189
+ template: `
190
+ <div>
191
+ <farm-btn @click="addDynamic">Add field</farm-btn>
192
+
193
+ <farm-form v-model="validForm" :style="[styles.vForm]" ref="form">
194
+ <farm-label :required="true">Nome</farm-label>
195
+ <farm-textfield-v2 v-model="form.name" :rules="[rules.required]" />
196
+
197
+ <farm-label :required="true">Select</farm-label>
198
+ <farm-select :rules="[rules.required]" :items="items" v-model="form.selectId"/>
199
+
200
+ <div :style="[styles.dynamicField]" v-for="(dynamic, index) in form.dynamics">
201
+ <div>
202
+ <farm-label :key="index + 'label'" :required="true">Dynamic</farm-label>
203
+ <farm-textfield-v2 :key="index" v-model="dynamic.name2" :rules="[rules.required]" />
204
+ </div>
205
+
206
+ <farm-icon @click="removeDynamic(index)">delete</farm-icon>
207
+ </div>
208
+
209
+ Is valid form: {{ validForm }}
210
+
211
+ <farm-line />
212
+
213
+ <footer class="footer" :style="[styles.footer, styles.footerMargin]">
214
+ <farm-btn outlined @click="$refs.form.restart()" class="mr-3">Restart</farm-btn>
215
+ <farm-btn outlined @click="$refs.form.restartValidation()" class="mr-3">Restart Validation</farm-btn>
216
+ <farm-btn outlined @click="$refs.form.reset()" class="mr-3">Reset</farm-btn>
217
+ <farm-btn :disabled="!validForm">Salvar</farm-btn>
218
+ </footer>
219
+ </farm-form>
220
+ </div>`,
221
+ });
222
+
144
223
  export const InitInvalid = () => ({
145
224
  data() {
146
225
  return {
@@ -2,7 +2,7 @@
2
2
  <form class="farm-form"><slot></slot></form>
3
3
  </template>
4
4
  <script lang="ts">
5
- import { onMounted, reactive, ref, getCurrentInstance, defineComponent } from 'vue';
5
+ import { onMounted, ref, getCurrentInstance, defineComponent } from 'vue';
6
6
 
7
7
  type ErrorsBag = Record<number, boolean>;
8
8
 
@@ -14,13 +14,13 @@ export default defineComponent({
14
14
  inheritAttrs: true,
15
15
  setup(props, { emit }) {
16
16
  const innerValue = ref(props.value);
17
- let errorsBag = reactive({} as ErrorsBag);
17
+ let errorsBag = ref({} as ErrorsBag);
18
18
  let validationFields = [];
19
19
  const instance = getCurrentInstance().proxy;
20
20
 
21
21
  const dispatchError = () => {
22
- const keys = Object.keys(errorsBag);
23
- const errorsIds = keys.filter(key => !errorsBag[key]);
22
+ const keys = Object.keys(errorsBag.value);
23
+ const errorsIds = keys.filter(key => !errorsBag.value[key]);
24
24
  emit('input', errorsIds.length === 0);
25
25
  };
26
26
 
@@ -28,7 +28,7 @@ export default defineComponent({
28
28
  field.$watch(
29
29
  'hasError',
30
30
  () => {
31
- errorsBag[field._uid] = field.valid;
31
+ errorsBag.value[field._uid] = field.valid;
32
32
  dispatchError();
33
33
  },
34
34
  { immediate: true }
@@ -67,7 +67,7 @@ export default defineComponent({
67
67
 
68
68
  const restartValidation = () => {
69
69
  validationFields = [];
70
- errorsBag = {};
70
+ errorsBag.value = {};
71
71
  recursiveFormField(instance);
72
72
  validationFields.forEach(field => {
73
73
  watchInput(field);
@@ -78,10 +78,14 @@ export default defineComponent({
78
78
 
79
79
  const restart = () => {
80
80
  validationFields = [];
81
- errorsBag = {};
82
- recursiveFormField(instance);
83
- validationFields.forEach(field => {
84
- watchInput(field);
81
+ errorsBag.value = {};
82
+
83
+ instance.$nextTick(() => {
84
+ recursiveFormField(instance);
85
+
86
+ validationFields.forEach(field => {
87
+ watchInput(field);
88
+ });
85
89
  });
86
90
  };
87
91
 
@@ -1,14 +1,76 @@
1
1
  import { shallowMount } from '@vue/test-utils';
2
2
  import Form from '../Form';
3
+ import { formWithChildrenFactory, getDeepErrorsBag } from './helpers';
3
4
 
4
5
  describe('Form component', () => {
5
- let wrapper;
6
+ let wrapper, component;
6
7
 
7
8
  beforeEach(() => {
8
9
  wrapper = shallowMount(Form);
10
+ component = wrapper.vm;
9
11
  });
10
12
 
11
13
  test('Created hook', () => {
12
14
  expect(wrapper).toBeDefined();
13
15
  });
16
+
17
+ describe('errors bag', () => {
18
+ beforeEach(() => {
19
+ const helper = formWithChildrenFactory(Form);
20
+
21
+ wrapper = helper.wrapper;
22
+ component = helper.component;
23
+ });
24
+
25
+ it('should have errors bag after mount form', () => {
26
+ const { errorsBag, errorsBagLength } = getDeepErrorsBag(component);
27
+
28
+ expect(component.errorsBag).toStrictEqual(errorsBag.value);
29
+ expect(Object.keys(component.errorsBag).length).toBe(errorsBagLength);
30
+ });
31
+
32
+ it('should update errors bag after change validatable items', async () => {
33
+ const { errorsBag, errorsBagLength } = getDeepErrorsBag(component);
34
+
35
+ expect(component.errorsBag).toStrictEqual(errorsBag.value);
36
+ expect(Object.keys(component.errorsBag).length).toBe(errorsBagLength);
37
+
38
+ await component.addDynamic();
39
+ await component.restart();
40
+
41
+ const { errorsBag: plusOneErrorsBag, errorsBagLength: plusOneErrorsBagLength } =
42
+ getDeepErrorsBag(component);
43
+
44
+ expect(Object.keys(component.errorsBag).length).toBe(plusOneErrorsBagLength);
45
+ expect(component.errorsBag).toStrictEqual(plusOneErrorsBag.value);
46
+
47
+ await component.removeDynamic(1);
48
+ await component.restart();
49
+
50
+ const { errorsBag: minusOneErrorsBag, errorsBagLength: minusOneErrorsBagLength } =
51
+ getDeepErrorsBag(component);
52
+
53
+ expect(Object.keys(component.errorsBag).length).toBe(minusOneErrorsBagLength);
54
+ expect(component.errorsBag).toStrictEqual(minusOneErrorsBag.value);
55
+ });
56
+ });
57
+
58
+ describe('isValidForm', () => {
59
+ let isValidForm;
60
+
61
+ beforeEach(() => {
62
+ const helper = formWithChildrenFactory(Form);
63
+
64
+ wrapper = helper.wrapper;
65
+ component = helper.component;
66
+ isValidForm = helper.isValidForm;
67
+ });
68
+
69
+ it('should be invalid until all validatable fields are valid', () => {
70
+ const { errorsBag } = getDeepErrorsBag(component);
71
+
72
+ expect(isValidForm.value).toBeFalsy();
73
+ expect(Object.values(errorsBag.value)).toEqual([false, false, false]);
74
+ });
75
+ });
14
76
  });
@@ -0,0 +1,106 @@
1
+ import { mount } from '@vue/test-utils';
2
+ import { ref } from 'vue';
3
+
4
+ export const errorsBag = ref({});
5
+
6
+ export function getDeepErrorsBag($node) {
7
+ const getDeep = $node => {
8
+ $node.$children.forEach($leaf => {
9
+ if ($leaf.validate) {
10
+ errorsBag.value[$leaf._uid] = $leaf.valid;
11
+ } else if ($leaf.$children.length > 1) {
12
+ getDeep($leaf);
13
+ } else if ($leaf.$children[0] && $leaf.$children[0].validate) {
14
+ errorsBag.value[$leaf.$children[0]._uid] = $leaf.$children[0].valid;
15
+ } else if ($leaf.validatable) {
16
+ errorsBag.value[$leaf._uid] = $leaf.valid;
17
+ } else {
18
+ getDeep($leaf);
19
+ }
20
+ });
21
+ };
22
+
23
+ getDeep($node);
24
+
25
+ return {
26
+ errorsBag,
27
+ errorsBagLength: errorsBag.value ? Object.keys(errorsBag.value).length : 0,
28
+ };
29
+ }
30
+
31
+ export function resetErrorsBag() {
32
+ errorsBag.value = {};
33
+ }
34
+
35
+ function mountSlot() {
36
+ const normalComponent = {
37
+ props: {
38
+ form: {
39
+ type: Object,
40
+ required: true,
41
+ },
42
+ },
43
+ template: `
44
+ <div>
45
+ <farm-textfield-v2 :rules=[rules.required] />
46
+ <farm-textfield-v2 :rules=[rules.required] />
47
+ <farm-textfield-v2 :rules=[rules.required] />
48
+
49
+ section id="dynamics">
50
+ <farm-textfield-v2 v-for="(dynamic, index) in form?.dynamics" :key="index" :name="'dynamic-' + index" />
51
+ </section>
52
+ </div>
53
+ `,
54
+ };
55
+
56
+ return {
57
+ normalComponent,
58
+ };
59
+ }
60
+
61
+ export function formWithChildrenFactory(formComponent) {
62
+ resetErrorsBag();
63
+
64
+ const { normalComponent } = mountSlot();
65
+
66
+ const form = ref({
67
+ dynamics: [],
68
+ });
69
+ const isValidForm = ref(false);
70
+
71
+ const wrapper = mount(formComponent, {
72
+ propsData: {
73
+ value: isValidForm,
74
+ },
75
+ slots: {
76
+ default: '<normal-component :form="form" />',
77
+ },
78
+ mocks: {
79
+ form,
80
+ rules: {
81
+ required: value => !!value || 'Campo obrigatório',
82
+ },
83
+ addDynamic() {
84
+ form.value.dynamics.push({
85
+ name: '',
86
+ });
87
+
88
+ return form.value.dynamics;
89
+ },
90
+ removeDynamic(index) {
91
+ form.value.dynamics.splice(index, 1);
92
+
93
+ return form.value.dynamics;
94
+ },
95
+ },
96
+ stubs: {
97
+ 'normal-component': normalComponent,
98
+ },
99
+ });
100
+
101
+ return {
102
+ isValidForm,
103
+ wrapper,
104
+ component: wrapper.vm,
105
+ };
106
+ }