diff --git a/t29-project-1/src/docs/FILTER_STORAGE.md b/t29-project-1/src/docs/FILTER_STORAGE.md new file mode 100644 index 0000000..84e9422 --- /dev/null +++ b/t29-project-1/src/docs/FILTER_STORAGE.md @@ -0,0 +1,78 @@ +# Breed filter storage guide + +This document explains how the selected breed filter is persisted in +`localStorage`, so the choice is restored after a page reload. + +## Functions + +The storage logic lives in `src/utils/breedFilter.ts` and exposes two +functions: + +```ts +getStoredBreedFilter(): string | null +setStoredBreedFilter(breedId: string | null): void +``` + +`getStoredBreedFilter` reads the saved breed ID, or returns `null` if none has +been saved yet: + +```ts +import { getStoredBreedFilter } from '../utils/breedFilter'; + +const breedId = getStoredBreedFilter(); +``` + +`setStoredBreedFilter` saves a breed ID: + +```ts +import { setStoredBreedFilter } from '../utils/breedFilter'; + +setStoredBreedFilter('beng'); +``` + +## Storage key + +The value is stored under the key `selectedBreedId`. Passing `null` (or an +empty string) to `setStoredBreedFilter` clears the filter. + +```ts +setStoredBreedFilter(null); +``` + +Clearing removes the key from `localStorage` entirely, instead of storing an +empty string. This has two purposes: + +- **No leftover state.** Once a filter is cleared, nothing related to it stays + in storage. +- **One consistent "not set" value.** `getStoredBreedFilter` always returns + `null` when there is no filter, never an empty string. Callers only have to + check for one value instead of two that mean the same thing. + +## Error handling + +Both functions wrap their `localStorage` access in a `try/catch`. If storage +is unavailable, for example in private browsing mode, the functions fail +silently instead of throwing. `getStoredBreedFilter` returns `null` and +`setStoredBreedFilter` simply does not persist the value. Callers do not need +their own error handling for this. + +## Intended usage + +No breed dropdown exists yet, but once one is built it should read the saved +value as its initial state and call `setStoredBreedFilter` whenever the +selection changes. + +## Testing + +Tests for this module are in `src/tests/breedFilter.test.ts` and run with the +rest of the suite: + +```bash +npm test +``` + +To run only this file, pass its path through to Vitest: + +```bash +npm test -- src/tests/breedFilter.test.ts +``` diff --git a/t29-project-1/src/tests/breedFilter.test.ts b/t29-project-1/src/tests/breedFilter.test.ts new file mode 100644 index 0000000..5a4d985 --- /dev/null +++ b/t29-project-1/src/tests/breedFilter.test.ts @@ -0,0 +1,35 @@ +import { beforeEach, describe, expect, it } from 'vitest'; +import { + getStoredBreedFilter, + setStoredBreedFilter, +} from '../utils/breedFilter'; + +describe('breed filter storage', () => { + beforeEach(() => { + localStorage.clear(); + }); + + it('returns null when no breed filter has been saved', () => { + expect(getStoredBreedFilter()).toBeNull(); + }); + + it('saves and retrieves the selected breed filter', () => { + setStoredBreedFilter('abys'); + + expect(getStoredBreedFilter()).toBe('abys'); + }); + + it('overwrites a previously saved breed filter', () => { + setStoredBreedFilter('abys'); + setStoredBreedFilter('beng'); + + expect(getStoredBreedFilter()).toBe('beng'); + }); + + it('clears the stored filter when set to null', () => { + setStoredBreedFilter('abys'); + setStoredBreedFilter(null); + + expect(getStoredBreedFilter()).toBeNull(); + }); +}); diff --git a/t29-project-1/src/utils/breedFilter.ts b/t29-project-1/src/utils/breedFilter.ts new file mode 100644 index 0000000..045842b --- /dev/null +++ b/t29-project-1/src/utils/breedFilter.ts @@ -0,0 +1,21 @@ +const BREED_FILTER_STORAGE_KEY = 'selectedBreedId'; + +export function getStoredBreedFilter(): string | null { + try { + return localStorage.getItem(BREED_FILTER_STORAGE_KEY); + } catch { + return null; + } +} + +export function setStoredBreedFilter(breedId: string | null): void { + try { + if (breedId) { + localStorage.setItem(BREED_FILTER_STORAGE_KEY, breedId); + } else { + localStorage.removeItem(BREED_FILTER_STORAGE_KEY); + } + } catch { + // localStorage unavailable (e.g. private browsing) — filter just won't persist + } +}