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
2 changes: 1 addition & 1 deletion src/content.resize.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ describe('resize handling', () => {
});

it('re-renders the grid when the window is resized', async () => {
const stored = normalizeSettings(DEFAULT_SETTINGS);
const stored = normalizeSettings({ ...DEFAULT_SETTINGS, enabled: true });
vi.stubGlobal('chrome', {
runtime: {
getURL: (path: string) => `chrome-extension://test-id/${path}`,
Expand Down
42 changes: 24 additions & 18 deletions src/content.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,9 +95,15 @@ afterEach(() => {
});

describe('initial render', () => {
it('renders the overlay with the default stretch column grid', async () => {
it('does not render the overlay while disabled by default', async () => {
await loadContentScript();

expect(document.getElementById(GRID_OVERLAY_ID)).toBeNull();
});

it('renders the overlay with the default stretch column grid when enabled', async () => {
await loadContentScript({ enabled: true });

const overlay = getOverlay();
const gridLayer = overlay.querySelector('.grid-ui__layer') as HTMLElement;
const frame = gridLayer.firstElementChild as HTMLElement;
Expand All @@ -106,10 +112,10 @@ describe('initial render', () => {
expect(frame.style.gridTemplateColumns).toBe(`repeat(${DEFAULT_SETTINGS.count}, minmax(0, 1fr))`);
});

it('reads the site identity from the page metadata for the popover header', async () => {
it('reads the site identity from the page metadata for the popover header when enabled', async () => {
document.head.innerHTML = '<meta property="og:site_name" content="Acme Corp" />';

await loadContentScript();
await loadContentScript({ enabled: true });

const title = document.querySelector('.grid-ui__popover-title');
expect(title?.textContent).toBe('Acme Corp');
Expand All @@ -118,7 +124,7 @@ describe('initial render', () => {

describe('visibility toggle', () => {
it('hides the grid layer and flips the trigger icon state', async () => {
await loadContentScript();
await loadContentScript({ enabled: true });
const overlay = getOverlay();
const axisTrigger = overlay.querySelector('.grid-ui__anchor--start') as HTMLButtonElement;
const gridLayer = overlay.querySelector('.grid-ui__layer') as HTMLElement;
Expand All @@ -131,7 +137,7 @@ describe('visibility toggle', () => {
});

it('persists the change to chrome.storage.sync after the debounce delay', async () => {
const { chromeMock, getStoredSettings } = await loadContentScript();
const { chromeMock, getStoredSettings } = await loadContentScript({ enabled: true });
vi.useFakeTimers({ toFake: ['setTimeout', 'clearTimeout'] });

const overlay = getOverlay();
Expand All @@ -149,7 +155,7 @@ describe('visibility toggle', () => {

describe('adjust popover', () => {
it('opens on trigger click and closes when clicked again', async () => {
await loadContentScript();
await loadContentScript({ enabled: true });
const overlay = getOverlay();
const adjustTrigger = overlay.querySelector('.grid-ui__anchor--center') as HTMLButtonElement;
const popover = overlay.querySelector('[data-popover="adjust"]') as HTMLElement;
Expand All @@ -166,7 +172,7 @@ describe('adjust popover', () => {

describe('pattern picker', () => {
it('lists all variations and defaults regardless of the active axis', async () => {
await loadContentScript();
await loadContentScript({ enabled: true });
const overlay = getOverlay();
const adjustTrigger = overlay.querySelector('.grid-ui__anchor--center') as HTMLButtonElement;
const patternTrigger = overlay.querySelector('.grid-ui__pattern-trigger') as HTMLButtonElement;
Expand Down Expand Up @@ -202,7 +208,7 @@ describe('pattern picker', () => {
});

it('applies defaults onto the active variation and can snapshot a new version', async () => {
const { getStoredSettings } = await loadContentScript();
const { getStoredSettings } = await loadContentScript({ enabled: true });
const overlay = getOverlay();
const adjustTrigger = overlay.querySelector('.grid-ui__anchor--center') as HTMLButtonElement;
const patternTrigger = overlay.querySelector('.grid-ui__pattern-trigger') as HTMLButtonElement;
Expand Down Expand Up @@ -241,7 +247,7 @@ describe('pattern picker', () => {

describe('measurement controls', () => {
it('updates the count and the rendered grid when the count slider changes', async () => {
await loadContentScript();
await loadContentScript({ enabled: true });
const overlay = getOverlay();
const countRange = overlay.querySelector('.grid-ui__field--slider input[min="1"][max="24"]') as HTMLInputElement;

Expand All @@ -253,7 +259,7 @@ describe('measurement controls', () => {
});

it('hides the size field for stretch and shows it for a fixed distribution', async () => {
await loadContentScript();
await loadContentScript({ enabled: true });
const overlay = getOverlay();
const sizeField = overlay.querySelector('.grid-ui__field--slider--color')
?.parentElement?.querySelectorAll('label')[1] as HTMLLabelElement | undefined;
Expand All @@ -272,7 +278,7 @@ describe('measurement controls', () => {

describe('axis switching', () => {
it('resets the distribution and re-renders the grid for the new axis', async () => {
const { getStoredSettings } = await loadContentScript();
const { getStoredSettings } = await loadContentScript({ enabled: true });
const overlay = getOverlay();
const rowsOption = overlay.querySelector('[data-axis="rows"]') as HTMLButtonElement;

Expand All @@ -298,7 +304,7 @@ describe('axis switching', () => {

describe('disabling', () => {
it('removes the overlay from the DOM and persists enabled:false immediately when closed', async () => {
const { chromeMock, getStoredSettings } = await loadContentScript();
const { chromeMock, getStoredSettings } = await loadContentScript({ enabled: true });
const overlay = getOverlay();
const closeTrigger = overlay.querySelector('.grid-ui__close') as HTMLButtonElement;

Expand All @@ -313,25 +319,25 @@ describe('disabling', () => {

describe('background/popup messaging', () => {
it('re-renders when a GRID_SETTINGS_UPDATED message arrives', async () => {
const { emitMessage } = await loadContentScript();
const { emitMessage } = await loadContentScript({ enabled: true });
const overlay = getOverlay();

emitMessage({
type: GRID_MESSAGE_TYPE,
settings: { ...DEFAULT_SETTINGS, axis: 'grid', size: 70 },
settings: { ...DEFAULT_SETTINGS, enabled: true, axis: 'grid', size: 70 },
});

const frame = (overlay.querySelector('.grid-ui__layer') as HTMLElement).firstElementChild as HTMLElement;
expect(frame.style.backgroundSize).toBe('70px 70px');
});

it('shows only size and color controls in grid mode', async () => {
const { emitMessage } = await loadContentScript();
const { emitMessage } = await loadContentScript({ enabled: true });
const overlay = getOverlay();

emitMessage({
type: GRID_MESSAGE_TYPE,
settings: { ...DEFAULT_SETTINGS, axis: 'grid', size: 70 },
settings: { ...DEFAULT_SETTINGS, enabled: true, axis: 'grid', size: 70 },
});

const adjustTrigger = overlay.querySelector('.grid-ui__anchor--center') as HTMLButtonElement;
Expand Down Expand Up @@ -359,7 +365,7 @@ describe('background/popup messaging', () => {
});

it('ignores messages with an unrelated type', async () => {
const { emitMessage } = await loadContentScript();
const { emitMessage } = await loadContentScript({ enabled: true });
const overlay = getOverlay();
const frameBefore = (overlay.querySelector('.grid-ui__layer') as HTMLElement).firstElementChild as HTMLElement;
const childCountBefore = frameBefore.children.length;
Expand All @@ -373,7 +379,7 @@ describe('background/popup messaging', () => {

describe('controller drag', () => {
it('adds and removes dragging class during pointer interaction', async () => {
await loadContentScript();
await loadContentScript({ enabled: true });
const overlay = getOverlay();
const controller = overlay.querySelector('.grid-ui__controller') as HTMLElement;

Expand Down
83 changes: 52 additions & 31 deletions src/site-patterns.ts
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,19 @@ function normalizeSession(
};
}

function mergeSessionSettings(
settings: GridSettings,
siteState: SiteGridState,
): GridSettings {
return normalizeSettings({
...settings,
enabled: siteState.session?.enabled ?? settings.enabled,
visible: siteState.session?.visible ?? settings.visible,
toolbarX: siteState.session?.toolbarX ?? settings.toolbarX,
toolbarY: siteState.session?.toolbarY ?? settings.toolbarY,
});
}

function mergePresetPattern(
pattern: GridPattern,
siteState: SiteGridState,
Expand All @@ -227,19 +240,13 @@ function mergePresetPattern(
color: DEFAULT_SETTINGS.color,
opacity: DEFAULT_SETTINGS.opacity,
};
const session: Partial<Pick<GridSettings, 'enabled' | 'visible' | 'toolbarX' | 'toolbarY'>> =
siteState.session ?? {};

return {
...pattern,
settings: normalizeSettings({
...pattern.settings,
...appearance,
enabled: session.enabled ?? DEFAULT_SETTINGS.enabled,
visible: session.visible ?? DEFAULT_SETTINGS.visible,
toolbarX: session.toolbarX ?? DEFAULT_SETTINGS.toolbarX,
toolbarY: session.toolbarY ?? DEFAULT_SETTINGS.toolbarY,
}),
settings: mergeSessionSettings(
normalizeSettings({ ...pattern.settings, ...appearance }),
siteState,
),
};
}

Expand Down Expand Up @@ -400,7 +407,10 @@ export function getActivePattern(siteState: SiteGridState): GridPattern {
return mergePresetPattern(pattern, siteState);
}

return pattern;
return {
...pattern,
settings: mergeSessionSettings(pattern.settings, siteState),
};
}

export function getPatternsForAxis(
Expand Down Expand Up @@ -448,7 +458,6 @@ export function selectPattern(siteState: SiteGridState, patternId: string): Site
...siteState,
activePatternId: patternId,
appearance: undefined,
session: undefined,
};
}

Expand All @@ -458,13 +467,20 @@ export function ensureAxisPattern(siteState: SiteGridState, axis: GridAxis): Sit
return selectPattern(siteState, existingVariation.id);
}

const currentSettings = getActivePattern(siteState).settings;
const template = getFirstPresetForAxis(siteState, axis)?.settings ?? { ...DEFAULT_SETTINGS, axis };
const nextPattern = createVariationPattern(siteState.patterns, template);
const nextPattern = createVariationPattern(siteState.patterns, {
...template,
enabled: currentSettings.enabled,
visible: currentSettings.visible,
toolbarX: currentSettings.toolbarX,
toolbarY: currentSettings.toolbarY,
});
return {
activePatternId: nextPattern.id,
patterns: [nextPattern, ...siteState.patterns],
appearance: undefined,
session: undefined,
session: siteState.session,
};
}

Expand All @@ -473,30 +489,30 @@ export function updateActivePatternSettings(
settings: GridSettings,
): SiteGridState {
const normalized = normalizeSettings(settings);
const currentSettings = getActivePattern(siteState).settings;

if (!patternSettingsChanged(currentSettings, normalized)) {
return {
...siteState,
session: {
enabled: normalized.enabled,
visible: normalized.visible,
toolbarX: normalized.toolbarX,
toolbarY: normalized.toolbarY,
},
};
}

const rawActive = siteState.patterns.find((pattern) => pattern.id === siteState.activePatternId);

if (rawActive?.kind === 'preset') {
const currentSettings = getActivePattern(siteState).settings;

if (!patternSettingsChanged(currentSettings, normalized)) {
return {
...siteState,
session: {
enabled: normalized.enabled,
visible: normalized.visible,
toolbarX: normalized.toolbarX,
toolbarY: normalized.toolbarY,
},
};
}

const nextPattern = createVariationPattern(siteState.patterns, normalized);
return {
...siteState,
activePatternId: nextPattern.id,
patterns: [nextPattern, ...siteState.patterns],
appearance: undefined,
session: undefined,
session: siteState.session,
};
}

Expand All @@ -505,7 +521,7 @@ export function updateActivePatternSettings(
...ensuredState,
activePatternId: getActivePattern(ensuredState).id,
appearance: undefined,
session: undefined,
session: siteState.session,
patterns: ensuredState.patterns.map((pattern) =>
pattern.id === ensuredState.activePatternId
? {
Expand Down Expand Up @@ -537,7 +553,12 @@ export function applyPatternSelection(siteState: SiteGridState, patternId: strin
color: currentSettings.color,
opacity: currentSettings.opacity,
},
session: siteState.session,
session: siteState.session ?? {
enabled: currentSettings.enabled,
visible: currentSettings.visible,
toolbarX: currentSettings.toolbarX,
toolbarY: currentSettings.toolbarY,
},
};
}

Expand Down
2 changes: 1 addition & 1 deletion src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ const MIN_SPACING = 0;
const MAX_SPACING = 240;

export const DEFAULT_SETTINGS: GridSettings = {
enabled: true,
enabled: false,
visible: true,
toolbarX: null,
toolbarY: null,
Expand Down
Loading