Skip to content

WEBDEV-9054: Migrate the donation form elements and controller into elements - #105

Open
jbuckner wants to merge 24 commits into
mainfrom
WEBDEV-9054-migrate-donation-form-elements
Open

jbuckner wants to merge 24 commits into
mainfrom
WEBDEV-9054-migrate-donation-form-elements

Conversation

@jbuckner

@jbuckner jbuckner commented Sep 15, 2026 •

Copy link
Copy Markdown
Collaborator

WEBDEV-9054. Stacked on #103. Last of the five in WEBDEV-8602.

Everything left in donation-form: the form, controller, header, summary, total amount, badged input, payment selector and contact form as ia-donation-* elements, the three modal contents, and the payment flow handlers with the donation flow modal manager. It all lands under src/elements/ia-donation-form/. The icon-* packages become SVGs (masked for the monochrome ones, plain <img> for the brand logos). The controller gets a story against the Braintree sandbox, with a demo endpoint manager, an analytics readout and buttons to pop the upsell and confirmation modals. The story renders in the light DOM (Braintree and PayPal find their containers with document.querySelector) and only loads the third-party scripts after you press "Connect to the sandbox", so the all-elements demo page and app-root.test.ts don't reach out to Braintree, PayPal, Google or reCAPTCHA on their own.

What changed beyond the move:

  • The PayPal cover is a <button>. It was a <div> with a click handler, so it had no keyboard access or accessible name. It has an aria-label like the other provider buttons now, and the brand logos carry alt text.
  • The controller keeps a paymentClients you hand it. It built its own on every environment change, replacing yours if environment landed in a later update. It only rebuilds the set it created itself now.
  • The form has a default donationInfo. Without one the header's edit-donation element threw on render. It uses the same default selection as the controller.
  • The PayPal cover stays put after you press it. With the cover now a <button>, the "collapse the other providers" rule caught it too, so one press with a bad amount hid the cover and the next click reached PayPal's real button. The rule only collapses the provider buttons now, with a test.
  • The upsell thank-you fires once. showThankYouModal logged Donated-X-upsell and called donationSuccessful twice on the upsell path. Once now.
  • The card fields actually hide. The credit card container had two class attributes, so the hidden one was dropped by the parser.
  • Small fixes: TYD is TWD in the currency symbols, the confirm modal's currencyType defaults to USD rather than $, and a Google Pay billing name is trimmed before the first/last split so the surname doesn't keep a leading space.
  • The credit card form is reworked to match iaux-donation-form#190. The card fields are their own ia-donation-credit-card-fields element and sit under the payment selector, with the card number focused when you pick Credit Card. The contact form has visible labels and no placeholders, no Address Line 2, first and last name on one row, and country above state and zip. An empty card field isn't marked red on blur. The contact section is titled "Enter contact information". The unused email, user and locale pin icons are gone.
  • The PayPal button render reads window.paypal.FUNDING defensively. Only the Venmo exclusion needs it, and the script isn't there in tests.
  • Theming variables are --ia-donation-form-* (text, background, badge, payment option and selected option colors, donate button, base font size). The base font size flows down from the form's host to the section and edit elements.
  • Analytics goes through AnalyticsManagerInterface from @internetarchive/analytics-manager, and the modals through @internetarchive/modal-manager. Both are regular dependencies, as decided for the stack.

Tests moved from web-test-runner to Vitest, 131 of them, with a test-mock-modal-manager element and mock flow handlers under test-helpers/. Two contact form tests now assert what the browser actually does on reportValidity(): it focuses the first invalid field, and focusing a field clears its error mark.

Verified locally: build, full suite 1248 passing, eslint and prettier clean, no circular dependencies.

🤖 Generated with Claude Code

https://claude.ai/code/session_01QnX6VHo3EJRzcKfhzBChCf

jbuckner and others added 4 commits September 14, 2026 16:32
… form section into elements

Bottom rung of the donation form stack. The data-models, currency-validator
and form-section packages from iaux-donation-form land in
src/elements/ia-donation-form/ as models/, currency-validator.ts and
form-elements/ia-donation-section.ts.

The 14 model files fold into six. The enums become as-const objects plus a
type, since erasableSyntaxOnly rules out enum. The classes stay classes:
offshoot, the monthly giving portal and the petabox banner all construct
them. The section is ia-donation-section, sized off a 10px base font size
variable like the item navigator so it renders the same under any root font
size, with its CSS variables renamed to --ia-donation-section-*.

story-template gets an importPath property so an element nested inside
another component's directory shows the right import snippet.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QnX6VHo3EJRzcKfhzBChCf
The edit-donation package from iaux-donation-form lands as
ia-donation-edit-donation under src/elements/ia-donation-form/form-elements/,
rendering ia-donation-section for its two steps. The petabox banner uses it
on its own, so it gets its own story.

Enums become as-const objects plus a type. CSS variables move to
--ia-donation-edit-*, sized off a 10px base font size that is passed through
to the sections. The strings go through msg(), the custom amount field gets
an accessible name, and currency.js becomes a dependency.

One inherited bug fixed: with the custom amount hidden, the "is the donor
typing in the custom field" check compared null to null and came out true,
so a preset set programmatically could end up with nothing selected. Guarded
on the field existing, with a test.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QnX6VHo3EJRzcKfhzBChCf
The Braintree manager, payment clients and the five provider handlers
(credit card, PayPal, Venmo, Apple Pay, Google Pay) land under
src/elements/ia-donation-form/braintree/, with the recaptcha manager, the
Venmo session restoration handler and DonationFormError alongside. No
custom elements in this rung. The flow handlers go with the modals next.

The vendored 2020 braintree-web and paypal-checkout-components d.ts files
are replaced by the real @types packages, which split the library module
(typeof braintree.client) from the instance (braintree.Client), so the
clients and handlers are typed that way. The fee_amount_covered custom
field is calculated from the fields like the total already was, since
donationInfo can arrive as a plain object. The hosted fields retry no
longer uses an async promise executor and clears its timer whichever way
the race goes. The recaptcha onload callback global is namespaced so it
can't collide with the old package's on a page that loads both.

Tests move from web-test-runner and sinon to Vitest, with the payment
library mocks under test-helpers/.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QnX6VHo3EJRzcKfhzBChCf
…lements

The rest of donation-form lands under src/elements/ia-donation-form/: the
form, controller, header, summary, total amount, badged input, payment
selector and contact form as ia-donation-* elements, the three modal
contents under modals/, and the payment flow handlers with the donation
flow modal manager under flow-handlers/. The icon-* packages become SVGs,
masked for the monochrome ones and plain <img> for the brand logos. The
controller gets a story wired to the Braintree sandbox with a demo
endpoint manager and buttons for the modals. The story renders in the
light DOM, since Braintree and PayPal find their containers with
document.querySelector, and only loads the third-party scripts once you
press "Connect to the sandbox", so the demo page and its test stay off the
network.

Beyond the move: the payment selector's PayPal cover is a real button
with an aria-label like the other providers, the brand logos have alt
text, and the collapse rule only hides the provider buttons so the cover
survives being pressed with an invalid amount. The upsell thank-you logs
and calls donationSuccessful once instead of twice. The credit card
container lost its duplicate class attribute so the hidden class applies.
The controller no longer replaces a consumer-supplied paymentClients when
environment is set in a later update. The form defaults donationInfo to
the standard selection so the header renders without one. The PayPal
button render reads the funding constants off window.paypal defensively.
TYD is TWD, the confirm modal's currencyType defaults to USD, the hosted
field placeholders go through msg(), and a Google Pay billing name is
trimmed before the first/last split. The form's theming variables are
--ia-donation-form-* and the base font size flows down to the section and
edit elements from the form's host.

modal-manager and analytics-manager are npm dependencies. Tests move to
Vitest, with a mock modal manager element and mock flow handlers under
test-helpers/.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QnX6VHo3EJRzcKfhzBChCf
@jbuckner
jbuckner added this pull request to stack #101 September 15, 2026 06:00
@github-actions

github-actions Bot commented Sep 15, 2026 •

Copy link
Copy Markdown
PR Preview Action v1.8.1

🚀 View preview at
https://internetarchive.github.io/elements/pr/pr-105/

Built to branch ghpages at 2026-10-12 21:37 UTC.
Preview will be ready when the GitHub Pages deployment is complete.

@codecov-commenter

codecov-commenter commented Sep 15, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 64.34109% with 322 lines in your changes missing coverage. Please review.
✅ Project coverage is 86.36%. Comparing base (08d480a) to head (4e06140).

Files with missing lines Patch % Lines
src/elements/ia-donation-form/ia-donation-form.ts 58.44% 49 Missing and 15 partials ⚠️
...form/flow-handlers/handlers/paypal-flow-handler.ts 11.29% 53 Missing and 2 partials ⚠️
...ts/ia-donation-form/ia-donation-form-controller.ts 77.53% 23 Missing and 8 partials ⚠️
...rm/test-helpers/mock-flow-handlers.test-helpers.ts 43.18% 25 Missing ⚠️
...m/flow-handlers/handlers/apple-pay-flow-handler.ts 0.00% 24 Missing ⚠️
...-form/flow-handlers/handlers/venmo-flow-handler.ts 20.68% 22 Missing and 1 partial ⚠️
.../flow-handlers/handlers/google-pay-flow-handler.ts 15.38% 22 Missing ⚠️
...-donation-form/modals/ia-donation-confirm-modal.ts 46.15% 20 Missing and 1 partial ⚠️
...-form/flow-handlers/donation-flow-modal-manager.ts 71.87% 17 Missing and 1 partial ⚠️
...flow-handlers/handlers/credit-card-flow-handler.ts 63.04% 16 Missing and 1 partial ⚠️
... and 7 more
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #105      +/-   ##
==========================================
- Coverage   90.94%   86.36%   -4.58%     
==========================================
  Files         118      143      +25     
  Lines        4107     5010     +903     
  Branches      904     1037     +133     
==========================================
+ Hits         3735     4327     +592     
- Misses        171      446     +275     
- Partials      201      237      +36     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

jbuckner and others added 4 commits September 14, 2026 23:25
The modal-manager element paints its backdrop whenever it's in the DOM, so
the demo page was covered by a black layer that swallowed every click on
every story. The host is expected to hide it, as its README says.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QnX6VHo3EJRzcKfhzBChCf
The demo derives the sidebar entry and the anchor from the story's file
name, so ia-donation-form-story.ts showed up as <ia-donation-form> while
the card inside said <ia-donation-form-controller>.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QnX6VHo3EJRzcKfhzBChCf
…onation-form-models

* origin/main:
  1.1.1 (#118)
  WEBDEV-9132: Fix Safari image-viewer wide-layout breakpoint (#115)
  1.1.0 (#116)
  WEBDEV-9129: Exclude story files from coverage reporting (#113)
  WEBDEV-9121: Migrate histogram-date-range into elements (#112)
  WEBDEV-9130: Exclude nested dist and worktree tests from local runs (#114)
  v1.0.0 (#111)
  WEBDEV-9025: Add a CLAUDE.md covering the publish flow (#94)
  WEBDEV-9063: Typecheck in CI (#107)
  v0.4.1 (#108)
  WEBDEV-9066: Guard custom element registration against duplicate defines (#106)
  v0.4.0 (#99)
  WEBDEV-9022: Migrate the image viewer into elements (#93)
jbuckner and others added 14 commits October 6, 2026 17:18
…onation-form-models

Main removed src/elements/index.ts (WEBDEV-9214), so the donation form
re-exports this branch added to it go with it.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S8FoDPcJXekJd1rL9b9oJJ
…te-edit-donation

Keeps both currency.js and dayjs in package.json. Main removed
src/elements/index.ts (WEBDEV-9214), so the edit-donation re-export goes
with it.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S8FoDPcJXekJd1rL9b9oJJ
The lint rule that came in from main restricts customElement from
lit/decorators.js. Same change #98 made for the section.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S8FoDPcJXekJd1rL9b9oJJ
Ports iaux-donation-form#188 (WEBDEV-8781). Safari private browsing throws
a SecurityError from reading the localStorage or sessionStorage global, so
the lookup now happens inside the guarded probe.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S8FoDPcJXekJd1rL9b9oJJ
Ports the payment layer part of iaux-donation-form#190. The credit card form
focuses the card number field when the donor picks credit card. The vendored
braintree hosted-fields type patch isn't needed here since
@types/braintree-web already declares HostedFields.focus.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S8FoDPcJXekJd1rL9b9oJJ
…ent-layer

Main removed src/elements/index.ts (WEBDEV-9214), so the payment layer
re-exports go with it.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S8FoDPcJXekJd1rL9b9oJJ
…tion-form-elements

Main removed src/elements/index.ts (WEBDEV-9214), so the re-exports go
with it.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S8FoDPcJXekJd1rL9b9oJJ
…tomElement

The lint rule that came in from main restricts customElement from
lit/decorators.js. Same change #98 made for the section.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S8FoDPcJXekJd1rL9b9oJJ
Ports iaux-donation-form#190 (Credit Card Form Update). The card fields move
into a new ia-donation-credit-card-fields element that renders under the
payment selector, and the card number gets focus when Credit Card is
picked. The contact form gets visible labels, drops Address Line 2, puts
first and last name side by side and moves country above state and zip.
An empty hosted field is no longer marked red on blur.

The demo's font variable panel is covered by new Font settings in the
controller story.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S8FoDPcJXekJd1rL9b9oJJ
…dit-donation

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S8FoDPcJXekJd1rL9b9oJJ

* origin/main:
  WEBDEV-9051: Migrate the donation form models, currency validator and form section into elements (#98)

# Conflicts:
#	src/elements/ia-donation-form/element-names.test.ts
…ent-layer

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S8FoDPcJXekJd1rL9b9oJJ

* WEBDEV-9052-migrate-edit-donation:
  WEBDEV-9051: Migrate the donation form models, currency validator and form section into elements (#98)
…tion-form-elements

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S8FoDPcJXekJd1rL9b9oJJ

* WEBDEV-9053-migrate-payment-layer:
  WEBDEV-9051: Migrate the donation form models, currency validator and form section into elements (#98)
Use the stable promised-singleton 0.2.1.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012rHVriHUtoMwtEUwFdyZdS
…tion-form-elements

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012rHVriHUtoMwtEUwFdyZdS
Base automatically changed from WEBDEV-9053-migrate-payment-layer to main October 12, 2026 20:24
jbuckner and others added 2 commits October 12, 2026 13:25
…d blocks

The icons are drawn as CSS masks, which only use alpha, so the white
shapes inside them painted as black. Make those shapes real holes.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0157trZFYVoSW5pgbgkE6aiG

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants