@farm-investimentos/front-mfe-components 12.1.0 → 12.1.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": "12.1.0",
3
+ "version": "12.1.2",
4
4
  "author": "farm investimentos",
5
5
  "private": false,
6
6
  "main": "./dist/front-mfe-components.common.js",
@@ -8,6 +8,7 @@
8
8
  .farm-contextmenu__popup {
9
9
  visibility: hidden;
10
10
  opacity: 0;
11
+ padding: 2px;
11
12
  position: absolute;
12
13
  display: block;
13
14
  overflow-y: auto;
@@ -18,11 +19,10 @@
18
19
  transform-origin: left top;
19
20
  transition: visibility 0.1s linear, opacity 0.1s linear;
20
21
 
21
- border-radius: 4px;
22
22
  left: 0;
23
23
 
24
- background: #FFFFFF;
25
- border: 1px solid var(--farm-stroke-base);
24
+ background: #ffffff;
25
+
26
26
  @include addShadow;
27
27
  border-radius: 5px;
28
28
 
@@ -30,4 +30,4 @@
30
30
  opacity: 1;
31
31
  visibility: visible;
32
32
  }
33
- }
33
+ }
@@ -4,10 +4,14 @@
4
4
  <slot name="activator"></slot>
5
5
  </span>
6
6
 
7
- <div ref="popup" :class="{
8
- 'farm-contextmenu__popup': true,
9
- 'farm-contextmenu__popup--visible': inputValue,
10
- }" :style="styles">
7
+ <div
8
+ ref="popup"
9
+ :class="{
10
+ 'farm-contextmenu__popup': true,
11
+ 'farm-contextmenu__popup--visible': inputValue,
12
+ }"
13
+ :style="styles"
14
+ >
11
15
  <slot></slot>
12
16
  </div>
13
17
  </div>
@@ -41,12 +45,19 @@ export default Vue.extend({
41
45
  type: [Number, String],
42
46
  default: 320,
43
47
  },
48
+ /**
49
+ * Should stay open when click inside
50
+ */
51
+ stayOpen: {
52
+ type: Boolean,
53
+ default: false,
54
+ },
44
55
  },
45
56
  setup(props, { emit }) {
46
57
  const parent = ref(null);
47
58
  const popup = ref(null);
48
59
  const activator = ref(null);
49
- const { bottom, maxHeight } = toRefs(props);
60
+ const { bottom, maxHeight, stayOpen } = toRefs(props);
50
61
 
51
62
  const styles = reactive({
52
63
  minWidth: 0,
@@ -59,8 +70,16 @@ export default Vue.extend({
59
70
 
60
71
  let hasBeenBoostrapped = false;
61
72
 
62
- const outClick = (event: Event) => {
63
- if (activator && !activator.value.contains(event.target)) {
73
+ const outClick = (event: Record<string, any>) => {
74
+ const isInside =
75
+ event.path.some((e: HTMLElement) => {
76
+ if (e.classList) {
77
+ return e.classList.contains('farm-contextmenu__popup--visible');
78
+ }
79
+ return false;
80
+ }) && stayOpen.value;
81
+
82
+ if (activator && !activator.value.contains(event.target) && !isInside) {
64
83
  emit('input', false);
65
84
  inputValue.value = false;
66
85
  }
@@ -15,4 +15,15 @@
15
15
  &--rotate {
16
16
  transform: rotate(180deg);
17
17
  }
18
- }
18
+ }
19
+
20
+ .farm-select__checkbox {
21
+ margin-right: 8px;
22
+ }
23
+
24
+ ::v-deep .farm-listitem:hover .farm-checkbox .farm-icon {
25
+ color: var(--farm-primary-lighten);
26
+ }
27
+ ::v-deep .farm-listitem:hover .farm-checkbox.farm-checkbox--checked .farm-icon {
28
+ color: white;
29
+ }
@@ -34,7 +34,7 @@ export const Primary = () => ({
34
34
  ],
35
35
  };
36
36
  },
37
- template: `<div style="width: 120px">
37
+ template: `<div style="width: 120px;">
38
38
  <farm-select v-model="v" :items="items" />
39
39
  v-model: {{ v }}
40
40
  </div>`,
@@ -195,3 +195,54 @@ export const NoItems = () => ({
195
195
  v-model: {{ v }}
196
196
  </div>`,
197
197
  });
198
+
199
+ export const Multiple = () => ({
200
+ data() {
201
+ return {
202
+ v: null,
203
+ items: [
204
+ { value: 0, text: 'value 0' },
205
+ { value: 1, text: 'value 1' },
206
+ { value: 2, text: 'value 2' },
207
+ { value: 3, text: 'value 3' },
208
+ ],
209
+ };
210
+ },
211
+ template: `<div style="width: 400px">
212
+ <farm-select v-model="v" :items="items" multiple />
213
+ v-model: {{ v }}
214
+ </div>`,
215
+ });
216
+
217
+ export const MultipleInitValue = () => ({
218
+ data() {
219
+ return {
220
+ v: [2, 3],
221
+ items: [
222
+ { id: 0, label: 'value 0' },
223
+ { id: 1, label: 'value 1' },
224
+ { id: 2, label: 'value 2' },
225
+ { id: 3, label: 'value 3' },
226
+ ],
227
+ };
228
+ },
229
+ methods: {
230
+ click() {
231
+ this.$refs.select.reset();
232
+ },
233
+ },
234
+ template: `<div style="width: 400px">
235
+ <farm-select
236
+ v-model="v"
237
+ item-value="id"
238
+ item-text="label"
239
+ ref="select"
240
+ multiple
241
+ :items="items"
242
+ />
243
+ v-model: {{ v }}
244
+ <farm-btn @click="click">
245
+ reset
246
+ </farm-btn>
247
+ </div>`,
248
+ });
@@ -11,7 +11,7 @@
11
11
  }"
12
12
  v-if="!readonly && !disabled"
13
13
  >
14
- <farm-contextmenu bottom v-model="isVisible">
14
+ <farm-contextmenu bottom v-model="isVisible" :stay-open="multiple">
15
15
  <farm-list v-if="!readonly">
16
16
  <farm-listitem
17
17
  v-for="(item, index) in items"
@@ -22,7 +22,21 @@
22
22
  :class="{ 'farm-listitem--selected': item[itemValue] === innerValue }"
23
23
  @click="selectItem(item)"
24
24
  >
25
- <farm-caption bold tag="span">{{ item[itemText] }}</farm-caption>
25
+ <farm-checkbox
26
+ class="farm-select__checkbox"
27
+ v-model="checked"
28
+ value="1"
29
+ size="sm"
30
+ v-if="isChecked(item)"
31
+ ></farm-checkbox>
32
+ <farm-checkbox
33
+ class="farm-select__checkbox"
34
+ v-model="checked"
35
+ value="2"
36
+ size="sm"
37
+ v-else-if="multiple"
38
+ ></farm-checkbox
39
+ ><farm-caption bold tag="span">{{ item[itemText] }}</farm-caption>
26
40
  </farm-listitem>
27
41
  <farm-listitem v-if="!items || items.length === 0">
28
42
  {{ noDataText }}
@@ -68,7 +82,7 @@ export default Vue.extend({
68
82
  /**
69
83
  * v-model binding
70
84
  */
71
- value: { type: [String, Number], default: '' },
85
+ value: { type: [String, Number, Array], default: '' },
72
86
  hint: {
73
87
  type: String,
74
88
  default: null,
@@ -123,14 +137,24 @@ export default Vue.extend({
123
137
  type: String,
124
138
  default: 'Não há dados',
125
139
  },
140
+ /**
141
+ * Set a multiple select
142
+ */
143
+ multiple: {
144
+ type: Boolean,
145
+ default: false,
146
+ },
126
147
  },
127
148
  setup(props, { emit }) {
128
- const { rules, items, itemText, itemValue, disabled } = toRefs(props);
149
+ const { rules, items, itemText, itemValue, disabled, multiple } = toRefs(props);
129
150
  const innerValue = ref(props.value);
130
151
  const isTouched = ref(false);
131
152
  const isBlured = ref(false);
132
153
  const isVisible = ref(false);
133
154
  const selectedText = ref('');
155
+ const multipleValues = ref(Array.isArray(props.value) ? [...props.value] : []);
156
+ const checked = ref('1');
157
+ const notChecked = ref(false);
134
158
 
135
159
  const { errorBucket, valid, validatable } = validateFormStateBuilder();
136
160
 
@@ -169,6 +193,7 @@ export default Vue.extend({
169
193
  () => {
170
194
  isTouched.value = true;
171
195
  isBlured.value = true;
196
+ validate(innerValue.value);
172
197
  emit('input', innerValue.value);
173
198
  emit('change', innerValue.value);
174
199
  }
@@ -193,8 +218,13 @@ export default Vue.extend({
193
218
  return;
194
219
  }
195
220
  innerValue.value = null;
221
+ multipleValues.value = [];
196
222
  selectedText.value = '';
197
223
  isTouched.value = true;
224
+ if (multiple.value) {
225
+ innerValue.value = [];
226
+ return;
227
+ }
198
228
  emit('input', innerValue.value);
199
229
  };
200
230
 
@@ -209,7 +239,21 @@ export default Vue.extend({
209
239
  };
210
240
 
211
241
  const selectItem = item => {
212
- selectedText.value = item[itemText.value];
242
+ if (multiple.value) {
243
+ const alreadyAdded = multipleValues.value.findIndex(
244
+ val => val === item[itemValue.value]
245
+ );
246
+ checked.value = '1';
247
+ if (alreadyAdded !== -1) {
248
+ multipleValues.value.splice(alreadyAdded, 1);
249
+ } else {
250
+ multipleValues.value.push(item[itemValue.value]);
251
+ }
252
+ innerValue.value = [...multipleValues.value];
253
+
254
+ return;
255
+ }
256
+
213
257
  innerValue.value = item[itemValue.value];
214
258
  isVisible.value = false;
215
259
  };
@@ -224,16 +268,51 @@ export default Vue.extend({
224
268
  };
225
269
 
226
270
  const updateSelectedTextValue = () => {
227
- if (!items.value || items.value.length === 0 || innerValue.value === null) {
271
+ if (
272
+ !items.value ||
273
+ items.value.length === 0 ||
274
+ innerValue.value === null ||
275
+ (multiple.value && multipleValues.value.length === 0)
276
+ ) {
228
277
  selectedText.value = '';
229
278
  return;
230
279
  }
231
280
  const selectedItem = items.value.find(
232
281
  item => item[itemValue.value] == innerValue.value
233
282
  );
283
+
234
284
  if (selectedItem) {
235
285
  selectedText.value = selectedItem[itemText.value];
236
286
  }
287
+
288
+ addLabelToMultiple();
289
+ };
290
+
291
+ const addLabelToMultiple = () => {
292
+ if (multiple.value && Array.isArray(innerValue.value) && innerValue.value.length > 0) {
293
+ const labelItem = items.value.find(
294
+ item => item[itemValue.value] === innerValue.value[0]
295
+ );
296
+
297
+ if (innerValue.value.length === 0) {
298
+ selectedText.value = '';
299
+ return;
300
+ } else if (innerValue.value.length === 1) {
301
+ selectedText.value = labelItem[itemText.value];
302
+ return;
303
+ }
304
+
305
+ selectedText.value = `${labelItem[itemText.value]} (+${
306
+ innerValue.value.length - 1
307
+ } ${innerValue.value.length - 1 === 1 ? 'outro' : 'outros'})`;
308
+ }
309
+ };
310
+
311
+ const isChecked = item => {
312
+ return (
313
+ multiple.value &&
314
+ multipleValues.value.findIndex(val => val === item[itemValue.value]) !== -1
315
+ );
237
316
  };
238
317
 
239
318
  return {
@@ -256,6 +335,11 @@ export default Vue.extend({
256
335
  clickInput,
257
336
  updateSelectedTextValue,
258
337
  makePristine,
338
+ checked,
339
+ notChecked,
340
+ isChecked,
341
+ multipleValues,
342
+ addLabelToMultiple,
259
343
  };
260
344
  },
261
345
  });
@@ -47,5 +47,82 @@ describe('Select component', () => {
47
47
  component.makePristine();
48
48
  expect(component.isTouched).toBeFalsy();
49
49
  });
50
+
51
+ describe('isChecked', () => {
52
+ it('should return false when multiple is false', async () => {
53
+ component.multipleValues = [0, 1, 2];
54
+ const result = component.isChecked({ value: 1 });
55
+ expect(result).toBe(false);
56
+ });
57
+ it('should return true when multiple is true and item is checked', async () => {
58
+ await wrapper.setProps({
59
+ multiple: true,
60
+ });
61
+ component.multipleValues = [0, 1, 2];
62
+ const result = component.isChecked({ value: 1 });
63
+ expect(result).toBe(true);
64
+ });
65
+ it('should return false when item is not checked', async () => {
66
+ await wrapper.setProps({
67
+ multiple: true,
68
+ });
69
+ component.multipleValues = [0, 1, 2];
70
+ const result = component.isChecked({ value: 3 });
71
+ expect(result).toBe(false);
72
+ });
73
+ });
74
+ describe('addLabelToMultiple', () => {
75
+ it('should not do anything when multiple is false', async () => {
76
+ component.addLabelToMultiple();
77
+
78
+ expect(component.selectedText).toBe('');
79
+ });
80
+ it('should return a selectedText to a selected item', async () => {
81
+ await wrapper.setProps({
82
+ multiple: true,
83
+ items: [
84
+ { value: 0, text: 'value 0' },
85
+ { value: 1, text: 'value 1' },
86
+ { value: 2, text: 'value 2' },
87
+ { value: 3, text: 'value 3' },
88
+ ],
89
+ value: [0],
90
+ });
91
+
92
+ component.addLabelToMultiple();
93
+ expect(component.selectedText).toBe('value 0');
94
+ });
95
+ it('should return a selectedText to two selected item', async () => {
96
+ await wrapper.setProps({
97
+ multiple: true,
98
+ items: [
99
+ { value: 0, text: 'value 0' },
100
+ { value: 1, text: 'value 1' },
101
+ { value: 2, text: 'value 2' },
102
+ { value: 3, text: 'value 3' },
103
+ ],
104
+ value: [0, 1],
105
+ });
106
+
107
+ component.addLabelToMultiple();
108
+ expect(component.selectedText).toBe('value 0 (+1 outro)');
109
+ });
110
+
111
+ it('should return a selectedText to three or more selected item', async () => {
112
+ await wrapper.setProps({
113
+ multiple: true,
114
+ items: [
115
+ { value: 0, text: 'value 0' },
116
+ { value: 1, text: 'value 1' },
117
+ { value: 2, text: 'value 2' },
118
+ { value: 3, text: 'value 3' },
119
+ ],
120
+ value: [0, 1, 2],
121
+ });
122
+
123
+ component.addLabelToMultiple();
124
+ expect(component.selectedText).toBe('value 0 (+2 outros)');
125
+ });
126
+ });
50
127
  });
51
128
  });
@@ -11,6 +11,7 @@
11
11
  border-radius: 5px;
12
12
  padding: 8px;
13
13
  margin-bottom: 4px;
14
+ background-color: white;
14
15
 
15
16
  >button {
16
17
  display: flex;
@@ -19,7 +19,7 @@ export default {
19
19
  docs: {
20
20
  description: {
21
21
  component: `Text field v2<br />
22
- selector: <em>farm-texfield-v2</em><br />
22
+ selector: <em>farm-textfield-v2</em><br />
23
23
  <span style="color: var(--farm-primary-base);">ready for use</span>
24
24
  `,
25
25
  },
@@ -38,7 +38,7 @@ export const Primary = () => ({
38
38
  v: 'input text',
39
39
  };
40
40
  },
41
- template: `<div style="width: 480px">
41
+ template: `<div style="width: 480px;">
42
42
  <farm-textfield-v2 v-model="v" />
43
43
  v-model: {{ v }}
44
44
  </div>`,