Commit 2aee8f6c authored by Sujie Zhu's avatar Sujie Zhu Committed by Commit Bot

[Nickname Management][Settings page] redesign expired error message.

When nickname management is disabled, hide the new error message.

When nickname management is enabled, we will hide the legacy error message,
and use the new expired error message format.
When expired, display error message below the dropdown.
Also change the expiration date label's color when expired.
Note that for valid expiration, we don't hide the message, we set
visibility of the error message to hidden in order to reserve the space.
(Same behavior as the cr-input).

Bug: 1082013
Change-Id: Id61262ea315bce62ffa5aa4b3bffa26aff3f2632
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2223151
Commit-Queue: Sujie Zhu <sujiezhu@google.com>
Reviewed-by: default avatardpapad <dpapad@chromium.org>
Reviewed-by: default avatarJared Saul <jsaul@google.com>
Cr-Commit-Position: refs/heads/master@{#774446}
parent 11b209ca
......@@ -56,6 +56,32 @@
#nicknameInput:not(:focus-within) #charCount {
display: none;
}
/* Same style as cr-input error.*/
#expired-error {
display: block;
font-size: var(--cr-form-field-label-font-size);
height: var(--cr-form-field-label-height);
line-height: var(--cr-form-field-label-line-height);
margin: 8px 0;
visibility: hidden;
}
:host([expired_]) #expired-error {
visibility: visible;
}
#expired-error,
:host([nickname-management-enabled_][expired_]) #expiration {
color: var(--google-red-600);
}
@media (prefers-color-scheme: dark) {
#expired-error,
:host([nickname-management-enabled_][expired_]) #expiration {
color: var(--google-red-refresh-300);
}
}
</style>
<cr-dialog id="dialog" close-text="$i18n{close}">
<div slot="title">[[title_]]</div>
......@@ -72,7 +98,7 @@
<!-- aria-hidden for creditCardExpiration label since
creditCardExpirationMonth and creditCardExpirationYear provide
sufficient labeling. -->
<label class="cr-form-field-label" aria-hidden="true">
<label id='expiration' class="cr-form-field-label" aria-hidden="true">
$i18n{creditCardExpiration}
</label>
<select class="md-select" id="month" value="[[expirationMonth_]]"
......@@ -89,9 +115,16 @@
<option>[[item]]</option>
</template>
</select>
<span id="expired"
hidden="[[!checkIfCardExpired_(expirationMonth_, expirationYear_)]]"
>
<!-- Use new error message text under the drop down when nickname
management is enabled.-->
<div id="expired-error" aria-hidden$="[[getAriaHidden_(expired_)]]"
hidden="[[!nicknameManagementEnabled_]]">
$i18n{creditCardExpired}
</div>
<!-- Reuse current error message span when nickname management is
disabled.-->
<span id="expired" hidden="[[!showLegacyExpiredError_(expired_,
nicknameManagementEnabled_)]]">
$i18n{creditCardExpired}
</span>
<!-- Place cardholder name field and nickname field after expiration
......@@ -121,7 +154,7 @@
<cr-button id="saveButton" class="action-button"
on-click="onSaveButtonTap_"
disabled="[[!saveEnabled_(nicknameInvalid_, creditCard.*,
expirationMonth_, expirationYear_)]]">
expired_)]]">
$i18n{save}
</cr-button>
</div>
......
......@@ -76,6 +76,7 @@ Polymer({
*/
nicknameManagementEnabled_: {
type: Boolean,
reflectToAttribute: true,
value() {
return loadTimeData.getBoolean('nicknameManagementEnabled');
}
......@@ -89,6 +90,13 @@ Polymer({
type: Boolean,
value: false,
},
/** @private */
expired_: {
type: Boolean,
computed: 'computeExpired_(expirationMonth_, expirationYear_)',
reflectToAttribute: true,
},
},
behaviors: [
......@@ -99,12 +107,19 @@ Polymer({
* @return {boolean} True iff the provided expiration date is passed.
* @private
*/
checkIfCardExpired_(expirationMonth_, expirationYear_) {
computeExpired_() {
if (this.expirationYear_ === undefined ||
this.expirationMonth_ === undefined) {
return false;
}
const now = new Date();
// Convert string (e.g. '06') to number (e.g. 6) for comparison.
const expirationYear = parseInt(this.expirationYear_, 10);
const expirationMonth = parseInt(this.expirationMonth_, 10);
return (
expirationYear_ < now.getFullYear() ||
(expirationYear_ == now.getFullYear() &&
expirationMonth_ <= now.getMonth()));
expirationYear < now.getFullYear() ||
(expirationYear === now.getFullYear() &&
expirationMonth <= now.getMonth()));
},
/** @override */
......@@ -191,9 +206,18 @@ Polymer({
return ((this.creditCard.name && this.creditCard.name.trim()) ||
(this.creditCard.cardNumber &&
this.creditCard.cardNumber.trim())) &&
!this.checkIfCardExpired_(
this.expirationMonth_, this.expirationYear_) &&
!this.nicknameInvalid_;
!this.expired_ && !this.nicknameInvalid_;
},
/**
* @return {boolean} True iff the card is expired and nickname management is
* disabled.
* @private
*/
// TODO(crbug.com/1082013): Remove legacy expired error message when nickname
// management is fully enabled.
showLegacyExpiredError_() {
return !this.nicknameManagementEnabled_ && this.expired_;
},
/**
......@@ -214,4 +238,13 @@ Polymer({
computeNicknameCharCount_(nickname) {
return (nickname || '').length;
},
/**
* @return {string} 'true' or 'false', indicating whether the expired error
* message should be aria-hidden.
* @private
*/
getAriaHidden_() {
return this.expired_ ? 'false' : 'true';
},
});
......@@ -90,6 +90,21 @@ suite('PaymentsSectionCreditCardEditDialogTest', function() {
return creditCardDialog;
}
/** @return {string} */
function nextYear() {
return (new Date().getFullYear() + 1).toString();
}
/** @return {string} */
function farFutureYear() {
return (new Date().getFullYear() + 15).toString();
}
/** @return {string} */
function lastYear() {
return (new Date().getFullYear() - 1).toString();
}
test('add card dialog when nickname management disabled', async function() {
loadTimeData.overrideValues({nicknameManagementEnabled: false});
const creditCardDialog = createAddCreditCardDialog();
......@@ -139,6 +154,9 @@ suite('PaymentsSectionCreditCardEditDialogTest', function() {
// Fill in name to the legacy name input field and card number.
creditCardDialog.$$('#legacyNameInput').value = 'Jane Doe';
creditCardDialog.$$('#numberInput').value = '4111111111111111';
// Select next year as the expiration year.
creditCardDialog.$.year.value = nextYear();
creditCardDialog.$.year.dispatchEvent(new CustomEvent('change'));
flush();
assertTrue(creditCardDialog.$.expired.hidden);
......@@ -153,6 +171,7 @@ suite('PaymentsSectionCreditCardEditDialogTest', function() {
assertEquals(saveEvent.detail.guid, undefined);
assertEquals(saveEvent.detail.name, 'Jane Doe');
assertEquals(saveEvent.detail.cardNumber, '4111111111111111');
assertEquals(saveEvent.detail.expirationYear, nextYear());
});
test('save new card when nickname management is enabled', async function() {
......@@ -162,11 +181,13 @@ suite('PaymentsSectionCreditCardEditDialogTest', function() {
// Wait for the dialog to open.
await whenAttributeIs(creditCardDialog.$$('#dialog'), 'open', '');
// Fill in name, card number and card nickname, and trigger the on-input
// handler.
// Fill in name, card number, expiration year and card nickname, and trigger
// the on-input handler.
creditCardDialog.$$('#nameInput').value = 'Jane Doe';
creditCardDialog.$$('#numberInput').value = '4111111111111111';
typeInNickname(creditCardDialog.$$('#nicknameInput'), 'Grocery Card');
creditCardDialog.$.year.value = nextYear();
creditCardDialog.$.year.dispatchEvent(new CustomEvent('change'));
flush();
assertTrue(creditCardDialog.$.expired.hidden);
......@@ -182,6 +203,7 @@ suite('PaymentsSectionCreditCardEditDialogTest', function() {
assertEquals(saveEvent.detail.name, 'Jane Doe');
assertEquals(saveEvent.detail.cardNumber, '4111111111111111');
assertEquals(saveEvent.detail.nickname, 'Grocery Card');
assertEquals(saveEvent.detail.expirationYear, nextYear());
});
test('update local card value', async function() {
......@@ -189,9 +211,8 @@ suite('PaymentsSectionCreditCardEditDialogTest', function() {
const creditCard = createCreditCardEntry();
creditCard.name = 'Wrong name';
creditCard.nickname = 'Shopping Card';
const now = new Date();
// Set the expiration year to next year to avoid expired card.
creditCard.expirationYear = now.getFullYear() + 1;
creditCard.expirationYear = nextYear();
creditCard.cardNumber = '4444333322221111';
const creditCardDialog = createEditCreditCardDialog([creditCard]);
......@@ -202,15 +223,18 @@ suite('PaymentsSectionCreditCardEditDialogTest', function() {
assertEquals(creditCardDialog.$$('#nameInput').value, 'Wrong name');
assertEquals(creditCardDialog.$$('#nicknameInput').value, 'Shopping Card');
assertEquals(creditCardDialog.$$('#numberInput').value, '4444333322221111');
assertEquals(creditCardDialog.$.year.value, nextYear());
assertTrue(creditCardDialog.$.expired.hidden);
assertFalse(creditCardDialog.$.saveButton.disabled);
// Update cardholder name, card number and nickname, and trigger the
// on-input handler.
// Update cardholder name, card number, expiration year and nickname, and
// trigger the on-input handler.
creditCardDialog.$$('#nameInput').value = 'Jane Doe';
creditCardDialog.$$('#numberInput').value = '4111111111111111';
typeInNickname(creditCardDialog.$$('#nicknameInput'), 'Grocery Card');
creditCardDialog.$.year.value = farFutureYear();
creditCardDialog.$.year.dispatchEvent(new CustomEvent('change'));
flush();
const savedPromise = eventToPromise('save-credit-card', creditCardDialog);
......@@ -222,6 +246,7 @@ suite('PaymentsSectionCreditCardEditDialogTest', function() {
assertEquals(saveEvent.detail.name, 'Jane Doe');
assertEquals(saveEvent.detail.cardNumber, '4111111111111111');
assertEquals(saveEvent.detail.nickname, 'Grocery Card');
assertEquals(saveEvent.detail.expirationYear, farFutureYear());
});
test('show error message when input nickname is invalid', async function() {
......@@ -272,9 +297,8 @@ suite('PaymentsSectionCreditCardEditDialogTest', function() {
loadTimeData.overrideValues({nicknameManagementEnabled: true});
const creditCard = createCreditCardEntry();
creditCard.name = 'Wrong name';
const now = new Date();
// Set the expiration year to next year to avoid expired card.
creditCard.expirationYear = now.getFullYear() + 1;
creditCard.expirationYear = nextYear();
creditCard.cardNumber = '4444333322221111';
// Edit dialog for an existing card with no nickname.
const creditCardDialog = createEditCreditCardDialog([creditCard]);
......@@ -333,4 +357,65 @@ suite('PaymentsSectionCreditCardEditDialogTest', function() {
assertTrue(isVisible(characterCount));
assertTrue(characterCount.textContent.includes('5/25'));
});
test('expired card when nickname management is disabled', async function() {
loadTimeData.overrideValues({nicknameManagementEnabled: false});
const creditCard = createCreditCardEntry();
// Set the expiration year to the previous year to simulate expired card.
creditCard.expirationYear = lastYear();
// Edit dialog for an existing card with no nickname.
const creditCardDialog = createEditCreditCardDialog([creditCard]);
// Wait for the dialog to open.
await whenAttributeIs(creditCardDialog.$$('#dialog'), 'open', '');
// Verify save button is disabled for expired credit card.
assertTrue(creditCardDialog.$.saveButton.disabled);
// Legacy expired error message is shown, the new error message is still
// hidden.
assertFalse(creditCardDialog.$.expired.hidden);
assertTrue(creditCardDialog.$$('#expired-error').hidden);
// Update the expiration year to next year to avoid expired card.
creditCardDialog.$.year.value = nextYear();
creditCardDialog.$.year.dispatchEvent(new CustomEvent('change'));
flush();
// Expired error message is hidden for valid expiration date.
assertTrue(creditCardDialog.$.expired.hidden);
assertTrue(creditCardDialog.$$('#expired-error').hidden);
assertFalse(creditCardDialog.$.saveButton.disabled);
});
test('expired card when nickname management is enabled', async function() {
loadTimeData.overrideValues({nicknameManagementEnabled: true});
const creditCard = createCreditCardEntry();
// Set the expiration year to the previous year to simulate expired card.
creditCard.expirationYear = lastYear();
// Edit dialog for an existing card with no nickname.
const creditCardDialog = createEditCreditCardDialog([creditCard]);
// Wait for the dialog to open.
await whenAttributeIs(creditCardDialog.$$('#dialog'), 'open', '');
// Verify save button is disabled for expired credit card.
assertTrue(creditCardDialog.$.saveButton.disabled);
const expiredError = creditCardDialog.$$('#expired-error');
// The new expired error message is shown, the legacy error message is still
// hidden.
assertEquals('visible', getComputedStyle(expiredError).visibility);
assertEquals('false', expiredError.getAttribute('aria-hidden'));
assertTrue(creditCardDialog.$.expired.hidden);
// Update the expiration year to next year to avoid expired card.
creditCardDialog.$.year.value = nextYear();
creditCardDialog.$.year.dispatchEvent(new CustomEvent('change'));
flush();
// Expired error message is hidden for valid expiration date.
assertEquals('hidden', getComputedStyle(expiredError).visibility);
assertEquals('true', expiredError.getAttribute('aria-hidden'));
assertTrue(creditCardDialog.$.expired.hidden);
assertFalse(creditCardDialog.$.saveButton.disabled);
});
});
......@@ -301,22 +301,6 @@ suite('PaymentsSection', function() {
});
});
test('verify save disabled for expired credit card', function() {
const creditCard = createEmptyCreditCardEntry();
const now = new Date();
creditCard.expirationYear = now.getFullYear() - 2;
// works fine for January.
creditCard.expirationMonth = now.getMonth() - 1;
const creditCardDialog = createCreditCardDialog(creditCard);
return whenAttributeIs(creditCardDialog.$.dialog, 'open', '')
.then(function() {
assertTrue(creditCardDialog.$.saveButton.disabled);
});
});
test('verify save new credit card', function() {
const creditCard = createEmptyCreditCardEntry();
const creditCardDialog = createCreditCardDialog(creditCard);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment