-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(VsForm): create vs-form component & form composable (#31)
- Loading branch information
Showing
19 changed files
with
690 additions
and
13 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -76,5 +76,8 @@ | |
"vite": "^4.4.5", | ||
"vitest": "^0.34.6", | ||
"vue-tsc": "^1.8.5" | ||
}, | ||
"dependencies": { | ||
"nanoid": "^5.0.4" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
<template> | ||
<div class="vs-form"> | ||
<vs-container> | ||
<slot /> | ||
</vs-container> | ||
</div> | ||
</template> | ||
|
||
<script lang="ts"> | ||
import { computed, defineComponent, nextTick, provide, watch } from 'vue'; | ||
import { VsComponent, VsFormProvide } from '@/declaration/types'; | ||
import VsContainer from '@/components/vs-container/VsContainer/VsContainer.vue'; | ||
import { useFormProvide } from '@/composables'; | ||
export const name = VsComponent.VsForm; | ||
const VsForm = defineComponent({ | ||
name: 'vs-form', | ||
components: { VsContainer }, | ||
props: { | ||
// v-model | ||
changed: { type: Boolean, default: false }, | ||
valid: { type: Boolean, default: true }, | ||
}, | ||
emits: ['update:changed', 'update:valid', 'error'], | ||
expose: ['validate', 'clear'], | ||
setup(_, { emit }) { | ||
const { labelObj, validObj, changedObj, validateFlag, clearFlag, getFormProvide } = useFormProvide(); | ||
provide<VsFormProvide>('vs-form', getFormProvide()); | ||
const isValid = computed(() => Object.values(validObj.value).every((v) => v)); | ||
const isChanged = computed(() => Object.values(changedObj.value).some((v) => v)); | ||
async function validate() { | ||
validateFlag.value = !validateFlag.value; | ||
await nextTick(); | ||
if (!isValid.value) { | ||
// on error callback with invalid labels | ||
const invalidIds = Object.keys(validObj.value).filter((id) => !validObj.value[id]); | ||
const invalidLabels = invalidIds.map((id) => labelObj.value[id]); | ||
emit('error', invalidLabels); | ||
} | ||
return isValid.value; | ||
} | ||
function clear() { | ||
clearFlag.value = !clearFlag.value; | ||
} | ||
watch(isValid, (valid) => { | ||
emit('update:valid', valid); | ||
}); | ||
watch(isChanged, (changed) => { | ||
emit('update:changed', changed); | ||
}); | ||
return { | ||
labelObj, | ||
changedObj, | ||
validObj, | ||
validateFlag, | ||
clearFlag, | ||
isValid, | ||
isChanged, | ||
validate, | ||
clear, | ||
}; | ||
}, | ||
}); | ||
export default VsForm; | ||
export type VsFormInstance = InstanceType<typeof VsForm>; | ||
</script> |
89 changes: 89 additions & 0 deletions
89
packages/vlossom/src/components/vs-form/VsForm/__tests__/vs-form.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
import { beforeEach, afterEach, describe, it, expect } from 'vitest'; | ||
import VsForm from './../VsForm.vue'; | ||
import { mount } from '@vue/test-utils'; | ||
import { nextTick } from 'vue'; | ||
|
||
function mountComponent() { | ||
return mount(VsForm); | ||
} | ||
|
||
describe('vs-form', () => { | ||
let wrapper: ReturnType<typeof mountComponent>; | ||
|
||
beforeEach(() => { | ||
wrapper = mount(VsForm, { | ||
props: { | ||
changed: false, | ||
'onUpdate:changed': (v: boolean) => wrapper.setProps({ changed: v }), | ||
valid: false, | ||
'onUpdate:valid': (v: boolean) => wrapper.setProps({ valid: v }), | ||
}, | ||
}); | ||
}); | ||
|
||
afterEach(() => { | ||
wrapper.unmount(); | ||
}); | ||
|
||
describe('validate', () => { | ||
it('validate 함수를 실행하면, validateFlag가 바뀐다', async () => { | ||
// when | ||
const valid = await wrapper.vm.validate(); | ||
|
||
// then | ||
expect(wrapper.vm.validateFlag).toBe(true); | ||
expect(valid).toBe(true); | ||
}); | ||
|
||
it('valid 여부가 바뀌면 update:valid event가 emit된다', async () => { | ||
// when | ||
wrapper.vm.validObj = { | ||
test: false, | ||
}; | ||
await nextTick(); | ||
|
||
// then | ||
expect(wrapper.emitted('update:valid')?.[0][0]).toBe(false); | ||
}); | ||
|
||
it('유효하지 않은 input의 label을 error 이벤트로 emit한다', async () => { | ||
// given | ||
wrapper.vm.validObj = { | ||
test: false, | ||
}; | ||
wrapper.vm.labelObj = { | ||
test: 'test', | ||
}; | ||
|
||
// when | ||
const valid = await wrapper.vm.validate(); | ||
|
||
// then | ||
expect(valid).toBe(false); | ||
expect(wrapper.emitted('error')?.[0][0]).toEqual(['test']); | ||
}); | ||
}); | ||
|
||
describe('changed', () => { | ||
it('변경이 있으면 update:changed event가 emit된다', async () => { | ||
// when | ||
wrapper.vm.changedObj = { | ||
test: true, | ||
}; | ||
await nextTick(); | ||
|
||
// then | ||
expect(wrapper.emitted('update:changed')?.[0][0]).toBe(true); | ||
}); | ||
}); | ||
|
||
describe('clear', () => { | ||
it('clear 함수를 실행하면, clearFlag가 바뀐다', async () => { | ||
// when | ||
await wrapper.vm.clear(); | ||
|
||
// then | ||
expect(wrapper.vm.clearFlag).toBe(true); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
import type { App } from 'vue'; | ||
import VsForm from './VsForm.vue'; | ||
|
||
export default { | ||
install(app: App<Element>) { | ||
app.component('vs-form', VsForm); | ||
}, | ||
}; | ||
export * from './VsForm.vue'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
export { default as VsForm } from './VsForm'; | ||
export type { VsFormInstance } from './VsForm'; |
60 changes: 60 additions & 0 deletions
60
packages/vlossom/src/composables/__tests__/form-provide-composable.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
import { describe, expect, it } from 'vitest'; | ||
import { useFormProvide } from '@/composables'; | ||
|
||
describe('form-provide-composable', () => { | ||
it('labelObj에 label 값이 업데이트 된다', () => { | ||
// given | ||
const { labelObj, updateLabel } = useFormProvide(); | ||
const id = 'test'; | ||
const label = 'Test label'; | ||
|
||
// when | ||
updateLabel(id, label); | ||
|
||
// then | ||
expect(labelObj.value[id]).toBe(label); | ||
}); | ||
|
||
it('changedObj에 changed 값이 업데이트 된다', () => { | ||
// given | ||
const { changedObj, updateChanged } = useFormProvide(); | ||
const id = 'test'; | ||
const changed = true; | ||
|
||
// when | ||
updateChanged(id, changed); | ||
|
||
// then | ||
expect(changedObj.value[id]).toBe(changed); | ||
}); | ||
|
||
it('validObj에 valid 값이 업데이트 된다', () => { | ||
// given | ||
const { validObj, updateValid } = useFormProvide(); | ||
const id = 'test'; | ||
const valid = true; | ||
|
||
// when | ||
updateValid(id, valid); | ||
|
||
// then | ||
expect(validObj.value[id]).toBe(valid); | ||
}); | ||
|
||
it('form에서 제거된다', () => { | ||
// given | ||
const { labelObj, changedObj, validObj, removeFromForm } = useFormProvide(); | ||
const id = 'test'; | ||
labelObj.value[id] = 'test'; | ||
changedObj.value[id] = true; | ||
validObj.value[id] = true; | ||
|
||
// when | ||
removeFromForm(id); | ||
|
||
// then | ||
expect(labelObj.value[id]).toBeUndefined(); | ||
expect(changedObj.value[id]).toBeUndefined(); | ||
expect(validObj.value[id]).toBeUndefined(); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.