Skip to content

Commit fd08f9b

Browse files
authored
chore(refactor): improved code sharing between form components (#6100)
* chore(refactor): improved code sharing between form components * Update form-checkbox-group.spec.js * Update form-radio-group.spec.js * Update form-radio-group.js * Update form-radio-check-group.js * Update form-radio-group.js
1 parent 5082976 commit fd08f9b

File tree

9 files changed

+72
-99
lines changed

9 files changed

+72
-99
lines changed

src/components/form-checkbox/form-checkbox-group.js

Lines changed: 7 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,60 +1,41 @@
11
import Vue from '../../vue'
22
import { NAME_FORM_CHECKBOX_GROUP } from '../../constants/components'
33
import { makePropsConfigurable } from '../../utils/config'
4-
import formControlMixin, { props as formControlProps } from '../../mixins/form-control'
5-
import formOptionsMixin, { props as formOptionsProps } from '../../mixins/form-options'
64
import formRadioCheckGroupMixin, {
75
props as formRadioCheckGroupProps
86
} from '../../mixins/form-radio-check-group'
9-
import formSizeMixin, { props as formSizeProps } from '../../mixins/form-size'
10-
import formStateMixin, { props as formStateProps } from '../../mixins/form-state'
11-
import idMixin from '../../mixins/id'
127

138
// --- Props ---
149

1510
export const props = makePropsConfigurable(
1611
{
17-
...formControlProps,
18-
...formOptionsProps,
1912
...formRadioCheckGroupProps,
20-
...formSizeProps,
21-
...formStateProps,
13+
checked: {
14+
type: Array,
15+
default: () => []
16+
},
2217
switches: {
2318
// Custom switch styling
2419
type: Boolean,
2520
default: false
26-
},
27-
checked: {
28-
type: Array,
29-
default: null
3021
}
3122
},
3223
NAME_FORM_CHECKBOX_GROUP
3324
)
3425

3526
// --- Main component ---
27+
3628
// @vue/component
3729
export const BFormCheckboxGroup = /*#__PURE__*/ Vue.extend({
3830
name: NAME_FORM_CHECKBOX_GROUP,
39-
mixins: [
40-
idMixin,
41-
formControlMixin,
42-
formRadioCheckGroupMixin, // Includes render function
43-
formOptionsMixin,
44-
formSizeMixin,
45-
formStateMixin
46-
],
31+
// Includes render function
32+
mixins: [formRadioCheckGroupMixin],
4733
provide() {
4834
return {
4935
bvCheckGroup: this
5036
}
5137
},
5238
props,
53-
data() {
54-
return {
55-
localChecked: this.checked || []
56-
}
57-
},
5839
computed: {
5940
isRadioGroup() {
6041
return false

src/components/form-checkbox/form-checkbox-group.spec.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -311,11 +311,11 @@ describe('form-checkbox-group', () => {
311311
}
312312
})
313313

314-
expect(wrapper.classes()).toBeDefined()
314+
expect(wrapper.vm.isRadioGroup).toEqual(false)
315+
expect(wrapper.vm.localChecked).toEqual([])
315316

316317
const $inputs = wrapper.findAll('input')
317318
expect($inputs.length).toBe(3)
318-
expect(wrapper.vm.localChecked).toEqual([])
319319
expect($inputs.wrappers.every(c => c.find('input[type=checkbox]').exists())).toBe(true)
320320

321321
wrapper.destroy()

src/components/form-group/form-group.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -419,7 +419,7 @@ export const BFormGroup = {
419419
id: this.safeId(),
420420
disabled: isFieldset ? this.disabled : null,
421421
role: isFieldset ? null : 'group',
422-
'aria-invalid': state === false ? 'true' : null,
422+
'aria-invalid': this.computedAriaInvalid,
423423
// Only apply aria-labelledby if we are a horizontal fieldset
424424
// as the legend is no longer a direct child of fieldset
425425
'aria-labelledby': isFieldset && isHorizontal ? labelId : null,

src/components/form-radio/form-radio-group.js

Lines changed: 3 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,55 +1,26 @@
11
import Vue from '../../vue'
22
import { NAME_FORM_RADIO_GROUP } from '../../constants/components'
33
import { makePropsConfigurable } from '../../utils/config'
4-
import idMixin from '../../mixins/id'
5-
import formControlMixin, { props as formControlProps } from '../../mixins/form-control'
6-
import formOptionsMixin, { props as formOptionsProps } from '../../mixins/form-options'
74
import formRadioCheckGroupMixin, {
85
props as formRadioCheckGroupProps
96
} from '../../mixins/form-radio-check-group'
10-
import formSizeMixin, { props as formSizeProps } from '../../mixins/form-size'
11-
import formStateMixin, { props as formStateProps } from '../../mixins/form-state'
127

138
// --- Props ---
149

15-
export const props = makePropsConfigurable(
16-
{
17-
...formControlProps,
18-
...formOptionsProps,
19-
...formRadioCheckGroupProps,
20-
...formSizeProps,
21-
...formStateProps,
22-
checked: {
23-
// type: [String, Number, Boolean, Object],
24-
default: null
25-
}
26-
},
27-
NAME_FORM_RADIO_GROUP
28-
)
10+
export const props = makePropsConfigurable(formRadioCheckGroupProps, NAME_FORM_RADIO_GROUP)
2911

3012
// --- Main component ---
13+
3114
// @vue/component
3215
export const BFormRadioGroup = /*#__PURE__*/ Vue.extend({
3316
name: NAME_FORM_RADIO_GROUP,
34-
mixins: [
35-
idMixin,
36-
formControlMixin,
37-
formRadioCheckGroupMixin, // Includes render function
38-
formOptionsMixin,
39-
formSizeMixin,
40-
formStateMixin
41-
],
17+
mixins: [formRadioCheckGroupMixin],
4218
provide() {
4319
return {
4420
bvRadioGroup: this
4521
}
4622
},
4723
props,
48-
data() {
49-
return {
50-
localChecked: this.checked
51-
}
52-
},
5324
computed: {
5425
isRadioGroup() {
5526
return true

src/components/form-radio/form-radio-group.spec.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -288,10 +288,12 @@ describe('form-radio-group', () => {
288288
checked: ''
289289
}
290290
})
291-
expect(wrapper.classes()).toBeDefined()
291+
292+
expect(wrapper.vm.isRadioGroup).toEqual(true)
293+
expect(wrapper.vm.localChecked).toEqual('')
294+
292295
const radios = wrapper.findAll('input')
293296
expect(radios.length).toBe(3)
294-
expect(wrapper.vm.localChecked).toEqual('')
295297
expect(radios.wrappers.every(c => c.find('input[type=radio]').exists())).toBe(true)
296298

297299
wrapper.destroy()

src/components/form-select/form-select.js

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -77,12 +77,6 @@ export const BFormSelect = /*#__PURE__*/ Vue.extend({
7777
this.size && !this.plain ? `custom-select-${this.size}` : null,
7878
this.stateClass
7979
]
80-
},
81-
computedAriaInvalid() {
82-
if (this.ariaInvalid === true || this.ariaInvalid === 'true') {
83-
return 'true'
84-
}
85-
return this.stateClass === 'is-invalid' ? 'true' : null
8680
}
8781
},
8882
watch: {

src/mixins/form-radio-check-group.js

Lines changed: 48 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,27 @@ import { makePropsConfigurable } from '../utils/config'
44
import { htmlOrText } from '../utils/html'
55
import { BFormCheckbox } from '../components/form-checkbox/form-checkbox'
66
import { BFormRadio } from '../components/form-radio/form-radio'
7+
import formControlMixin, { props as formControlProps } from './form-control'
78
import formCustomMixin, { props as formCustomProps } from './form-custom'
9+
import formOptionsMixin, { props as formOptionsProps } from './form-options'
10+
import formSizeMixin, { props as formSizeProps } from './form-size'
11+
import formStateMixin, { props as formStateProps } from './form-state'
12+
import idMixin from './id'
813
import normalizeSlotMixin from './normalize-slot'
914

1015
// --- Props ---
1116

1217
export const props = makePropsConfigurable(
1318
{
19+
...formControlProps,
20+
...formOptionsProps,
21+
...formSizeProps,
22+
...formStateProps,
1423
...formCustomProps,
24+
checked: {
25+
// type: [Boolean, Number, Object, String]
26+
default: null
27+
},
1528
validated: {
1629
type: Boolean,
1730
default: false
@@ -39,14 +52,28 @@ export const props = makePropsConfigurable(
3952
)
4053

4154
// --- Mixin ---
55+
4256
// @vue/component
4357
export default {
44-
mixins: [formCustomMixin, normalizeSlotMixin],
58+
mixins: [
59+
idMixin,
60+
normalizeSlotMixin,
61+
formControlMixin,
62+
formOptionsMixin,
63+
formSizeMixin,
64+
formStateMixin,
65+
formCustomMixin
66+
],
4567
model: {
4668
prop: 'checked',
4769
event: 'input'
4870
},
4971
props,
72+
data() {
73+
return {
74+
localChecked: this.checked
75+
}
76+
},
5077
computed: {
5178
inline() {
5279
return !this.stacked
@@ -57,28 +84,28 @@ export default {
5784
return this.name || this.safeId()
5885
},
5986
groupClasses() {
87+
const { inline, size, validated } = this
88+
89+
let classes = { 'was-validated': validated }
6090
if (this.buttons) {
61-
return [
91+
classes = [
92+
classes,
6293
'btn-group-toggle',
63-
this.inline ? 'btn-group' : 'btn-group-vertical',
64-
this.size ? `btn-group-${this.size}` : '',
65-
this.validated ? `was-validated` : ''
94+
{
95+
'btn-group': inline,
96+
'btn-group-vertical': !inline,
97+
[`btn-group-${size}`]: !!size
98+
}
6699
]
67100
}
68-
return [this.validated ? `was-validated` : '']
69-
},
70-
computedAriaInvalid() {
71-
const ariaInvalid = this.ariaInvalid
72-
if (ariaInvalid === true || ariaInvalid === 'true' || ariaInvalid === '') {
73-
return 'true'
74-
}
75-
return this.computedState === false ? 'true' : null
101+
102+
return classes
76103
}
77104
},
78105
watch: {
79-
checked(newVal) {
80-
if (!looseEqual(newVal, this.localChecked)) {
81-
this.localChecked = newVal
106+
checked(newValue) {
107+
if (!looseEqual(newValue, this.localChecked)) {
108+
this.localChecked = newValue
82109
}
83110
},
84111
localChecked(newValue, oldValue) {
@@ -88,11 +115,14 @@ export default {
88115
}
89116
},
90117
render(h) {
118+
const { isRadioGroup } = this
119+
const optionComponent = isRadioGroup ? BFormRadio : BFormCheckbox
120+
91121
const $inputs = this.formOptions.map((option, index) => {
92122
const key = `BV_option_${index}`
93123

94124
return h(
95-
this.isRadioGroup ? BFormRadio : BFormCheckbox,
125+
optionComponent,
96126
{
97127
props: {
98128
id: this.safeId(key),
@@ -116,7 +146,7 @@ export default {
116146
class: [this.groupClasses, 'bv-no-focus-ring'],
117147
attrs: {
118148
id: this.safeId(),
119-
role: this.isRadioGroup ? 'radiogroup' : 'group',
149+
role: isRadioGroup ? 'radiogroup' : 'group',
120150
// Add `tabindex="-1"` to allow group to be focused if needed by screen readers
121151
tabindex: '-1',
122152
'aria-required': this.required ? 'true' : null,

src/mixins/form-state.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,13 @@ export default {
3434
stateClass() {
3535
const state = this.computedState
3636
return state === true ? 'is-valid' : state === false ? 'is-invalid' : null
37+
},
38+
computedAriaInvalid() {
39+
const { ariaInvalid } = this
40+
if (ariaInvalid === true || ariaInvalid === 'true' || ariaInvalid === '') {
41+
return 'true'
42+
}
43+
return this.computedState === false ? 'true' : ariaInvalid
3744
}
3845
}
3946
}

src/mixins/form-text.js

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -95,18 +95,6 @@ export default {
9595
this.stateClass
9696
]
9797
},
98-
computedAriaInvalid() {
99-
if (!this.ariaInvalid || this.ariaInvalid === 'false') {
100-
// `this.ariaInvalid` is `null` or `false` or 'false'
101-
return this.computedState === false ? 'true' : null
102-
}
103-
if (this.ariaInvalid === true) {
104-
// User wants explicit `:aria-invalid="true"`
105-
return 'true'
106-
}
107-
// Most likely a string value (which could be the string 'true')
108-
return this.ariaInvalid
109-
},
11098
computedDebounce() {
11199
// Ensure we have a positive number equal to or greater than 0
112100
return mathMax(toInteger(this.debounce, 0), 0)

0 commit comments

Comments
 (0)