Repository: ionic-team/ionic-framework
File: core/src/components/modal/modal.tsx (TypeScript)

Code before:
          ...getClassMap(this.cssClass),
        }}
        id={modalId}
        onIonBackdropTap={this.onBackdropTap}
        onIonModalDidPresent={this.onLifecycle}
        onIonModalWillPresent={this.onLifecycle}
        onIonModalWillDismiss={this.onLifecycle}
        onIonModalDidDismiss={this.onLifecycle}
      >
        <ion-backdrop
          ref={(el) => (this.backdropEl = el)}
          visible={this.showBackdrop}
          tappable={this.backdropDismiss}
          part="backdrop"
        />

        {mode === 'ios' && <div class="modal-shadow"></div>}

        <div role="dialog" class="modal-wrapper ion-overlay-wrapper" part="content" ref={(el) => (this.wrapperEl = el)}>
          {showHandle && (
            <button
              class="modal-handle"
              // Prevents the handle from receiving keyboard focus when it does not cycle
              tabIndex={!isHandleCycle ? -1 : 0}
              aria-label={isHandleCycle ? 'Activate to adjust the size of the dialog overlaying the screen' : null}
              onClick={this.onHandleClick}
              part="handle"
            ></button>
          )}
          <slot></slot>
        </div>
      </Host>
    );
  }
}

const LIFECYCLE_MAP: any = {
  ionModalDidPresent: 'ionViewDidEnter',
  ionModalWillPresent: 'ionViewWillEnter',
  ionModalWillDismiss: 'ionViewWillLeave',
  ionModalDidDismiss: 'ionViewDidLeave',
};

let modalIds = 0;


Review comment (suggestion):
We should remove the event listener if `!isHandleCycle`. I believe some screen readers will indicate that clicking the handle is supposed to do something if there's a click listener.

Code after:
    const isHandleCycle = handleBehavior === 'cycle';

    return (
      <Host
        no-router
        aria-modal="true"
        tabindex="-1"
        {...(htmlAttributes as any)}
        style={{
          zIndex: `${20000 + this.overlayIndex}`,
        }}
        class={{
          [mode]: true,
          ['modal-default']: !isCardModal && !isSheetModal,
          [`modal-card`]: isCardModal,
          [`modal-sheet`]: isSheetModal,
          'overlay-hidden': true,
          ...getClassMap(this.cssClass),
        }}
        id={modalId}
        onIonBackdropTap={this.onBackdropTap}
        onIonModalDidPresent={this.onLifecycle}
        onIonModalWillPresent={this.onLifecycle}
        onIonModalWillDismiss={this.onLifecycle}
        onIonModalDidDismiss={this.onLifecycle}
      >
        <ion-backdrop
          ref={(el) => (this.backdropEl = el)}
          visible={this.showBackdrop}
          tappable={this.backdropDismiss}
          part="backdrop"
        />

        {mode === 'ios' && <div class="modal-shadow"></div>}

        <div role="dialog" class="modal-wrapper ion-overlay-wrapper" part="content" ref={(el) => (this.wrapperEl = el)}>
          {showHandle && (
            <button
              class="modal-handle"
              // Prevents the handle from receiving keyboard focus when it does not cycle
              tabIndex={!isHandleCycle ? -1 : 0}
              aria-label="Activate to adjust the size of the dialog overlaying the screen"
              onClick={isHandleCycle ? this.onHandleClick : undefined}
              part="handle"
            ></button>
          )}
          <slot></slot>
        </div>
      </Host>
    );
  }