106 lines
9.8 KiB
Markdown
106 lines
9.8 KiB
Markdown
# Phase 9 — Form A Jotform iframe adapter
|
|
|
|
**Date:** 2026-10-25 UTC
|
|
**Repository state:** `faithful-astro-migration` at `f41af1966e5fc46ff3fe37019c30826304997b67` before this unstaged implementation
|
|
**Scope:** prepare only the owner-authorized, isolated-test Form A iframe adapter for `/contact` and `/es/contact`
|
|
|
|
## Scope and evidence boundary
|
|
|
|
The owner supplied the Form A identifier `262946078314059`, hosted origin `https://form.jotform.com`, the exact case-sensitive hidden-field Unique Name `formContext`, fixed contact value `contact`, and English/Spanish form-language availability. Those facts establish candidate embed URLs; they do not establish that the disabled form is ready for live use or that Jotform's `language` parameter has passed runtime acceptance.
|
|
|
|
No Jotform account was opened or modified. No form was enabled, submitted, embedded in a normal staging artifact, or contacted over the network. No patient, parent, staff, or synthetic form answers were entered, stored, logged, emailed, or exported.
|
|
|
|
The existing Form A mock remains intact. The four campaign landing pages retain their permanent logical Form B mapping. Their temporary Form A mock behavior is limited to explicit mock builds and is not affected by this adapter.
|
|
|
|
## Implemented fail-closed contract
|
|
|
|
`src/utils/form-integration.ts` recognizes a fourth configuration value, `isolated-test`, while preserving the existing `off`, `mock`, and fail-closed `live` behavior.
|
|
|
|
| Requirement | Value required to render the adapter |
|
|
|---|---|
|
|
| Integration mode | `PUBLIC_AIA_FORMS_MODE=isolated-test` |
|
|
| Environment role | `PUBLIC_AIA_FORMS_ENVIRONMENT=isolated-test` |
|
|
| Separate authorization | `PUBLIC_AIA_FORMS_ISOLATED_TEST_APPROVED=true` |
|
|
| Publication mode | `PUBLIC_ALLOW_INDEXING` must not be `true` |
|
|
| Route and locale | `/contact` + `en`, or `/es/contact` + `es` |
|
|
|
|
Any missing or invalid value renders the existing disabled Astro contact surface. `live`, including `PUBLIC_AIA_FORMS_LIVE_APPROVED=true`, is not a valid adapter path and remains inactive. The normal runtime resolver still only returns `off` or `mock` to every other form surface.
|
|
|
|
The allowlisted URLs are constructed only from fixed values:
|
|
|
|
| Route | URL |
|
|
|---|---|
|
|
| `/contact` | `https://form.jotform.com/262946078314059?formContext=contact&language=en` |
|
|
| `/es/contact` | `https://form.jotform.com/262946078314059?formContext=contact&language=es` |
|
|
|
|
No visitor query parameter, referrer value, cookie, form value, or attribution data participates in URL construction. The `formContext` prefill selects presentation only; it is not treated as authorization, attribution, or a security control.
|
|
|
|
## Adapter and presentation
|
|
|
|
`src/components/JotformIframe.astro` is a reusable, server-rendered Form A frame component. It preserves the Astro-owned form-card heading and surrounding contact-page layout, supplies a localized iframe title, and provides a localized direct hosted-form fallback. It uses no Jotform helper script, cross-origin DOM access, `postMessage`, load-event success inference, tracking, storage, or parent-native form.
|
|
|
|
The iframe has `referrerpolicy="no-referrer"`, no permissive `allow` attribute, and the minimal current sandbox `allow-forms allow-scripts allow-same-origin`. The form is disabled and no actual Jotform runtime was loaded, so the owner must verify this sandbox against Jotform's real validation, confirmation, accessibility, and language behavior before accepting it for any live workflow. The responsive frame is 54rem at desktop and 60rem below 640px; this prevents parent horizontal scrolling in local tests but its final height remains an acceptance item with the real form.
|
|
|
|
The Jotform Builder CSS at `integrations/jotform/aia-form-a.css` now includes narrowly scoped `!important` overrides for the Builder theme's outer surface, labels, inputs, and submit-button design tokens. Required indicators, errors, and focus styles remain visible. This reconciles the owner-provided Builder styling result; it is not an independent account or generated-DOM audit. `integrations/jotform/README.md` documents the style reconciliation and isolated-test contract without changing the separate P9-03 contract or the open documentation PR.
|
|
|
|
## Changed files
|
|
|
|
- `.env.example` — documents the two additional default-false/local isolated-test gates.
|
|
- `integrations/jotform/aia-form-a.css` — applies Builder theme overrides while retaining error and focus visibility.
|
|
- `integrations/jotform/README.md` — records the CSS and isolated-test adapter instructions.
|
|
- `src/utils/form-integration.ts` — typed isolated-test resolver, fixed Form A URL builder, localized adapter copy, and route/locale gates.
|
|
- `src/components/JotformIframe.astro` — accessible, responsive iframe and hosted fallback.
|
|
- `src/components/pages/ContactPage.astro` — selects adapter only after the full resolver accepts `/contact` or `/es/contact`.
|
|
- `tools/jotform-form-a.test.mjs` — pure configuration, URL, route, source-safety, and CSS tests.
|
|
- `tools/jotform-form-a-browser.mjs` — loopback-only, request-intercepted Chromium adapter check.
|
|
- `tools/phase10-artifact-verify.mjs` and `tools/phase10-artifact.test.mjs` — reject the isolated-test Form A identifier in shipped staging or indexing artifacts, including text assets.
|
|
- `tools/library.test.mjs` — preserves the signed slashless nginx-template preamble in the existing redirect-template assertion.
|
|
|
|
## Verification
|
|
|
|
### Static and unit checks
|
|
|
|
| Command | Result |
|
|
|---|---:|
|
|
| `node --experimental-strip-types --test tools/jotform-form-a.test.mjs tools/form-a-preview.test.mjs tools/phase9-nonproduction.test.mjs` | 14 passed |
|
|
| `npm run check` | 0 errors, 0 warnings, 0 hints |
|
|
| `npm run test:markdown` | 20 passed |
|
|
| `npm run test:publication` | 33 passed |
|
|
| `npm run test:seo` | 35 passed |
|
|
| `npm run test:library` | 53 passed |
|
|
| `npm run test:routes` | 10 passed |
|
|
| `node --test tools/campaign.test.mjs tools/phase10-artifact.test.mjs` | 18 passed |
|
|
|
|
The first Library run exposed an assertion that predated the signed slashless-routing template. The test was corrected to require its documented 308/static-serving preamble and to require that the generated redirect rules remain the template suffix. It did not alter `nginx-rewrites.conf` or any deployment setting.
|
|
|
|
### Build artifacts
|
|
|
|
| Build | Result |
|
|
|---|---|
|
|
| Normal explicit `off` staging build | 128 HTML routes; all `noindex,nofollow`; empty sitemap; zero hreflang; no Form A identifier or iframe; zero active non-search native form actions |
|
|
| Explicit `mock` build | 128 routes; no Form A identifier or iframe in the generated artifact |
|
|
| Fully gated `isolated-test` build | 128 routes; adapter emitted only at the two contact routes with fixed EN/ES URLs |
|
|
| Indexing-enabled build with all isolated flags set | 127 routes; 109 sitemap URLs; 180 hreflang links; `/lp` absent; Form A adapter and identifier excluded; contact pages render the off fallback |
|
|
|
|
`phase10-artifact-verify.mjs` passed for normal staging and indexing output. Its new exact-ID check deliberately rejects this isolated-test adapter from any normal release artifact.
|
|
|
|
### Guarded Chromium evidence
|
|
|
|
Playwright 1.61.0 and Chromium at `/etc/profiles/per-user/alice/bin/chromium` ran against loopback-only Astro previews.
|
|
|
|
- Mock mode opened `/contact` and `/es/contact` at 390px and 1440px: four checks passed, no Jotform attempt, no non-read request. Existing Leaflet CSS/JS GET attempts were blocked before transmission.
|
|
- Isolated-test mode opened the same two routes at 390px and 1440px, plus both routes at 390px with JavaScript disabled: six checks passed. The localized title, exact fixed URL, immediately visible fallback, sandbox, no-referrer policy, parent focusability, single H1, no parent native form, no-JavaScript fallback, and no parent overflow all passed. Four full-page screenshots were written outside the repository in `/tmp/aia-form-a-iframe-9tLPnc`.
|
|
- The browser attempted exactly six Jotform iframe **GET** loads — one EN and one ES URL at each JavaScript-enabled viewport and one per locale with JavaScript disabled. The test route handler intercepted and aborted all six before network transmission. It recorded zero POST, DELETE, WebSocket, production API, analytics, advertising, or conversion-event attempt. It did not inspect iframe content or treat the load attempt as success.
|
|
|
|
These results validate the local adapter boundaries only. They do not prove that the disabled Jotform is reachable, that either language runtime works, that a form can submit, or that an accepted lead exists.
|
|
|
|
## Required owner-authorized acceptance before any live use
|
|
|
|
1. Keep Form A disabled until the JF-01 through JF-08 configuration review is recorded, including question properties, PHI protection, conditions, recipients, templates, integrations, retention, redirect/POST-forwarding settings, and availability.
|
|
2. Verify that the Jotform runtime recognizes the fixed `language=en` and `language=es` URLs, with complete English/Spanish labels, validation, confirmation, and accessible native Submit-button behavior.
|
|
3. Test the Builder CSS, sandbox, frame height, responsive reflow, keyboard flow, focus, fallback, blocked/slow frame state, and real screen-reader experience at 320px, 390px, 768px, and 1440px.
|
|
4. Separately authorize JF-09 synthetic testing: an approved environment, test window, permitted origins/methods, synthetic data, recipient roles, generic notification content, failure/duplicate behavior, and no PHI/Contabo/Google transfer.
|
|
5. Obtain operations, clinical, fluent-Spanish, privacy/legal/security, hosting/CSP/referrer, and release-owner approval before mapping this adapter to any non-isolated or production artifact.
|
|
|
|
No Google Ads, GA4, GTM, analytics, advertising, Jotform webhook, Contabo form endpoint, native POST, or API/counter behavior was added or activated. The Nginx template remains an untested deployment template. No deployment, Git staging, commit, merge, or push occurred.
|