Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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}
Comment on lines +11 to +12
Copy link

Copilot AI Oct 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The @see links reference './strategies/' but the actual files are in './persistenceStrategies/'. Update the paths to '@see {@link ./persistenceStrategies/UrlPersistenceStrategy.ts}' and '@see {@link ./persistenceStrategies/InMemoryPersistenceStrategy.ts}'.

Suggested change
* @see {@link ./strategies/UrlPersistenceStrategy.ts}
* @see {@link ./strategies/InMemoryPersistenceStrategy.ts}
* @see {@link ./persistenceStrategies/UrlPersistenceStrategy.ts}
* @see {@link ./persistenceStrategies/InMemoryPersistenceStrategy.ts}

Copilot uses AI. Check for mistakes.
*/
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;
}
Original file line number Diff line number Diff line change
@@ -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;
}
Copy link

Copilot AI Oct 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] Missing blank line between the closing brace of InMemoryPersistenceStrategyConfig and the JSDoc comment. Add a blank line for consistency with TypeScript formatting conventions.

Suggested change
}
}

Copilot uses AI. Check for mistakes.
/**
* 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");
}
}
Original file line number Diff line number Diff line change
@@ -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<string, string | undefined>;
}

/**
* 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");
}
}
Loading