diff --git a/core/src/components/action-sheet/action-sheet.tsx b/core/src/components/action-sheet/action-sheet.tsx index 9a877054cf..0c94a9410f 100644 --- a/core/src/components/action-sheet/action-sheet.tsx +++ b/core/src/components/action-sheet/action-sheet.tsx @@ -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 type { Gesture } from '@utils/gesture'; import { createButtonActiveGesture } from '@utils/gesture/button-active'; +import { raf } from '@utils/helpers'; import { BACKDROP, createDelegateController, @@ -318,25 +319,32 @@ export class ActionSheet implements ComponentInterface, OverlayInterface { componentDidLoad() { /** - * Do not create gesture if: - * 1. A gesture already exists - * 2. App is running in MD mode - * 3. A wrapper ref does not exist + * Only create gesture if: + * 1. A gesture does not already exist + * 2. App is running in iOS mode + * 3. A wrapper ref exists + * 4. A group ref exists */ const { groupEl, wrapperEl } = this; - if (this.gesture || getIonMode(this) === 'md' || !wrapperEl || !groupEl) { - return; + if (!this.gesture && getIonMode(this) === 'ios' && wrapperEl && groupEl) { + 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 (!isScrollable) { - this.gesture = createButtonActiveGesture(wrapperEl, (refEl: HTMLElement) => - refEl.classList.contains('action-sheet-button') - ); - this.gesture.enable(true); - } - }); + /** + * If action sheet was rendered with isOpen="true" + * then we should open action sheet immediately. + */ + if (this.isOpen === true) { + raf(() => this.present()); + } } render() { diff --git a/core/src/components/action-sheet/test/is-open/action-sheet.e2e.ts b/core/src/components/action-sheet/test/is-open/action-sheet.e2e.ts index 66eca8e6f0..512921485e 100644 --- a/core/src/components/action-sheet/test/is-open/action-sheet.e2e.ts +++ b/core/src/components/action-sheet/test/is-open/action-sheet.e2e.ts @@ -30,5 +30,10 @@ configs({ directions: ['ltr'], modes: ['ios'] }).forEach(({ config, title }) => await expect(actionSheet).toBeHidden(); }); + + test('should open if isOpen is true on load', async ({ page }) => { + await page.setContent('', config); + await expect(page.locator('ion-action-sheet')).toBeVisible(); + }); }); }); diff --git a/core/src/components/alert/alert.tsx b/core/src/components/alert/alert.tsx index 8ce5094492..0c67665bf9 100644 --- a/core/src/components/alert/alert.tsx +++ b/core/src/components/alert/alert.tsx @@ -3,6 +3,7 @@ import { Component, Element, Event, Host, Listen, Method, Prop, Watch, forceUpda import { ENABLE_HTML_CONTENT_DEFAULT } from '@utils/config'; import type { Gesture } from '@utils/gesture'; import { createButtonActiveGesture } from '@utils/gesture/button-active'; +import { raf } from '@utils/helpers'; import { createDelegateController, createTriggerController, @@ -346,19 +347,25 @@ export class Alert implements ComponentInterface, OverlayInterface { componentDidLoad() { /** - * Do not create gesture if: - * 1. A gesture already exists - * 2. App is running in MD mode - * 3. A wrapper ref does not exist + * Only create gesture if: + * 1. A gesture does not already exist + * 2. App is running in iOS mode + * 3. A wrapper ref exists */ - if (this.gesture || getIonMode(this) === 'md' || !this.wrapperEl) { - return; + if (!this.gesture && getIonMode(this) === 'ios' && this.wrapperEl) { + 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') - ); - this.gesture.enable(true); + /** + * If alert was rendered with isOpen="true" + * then we should open alert immediately. + */ + if (this.isOpen === true) { + raf(() => this.present()); + } } /** diff --git a/core/src/components/alert/test/is-open/alert.e2e.ts b/core/src/components/alert/test/is-open/alert.e2e.ts index 5fa7980c7e..84fadf6997 100644 --- a/core/src/components/alert/test/is-open/alert.e2e.ts +++ b/core/src/components/alert/test/is-open/alert.e2e.ts @@ -29,5 +29,10 @@ configs({ directions: ['ltr'], modes: ['ios'] }).forEach(({ config, title }) => await ionAlertDidDismiss.next(); await expect(alert).toBeHidden(); }); + + test('should open if isOpen is true on load', async ({ page }) => { + await page.setContent('', config); + await expect(page.locator('ion-alert')).toBeVisible(); + }); }); }); diff --git a/core/src/components/loading/test/is-open/loading.e2e.ts b/core/src/components/loading/test/is-open/loading.e2e.ts index 9a3020b2d0..06bfdcacb9 100644 --- a/core/src/components/loading/test/is-open/loading.e2e.ts +++ b/core/src/components/loading/test/is-open/loading.e2e.ts @@ -23,5 +23,10 @@ configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, config }) => await ionLoadingDidDismiss.next(); await expect(loading).toBeHidden(); }); + + test('should open if isOpen is true on load', async ({ page }) => { + await page.setContent('', config); + await expect(page.locator('ion-loading')).toBeVisible(); + }); }); }); diff --git a/core/src/components/modal/test/is-open/modal.e2e.ts b/core/src/components/modal/test/is-open/modal.e2e.ts index b56e0333d2..651505f43f 100644 --- a/core/src/components/modal/test/is-open/modal.e2e.ts +++ b/core/src/components/modal/test/is-open/modal.e2e.ts @@ -20,5 +20,10 @@ configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, config }) => await ionModalDidDismiss.next(); await expect(modal).toBeHidden(); }); + + test('should open if isOpen is true on load', async ({ page }) => { + await page.setContent('', config); + await expect(page.locator('ion-modal')).toBeVisible(); + }); }); }); diff --git a/core/src/components/picker/picker.tsx b/core/src/components/picker/picker.tsx index 66eeb0aa0c..9f1f587086 100644 --- a/core/src/components/picker/picker.tsx +++ b/core/src/components/picker/picker.tsx @@ -1,5 +1,6 @@ import type { ComponentInterface, EventEmitter } from '@stencil/core'; import { Component, Element, Event, Host, Method, Prop, State, Watch, h } from '@stencil/core'; +import { raf } from '@utils/helpers'; import { createDelegateController, createTriggerController, @@ -199,6 +200,16 @@ export class Picker implements ComponentInterface, OverlayInterface { 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. */ diff --git a/core/src/components/picker/test/is-open/picker.e2e.ts b/core/src/components/picker/test/is-open/picker.e2e.ts index ab977cdb43..026da1abfd 100644 --- a/core/src/components/picker/test/is-open/picker.e2e.ts +++ b/core/src/components/picker/test/is-open/picker.e2e.ts @@ -23,5 +23,10 @@ configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, config }) => await ionPickerDidDismiss.next(); await expect(picker).toBeHidden(); }); + + test('should open if isOpen is true on load', async ({ page }) => { + await page.setContent('', config); + await expect(page.locator('ion-picker')).toBeVisible(); + }); }); }); diff --git a/core/src/components/popover/test/is-open/popover.e2e.ts b/core/src/components/popover/test/is-open/popover.e2e.ts new file mode 100644 index 0000000000..cd3604a2fb --- /dev/null +++ b/core/src/components/popover/test/is-open/popover.e2e.ts @@ -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('', config); + await expect(page.locator('ion-popover')).toBeVisible(); + }); + }); +}); diff --git a/core/src/components/toast/test/is-open/toast.e2e.ts b/core/src/components/toast/test/is-open/toast.e2e.ts index 2cb6daa4bf..8d55e1b192 100644 --- a/core/src/components/toast/test/is-open/toast.e2e.ts +++ b/core/src/components/toast/test/is-open/toast.e2e.ts @@ -24,5 +24,10 @@ configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, config }) => await ionToastDidDismiss.next(); await expect(toast).toBeHidden(); }); + + test('should open if isOpen is true on load', async ({ page }) => { + await page.setContent('', config); + await expect(page.locator('ion-toast')).toBeVisible(); + }); }); }); diff --git a/core/src/components/toast/toast.tsx b/core/src/components/toast/toast.tsx index 4f397d94a7..f0efc640ad 100644 --- a/core/src/components/toast/toast.tsx +++ b/core/src/components/toast/toast.tsx @@ -1,6 +1,7 @@ import type { ComponentInterface, EventEmitter } 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 { raf } from '@utils/helpers'; import { printIonWarning } from '@utils/logging'; import { createDelegateController, @@ -253,6 +254,16 @@ export class Toast implements ComponentInterface, OverlayInterface { 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. */