implementation #1
4 changed files with 129 additions and 13 deletions
87
package-lock.json
generated
87
package-lock.json
generated
|
@ -16,7 +16,8 @@
|
||||||
"react-scripts": "5.0.1"
|
"react-scripts": "5.0.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"html-react-parser": "^5.2.5"
|
"html-react-parser": "^5.2.5",
|
||||||
|
"react-imask": "^7.6.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@adobe/css-tools": {
|
"node_modules/@adobe/css-tools": {
|
||||||
|
@ -1812,6 +1813,19 @@
|
||||||
"node": ">=6.9.0"
|
"node": ">=6.9.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@babel/runtime-corejs3": {
|
||||||
|
"version": "7.27.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.27.6.tgz",
|
||||||
|
"integrity": "sha512-vDVrlmRAY8z9Ul/HxT+8ceAru95LQgkSKiXkSYZvqtbkPSfhZJgpRp45Cldbh1GJ1kxzQkI70AqyrTI58KpaWQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"core-js-pure": "^3.30.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.9.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@babel/template": {
|
"node_modules/@babel/template": {
|
||||||
"version": "7.23.9",
|
"version": "7.23.9",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.23.9.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.23.9.tgz",
|
||||||
|
@ -5641,10 +5655,11 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/core-js-pure": {
|
"node_modules/core-js-pure": {
|
||||||
"version": "3.29.0",
|
"version": "3.43.0",
|
||||||
"resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.29.0.tgz",
|
"resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.43.0.tgz",
|
||||||
"integrity": "sha512-v94gUjN5UTe1n0yN/opTihJ8QBWD2O8i19RfTZR7foONPWArnjB96QA/wk5ozu1mm6ja3udQCzOzwQXTxi3xOQ==",
|
"integrity": "sha512-i/AgxU2+A+BbJdMxh3v7/vxi2SbFqxiFmg6VsDwYB4jkucrd1BZNA9a9gphC0fYMG5IBSgQcbQnk865VCLe7xA==",
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
|
"license": "MIT",
|
||||||
"funding": {
|
"funding": {
|
||||||
"type": "opencollective",
|
"type": "opencollective",
|
||||||
"url": "https://opencollective.com/core-js"
|
"url": "https://opencollective.com/core-js"
|
||||||
|
@ -8809,6 +8824,19 @@
|
||||||
"node": ">= 4"
|
"node": ">= 4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/imask": {
|
||||||
|
"version": "7.6.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/imask/-/imask-7.6.1.tgz",
|
||||||
|
"integrity": "sha512-sJlIFM7eathUEMChTh9Mrfw/IgiWgJqBKq2VNbyXvBZ7ev/IlO6/KQTKlV/Fm+viQMLrFLG/zCuudrLIwgK2dg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime-corejs3": "^7.24.4"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"npm": ">=4.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/immer": {
|
"node_modules/immer": {
|
||||||
"version": "9.0.19",
|
"version": "9.0.19",
|
||||||
"resolved": "https://registry.npmjs.org/immer/-/immer-9.0.19.tgz",
|
"resolved": "https://registry.npmjs.org/immer/-/immer-9.0.19.tgz",
|
||||||
|
@ -14162,6 +14190,23 @@
|
||||||
"resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.11.tgz",
|
"resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.11.tgz",
|
||||||
"integrity": "sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg=="
|
"integrity": "sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg=="
|
||||||
},
|
},
|
||||||
|
"node_modules/react-imask": {
|
||||||
|
"version": "7.6.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-imask/-/react-imask-7.6.1.tgz",
|
||||||
|
"integrity": "sha512-vLNfzcCz62Yzx/GRGh5tiCph9Gbh2cZu+Tz8OiO5it2eNuuhpA0DWhhSlOtVtSJ80+Bx+vFK5De8eQ9AmbkXzA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"imask": "^7.6.1",
|
||||||
|
"prop-types": "^15.8.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"npm": ">=4.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": ">=0.14.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/react-is": {
|
"node_modules/react-is": {
|
||||||
"version": "17.0.2",
|
"version": "17.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
|
||||||
|
@ -18265,6 +18310,15 @@
|
||||||
"regenerator-runtime": "^0.13.11"
|
"regenerator-runtime": "^0.13.11"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@babel/runtime-corejs3": {
|
||||||
|
"version": "7.27.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.27.6.tgz",
|
||||||
|
"integrity": "sha512-vDVrlmRAY8z9Ul/HxT+8ceAru95LQgkSKiXkSYZvqtbkPSfhZJgpRp45Cldbh1GJ1kxzQkI70AqyrTI58KpaWQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"core-js-pure": "^3.30.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@babel/template": {
|
"@babel/template": {
|
||||||
"version": "7.23.9",
|
"version": "7.23.9",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.23.9.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.23.9.tgz",
|
||||||
|
@ -21026,9 +21080,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"core-js-pure": {
|
"core-js-pure": {
|
||||||
"version": "3.29.0",
|
"version": "3.43.0",
|
||||||
"resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.29.0.tgz",
|
"resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.43.0.tgz",
|
||||||
"integrity": "sha512-v94gUjN5UTe1n0yN/opTihJ8QBWD2O8i19RfTZR7foONPWArnjB96QA/wk5ozu1mm6ja3udQCzOzwQXTxi3xOQ=="
|
"integrity": "sha512-i/AgxU2+A+BbJdMxh3v7/vxi2SbFqxiFmg6VsDwYB4jkucrd1BZNA9a9gphC0fYMG5IBSgQcbQnk865VCLe7xA=="
|
||||||
},
|
},
|
||||||
"core-util-is": {
|
"core-util-is": {
|
||||||
"version": "1.0.3",
|
"version": "1.0.3",
|
||||||
|
@ -23301,6 +23355,15 @@
|
||||||
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz",
|
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz",
|
||||||
"integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ=="
|
"integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ=="
|
||||||
},
|
},
|
||||||
|
"imask": {
|
||||||
|
"version": "7.6.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/imask/-/imask-7.6.1.tgz",
|
||||||
|
"integrity": "sha512-sJlIFM7eathUEMChTh9Mrfw/IgiWgJqBKq2VNbyXvBZ7ev/IlO6/KQTKlV/Fm+viQMLrFLG/zCuudrLIwgK2dg==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"@babel/runtime-corejs3": "^7.24.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"immer": {
|
"immer": {
|
||||||
"version": "9.0.19",
|
"version": "9.0.19",
|
||||||
"resolved": "https://registry.npmjs.org/immer/-/immer-9.0.19.tgz",
|
"resolved": "https://registry.npmjs.org/immer/-/immer-9.0.19.tgz",
|
||||||
|
@ -26973,6 +27036,16 @@
|
||||||
"resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.11.tgz",
|
"resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.11.tgz",
|
||||||
"integrity": "sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg=="
|
"integrity": "sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg=="
|
||||||
},
|
},
|
||||||
|
"react-imask": {
|
||||||
|
"version": "7.6.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-imask/-/react-imask-7.6.1.tgz",
|
||||||
|
"integrity": "sha512-vLNfzcCz62Yzx/GRGh5tiCph9Gbh2cZu+Tz8OiO5it2eNuuhpA0DWhhSlOtVtSJ80+Bx+vFK5De8eQ9AmbkXzA==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"imask": "^7.6.1",
|
||||||
|
"prop-types": "^15.8.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"react-is": {
|
"react-is": {
|
||||||
"version": "17.0.2",
|
"version": "17.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
|
||||||
|
|
|
@ -35,6 +35,7 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"html-react-parser": "^5.2.5"
|
"html-react-parser": "^5.2.5",
|
||||||
|
|||||||
|
"react-imask": "^7.6.1"
|
||||||
gaiety
commented
Many react mask plugins don't work, and very few address accessibility. If this was a real project I'd pair with designers to find alternative ways to implement these forms without masking, more in line with the GOV.UK design system does. Many react mask plugins don't work, and very few address accessibility.
If this was a real project I'd pair with designers to find alternative ways to implement these forms _without_ masking, more in line with the GOV.UK design system does.
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { useState, useRef } from 'react';
|
import { useState, useRef } from 'react';
|
||||||
|
import { IMaskInput } from 'react-imask';
|
||||||
|
|
||||||
const CentsInADollar = 100;
|
const CentsInADollar = 100;
|
||||||
const DefaultTotal = 0;
|
const DefaultTotal = 0;
|
||||||
|
@ -21,12 +22,11 @@ function BandForm({ band }) {
|
||||||
const formData = new FormData(formRef.current);
|
const formData = new FormData(formRef.current);
|
||||||
const ticketQuantities = formData.getAll('ticketQuantity');
|
const ticketQuantities = formData.getAll('ticketQuantity');
|
||||||
|
|
||||||
|
const total = ticketQuantities.reduce((total, quantity, index) => {
|
||||||
setTotal(
|
|
||||||
ticketQuantities.reduce((total, quantity, index) => {
|
|
||||||
return total += (band.ticketTypes[index].cost * quantity);
|
return total += (band.ticketTypes[index].cost * quantity);
|
||||||
}, 0)
|
}, 0)
|
||||||
)
|
|
||||||
|
setTotal(total > 0 ? total : 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -50,6 +50,7 @@ function BandForm({ band }) {
|
||||||
id={name}
|
id={name}
|
||||||
type="number"
|
type="number"
|
||||||
step="1"
|
step="1"
|
||||||
|
min="0"
|
||||||
onChange={ticketQuantityChanged}
|
onChange={ticketQuantityChanged}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -66,12 +67,14 @@ function BandForm({ band }) {
|
||||||
<div className="information">
|
<div className="information">
|
||||||
<div className="form-row">
|
<div className="form-row">
|
||||||
<input
|
<input
|
||||||
|
required
|
||||||
name="first_name"
|
name="first_name"
|
||||||
label="First Name"
|
label="First Name"
|
||||||
placeholder="First Name"
|
placeholder="First Name"
|
||||||
type="text"
|
type="text"
|
||||||
/>
|
/>
|
||||||
<input
|
<input
|
||||||
|
required
|
||||||
name="last_name"
|
name="last_name"
|
||||||
label="Last Name"
|
label="Last Name"
|
||||||
placeholder="Last Name"
|
placeholder="Last Name"
|
||||||
|
@ -79,6 +82,8 @@ function BandForm({ band }) {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<input
|
<input
|
||||||
|
required
|
||||||
|
{/* TODO: third party library address verification */}
|
||||||
name="address"
|
name="address"
|
||||||
label="Address"
|
label="Address"
|
||||||
placeholder="Address"
|
placeholder="Address"
|
||||||
|
@ -88,6 +93,39 @@ function BandForm({ band }) {
|
||||||
|
|
||||||
<fieldset className="payment">
|
<fieldset className="payment">
|
||||||
<legend>Payment Details</legend>
|
<legend>Payment Details</legend>
|
||||||
|
|
||||||
|
<IMaskInput
|
||||||
gaiety
commented
Need to test this with an actual screenreader. Should announce when prices change so the user is aware how much their "cart" costs. Need to test this with an actual screenreader. Should announce when prices change so the user is aware how much their "cart" costs.
|
|||||||
|
required
|
||||||
|
mask="0000 0000 0000 0000"
|
||||||
|
{/* TODO: regex pattern */}
|
||||||
|
name="cardNumber"
|
||||||
|
label="Card Number"
|
||||||
gaiety
commented
All of these should have All of these should have `autocomplete` properties for easier autofill.
|
|||||||
|
placeholder="0000 0000 0000 0000"
|
||||||
|
type="text"
|
||||||
|
></IMaskInput>
|
||||||
|
|
||||||
|
<div className="form-row">
|
||||||
|
<IMaskInput
|
||||||
|
required
|
||||||
|
{/* TODO: regex pattern */}
|
||||||
|
mask="00 / 00"
|
||||||
|
pattern="\d*"
|
||||||
|
name="cardExpiration"
|
||||||
|
label="Card Expiration (MM/YY)"
|
||||||
|
placeholder="MM / YY"
|
||||||
|
type="text"
|
||||||
|
></IMaskInput>
|
||||||
|
|
||||||
|
<IMaskInput
|
||||||
|
required
|
||||||
|
{/* TODO: regex pattern */}
|
||||||
|
mask={Number}
|
||||||
|
name="cardCVV"
|
||||||
|
label="Card CVV"
|
||||||
|
placeholder="CVV"
|
||||||
|
type="text"
|
||||||
|
></IMaskInput>
|
||||||
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
gaiety
commented
Currently only checks that anything is entered, given more time I'd write regex for validation (likely against the HTML Currently only checks that anything is entered, given more time I'd write regex for validation (likely against the HTML `pattern` attribute).
|
|||||||
<button type="submit" disabled={formIsPending}>Get Tickets</button>
|
<button type="submit" disabled={formIsPending}>Get Tickets</button>
|
||||||
|
|
|
@ -112,5 +112,9 @@ h4 {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: var(--spacing-md);
|
gap: var(--spacing-md);
|
||||||
|
|
||||||
|
legend {
|
||||||
|
margin-bottom: var(--spacing-md);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue
Description comes from JSON as raw HTML, this is a quick solution so we don't trust the HTML too far. But I'd do more research into the cleanest solution.
Extra points for the backend simply storing as markdown instead.