chromium/ios/chrome/browser/ui/search_engine_choice/snippet_search_engine_button.mm

// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#import "ios/chrome/browser/ui/search_engine_choice/snippet_search_engine_button.h"

#import "base/check.h"
#import "base/check_op.h"
#import "base/metrics/user_metrics.h"
#import "base/metrics/user_metrics_action.h"
#import "base/notreached.h"
#import "base/strings/sys_string_conversions.h"
#import "ios/chrome/browser/shared/ui/symbols/symbols.h"
#import "ios/chrome/browser/ui/search_engine_choice/search_engine_choice_constants.h"
#import "ios/chrome/common/ui/colors/semantic_color_names.h"
#import "ios/chrome/common/ui/util/constraints_ui_util.h"
#import "ios/chrome/common/ui/util/image_util.h"
#import "ios/chrome/grit/ios_strings.h"
#import "ui/base/l10n/l10n_util.h"

namespace {

// Radius for the favicon container.
constexpr CGFloat kFaviconContainerViewRadius = 7.;
// Size for the favicon container.
constexpr CGFloat kFaviconContainerViewSize = 32.;
// Margin between the name and snippet labels.
constexpr CGFloat kNameSnippetLabelMargin = 2.;
// The size of the radio button size.
constexpr CGFloat kRadioButtonSize = 24.;
// The size of the radio button image.
constexpr CGFloat kRadioButtonImageSize = 20.;
// Upper vertical margin for name label in the button.
constexpr CGFloat kUpperVerticalMargin = 10.;
// Lower vertical margin for the snippet label in the button.
constexpr CGFloat kLowerVerticalMargin = 12.;
// Horizontal margin between elements in the button.
constexpr CGFloat kInnerHorizontalMargin = 12.;
// Horizontal margin between favicon/radio button image and the border.
constexpr CGFloat kBorderHorizontalMargin = 16.;
// Chevron button size.
constexpr CGFloat kChevronButtonSize = 44.;
// Horizontal chevron margin with the separtor, the name label and the snippet
// label.
constexpr CGFloat kChevronButtonHorizontalMargin = 2.;
// Thickness of the vertical separator.
constexpr CGFloat kSeparatorThickness = 1.;
// Duration of the snippet animation when changing state.
constexpr NSTimeInterval kSnippetAnimationDurationInSecond = .3;
// Alpha value for the checked background color.
constexpr NSTimeInterval kCheckedBackgroundColorAlpha = .1;

// Returns a snippet label.
UILabel* SnippetLabel() {
  UILabel* snippetLabel = [[UILabel alloc] init];
  snippetLabel.translatesAutoresizingMaskIntoConstraints = NO;
  snippetLabel.textColor = [UIColor colorNamed:kTextSecondaryColor];
  snippetLabel.font =
      [UIFont preferredFontForTextStyle:UIFontTextStyleFootnote];
  snippetLabel.adjustsFontForContentSizeCategory = YES;
  snippetLabel.textColor = [UIColor colorNamed:kTextSecondaryColor];
  return snippetLabel;
}

// Background color for selected element.
UIColor* GetCheckedBackgroundColor() {
  return [[UIColor colorNamed:kBlueColor]
      colorWithAlphaComponent:kCheckedBackgroundColorAlpha];
}

// Color for the tint of the radio button of the selected element.
UIColor* GetCheckedTintColor() {
  return [UIColor colorNamed:kBlueColor];
}

}  // namespace

@implementation SnippetSearchEngineButton {
  // Container View for the faviconView.
  UIView* _faviconContainerView;
  UIImageView* _faviconImageView;
  UILabel* _nameLabel;
  SnippetButtonState _snippetButtonState;
  UIButton* _chevronButton;
  BOOL _isChevronButtonEnabled;
  UIImageView* _radioButtonImageView;
  // Horizontal separator, shown only if `horizontalSeparatorHidden` is NO.
  UIView* _horizontalSeparator;
  // UILabel to display the first line of the snippet.
  UILabel* _snippetLabelOneLine;
  // UILabel to display the snippet with all lines.
  UILabel* _snippetLabelExpanded;
  // Constraint to activate when the button is collapsed. This contraint
  // locks the bottom of `_snippetLabelOneLine` with the bottom of
  // SnippetSearchEngineButton (with the right margin).
  // `_snippetLabelExpandedConstraint` needs to be disabled.
  NSLayoutConstraint* _snippetLabelOneLineConstraint;
  // Constraint to activate when the button is expanded. This contraint
  // locks the bottom of `_snippetLabelExpanded` with the bottom of
  // SnippetSearchEngineButton (with the right margin).
  // `_snippetLabelOneLineConstraint` needs to be disabled.
  NSLayoutConstraint* _snippetLabelExpandedConstraint;
}

- (instancetype)initWithFrame:(CGRect)frame {
  self = [super initWithFrame:frame];
  if (self) {
    self.clipsToBounds = YES;
    // Add the favicon container view and the favicon image view.
    _faviconContainerView = [[UIView alloc] init];
    _faviconContainerView.userInteractionEnabled = NO;
    _faviconContainerView.translatesAutoresizingMaskIntoConstraints = NO;
    _faviconContainerView.backgroundColor =
        [UIColor colorNamed:kPrimaryBackgroundColor];
    _faviconContainerView.layer.cornerRadius = kFaviconContainerViewRadius;
    _faviconContainerView.layer.masksToBounds = YES;
    [self addSubview:_faviconContainerView];
    _faviconImageView = [[UIImageView alloc] init];
    _faviconImageView.translatesAutoresizingMaskIntoConstraints = NO;
    _faviconImageView.layer.cornerRadius = kFaviconImageViewRadius;
    _faviconImageView.clipsToBounds = YES;
    [_faviconContainerView addSubview:_faviconImageView];
    [_faviconImageView
        setContentCompressionResistancePriority:UILayoutPriorityRequired
                                        forAxis:
                                            UILayoutConstraintAxisHorizontal];
    // Add name label.
    _nameLabel = [[UILabel alloc] init];
    _nameLabel.translatesAutoresizingMaskIntoConstraints = NO;
    _nameLabel.font = [UIFont preferredFontForTextStyle:UIFontTextStyleBody];
    _nameLabel.adjustsFontForContentSizeCategory = YES;
    [self addSubview:_nameLabel];
    // Make sure iOS prefers to strech the margins and not the name label,
    // by increasing the hugging priority.
    [_nameLabel setContentHuggingPriority:UILayoutPriorityDefaultHigh + 1
                                  forAxis:UILayoutConstraintAxisVertical];
    // Add one line snippet.
    _snippetLabelOneLine = SnippetLabel();
    _snippetLabelOneLine.numberOfLines = 1;
    // Make sure the snippet is not streched.
    [_snippetLabelOneLine
        setContentHuggingPriority:UILayoutPriorityDefaultHigh + 1
                          forAxis:UILayoutConstraintAxisVertical];
    [self addSubview:_snippetLabelOneLine];
    // Add expanded snippet.
    _snippetLabelExpanded = SnippetLabel();
    _snippetLabelExpanded.numberOfLines = 0;
    // Make sure the snippet is not streched.
    [_snippetLabelExpanded
        setContentHuggingPriority:UILayoutPriorityDefaultHigh + 1
                          forAxis:UILayoutConstraintAxisVertical];
    [self addSubview:_snippetLabelExpanded];
    // Add Chevron.
    _chevronButton = [[UIButton alloc] init];
    _chevronButton.translatesAutoresizingMaskIntoConstraints = NO;
    [_chevronButton setImage:DefaultSymbolTemplateWithPointSize(
                                 kChevronDownSymbol, kSymbolAccessoryPointSize)
                    forState:UIControlStateNormal];

    _chevronButton.tintColor = [UIColor colorNamed:kTextQuaternaryColor];
    [_chevronButton addTarget:self
                       action:@selector(chevronToggleAction:)
             forControlEvents:UIControlEventTouchUpInside];
    [self addSubview:_chevronButton];
    // Add vertical separator.
    UIView* verticalSeparator = [[UIView alloc] init];
    verticalSeparator.userInteractionEnabled = NO;
    verticalSeparator.translatesAutoresizingMaskIntoConstraints = NO;
    verticalSeparator.backgroundColor = [UIColor colorNamed:kGrey300Color];
    [self addSubview:verticalSeparator];
    // Add Horizontal separator.
    _horizontalSeparator = [[UIView alloc] init];
    _horizontalSeparator.userInteractionEnabled = NO;
    _horizontalSeparator.translatesAutoresizingMaskIntoConstraints = NO;
    _horizontalSeparator.backgroundColor = [UIColor colorNamed:kGrey300Color];
    [self addSubview:_horizontalSeparator];
    // Add the checked circle holder.
    _radioButtonImageView = [[UIImageView alloc] init];
    _radioButtonImageView.userInteractionEnabled = NO;
    _radioButtonImageView.translatesAutoresizingMaskIntoConstraints = NO;
    [self addSubview:_radioButtonImageView];
    // This layout guide is to generate the middle between _nameLabel and
    // _snippetLabelOneLine. This vertical middle is used to vertically aligned
    // the favicon, the chevron the vertical separator and the radio button.
    UILayoutGuide* nameAndOneLineSnippetLayoutGuide =
        [[UILayoutGuide alloc] init];
    [self addLayoutGuide:nameAndOneLineSnippetLayoutGuide];
    // Constraint when the snippet is expanded.
    _snippetLabelExpandedConstraint = [_snippetLabelExpanded.bottomAnchor
        constraintEqualToAnchor:self.bottomAnchor
                       constant:-kLowerVerticalMargin];
    // Constraint when the snippet is only one line.
    _snippetLabelOneLineConstraint =
        [nameAndOneLineSnippetLayoutGuide.bottomAnchor
            constraintEqualToAnchor:self.bottomAnchor
                           constant:-kLowerVerticalMargin];
    NSArray* constraints = @[
      // Constraints for avicon and favicon container.
      [_faviconContainerView.leadingAnchor
          constraintEqualToAnchor:self.leadingAnchor
                         constant:kBorderHorizontalMargin],
      [_faviconContainerView.centerYAnchor
          constraintEqualToAnchor:nameAndOneLineSnippetLayoutGuide
                                      .centerYAnchor],
      [_faviconContainerView.widthAnchor
          constraintEqualToConstant:kFaviconContainerViewSize],
      [_faviconContainerView.heightAnchor
          constraintEqualToConstant:kFaviconContainerViewSize],
      [_faviconImageView.centerXAnchor
          constraintEqualToAnchor:_faviconContainerView.centerXAnchor],
      [_faviconImageView.centerYAnchor
          constraintEqualToAnchor:_faviconContainerView.centerYAnchor],
      [_faviconImageView.widthAnchor
          constraintEqualToConstant:kFaviconImageViewSize],
      [_faviconImageView.heightAnchor
          constraintEqualToConstant:kFaviconImageViewSize],
      // Constraints for layout guide for _nameLabel and _snippetLabelOneLine.
      [nameAndOneLineSnippetLayoutGuide.topAnchor
          constraintEqualToAnchor:self.topAnchor
                         constant:kUpperVerticalMargin],
      _snippetLabelOneLineConstraint,
      [nameAndOneLineSnippetLayoutGuide.leadingAnchor
          constraintEqualToAnchor:_faviconContainerView.trailingAnchor
                         constant:kInnerHorizontalMargin],
      [nameAndOneLineSnippetLayoutGuide.trailingAnchor
          constraintEqualToAnchor:_chevronButton.leadingAnchor
                         constant:-kChevronButtonHorizontalMargin],
      // Constraints for name label.
      [_nameLabel.topAnchor
          constraintEqualToAnchor:nameAndOneLineSnippetLayoutGuide.topAnchor],
      [_nameLabel.leadingAnchor
          constraintEqualToAnchor:nameAndOneLineSnippetLayoutGuide
                                      .leadingAnchor],
      [_nameLabel.trailingAnchor
          constraintLessThanOrEqualToAnchor:nameAndOneLineSnippetLayoutGuide
                                                .trailingAnchor],
      [_nameLabel.bottomAnchor
          constraintEqualToAnchor:_snippetLabelOneLine.topAnchor
                         constant:-kNameSnippetLabelMargin],
      // Constraints for _snippetLabelOneLine.
      [_snippetLabelOneLine.bottomAnchor
          constraintEqualToAnchor:nameAndOneLineSnippetLayoutGuide
                                      .bottomAnchor],
      [_snippetLabelOneLine.leadingAnchor
          constraintEqualToAnchor:nameAndOneLineSnippetLayoutGuide
                                      .leadingAnchor],
      [_snippetLabelOneLine.trailingAnchor
          constraintLessThanOrEqualToAnchor:nameAndOneLineSnippetLayoutGuide
                                                .trailingAnchor],
      // Constraints for _snippetLabelExpanded.
      [_snippetLabelExpanded.topAnchor
          constraintEqualToAnchor:_snippetLabelOneLine.topAnchor],
      [_snippetLabelExpanded.leadingAnchor
          constraintEqualToAnchor:nameAndOneLineSnippetLayoutGuide
                                      .leadingAnchor],
      [_snippetLabelExpanded.trailingAnchor
          constraintLessThanOrEqualToAnchor:nameAndOneLineSnippetLayoutGuide
                                                .trailingAnchor],
      // Constraints for chevron.
      [_chevronButton.heightAnchor
          constraintEqualToConstant:kChevronButtonSize],
      [_chevronButton.widthAnchor constraintEqualToConstant:kChevronButtonSize],
      [_chevronButton.centerYAnchor
          constraintEqualToAnchor:nameAndOneLineSnippetLayoutGuide
                                      .centerYAnchor],
      [_chevronButton.trailingAnchor
          constraintEqualToAnchor:verticalSeparator.leadingAnchor
                         constant:-kChevronButtonHorizontalMargin],
      // Constraints for vertical separator.
      [verticalSeparator.heightAnchor
          constraintEqualToAnchor:_nameLabel.heightAnchor],
      [verticalSeparator.centerYAnchor
          constraintEqualToAnchor:nameAndOneLineSnippetLayoutGuide
                                      .centerYAnchor],
      [verticalSeparator.widthAnchor
          constraintEqualToConstant:kSeparatorThickness],
      [verticalSeparator.trailingAnchor
          constraintEqualToAnchor:_radioButtonImageView.leadingAnchor
                         constant:-kInnerHorizontalMargin],
      // Constraints for radio button.
      [_radioButtonImageView.centerYAnchor
          constraintEqualToAnchor:nameAndOneLineSnippetLayoutGuide
                                      .centerYAnchor],
      [_radioButtonImageView.widthAnchor
          constraintEqualToConstant:kRadioButtonSize],
      [_radioButtonImageView.heightAnchor
          constraintEqualToConstant:kRadioButtonSize],
      [_radioButtonImageView.trailingAnchor
          constraintEqualToAnchor:self.trailingAnchor
                         constant:-kBorderHorizontalMargin],
      // Constraints for horizontal separator.
      [_horizontalSeparator.leadingAnchor
          constraintEqualToAnchor:nameAndOneLineSnippetLayoutGuide
                                      .leadingAnchor],
      [_horizontalSeparator.trailingAnchor
          constraintEqualToAnchor:self.trailingAnchor],
      [_horizontalSeparator.bottomAnchor
          constraintEqualToAnchor:self.bottomAnchor],
      [_horizontalSeparator.heightAnchor
          constraintEqualToConstant:kSeparatorThickness],
    ];
    [NSLayoutConstraint activateConstraints:constraints];
    [self updateCellWithSnippetSate:SnippetButtonState::kOneLine animate:NO];
    [self updateCircleImageView];
    self.userInteractionEnabled = YES;
    self.accessibilityTraits = UIAccessibilityTraitButton;
  }
  return self;
}

- (void)updateAccessibilityTraits {
  self.accessibilityTraits |= UIAccessibilityTraitButton;
  if (_checked) {
    self.accessibilityTraits |= UIAccessibilityTraitSelected;
  } else {
    self.accessibilityTraits &= ~UIAccessibilityTraitSelected;
  }
}

#pragma mark - UIView

- (void)touchesBegan:(NSSet<UITouch*>*)touches withEvent:(UIEvent*)event {
  [super touchesBegan:touches withEvent:event];
  self.backgroundColor = GetCheckedBackgroundColor();
}

- (void)touchesCancelled:(NSSet<UITouch*>*)touches withEvent:(UIEvent*)event {
  [super touchesCancelled:touches withEvent:event];
  if (!_checked) {
    // This case can happen if the user taps on the selected search engine,
    // and cancels the tap by moving the finger away. The button background
    // color needs to be selected.
    self.backgroundColor = nil;
  }
}

- (void)layoutSubviews {
  [super layoutSubviews];
  // To know if the chevron is needed, we need to compare
  // `_snippetLabelExpanded` height and `_snippetLabelOneLine` height. To avoid
  // any issues with float approximations, there is one pixel margin.
  _isChevronButtonEnabled = _snippetLabelExpanded.frame.size.height -
                                _snippetLabelOneLine.frame.size.height >
                            1.;
  _chevronButton.alpha = (_isChevronButtonEnabled) ? 1. : .4;
}

#pragma mark - Properties

- (void)setChecked:(BOOL)checked {
  if (checked == _checked) {
    return;
  }
  _checked = checked;
  // `-[SnippetSearchEngineButton touchesEnded:withEvent:]` is called first,
  // which sets the background color with the animation. Then the view
  // controller receives the target action, which calls
  // `-[SnippetSearchEngineButton setChecked:YES]`. During the `setChecked:`
  // call, if the background color is updated, the animation is canceled.
  // So if the background color is already set, there is no point to set it
  // again.
  if (_checked && !self.backgroundColor) {
    self.backgroundColor = GetCheckedBackgroundColor();
  } else if (!_checked) {
    self.backgroundColor = nil;
  }
  [self updateCircleImageView];
  [self updateAccessibilityTraits];
}

- (void)setFaviconImage:(UIImage*)faviconImage {
  CGSize faviconImageSize =
      CGSizeMake(kFaviconImageViewSize, kFaviconImageViewSize);
  ResizeImage(faviconImage, faviconImageSize, ProjectionMode::kAspectFit);
  _faviconImageView.image = faviconImage;
}

- (UIImage*)faviconImage {
  return _faviconImageView.image;
}

- (void)setSearchEngineName:(NSString*)name {
  _nameLabel.text = name;
  [self updateChevronIdentifier];
}

- (NSString*)searchEngineName {
  return _nameLabel.text;
}

- (void)setSnippetButtonState:(SnippetButtonState)snippetButtonState {
  // This method should be called only when being configured, before to be
  // added to the view. Therefore there should be no animation.
  [self updateCellWithSnippetSate:snippetButtonState animate:NO];
}

- (void)setSnippetText:(NSString*)snippetText {
  _snippetText = [snippetText copy];
  _snippetLabelExpanded.text = _snippetText;
  _snippetLabelOneLine.text = _snippetText;
}

- (void)setHorizontalSeparatorHidden:(BOOL)hidden {
  _horizontalSeparator.hidden = hidden;
}

- (BOOL)horizontalSeparatorHidden {
  return _horizontalSeparator.hidden;
}

#pragma mark - Private

// Called by the chevron button.
- (void)chevronToggleAction:(id)sender {
  if (!_isChevronButtonEnabled) {
    return;
  }
  switch (_snippetButtonState) {
    case SnippetButtonState::kExpanded: {
      [self updateCellWithSnippetSate:SnippetButtonState::kOneLine animate:YES];
      NSString* collapsedFeedback = l10n_util::GetNSString(
          IDS_IOS_SEARCH_ENGINE_ACCESSIBILITY_SNIPPET_COLLAPSED);
      UIAccessibilityPostNotification(UIAccessibilityAnnouncementNotification,
                                      collapsedFeedback);
      break;
    }
    case SnippetButtonState::kOneLine: {
      [self updateCellWithSnippetSate:SnippetButtonState::kExpanded
                              animate:YES];
      UIAccessibilityPostNotification(UIAccessibilityAnnouncementNotification,
                                      self.snippetText);
      break;
    }
  }
}

// Updates the UI according to the new snippet state.
- (void)updateCellWithSnippetSate:(SnippetButtonState)newSnippetButtonState
                          animate:(BOOL)animate {
  // Need to avoid `if (_snippetButtonState == newSnippetButtonState) return;`,
  // so this method can be used by init method to setup the cell.
  _snippetButtonState = newSnippetButtonState;
  const float downRotation = 0;
  const float upRotation = downRotation + M_PI;
  UIButton* chevronButton = _chevronButton;
  UILabel* snippetLabelOneLine = _snippetLabelOneLine;
  UILabel* snippetLabelExpanded = _snippetLabelExpanded;
  NSLayoutConstraint* snippetLabelOneLineConstraint =
      _snippetLabelOneLineConstraint;
  NSLayoutConstraint* snippetLabelExpandedConstraint =
      _snippetLabelExpandedConstraint;
  [self updateChevronIdentifier];
  ProceduralBlock changesBlock = ^{
    switch (newSnippetButtonState) {
      case SnippetButtonState::kOneLine:
        chevronButton.transform =
            CGAffineTransformRotate(CGAffineTransformIdentity, downRotation);
        snippetLabelOneLine.alpha = 1;
        snippetLabelExpanded.alpha = 0;
        snippetLabelOneLineConstraint.active = YES;
        snippetLabelExpandedConstraint.active = NO;
        break;
      case SnippetButtonState::kExpanded:
        chevronButton.transform =
            CGAffineTransformRotate(CGAffineTransformIdentity, upRotation);
        snippetLabelOneLine.alpha = 0;
        snippetLabelExpanded.alpha = 1;
        snippetLabelOneLineConstraint.active = NO;
        snippetLabelExpandedConstraint.active = YES;
        break;
    }
    if (animate) {
      // Layout all the view to have a smooth transition.
      [self.animatedLayoutView layoutIfNeeded];
    }
  };
  if (animate) {
    [UIView animateWithDuration:kSnippetAnimationDurationInSecond
                     animations:changesBlock];
  } else {
    changesBlock();
  }
}

// Updates `_radioButtonImageView` based on `_checked`.
- (void)updateCircleImageView {
  UIImage* circleImage;
  if (_checked) {
    circleImage = DefaultSymbolWithPointSize(kCheckmarkCircleFillSymbol,
                                             kRadioButtonImageSize);
    _radioButtonImageView.tintColor = GetCheckedTintColor();
  } else {
    circleImage =
        DefaultSymbolWithPointSize(kCircleSymbol, kRadioButtonImageSize);
    _radioButtonImageView.tintColor = [UIColor colorNamed:kTextQuaternaryColor];
  }
  _radioButtonImageView.image = circleImage;
}

#pragma mark - Accessibility

- (NSString*)accessibilityLabel {
  CHECK_NE(self.snippetText.length, 0ul, base::NotFatalUntil::M127)
      << base::SysNSStringToUTF8(self.searchEngineName) << " "
      << base::SysNSStringToUTF8(self.snippetText);
  return [NSString
      stringWithFormat:@"%@. %@", self.searchEngineName, self.snippetText];
}

- (NSArray<NSString*>*)accessibilityUserInputLabels {
  CHECK_NE(self.searchEngineName.length, 0ul, base::NotFatalUntil::M127)
      << base::SysNSStringToUTF8(self.searchEngineName) << " "
      << base::SysNSStringToUTF8(self.snippetText);
  return @[ self.searchEngineName ];
}

- (NSString*)accessibilityIdentifier {
  return
      [NSString stringWithFormat:@"%@%@", kSnippetSearchEngineIdentifierPrefix,
                                 self.searchEngineName];
}

- (BOOL)isAccessibilityElement {
  return YES;
}

- (NSArray<UIAccessibilityCustomAction*>*)accessibilityCustomActions {
  if (!_isChevronButtonEnabled) {
    return [super accessibilityCustomActions];
  }
  NSString* actionName = nil;
  switch (_snippetButtonState) {
    case SnippetButtonState::kOneLine:
      actionName = l10n_util::GetNSString(
          IDS_IOS_SEARCH_ENGINE_ACCESSIBILITY_EXPAND_SNIPPET);
      break;
    case SnippetButtonState::kExpanded:
      actionName = l10n_util::GetNSString(
          IDS_IOS_SEARCH_ENGINE_ACCESSIBILITY_COLLAPSE_SNIPPET);
      break;
  }
  UIAccessibilityCustomAction* action = [[UIAccessibilityCustomAction alloc]
      initWithName:actionName
            target:self
          selector:@selector(chevronToggleAction:)];
  NSArray<UIAccessibilityCustomAction*>* actions = @[ action ];
  return actions;
}

- (void)updateChevronIdentifier {
  switch (_snippetButtonState) {
    case SnippetButtonState::kOneLine:
      _chevronButton.accessibilityIdentifier = [NSString
          stringWithFormat:@"%@%@",
                           kSnippetSearchEngineOneLineChevronIdentifierPrefix,
                           self.searchEngineName];
      break;
    case SnippetButtonState::kExpanded:
      base::RecordAction(
          base::UserMetricsAction(kExpandSearchEngineDescriptionUserAction));
      _chevronButton.accessibilityIdentifier = [NSString
          stringWithFormat:@"%@%@",
                           kSnippetSearchEngineExpandedChevronIdentifierPrefix,
                           self.searchEngineName];
      break;
  }
}

@end