diff --git a/src/components/ConditionalFilter/ValueProvider/FilterValueController.ts b/src/components/ConditionalFilter/ValueProvider/FilterValueController.ts new file mode 100644 index 00000000000..c2d4290f69e --- /dev/null +++ b/src/components/ConditionalFilter/ValueProvider/FilterValueController.ts @@ -0,0 +1,91 @@ +import { FilterContainer, FilterElement } from "../FilterElement"; +import { UrlToken } from "./UrlToken"; + +/** + * PersistenceStrategy defines the contract for persisting and clearing filter state. + * + * Implementations: + * - UrlPersistenceStrategy: stores state in URL query parameters (for list pages) + * - InMemoryPersistenceStrategy: stores state in React component state (for modals) + * + * @see {@link ./strategies/UrlPersistenceStrategy.ts} + * @see {@link ./strategies/InMemoryPersistenceStrategy.ts} + */ +export interface PersistenceStrategy { + /** + * Persist the filter state to the underlying storage mechanism. + * @param value The filter container to persist + */ + persist(value: FilterContainer): void; + + /** + * Clear all persisted filter state from the underlying storage mechanism. + */ + clear(): void; +} + +export interface FilterValueControllerConfig { + persistenceStrategy: PersistenceStrategy; + + /** + * Initial filter state to populate the controller with. + * If not provided, controller starts with an empty state + */ + initialValue?: FilterContainer; + + initialLoading?: boolean; + + /** + * Callback invoked when the filter value changes. + * Can be used to trigger side effects like data fetching. + */ + onChange?: (value: FilterContainer) => void; +} + +/** + * FilterValueController is used for managing filter state with different strategies (URL, in-memory) + * + * This interface matches FilterValueProvider to ensure backward compatibility + * with existing code that uses useUrlValueProvider. + */ +export interface FilterValueController { + readonly value: FilterContainer; + + readonly loading: boolean; + + /** + * Persist a new filter value using the configured persistence strategy. + * @param newValue The new filter container to persist + */ + persist(newValue: FilterContainer): void; + + /** + * Check if a specific filter element is present in the current state. + * @param element The filter element to check + */ + isPersisted(element: FilterElement): boolean; + + /** + * Clear all filters using the configured persistence strategy. + */ + clear(): void; + + /** + * Get a specific token by name from the mirrored UrlToken[] representation. + * This method provides backward compatibility with existing code that expects + * URL token access for initial state hydration. + * + * @param name The name of the token to retrieve + */ + getTokenByName(name: string): UrlToken | undefined; + + /** + * The number of active filter elements + */ + readonly count: number; + + /** + * Must be called when page is unloaded in useEffect + */ + cleanup(): void; +} diff --git a/src/components/ConditionalFilter/ValueProvider/persistenceStrategies/InMemoryPersistenceStrategy.ts b/src/components/ConditionalFilter/ValueProvider/persistenceStrategies/InMemoryPersistenceStrategy.ts new file mode 100644 index 00000000000..e3e0c0de8f7 --- /dev/null +++ b/src/components/ConditionalFilter/ValueProvider/persistenceStrategies/InMemoryPersistenceStrategy.ts @@ -0,0 +1,35 @@ +import { FilterContainer } from "../../FilterElement"; +import { PersistenceStrategy } from "../FilterValueController"; + +export interface InMemoryPersistenceStrategyConfig { + /** + * Initial state for the in-memory storage. + * If not provided, starts with an empty FilterContainer + */ + initialState?: FilterContainer; + + /** + * Callback when state changes in memory + */ + onStateChange?: (state: FilterContainer) => void; +} +/** + * TODO: + * - Use internal state to store FilterContainer + * - Support initial state from config + * - Call onStateChange callback when state updates + * - Ensure proper cleanup + */ +export class InMemoryPersistenceStrategy implements PersistenceStrategy { + constructor(private config: InMemoryPersistenceStrategyConfig) {} + + persist(_value: FilterContainer): void { + // TODO: Implementation in subtask 1.3 + throw new Error("InMemoryPersistenceStrategy.persist: Not implemented yet"); + } + + clear(): void { + // TODO: Implementation in subtask 1.3 + throw new Error("InMemoryPersistenceStrategy.clear: Not implemented yet"); + } +} diff --git a/src/components/ConditionalFilter/ValueProvider/persistenceStrategies/UrlPersistenceStrategy.ts b/src/components/ConditionalFilter/ValueProvider/persistenceStrategies/UrlPersistenceStrategy.ts new file mode 100644 index 00000000000..e9e89f3984e --- /dev/null +++ b/src/components/ConditionalFilter/ValueProvider/persistenceStrategies/UrlPersistenceStrategy.ts @@ -0,0 +1,51 @@ +import { FilterContainer } from "../../FilterElement"; +import { PersistenceStrategy } from "../FilterValueController"; + +export interface UrlPersistenceStrategyConfig { + /** + * The router history object for URL manipulation. + * Should be compatible with react-router's history API. + */ + history: { + replace: (location: { pathname: string; search?: string }) => void; + }; + + /** + * Current location information from the router. + */ + location: { + pathname: string; + search: string; + }; + + /** + * Additional query parameters to preserve during filter persistence. + * These params are merged with filter state when updating the URL. + * + * For example: + * - query: Search query + * - before: Pagination cursor + * - after: Pagination cursor + * etc. + */ + preservedParams?: Record; +} + +/** + * TODO: + * - Wrap router.history.replace calls from useUrlValueProvider + * - Handle URL query parameter serialization using prepareStructure utility + * - Preserve additional params (activeTab, query, before, after) + * - Ensure FilterContainer format compatibility + */ +export class UrlPersistenceStrategy implements PersistenceStrategy { + constructor(private config: UrlPersistenceStrategyConfig) {} + + persist(_value: FilterContainer): void { + throw new Error("UrlPersistenceStrategy.persist: Not implemented yet"); + } + + clear(): void { + throw new Error("UrlPersistenceStrategy.clear: Not implemented yet"); + } +}