diff --git a/changelog.md b/changelog.md index 3d6bf121..29bc19e5 100644 --- a/changelog.md +++ b/changelog.md @@ -217,3 +217,5 @@ - BO : Fixed issue when a freshly installed module logged an account error before any API credentials were entered - BO : Fixed issue when the "Could not reach your Saferpay account" warning kept showing after payment methods had loaded successfully - Fixed issue when files removed in this version stayed on disk after an upgrade, leaving obsolete iframe checkout controllers reachable and re-creating obsolete menu tabs on module reset +- FO : Fixed issue when the Saferpay Fields CVC field looked editable while the card number was incomplete, showing no cursor and accepting no input +- FO : Fixed issue when the Saferpay Fields card form accepted clicks and keystrokes while it was still loading, so the first characters typed were invisible and appeared to be lost diff --git a/views/css/front/saferpay_checkout.css b/views/css/front/saferpay_checkout.css index fbb08c87..d22269c6 100644 --- a/views/css/front/saferpay_checkout.css +++ b/views/css/front/saferpay_checkout.css @@ -106,9 +106,33 @@ input[type="radio"][name^="saved_card_"] { /* While the SDK initialises, each iframe briefly paints with its default input styling before our injected stylesheet applies — keep the iframes invisible until the SDK's - init onSuccess lifts this class, leaving the outlined fieldsets as loading skeleton. */ + init onSuccess lifts this class, leaving the outlined fieldsets as loading skeleton. + An invisible iframe still takes clicks and keystrokes, so without pointer-events a + customer clicking a field during init focuses and types into a field they cannot see: + no caret, no characters, and the field reads as broken. */ .saferpay-inline-fields.saferpay-fields-loading iframe { opacity: 0; + pointer-events: none; +} + +/* Two states where a field is drawn but cannot be typed into: while the SDK initialises, and + while the SDK holds the CVC disabled until the card number passes its CheckCard lookup (the + inner input is muted through the injected :disabled rule, see inline-fields.js). Muting the + outline and label too means the field reads as unavailable before it is clicked instead of + after. Declared ahead of the focus/error rules below so both still take precedence. */ +.saferpay-inline-fields.saferpay-fields-loading .saferpay-field, +.saferpay-inline-fields .saferpay-field.is-locked { + border-color: #e0e0e0; + background: #f7f8f8; +} + +.saferpay-inline-fields.saferpay-fields-loading .saferpay-field legend, +.saferpay-inline-fields .saferpay-field.is-locked legend { + color: #9aa4a8; +} + +.saferpay-inline-fields.saferpay-fields-loading .saferpay-field { + cursor: progress; } /* Focus feedback: the SDK's own focus styling is cleared (it shrank the field), so the diff --git a/views/js/front/inline-fields.js b/views/js/front/inline-fields.js index 62c3e7ec..aa4bdad8 100644 --- a/views/js/front/inline-fields.js +++ b/views/js/front/inline-fields.js @@ -47,6 +47,13 @@ // rebuild+re-init when the same option fires a spurious change event. var renderedContainerId = null; + // The loading state makes the fields inert, so it must not outlive an init callback that + // never arrives: a silently failed SDK init would otherwise leave a form nobody can type + // into. Generous on purpose: it is a last resort, not the normal path (init is well under + // a second), and lifting it early would show the fields before they are styled. + var LOADING_TIMEOUT = 10000; + var loadingTimeout = null; + // The customer-entered card inputs live in cross-origin Saferpay iframes, so their // validity is only known through the SDK's onValidated callback. It fires when a field // loses focus; an untouched field never fires it. We therefore default every required @@ -103,10 +110,12 @@ ''; } - // The slot starts in the "loading" state: the field iframes are kept invisible until - // the SDK reports successful initialisation, because each iframe first paints with the - // SDK's default input styling and only then applies our injected stylesheet — showing - // it earlier flashes an unstyled square input inside the outlined field. + // The slot starts in the "loading" state: the field iframes are kept invisible and inert + // until the SDK reports successful initialisation, because each iframe first paints with + // the SDK's default input styling and only then applies our injected stylesheet, so + // showing it earlier flashes an unstyled square input inside the outlined field. The + // fieldsets render muted while it lasts (see saferpay_checkout.css), because a field that + // looks ready but silently drops the click reads as broken. function fieldsFormMarkup() { return '' + '