Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(BCheckbox)!: Implement reverse and without label #1823

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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) => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am a fan of this change, emitting events for this when it really is just a copy of modelvalue is strange. However, first: it needs to be labeled as a breaking change commit. Second: BFormRadioGroup also has this same code, but it is not removed.

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