fix(search-tabs): keep tabs without explicit location across reloads (#108)
This commit is contained in:
parent
3edbd0c11e
commit
d3c62aa5a2
103
src/client/features/search-tabs/useSearchTabs.test.ts
Normal file
103
src/client/features/search-tabs/useSearchTabs.test.ts
Normal file
@ -0,0 +1,103 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { parseStoredState } from "./useSearchTabs";
|
||||
|
||||
function persistedTab(input: unknown) {
|
||||
return {
|
||||
id: "tab-1",
|
||||
label: "example",
|
||||
createdAt: 1,
|
||||
viewedAt: null,
|
||||
input,
|
||||
};
|
||||
}
|
||||
|
||||
describe("parseStoredState", () => {
|
||||
it("keeps domain tabs persisted without a locationCode (default location)", () => {
|
||||
const state = parseStoredState({
|
||||
activeTabId: "tab-1",
|
||||
tabs: [
|
||||
persistedTab({
|
||||
type: "domain",
|
||||
domain: "example.com",
|
||||
subdomains: true,
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
expect(state.tabs).toHaveLength(1);
|
||||
expect(state.activeTabId).toBe("tab-1");
|
||||
expect(state.tabs[0].input).toEqual({
|
||||
type: "domain",
|
||||
domain: "example.com",
|
||||
subdomains: true,
|
||||
locationCode: undefined,
|
||||
});
|
||||
});
|
||||
|
||||
it("keeps keyword tabs persisted without a locationCode (default location)", () => {
|
||||
const state = parseStoredState({
|
||||
activeTabId: "tab-1",
|
||||
tabs: [
|
||||
persistedTab({
|
||||
type: "keyword",
|
||||
keyword: "seo tools",
|
||||
resultLimit: 150,
|
||||
mode: "auto",
|
||||
clickstream: false,
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
expect(state.tabs).toHaveLength(1);
|
||||
expect(state.activeTabId).toBe("tab-1");
|
||||
expect(state.tabs[0].input).toEqual({
|
||||
type: "keyword",
|
||||
keyword: "seo tools",
|
||||
locationCode: undefined,
|
||||
resultLimit: 150,
|
||||
mode: "auto",
|
||||
clickstream: false,
|
||||
});
|
||||
});
|
||||
|
||||
it("keeps tabs persisted with an explicit locationCode", () => {
|
||||
const state = parseStoredState({
|
||||
activeTabId: "tab-1",
|
||||
tabs: [
|
||||
persistedTab({
|
||||
type: "domain",
|
||||
domain: "example.com",
|
||||
subdomains: false,
|
||||
locationCode: 2840,
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
expect(state.tabs).toHaveLength(1);
|
||||
expect(state.tabs[0].input).toMatchObject({ locationCode: 2840 });
|
||||
});
|
||||
|
||||
it("still rejects malformed tab inputs", () => {
|
||||
const state = parseStoredState({
|
||||
activeTabId: null,
|
||||
tabs: [
|
||||
persistedTab({ type: "domain", subdomains: true }),
|
||||
persistedTab({
|
||||
type: "keyword",
|
||||
keyword: "seo tools",
|
||||
resultLimit: 999,
|
||||
mode: "auto",
|
||||
}),
|
||||
persistedTab({
|
||||
type: "domain",
|
||||
domain: "example.com",
|
||||
subdomains: true,
|
||||
locationCode: "us",
|
||||
}),
|
||||
persistedTab({ type: "unknown" }),
|
||||
],
|
||||
});
|
||||
|
||||
expect(state.tabs).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
@ -44,18 +44,33 @@ function parseTabInput(value: unknown): SearchTabInput | null {
|
||||
if (value.type === "domain") {
|
||||
if (typeof value.domain !== "string" || value.domain === "") return null;
|
||||
if (typeof value.subdomains !== "boolean") return null;
|
||||
if (typeof value.locationCode !== "number") return null;
|
||||
// locationCode is optional in DomainSearchTabInput: tabs opened at the
|
||||
// default location persist no loc param, so accept a missing key.
|
||||
if (
|
||||
value.locationCode !== undefined &&
|
||||
typeof value.locationCode !== "number"
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
type: "domain",
|
||||
domain: value.domain,
|
||||
subdomains: value.subdomains,
|
||||
locationCode: value.locationCode,
|
||||
locationCode:
|
||||
typeof value.locationCode === "number" ? value.locationCode : undefined,
|
||||
};
|
||||
}
|
||||
|
||||
if (value.type === "keyword") {
|
||||
if (typeof value.keyword !== "string" || value.keyword === "") return null;
|
||||
if (typeof value.locationCode !== "number") return null;
|
||||
// locationCode is optional in KeywordSearchTabInput: tabs opened at the
|
||||
// default location persist no loc param, so accept a missing key.
|
||||
if (
|
||||
value.locationCode !== undefined &&
|
||||
typeof value.locationCode !== "number"
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
if (
|
||||
value.resultLimit !== 150 &&
|
||||
value.resultLimit !== 300 &&
|
||||
@ -74,7 +89,8 @@ function parseTabInput(value: unknown): SearchTabInput | null {
|
||||
return {
|
||||
type: "keyword",
|
||||
keyword: value.keyword,
|
||||
locationCode: value.locationCode,
|
||||
locationCode:
|
||||
typeof value.locationCode === "number" ? value.locationCode : undefined,
|
||||
resultLimit: value.resultLimit,
|
||||
mode: value.mode,
|
||||
// Tabs persisted before the clickstream toggle existed default to off.
|
||||
@ -93,7 +109,8 @@ function tabInputKey(value: unknown): string {
|
||||
return JSON.stringify(value);
|
||||
}
|
||||
|
||||
function parseStoredState(value: unknown): TabsState {
|
||||
// Exported for unit tests.
|
||||
export function parseStoredState(value: unknown): TabsState {
|
||||
if (!isRecord(value)) return EMPTY_STATE;
|
||||
if (!Array.isArray(value.tabs)) return EMPTY_STATE;
|
||||
const tabs = value.tabs
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user