fix(many): overlays present if isOpen is true on load (#27933)

Issue number: resolves #27928 

---------

<!-- Please do not submit updates to dependencies unless it fixes an
issue. -->

<!-- Please try to limit your pull request to one type (bugfix, feature,
etc). Submit multiple pull requests if needed. -->

## What is the current behavior?
<!-- Please describe the current behavior that you are modifying. -->

Action sheet, alert, picker, and toast did not have logic where the
overlay presents if `isOpen="true"` on load. Modal, popover, and loading
had this logic but did not have test coverage.

## What is the new behavior?
<!-- Please describe the behavior or changes that are being added by
this PR. -->

- Updated action sheet, alert, picker, and toast to present if
`isOpen="true"` on load
- Added test coverage to all overlays for this functionality.

## Does this introduce a breaking change?

- [ ] Yes
- [x] No

<!-- If this introduces a breaking change, please describe the impact
and migration path for existing applications below. -->


## Other information

<!-- Any other information that is important to this PR such as
screenshots of how the component looks before and after the change. -->

Dev build: `7.2.3-dev.11691156718.1638345c`
This commit is contained in:
Liam DeBeasi
2023-08-04 13:00:21 -04:00
committed by GitHub
parent da55ab949e
commit a0e6ac6013
11 changed files with 103 additions and 25 deletions

View File

@ -2,6 +2,7 @@ import type { ComponentInterface, EventEmitter } from '@stencil/core';
import { Watch, Component, Element, Event, Host, Method, Prop, h, readTask } from '@stencil/core'; import { Watch, Component, Element, Event, Host, Method, Prop, h, readTask } from '@stencil/core';
import type { Gesture } from '@utils/gesture'; import type { Gesture } from '@utils/gesture';
import { createButtonActiveGesture } from '@utils/gesture/button-active'; import { createButtonActiveGesture } from '@utils/gesture/button-active';
import { raf } from '@utils/helpers';
import { import {
BACKDROP, BACKDROP,
createDelegateController, createDelegateController,
@ -318,25 +319,32 @@ export class ActionSheet implements ComponentInterface, OverlayInterface {
componentDidLoad() { componentDidLoad() {
/** /**
* Do not create gesture if: * Only create gesture if:
* 1. A gesture already exists * 1. A gesture does not already exist
* 2. App is running in MD mode * 2. App is running in iOS mode
* 3. A wrapper ref does not exist * 3. A wrapper ref exists
* 4. A group ref exists
*/ */
const { groupEl, wrapperEl } = this; const { groupEl, wrapperEl } = this;
if (this.gesture || getIonMode(this) === 'md' || !wrapperEl || !groupEl) { if (!this.gesture && getIonMode(this) === 'ios' && wrapperEl && groupEl) {
return; readTask(() => {
const isScrollable = groupEl.scrollHeight > groupEl.clientHeight;
if (!isScrollable) {
this.gesture = createButtonActiveGesture(wrapperEl, (refEl: HTMLElement) =>
refEl.classList.contains('action-sheet-button')
);
this.gesture.enable(true);
}
});
} }
readTask(() => { /**
const isScrollable = groupEl.scrollHeight > groupEl.clientHeight; * If action sheet was rendered with isOpen="true"
if (!isScrollable) { * then we should open action sheet immediately.
this.gesture = createButtonActiveGesture(wrapperEl, (refEl: HTMLElement) => */
refEl.classList.contains('action-sheet-button') if (this.isOpen === true) {
); raf(() => this.present());
this.gesture.enable(true); }
}
});
} }
render() { render() {

View File

@ -30,5 +30,10 @@ configs({ directions: ['ltr'], modes: ['ios'] }).forEach(({ config, title }) =>
await expect(actionSheet).toBeHidden(); await expect(actionSheet).toBeHidden();
}); });
test('should open if isOpen is true on load', async ({ page }) => {
await page.setContent('<ion-action-sheet is-open="true"></ion-action-sheet>', config);
await expect(page.locator('ion-action-sheet')).toBeVisible();
});
}); });
}); });

View File

@ -3,6 +3,7 @@ import { Component, Element, Event, Host, Listen, Method, Prop, Watch, forceUpda
import { ENABLE_HTML_CONTENT_DEFAULT } from '@utils/config'; import { ENABLE_HTML_CONTENT_DEFAULT } from '@utils/config';
import type { Gesture } from '@utils/gesture'; import type { Gesture } from '@utils/gesture';
import { createButtonActiveGesture } from '@utils/gesture/button-active'; import { createButtonActiveGesture } from '@utils/gesture/button-active';
import { raf } from '@utils/helpers';
import { import {
createDelegateController, createDelegateController,
createTriggerController, createTriggerController,
@ -346,19 +347,25 @@ export class Alert implements ComponentInterface, OverlayInterface {
componentDidLoad() { componentDidLoad() {
/** /**
* Do not create gesture if: * Only create gesture if:
* 1. A gesture already exists * 1. A gesture does not already exist
* 2. App is running in MD mode * 2. App is running in iOS mode
* 3. A wrapper ref does not exist * 3. A wrapper ref exists
*/ */
if (this.gesture || getIonMode(this) === 'md' || !this.wrapperEl) { if (!this.gesture && getIonMode(this) === 'ios' && this.wrapperEl) {
return; this.gesture = createButtonActiveGesture(this.wrapperEl, (refEl: HTMLElement) =>
refEl.classList.contains('alert-button')
);
this.gesture.enable(true);
} }
this.gesture = createButtonActiveGesture(this.wrapperEl, (refEl: HTMLElement) => /**
refEl.classList.contains('alert-button') * If alert was rendered with isOpen="true"
); * then we should open alert immediately.
this.gesture.enable(true); */
if (this.isOpen === true) {
raf(() => this.present());
}
} }
/** /**

View File

@ -29,5 +29,10 @@ configs({ directions: ['ltr'], modes: ['ios'] }).forEach(({ config, title }) =>
await ionAlertDidDismiss.next(); await ionAlertDidDismiss.next();
await expect(alert).toBeHidden(); await expect(alert).toBeHidden();
}); });
test('should open if isOpen is true on load', async ({ page }) => {
await page.setContent('<ion-alert is-open="true"></ion-alert>', config);
await expect(page.locator('ion-alert')).toBeVisible();
});
}); });
}); });

View File

@ -23,5 +23,10 @@ configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, config }) =>
await ionLoadingDidDismiss.next(); await ionLoadingDidDismiss.next();
await expect(loading).toBeHidden(); await expect(loading).toBeHidden();
}); });
test('should open if isOpen is true on load', async ({ page }) => {
await page.setContent('<ion-loading is-open="true"></ion-loading>', config);
await expect(page.locator('ion-loading')).toBeVisible();
});
}); });
}); });

View File

@ -20,5 +20,10 @@ configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, config }) =>
await ionModalDidDismiss.next(); await ionModalDidDismiss.next();
await expect(modal).toBeHidden(); await expect(modal).toBeHidden();
}); });
test('should open if isOpen is true on load', async ({ page }) => {
await page.setContent('<ion-modal is-open="true"></ion-modal>', config);
await expect(page.locator('ion-modal')).toBeVisible();
});
}); });
}); });

View File

@ -1,5 +1,6 @@
import type { ComponentInterface, EventEmitter } from '@stencil/core'; import type { ComponentInterface, EventEmitter } from '@stencil/core';
import { Component, Element, Event, Host, Method, Prop, State, Watch, h } from '@stencil/core'; import { Component, Element, Event, Host, Method, Prop, State, Watch, h } from '@stencil/core';
import { raf } from '@utils/helpers';
import { import {
createDelegateController, createDelegateController,
createTriggerController, createTriggerController,
@ -199,6 +200,16 @@ export class Picker implements ComponentInterface, OverlayInterface {
setOverlayId(this.el); setOverlayId(this.el);
} }
componentDidLoad() {
/**
* If picker was rendered with isOpen="true"
* then we should open picker immediately.
*/
if (this.isOpen === true) {
raf(() => this.present());
}
}
/** /**
* Present the picker overlay after it has been created. * Present the picker overlay after it has been created.
*/ */

View File

@ -23,5 +23,10 @@ configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, config }) =>
await ionPickerDidDismiss.next(); await ionPickerDidDismiss.next();
await expect(picker).toBeHidden(); await expect(picker).toBeHidden();
}); });
test('should open if isOpen is true on load', async ({ page }) => {
await page.setContent('<ion-picker is-open="true"></ion-picker>', config);
await expect(page.locator('ion-picker')).toBeVisible();
});
}); });
}); });

View File

@ -0,0 +1,11 @@
import { expect } from '@playwright/test';
import { configs, test } from '@utils/test/playwright';
configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, config }) => {
test.describe(title('popover: isOpen'), () => {
test('should open if isOpen is true on load', async ({ page }) => {
await page.setContent('<ion-popover is-open="true"></ion-popover>', config);
await expect(page.locator('ion-popover')).toBeVisible();
});
});
});

View File

@ -24,5 +24,10 @@ configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, config }) =>
await ionToastDidDismiss.next(); await ionToastDidDismiss.next();
await expect(toast).toBeHidden(); await expect(toast).toBeHidden();
}); });
test('should open if isOpen is true on load', async ({ page }) => {
await page.setContent('<ion-toast is-open="true"></ion-toast>', config);
await expect(page.locator('ion-toast')).toBeVisible();
});
}); });
}); });

View File

@ -1,6 +1,7 @@
import type { ComponentInterface, EventEmitter } from '@stencil/core'; import type { ComponentInterface, EventEmitter } from '@stencil/core';
import { State, Watch, Component, Element, Event, h, Host, Method, Prop } from '@stencil/core'; import { State, Watch, Component, Element, Event, h, Host, Method, Prop } from '@stencil/core';
import { ENABLE_HTML_CONTENT_DEFAULT } from '@utils/config'; import { ENABLE_HTML_CONTENT_DEFAULT } from '@utils/config';
import { raf } from '@utils/helpers';
import { printIonWarning } from '@utils/logging'; import { printIonWarning } from '@utils/logging';
import { import {
createDelegateController, createDelegateController,
@ -253,6 +254,16 @@ export class Toast implements ComponentInterface, OverlayInterface {
setOverlayId(this.el); setOverlayId(this.el);
} }
componentDidLoad() {
/**
* If toast was rendered with isOpen="true"
* then we should open toast immediately.
*/
if (this.isOpen === true) {
raf(() => this.present());
}
}
/** /**
* Present the toast overlay after it has been created. * Present the toast overlay after it has been created.
*/ */