@farm-investimentos/front-mfe-components 12.1.4 → 12.2.0

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.4",
3
+ "version": "12.2.0",
4
4
  "author": "farm investimentos",
5
5
  "private": false,
6
6
  "main": "./dist/front-mfe-components.common.js",
@@ -87,4 +87,10 @@ export const CustomTypography = () => ({
87
87
  <farm-bodytext :type="2">BodyText 2</farm-bodytext>
88
88
  </farm-chip>
89
89
  </div>`,
90
+ });
91
+
92
+ export const Ellipsis = () => ({
93
+ template: `<div style="width: 100px;">
94
+ <farm-chip color="primary">Very long text here</farm-chip>
95
+ </div>`,
90
96
  });
@@ -8,7 +8,7 @@
8
8
  'farm-chip--darken': variation === 'darken',
9
9
  }"
10
10
  >
11
- <farm-typography tag="span" size="sm" color="white"> <slot></slot> </farm-typography>
11
+ <farm-typography tag="span" size="sm" color="white" ellipsis> <slot></slot> </farm-typography>
12
12
  </span>
13
13
  </template>
14
14
  <script lang="ts">
@@ -1,3 +1,5 @@
1
+ @import '../../configurations/mixins';
2
+
1
3
  .farm-dialog-header__close {
2
4
  position: absolute;
3
5
  margin-top: 0;
@@ -26,8 +28,6 @@ header {
26
28
  align-items: center;
27
29
  width: 100%;
28
30
  margin-bottom: 0;
29
- white-space: nowrap;
30
- overflow: hidden;
31
- text-overflow: ellipsis;
31
+ @include ellipsis;
32
32
  }
33
33
  }
@@ -1,3 +1,5 @@
1
+ @import '../../configurations/mixins';
2
+
1
3
  .idcaption {
2
4
  display: flex;
3
5
  min-height: 48px;
@@ -34,9 +36,7 @@
34
36
 
35
37
  >span {
36
38
  max-width: 100%;
37
- white-space: nowrap;
38
- overflow: hidden;
39
- text-overflow: ellipsis;
39
+ @include ellipsis;
40
40
  }
41
41
  }
42
42
 
@@ -0,0 +1,5 @@
1
+ ::v-deep .farm-textfield--input {
2
+ input {
3
+ cursor: pointer !important;
4
+ }
5
+ }
@@ -1,104 +1,87 @@
1
1
  <template>
2
- <v-col cols="12" sm="12" :md="config.md ? config.md : 2" class="v-col-fieldset-default">
3
- <div class="v-text-field">
4
- <label :for="inputId">
5
- {{ label }}
6
- <span class="required" v-if="config.required">*</span>
7
- </label>
8
- <v-text-field
9
- append-icon="mdi-magnify "
10
- color="secondary"
11
- outlined
12
- v-on:keyup="clearTextField"
13
- dense
14
- readonly
15
- :id="inputId"
16
- :value="selectedValueText"
17
- @click="openModal"
18
- />
19
- </div>
20
- <v-dialog content-class="modal-default modal-default-small" v-model="showModal">
21
- <DialogHeader class="dialog-header" :title="modalTitle" @onClose="closeModal" />
2
+ <farm-col cols="12" :md="config.md ? config.md : 2">
3
+ <farm-label :for="inputId" :required="config.required">
4
+ {{ label }}
5
+ </farm-label>
6
+ <farm-textfield-v2
7
+ ref="searchTextField"
8
+ icon="magnify"
9
+ readonly
10
+ :id="inputId"
11
+ :value="selectedValueText"
12
+ @click="openModal"
13
+ @keyup="clearTextField"
14
+ />
22
15
 
23
- <v-main class="mt-9">
24
- <Loader class="text-center mb-2" v-if="isLoading" />
25
- <fieldset class="fieldset-default mx-4 mb-3" v-if="!isLoading">
26
- <label for="searchInput"> {{ label }} </label>
27
- <v-text-field
28
- color="secondary"
29
- id="searchInput"
30
- outlined
31
- dense
32
- hide-details
33
- :placeholder="placeholder"
34
- v-model="searchValue"
35
- />
36
- </fieldset>
16
+ <farm-modal v-model="showModal" size="sm" :offsetTop="48" :offsetBottom="68">
17
+ <template v-slot:header>
18
+ <farm-dialog-header :title="modalTitle" @onClose="closeModal" />
19
+ </template>
20
+ <template v-slot:content>
21
+ <div class="mx-n4">
22
+ <farm-box class="mt-3">
23
+ <farm-loader class="text-center mb-2" v-if="isLoading" />
24
+ <fieldset class="fieldset-default mx-4 mb-3" v-if="!isLoading">
25
+ <farm-label for="searchInput"> {{ label }} </farm-label>
26
+ <farm-textfield-v2
27
+ id="searchInput"
28
+ v-model="searchValue"
29
+ :placeholder="placeholder"
30
+ />
31
+ </fieldset>
37
32
 
38
- <v-data-table
39
- v-if="!isLoading"
40
- id="inputModalOptionsTable"
41
- class="v-data-table__clickable v-data-table__select-modal"
42
- hide-default-footer
43
- hide-default-header
44
- :items="items"
45
- :headers="headers"
46
- :options.sync="pagination"
47
- :search="searchValue"
48
- :custom-filter="customFilter"
49
- @click:row="handleClick"
50
- @pagination="handlePagination"
51
- >
52
- <template slot="no-data">
53
- <DataTableEmptyWrapper />
54
- </template>
55
- <template slot="no-results">
56
- <DataTableEmptyWrapper />
57
- </template>
58
- <template v-slot:[`item.label`]="{ item }">
59
- <td :title="getItemLabel(item)" aria-role="button">
60
- {{ getItemLabel(item) }}
61
- </td>
62
- </template>
33
+ <v-data-table
34
+ v-if="!isLoading"
35
+ id="inputModalOptionsTable"
36
+ class="v-data-table__clickable v-data-table__select-modal"
37
+ hide-default-footer
38
+ hide-default-header
39
+ :items="items"
40
+ :headers="headers"
41
+ :options.sync="pagination"
42
+ :search="searchValue"
43
+ :custom-filter="customFilter"
44
+ @click:row="handleClick"
45
+ @pagination="handlePagination"
46
+ >
47
+ <template slot="no-data">
48
+ <farm-emptywrapper />
49
+ </template>
50
+ <template slot="no-results">
51
+ <farm-emptywrapper />
52
+ </template>
53
+ <template v-slot:[`item.label`]="{ item }">
54
+ <td :title="getItemLabel(item)" aria-role="button">
55
+ {{ getItemLabel(item) }}
56
+ </td>
57
+ </template>
63
58
 
64
- <template v-slot:footer>
65
- <DataTablePaginator
66
- class="my-6"
67
- hidePerPageOptions
68
- :initialLimitPerPage="pagination.itemsPerPage"
69
- :page="pagination.page"
70
- :totalPages="pagination.pages"
71
- @onChangePage="onChangePage"
72
- />
73
- </template>
74
- </v-data-table>
75
- </v-main>
76
- <DialogFooter :hasConfirm="false" @onClose="closeModal" />
77
- </v-dialog>
78
- </v-col>
59
+ <template v-slot:footer>
60
+ <farm-datatable-paginator
61
+ class="my-6"
62
+ hidePerPageOptions
63
+ :initialLimitPerPage="pagination.itemsPerPage"
64
+ :page="pagination.page"
65
+ :totalPages="pagination.pages"
66
+ @onChangePage="onChangePage"
67
+ />
68
+ </template>
69
+ </v-data-table>
70
+ </farm-box>
71
+ </div>
72
+ </template>
73
+
74
+ <template v-slot:footer>
75
+ <farm-dialog-footer :hasConfirm="false" @onClose="closeModal" />
76
+ </template>
77
+ </farm-modal>
78
+ </farm-col>
79
79
  </template>
80
- <script>
80
+ <script lang="ts">
81
81
  import Vue from 'vue';
82
- import VTextField from 'vuetify/lib/components/VTextField';
83
- import { VCol } from 'vuetify/lib/components/VGrid';
84
- import { VMain } from 'vuetify/lib/components/VMain';
85
- import { VDataTable } from 'vuetify/lib/components/VDataTable/';
86
- import { VDialog } from 'vuetify/lib/components/VDialog/';
87
- import { DialogHeader, DialogFooter, DataTableEmptyWrapper, DataTablePaginator } from '../../main';
88
82
 
89
83
  export default Vue.extend({
90
84
  name: 'farm-select-modal-options',
91
- components: {
92
- VTextField,
93
- VCol,
94
- VMain,
95
- VDataTable,
96
- VDialog,
97
- DialogHeader,
98
- DialogFooter,
99
- DataTableEmptyWrapper,
100
- DataTablePaginator,
101
- },
102
85
  props: {
103
86
  /**
104
87
  * Input Label
@@ -248,8 +231,12 @@ export default Vue.extend({
248
231
  this.showModal = true;
249
232
  },
250
233
  closeModal() {
234
+ this.focusOnInput();
251
235
  this.showModal = false;
252
236
  },
237
+ focusOnInput() {
238
+ this.$refs.searchTextField.$el.firstChild.firstElementChild.focus();
239
+ },
253
240
  onChangePage(newPage) {
254
241
  this.pagination.page = newPage;
255
242
  },
@@ -263,6 +250,7 @@ export default Vue.extend({
263
250
  this.inputVal = item[this.selectIdentifier];
264
251
  this.showModal = false;
265
252
  this.selectedItem = item;
253
+ this.focusOnInput();
266
254
  },
267
255
  customFilter(_, search, item) {
268
256
  const label = this.getItemLabel(item);
@@ -277,11 +265,5 @@ export default Vue.extend({
277
265
  });
278
266
  </script>
279
267
  <style lang="scss" scoped>
280
- .v-text-field::v-deep {
281
- margin-top: 0;
282
- padding-top: 0;
283
- input {
284
- cursor: pointer;
285
- }
286
- }
268
+ @import 'SelectModalOptions.scss';
287
269
  </style>
@@ -19,6 +19,7 @@ describe('SelectModalOptions component', () => {
19
19
  },
20
20
  });
21
21
  component = wrapper.vm;
22
+ jest.spyOn(component, 'focusOnInput').mockImplementationOnce(() => {});
22
23
  });
23
24
 
24
25
  test('SelectModalOptions created', () => {
@@ -0,0 +1,62 @@
1
+ .farm-textarea {
2
+ min-height: 64px;
3
+
4
+ &--textarea {
5
+ display: flex;
6
+ align-items: center;
7
+ gap: 8px;
8
+ border: 1px solid;
9
+ border-color: var(--farm-gray-lighten);
10
+ min-height: 36px;
11
+ border-radius: 5px;
12
+ padding: 8px 0 0 8px;
13
+ margin-bottom: 4px;
14
+ background-color: white;
15
+ width: 100%;
16
+
17
+
18
+ &>textarea {
19
+ flex: 1;
20
+ outline: none;
21
+ color: var(--farm-text-primary);
22
+ font-size: 12px;
23
+ font-weight: 400;
24
+ max-width: 100%;
25
+ max-height: 100%;
26
+ }
27
+ }
28
+
29
+ &--disabled {
30
+ textarea {
31
+ color: var(--farm-gray-base);
32
+ }
33
+ }
34
+
35
+ .farm-caption {
36
+ line-height: 12px;
37
+ }
38
+ }
39
+
40
+ .farm-textarea--touched.farm-textarea--validatable {
41
+ &.farm-textarea--error {
42
+ .farm-textarea {
43
+ &--textarea {
44
+ border-color: var(--farm-error-base);
45
+
46
+ &>textarea {
47
+ color: var(--farm-neutral-darken);
48
+ }
49
+ }
50
+ }
51
+ }
52
+ }
53
+
54
+ .farm-textarea--blured.farm-textarea--validatable:not(.farm-textarea--error) {
55
+ .farm-textarea--textarea {
56
+ border-color: var(--farm-primary-base);
57
+
58
+ &>textarea {
59
+ color: var(--farm-neutral-darken);
60
+ }
61
+ }
62
+ }
@@ -0,0 +1,136 @@
1
+ import { withDesign } from 'storybook-addon-designs';
2
+ import TextArea from './TextArea.vue';
3
+
4
+ export default {
5
+ title: 'Form/TextArea',
6
+ component: TextArea,
7
+ decorators: [withDesign],
8
+ parameters: {
9
+ docs: {
10
+ description: {
11
+ component: `Text Area<br />
12
+ selector: <em>farm-textarea</em><br />
13
+ <span style="color: var(--farm-primary-base);">ready for use</span>
14
+ `,
15
+ },
16
+ },
17
+ design: {
18
+ type: 'figma',
19
+ url: 'https://www.figma.com/file/jnZXo2e0nRJ3fVXl4Et8t4/%E2%9C%8D-Design-System-%7C-BACKUP-(10%2F10%2F2022)?node-id=2491%3A4487',
20
+ },
21
+ viewMode: 'docs',
22
+ },
23
+ };
24
+
25
+ export const Primary = () => ({
26
+ data() {
27
+ return {
28
+ v: 'input text',
29
+ };
30
+ },
31
+ template: `<div style="width: 480px;">
32
+ <farm-textarea v-model="v" />
33
+ v-model: {{ v }}
34
+ </div>`,
35
+ });
36
+
37
+ export const Rows = () => ({
38
+ data() {
39
+ return {
40
+ v: 'input text',
41
+ };
42
+ },
43
+ template: `<div style="width: 480px;">
44
+ <farm-textarea v-model="v" rows="10" />
45
+ <farm-textarea v-model="v" rows="3" />
46
+ </div>`,
47
+ });
48
+
49
+ export const Disabled = () => ({
50
+ data() {
51
+ return {
52
+ v: 'input text',
53
+ };
54
+ },
55
+ template: `<div style="width: 480px">
56
+ <farm-textarea v-model="v" disabled />
57
+ </div>`,
58
+ });
59
+
60
+ export const Readonly = () => ({
61
+ data() {
62
+ return {
63
+ v: 'input text',
64
+ };
65
+ },
66
+ template: `<div style="width: 480px">
67
+ <farm-textarea v-model="v" readonly />
68
+ </div>`,
69
+ });
70
+
71
+ export const Validate = () => ({
72
+ data() {
73
+ return {
74
+ v1: 'input 1',
75
+ v2: '',
76
+ v4: '',
77
+ rules: {
78
+ required: value => !!value || 'Required field',
79
+ email: v =>
80
+ !v ||
81
+ /^\w+([.-]?\w+)*@\w+([.-]?\w+)*(\.\w{2,3})+$/.test(v) ||
82
+ 'Must be an e-mail',
83
+ },
84
+ };
85
+ },
86
+ template: `<div style="width: 480px">
87
+ <farm-label required>Required field</farm-label>
88
+ <farm-textarea v-model="v1" :rules="[rules.required]" />
89
+
90
+ <farm-label>E-mail</farm-label>
91
+ <farm-textarea v-model="v2" :rules="[rules.email]" />
92
+
93
+ <farm-label required>Required field with hint</farm-label>
94
+ <farm-textarea v-model="v4" :rules="[rules.required]" hint="hint text" />
95
+
96
+ </div>`,
97
+ });
98
+
99
+ export const HintText = () => ({
100
+ data() {
101
+ return {
102
+ v: 'input text',
103
+ };
104
+ },
105
+ template: `<div style="width: 480px; display: flex;">
106
+ <farm-textarea v-model="v" hint="Hint text" />
107
+ </div>`,
108
+ });
109
+
110
+ export const Reset = () => ({
111
+ data() {
112
+ return {
113
+ v: 'input text',
114
+ rules: {
115
+ required: value => !!value || 'Required field',
116
+ },
117
+ };
118
+ },
119
+ methods: {
120
+ reset() {
121
+ this.$refs.input.reset();
122
+ this.$refs.inputValidatable.reset();
123
+ },
124
+ },
125
+ template: `<div style="width: 480px">
126
+
127
+ <farm-label>Not Required</farm-label>
128
+ <farm-textarea v-model="v" ref="input" />
129
+
130
+ <farm-label required>Required</farm-label>
131
+ <farm-textarea v-model="v" ref="inputValidatable" :rules="[rules.required]" />
132
+
133
+ <farm-btn @click="reset">reset</farm-btn>
134
+
135
+ </div>`,
136
+ });
@@ -0,0 +1,179 @@
1
+ <template>
2
+ <div
3
+ class="farm-textarea"
4
+ :class="{
5
+ 'farm-textarea': true,
6
+ 'farm-textarea--validatable': rules.length > 0,
7
+ 'farm-textarea--touched': isTouched,
8
+ 'farm-textarea--blured': isBlured,
9
+ 'farm-textarea--error': hasError,
10
+ 'farm-textarea--disabled': disabled,
11
+ }"
12
+ >
13
+ <div :class="{
14
+ 'farm-textarea--textarea': true,
15
+ }">
16
+
17
+ <textarea
18
+ v-bind="$attrs"
19
+ v-model="innerValue"
20
+ :rows="$props.rows"
21
+ :disabled="disabled"
22
+ :readonly="readonly"
23
+ @click="$emit('click')"
24
+ @keyup="onKeyUp"
25
+ @blur="onBlur"
26
+ />
27
+ </div>
28
+ <farm-caption v-if="showErrorText" color="error" variation="regular">
29
+ {{ errorBucket[0] }}
30
+ </farm-caption>
31
+ <farm-caption v-if="hint && !showErrorText" color="gray" variation="regular">
32
+ {{ hint }}
33
+ </farm-caption>
34
+ </div>
35
+ </template>
36
+
37
+ <script lang="ts">
38
+ import Vue, { computed, onBeforeMount, PropType, ref, toRefs, watch } from 'vue';
39
+ import validateFormStateBuilder from '../../composition/validateFormStateBuilder';
40
+ import validateFormFieldBuilder from '../../composition/validateFormFieldBuilder';
41
+ import validateFormMethodBuilder from '../../composition/validateFormMethodBuilder';
42
+ import deepEqual from '../../composition/deepEqual';
43
+
44
+ export default Vue.extend({
45
+ name: 'farm-textarea',
46
+ inheritAttrs: true,
47
+ props: {
48
+ /**
49
+ * v-model binding
50
+ */
51
+ value: { type: [String, Number], default: '' },
52
+ /**
53
+ * Show hint text
54
+ */
55
+ hint: {
56
+ type: String,
57
+ default: null,
58
+ },
59
+ /**
60
+ * Disabled the input
61
+ */
62
+ disabled: {
63
+ type: Boolean,
64
+ default: false,
65
+ },
66
+ /**
67
+ * Puts input in readonly state
68
+ */
69
+ readonly: {
70
+ type: Boolean,
71
+ default: false,
72
+ },
73
+
74
+ errorMessage: String,
75
+ /**
76
+ * Array of rules used for validation
77
+ */
78
+ rules: {
79
+ type: Array as PropType<Array<Function>>,
80
+ default: () => [],
81
+ },
82
+ /**
83
+ * Textarea rows
84
+ */
85
+ rows: {
86
+ default: 5,
87
+ type: [String, Number],
88
+ }
89
+ },
90
+ setup(props, { emit }) {
91
+ const { rules } = toRefs(props);
92
+ const innerValue = ref(props.value);
93
+ const isTouched = ref(false);
94
+ const isBlured = ref(false);
95
+
96
+ const { errorBucket, valid, validatable } = validateFormStateBuilder();
97
+
98
+ let fieldValidator = validateFormFieldBuilder(rules.value);
99
+
100
+ const hasError = computed(() => {
101
+ return errorBucket.value.length > 0;
102
+ });
103
+
104
+ const showErrorText = computed(() => hasError.value && isTouched.value);
105
+
106
+ watch(
107
+ () => props.value,
108
+ () => {
109
+ innerValue.value = props.value;
110
+ validate(innerValue.value);
111
+ }
112
+ );
113
+
114
+ watch(
115
+ () => innerValue.value,
116
+ () => {
117
+ emit('input', innerValue.value);
118
+ emit('change', innerValue.value);
119
+ }
120
+ );
121
+
122
+ watch(
123
+ () => props.rules,
124
+ (newVal, oldVal) => {
125
+ if (deepEqual(newVal, oldVal)) return;
126
+ fieldValidator = validateFormFieldBuilder(rules.value);
127
+ validate = validateFormMethodBuilder(errorBucket, valid, fieldValidator);
128
+ validate(innerValue.value);
129
+ }
130
+ );
131
+
132
+ onBeforeMount(() => {
133
+ validate(innerValue.value);
134
+ });
135
+
136
+ let validate = validateFormMethodBuilder(errorBucket, valid, fieldValidator);
137
+
138
+ const onKeyUp = (event: Event) => {
139
+ isTouched.value = true;
140
+ emit('keyup', event);
141
+ };
142
+
143
+ const onBlur = (event: Event) => {
144
+ isBlured.value = true;
145
+ emit('blur', event);
146
+ };
147
+
148
+ const reset = () => {
149
+ innerValue.value = '';
150
+ isTouched.value = true;
151
+ emit('input', innerValue.value);
152
+ };
153
+
154
+ const makePristine = () => {
155
+ isTouched.value = false;
156
+ isBlured.value = false;
157
+ };
158
+
159
+ return {
160
+ innerValue,
161
+ errorBucket,
162
+ valid,
163
+ validatable,
164
+ hasError,
165
+ isTouched,
166
+ isBlured,
167
+ showErrorText,
168
+ validate,
169
+ onKeyUp,
170
+ onBlur,
171
+ reset,
172
+ makePristine,
173
+ };
174
+ },
175
+ });
176
+ </script>
177
+ <style lang="scss" scoped>
178
+ @import 'TextArea';
179
+ </style>