implementation #1

Open
gaiety wants to merge 11 commits from implementation into main
4 changed files with 129 additions and 13 deletions
Showing only changes of commit 4b9e0627be - Show all commits

87
package-lock.json generated
View file

@ -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",

View file

@ -35,6 +35,7 @@
] ]
}, },
"devDependencies": { "devDependencies": {
"html-react-parser": "^5.2.5" "html-react-parser": "^5.2.5",
Review

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.

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.
"react-imask": "^7.6.1"
Review

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.
} }
} }

View file

@ -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) => {
return total += (band.ticketTypes[index].cost * quantity);
}, 0)
setTotal( setTotal(total > 0 ? total : 0)
ticketQuantities.reduce((total, quantity, index) => {
return total += (band.ticketTypes[index].cost * quantity);
}, 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
Review

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"
Review

All of these should have autocomplete properties for easier autofill.

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>
Review

Currently only checks that anything is entered, given more time I'd write regex for validation (likely against the HTML pattern attribute).

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>

View file

@ -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);
}
} }
} }