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 @@ ...@@ -56,6 +56,32 @@
#nicknameInput:not(:focus-within) #charCount { #nicknameInput:not(:focus-within) #charCount {
display: none; 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> </style>
<cr-dialog id="dialog" close-text="$i18n{close}"> <cr-dialog id="dialog" close-text="$i18n{close}">
<div slot="title">[[title_]]</div> <div slot="title">[[title_]]</div>
...@@ -72,7 +98,7 @@ ...@@ -72,7 +98,7 @@
<!-- aria-hidden for creditCardExpiration label since <!-- aria-hidden for creditCardExpiration label since
creditCardExpirationMonth and creditCardExpirationYear provide creditCardExpirationMonth and creditCardExpirationYear provide
sufficient labeling. --> sufficient labeling. -->
<label class="cr-form-field-label" aria-hidden="true"> <label id='expiration' class="cr-form-field-label" aria-hidden="true">
$i18n{creditCardExpiration} $i18n{creditCardExpiration}
</label> </label>
<select class="md-select" id="month" value="[[expirationMonth_]]" <select class="md-select" id="month" value="[[expirationMonth_]]"
...@@ -89,9 +115,16 @@ ...@@ -89,9 +115,16 @@
<option>[[item]]</option> <option>[[item]]</option>
</template> </template>
</select> </select>
<span id="expired" <!-- Use new error message text under the drop down when nickname
hidden="[[!checkIfCardExpired_(expirationMonth_, expirationYear_)]]" 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} $i18n{creditCardExpired}
</span> </span>
<!-- Place cardholder name field and nickname field after expiration <!-- Place cardholder name field and nickname field after expiration
...@@ -121,7 +154,7 @@ ...@@ -121,7 +154,7 @@
<cr-button id="saveButton" class="action-button" <cr-button id="saveButton" class="action-button"
on-click="onSaveButtonTap_" on-click="onSaveButtonTap_"
disabled="[[!saveEnabled_(nicknameInvalid_, creditCard.*, disabled="[[!saveEnabled_(nicknameInvalid_, creditCard.*,
expirationMonth_, expirationYear_)]]"> expired_)]]">
$i18n{save} $i18n{save}
</cr-button> </cr-button>
</div> </div>
......
...@@ -76,6 +76,7 @@ Polymer({ ...@@ -76,6 +76,7 @@ Polymer({
*/ */
nicknameManagementEnabled_: { nicknameManagementEnabled_: {
type: Boolean, type: Boolean,
reflectToAttribute: true,
value() { value() {
return loadTimeData.getBoolean('nicknameManagementEnabled'); return loadTimeData.getBoolean('nicknameManagementEnabled');
} }
...@@ -89,6 +90,13 @@ Polymer({ ...@@ -89,6 +90,13 @@ Polymer({
type: Boolean, type: Boolean,
value: false, value: false,
}, },
/** @private */
expired_: {
type: Boolean,
computed: 'computeExpired_(expirationMonth_, expirationYear_)',
reflectToAttribute: true,
},
}, },
behaviors: [ behaviors: [
...@@ -99,12 +107,19 @@ Polymer({ ...@@ -99,12 +107,19 @@ Polymer({
* @return {boolean} True iff the provided expiration date is passed. * @return {boolean} True iff the provided expiration date is passed.
* @private * @private
*/ */
checkIfCardExpired_(expirationMonth_, expirationYear_) { computeExpired_() {
if (this.expirationYear_ === undefined ||
this.expirationMonth_ === undefined) {
return false;
}
const now = new Date(); 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 ( return (
expirationYear_ < now.getFullYear() || expirationYear < now.getFullYear() ||
(expirationYear_ == now.getFullYear() && (expirationYear === now.getFullYear() &&
expirationMonth_ <= now.getMonth())); expirationMonth <= now.getMonth()));
}, },
/** @override */ /** @override */
...@@ -191,9 +206,18 @@ Polymer({ ...@@ -191,9 +206,18 @@ Polymer({
return ((this.creditCard.name && this.creditCard.name.trim()) || return ((this.creditCard.name && this.creditCard.name.trim()) ||
(this.creditCard.cardNumber && (this.creditCard.cardNumber &&
this.creditCard.cardNumber.trim())) && this.creditCard.cardNumber.trim())) &&
!this.checkIfCardExpired_( !this.expired_ && !this.nicknameInvalid_;
this.expirationMonth_, this.expirationYear_) && },
!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({ ...@@ -214,4 +238,13 @@ Polymer({
computeNicknameCharCount_(nickname) { computeNicknameCharCount_(nickname) {
return (nickname || '').length; 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() { ...@@ -90,6 +90,21 @@ suite('PaymentsSectionCreditCardEditDialogTest', function() {
return creditCardDialog; 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() { test('add card dialog when nickname management disabled', async function() {
loadTimeData.overrideValues({nicknameManagementEnabled: false}); loadTimeData.overrideValues({nicknameManagementEnabled: false});
const creditCardDialog = createAddCreditCardDialog(); const creditCardDialog = createAddCreditCardDialog();
...@@ -139,6 +154,9 @@ suite('PaymentsSectionCreditCardEditDialogTest', function() { ...@@ -139,6 +154,9 @@ suite('PaymentsSectionCreditCardEditDialogTest', function() {
// Fill in name to the legacy name input field and card number. // Fill in name to the legacy name input field and card number.
creditCardDialog.$$('#legacyNameInput').value = 'Jane Doe'; creditCardDialog.$$('#legacyNameInput').value = 'Jane Doe';
creditCardDialog.$$('#numberInput').value = '4111111111111111'; creditCardDialog.$$('#numberInput').value = '4111111111111111';
// Select next year as the expiration year.
creditCardDialog.$.year.value = nextYear();
creditCardDialog.$.year.dispatchEvent(new CustomEvent('change'));
flush(); flush();
assertTrue(creditCardDialog.$.expired.hidden); assertTrue(creditCardDialog.$.expired.hidden);
...@@ -153,6 +171,7 @@ suite('PaymentsSectionCreditCardEditDialogTest', function() { ...@@ -153,6 +171,7 @@ suite('PaymentsSectionCreditCardEditDialogTest', function() {
assertEquals(saveEvent.detail.guid, undefined); assertEquals(saveEvent.detail.guid, undefined);
assertEquals(saveEvent.detail.name, 'Jane Doe'); assertEquals(saveEvent.detail.name, 'Jane Doe');
assertEquals(saveEvent.detail.cardNumber, '4111111111111111'); assertEquals(saveEvent.detail.cardNumber, '4111111111111111');
assertEquals(saveEvent.detail.expirationYear, nextYear());
}); });
test('save new card when nickname management is enabled', async function() { test('save new card when nickname management is enabled', async function() {
...@@ -162,11 +181,13 @@ suite('PaymentsSectionCreditCardEditDialogTest', function() { ...@@ -162,11 +181,13 @@ suite('PaymentsSectionCreditCardEditDialogTest', function() {
// Wait for the dialog to open. // Wait for the dialog to open.
await whenAttributeIs(creditCardDialog.$$('#dialog'), 'open', ''); await whenAttributeIs(creditCardDialog.$$('#dialog'), 'open', '');
// Fill in name, card number and card nickname, and trigger the on-input // Fill in name, card number, expiration year and card nickname, and trigger
// handler. // the on-input handler.
creditCardDialog.$$('#nameInput').value = 'Jane Doe'; creditCardDialog.$$('#nameInput').value = 'Jane Doe';
creditCardDialog.$$('#numberInput').value = '4111111111111111'; creditCardDialog.$$('#numberInput').value = '4111111111111111';
typeInNickname(creditCardDialog.$$('#nicknameInput'), 'Grocery Card'); typeInNickname(creditCardDialog.$$('#nicknameInput'), 'Grocery Card');
creditCardDialog.$.year.value = nextYear();
creditCardDialog.$.year.dispatchEvent(new CustomEvent('change'));
flush(); flush();
assertTrue(creditCardDialog.$.expired.hidden); assertTrue(creditCardDialog.$.expired.hidden);
...@@ -182,6 +203,7 @@ suite('PaymentsSectionCreditCardEditDialogTest', function() { ...@@ -182,6 +203,7 @@ suite('PaymentsSectionCreditCardEditDialogTest', function() {
assertEquals(saveEvent.detail.name, 'Jane Doe'); assertEquals(saveEvent.detail.name, 'Jane Doe');
assertEquals(saveEvent.detail.cardNumber, '4111111111111111'); assertEquals(saveEvent.detail.cardNumber, '4111111111111111');
assertEquals(saveEvent.detail.nickname, 'Grocery Card'); assertEquals(saveEvent.detail.nickname, 'Grocery Card');
assertEquals(saveEvent.detail.expirationYear, nextYear());
}); });
test('update local card value', async function() { test('update local card value', async function() {
...@@ -189,9 +211,8 @@ suite('PaymentsSectionCreditCardEditDialogTest', function() { ...@@ -189,9 +211,8 @@ suite('PaymentsSectionCreditCardEditDialogTest', function() {
const creditCard = createCreditCardEntry(); const creditCard = createCreditCardEntry();
creditCard.name = 'Wrong name'; creditCard.name = 'Wrong name';
creditCard.nickname = 'Shopping Card'; creditCard.nickname = 'Shopping Card';
const now = new Date();
// Set the expiration year to next year to avoid expired card. // Set the expiration year to next year to avoid expired card.
creditCard.expirationYear = now.getFullYear() + 1; creditCard.expirationYear = nextYear();
creditCard.cardNumber = '4444333322221111'; creditCard.cardNumber = '4444333322221111';
const creditCardDialog = createEditCreditCardDialog([creditCard]); const creditCardDialog = createEditCreditCardDialog([creditCard]);
...@@ -202,15 +223,18 @@ suite('PaymentsSectionCreditCardEditDialogTest', function() { ...@@ -202,15 +223,18 @@ suite('PaymentsSectionCreditCardEditDialogTest', function() {
assertEquals(creditCardDialog.$$('#nameInput').value, 'Wrong name'); assertEquals(creditCardDialog.$$('#nameInput').value, 'Wrong name');
assertEquals(creditCardDialog.$$('#nicknameInput').value, 'Shopping Card'); assertEquals(creditCardDialog.$$('#nicknameInput').value, 'Shopping Card');
assertEquals(creditCardDialog.$$('#numberInput').value, '4444333322221111'); assertEquals(creditCardDialog.$$('#numberInput').value, '4444333322221111');
assertEquals(creditCardDialog.$.year.value, nextYear());
assertTrue(creditCardDialog.$.expired.hidden); assertTrue(creditCardDialog.$.expired.hidden);
assertFalse(creditCardDialog.$.saveButton.disabled); assertFalse(creditCardDialog.$.saveButton.disabled);
// Update cardholder name, card number and nickname, and trigger the // Update cardholder name, card number, expiration year and nickname, and
// on-input handler. // trigger the on-input handler.
creditCardDialog.$$('#nameInput').value = 'Jane Doe'; creditCardDialog.$$('#nameInput').value = 'Jane Doe';
creditCardDialog.$$('#numberInput').value = '4111111111111111'; creditCardDialog.$$('#numberInput').value = '4111111111111111';
typeInNickname(creditCardDialog.$$('#nicknameInput'), 'Grocery Card'); typeInNickname(creditCardDialog.$$('#nicknameInput'), 'Grocery Card');
creditCardDialog.$.year.value = farFutureYear();
creditCardDialog.$.year.dispatchEvent(new CustomEvent('change'));
flush(); flush();
const savedPromise = eventToPromise('save-credit-card', creditCardDialog); const savedPromise = eventToPromise('save-credit-card', creditCardDialog);
...@@ -222,6 +246,7 @@ suite('PaymentsSectionCreditCardEditDialogTest', function() { ...@@ -222,6 +246,7 @@ suite('PaymentsSectionCreditCardEditDialogTest', function() {
assertEquals(saveEvent.detail.name, 'Jane Doe'); assertEquals(saveEvent.detail.name, 'Jane Doe');
assertEquals(saveEvent.detail.cardNumber, '4111111111111111'); assertEquals(saveEvent.detail.cardNumber, '4111111111111111');
assertEquals(saveEvent.detail.nickname, 'Grocery Card'); assertEquals(saveEvent.detail.nickname, 'Grocery Card');
assertEquals(saveEvent.detail.expirationYear, farFutureYear());
}); });
test('show error message when input nickname is invalid', async function() { test('show error message when input nickname is invalid', async function() {
...@@ -272,9 +297,8 @@ suite('PaymentsSectionCreditCardEditDialogTest', function() { ...@@ -272,9 +297,8 @@ suite('PaymentsSectionCreditCardEditDialogTest', function() {
loadTimeData.overrideValues({nicknameManagementEnabled: true}); loadTimeData.overrideValues({nicknameManagementEnabled: true});
const creditCard = createCreditCardEntry(); const creditCard = createCreditCardEntry();
creditCard.name = 'Wrong name'; creditCard.name = 'Wrong name';
const now = new Date();
// Set the expiration year to next year to avoid expired card. // Set the expiration year to next year to avoid expired card.
creditCard.expirationYear = now.getFullYear() + 1; creditCard.expirationYear = nextYear();
creditCard.cardNumber = '4444333322221111'; creditCard.cardNumber = '4444333322221111';
// Edit dialog for an existing card with no nickname. // Edit dialog for an existing card with no nickname.
const creditCardDialog = createEditCreditCardDialog([creditCard]); const creditCardDialog = createEditCreditCardDialog([creditCard]);
...@@ -333,4 +357,65 @@ suite('PaymentsSectionCreditCardEditDialogTest', function() { ...@@ -333,4 +357,65 @@ suite('PaymentsSectionCreditCardEditDialogTest', function() {
assertTrue(isVisible(characterCount)); assertTrue(isVisible(characterCount));
assertTrue(characterCount.textContent.includes('5/25')); 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() { ...@@ -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() { test('verify save new credit card', function() {
const creditCard = createEmptyCreditCardEntry(); const creditCard = createEmptyCreditCardEntry();
const creditCardDialog = createCreditCardDialog(creditCard); 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