Skip to content

Commit

Permalink
feat(form): add toggle, radio's documentation and testcase
Browse files Browse the repository at this point in the history
  • Loading branch information
adenvt committed May 19, 2022
1 parent 1afaa3d commit 6a0600a
Show file tree
Hide file tree
Showing 14 changed files with 456 additions and 87 deletions.
2 changes: 1 addition & 1 deletion .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ root = true
end_of_line = lf
insert_final_newline = true

[*.{js,json,yml}]
[*.{js,json,yml,ts,vue}]
charset = utf-8
indent_style = space
indent_size = 2
10 changes: 10 additions & 0 deletions .vscode/extensions.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"recommendations": [
"darkriszty.markdown-table-prettify",
"vue.volar",
"cpylua.language-postcss",
"bradlc.vscode-tailwindcss",
"editorconfig.editorconfig",
"lokalise.i18n-ally"
]
}
34 changes: 16 additions & 18 deletions components/checkbox/use-checkbox.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@

import { syncRef } from "@vueuse/shared"
import { computed, getCurrentInstance, ref } from "vue-demi"
import { computed, getCurrentInstance, ref, watch } from "vue-demi"
import { InputProps } from "../input/use-input"
import { valueIn, isEqual } from "../utils/value"

Expand All @@ -17,30 +16,29 @@ export interface CheckboxProps extends InputProps<unknown> {
uncheckedValue: unknown,
}

export function useVModel<P extends CheckboxProps> (props: P) {
export function useVModel (props: CheckboxProps) {
const { emit } = getCurrentInstance()
const localValue = ref(props.checked)

const checked = props.value ?? true
const unchecked = props.uncheckedValue ?? false
const checked = props.value
const unchecked = props.uncheckedValue
const localValue = ref(isChecked(props.modelValue, checked) || props.checked)

const model = computed({
get () {
return isChecked(props.modelValue, checked) || props.checked
},
set (value: boolean) {
if (!props.readonly && !props.disabled) {
const newValue = value ? checked : unchecked

if (Array.isArray(props.modelValue)) {
if (value === true) {
if (!valueIn(props.modelValue, newValue))
emit('update:modelValue', [...props.modelValue, newValue])
} else
emit('update:modelValue', props.modelValue.filter((old) => !isEqual(old, checked)))
const newValue = value ? checked : unchecked

if (Array.isArray(props.modelValue)) {
if (value === true) {
if (!valueIn(props.modelValue, newValue))
emit('update:modelValue', [...props.modelValue, newValue])
} else
emit('update:modelValue', newValue)
}
emit('update:modelValue', props.modelValue.filter((old) => !isEqual(old, checked)))
} else
emit('update:modelValue', newValue)

emit('change', value)
}
})

Expand Down
18 changes: 8 additions & 10 deletions components/input/use-input.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,24 @@
import { syncRef } from "@vueuse/shared"
import { computed, getCurrentInstance, ref, Ref } from "vue-demi"
import { computed, getCurrentInstance, ref, Ref, watch } from "vue-demi"

export interface InputProps<V = string> {
modelValue: V,
readonly?: boolean,
disabled?: boolean,
}

export function useVModel<V>(props: InputProps<V>): Ref<V> {
const localValue = ref(props?.modelValue) as Ref<V>
const localValue = ref(props.modelValue) as Ref<V>
const { emit } = getCurrentInstance()
const model = computed<V>({

const model = computed<V>({
get () {
return localValue.value
return props.modelValue
},
set (newValue) {
if (!props.readonly && !props.disabled)
emit('update:modelValue', newValue)
set (value) {
emit('update:modelValue', value)
},
})

syncRef(localValue, model)

return model
return localValue
}
3 changes: 3 additions & 0 deletions components/overlay/component.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,3 +100,6 @@ function onClick () {
</tr>
</tbody>
</table>

## See Also
- [Spinner](/spinner/component)
125 changes: 125 additions & 0 deletions components/radio/Radio.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
import { fireEvent, render } from "@testing-library/vue"
import { vi } from "vitest"
import { ref } from "vue-demi"
import Radio from "./Radio.vue"

it('should render properly without any prop', () => {
const screen = render({
components: { Radio },
template : `
<Radio />
`,
})

const radio = screen.getByTestId('radio')

expect(radio).toBeInTheDocument()
expect(radio).toHaveClass('radio', 'radio--radio')
})

it('should checked at start if `checked` props is provided', () => {
const screen = render({
components: { Radio },
template : `
<Radio checked />
`,
})

const radio = screen.getByTestId('radio')

expect(radio).toBeInTheDocument()
expect(radio).toHaveClass('radio--checked')
})

it('should have readonly style if `readonly` props is provided', () => {
const screen = render({
components: { Radio },
template : `
<Radio readonly />
`,
})

const radio = screen.getByTestId('radio')

expect(radio).toBeInTheDocument()
expect(radio).toHaveClass('radio--readonly')
})

it('should have disabled style if `disabled` props is provided', () => {
const screen = render({
components: { Radio },
template : `
<Radio disabled />
`,
})

const radio = screen.getByTestId('radio')

expect(radio).toBeInTheDocument()
expect(radio).toHaveClass('radio--disabled')
})

it('should have style checkbox if `apperance` set to "checkbox"', () => {
const screen = render({
components: { Radio },
template : `
<Radio apperance="checkbox" />
`,
})

const radio = screen.getByTestId('radio')

expect(radio).toBeInTheDocument()
expect(radio).toHaveClass('radio--checkbox')
expect(radio).not.toHaveClass('radio--radio')
})

it('should modify state in v-model', async () => {
const model = ref()
const screen = render({
components: { Radio },
template : `
<Radio v-model="model" value="apple">Apple</Radio>
<Radio v-model="model" value="grape">Grape</Radio>
`,
setup () {
return {
model,
}
}
})

const radioApple = screen.queryByText('Apple')
const radioGrape = screen.queryByText('Grape')

await fireEvent.click(radioApple)

expect(model.value).toBe('apple')

await fireEvent.click(radioGrape)

expect(model.value).toBe('grape')
})

it('should trigger event "change" when clicked', async () => {
const onChange = vi.fn()
const screen = render({
components: { Radio },
template : `
<Radio @change="onChange">
Apple
</Radio>
`,
setup () {
return {
onChange,
}
}
})

const radioApple = screen.queryByText('Apple')

await fireEvent.click(radioApple)

expect(onChange).toBeCalledWith(true)
})
55 changes: 43 additions & 12 deletions components/radio/Radio.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,27 @@
<label
data-testid="radio"
class="radio"
:class="classNames">
:class="classNames"
@click.prevent="toggle">
<input
v-model="value"
type="radio"
:value="model"
:name="name"
:disabled="disabled || readonly">
<span class="radio__icon">
<svg width="10" height="10" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<svg
v-if="apperance === 'checkbox'"
width="10"
height="10"
viewBox="0 0 14 10"
fill="none"
xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M3.81581 8.48528L5.23002 9.8995L6.64423 8.48528L13.7153 1.41421L12.3011 0L5.23002 7.07107L1.69449 3.53553L0.280273 4.94975L3.81581 8.48528Z" fill="white"/>
</svg>

<svg
v-else
width="10" height="10" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="7" cy="7" r="6.25" fill="white"/>
</svg>
</span>
Expand All @@ -21,13 +34,15 @@

<script lang="ts">
import { useVModel } from "./use-radio"
import { computed, defineComponent, ref } from "vue-demi"
import { computed, defineComponent, PropType } from "vue-demi"
export interface ChangedInteface {
value: any,
state: boolean,
}
type ApperanceType = 'radio' | 'checkbox'
export default defineComponent({
props: {
name: {
Expand All @@ -51,9 +66,9 @@ export default defineComponent({
type : Boolean,
default: false,
},
indeterminate: {
type : Boolean,
default: false,
apperance: {
type : String as PropType<ApperanceType>,
default: 'radio',
},
},
models: {
Expand All @@ -62,28 +77,38 @@ export default defineComponent({
},
emits: [
'update:modelValue',
'change',
],
setup (props) {
const value = useVModel(props)
const model = useVModel(props)
const classNames = computed(() => {
const result: string[] = []
if (value.value)
if (model.value)
result.push('radio--checked')
if (props.indeterminate)
result.push('radio--indeterminate')
if (props.readonly)
result.push('radio--readonly')
if (props.disabled)
result.push('radio--disabled')
if (props.apperance)
result.push(`radio--${props.apperance}`)
return result
})
function toggle () {
if (!props.disabled && !props.readonly)
model.value = true
}
return {
value,
model,
classNames,
toggle,
}
},
})
Expand Down Expand Up @@ -114,5 +139,11 @@ export default defineComponent({
@apply bg-primary-100;
}
}
&--checkbox {
.radio__icon {
@apply rounded-tn;
}
}
}
</style>
Loading

0 comments on commit 6a0600a

Please sign in to comment.