Skip to content

Commit

Permalink
feat(beforeChange): change beforeChange function parameters (#161)
Browse files Browse the repository at this point in the history
  • Loading branch information
smithoo authored Apr 11, 2024
1 parent 8922843 commit ca64bce
Show file tree
Hide file tree
Showing 11 changed files with 149 additions and 34 deletions.
24 changes: 10 additions & 14 deletions packages/vlossom/src/components/vs-checkbox-set/VsCheckboxSet.vue
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ export default defineComponent({
colorScheme: { type: String as PropType<ColorScheme> },
styleSet: { type: [String, Object] as PropType<string | VsCheckboxSetStyleSet> },
beforeChange: {
type: Function as PropType<(checked: boolean, target: any) => Promise<boolean> | null>,
type: Function as PropType<(from: any, to: any, option: any) => Promise<boolean> | null>,
default: null,
},
vertical: { type: Boolean, default: false },
Expand Down Expand Up @@ -131,10 +131,6 @@ export default defineComponent({
const inputValue = ref(modelValue.value);
function onClear() {
inputValue.value = [];
}
const { getOptionLabel, getOptionValue } = useInputOption(
inputValue,
options,
Expand All @@ -153,7 +149,9 @@ export default defineComponent({
messages,
rules: allRules,
callbacks: {
onClear,
onClear: () => {
inputValue.value = [];
},
},
});
Expand All @@ -163,20 +161,18 @@ export default defineComponent({
async function onToggle(option: any, checked: boolean) {
const beforeChangeFn = beforeChange.value;
const targetOptionValue = getOptionValue(option);
const toValue = checked
? [...inputValue.value, targetOptionValue]
: inputValue.value.filter((v: any) => !utils.object.isEqual(v, targetOptionValue));
if (beforeChangeFn) {
const result = await beforeChangeFn(isChecked(option), option);
const result = await beforeChangeFn(inputValue.value, toValue, option);
if (!result) {
return;
}
}
const targetValue = getOptionValue(option);
if (checked) {
inputValue.value = [...inputValue.value, targetValue];
} else {
inputValue.value = inputValue.value.filter((v: any) => !utils.object.isEqual(v, targetValue));
}
inputValue.value = toValue;
}
function onFocus(option: any, e: FocusEvent) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { describe, expect, it } from 'vitest';
import { afterEach, describe, expect, it, vi } from 'vitest';
import { mount } from '@vue/test-utils';
import { nextTick } from 'vue';
import VsCheckboxSet from '../VsCheckboxSet.vue';
Expand Down Expand Up @@ -286,6 +286,29 @@ describe('vs-checkbox-set', () => {
});

describe('before change', () => {
afterEach(() => {
vi.restoreAllMocks();
});

it('beforeChange ํ•จ์ˆ˜์— from, to, option ์ธ์ž๊ฐ€ ์ „๋‹ฌ๋œ๋‹ค', async () => {
// given
const beforeChange = vi.fn().mockResolvedValue(true);
const wrapper: ReturnType<typeof mountComponent> = mount(VsCheckboxSet, {
props: {
modelValue: ['A'],
'onUpdate:modelValue': (e) => wrapper.setProps({ modelValue: e }),
options: ['A', 'B', 'C'],
beforeChange,
},
});

// when
await wrapper.find('input[value="B"]').setValue(true);

// then
expect(beforeChange).toHaveBeenCalledWith(['A'], ['A', 'B'], 'B');
});

it('beforeChange ํ•จ์ˆ˜๊ฐ€ Promise<true>๋ฅผ ๋ฆฌํ„ดํ•˜๋ฉด ๊ฐ’์ด ์—…๋ฐ์ดํŠธ ๋œ๋‹ค', async () => {
// given
const wrapper: ReturnType<typeof mountComponent> = mount(VsCheckboxSet, {
Expand Down
7 changes: 4 additions & 3 deletions packages/vlossom/src/components/vs-checkbox/VsCheckbox.vue
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ export default defineComponent({
styleSet: { type: [String, Object] as PropType<string | VsCheckboxStyleSet> },
ariaLabel: { type: String, default: '' },
beforeChange: {
type: Function as PropType<(value: any) => Promise<boolean> | null>,
type: Function as PropType<(from: any, to: any) => Promise<boolean> | null>,
default: null,
},
checked: { type: Boolean, default: false },
Expand Down Expand Up @@ -143,14 +143,15 @@ export default defineComponent({
async function onToggle(c: boolean) {
const beforeChangeFn = beforeChange.value;
const toValue = getUpdatedValue(c, inputValue.value);
if (beforeChangeFn) {
const result = await beforeChangeFn(inputValue.value);
const result = await beforeChangeFn(inputValue.value, toValue);
if (!result) {
return;
}
}
inputValue.value = getUpdatedValue(c, inputValue.value);
inputValue.value = toValue;
}
function onFocus(e: FocusEvent) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { describe, expect, it } from 'vitest';
import { afterEach, describe, expect, it, vi } from 'vitest';
import { mount } from '@vue/test-utils';
import { nextTick } from 'vue';
import VsCheckbox from './../VsCheckbox.vue';
Expand Down Expand Up @@ -389,6 +389,28 @@ describe('vs-checkbox', () => {
});

describe('before change', () => {
afterEach(() => {
vi.restoreAllMocks();
});

it('beforeChange ํ•จ์ˆ˜์— from, to ์ธ์ž๊ฐ€ ์ „๋‹ฌ๋œ๋‹ค', async () => {
// given
const beforeChange = vi.fn().mockResolvedValue(true);
const wrapper: ReturnType<typeof mountComponent> = mount(VsCheckbox, {
props: {
trueValue: 'A',
falseValue: 'B',
beforeChange,
},
});

// when
await wrapper.find('input').setValue('A');

// then
expect(beforeChange).toHaveBeenCalledWith('B', 'A');
});

it('beforeChange ํ•จ์ˆ˜๊ฐ€ Promise<true>๋ฅผ ๋ฆฌํ„ดํ•˜๋ฉด ๊ฐ’์ด ์—…๋ฐ์ดํŠธ ๋œ๋‹ค', async () => {
// given
const wrapper: ReturnType<typeof mountComponent> = mount(VsCheckbox, {
Expand Down
8 changes: 5 additions & 3 deletions packages/vlossom/src/components/vs-radio-set/VsRadioSet.vue
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ export default defineComponent({
colorScheme: { type: String as PropType<ColorScheme> },
styleSet: { type: [String, Object] as PropType<string | VsRadioSetStyleSet> },
beforeChange: {
type: Function as PropType<(option: any) => Promise<boolean> | null>,
type: Function as PropType<(from: any, to: any, option: any) => Promise<boolean> | null>,
default: null,
},
name: { type: String, required: true },
Expand Down Expand Up @@ -156,15 +156,17 @@ export default defineComponent({
});
async function onToggle(option: any) {
// radio change event value is always true
const beforeChangeFn = beforeChange.value;
const toValue = getOptionValue(option);
if (beforeChangeFn) {
const result = await beforeChangeFn(option);
const result = await beforeChangeFn(inputValue.value, toValue, option);
if (!result) {
return;
}
}
inputValue.value = getOptionValue(option);
inputValue.value = toValue;
}
function onFocus(option: any, e: FocusEvent) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { afterEach, describe, expect, it } from 'vitest';
import { afterEach, describe, expect, it, vi } from 'vitest';
import { mount } from '@vue/test-utils';
import { nextTick } from 'vue';
import VsRadioSet from './../VsRadioSet.vue';
Expand Down Expand Up @@ -327,6 +327,26 @@ describe('vs-radio-set', () => {
});

describe('before change', () => {
it('beforeChange ํ•จ์ˆ˜์— from, to ์ธ์ž๊ฐ€ ์ „๋‹ฌ๋œ๋‹ค ', async () => {
// given
const beforeChange = vi.fn().mockResolvedValue(true);
const wrapper: ReturnType<typeof mountComponent> = mount(VsRadioSet, {
props: {
name: 'radio',
modelValue: 'A',
'onUpdate:modelValue': (e) => wrapper.setProps({ modelValue: e }),
options: ['A', 'B', 'C'],
beforeChange,
},
});

// when
await wrapper.find('input[value="C"]').trigger('change');

// then
expect(beforeChange).toHaveBeenCalledWith('A', 'C', 'C');
});

it('beforeChange ํ•จ์ˆ˜๊ฐ€ Promise<true>๋ฅผ ๋ฆฌํ„ดํ•˜๋ฉด ๊ฐ’์ด ์—…๋ฐ์ดํŠธ ๋œ๋‹ค', async () => {
// given
const wrapper: ReturnType<typeof mountComponent> = mount(VsRadioSet, {
Expand Down
9 changes: 5 additions & 4 deletions packages/vlossom/src/components/vs-radio/VsRadio.vue
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ export default defineComponent({
styleSet: { type: [String, Object] as PropType<string | VsRadioStyleSet> },
ariaLabel: { type: String, default: '' },
beforeChange: {
type: Function as PropType<(value: any) => Promise<boolean> | null>,
type: Function as PropType<(from: any, to: any) => Promise<boolean> | null>,
default: null,
},
checked: { type: Boolean, default: false },
Expand Down Expand Up @@ -131,16 +131,17 @@ export default defineComponent({
});
async function onToggle() {
// radio changed value is always true
// radio change event value is always true
const beforeChangeFn = beforeChange.value;
const toValue = radioValue.value;
if (beforeChangeFn) {
const result = await beforeChangeFn(inputValue.value);
const result = await beforeChangeFn(inputValue.value, toValue);
if (!result) {
return;
}
}
inputValue.value = radioValue.value;
inputValue.value = toValue;
}
function onFocus(e: FocusEvent) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { beforeEach, afterEach, describe, expect, it } from 'vitest';
import { beforeEach, afterEach, describe, expect, it, vi } from 'vitest';
import { mount } from '@vue/test-utils';
import { nextTick } from 'vue';
import VsRadio from './../VsRadio.vue';
Expand Down Expand Up @@ -274,6 +274,10 @@ describe('vs-radio (multiple inputs)', () => {
});
});

afterEach(() => {
vi.restoreAllMocks();
});

it('์ฒ˜์Œ์—๋Š” ์•„๋ฌด radio๋„ ์„ ํƒ๋˜์–ด ์žˆ์ง€ ์•Š๋‹ค', () => {
// then
expect(radio1.vm.isChecked).toBe(false);
Expand Down Expand Up @@ -334,4 +338,24 @@ describe('vs-radio (multiple inputs)', () => {
expect(radio2.vm.computedMessages).toHaveLength(1);
expect(radio2.html()).toContain('required');
});

it('beforeChange ํ•จ์ˆ˜์— from, to ์ธ์ž๊ฐ€ ์ „๋‹ฌ๋œ๋‹ค ', async () => {
// given
const beforeChange = vi.fn().mockResolvedValue(true);
const radio3: ReturnType<typeof mountComponent> = mount(VsRadio, {
props: {
name: 'radio',
radioValue: 'C',
modelValue: 'A',
'onUpdate:modelValue': (e) => radio3.setProps({ modelValue: e }),
beforeChange,
},
});

// when
await radio3.find('input').trigger('change');

// then
expect(beforeChange).toHaveBeenCalledWith('A', 'C');
});
});
7 changes: 4 additions & 3 deletions packages/vlossom/src/components/vs-switch/VsSwitch.vue
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ export default defineComponent({
colorScheme: { type: String as PropType<ColorScheme> },
styleSet: { type: [String, Object] as PropType<string | VsSwitchStyleSet> },
beforeChange: {
type: Function as PropType<(value: any) => Promise<boolean> | null>,
type: Function as PropType<(from: any, to: any) => Promise<boolean> | null>,
default: null,
},
multiple: { type: Boolean, default: false },
Expand Down Expand Up @@ -164,14 +164,15 @@ export default defineComponent({
}
const beforeChangeFn = beforeChange.value;
const toValue = getUpdatedValue(!isChecked.value, inputValue.value);
if (beforeChangeFn) {
const result = await beforeChangeFn(inputValue.value);
const result = await beforeChangeFn(inputValue.value, toValue);
if (!result) {
return;
}
}
inputValue.value = getUpdatedValue(!isChecked.value, inputValue.value);
inputValue.value = toValue;
}
function onFocus(e: FocusEvent) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { describe, expect, it } from 'vitest';
import { afterEach, describe, expect, it, vi } from 'vitest';
import { mount } from '@vue/test-utils';
import { nextTick } from 'vue';
import VsSwitch from '../VsSwitch.vue';
Expand Down Expand Up @@ -544,6 +544,28 @@ describe('vs-switch', () => {
});

describe('before change', () => {
afterEach(() => {
vi.restoreAllMocks();
});

it('beforeChange ํ•จ์ˆ˜์— from, to ์ธ์ž๊ฐ€ ์ „๋‹ฌ๋œ๋‹ค', async () => {
// given
const beforeChange = vi.fn().mockResolvedValue(true);
const wrapper: ReturnType<typeof mountComponent> = mount(VsSwitch, {
props: {
modelValue: false,
'onUpdate:modelValue': (e) => wrapper.setProps({ modelValue: e }),
beforeChange,
},
});

// when
await wrapper.find('input').setValue(true);

// then
expect(beforeChange).toHaveBeenCalledWith(false, true);
});

it('beforeChange ํ•จ์ˆ˜๊ฐ€ Promise<true>๋ฅผ ๋ฆฌํ„ดํ•˜๋ฉด ๊ฐ’์ด ์—…๋ฐ์ดํŠธ ๋œ๋‹ค', async () => {
// given
const wrapper: ReturnType<typeof mountComponent> = mount(VsSwitch, {
Expand Down
7 changes: 5 additions & 2 deletions packages/vlossom/src/composables/input-option-composable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,11 @@ export function useInputOption(
if (utils.object.isPlainObject(option) && optionValue.value) {
const value = _.at(option, [optionValue.value])[0];

if (!value) {
console.error(`optionValue: ${optionValue.value} is not found in option: ${JSON.stringify(option)}`);
if (value === undefined || value === null) {
logUtil.logError(
'optionValue',
`${optionValue.value} is not found in option: ${JSON.stringify(option)}`,
);
}

return value;
Expand Down

0 comments on commit ca64bce

Please sign in to comment.