Skip to content
Merged
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
Expand Up @@ -12,10 +12,12 @@ import {

const STORAGE_KEY = 'macroflows:template-search-tab'

/**
* Default tab to use when no preference is saved.
*/
export const DEFAULT_TAB: TemplateSearchTab = availableTabs.Todos.id
const DEFAULT_TAB: TemplateSearchTab = availableTabs.Todos.id

type TemplateSearchTabPreference = {
loadTabPreference: () => TemplateSearchTab
saveTabPreference: (tab: TemplateSearchTab) => void
}

/**
* Checks if a value is a valid TemplateSearchTab.
Expand All @@ -31,7 +33,7 @@ function isValidTab(value: string): value is TemplateSearchTab {
* Loads the saved template search tab preference from localStorage.
* @returns The saved tab preference, or the default tab if none is saved or invalid
*/
export function loadTabPreference(): TemplateSearchTab {
function loadTabPreference(): TemplateSearchTab {
try {
const stored = localStorage.getItem(STORAGE_KEY)
if (stored !== null && isValidTab(stored)) {
Expand All @@ -47,7 +49,7 @@ export function loadTabPreference(): TemplateSearchTab {
* Saves the template search tab preference to localStorage.
* @param tab - The tab to save
*/
export function saveTabPreference(tab: TemplateSearchTab): void {
function saveTabPreference(tab: TemplateSearchTab): void {
try {
// Don't persist the 'hidden' state
if (tab !== 'hidden') {
Expand All @@ -57,3 +59,14 @@ export function saveTabPreference(tab: TemplateSearchTab): void {
// localStorage may not be available (SSR, private mode, etc.)
}
}

/**
* Creates a localStorage-backed template search tab preference adapter.
* @returns An adapter with methods to load and save the selected tab
*/
export function createTemplateSearchTabPreference(): TemplateSearchTabPreference {
return {
loadTabPreference,
saveTabPreference,
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,67 +31,79 @@ describe('templateSearchTabPreference', () => {

describe('loadTabPreference', () => {
it('returns default tab when localStorage is empty', async () => {
const { loadTabPreference, DEFAULT_TAB } =
const { createTemplateSearchTabPreference } =
await import('~/modules/template-search/infrastructure/templateSearchTabPreference')
expect(loadTabPreference()).toBe(DEFAULT_TAB)
const tabPreference = createTemplateSearchTabPreference()

expect(tabPreference.loadTabPreference()).toBe(availableTabs.Todos.id)
})

it('returns persisted tab from localStorage', async () => {
localStorageMock[STORAGE_KEY] = availableTabs.Favoritos.id
setMockLocalStorage()
vi.resetModules()

const { loadTabPreference } =
const { createTemplateSearchTabPreference } =
await import('~/modules/template-search/infrastructure/templateSearchTabPreference')
expect(loadTabPreference()).toBe(availableTabs.Favoritos.id)
const tabPreference = createTemplateSearchTabPreference()

expect(tabPreference.loadTabPreference()).toBe(availableTabs.Favoritos.id)
})

it('returns persisted Recentes tab from localStorage', async () => {
localStorageMock[STORAGE_KEY] = availableTabs.Recentes.id
setMockLocalStorage()
vi.resetModules()

const { loadTabPreference } =
const { createTemplateSearchTabPreference } =
await import('~/modules/template-search/infrastructure/templateSearchTabPreference')
expect(loadTabPreference()).toBe(availableTabs.Recentes.id)
const tabPreference = createTemplateSearchTabPreference()

expect(tabPreference.loadTabPreference()).toBe(availableTabs.Recentes.id)
})

it('returns persisted Receitas tab from localStorage', async () => {
localStorageMock[STORAGE_KEY] = availableTabs.Receitas.id
setMockLocalStorage()
vi.resetModules()

const { loadTabPreference } =
const { createTemplateSearchTabPreference } =
await import('~/modules/template-search/infrastructure/templateSearchTabPreference')
expect(loadTabPreference()).toBe(availableTabs.Receitas.id)
const tabPreference = createTemplateSearchTabPreference()

expect(tabPreference.loadTabPreference()).toBe(availableTabs.Receitas.id)
})

it('returns default tab for invalid stored value', async () => {
localStorageMock[STORAGE_KEY] = 'invalid-tab-id'
setMockLocalStorage()
vi.resetModules()

const { loadTabPreference, DEFAULT_TAB } =
const { createTemplateSearchTabPreference } =
await import('~/modules/template-search/infrastructure/templateSearchTabPreference')
expect(loadTabPreference()).toBe(DEFAULT_TAB)
const tabPreference = createTemplateSearchTabPreference()

expect(tabPreference.loadTabPreference()).toBe(availableTabs.Todos.id)
})
})

describe('saveTabPreference', () => {
it('saves tab preference to localStorage', async () => {
const { saveTabPreference } =
const { createTemplateSearchTabPreference } =
await import('~/modules/template-search/infrastructure/templateSearchTabPreference')
const tabPreference = createTemplateSearchTabPreference()

saveTabPreference(availableTabs.Favoritos.id)
tabPreference.saveTabPreference(availableTabs.Favoritos.id)

expect(localStorageMock[STORAGE_KEY]).toBe(availableTabs.Favoritos.id)
})

it('does not persist hidden tab state', async () => {
const { saveTabPreference } =
const { createTemplateSearchTabPreference } =
await import('~/modules/template-search/infrastructure/templateSearchTabPreference')
const tabPreference = createTemplateSearchTabPreference()

saveTabPreference('hidden')
tabPreference.saveTabPreference('hidden')

expect(localStorageMock[STORAGE_KEY]).toBeUndefined()
})
Expand All @@ -101,38 +113,47 @@ describe('templateSearchTabPreference', () => {
setMockLocalStorage()
vi.resetModules()

const { saveTabPreference } =
const { createTemplateSearchTabPreference } =
await import('~/modules/template-search/infrastructure/templateSearchTabPreference')
const tabPreference = createTemplateSearchTabPreference()

saveTabPreference(availableTabs.Recentes.id)
tabPreference.saveTabPreference(availableTabs.Recentes.id)

expect(localStorageMock[STORAGE_KEY]).toBe(availableTabs.Recentes.id)
})
})

describe('round-trip persistence', () => {
it('can save and load tab preference', async () => {
const { saveTabPreference } =
const { createTemplateSearchTabPreference } =
await import('~/modules/template-search/infrastructure/templateSearchTabPreference')
const tabPreference = createTemplateSearchTabPreference()

// Save a preference
saveTabPreference(availableTabs.Receitas.id)
tabPreference.saveTabPreference(availableTabs.Receitas.id)

// Re-import to get fresh module state
vi.resetModules()
const freshModule =
await import('~/modules/template-search/infrastructure/templateSearchTabPreference')
const freshTabPreference = freshModule.createTemplateSearchTabPreference()

// Load should return the saved preference
expect(freshModule.loadTabPreference()).toBe(availableTabs.Receitas.id)
expect(freshTabPreference.loadTabPreference()).toBe(
availableTabs.Receitas.id,
)
})
})

describe('DEFAULT_TAB', () => {
it('is the Todos tab', async () => {
const { DEFAULT_TAB } =
describe('createTemplateSearchTabPreference', () => {
it('returns load and save methods', async () => {
const { createTemplateSearchTabPreference } =
await import('~/modules/template-search/infrastructure/templateSearchTabPreference')
expect(DEFAULT_TAB).toBe(availableTabs.Todos.id)

const tabPreference = createTemplateSearchTabPreference()

expect(typeof tabPreference.loadTabPreference).toBe('function')
expect(typeof tabPreference.saveTabPreference).toBe('function')
})
})
})
11 changes: 5 additions & 6 deletions src/sections/search/components/TemplateSearchModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,7 @@ import {
import { type Template } from '~/modules/diet/template/domain/template'
import { isTemplateRecipe } from '~/modules/diet/template/domain/template'
import { type TemplateItem } from '~/modules/diet/template-item/domain/templateItem'
import {
loadTabPreference,
saveTabPreference,
} from '~/modules/template-search/infrastructure/templateSearchTabPreference'
import { createTemplateSearchTabPreference } from '~/modules/template-search/infrastructure/templateSearchTabPreference'
import {
showError,
showSuccess,
Expand All @@ -39,6 +36,8 @@ import {
} from '~/shared/modal/helpers/modalHelpers'
import { logging } from '~/shared/utils/logging'

const templateSearchTabPreference = createTemplateSearchTabPreference()

export type TemplateSearchModalProps = {
targetName: string
onNewItem?: (item: Item, originalAddedItem: TemplateItem) => void
Expand Down Expand Up @@ -211,7 +210,7 @@ export function TemplateSearch(props: {

// Load persisted tab preference on mount (only once)
onMount(() => {
const persistedTab = loadTabPreference()
const persistedTab = templateSearchTabPreference.loadTabPreference()
templateSearchState.setTemplateSearchTab(persistedTab)
})

Expand All @@ -228,7 +227,7 @@ export function TemplateSearch(props: {
: tabOrUpdater

templateSearchState.setTemplateSearchTab(newTab)
saveTabPreference(newTab)
templateSearchTabPreference.saveTabPreference(newTab)
}

return (
Expand Down
Loading