chromium/ash/webui/common/resources/network/apn_selection_dialog.js

// 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.

/**
 * @fileoverview
 * Dialog that displays APNs for a user to select to be attempted to be used.
 */

import '//resources/ash/common/cr_elements/localized_link/localized_link.js';
import '//resources/ash/common/cr_elements/cr_button/cr_button.js';
import '//resources/ash/common/cr_elements/cr_dialog/cr_dialog.js';
import '//resources/ash/common/cr_elements/cr_shared_style.css.js';
import '//resources/ash/common/network/apn_selection_dialog_list_item.js';
import '//resources/polymer/v3_0/iron-list/iron-list.js';

import {assert} from '//resources/ash/common/assert.js';
import {I18nBehavior, I18nBehaviorInterface} from '//resources/ash/common/i18n_behavior.js';
import {focusWithoutInk} from '//resources/js/focus_without_ink.js';
import {ApnProperties, CrosNetworkConfigInterface} from '//resources/mojo/chromeos/services/network_config/public/mojom/cros_network_config.mojom-webui.js';
import {afterNextRender, mixinBehaviors, PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';

import {getTemplate} from './apn_selection_dialog.html.js';
import {MojoInterfaceProviderImpl} from './mojo_interface_provider.js';

/**
 * @constructor
 * @extends {PolymerElement}
 * @implements {I18nBehaviorInterface}
 */
const ApnSelectionDialogElementBase =
    mixinBehaviors([I18nBehavior], PolymerElement);

/** @polymer */
export class ApnSelectionDialog extends ApnSelectionDialogElementBase {
  static get is() {
    return 'apn-selection-dialog';
  }

  static get template() {
    return getTemplate();
  }

  static get properties() {
    return {
      /** @type {Array<ApnProperties>} */
      apnList: {
        type: Array,
        value: [],
      },

      /** The GUID of the network to select known APNs for. */
      guid: String,

      shouldOmitLinks: {
        type: Boolean,
        value: false,
      },

      /** @type {ApnProperties} */
      selectedApn_: {
        type: Object,
      },

      /**
       * If |shouldAnnounceA11yActionButtonState_| === true, an a11y
       * announcement will be made. No announcement will be made until the
       * enable state of the action button changes as a result of user changes
       * in the dialog, and subsequent action button state changes (i.e the
       * initial enabled state of the button will not be announced).
       * @private {boolean|undefined}
       */
      shouldAnnounceA11yActionButtonState_: {
        type: Object,
        value: undefined,
      },

      /** @private */
      actionButtonEnabledA11yText_: {
        type: String,
        value: '',
        observer: 'onActionButtonEnabledStateA11yTextChanged_',
        computed:
            'computeActionButtonEnabledStateA11yText_(apnList, selectedApn_)',
      },
    };
  }

  /** @override */
  constructor() {
    super();

    /** @private {!CrosNetworkConfigInterface} */
    this.networkConfig_ =
        MojoInterfaceProviderImpl.getInstance().getMojoServiceRemote();
  }

  /** @override */
  connectedCallback() {
    super.connectedCallback();

    // Set the default focus when the dialog opens.
    afterNextRender(this, function() {
      focusWithoutInk(this.shadowRoot.querySelector('.cancel-button'));

      // Only after dialog is connected and the intended element is focused can
      // action enabled state changes be a11y announced.
      assert(this.shouldAnnounceA11yActionButtonState_ === undefined);
      this.shouldAnnounceA11yActionButtonState_ = false;
    });
  }

  /**
   * @param {!Event} event
   * @private
   */
  onCancelClicked_(event) {
    event.stopPropagation();
    if (this.$.apnSelectionDialog.open) {
      this.$.apnSelectionDialog.close();
    }
  }

  /**
   * @param {!Event} event
   * @private
   */
  onActionButtonClicked_(event) {
    assert(this.guid);

    if (!this.selectedApn_) {
      return;
    }

    this.networkConfig_.createExclusivelyEnabledCustomApn(
        this.guid, this.selectedApn_);
    this.$.apnSelectionDialog.close();
  }

  /**
   * @param {!ApnProperties} apn
   * @return {boolean}
   * @private
   */
  isApnSelected_(apn) {
    return apn === this.selectedApn_;
  }

  /**
   * @return {string}
   * @private
   */
  computeActionButtonEnabledStateA11yText_() {
    return this.selectedApn_ ?
        this.i18n('apnSelectionDialogA11yUseApnEnabled') :
        this.i18n('apnSelectionDialogA11yUseApnDisabled');
  }

  /**
   * @param {string} newVal
   * @param {string} oldVal
   * @private
   */
  onActionButtonEnabledStateA11yTextChanged_(newVal, oldVal) {
    if (this.shouldAnnounceA11yActionButtonState_ === undefined) {
      return;
    }
    if (!newVal || !oldVal) {
      this.shouldAnnounceA11yActionButtonState_ = false;
      return;
    }
    this.shouldAnnounceA11yActionButtonState_ = oldVal !== newVal;
  }
}

customElements.define(ApnSelectionDialog.is, ApnSelectionDialog);