From 727f84e4c20239c65e33f7964212799e22202b18 Mon Sep 17 00:00:00 2001 From: ThatGuySam Date: Sun, 15 Mar 2026 13:41:33 -0500 Subject: [PATCH] refactor(search): extract shared filter helper Share filter parsing and provider mapping logic between Stork and upcoming search adapters. This keeps the existing Stork API stable while adding Pagefind-oriented filter serialization under test. --- helpers/search/filters.js | 119 +++++++++++++++++++++++++++++++++ helpers/stork/browser.js | 122 +--------------------------------- test/prebuild/filters.test.js | 14 ++++ 3 files changed, 135 insertions(+), 120 deletions(-) create mode 100644 helpers/search/filters.js diff --git a/helpers/search/filters.js b/helpers/search/filters.js new file mode 100644 index 0000000..ed65174 --- /dev/null +++ b/helpers/search/filters.js @@ -0,0 +1,119 @@ +import { filterSeparator } from '~/helpers/constants.js' +import { isString } from '~/helpers/check-types.js' + +export class SearchFilters { + constructor({ + initialFilters = {} + } = {}) { + this.initialFilters = initialFilters + + this.filters = { + ...initialFilters + } + } + + get list () { + return Object.entries( this.filters ).map( ([ filterKey, filterValue ]) => { + return `${ filterKey }${ filterSeparator }${ filterValue }` + } ) + } + + get asQuery () { + return this.list.join(' ') + } + + get asPagefindFilters () { + return Object.fromEntries( Object.entries( this.filters ).map( ([ filterKey, filterValue ]) => { + return [ filterKey, [ filterValue ] ] + }) ) + } + + getByKey ( key ) { + return `${ key }${ filterSeparator }${ this.filters[ key ] }` + } + + isQueryValue ( filterNameOrQueryValue ) { + return filterNameOrQueryValue.includes( filterSeparator ) + } + + getKeyAndValue ( filterQueryValue ) { + const key = filterQueryValue.substring(0, filterQueryValue.indexOf( filterSeparator )) + const value = filterQueryValue.substring(filterQueryValue.indexOf( filterSeparator )+1) + + return { key, value } + } + + getFilterNameAndValueFromString ( filterNameOrQueryValue ) { + if ( this.isQueryValue( filterNameOrQueryValue ) ) { + return this.getKeyAndValue( filterNameOrQueryValue ) + } + + return { + key: filterNameOrQueryValue, + value: null + } + } + + remove ( filterName ) { + if ( this.isQueryValue( filterName ) ) throw new Error(`${ filterName } is not a valid filter name`) + + delete this.filters[ filterName ] + } + + setFromStringArray ( filterStringArray ) { + filterStringArray.forEach( filterString => { + const { key, value } = this.getFilterNameAndValueFromString( filterString ) + + this.filters[ key ] = value + }) + } + + setFromString ( filterNameOrQueryValue ) { + const { + key, + value = '' + } = this.getFilterNameAndValueFromString( filterNameOrQueryValue ) + + if ( value.trim().length === 0 ) throw new Error(`${ filterNameOrQueryValue } is not a valid filter value`) + + this.set( key, value ) + } + + set ( filterName, filterValue ) { + if ( this.isQueryValue( filterName ) ) throw new Error(`${ filterName } is not a valid filter name`) + + this.filters[ filterName ] = filterValue + } + + toggleFilter ( filterNameOrQueryValue, filterValue = null ) { + const fromString = this.getFilterNameAndValueFromString( filterNameOrQueryValue ) + + const filterName = fromString.key + filterValue = filterValue || fromString.value + + if ( this.has( filterNameOrQueryValue ) ) { + this.remove( filterName ) + + return + } + + if ( typeof filterValue !== 'string' ) { + throw new Error(`Filter value must be a string. Got ${ typeof filterValue }`) + } + + this.set( filterName, filterValue ) + } + + has ( filterNameOrQueryValue ) { + const { + key : filterName, + value : filterValue = null + } = this.getFilterNameAndValueFromString( filterNameOrQueryValue ) + + if ( isString( filterValue ) ) { + return !!this.filters[ filterName ] && this.filters[ filterName ] === filterValue + } + + return !!this.filters[ filterName ] + } +} diff --git a/helpers/stork/browser.js b/helpers/stork/browser.js index 851c06e..18bd324 100644 --- a/helpers/stork/browser.js +++ b/helpers/stork/browser.js @@ -1,6 +1,5 @@ -import { filterSeparator } from '~/helpers/constants.js' - import { isString } from '~/helpers/check-types.js' +import { SearchFilters } from '~/helpers/search/filters.js' import { storkIndexRelativeURL, @@ -276,121 +275,4 @@ export class StorkClient { } } -export class StorkFilters { - constructor({ - initialFilters = {} - } = {}) { - this.initialFilters = initialFilters - - this.filters = { - ...initialFilters - } - } - - get list () { - return Object.entries( this.filters ).map( ([ filterKey, filterValue ]) => { - return `${ filterKey }${ filterSeparator }${ filterValue }` - } ) - } - - get asQuery () { - return this.list.join(' ') - } - - getByKey ( key ) { - return `${ key }${ filterSeparator }${ this.filters[ key ] }` - } - - isQueryValue ( filterNameOrQueryValue ) { - return filterNameOrQueryValue.includes( filterSeparator ) - } - - getKeyAndValue ( filterQueryValue ) { - const key = filterQueryValue.substring(0, filterQueryValue.indexOf( filterSeparator )) - const value = filterQueryValue.substring(filterQueryValue.indexOf( filterSeparator )+1) - - return { key, value } - } - - getFilterNameAndValueFromString ( filterNameOrQueryValue ) { - if ( this.isQueryValue( filterNameOrQueryValue ) ) { - return this.getKeyAndValue( filterNameOrQueryValue ) - } - - return { - key: filterNameOrQueryValue, - value: null - } - } - - remove ( filterName ) { - // Throw error if it's not a valid filter name - if ( this.isQueryValue( filterName ) ) throw new Error(`${ filterName } is not a valid filter name`) - - delete this.filters[ filterName ] - } - - setFromStringArray ( filterStringArray ) { - filterStringArray.forEach( filterString => { - const { key, value } = this.getFilterNameAndValueFromString( filterString ) - - this.filters[ key ] = value - }) - } - - setFromString ( filterNameOrQueryValue ) { - const { - key, - value = '' - } = this.getFilterNameAndValueFromString( filterNameOrQueryValue ) - - // Throw for empty values - if ( value.trim().length === 0 ) throw new Error(`${ filterNameOrQueryValue } is not a valid filter value`) - - this.set( key, value ) - } - - set ( filterName, filterValue ) { - // Throw error if it's not a valid filter name - if ( this.isQueryValue( filterName ) ) throw new Error(`${ filterName } is not a valid filter name`) - - this.filters[ filterName ] = filterValue - } - - toggleFilter ( filterNameOrQueryValue, filterValue = null ) { - - const fromString = this.getFilterNameAndValueFromString( filterNameOrQueryValue ) - - const filterName = fromString.key - filterValue = filterValue || fromString.value - - // If the filter is already set, remove it - if ( this.has( filterNameOrQueryValue ) ) { - this.remove( filterName ) - - return - } - - // Throw error if filter value is not a string - if ( typeof filterValue !== 'string' ) { - throw new Error(`Filter value must be a string. Got ${ typeof filterValue }`) - } - - this.set( filterName, filterValue ) - } - - has ( filterNameOrQueryValue ) { - - const { - key : filterName, - value : filterValue = null - } = this.getFilterNameAndValueFromString( filterNameOrQueryValue ) - - // If this filter is a name and value, check if it's set - if ( isString( filterValue ) ) { - return !!this.filters[ filterName ] && this.filters[ filterName ] === filterValue - } - - return !!this.filters[ filterName ] - } -} +export class StorkFilters extends SearchFilters {} diff --git a/test/prebuild/filters.test.js b/test/prebuild/filters.test.js index 302962b..e055908 100644 --- a/test/prebuild/filters.test.js +++ b/test/prebuild/filters.test.js @@ -84,5 +84,19 @@ describe('StorkFilters', () => { filters.setFromString('test_works_yes') expect(filters.asQuery).toBe('test_works_yes') }) + + it('should map filters for Pagefind', () => { + const filters = new StorkFilters() + + filters.setFromStringArray([ + 'status_native', + 'category_system_tools' + ]) + + expect(filters.asPagefindFilters).toEqual({ + status: [ 'native' ], + category: [ 'system_tools' ] + }) + }) }) })