Commit eb8892ff authored by mathp's avatar mathp Committed by Commit bot

[Payments] Display the shipping option error alongside the address

In the Payment Sheet and the Address selector, display the shipping
option error.

BUG=710004
TEST=browser_tests

Review-Url: https://codereview.chromium.org/2855203002
Cr-Commit-Position: refs/heads/master@{#469397}
parent 63c287aa
...@@ -178,11 +178,9 @@ public class PaymentRequestDynamicShippingMultipleAddressesTest extends PaymentR ...@@ -178,11 +178,9 @@ public class PaymentRequestDynamicShippingMultipleAddressesTest extends PaymentR
AUTOFILL_PROFILES[3].getFullName())); AUTOFILL_PROFILES[3].getFullName()));
clickOnShippingAddressSuggestionOptionAndWait(0, mSelectionChecked); clickOnShippingAddressSuggestionOptionAndWait(0, mSelectionChecked);
// The string should indicate that the shipping address isn't valid. // The string should reflect the error sent from the merchant.
CharSequence actualString = getShippingAddressOptionRowAtIndex(0).getLabelText(); CharSequence actualString = getShippingAddressOptionRowAtIndex(0).getLabelText();
CharSequence expectedString = getInstrumentation().getTargetContext().getString( assertEquals("We do not ship to this address", actualString);
R.string.payments_unsupported_shipping_address);
assertEquals(expectedString, actualString);
} }
/** /**
......
...@@ -110,7 +110,12 @@ class PaymentMethodListItem : public payments::PaymentRequestItemList::Item { ...@@ -110,7 +110,12 @@ class PaymentMethodListItem : public payments::PaymentRequestItemList::Item {
} }
} }
bool CanBeSelected() const override { bool IsEnabled() override {
// All items are enabled.
return true;
}
bool CanBeSelected() override {
// If an instrument can't be selected, PerformSelectionFallback is called, // If an instrument can't be selected, PerformSelectionFallback is called,
// where the instrument can be made complete. // where the instrument can be made complete.
return instrument_->IsCompleteForPayment(); return instrument_->IsCompleteForPayment();
......
...@@ -588,6 +588,13 @@ void PaymentRequestBrowserTestBase::WaitForAnimation() { ...@@ -588,6 +588,13 @@ void PaymentRequestBrowserTestBase::WaitForAnimation() {
} }
} }
const base::string16& PaymentRequestBrowserTestBase::GetLabelText(
DialogViewID view_id) {
views::View* view = dialog_view()->GetViewByID(static_cast<int>(view_id));
DCHECK(view);
return static_cast<views::Label*>(view)->text();
}
const base::string16& PaymentRequestBrowserTestBase::GetStyledLabelText( const base::string16& PaymentRequestBrowserTestBase::GetStyledLabelText(
DialogViewID view_id) { DialogViewID view_id) {
views::View* view = dialog_view()->GetViewByID(static_cast<int>(view_id)); views::View* view = dialog_view()->GetViewByID(static_cast<int>(view_id));
......
...@@ -181,8 +181,9 @@ class PaymentRequestBrowserTestBase ...@@ -181,8 +181,9 @@ class PaymentRequestBrowserTestBase
// Sets proper animation delegates and waits for animation to finish. // Sets proper animation delegates and waits for animation to finish.
void WaitForAnimation(); void WaitForAnimation();
// Returns the text of the StyledLabel with the specific |view_id| that is a // Returns the text of the Label or StyledLabel with the specific |view_id|
// child of the Payment Request dialog view. // that is a child of the Payment Request dialog view.
const base::string16& GetLabelText(DialogViewID view_id);
const base::string16& GetStyledLabelText(DialogViewID view_id); const base::string16& GetStyledLabelText(DialogViewID view_id);
// Returns the error label text associated with a given field |type|. // Returns the error label text associated with a given field |type|.
const base::string16& GetErrorLabelForType(autofill::ServerFieldType type); const base::string16& GetErrorLabelForType(autofill::ServerFieldType type);
......
...@@ -37,13 +37,15 @@ enum class DialogViewID : int { ...@@ -37,13 +37,15 @@ enum class DialogViewID : int {
PAYMENT_SHEET_SHIPPING_ADDRESS_SECTION_BUTTON, PAYMENT_SHEET_SHIPPING_ADDRESS_SECTION_BUTTON,
PAYMENT_SHEET_SHIPPING_OPTION_SECTION_BUTTON, PAYMENT_SHEET_SHIPPING_OPTION_SECTION_BUTTON,
// The following are Label objects. // The following are StyledLabel objects.
ORDER_SUMMARY_TOTAL_AMOUNT_LABEL, ORDER_SUMMARY_TOTAL_AMOUNT_LABEL,
ORDER_SUMMARY_LINE_ITEM_1, ORDER_SUMMARY_LINE_ITEM_1,
ORDER_SUMMARY_LINE_ITEM_2, ORDER_SUMMARY_LINE_ITEM_2,
ORDER_SUMMARY_LINE_ITEM_3, ORDER_SUMMARY_LINE_ITEM_3,
// The following are Label objects.
SHIPPING_OPTION_DESCRIPTION, SHIPPING_OPTION_DESCRIPTION,
SHIPPING_OPTION_AMOUNT, SHIPPING_OPTION_AMOUNT,
SHIPPING_ADDRESS_OPTION_ERROR,
// Used in profile labels to annotate each line of the grouping. // Used in profile labels to annotate each line of the grouping.
PROFILE_LABEL_LINE_1, PROFILE_LABEL_LINE_1,
......
...@@ -73,7 +73,8 @@ std::unique_ptr<views::View> PaymentRequestItemList::Item::CreateItemView() { ...@@ -73,7 +73,8 @@ std::unique_ptr<views::View> PaymentRequestItemList::Item::CreateItemView() {
std::unique_ptr<views::View> content = CreateContentView(); std::unique_ptr<views::View> content = CreateContentView();
std::unique_ptr<PaymentRequestRowView> row = std::unique_ptr<PaymentRequestRowView> row =
base::MakeUnique<PaymentRequestRowView>(this, /* clickable= */ true); base::MakeUnique<PaymentRequestRowView>(this,
/* clickable= */ IsEnabled());
views::GridLayout* layout = new views::GridLayout(row.get()); views::GridLayout* layout = new views::GridLayout(row.get());
row->SetLayoutManager(layout); row->SetLayoutManager(layout);
......
...@@ -76,10 +76,14 @@ class PaymentRequestItemList { ...@@ -76,10 +76,14 @@ class PaymentRequestItemList {
// item's view, such as the credit card icon. // item's view, such as the credit card icon.
virtual std::unique_ptr<views::View> CreateExtraView(); virtual std::unique_ptr<views::View> CreateExtraView();
// Whether the item should be enabled (if disabled, the user will not be
// able to click on the item).
virtual bool IsEnabled() = 0;
// Returns whether this item is complete/valid and can be selected by the // Returns whether this item is complete/valid and can be selected by the
// user. If this returns false when the user attempts to select this item, // user. If this returns false when the user attempts to select this item,
// PerformSelectionFallback will be called instead. // PerformSelectionFallback will be called instead.
virtual bool CanBeSelected() const = 0; virtual bool CanBeSelected() = 0;
// Performs the action that replaces selection when CanBeSelected returns // Performs the action that replaces selection when CanBeSelected returns
// false. This will usually be to display an editor. // false. This will usually be to display an editor.
......
...@@ -31,7 +31,9 @@ class TestListItem : public PaymentRequestItemList::Item { ...@@ -31,7 +31,9 @@ class TestListItem : public PaymentRequestItemList::Item {
return base::MakeUnique<views::View>(); return base::MakeUnique<views::View>();
} }
bool CanBeSelected() const override { return true; } bool IsEnabled() override { return true; }
bool CanBeSelected() override { return true; }
void PerformSelectionFallback() override {} void PerformSelectionFallback() override {}
......
...@@ -66,14 +66,13 @@ base::string16 GetAddressFromProfile(const autofill::AutofillProfile& profile, ...@@ -66,14 +66,13 @@ base::string16 GetAddressFromProfile(const autofill::AutofillProfile& profile,
} }
// |s1|, |s2|, and |s3| are lines identifying the profile. |s1| is the // |s1|, |s2|, and |s3| are lines identifying the profile. |s1| is the
// "headline" which may be emphasized depending on |type|. |error| is a // "headline" which may be emphasized depending on |type|. If |disabled_state|
// message indicating errors that need to be resolved before using this // is true, the labels will look disabled.
// profile. std::unique_ptr<views::View> GetBaseProfileLabel(AddressStyleType type,
std::unique_ptr<views::View> GetProfileLabel(AddressStyleType type, const base::string16& s1,
const base::string16& s1, const base::string16& s2,
const base::string16& s2, const base::string16& s3,
const base::string16& s3, bool disabled_state = false) {
const base::string16& error) {
std::unique_ptr<views::View> container = base::MakeUnique<views::View>(); std::unique_ptr<views::View> container = base::MakeUnique<views::View>();
std::unique_ptr<views::BoxLayout> layout = std::unique_ptr<views::BoxLayout> layout =
base::MakeUnique<views::BoxLayout>(views::BoxLayout::kVertical, 0, 0, 0); base::MakeUnique<views::BoxLayout>(views::BoxLayout::kVertical, 0, 0, 0);
...@@ -89,6 +88,10 @@ std::unique_ptr<views::View> GetProfileLabel(AddressStyleType type, ...@@ -89,6 +88,10 @@ std::unique_ptr<views::View> GetProfileLabel(AddressStyleType type,
} }
label->set_id(static_cast<int>(DialogViewID::PROFILE_LABEL_LINE_1)); label->set_id(static_cast<int>(DialogViewID::PROFILE_LABEL_LINE_1));
label->SetHorizontalAlignment(gfx::ALIGN_LEFT); label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
if (disabled_state) {
label->SetEnabledColor(label->GetNativeTheme()->GetSystemColor(
ui::NativeTheme::kColorId_LabelDisabledColor));
}
container->AddChildView(label.release()); container->AddChildView(label.release());
} }
...@@ -96,6 +99,10 @@ std::unique_ptr<views::View> GetProfileLabel(AddressStyleType type, ...@@ -96,6 +99,10 @@ std::unique_ptr<views::View> GetProfileLabel(AddressStyleType type,
std::unique_ptr<views::Label> label = base::MakeUnique<views::Label>(s2); std::unique_ptr<views::Label> label = base::MakeUnique<views::Label>(s2);
label->set_id(static_cast<int>(DialogViewID::PROFILE_LABEL_LINE_2)); label->set_id(static_cast<int>(DialogViewID::PROFILE_LABEL_LINE_2));
label->SetHorizontalAlignment(gfx::ALIGN_LEFT); label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
if (disabled_state) {
label->SetEnabledColor(label->GetNativeTheme()->GetSystemColor(
ui::NativeTheme::kColorId_LabelDisabledColor));
}
container->AddChildView(label.release()); container->AddChildView(label.release());
} }
...@@ -103,19 +110,43 @@ std::unique_ptr<views::View> GetProfileLabel(AddressStyleType type, ...@@ -103,19 +110,43 @@ std::unique_ptr<views::View> GetProfileLabel(AddressStyleType type,
std::unique_ptr<views::Label> label = base::MakeUnique<views::Label>(s3); std::unique_ptr<views::Label> label = base::MakeUnique<views::Label>(s3);
label->set_id(static_cast<int>(DialogViewID::PROFILE_LABEL_LINE_3)); label->set_id(static_cast<int>(DialogViewID::PROFILE_LABEL_LINE_3));
label->SetHorizontalAlignment(gfx::ALIGN_LEFT); label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
if (disabled_state) {
label->SetEnabledColor(label->GetNativeTheme()->GetSystemColor(
ui::NativeTheme::kColorId_LabelDisabledColor));
}
container->AddChildView(label.release()); container->AddChildView(label.release());
} }
return container;
}
if (!error.empty()) { // Returns a label representing the |profile| as a shipping address. See
std::unique_ptr<views::Label> label = base::MakeUnique<views::Label>(error); // GetBaseProfileLabel() for more documentation.
label->set_id(static_cast<int>(DialogViewID::PROFILE_LABEL_ERROR)); std::unique_ptr<views::View> GetShippingAddressLabel(
label->SetFontList(label->GetDefaultFontList().DeriveWithSizeDelta(-1)); AddressStyleType type,
label->SetEnabledColor(label->GetNativeTheme()->GetSystemColor( const std::string& locale,
ui::NativeTheme::kColorId_LinkEnabled)); const autofill::AutofillProfile& profile,
container->AddChildView(label.release()); bool disabled_state) {
} base::string16 name =
profile.GetInfo(autofill::AutofillType(autofill::NAME_FULL), locale);
return container; base::string16 address = GetAddressFromProfile(profile, locale);
base::string16 phone = profile.GetInfo(
autofill::AutofillType(autofill::PHONE_HOME_WHOLE_NUMBER), locale);
return GetBaseProfileLabel(type, name, address, phone, disabled_state);
}
std::unique_ptr<views::Label> GetLabelForMissingInformation(
const base::string16& missing_info) {
std::unique_ptr<views::Label> label =
base::MakeUnique<views::Label>(missing_info);
label->set_id(static_cast<int>(DialogViewID::PROFILE_LABEL_ERROR));
label->SetFontList(label->GetDefaultFontList().DeriveWithSizeDelta(-1));
// Missing information typically has a nice shade of blue.
label->SetEnabledColor(label->GetNativeTheme()->GetSystemColor(
ui::NativeTheme::kColorId_LinkEnabled));
return label;
} }
// Paints the gray horizontal line that doesn't span the entire width of the // Paints the gray horizontal line that doesn't span the entire width of the
...@@ -243,23 +274,39 @@ std::unique_ptr<views::View> CreateProductLogoFooterView() { ...@@ -243,23 +274,39 @@ std::unique_ptr<views::View> CreateProductLogoFooterView() {
return content_view; return content_view;
} }
std::unique_ptr<views::View> GetShippingAddressLabel( std::unique_ptr<views::View> GetShippingAddressLabelWithError(
AddressStyleType type, AddressStyleType type,
const std::string& locale, const std::string& locale,
const autofill::AutofillProfile& profile, const autofill::AutofillProfile& profile,
const PaymentOptionsProvider& options, const base::string16& error,
const PaymentsProfileComparator& comp) { bool disabled_state) {
base::string16 name = std::unique_ptr<views::View> base_label =
profile.GetInfo(autofill::AutofillType(autofill::NAME_FULL), locale); GetShippingAddressLabel(type, locale, profile, disabled_state);
base::string16 address = GetAddressFromProfile(profile, locale);
base::string16 phone = profile.GetInfo( if (!error.empty()) {
autofill::AutofillType(autofill::PHONE_HOME_WHOLE_NUMBER), locale); std::unique_ptr<views::Label> label = base::MakeUnique<views::Label>(error);
label->set_id(static_cast<int>(DialogViewID::PROFILE_LABEL_ERROR));
label->SetFontList(label->GetDefaultFontList().DeriveWithSizeDelta(-1));
// Error information is typically in red.
label->SetEnabledColor(label->GetNativeTheme()->GetSystemColor(
ui::NativeTheme::kColorId_AlertSeverityHigh));
base_label->AddChildView(label.release());
}
return base_label;
}
base::string16 error = comp.GetStringForMissingShippingFields(profile); std::unique_ptr<views::View> GetShippingAddressLabelWithMissingInfo(
AddressStyleType type,
const std::string& locale,
const autofill::AutofillProfile& profile,
const PaymentsProfileComparator& comp) {
std::unique_ptr<views::View> base_label =
GetShippingAddressLabel(type, locale, profile, /*disabled_state=*/false);
return GetProfileLabel(type, name, address, phone, error); base_label->AddChildView(GetLabelForMissingInformation(
comp.GetStringForMissingShippingFields(profile))
.release());
return base_label;
} }
// TODO(anthonyvd): unit test the label layout. // TODO(anthonyvd): unit test the label layout.
...@@ -287,9 +334,13 @@ std::unique_ptr<views::View> GetContactInfoLabel( ...@@ -287,9 +334,13 @@ std::unique_ptr<views::View> GetContactInfoLabel(
locale) locale)
: base::string16(); : base::string16();
base::string16 error = comp.GetStringForMissingContactFields(profile); std::unique_ptr<views::View> base_label =
GetBaseProfileLabel(type, name, phone, email);
return GetProfileLabel(type, name, phone, email, error); base_label->AddChildView(GetLabelForMissingInformation(
comp.GetStringForMissingContactFields(profile))
.release());
return base_label;
} }
std::unique_ptr<views::Border> CreatePaymentRequestRowBorder() { std::unique_ptr<views::Border> CreatePaymentRequestRowBorder() {
......
...@@ -78,12 +78,24 @@ std::unique_ptr<views::View> CreateProductLogoFooterView(); ...@@ -78,12 +78,24 @@ std::unique_ptr<views::View> CreateProductLogoFooterView();
enum class AddressStyleType { SUMMARY, DETAILED }; enum class AddressStyleType { SUMMARY, DETAILED };
// Extracts and formats descriptive text from the given |profile| to represent // Extracts and formats descriptive text from the given |profile| to represent
// the address in the context specified by |type|. // the address in the context specified by |type|. If |error| is specified,
std::unique_ptr<views::View> GetShippingAddressLabel( // this will display it as the last item in an error state. |disabled_state|
// will make the various label lines look disabled.
std::unique_ptr<views::View> GetShippingAddressLabelWithError(
AddressStyleType type,
const std::string& locale,
const autofill::AutofillProfile& profile,
const base::string16& error,
bool disabled_state);
// Extracts and formats descriptive text from the given |profile| to represent
// the address in the context specified by |type|. The missing information will
// be computed using |comp| and displayed as the last line in an informative
// manner.
std::unique_ptr<views::View> GetShippingAddressLabelWithMissingInfo(
AddressStyleType type, AddressStyleType type,
const std::string& locale, const std::string& locale,
const autofill::AutofillProfile& profile, const autofill::AutofillProfile& profile,
const PaymentOptionsProvider& options,
const PaymentsProfileComparator& comp); const PaymentsProfileComparator& comp);
// Extracts and formats descriptive text from the given |profile| to represent // Extracts and formats descriptive text from the given |profile| to represent
......
...@@ -625,14 +625,24 @@ PaymentSheetViewController::CreateShippingSectionContent() { ...@@ -625,14 +625,24 @@ PaymentSheetViewController::CreateShippingSectionContent() {
if (current_update_reason_ == if (current_update_reason_ ==
PaymentRequestSpec::UpdateReason::SHIPPING_ADDRESS) { PaymentRequestSpec::UpdateReason::SHIPPING_ADDRESS) {
return CreateCheckingSpinnerView(); return CreateCheckingSpinnerView();
} else { }
auto* profile = state()->selected_shipping_profile();
return profile autofill::AutofillProfile* profile = state()->selected_shipping_profile();
? GetShippingAddressLabel( if (!profile)
AddressStyleType::SUMMARY, state()->GetApplicationLocale(), return base::MakeUnique<views::Label>(base::string16());
*profile, *spec(), *(state()->profile_comparator()))
: base::MakeUnique<views::Label>(base::string16()); // If there is a shipping option error related to the selected address,
// display it (without disabling the row because the user should feel like
// they can click on it to correct the problem). Otherwise, display the
// address possibly with the missing information to make it complete.
if (!spec()->selected_shipping_option_error().empty()) {
return GetShippingAddressLabelWithError(
AddressStyleType::SUMMARY, state()->GetApplicationLocale(), *profile,
spec()->selected_shipping_option_error(), /*disabled_state=*/false);
} else {
return GetShippingAddressLabelWithMissingInfo(
AddressStyleType::SUMMARY, state()->GetApplicationLocale(), *profile,
*(state()->profile_comparator()));
} }
} }
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
#include "chrome/browser/ui/views/payments/profile_list_view_controller.h" #include "chrome/browser/ui/views/payments/profile_list_view_controller.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/ui/views/payments/payment_request_dialog_view.h" #include "chrome/browser/ui/views/payments/payment_request_dialog_view.h"
#include "chrome/browser/ui/views/payments/payment_request_dialog_view_ids.h" #include "chrome/browser/ui/views/payments/payment_request_dialog_view_ids.h"
#include "chrome/browser/ui/views/payments/payment_request_row_view.h" #include "chrome/browser/ui/views/payments/payment_request_row_view.h"
...@@ -15,6 +16,9 @@ ...@@ -15,6 +16,9 @@
#include "components/payments/core/strings_util.h" #include "components/payments/core/strings_util.h"
#include "components/strings/grit/components_strings.h" #include "components/strings/grit/components_strings.h"
#include "ui/base/l10n/l10n_util.h" #include "ui/base/l10n/l10n_util.h"
#include "ui/gfx/paint_vector_icon.h"
#include "ui/native_theme/native_theme.h"
#include "ui/vector_icons/vector_icons.h"
#include "ui/views/controls/button/label_button.h" #include "ui/views/controls/button/label_button.h"
#include "ui/views/controls/button/md_text_button.h" #include "ui/views/controls/button/md_text_button.h"
#include "ui/views/controls/image_view.h" #include "ui/views/controls/image_view.h"
...@@ -75,12 +79,19 @@ class ProfileItem : public PaymentRequestItemList::Item { ...@@ -75,12 +79,19 @@ class ProfileItem : public PaymentRequestItemList::Item {
} }
} }
bool CanBeSelected() const override { bool IsEnabled() override { return parent_view_->IsEnabled(profile_); }
return parent_view_->IsValidProfile(*profile_);
bool CanBeSelected() override {
// In order to be selectable, a profile entry needs to be enabled, and the
// profile valid according to the controller. If either condition is false,
// PerformSelectionFallback() is called.
return IsEnabled() && parent_view_->IsValidProfile(*profile_);
} }
void PerformSelectionFallback() override { void PerformSelectionFallback() override {
parent_view_->ShowEditor(profile_); // If enabled, the editor is opened to complete the invalid profile.
if (IsEnabled())
parent_view_->ShowEditor(profile_);
} }
ProfileListViewController* parent_view_; ProfileListViewController* parent_view_;
...@@ -106,9 +117,16 @@ class ShippingProfileViewController : public ProfileListViewController { ...@@ -106,9 +117,16 @@ class ShippingProfileViewController : public ProfileListViewController {
// ProfileListViewController: // ProfileListViewController:
std::unique_ptr<views::View> GetLabel( std::unique_ptr<views::View> GetLabel(
autofill::AutofillProfile* profile) override { autofill::AutofillProfile* profile) override {
return GetShippingAddressLabel(AddressStyleType::DETAILED, if (!IsEnabled(profile)) {
state()->GetApplicationLocale(), *profile, // The error is not shown in the label itself on this screen, but the
*spec(), *(state()->profile_comparator())); // entry is disabled.
return GetShippingAddressLabelWithError(
AddressStyleType::DETAILED, state()->GetApplicationLocale(), *profile,
/*error=*/base::string16(), /*disabled_state=*/true);
}
return GetShippingAddressLabelWithMissingInfo(
AddressStyleType::DETAILED, state()->GetApplicationLocale(), *profile,
*(state()->profile_comparator()));
} }
void SelectProfile(autofill::AutofillProfile* profile) override { void SelectProfile(autofill::AutofillProfile* profile) override {
...@@ -127,7 +145,10 @@ class ShippingProfileViewController : public ProfileListViewController { ...@@ -127,7 +145,10 @@ class ShippingProfileViewController : public ProfileListViewController {
} }
autofill::AutofillProfile* GetSelectedProfile() override { autofill::AutofillProfile* GetSelectedProfile() override {
return state()->selected_shipping_profile(); // If there are no errors with the currently selected profile, return it.
return spec()->selected_shipping_option_error().empty()
? state()->selected_shipping_profile()
: nullptr;
} }
bool IsValidProfile(const autofill::AutofillProfile& profile) override { bool IsValidProfile(const autofill::AutofillProfile& profile) override {
...@@ -142,6 +163,42 @@ class ShippingProfileViewController : public ProfileListViewController { ...@@ -142,6 +163,42 @@ class ShippingProfileViewController : public ProfileListViewController {
return DialogViewID::SHIPPING_ADDRESS_SHEET_LIST_VIEW; return DialogViewID::SHIPPING_ADDRESS_SHEET_LIST_VIEW;
} }
std::unique_ptr<views::View> CreateHeaderView() override {
if (spec()->selected_shipping_option_error().empty())
return nullptr;
std::unique_ptr<views::View> header_view = base::MakeUnique<views::View>();
// 8 pixels between the warning icon view and the text.
constexpr int kRowHorizontalSpacing = 8;
views::BoxLayout* layout = new views::BoxLayout(
views::BoxLayout::kHorizontal,
payments::kPaymentRequestRowHorizontalInsets, 0, kRowHorizontalSpacing);
layout->set_main_axis_alignment(
views::BoxLayout::MAIN_AXIS_ALIGNMENT_START);
layout->set_cross_axis_alignment(
views::BoxLayout::CROSS_AXIS_ALIGNMENT_STRETCH);
header_view->SetLayoutManager(layout);
std::unique_ptr<views::ImageView> warning_icon =
base::MakeUnique<views::ImageView>();
warning_icon->set_can_process_events_within_subtree(false);
warning_icon->SetImage(gfx::CreateVectorIcon(
ui::kWarningIcon, 16,
warning_icon->GetNativeTheme()->GetSystemColor(
ui::NativeTheme::kColorId_AlertSeverityHigh)));
header_view->AddChildView(warning_icon.release());
std::unique_ptr<views::Label> label = base::MakeUnique<views::Label>(
spec()->selected_shipping_option_error());
label->set_id(
static_cast<int>(DialogViewID::SHIPPING_ADDRESS_OPTION_ERROR));
label->SetEnabledColor(label->GetNativeTheme()->GetSystemColor(
ui::NativeTheme::kColorId_AlertSeverityHigh));
label->SetMultiLine(true);
header_view->AddChildView(label.release());
return header_view;
}
base::string16 GetSheetTitle() override { base::string16 GetSheetTitle() override {
return GetShippingAddressSectionString(spec()->shipping_type()); return GetShippingAddressSectionString(spec()->shipping_type());
} }
...@@ -256,6 +313,21 @@ ProfileListViewController::ProfileListViewController( ...@@ -256,6 +313,21 @@ ProfileListViewController::ProfileListViewController(
ProfileListViewController::~ProfileListViewController() {} ProfileListViewController::~ProfileListViewController() {}
bool ProfileListViewController::IsEnabled(autofill::AutofillProfile* profile) {
// If selected_shipping_option_error() is not empty, it means the current
// selected_shipping_profile() is not supported by this merchant and should be
// shown in a disabled state. Therefore, |profile| is enabled in cases where
// (1) it's not the selected_shipping_profile
// OR
// (2) if it is, there is no shipping option error reported by the merchant.
return profile != state()->selected_shipping_profile() ||
spec()->selected_shipping_option_error().empty();
}
std::unique_ptr<views::View> ProfileListViewController::CreateHeaderView() {
return nullptr;
}
void ProfileListViewController::PopulateList() { void ProfileListViewController::PopulateList() {
autofill::AutofillProfile* selected_profile = GetSelectedProfile(); autofill::AutofillProfile* selected_profile = GetSelectedProfile();
...@@ -269,7 +341,15 @@ void ProfileListViewController::PopulateList() { ...@@ -269,7 +341,15 @@ void ProfileListViewController::PopulateList() {
} }
void ProfileListViewController::FillContentView(views::View* content_view) { void ProfileListViewController::FillContentView(views::View* content_view) {
content_view->SetLayoutManager(new views::FillLayout); views::BoxLayout* layout =
new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 0);
layout->set_main_axis_alignment(views::BoxLayout::MAIN_AXIS_ALIGNMENT_START);
layout->set_cross_axis_alignment(
views::BoxLayout::CROSS_AXIS_ALIGNMENT_STRETCH);
content_view->SetLayoutManager(layout);
std::unique_ptr<views::View> header_view = CreateHeaderView();
if (header_view)
content_view->AddChildView(header_view.release());
std::unique_ptr<views::View> list_view = list_.CreateListView(); std::unique_ptr<views::View> list_view = list_.CreateListView();
list_view->set_id(static_cast<int>(GetDialogViewId())); list_view->set_id(static_cast<int>(GetDialogViewId()));
content_view->AddChildView(list_view.release()); content_view->AddChildView(list_view.release());
......
...@@ -67,6 +67,9 @@ class ProfileListViewController : public PaymentRequestSheetController { ...@@ -67,6 +67,9 @@ class ProfileListViewController : public PaymentRequestSheetController {
virtual bool IsValidProfile(const autofill::AutofillProfile& profile) = 0; virtual bool IsValidProfile(const autofill::AutofillProfile& profile) = 0;
// Whether |profile| should be displayed in an enabled state and selectable.
bool IsEnabled(autofill::AutofillProfile* profile);
protected: protected:
// Does not take ownership of the arguments, which should outlive this object. // Does not take ownership of the arguments, which should outlive this object.
ProfileListViewController(PaymentRequestSpec* spec, ProfileListViewController(PaymentRequestSpec* spec,
...@@ -79,6 +82,10 @@ class ProfileListViewController : public PaymentRequestSheetController { ...@@ -79,6 +82,10 @@ class ProfileListViewController : public PaymentRequestSheetController {
virtual DialogViewID GetDialogViewId() = 0; virtual DialogViewID GetDialogViewId() = 0;
// Subclasses may choose to provide a header view to go on top of the item
// list view.
virtual std::unique_ptr<views::View> CreateHeaderView();
void PopulateList(); void PopulateList();
// PaymentRequestSheetController: // PaymentRequestSheetController:
......
...@@ -43,7 +43,12 @@ class ShippingOptionItem : public PaymentRequestItemList::Item { ...@@ -43,7 +43,12 @@ class ShippingOptionItem : public PaymentRequestItemList::Item {
} }
} }
bool CanBeSelected() const override { bool IsEnabled() override {
// Shipping options are vetted by the website; none are disabled.
return true;
}
bool CanBeSelected() override {
// Shipping options are vetted by the website; they're all OK to select. // Shipping options are vetted by the website; they're all OK to select.
return true; return true;
} }
......
...@@ -26,7 +26,7 @@ class PaymentRequestShippingOptionViewControllerTest ...@@ -26,7 +26,7 @@ class PaymentRequestShippingOptionViewControllerTest
}; };
IN_PROC_BROWSER_TEST_F(PaymentRequestShippingOptionViewControllerTest, IN_PROC_BROWSER_TEST_F(PaymentRequestShippingOptionViewControllerTest,
OrderSummaryReflectsShippingOption) { SelectingVariousShippingOptions) {
// In MI state, shipping is $5.00. // In MI state, shipping is $5.00.
autofill::AutofillProfile michigan = autofill::test::GetFullProfile2(); autofill::AutofillProfile michigan = autofill::test::GetFullProfile2();
michigan.set_use_count(100U); michigan.set_use_count(100U);
...@@ -80,6 +80,18 @@ IN_PROC_BROWSER_TEST_F(PaymentRequestShippingOptionViewControllerTest, ...@@ -80,6 +80,18 @@ IN_PROC_BROWSER_TEST_F(PaymentRequestShippingOptionViewControllerTest,
// available for Canada. // available for Canada.
EXPECT_EQ(nullptr, dialog_view()->GetViewByID(static_cast<int>( EXPECT_EQ(nullptr, dialog_view()->GetViewByID(static_cast<int>(
DialogViewID::PAYMENT_SHEET_SHIPPING_OPTION_SECTION))); DialogViewID::PAYMENT_SHEET_SHIPPING_OPTION_SECTION)));
// There is now an appropriate error that is shown in the shipping address
// section of the Payment Sheet. The string is returned by the merchant.
EXPECT_EQ(base::ASCIIToUTF16("We do not ship to this address"),
GetProfileLabelValues(
DialogViewID::PAYMENT_SHEET_SHIPPING_ADDRESS_SECTION)
.back());
// Go to the address selector and see this error as well.
OpenShippingAddressSectionScreen();
EXPECT_EQ(base::ASCIIToUTF16("We do not ship to this address"),
GetLabelText(DialogViewID::SHIPPING_ADDRESS_OPTION_ERROR));
} }
} // namespace payments } // namespace payments
...@@ -83,6 +83,7 @@ function updateDetails(details, addr) { ...@@ -83,6 +83,7 @@ function updateDetails(details, addr) {
details.shippingOptions = [shippingOption]; details.shippingOptions = [shippingOption];
} else { } else {
delete details.shippingOptions; delete details.shippingOptions;
details.error = 'We do not ship to this address';
} }
return details; return details;
} }
...@@ -22,6 +22,7 @@ static_library("content") { ...@@ -22,6 +22,7 @@ static_library("content") {
"//components/autofill/core/browser", "//components/autofill/core/browser",
"//components/payments/core", "//components/payments/core",
"//components/payments/mojom", "//components/payments/mojom",
"//components/strings:components_strings_grit",
"//content/public/browser", "//content/public/browser",
"//mojo/public/cpp/bindings", "//mojo/public/cpp/bindings",
"//third_party/libphonenumber", "//third_party/libphonenumber",
......
...@@ -7,8 +7,11 @@ ...@@ -7,8 +7,11 @@
#include <utility> #include <utility>
#include "base/logging.h" #include "base/logging.h"
#include "base/strings/utf_string_conversions.h"
#include "components/payments/core/payment_method_data.h" #include "components/payments/core/payment_method_data.h"
#include "components/payments/core/payment_request_data_util.h" #include "components/payments/core/payment_request_data_util.h"
#include "components/strings/grit/components_strings.h"
#include "ui/base/l10n/l10n_util.h"
namespace payments { namespace payments {
...@@ -166,6 +169,7 @@ void PaymentRequestSpec::UpdateSelectedShippingOption() { ...@@ -166,6 +169,7 @@ void PaymentRequestSpec::UpdateSelectedShippingOption() {
if (!request_shipping()) if (!request_shipping())
return; return;
selected_shipping_option_error_.clear();
// As per the spec, the selected shipping option should initially be the last // As per the spec, the selected shipping option should initially be the last
// one in the array that has its selected field set to true. // one in the array that has its selected field set to true.
auto selected_shipping_option_it = std::find_if( auto selected_shipping_option_it = std::find_if(
...@@ -177,7 +181,12 @@ void PaymentRequestSpec::UpdateSelectedShippingOption() { ...@@ -177,7 +181,12 @@ void PaymentRequestSpec::UpdateSelectedShippingOption() {
selected_shipping_option_ = selected_shipping_option_it->get(); selected_shipping_option_ = selected_shipping_option_it->get();
} else { } else {
// It's possible that there is no selected shipping option. // It's possible that there is no selected shipping option.
// TODO(crbug.com/710004): Show an error in this case. if (!details().error.empty()) {
selected_shipping_option_error_ = base::UTF8ToUTF16(details().error);
} else {
selected_shipping_option_error_ =
l10n_util::GetStringUTF16(IDS_PAYMENTS_UNSUPPORTED_SHIPPING_ADDRESS);
}
selected_shipping_option_ = nullptr; selected_shipping_option_ = nullptr;
} }
} }
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include "base/macros.h" #include "base/macros.h"
#include "base/observer_list.h" #include "base/observer_list.h"
#include "base/strings/string16.h"
#include "components/payments/core/currency_formatter.h" #include "components/payments/core/currency_formatter.h"
#include "components/payments/core/payment_options_provider.h" #include "components/payments/core/payment_options_provider.h"
#include "components/payments/mojom/payment_request.mojom.h" #include "components/payments/mojom/payment_request.mojom.h"
...@@ -94,6 +95,11 @@ class PaymentRequestSpec : public PaymentOptionsProvider { ...@@ -94,6 +95,11 @@ class PaymentRequestSpec : public PaymentOptionsProvider {
mojom::PaymentShippingOption* selected_shipping_option() const { mojom::PaymentShippingOption* selected_shipping_option() const {
return selected_shipping_option_; return selected_shipping_option_;
} }
// if |selected_shipping_option()| is nullptr, this may contain a non-empty
// error returned by the merchant.
const base::string16& selected_shipping_option_error() const {
return selected_shipping_option_error_;
}
const mojom::PaymentDetails& details() const { return *details_.get(); } const mojom::PaymentDetails& details() const { return *details_.get(); }
...@@ -132,6 +138,7 @@ class PaymentRequestSpec : public PaymentOptionsProvider { ...@@ -132,6 +138,7 @@ class PaymentRequestSpec : public PaymentOptionsProvider {
const std::string app_locale_; const std::string app_locale_;
// The currently shipping option as specified by the merchant. // The currently shipping option as specified by the merchant.
mojom::PaymentShippingOption* selected_shipping_option_; mojom::PaymentShippingOption* selected_shipping_option_;
base::string16 selected_shipping_option_error_;
std::unique_ptr<CurrencyFormatter> currency_formatter_; std::unique_ptr<CurrencyFormatter> currency_formatter_;
......
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