From edbe676646a72b25540fad84a224524f33843481 Mon Sep 17 00:00:00 2001 From: Gauthier Roebroeck Date: Fri, 30 Jan 2026 17:22:39 +0800 Subject: [PATCH] filters --- next-ui/src/types/filter.test.ts | 45 ++++++++++++++++++++++++- next-ui/src/types/filter.ts | 57 +++++++++++++++++++++++++++++--- 2 files changed, 96 insertions(+), 6 deletions(-) diff --git a/next-ui/src/types/filter.test.ts b/next-ui/src/types/filter.test.ts index 031df192..3469352b 100644 --- a/next-ui/src/types/filter.test.ts +++ b/next-ui/src/types/filter.test.ts @@ -1,6 +1,11 @@ import { describe, expect, test } from 'vitest' import * as v from 'valibot' -import { SchemaAnyAll } from '@/types/filter' +import { + SchemaAnyAll, + SchemaFilterSeriesStatus, + SchemaFilterStrings, + SchemaSeriesStatus, +} from '@/types/filter' describe('schema any all', () => { test('anyOf', () => { @@ -30,3 +35,41 @@ describe('schema any all', () => { expect(defaults).toStrictEqual(input) }) }) + +describe('schema series status', () => { + test('correct value', () => { + const input = { v: 'ENDED' } + const result = v.parse(SchemaSeriesStatus, input) + + expect(result).toStrictEqual(input) + }) + + test('case insensitive', () => { + const input = { v: 'enDeD' } + const result = v.parse(SchemaSeriesStatus, input) + + expect(result).toStrictEqual({ v: 'ENDED' }) + }) + + test('other value throws error', () => { + const input = { v: 'whatever' } + + expect(() => v.parse(SchemaSeriesStatus, input)).toThrowError() + }) +}) + +describe('filter schemas have a default value', () => { + test('SchemaFilterSeriesStatus', () => { + const expected = { m: 'anyOf', v: [] } + const defaults = v.getDefaults(SchemaFilterSeriesStatus) + + expect(defaults).toStrictEqual(expected) + }) + + test('SchemaFilterStrings', () => { + const expected = { m: 'anyOf', v: [] } + const defaults = v.getDefaults(SchemaFilterStrings) + + expect(defaults).toStrictEqual(expected) + }) +}) diff --git a/next-ui/src/types/filter.ts b/next-ui/src/types/filter.ts index 05a0b99b..85c8ab7d 100644 --- a/next-ui/src/types/filter.ts +++ b/next-ui/src/types/filter.ts @@ -2,23 +2,70 @@ import * as v from 'valibot' export type AnyAll = 'anyOf' | 'allOf' +/////////////////////////////////////////////////////////////////////////// +// This file contains all the valibot schemas for the Criteria API. +// Those objects end up serialized in the address bar in the query params, +// that's why the properties and values are kept to the shortest. +/////////////////////////////////////////////////////////////////////////// + +/** + * Schema for criteria API with `anyOf` or `allOf` condition. + */ export const SchemaAnyAll = v.object({ + /** + * Shorthand for `mode`. + */ m: v.optional(v.picklist(['anyOf', 'allOf']), 'anyOf'), }) -const SchemaIncludeExclude = v.object({ i: v.optional(v.picklist(['i', 'e'])) }) +const SchemaIncludeExclude = v.object({ + /** + * Shorthand for `include`. + * + * Values can be: include (`i`) or exclude (`e`) + */ + i: v.optional(v.picklist(['i', 'e'])), +}) export const SchemaSeriesStatus = v.object({ ...SchemaIncludeExclude.entries, - v: v.optional(v.picklist(['ENDED', 'ONGOING', 'ABANDONED', 'HIATUS'])), + /** + * Shorthand for `value`. + */ + v: v.pipe(v.string(), v.toUpperCase(), v.picklist(['ENDED', 'ONGOING', 'ABANDONED', 'HIATUS'])), }) +export const SchemaString = v.object({ + ...SchemaIncludeExclude.entries, + /** + * Shorthand for `value`. + */ + v: v.string(), +}) + +//////////////////////////////////////////////////// +// All schema filters need to have a default value +//////////////////////////////////////////////////// + +/** + * Schema for Series Status. + */ export const SchemaFilterSeriesStatus = v.object({ ...SchemaAnyAll.entries, - v: v.fallback(v.optional(v.array(SchemaSeriesStatus), []), []), + /** + * Shorthand for 'value' + */ + v: v.optional(v.array(SchemaSeriesStatus), []), }) -export const FilterSeriesGenre = v.object({ +/** + * Schema for a list of string. + * Can be used for tags, genre, sharing labels… + */ +export const SchemaFilterStrings = v.object({ ...SchemaAnyAll.entries, - value: v.array(v.string()), + /** + * Shorthand for 'value' + */ + v: v.optional(v.array(SchemaString), []), })