@farm-investimentos/front-mfe-components 9.3.1 → 9.4.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.
Files changed (42) hide show
  1. package/dist/front-mfe-components.common.js +558 -118
  2. package/dist/front-mfe-components.common.js.map +1 -1
  3. package/dist/front-mfe-components.css +2 -2
  4. package/dist/front-mfe-components.umd.js +558 -118
  5. package/dist/front-mfe-components.umd.js.map +1 -1
  6. package/dist/front-mfe-components.umd.min.js +1 -1
  7. package/dist/front-mfe-components.umd.min.js.map +1 -1
  8. package/package.json +1 -1
  9. package/src/components/Card/Card.stories.js +2 -1
  10. package/src/components/Checkbox/Checkbox.stories.js +2 -1
  11. package/src/components/Checkbox/Checkbox.vue +52 -4
  12. package/src/components/Checkbox/__tests__/Checkbox.spec.js +5 -1
  13. package/src/components/Chip/Chip.scss +9 -0
  14. package/src/components/Form/Form.stories.js +146 -0
  15. package/src/components/Form/Form.vue +69 -0
  16. package/src/components/Form/__tests__/Form.spec.js +16 -0
  17. package/src/components/Form/index.ts +4 -0
  18. package/src/components/Loader/Loader.stories.ts +2 -1
  19. package/src/components/Logger/Logger.stories.js +2 -1
  20. package/src/components/Modal/Modal.stories.js +1 -0
  21. package/src/components/RadioGroup/IRadioGroup.ts +6 -0
  22. package/src/components/RadioGroup/RadioGroup.scss +82 -0
  23. package/src/components/RadioGroup/RadioGroup.stories.js +34 -0
  24. package/src/components/RadioGroup/RadioGroup.vue +71 -0
  25. package/src/components/RadioGroup/__tests__/RadioGroup.spec.js +20 -0
  26. package/src/components/RadioGroup/index.ts +4 -0
  27. package/src/components/layout/Container/Container.stories.js +0 -1
  28. package/src/components/layout/Row/Row.scss +6 -0
  29. package/src/components/layout/Row/Row.stories.js +23 -0
  30. package/src/components/layout/Row/Row.vue +22 -0
  31. package/src/components/layout/Row/__tests__/Row.spec.js +22 -0
  32. package/src/components/layout/Row/index.ts +4 -0
  33. package/src/composition/__tests__/deepEqual.spec.js +13 -0
  34. package/src/composition/__tests__/validateFormFieldBuilder.spec.js +8 -0
  35. package/src/composition/__tests__/validateFormStateBuilder.spec.js +8 -0
  36. package/src/composition/deepEqual.ts +24 -0
  37. package/src/composition/validateFormFieldBuilder.ts +19 -0
  38. package/src/composition/validateFormMethodBuilder.ts +9 -0
  39. package/src/composition/validateFormStateBuilder.ts +13 -0
  40. package/src/configurations/_theme-colors.scss +3 -3
  41. package/src/examples/Form/Full.stories.js +1 -1
  42. package/src/main.ts +3 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@farm-investimentos/front-mfe-components",
3
- "version": "9.3.1",
3
+ "version": "9.4.0",
4
4
  "author": "farm investimentos",
5
5
  "private": false,
6
6
  "main": "./dist/front-mfe-components.common.js",
@@ -7,7 +7,8 @@ export default {
7
7
  docs: {
8
8
  description: {
9
9
  component: `Card<br />
10
- selector: <em>farm-card</em>
10
+ selector: <em>farm-card</em><br />
11
+ <span style="color: green;">ready for use</span>
11
12
  `,
12
13
  },
13
14
  },
@@ -11,7 +11,8 @@ export default {
11
11
  docs: {
12
12
  description: {
13
13
  component: `Checkbox<br />
14
- selector: <em>farm-checkbox</em>
14
+ selector: <em>farm-checkbox</em><br />
15
+ <span style="color: green;">ready for use</span>
15
16
  `,
16
17
  },
17
18
  },
@@ -18,7 +18,11 @@
18
18
  </div>
19
19
  </template>
20
20
  <script lang="ts">
21
- import Vue, { ref, toRefs, watch } from 'vue';
21
+ import Vue, { onBeforeMount, PropType, ref, toRefs, watch } from 'vue';
22
+ import validateFormStateBuilder from '../../composition/validateFormStateBuilder';
23
+ import validateFormFieldBuilder from '../../composition/validateFormFieldBuilder';
24
+ import validateFormMethodBuilder from '../../composition/validateFormMethodBuilder';
25
+ import deepEqual from '../../composition/deepEqual';
22
26
 
23
27
  export default Vue.extend({
24
28
  name: 'farm-checkbox',
@@ -32,21 +36,34 @@ export default Vue.extend({
32
36
  */
33
37
  label: { type: String, default: null },
34
38
  /**
35
- * disabled
39
+ * Is disabled?
36
40
  */
37
41
  disabled: { type: Boolean, default: false },
42
+ /**
43
+ * Variation
44
+ */
38
45
  variation: {
39
- type: String,
46
+ type: String as PropType<'' | 'lighten' | 'darken'>,
40
47
  default: '',
41
48
  },
42
49
  color: {
43
50
  type: String,
44
51
  default: 'primary',
45
52
  },
53
+ /**
54
+ * Array of rules used for validation
55
+ */
56
+ rules: {
57
+ type: Array as PropType<Array<Function>>,
58
+ default: () => [],
59
+ },
46
60
  },
47
61
  setup(props, { emit }) {
48
62
  const innerValue = ref(props.value);
49
- const { label, disabled } = toRefs(props);
63
+ const { label, disabled, rules } = toRefs(props);
64
+ const { errorBucket, valid, validatable } = validateFormStateBuilder();
65
+
66
+ let fieldValidator = validateFormFieldBuilder(rules.value);
50
67
 
51
68
  const toggleValue = () => {
52
69
  if (disabled.value) {
@@ -54,20 +71,51 @@ export default Vue.extend({
54
71
  }
55
72
  innerValue.value = !innerValue.value;
56
73
  emit('input', innerValue.value);
74
+ validate(innerValue.value);
57
75
  };
58
76
 
59
77
  watch(
60
78
  () => props.value,
61
79
  () => {
62
80
  innerValue.value = props.value;
81
+ validate(innerValue.value);
63
82
  }
64
83
  );
65
84
 
85
+ watch(
86
+ () => props.rules,
87
+ (newVal, oldVal) => {
88
+ if (deepEqual(newVal, oldVal)) return;
89
+ fieldValidator = validateFormFieldBuilder(rules.value);
90
+ validate = validateFormMethodBuilder(errorBucket, valid, fieldValidator);
91
+ validate(innerValue.value);
92
+ }
93
+ );
94
+
95
+ const reset = () => {
96
+ if (disabled.value) {
97
+ return false;
98
+ }
99
+ innerValue.value = false;
100
+ emit('input', innerValue.value);
101
+ };
102
+
103
+ onBeforeMount(() => {
104
+ validate(innerValue.value);
105
+ });
106
+
107
+ let validate = validateFormMethodBuilder(errorBucket, valid, fieldValidator);
108
+
66
109
  return {
67
110
  innerValue,
68
111
  label,
69
112
  disabled,
113
+ errorBucket,
114
+ valid,
115
+ validatable,
70
116
  toggleValue,
117
+ reset,
118
+ validate,
71
119
  };
72
120
  },
73
121
  });
@@ -5,7 +5,11 @@ describe('Checkbox component', () => {
5
5
  let wrapper;
6
6
 
7
7
  beforeEach(() => {
8
- wrapper = shallowMount(Checkbox, {});
8
+ wrapper = shallowMount(Checkbox, {
9
+ propsData: {
10
+ value: false,
11
+ },
12
+ });
9
13
  });
10
14
 
11
15
  test('Created hook', () => {
@@ -50,6 +50,15 @@
50
50
  &.farm-chip--lighten {
51
51
  color: themeColor('secondary');
52
52
  }
53
+ &.farm-chip--darken {
54
+ color: white;
55
+ }
56
+ }
57
+
58
+ &[color='secondary'] {
59
+ &.farm-chip--lighten {
60
+ color: white;
61
+ }
53
62
  }
54
63
 
55
64
  &--outlined {
@@ -0,0 +1,146 @@
1
+ import Form from './Form.vue';
2
+
3
+ export default {
4
+ title: 'Form/Form',
5
+ component: Form,
6
+ parameters: {
7
+ docs: {
8
+ description: {
9
+ component: `Form<br />
10
+ selector: <em>farm-form</em><br />
11
+ <span style="color: green;">ready for use</span>
12
+ `,
13
+ },
14
+ },
15
+ viewMode: 'docs',
16
+ },
17
+ };
18
+
19
+ const styles = {
20
+ vForm: {
21
+ maxWidth: '480px',
22
+ },
23
+ footer: {
24
+ display: 'flex',
25
+ justifyContent: 'end',
26
+ },
27
+ };
28
+
29
+ export const Primary = () => ({
30
+ data() {
31
+ return {
32
+ form: {
33
+ document: 'Document',
34
+ name: 'Name',
35
+ checkbox: true,
36
+ },
37
+ validForm: false,
38
+ rules: {
39
+ required: value => !!value || 'Campo obrigatório',
40
+ checked: value => !!value || 'Deve estar marcado',
41
+ },
42
+ styles,
43
+ };
44
+ },
45
+ template: `
46
+ <farm-form v-model="validForm" :style="[styles.vForm]" ref="form">
47
+
48
+ <farm-label :required="true">Documento</farm-label>
49
+ <farm-textfield v-model="form.document" :rules="[rules.required]" />
50
+
51
+ <farm-label :required="true">Nome</farm-label>
52
+ <farm-textfield v-model="form.name" :rules="[rules.required]" />
53
+
54
+ <farm-label :required="true">True/false</farm-label>
55
+ <farm-checkbox v-model="form.checkbox" :rules="[rules.checked]" />
56
+
57
+ <div class="footer" :style="[styles.footer]">
58
+ <farm-btn color="secondary" outlined @click="$refs.form.reset()" class="mr-3">Reset</farm-btn>
59
+ <farm-btn color="secondary" :disabled="!validForm">Salvar</farm-btn>
60
+ </div>
61
+ </farm-form>
62
+ `,
63
+ });
64
+
65
+ export const Secondary = () => ({
66
+ data() {
67
+ return {
68
+ form: {
69
+ document: 'Document',
70
+ not: 'Not required field',
71
+ checkbox: false,
72
+ },
73
+ validForm: false,
74
+ rules: {
75
+ required: value => !!value || 'Campo obrigatório',
76
+ },
77
+ styles,
78
+ };
79
+ },
80
+ template: `
81
+ <farm-form v-model="validForm" :style="[styles.vForm]" ref="form">
82
+ <section>
83
+ <div>
84
+ <farm-label :required="true">Documento</farm-label>
85
+ <farm-textfield v-model="form.document" :rules="[rules.required]" />
86
+ </div>
87
+ </section>
88
+ <section>
89
+ <div>
90
+ <farm-label>not</farm-label>
91
+ <farm-textfield v-model="form.not" />
92
+ </div>
93
+ </section>
94
+
95
+ <farm-label>Not required</farm-label>
96
+ <farm-checkbox v-model="form.checkbox" />
97
+
98
+ <div class="footer" :style="[styles.footer]">
99
+ <farm-btn color="secondary" outlined @click="$refs.form.reset()" class="mr-3">Reset</farm-btn>
100
+ <farm-btn color="secondary" :disabled="!validForm">Salvar</farm-btn>
101
+ </div>
102
+ </farm-form>
103
+ `,
104
+ });
105
+
106
+ export const InitInvalid = () => ({
107
+ data() {
108
+ return {
109
+ form: {
110
+ document: '',
111
+ name: '',
112
+ checkbox: false,
113
+ },
114
+ validForm: false,
115
+ rules: {
116
+ required: value => !!value || 'Campo obrigatório',
117
+ checked: value => !!value || 'Deve estar marcado',
118
+ },
119
+ styles,
120
+ };
121
+ },
122
+ template: `
123
+ <farm-form v-model="validForm" :style="[styles.vForm]" ref="form">
124
+ <section>
125
+ <div>
126
+ <farm-label :required="true">Documento</farm-label>
127
+ <farm-textfield v-model="form.document" :rules="[rules.required]" />
128
+ </div>
129
+ </section>
130
+ <section>
131
+ <div>
132
+ <farm-label :required="true">Nome</farm-label>
133
+ <farm-textfield v-model="form.name" :rules="[rules.required]" />
134
+ </div>
135
+ </section>
136
+
137
+ <farm-label :required="true">True/false</farm-label>
138
+ <farm-checkbox v-model="form.checkbox" :rules="[rules.checked]" />
139
+
140
+ <div class="footer" :style="[styles.footer]">
141
+ <farm-btn color="secondary" outlined @click="$refs.form.reset()" class="mr-3">Reset</farm-btn>
142
+ <farm-btn color="secondary" :disabled="!validForm">Salvar</farm-btn>
143
+ </div>
144
+ </farm-form>
145
+ `,
146
+ });
@@ -0,0 +1,69 @@
1
+ <template>
2
+ <form class="farm-form"><slot></slot></form>
3
+ </template>
4
+ <script lang="ts">
5
+ import Vue, { onMounted, reactive, ref, getCurrentInstance } from 'vue';
6
+
7
+ type ErrorsBag = Record<number, boolean>;
8
+
9
+ export default Vue.extend({
10
+ name: 'farm-form',
11
+ props: {
12
+ value: { type: [Array, Boolean], required: true },
13
+ },
14
+ inheritAttrs: true,
15
+ setup(props, { emit }) {
16
+ const innerValue = ref(props.value);
17
+ const errorsBag = reactive({} as ErrorsBag);
18
+ let validationFields = [];
19
+
20
+ const dispatchError = () => {
21
+ const keys = Object.keys(errorsBag);
22
+ const errorsIds = keys.filter(key => !errorsBag[key]);
23
+ emit('input', errorsIds.length === 0);
24
+ };
25
+
26
+ const watchInput = (field: any) => {
27
+ field.$watch(
28
+ 'hasError',
29
+ () => {
30
+ errorsBag[field._uid] = field.valid;
31
+ dispatchError();
32
+ },
33
+ { immediate: true }
34
+ );
35
+ };
36
+
37
+ const reset = () => {
38
+ validationFields.forEach(field => {
39
+ field.reset();
40
+ });
41
+ };
42
+
43
+ const recursiveFormField = $node => {
44
+ $node.$children.forEach($leaf => {
45
+ if ($leaf.$children.length > 1) {
46
+ recursiveFormField($leaf);
47
+ } else if ($leaf.$children[0] && $leaf.$children[0].validate) {
48
+ validationFields.push($leaf.$children[0]);
49
+ } else if ($leaf.validatable) {
50
+ validationFields.push($leaf);
51
+ }
52
+ });
53
+ };
54
+
55
+ onMounted(() => {
56
+ recursiveFormField(getCurrentInstance().proxy);
57
+ validationFields.forEach(field => {
58
+ watchInput(field);
59
+ });
60
+ });
61
+
62
+ return {
63
+ innerValue,
64
+ errorsBag,
65
+ reset,
66
+ };
67
+ },
68
+ });
69
+ </script>
@@ -0,0 +1,16 @@
1
+ import { shallowMount } from '@vue/test-utils';
2
+ import Form from '../Form';
3
+
4
+ describe('Form component', () => {
5
+ let wrapper;
6
+ let component;
7
+
8
+ beforeEach(() => {
9
+ wrapper = shallowMount(Form);
10
+ component = wrapper.vm;
11
+ });
12
+
13
+ test('Created hook', () => {
14
+ expect(wrapper).toBeDefined();
15
+ });
16
+ });
@@ -0,0 +1,4 @@
1
+ import Form from './Form.vue';
2
+
3
+ export { Form };
4
+ export default Form;
@@ -9,7 +9,8 @@ export default {
9
9
  docs: {
10
10
  description: {
11
11
  component: `Loader<br />
12
- selector: <em>farm-loader</em>`,
12
+ selector: <em>farm-loader</em><br />
13
+ <span style="color: green;">ready for use</span>`,
13
14
  },
14
15
  },
15
16
  },
@@ -7,7 +7,8 @@ export default {
7
7
  docs: {
8
8
  description: {
9
9
  component: `Logger<br />
10
- selector: <em>farm-logger</em>
10
+ selector: <em>farm-logger</em><br />
11
+ <span style="color: green;">ready for use</span>
11
12
  `,
12
13
  },
13
14
  },
@@ -8,6 +8,7 @@ export default {
8
8
  description: {
9
9
  component: `Modal<br />
10
10
  selector: <em>farm-modal</em>
11
+ <span style="color: yellow;">wait</span>
11
12
  `,
12
13
  },
13
14
  },
@@ -0,0 +1,6 @@
1
+ interface IRadioGroup {
2
+ id: Number;
3
+ label: String;
4
+ }
5
+
6
+ export default IRadioGroup;
@@ -0,0 +1,82 @@
1
+ .farm-radio-group {
2
+ border: none;
3
+ cursor: default;
4
+ display: flex;
5
+ width: 100%;
6
+ &__item {
7
+ font-size: 16px;
8
+ line-height: 1.1;
9
+ cursor: pointer;
10
+ display: grid;
11
+ grid-template-columns: 16px auto;
12
+ gap: 8px;
13
+ color: rgba(0,0,0,.6);
14
+ margin-top: 16px;
15
+ margin-right: 16px;
16
+ }
17
+ }
18
+
19
+ .farm-radio-group--column {
20
+ flex-direction: column;
21
+ }
22
+
23
+ input[type="radio"] {
24
+ -webkit-appearance: none;
25
+ appearance: none;
26
+ background-color: #ffffff;
27
+ margin: 0;
28
+ font: inherit;
29
+ color: rgba(0,0,0,.6);
30
+ width: 18px;
31
+ height: 18px;
32
+ border: 1.5px solid rgba(0,0,0,.6);
33
+ border-radius: 50%;
34
+ transform: translate(-12px);
35
+ display: grid;
36
+ place-content: center;
37
+ cursor: pointer;
38
+ }
39
+
40
+ input[type="radio"]::before {
41
+ content: "";
42
+ width: 10px;
43
+ height: 10px;
44
+ border-radius: 50%;
45
+ transform: scale(0);
46
+ transition: 120ms transform ease-in-out;
47
+ box-shadow: inset 16px 16px var(--radio-group-color);
48
+ background-color: CanvasText;
49
+ }
50
+
51
+ input[type="radio"]:checked::before {
52
+ transform: scale(1);
53
+ }
54
+
55
+ input[type="radio"]:focus {
56
+ outline: none;
57
+ outline-offset: none;
58
+ }
59
+
60
+ input[type="radio"]:checked {
61
+ border: 1.5px solid var(--radio-group-color);
62
+ }
63
+
64
+ input[type='radio']:hover {
65
+ box-shadow: 0px 0px 0px 8px rgba(0, 0, 0, 0.1);
66
+ background-color: rgba(0, 0, 0, 0.1);
67
+ border-radius: 50%;
68
+ opacity: 1;
69
+ }
70
+
71
+ input[type='radio']:active {
72
+ animation: pulse 0.2s 1 ease-out;
73
+ }
74
+
75
+ @keyframes pulse {
76
+ from {
77
+ box-shadow: 0 0 0 0 rgba(0, 0, 0, 0.2), 0 0 0 0 rgba(0, 0, 0, 0.2);
78
+ }
79
+ to {
80
+ box-shadow: 0 0 0 8px rgba(0, 0, 0, 0.2), 0 0 0 8px rgba(0, 0, 0, 0.2);
81
+ }
82
+ }
@@ -0,0 +1,34 @@
1
+ import RadioGroup from './RadioGroup';
2
+
3
+ export default {
4
+ title: 'Form/RadioGroup',
5
+ component: RadioGroup,
6
+ parameters: {
7
+ docs: {
8
+ description: {
9
+ component: `RadioGroup<br />
10
+ selector: <em>farm-radio-group</em>
11
+ `,
12
+ },
13
+ },
14
+ viewMode: 'docs',
15
+ },
16
+ };
17
+
18
+ export const Primary = () => ({
19
+ data() {
20
+ return {
21
+ buttons: [
22
+ { label: 'Button 1', id: 1 },
23
+ { label: 'Button 2', id: 2 },
24
+ { label: 'Button 3', id: 3 }
25
+ ],
26
+ checkedValue: 1
27
+ };
28
+ },
29
+ template: `<div>
30
+ <farm-radio-group v-model="checkedValue" column :buttons="buttons" />
31
+ </div>`,
32
+ });
33
+
34
+ Primary.storyName = 'Basic';
@@ -0,0 +1,71 @@
1
+ <template>
2
+ <div
3
+ :class="{
4
+ 'farm-radio-group': true,
5
+ 'farm-radio-group--column': $props.column,
6
+ }">
7
+ <div class="farm-radio-group__item"
8
+ v-for="(button, index) in buttons"
9
+ :key="`farm-radio-group_` + index"
10
+ @click="clicked(button.id)"
11
+ >
12
+ <input
13
+ type="radio"
14
+ name="radio"
15
+ :checked="button.id === $props.value"
16
+ :id="`farm-radio-group_` + index"
17
+ :style="cssVars"
18
+ :value="button.id"
19
+ >
20
+ <label>
21
+ {{button.label}}
22
+ </label>
23
+ </div>
24
+ </div>
25
+ </template>
26
+ <script lang="ts">
27
+ import Vue, { PropType } from 'vue';
28
+ import IRadioGroup from './IRadioGroup';
29
+ export default Vue.extend({
30
+ name: 'farm-radio-group',
31
+ props: {
32
+ /**
33
+ * Array of buttons
34
+ */
35
+ buttons: {
36
+ type: Array as PropType<Array<IRadioGroup>>,
37
+ default: () => [],
38
+ },
39
+ /**
40
+ * v-model
41
+ */
42
+ value: {
43
+ required: true,
44
+ },
45
+ column: {
46
+ type: Boolean,
47
+ default: false
48
+ },
49
+ color: {
50
+ type: String,
51
+ default: '#00B493',
52
+ }
53
+ },
54
+ methods: {
55
+ clicked(value) {
56
+ this.$emit('input', value);
57
+ }
58
+ },
59
+ computed: {
60
+ cssVars () {
61
+ return {
62
+ '--radio-group-color': this.color
63
+ };
64
+ }
65
+ }
66
+
67
+ });
68
+ </script>
69
+ <style lang="scss" scoped>
70
+ @import './RadioGroup.scss';
71
+ </style>
@@ -0,0 +1,20 @@
1
+ import { shallowMount } from '@vue/test-utils';
2
+ import RadioGroup from '../RadioGroup';
3
+
4
+ describe('RadioGroup component', () => {
5
+ let wrapper;
6
+
7
+ beforeEach(() => {
8
+ wrapper = shallowMount(RadioGroup, {});
9
+ });
10
+
11
+ test('Created hook', () => {
12
+ expect(wrapper).toBeDefined();
13
+ });
14
+
15
+ describe('mount component', () => {
16
+ it('renders correctly', () => {
17
+ expect(wrapper.element).toMatchSnapshot();
18
+ });
19
+ });
20
+ });
@@ -0,0 +1,4 @@
1
+ import RadioGroup from './RadioGroup.vue';
2
+
3
+ export { RadioGroup };
4
+ export default RadioGroup;
@@ -16,7 +16,6 @@ export default {
16
16
  };
17
17
 
18
18
  export const Primary = () => ({
19
- components: { 'farm-container': Container },
20
19
  template: '<farm-container>content</farm-container>',
21
20
  });
22
21
 
@@ -0,0 +1,6 @@
1
+ .farm-row {
2
+ display: flex;
3
+ flex-wrap: wrap;
4
+ flex: 1 1 auto;
5
+ margin: -12px;
6
+ }