diff --git a/.changeset/sharp-doors-behave.md b/.changeset/sharp-doors-behave.md
new file mode 100644
index 0000000..4bdbdac
--- /dev/null
+++ b/.changeset/sharp-doors-behave.md
@@ -0,0 +1,6 @@
+---
+"@fessional/razor-mobile": patch
+"@fessional/razor-common": patch
+---
+
+✨ ionicValidateInput support default v-model value #122
diff --git a/layers/common/package.json b/layers/common/package.json
index 868f319..2e88be8 100644
--- a/layers/common/package.json
+++ b/layers/common/package.json
@@ -1,6 +1,6 @@
{
"name": "@fessional/razor-common",
- "version": "0.3.5",
+ "version": "0.3.6",
"description": "common layer for mobile and desktop",
"type": "module",
"main": "./nuxt.config.ts",
diff --git a/layers/mobile/package.json b/layers/mobile/package.json
index 1d7b9f5..ce5c01b 100644
--- a/layers/mobile/package.json
+++ b/layers/mobile/package.json
@@ -1,6 +1,6 @@
{
"name": "@fessional/razor-mobile",
- "version": "0.3.5",
+ "version": "0.3.6",
"description": "mobile layer with ionic and capacitor",
"type": "module",
"main": "./nuxt.config.ts",
diff --git a/layers/mobile/tests/ionic-validator.test.ts b/layers/mobile/tests/ionic-validator.test.ts
index 3d5951b..3aaac9f 100644
--- a/layers/mobile/tests/ionic-validator.test.ts
+++ b/layers/mobile/tests/ionic-validator.test.ts
@@ -3,78 +3,104 @@ import { describe, it, expect, vi } from 'vitest';
import { ref } from 'vue';
import { ionicValidateInput } from '../utils/ionic-validator';
-// Mock Ionic IonInput component
-const mockClassList = {
- add: vi.fn(),
- remove: vi.fn(),
-};
-
-const mockInputRef = ref({
- $el: {
- classList: mockClassList,
- },
-});
-
-describe('validateIonicInput', () => {
- // Test case: valid input using regex pattern
- it('should add ion-valid class for valid input', () => {
- const validateFn = ionicValidateInput(mockInputRef, /^[1-9][0-9]?$/);
-
- // Simulate valid input
- const inputEvent = { target: { value: '10' } } as unknown as Event;
- const result = validateFn(inputEvent);
-
- expect(result).toBe(true);
+describe('ionicValidateInput', () => {
+ it('validates input correctly with a regex', () => {
+ const mockClassList = {
+ add: vi.fn(),
+ remove: vi.fn(),
+ };
+
+ const inputRef = ref({ $el: { classList: mockClassList } });
+ const checkFun = /^[1-9][0-9]?$/;
+ const modelRef = ref('');
+
+ const validator = ionicValidateInput(inputRef, checkFun, modelRef);
+
+ // Test valid input
+ const event = {
+ target: { value: '25' },
+ type: 'input',
+ } as unknown as Event;
+
+ expect(validator(event)).toBe(true);
expect(mockClassList.add).toHaveBeenCalledWith('ion-valid');
expect(mockClassList.remove).toHaveBeenCalledWith('ion-invalid');
- });
- // Test case: invalid input using regex pattern
- it('should add ion-invalid class for invalid input', () => {
- const validateFn = ionicValidateInput(mockInputRef, /^[1-9][0-9]?$/);
+ // Reset mocks
+ mockClassList.add.mockClear();
+ mockClassList.remove.mockClear();
- // Simulate invalid input
- const inputEvent = { target: { value: '100' } } as unknown as Event;
- const result = validateFn(inputEvent);
+ // Test invalid input
+ const invalidEvent = {
+ target: { value: '-5' },
+ type: 'input',
+ } as unknown as Event;
- expect(result).toBe(false);
+ expect(validator(invalidEvent)).toBe(false);
expect(mockClassList.add).toHaveBeenCalledWith('ion-invalid');
expect(mockClassList.remove).toHaveBeenCalledWith('ion-valid');
});
- // Test case: input blur event
- it('should add ion-touched class on blur', () => {
- const validateFn = ionicValidateInput(mockInputRef, /^[1-9][0-9]?$/);
+ it('adds ion-touched class on blur', () => {
+ const mockClassList = {
+ add: vi.fn(),
+ remove: vi.fn(),
+ };
- // Simulate blur event
- const blurEvent = new Event('blur');
- const result = validateFn(blurEvent);
+ const inputRef = ref({ $el: { classList: mockClassList } });
+ const checkFun = /^[1-9][0-9]?$/;
- expect(result).toBeNull();
+ const validator = ionicValidateInput(inputRef, checkFun);
+
+ const blurEvent = { type: 'blur' } as unknown as Event;
+
+ expect(validator(blurEvent)).toBe(null);
expect(mockClassList.add).toHaveBeenCalledWith('ion-touched');
});
- // Test case: direct string input
- it('should add ion-valid class for valid string input', () => {
- const validateFn = ionicValidateInput(mockInputRef, /^[1-9][0-9]?$/);
+ it('validates input with a custom function', () => {
+ const mockClassList = {
+ add: vi.fn(),
+ remove: vi.fn(),
+ };
- // Simulate valid string input
- const result = validateFn('5');
+ const inputRef = ref({ $el: { classList: mockClassList } });
+ const checkFun = (value: string) => value === 'valid';
+ const modelRef = ref('');
- expect(result).toBe(true);
+ const validator = ionicValidateInput(inputRef, checkFun, modelRef);
+
+ // Test valid input
+ expect(validator('valid')).toBe(true);
expect(mockClassList.add).toHaveBeenCalledWith('ion-valid');
expect(mockClassList.remove).toHaveBeenCalledWith('ion-invalid');
- });
- // Test case: invalid string input
- it('should add ion-invalid class for invalid string input', () => {
- const validateFn = ionicValidateInput(mockInputRef, /^[1-9][0-9]?$/);
+ // Reset mocks
+ mockClassList.add.mockClear();
+ mockClassList.remove.mockClear();
- // Simulate invalid string input
- const result = validateFn('100');
-
- expect(result).toBe(false);
+ // Test invalid input
+ expect(validator('invalid')).toBe(false);
expect(mockClassList.add).toHaveBeenCalledWith('ion-invalid');
expect(mockClassList.remove).toHaveBeenCalledWith('ion-valid');
});
+
+ it('uses modelRef value when event is null', () => {
+ const mockClassList = {
+ add: vi.fn(),
+ remove: vi.fn(),
+ };
+
+ const inputRef = ref({ $el: { classList: mockClassList } });
+ const checkFun = /^[1-9][0-9]?$/;
+ const modelRef = ref('42');
+
+ const validator = ionicValidateInput(inputRef, checkFun, modelRef);
+
+ expect(validator()).toBe(true);
+ expect(validator(null)).toBe(true);
+ expect(validator(undefined)).toBe(true);
+ expect(mockClassList.add).toHaveBeenCalledWith('ion-valid');
+ expect(mockClassList.remove).toHaveBeenCalledWith('ion-invalid');
+ });
});
diff --git a/layers/mobile/utils/ionic-validator.ts b/layers/mobile/utils/ionic-validator.ts
index 8f8908a..740afe2 100644
--- a/layers/mobile/utils/ionic-validator.ts
+++ b/layers/mobile/utils/ionic-validator.ts
@@ -12,7 +12,7 @@
*
*
* ```
*
@@ -24,18 +24,30 @@ export function ionicValidateInput(
// eslint-disable-next-line @typescript-eslint/no-explicit-any
inputRef: Ref,
checkFun: RegExp | ((value: string, event?: Event) => boolean),
-): (ev: Event | string) => boolean | null {
- return (ev: Event | string) => {
+ modelRef?: Ref,
+): (ev?: Event | string | null) => boolean | null {
+ return (ev?: Event | string | null) => {
const classList = inputRef.value.$el.classList;
- const isValue = typeof ev === 'string';
- if (isValue || /blur/i.test(ev.type)) {
- classList.add('ion-touched');
- if (!isValue) return null;
+ let value: string;
+ let event: Event | undefined;
+
+ if (ev == null) {
+ value = modelRef?.value ?? '';
+ }
+ else if (typeof ev === 'string') {
+ value = ev;
+ }
+ else {
+ if (/blur/i.test(ev.type)) {
+ classList.add('ion-touched');
+ return null;
+ }
+ event = ev;
+ value = (ev.target as HTMLInputElement).value;
}
- const value = isValue ? ev : (ev.target as HTMLInputElement).value;
- const valid = typeof checkFun === 'function' ? checkFun(value, isValue ? undefined : ev) : checkFun.test(value);
+ const valid = typeof checkFun === 'function' ? checkFun(value, event) : checkFun.test(value);
if (valid) {
classList.add('ion-valid');
diff --git a/package.json b/package.json
index 0fdc55e..b6fe323 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "@fessional/razor",
- "version": "0.3.5",
+ "version": "0.3.6",
"description": "Use front-end tech (Nuxt/Ts) to build multi-platform from one codebase, suitable for small team and app to write app once, apply almost anywhere.",
"type": "module",
"files": [