Skip to content

Commit

Permalink
feat(BCheckbox)!: Implement reverse and without label (#1825)
Browse files Browse the repository at this point in the history
feat(BFormCheckboxGroup)!: BFormCheckboxGroup no longer emits 'change' and 'input' events, listen for 'update:modelValue' instead

feat(BFormCheckbox): Add 'reverse' property to checkbox and checkboxgroup

feat(BFormCheckbox): Handle no-label case

feat(BRadioGroup)!: BRadioGroup no longer emits 'change' and 'input' events, listen for 'update:modelValue' instead
  • Loading branch information
dwgray committed Apr 2, 2024
1 parent f006662 commit 6c69ff9
Show file tree
Hide file tree
Showing 12 changed files with 113 additions and 116 deletions.
2 changes: 1 addition & 1 deletion apps/docs/src/data/components/ComponentReference.ts
Expand Up @@ -17,7 +17,7 @@ export interface ComponentReference {
description?: string
}[]
slots: {
scope: SlotScopeReference[]
scope?: SlotScopeReference[]
name: string
description?: string
}[]
Expand Down
58 changes: 12 additions & 46 deletions apps/docs/src/data/components/formCheckbox.data.ts
Expand Up @@ -116,6 +116,12 @@ export default {
description:
'Controls the validation state appearance of the component. `true` for valid, `false` for invalid, or `null` for no validation state',
},
{
prop: 'reverse',
type: 'boolean',
default: false,
description: 'When set, renders the checkbox or switch on the opposite side',
},
{
prop: 'switch',
type: 'boolean',
Expand Down Expand Up @@ -161,30 +167,6 @@ export default {
},
],
},
{
event: 'input',
description: 'Emitted before the checked value is changed',
args: [
{
arg: 'checked',
type: 'CheckboxValue | readonly CheckboxValue[]',
description:
'Value of the checkbox before the event. Value will be an array for grouped checkboxes or a single value for standalone checkboxes.',
},
],
},
{
event: 'change',
description: 'Emitted when the checked value is changed',
args: [
{
arg: 'checked',
type: 'CheckboxValue | readonly CheckboxValue[]',
description:
'Value of the checkbox. Value will be an array for grouped checkboxes or a single value for standalone checkboxes.',
},
],
},
],
slots: [
{
Expand Down Expand Up @@ -289,6 +271,12 @@ export default {
default: false,
description: 'Adds the `required` attribute to the form control',
},
{
prop: 'reverse',
type: 'boolean',
default: false,
description: 'When set, renders the checkboxes and switches on the opposite side',
},
{
prop: 'size',
type: 'Size',
Expand Down Expand Up @@ -345,28 +333,6 @@ export default {
},
],
},
{
event: 'input',
description: 'Emitted before the selected value(s) are changed',
args: [
{
arg: 'input',
type: 'CheckboxValue[]',
description: 'Value of the checkboxes before the event. Value will be an array.',
},
],
},
{
event: 'change',
description: 'Emitted when the selected value(s) are changed',
args: [
{
arg: 'change',
type: 'CheckboxValue[]',
description: 'Value of the checkboxes. Value will be an array.',
},
],
},
],
slots: [
{
Expand Down
50 changes: 6 additions & 44 deletions apps/docs/src/data/components/formRadio.data.ts
Expand Up @@ -80,6 +80,12 @@ export default {
type: 'boolean',
default: false,
},
{
prop: 'reverse',
type: 'boolean',
default: false,
description: 'When set, renders the radio button on the opposite side',
},
{
prop: 'state',
type: 'boolean | null',
Expand All @@ -92,28 +98,6 @@ export default {
},
],
emits: [
{
event: 'input',
description: '',
args: [
{
arg: 'input',
description: '',
type: 'boolean | string | unknown[] | Record<string, unknown> | number',
},
],
},
{
event: 'change',
description: '',
args: [
{
arg: 'change',
description: '',
type: 'boolean | string | unknown[] | Record<string, unknown> | number',
},
],
},
{
event: 'update:modelValue',
description: '',
Expand Down Expand Up @@ -239,17 +223,6 @@ export default {
},
],
emits: [
{
args: [
{
arg: 'input',
description: '',
type: 'unknown',
},
],
description: '',
event: 'input',
},
{
args: [
{
Expand All @@ -261,17 +234,6 @@ export default {
description: '',
event: 'update:modelValue',
},
{
args: [
{
arg: 'change',
description: '',
type: 'unknown',
},
],
description: '',
event: 'change',
},
],
slots: [],
},
Expand Down
23 changes: 21 additions & 2 deletions apps/docs/src/docs/components/form-checkbox.md
Expand Up @@ -412,6 +412,25 @@ Use the `size` prop to control the size of the checkbox. The default size is med
</template>
</HighlightCard>

## Reverse

Use the `reverse` prop to put your checkboxes and switches on the opposite side of the label.

<HighlightCard>
<BFormCheckbox reverse>Reverse checkbox</BFormCheckbox>
<BFormCheckbox reverse disabled>Disabled reverse checkbox</BFormCheckbox>
<BFormCheckbox reverse switch>Reverse switch ceckbox input</BFormCheckbox>
<template #html>

```vue-html
<BFormCheckbox reverse>Reverse checkbox</BFormCheckbox>
<BFormCheckbox reverse disabled>Disabled reverse checkbox</BFormCheckbox>
<BFormCheckbox reverse switch>Reverse switch ceckbox input</BFormCheckbox>
```

</template>
</HighlightCard>

## Checkbox values and `v-model`

By default, `BFormCheckbox` value will be true when checked and false when unchecked. You can customize the checked and unchecked values by specifying the `value` and `unchecked-value` properties, respectively.
Expand Down Expand Up @@ -934,7 +953,7 @@ const indeterminate = ref(true)
v-model:indeterminate="asIndeterminate"
aria-describedby="flavors"
aria-controls="flavors"
@change="toggleAll"
@update:modelValue="toggleAll"
>
{{ allSelected ? 'Un-select All' : 'Select All' }}
</BFormCheckbox>
Expand Down Expand Up @@ -972,7 +991,7 @@ const indeterminate = ref(true)
v-model:indeterminate="asIndeterminate"
aria-describedby="flavors"
aria-controls="flavors"
@change="toggleAll"
@update:modelValue="toggleAll"
>
{{ allSelected ? 'Un-select All' : 'Select All' }}
</BFormCheckbox>
Expand Down
Expand Up @@ -54,6 +54,7 @@ const props = withDefaults(
name?: string
plain?: boolean
required?: boolean
reverse?: boolean
size?: Size
state?: boolean | null
switch?: boolean
Expand All @@ -76,6 +77,7 @@ const props = withDefaults(
name: undefined,
plain: false,
required: undefined,
reverse: false,
size: undefined,
state: null,
switch: false,
Expand Down Expand Up @@ -138,10 +140,12 @@ const classesObject = computed(() => ({
plain: props.plain || (parentData?.plain.value ?? false),
button: props.button || (parentData?.buttons.value ?? false),
inline: props.inline || (parentData?.inline.value ?? false),
reverse: props.reverse || (parentData?.reverse.value ?? false),
switch: props.switch || (parentData?.switch.value ?? false),
state: props.state || parentData?.state.value,
size: props.size ?? parentData?.size.value ?? 'md', // This is where the true default is made
buttonVariant: props.buttonVariant ?? parentData?.buttonVariant.value ?? 'secondary', // This is where the true default is made
hasDefaultSlot: hasDefaultSlot.value,
}))
const computedClasses = getClasses(classesObject)
const inputClasses = getInputClasses(classesObject)
Expand Down
Expand Up @@ -21,7 +21,7 @@
</template>

<script setup lang="ts">
import {computed, nextTick, provide, ref, toRef, watch} from 'vue'
import {computed, provide, ref, toRef} from 'vue'
import BFormCheckbox from './BFormCheckbox.vue'
import type {AriaInvalid, ButtonVariant, CheckboxOptionRaw, CheckboxValue, Size} from '../../types'
import {getGroupAttr, getGroupClasses, useId} from '../../composables'
Expand All @@ -44,6 +44,7 @@ const props = withDefaults(
options?: readonly CheckboxOptionRaw[]
plain?: boolean
required?: boolean
reverse?: boolean
size?: Size
stacked?: boolean
state?: boolean | null
Expand All @@ -67,6 +68,7 @@ const props = withDefaults(
options: () => [],
plain: false,
required: false,
reverse: false,
size: 'md',
stacked: false,
state: null,
Expand All @@ -78,8 +80,6 @@ const props = withDefaults(
)
const emit = defineEmits<{
'change': [value: CheckboxValue[]]
'input': [value: CheckboxValue[]]
'update:modelValue': [value: CheckboxValue[]]
}>()
Expand Down Expand Up @@ -111,18 +111,12 @@ provide(checkboxGroupKey, {
plain: toRef(() => props.plain),
size: toRef(() => props.size),
inline: toRef(() => !props.stacked),
reverse: toRef(() => props.reverse),
required: toRef(() => props.required),
buttons: toRef(() => props.buttons),
disabled: toRef(() => props.disabled),
})
watch(modelValue, (newValue) => {
emit('input', [...newValue])
nextTick(() => {
emit('change', [...newValue])
})
})
const normalizeOptions = computed(() =>
props.options.map((el, ind) =>
typeof el === 'string' || typeof el === 'number'
Expand Down
Expand Up @@ -5,7 +5,7 @@ import BFormCheckbox from './BFormCheckbox.vue'
describe('form-checkbox', () => {
enableAutoUnmount(afterEach)

describe('useFormCHeck attributes', () => {
describe('useFormCheck attributes', () => {
it('tag is div', () => {
const wrapper = mount(BFormCheckbox)
expect(wrapper.element.tagName).toBe('DIV')
Expand All @@ -14,6 +14,9 @@ describe('form-checkbox', () => {
it('has class form-check if prop plain and prop button are false', () => {
const wrapper = mount(BFormCheckbox, {
props: {plain: false, button: false},
slots: {
default: 'checkbox',
},
})
expect(wrapper.classes()).toContain('form-check')
})
Expand All @@ -25,6 +28,11 @@ describe('form-checkbox', () => {
expect(wrapper.classes()).not.toContain('form-check')
})

it('does not have class form-check if default slot is empty', () => {
const wrapper = mount(BFormCheckbox, {})
expect(wrapper.classes()).not.toContain('form-check')
})

it('does not have class form-check if prop plain false and prop button true', () => {
const wrapper = mount(BFormCheckbox, {
props: {plain: false, button: true},
Expand All @@ -46,13 +54,27 @@ describe('form-checkbox', () => {
expect(wrapper.classes()).toContain('form-check-inline')
})

it('has class form-check-inline when prop inline', () => {
it('does not have class form-check-inline when prop inline', () => {
const wrapper = mount(BFormCheckbox, {
props: {inline: false},
})
expect(wrapper.classes()).not.toContain('form-check-inline')
})

it('has class form-check-reverse when prop reverse', () => {
const wrapper = mount(BFormCheckbox, {
props: {reverse: true},
})
expect(wrapper.classes()).toContain('form-check-reverse')
})

it('does not have class form-check-reverse when prop reverse', () => {
const wrapper = mount(BFormCheckbox, {
props: {reverse: false},
})
expect(wrapper.classes()).not.toContain('form-check-reverse')
})

it('does not have class form-switch when prop switch is false', () => {
const wrapper = mount(BFormCheckbox, {
props: {switch: false},
Expand Down
Expand Up @@ -50,6 +50,7 @@ const props = withDefaults(
name?: string
plain?: boolean
required?: boolean
reverse?: boolean
size?: Size
state?: boolean | null
value?: RadioValue
Expand All @@ -69,6 +70,7 @@ const props = withDefaults(
name: undefined,
plain: false,
required: false,
reverse: false,
size: undefined,
state: null,
value: true,
Expand Down Expand Up @@ -121,8 +123,10 @@ const classesObject = computed(() => ({
button: props.button || (parentData?.buttons.value ?? false),
inline: props.inline || (parentData?.inline.value ?? false),
state: props.state || parentData?.state.value,
reverse: props.reverse || (parentData?.reverse.value ?? false),
size: props.size ?? parentData?.size.value ?? 'md', // This is where the true default is made
buttonVariant: props.buttonVariant ?? parentData?.buttonVariant.value ?? 'secondary', // This is where the true default is made
hasDefaultSlot: hasDefaultSlot.value,
}))
const computedClasses = getClasses(classesObject)
const inputClasses = getInputClasses(classesObject)
Expand Down

0 comments on commit 6c69ff9

Please sign in to comment.