Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Gift cards/add recipient #2412

Merged
merged 70 commits into from
Mar 21, 2023
Merged
Show file tree
Hide file tree
Changes from 65 commits
Commits
Show all changes
70 commits
Select commit Hold shift + click to select a range
c49fbe9
Gift cards/add recipient add template (#102)
fredma Feb 13, 2023
e61dbff
Align gift card recipient checkbox to 1st line of text (#129)
fredma Feb 21, 2023
bc5dead
Clear Gift Card Recipient Form upon untick and cart adding(#132)
fredma Feb 23, 2023
e3f0cb5
Rename gift card template recipient line item properties
mzarud Feb 17, 2023
89d3e65
Improve properties display for gift card recipient (#137)
fredma Mar 2, 2023
41467d2
Display gift card balance on the Gift Card View page
DanamiteGS Jan 26, 2023
fa1d844
feat(#51542) Set the hidden control field in one of two ways
antiBaconMachine Feb 23, 2023
a7884a1
Remove nested form elements from buy button snippets
antiBaconMachine Feb 23, 2023
64488ef
fix(#51542): wrap dummy form in product form class
antiBaconMachine Feb 23, 2023
247a4e9
feat(#51543): Correct checkbox bahvior in all cases
antiBaconMachine Feb 23, 2023
e9380e0
feat(#51543): use the .no-js and .js classes to avoid content flashes…
antiBaconMachine Feb 23, 2023
b9f5486
feat(#51731): Change the label on the recipient email field depending…
antiBaconMachine Feb 23, 2023
1e9763f
feat(#51542): Error formatting for no js recipient form
antiBaconMachine Feb 23, 2023
49d19d5
fix: lower case field names
antiBaconMachine Feb 23, 2023
793406a
Restore buy buttons to their own block
antiBaconMachine Feb 24, 2023
1bb0d96
This attempts to standardise error handling between liquid and javasc…
antiBaconMachine Feb 24, 2023
617fc7f
clear error message when the checkbox is unchecked
antiBaconMachine Feb 24, 2023
f92b3d4
add error handling for name and message.
antiBaconMachine Feb 24, 2023
16f7fb2
close textarea immediately to avoid prefilling with spaces
antiBaconMachine Feb 28, 2023
70040ec
Use the error icon snippet instead of copypasta
antiBaconMachine Feb 28, 2023
84d8e85
remove some form sizing
antiBaconMachine Feb 28, 2023
46e9e2b
remove input field styling from form element
antiBaconMachine Feb 28, 2023
517d99c
copy max-width from product form to recipient form
antiBaconMachine Feb 28, 2023
57cb5a7
Add show errors option to buy buttons snippet on main product template
antiBaconMachine Mar 2, 2023
053cf8e
update recipient block copy
antiBaconMachine Mar 2, 2023
d624c4b
Group repeated css rules
antiBaconMachine Mar 3, 2023
701e107
Access form state for message in the correct location
fredma Mar 6, 2023
73a7cd3
Split up errors into their respective fields
antiBaconMachine Feb 28, 2023
ec25e91
swap html hidden for css display: none
antiBaconMachine Mar 6, 2023
fbcb62f
fix inline svg error icons
antiBaconMachine Mar 6, 2023
17defb7
only apply hidden style to form fields, not error header
antiBaconMachine Mar 6, 2023
ab74e9b
enumerate errors
antiBaconMachine Mar 6, 2023
c29e433
Don't clear error header text as it is static and liquid generated
antiBaconMachine Mar 6, 2023
a8cea7f
link to field not error message
antiBaconMachine Mar 6, 2023
aa7b4e5
clear error messages before rendering new ones
antiBaconMachine Mar 6, 2023
e94ecf0
copy liquid from email to name and message errors
antiBaconMachine Mar 6, 2023
cd8f958
Read localised field labels from DOM
antiBaconMachine Mar 7, 2023
b61ae4a
Save default error message header and restore it when needed
antiBaconMachine Mar 7, 2023
9543809
add period after error message
antiBaconMachine Mar 7, 2023
722bfee
rename recipient block
antiBaconMachine Mar 7, 2023
4a2a92f
rename recipient form internals
antiBaconMachine Mar 7, 2023
e698fc0
Don't abbreviate 'Element'
antiBaconMachine Mar 13, 2023
c251a19
Use single line if expressions
antiBaconMachine Mar 13, 2023
091373d
remove gitt card template and graft recipient form onto buy buttons b…
antiBaconMachine Mar 13, 2023
b470d20
PR feedback
antiBaconMachine Mar 13, 2023
f9f50d6
refactor the recipient form into its own snippet
antiBaconMachine Mar 13, 2023
b656d7a
PR feedback, mainly whitespace and semantic tweaks
antiBaconMachine Mar 14, 2023
fe869bb
Add aria-labelledby attributes to form
antiBaconMachine Mar 14, 2023
6aa7f56
Various whitespace and stylistic fixes
antiBaconMachine Mar 14, 2023
f753c47
rename class to be more bem-like
antiBaconMachine Mar 14, 2023
2a81400
remove gift card recipient form block
antiBaconMachine Mar 14, 2023
d30acab
Remove leading labels from fields
antiBaconMachine Mar 14, 2023
ff95e95
UX feedback fixes
antiBaconMachine Mar 15, 2023
e562f4e
use plaeholder instead of label as error key
antiBaconMachine Mar 15, 2023
fd30c0d
Dynamically calculate available space for label
antiBaconMachine Mar 15, 2023
405e665
Copy updates for gift card toggle
antiBaconMachine Mar 15, 2023
92380a6
default feature to off
antiBaconMachine Mar 15, 2023
69b07ee
Update 20 translation files
translation-platform[bot] Mar 16, 2023
7cd7e6c
Update 5 translation files
translation-platform[bot] Mar 16, 2023
5fd63f7
Update 16 translation files
translation-platform[bot] Mar 16, 2023
f1a3246
Update 5 translation files
translation-platform[bot] Mar 17, 2023
c9b9283
Update 4 translation files
translation-platform[bot] Mar 17, 2023
e192755
Increase stroke width of checkbox
antiBaconMachine Mar 20, 2023
149b323
remove unused variable
antiBaconMachine Mar 20, 2023
0204dfe
Merge branch 'main' into gift-cards/add-recipient
antiBaconMachine Mar 20, 2023
8e29538
Update snippets/gift-card-recipient-form.liquid
antiBaconMachine Mar 21, 2023
f578763
Update snippets/gift-card-recipient-form.liquid
antiBaconMachine Mar 21, 2023
e720fba
Remove leftover stuff for gift card block which no longer exists
antiBaconMachine Mar 21, 2023
d305bac
remove text alignement for order page, tweak check icon to match face…
ludoboludo Mar 21, 2023
4cecb78
fix translation file issue
ludoboludo Mar 21, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion assets/component-cart-items.css
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@

.product-option {
font-size: 1.4rem;
word-break: break-all;
word-break: break-word;
line-height: calc(1 + 0.5 / var(--font-body-scale));
}

Expand Down
3 changes: 2 additions & 1 deletion assets/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@ const ON_CHANGE_DEBOUNCE_TIMER = 300;
const PUB_SUB_EVENTS = {
cartUpdate: 'cart-update',
quantityUpdate: 'quantity-update',
variantChange: 'variant-change'
variantChange: 'variant-change',
cartError: 'cart-error'
};
4 changes: 4 additions & 0 deletions assets/customer.css
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,10 @@
text-align: right;
}

.customer td .properties {
text-align: left;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks odd to me 🤔 Even though aligned right is a bit odd as well I think it fits better with the rest of the layout:

Screenshot

cc: @danielvan could we get your input on this ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know this one is hard. You can ping @YoannJailin if needed.

I agree right aligned is best, but right now the stacking is weird. Could we force a line break? E.g.
Recipient email:
Ludo.segura
Recipient name:
Lu
Message:
Testing this out

We can't design a table or something custom here for gift cards, right?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed and left it aligned right for now. We can revisit this later 👍


.customer td::before {
color: rgba(var(--color-foreground), 0.75);
content: attr(data-label);
Expand Down
7 changes: 6 additions & 1 deletion assets/product-form.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ if (!customElements.get('product-form')) {
this.cart = document.querySelector('cart-notification') || document.querySelector('cart-drawer');
this.submitButton = this.querySelector('[type="submit"]');
if (document.querySelector('cart-drawer')) this.submitButton.setAttribute('aria-haspopup', 'dialog');

this.hideErrors = this.dataset.hideErrors === 'true';
}

onSubmitHandler(evt) {
Expand Down Expand Up @@ -37,6 +39,7 @@ if (!customElements.get('product-form')) {
.then((response) => response.json())
.then((response) => {
if (response.status) {
publish(PUB_SUB_EVENTS.cartError, {source: 'product-form', productVariantId: formData.get('id'), errors: response.description, message: response.message});
this.handleErrorMessage(response.description);

const soldOutMessage = this.submitButton.querySelector('.sold-out-message');
Expand All @@ -51,7 +54,7 @@ if (!customElements.get('product-form')) {
return;
}

if (!this.error) publish(PUB_SUB_EVENTS.cartUpdate, {source: 'product-form'});
if (!this.error) publish(PUB_SUB_EVENTS.cartUpdate, {source: 'product-form', productVariantId: formData.get('id')});
this.error = false;
const quickAddModal = this.closest('quick-add-modal');
if (quickAddModal) {
Expand All @@ -75,6 +78,8 @@ if (!customElements.get('product-form')) {
}

handleErrorMessage(errorMessage = false) {
if (this.hideErrors) return;

this.errorMessageWrapper = this.errorMessageWrapper || this.querySelector('.product-form__error-message-wrapper');
if (!this.errorMessageWrapper) return;
this.errorMessage = this.errorMessage || this.errorMessageWrapper.querySelector('.product-form__error-message');
Expand Down
139 changes: 139 additions & 0 deletions assets/recipient-form.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
if (!customElements.get('recipient-form')) {
customElements.define('recipient-form', class RecipientForm extends HTMLElement {
constructor() {
super();
this.checkboxInput = this.querySelector(`#Recipient-Checkbox-${ this.dataset.sectionId }`);
this.checkboxInput.disabled = false;
this.hiddenControlField = this.querySelector(`#Recipient-Control-${ this.dataset.sectionId }`);
this.hiddenControlField.disabled = true;
this.emailInput = this.querySelector(`#Recipient-email-${ this.dataset.sectionId }`);
this.nameInput = this.querySelector(`#Recipient-name-${ this.dataset.sectionId }`);
this.messageInput = this.querySelector(`#Recipient-message-${ this.dataset.sectionId }`);
this.errorMessageWrapper = this.querySelector('.product-form__recipient-error-message-wrapper');
this.errorMessageList = this.errorMessageWrapper?.querySelector('ul');
this.errorMessage = this.errorMessageWrapper?.querySelector('.error-message');
this.defaultErrorHeader = this.errorMessage?.innerText;
this.currentProductVariantId = this.dataset.productVariantId;
this.addEventListener('change', this.onChange.bind(this));
}

cartUpdateUnsubscriber = undefined;
variantChangeUnsubscriber = undefined;
cartErrorUnsubscriber = undefined;

connectedCallback() {
this.cartUpdateUnsubscriber = subscribe(PUB_SUB_EVENTS.cartUpdate, (event) => {
if (event.source === 'product-form' && event.productVariantId.toString() === this.currentProductVariantId) {
this.resetRecipientForm();
}
});

this.variantChangeUnsubscriber = subscribe(PUB_SUB_EVENTS.variantChange, (event) => {
if (event.data.sectionId === this.dataset.sectionId) {
this.currentProductVariantId = event.data.variant.id.toString();
}
});

this.cartUpdateUnsubscriber = subscribe(PUB_SUB_EVENTS.cartError, (event) => {
if (event.source === 'product-form' && event.productVariantId.toString() === this.currentProductVariantId) {
this.displayErrorMessage(event.message, event.errors);
}
});
}

disconnectedCallback() {
if (this.cartUpdateUnsubscriber) {
this.cartUpdateUnsubscriber();
}

if (this.variantChangeUnsubscriber) {
this.variantChangeUnsubscriber();
}

if (this.cartErrorUnsubscriber) {
this.cartErrorUnsubscriber();
}
}

onChange() {
if (!this.checkboxInput.checked) {
this.clearInputFields();
this.clearErrorMessage();
}
}
Comment on lines +58 to +63
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the accessibility part maybe in here we could add / remove content in a visually hidden element with a status role 🤔
This way when checked there can be a announcement that a form has appeared on the screen.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See #2672


clearInputFields() {
this.emailInput.value = '';
this.nameInput.value = '';
this.messageInput.value = '';
}

displayErrorMessage(title, body) {
this.clearErrorMessage();
this.errorMessageWrapper.hidden = false;
if (typeof body === 'object') {
this.errorMessage.innerText = this.defaultErrorHeader;
return Object.entries(body).forEach(([key, value]) => {
const errorMessageId = `RecipientForm-${ key }-error-${ this.dataset.sectionId }`
const fieldSelector = `#Recipient-${ key }-${ this.dataset.sectionId }`;
const placeholderElement = this.querySelector(`${fieldSelector}`);
const label = placeholderElement?.getAttribute('placeholder') || key;
const message = `${label} ${value}`;
const errorMessageElement = this.querySelector(`#${errorMessageId}`);
const errorTextElement = errorMessageElement?.querySelector('.error-message')
if (!errorTextElement) return;

if (this.errorMessageList) {
this.errorMessageList.appendChild(this.createErrorListItem(fieldSelector, message));
}

errorTextElement.innerText = `${message}.`;
errorMessageElement.classList.remove('hidden');

const inputElement = this[`${key}Input`];
if (!inputElement) return;

inputElement.setAttribute('aria-invalid', true);
inputElement.setAttribute('aria-describedby', errorMessageId);
});
}

this.errorMessage.innerText = body;
}

createErrorListItem(target, message) {
const li = document.createElement('li');
const a = document.createElement('a');
a.setAttribute('href', target);
a.innerText = message;
li.appendChild(a);
li.className = "error-message";
return li;
}

clearErrorMessage() {
this.errorMessageWrapper.hidden = true;

if (this.errorMessageList) this.errorMessageList.innerHTML = '';

this.querySelectorAll('.recipient-fields .form__message').forEach(field => {
field.classList.add('hidden');
const textField = field.querySelector('.error-message');
if (textField) textField.innerText = '';
});

[this.emailInput, this.messageInput, this.nameInput].forEach(inputElement => {
inputElement.setAttribute('aria-invalid', false);
inputElement.removeAttribute('aria-describedby');
});
}

resetRecipientForm() {
if (this.checkboxInput.checked) {
this.checkboxInput.checked = false;
this.clearInputFields();
this.clearErrorMessage();
}
}
});
}
128 changes: 127 additions & 1 deletion assets/section-main-product.css
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not something to deal with in this PR but a follow up for us to potentially look into is the font size usage. It seem to be a bit all over the place. cc: @danielvan

Screenshot

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, @ludoboludo. Thanks for this example. I'm working on a type standardization brief. For this one, the only lever we have would be to adjust the size of the denomination? But I guess this comes from the variant settings.

Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@
flex: 0 0 100%;
padding: 0;
margin: 0 0 1.2rem 0;
max-width: 37rem;
max-width: 44rem;
antiBaconMachine marked this conversation as resolved.
Show resolved Hide resolved
min-width: fit-content;
border: none;
}
Expand Down Expand Up @@ -1473,3 +1473,129 @@ a.product__text {
display: none;
}
}

/* Recipient form */
.recipient-form {
/* (2.88[line-height] - 1.6rem) / 2 */
--recipient-checkbox-margin-top: 0.64rem;

display: block;
position: relative;
max-width: 44rem;
margin-bottom: 2.5rem;
}

.recipient-form-field-label {
margin: 0.6rem 0;
}

.recipient-form-field-label--space-between {
display: flex;
justify-content: space-between;
}

.recipient-checkbox {
flex-grow: 1;
font-size: 1.6rem;
display: flex;
word-break: break-word;
align-items: flex-start;
max-width: inherit;
}

.no-js .recipient-checkbox {
display: none;
}

.recipient-form > input[type='checkbox'] {
position: absolute;
width: 1.6rem;
height: 1.6rem;
margin: var(--recipient-checkbox-margin-top) 0;
top: 0;
left: 0;
z-index: -1;
appearance: none;
-webkit-appearance: none;
}
Comment on lines +1511 to +1521
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be nice if any generic checkbox styles were added in a more global way. Though I recognize the facets checkbox styles were also unfortunately scoped specifically to that purpose, so I wouldn't say it's a hard requirement to change that here. Would probably increase testing scope if we moved any facet styles around too.


.recipient-fields__field {
margin: 0 0 2rem 0;
}

.recipient-fields .field__label {
white-space: nowrap;
text-overflow: ellipsis;
max-width: calc(100% - 3.5rem);
overflow: hidden;
}

.recipient-checkbox > svg {
margin-top: var(--recipient-checkbox-margin-top);
margin-right: 1.2rem;
flex-shrink: 0;
}

.recipient-form .icon-checkmark {
visibility: hidden;
position: absolute;
left: 0.35rem;
z-index: 5;
top: 0.45rem;
height: 0.7rem;
}

.recipient-form > input[type='checkbox']:checked + label .icon-checkmark {
visibility: visible;
}

.js .recipient-fields {
display: none;
}

.recipient-fields hr {
margin: 1.6rem auto;
}

.recipient-form > input[type='checkbox']:checked ~ .recipient-fields {
display: block;
animation: animateMenuOpen var(--duration-default) ease;
}
Comment on lines +1560 to +1563
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if we should provide more feedback to screenreader users that additional content gets revealed when checking the input 🤔 Maybe if we at least prevented the item from being successfully added to cart without a valid email this would be ok. But currently, it's possible you could check the checkbox and press enter to submit the form and add the item to the cart without noticing the additional fields. https://screenshot.click/17-02-h5lay-yhktj.mp4

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same feeling on my side. I brought the possibility to add something like toggling an attribute aria-expanded="true".
video

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah unfortunately I'm pretty sure aria-expanded isn't applicable to inputs like this. At least it's not meant to be. So I was picturing it having to be more of a live region type solution.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe something on which we could get @metamoni's take 👍

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I did some quick experiments with aria-expanded and concluded that it was the wrong tool for the job.

.recipient-form > input[type='checkbox']:not(:checked, :disabled) ~ .recipient-fields ,
.recipient-email-label {
display: none;
}

.js .recipient-email-label.required,
.no-js .recipient-email-label.optional {
display: inline;
}

.recipient-form ul {
line-height: calc(1 + 0.6 / var(--font-body-scale));
padding-left: 4.4rem;
text-align: left;
}

.recipient-form ul a {
display: inline;
}

.recipient-form .error-message::first-letter {
text-transform: capitalize;
}

@media screen and (forced-colors: active) {
.recipient-fields > hr {
border-top: 0.1rem solid rgb(var(--color-background));
}

.recipient-checkbox > svg {
background-color: inherit;
border: 0.1rem solid rgb(var(--color-background));
}

.recipient-form > input[type='checkbox']:checked + label .icon-checkmark {
border: none;
}
}