diff --git a/app/public/assets/posts/california-wealth-tax-calculator.png b/app/public/assets/posts/california-wealth-tax-calculator.png new file mode 100644 index 000000000..efb89b551 Binary files /dev/null and b/app/public/assets/posts/california-wealth-tax-calculator.png differ diff --git a/app/src/components/IframeContent.tsx b/app/src/components/IframeContent.tsx index b044f146a..7d6abff4d 100644 --- a/app/src/components/IframeContent.tsx +++ b/app/src/components/IframeContent.tsx @@ -24,7 +24,7 @@ export default function IframeContent({ // Derive allowed origin from iframe URL for postMessage validation const iframeOrigin = useMemo(() => { try { - return new URL(url).origin; + return new URL(url, window.location.origin).origin; } catch { return ''; } diff --git a/app/src/data/apps/appTransformers.test.ts b/app/src/data/apps/appTransformers.test.ts index b34f3d400..16cf5963a 100644 --- a/app/src/data/apps/appTransformers.test.ts +++ b/app/src/data/apps/appTransformers.test.ts @@ -28,8 +28,8 @@ describe('appTransformers', () => { test('all apps have valid source URLs', () => { apps.forEach((app) => { - // Allow external URLs (https://) or local paths (/assets/) - expect(app.source).toMatch(/^(https?:\/\/|\/assets\/)/); + // Allow external URLs, asset files, or same-origin app proxy paths. + expect(app.source).toMatch(/^(https?:\/\/|\/assets\/|\/[a-z]{2}\/)/); }); }); diff --git a/app/src/data/apps/apps.json b/app/src/data/apps/apps.json index 2b982f884..b33997b10 100644 --- a/app/src/data/apps/apps.json +++ b/app/src/data/apps/apps.json @@ -75,6 +75,19 @@ "date": "2026-03-06 12:00:00", "authors": ["max-ghenis", "pavel-makarchuk"] }, + { + "type": "iframe", + "slug": "california-wealth-tax", + "title": "California wealth tax fiscal impact calculator", + "description": "Estimate how California's proposed billionaire wealth tax changes revenue once avoidance, migration, return flows, and income-tax offsets are taken into account", + "source": "/us/california-wealth-tax/embed", + "tags": ["us", "us-ca", "policy", "interactives", "tax"], + "countryId": "us", + "displayWithResearch": true, + "image": "california-wealth-tax-calculator.png", + "date": "2026-03-25 12:00:00", + "authors": ["max-ghenis"] + }, { "type": "iframe", "slug": "uk-land-value-tax", diff --git a/app/src/tests/unit/components/IframeContent.test.tsx b/app/src/tests/unit/components/IframeContent.test.tsx new file mode 100644 index 000000000..7bd6397c2 --- /dev/null +++ b/app/src/tests/unit/components/IframeContent.test.tsx @@ -0,0 +1,43 @@ +import { render } from '@testing-library/react'; +import { MemoryRouter, Route, Routes } from 'react-router-dom'; +import { afterEach, describe, expect, test, vi } from 'vitest'; +import IframeContent from '@/components/IframeContent'; + +describe('IframeContent', () => { + afterEach(() => { + vi.restoreAllMocks(); + }); + + test('accepts same-origin relative iframe URLs for hash sync messages', () => { + const replaceStateSpy = vi.spyOn(window.history, 'replaceState'); + + render( + + + + } + /> + + + ); + + window.dispatchEvent( + new MessageEvent('message', { + origin: window.location.origin, + data: { + type: 'hashchange', + hash: '/scenario/rauh', + }, + }) + ); + + expect(replaceStateSpy).toHaveBeenCalledWith( + null, + '', + '/us/california-wealth-tax/scenario/rauh' + ); + }); +}); diff --git a/app/vite.config.mjs b/app/vite.config.mjs index 643f6f995..b88d01222 100644 --- a/app/vite.config.mjs +++ b/app/vite.config.mjs @@ -89,6 +89,13 @@ export default defineConfig({ // Use discovered ports in dev, defaults otherwise port: appMode === 'calculator' ? (calculatorPort ?? 3001) : (websitePort ?? 3000), strictPort: true, + proxy: { + '/us/california-wealth-tax/embed': { + target: 'https://california-wealth-tax.vercel.app', + changeOrigin: true, + secure: true, + }, + }, }, define: viteDefines, // Use separate cache directories for website and calculator to avoid conflicts diff --git a/vercel.json b/vercel.json index 50ccfe947..9296c2134 100644 --- a/vercel.json +++ b/vercel.json @@ -65,6 +65,14 @@ "source": "/us/keep-your-pay-act/:path*", "destination": "https://keep-your-pay-act.vercel.app/us/keep-your-pay-act/:path*" }, + { + "source": "/us/california-wealth-tax/embed", + "destination": "https://california-wealth-tax.vercel.app/us/california-wealth-tax/embed" + }, + { + "source": "/us/california-wealth-tax/embed/:path*", + "destination": "https://california-wealth-tax.vercel.app/us/california-wealth-tax/embed/:path*" + }, { "source": "/us/taxsim", "destination": "https://policyengine-taxsim-policy-engine.vercel.app/us/taxsim"