Skip to content

Commit

Permalink
✨ ionicValidateInput support default v-model value #122
Browse files Browse the repository at this point in the history
  • Loading branch information
trydofor committed Dec 19, 2024
1 parent 204ba88 commit add5910
Show file tree
Hide file tree
Showing 6 changed files with 107 additions and 63 deletions.
6 changes: 6 additions & 0 deletions .changeset/sharp-doors-behave.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@fessional/razor-mobile": patch
"@fessional/razor-common": patch
---

✨ ionicValidateInput support default v-model value #122
2 changes: 1 addition & 1 deletion layers/common/package.json
Original file line number Diff line number Diff line change
@@ -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",
Expand Down
2 changes: 1 addition & 1 deletion layers/mobile/package.json
Original file line number Diff line number Diff line change
@@ -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",
Expand Down
128 changes: 77 additions & 51 deletions layers/mobile/tests/ionic-validator.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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');
});
});
30 changes: 21 additions & 9 deletions layers/mobile/utils/ionic-validator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
* </template>
* <script setup lang="ts">
* const pkgInputRef = ref();
* const onPkgInput = validateIonicInput(pkgInputRef, /^[1-9][0-9]?$/);
* const onPkgInput = validateIonicInput(pkgInputRef, /^[1-9][0-9]?$/, pkgInput);
* </script>
* ```
*
Expand All @@ -24,18 +24,30 @@ export function ionicValidateInput(
// eslint-disable-next-line @typescript-eslint/no-explicit-any
inputRef: Ref<any>,
checkFun: RegExp | ((value: string, event?: Event) => boolean),
): (ev: Event | string) => boolean | null {
return (ev: Event | string) => {
modelRef?: Ref<string>,
): (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');
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -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": [
Expand Down

0 comments on commit add5910

Please sign in to comment.