Merge remote-tracking branch 'origin/main' into chore/sync-with-main-5-3

This commit is contained in:
Sean Perkins
2023-05-03 13:38:03 -04:00
2722 changed files with 42463 additions and 9932 deletions

View File

@ -47,6 +47,8 @@
outline: none;
color: $modal-text-color;
contain: strict;
}

View File

@ -4,6 +4,7 @@ import { findIonContent, printIonContentErrorMsg } from '@utils/content';
import { CoreDelegate, attachComponent, detachComponent } from '@utils/framework-delegate';
import { raf, inheritAttributes, hasLazyBuild } from '@utils/helpers';
import type { Attributes } from '@utils/helpers';
import { KEYBOARD_DID_OPEN } from '@utils/keyboard/keyboard';
import { printIonWarning } from '@utils/logging';
import { Style as StatusBarStyle, StatusBar } from '@utils/native/status-bar';
import {
@ -16,8 +17,9 @@ import {
present,
createTriggerController,
} from '@utils/overlays';
import type { OverlayEventDetail } from '@utils/overlays-interface';
import { getClassMap } from '@utils/theme';
import { deepReady } from '@utils/transition';
import { deepReady, waitForMount } from '@utils/transition';
import { config } from '../../global/config';
import { getIonMode } from '../../global/ionic-global';
@ -30,8 +32,6 @@ import type {
Gesture,
OverlayInterface,
} from '../../interface';
import { KEYBOARD_DID_OPEN } from '../../utils/keyboard/keyboard';
import type { OverlayEventDetail } from '../../utils/overlays-interface';
import { iosEnterAnimation } from './animations/ios.enter';
import { iosLeaveAnimation } from './animations/ios.leave';
@ -316,6 +316,16 @@ export class Modal implements ComponentInterface, OverlayInterface {
*/
@Event({ eventName: 'didDismiss' }) didDismissShorthand!: EventEmitter<OverlayEventDetail>;
/**
* Emitted before the modal has presented, but after the component
* has been mounted in the DOM.
* This event exists so iOS can run the entering
* transition properly
*
* @internal
*/
@Event() ionMount!: EventEmitter<void>;
breakpointsChanged(breakpoints: number[] | undefined) {
if (breakpoints !== undefined) {
this.sortedBreakpoints = breakpoints.sort((a, b) => a - b);
@ -443,7 +453,30 @@ export class Modal implements ComponentInterface, OverlayInterface {
const { inline, delegate } = this.getDelegate(true);
this.usersElement = await attachComponent(delegate, el, this.component, ['ion-page'], this.componentProps, inline);
hasLazyBuild(el) && (await deepReady(this.usersElement));
this.ionMount.emit();
/**
* When using the lazy loaded build of Stencil, we need to wait
* for every Stencil component instance to be ready before presenting
* otherwise there can be a flash of unstyled content. With the
* custom elements bundle we need to wait for the JS framework
* mount the inner contents of the overlay otherwise WebKit may
* get the transition incorrect.
*/
if (hasLazyBuild(el)) {
await deepReady(this.usersElement);
/**
* If keepContentsMounted="true" then the
* JS Framework has already mounted the inner
* contents so there is no need to wait.
* Otherwise, we need to wait for the JS
* Framework to mount the inner contents
* of this component.
*/
} else if (!this.keepContentsMounted) {
await waitForMount();
}
writeTask(() => this.el.classList.add('show-modal'));

View File

@ -20,3 +20,6 @@ $modal-inset-height-small: 500px !default;
/// @prop - Height of the large modal inset
$modal-inset-height-large: 600px !default;
/// @prop - Text color of the modal content
$modal-text-color: $text-color !default;

View File

@ -43,7 +43,7 @@ test.describe('modal: focus trapping', () => {
await page.goto('/src/components/modal/test/basic');
const ionModalDidPresent = await page.spyOnEvent('ionModalDidPresent');
const ionModalDidDismiss = await page.spyOnEvent('ionModalDidDismiss');
const modalButton = await page.locator('#basic-modal');
const modalButton = page.locator('#basic-modal');
const tabKey = browserName === 'webkit' ? 'Alt+Tab' : 'Tab';
// Focus #basic-modal button
@ -74,7 +74,7 @@ test.describe('modal: rendering', () => {
await ionModalWillPresent.next();
await ionModalDidPresent.next();
const modal = await page.locator('ion-modal');
const modal = page.locator('ion-modal');
await expect(modal).toHaveClass(/show-modal/);
await page.setIonViewport();
@ -112,7 +112,7 @@ test.describe('modal: htmlAttributes inheritance', () => {
await ionModalDidPresent.next();
const modal = await page.locator('ion-modal');
const modal = page.locator('ion-modal');
const attribute = await modal.getAttribute('data-testid');
expect(attribute).toBe('basic-modal');
@ -161,7 +161,7 @@ test.describe('modal: incorrect usage', () => {
await page.click('#basic-modal');
await ionModalDidPresent.next();
const modal = await page.locator('ion-modal');
const modal = page.locator('ion-modal');
await modal.evaluate((el: HTMLIonModalElement) => el.setCurrentBreakpoint(0.5));
expect(warnings.length).toBe(1);
@ -174,7 +174,7 @@ test.describe('modal: incorrect usage', () => {
await page.click('#basic-modal');
await ionModalDidPresent.next();
const modal = await page.locator('ion-modal');
const modal = page.locator('ion-modal');
const breakpoint = await modal.evaluate((el: HTMLIonModalElement) => {
return el.getCurrentBreakpoint();
});

View File

@ -5,7 +5,7 @@ test.describe('modal: canDismiss', () => {
test.describe('regular modal', () => {
test.beforeEach(async ({ page, skip }) => {
skip.rtl();
await page.goto('/src/components/modal/test/canDismiss');
await page.goto('/src/components/modal/test/can-dismiss');
});
test('should dismiss when canDismiss is true', async ({ page }) => {
const ionModalDidPresent = await page.spyOnEvent('ionModalDidPresent');
@ -14,7 +14,7 @@ test.describe('modal: canDismiss', () => {
await ionModalDidPresent.next();
const modal = await page.locator('ion-modal');
const modal = page.locator('ion-modal');
const returnValue = await modal.evaluate((el: HTMLIonModalElement) => el.dismiss());
expect(returnValue).toBe(true);
@ -27,7 +27,7 @@ test.describe('modal: canDismiss', () => {
await ionModalDidPresent.next();
const modal = await page.locator('ion-modal');
const modal = page.locator('ion-modal');
const returnValue = await modal.evaluate((el: HTMLIonModalElement) => el.dismiss());
expect(returnValue).toBe(false);
@ -40,7 +40,7 @@ test.describe('modal: canDismiss', () => {
await ionModalDidPresent.next();
const modal = await page.locator('ion-modal');
const modal = page.locator('ion-modal');
const returnValue = await modal.evaluate((el: HTMLIonModalElement) => el.dismiss());
expect(returnValue).toBe(true);
@ -53,7 +53,7 @@ test.describe('modal: canDismiss', () => {
await ionModalDidPresent.next();
const modal = await page.locator('ion-modal');
const modal = page.locator('ion-modal');
const returnValue = await modal.evaluate((el: HTMLIonModalElement) => el.dismiss());
expect(returnValue).toBe(false);
@ -80,7 +80,7 @@ test.describe('modal: canDismiss', () => {
test.beforeEach(async ({ page, skip }) => {
skip.rtl();
skip.mode('md');
await page.goto('/src/components/modal/test/canDismiss');
await page.goto('/src/components/modal/test/can-dismiss');
await page.click('#radio-card');
});
test('should dismiss when canDismiss is true', async ({ page }) => {
@ -90,7 +90,7 @@ test.describe('modal: canDismiss', () => {
await ionModalDidPresent.next();
const modal = await page.locator('ion-modal');
const modal = page.locator('ion-modal');
const returnValue = await modal.evaluate((el: HTMLIonModalElement) => el.dismiss());
expect(returnValue).toBe(true);
@ -103,7 +103,7 @@ test.describe('modal: canDismiss', () => {
await ionModalDidPresent.next();
const modal = await page.locator('ion-modal');
const modal = page.locator('ion-modal');
const returnValue = await modal.evaluate((el: HTMLIonModalElement) => el.dismiss());
expect(returnValue).toBe(false);
@ -116,7 +116,7 @@ test.describe('modal: canDismiss', () => {
await ionModalDidPresent.next();
const modal = await page.locator('ion-modal');
const modal = page.locator('ion-modal');
const returnValue = await modal.evaluate((el: HTMLIonModalElement) => el.dismiss());
expect(returnValue).toBe(true);
@ -129,7 +129,7 @@ test.describe('modal: canDismiss', () => {
await ionModalDidPresent.next();
const modal = await page.locator('ion-modal');
const modal = page.locator('ion-modal');
const returnValue = await modal.evaluate((el: HTMLIonModalElement) => el.dismiss());
expect(returnValue).toBe(false);
@ -139,7 +139,7 @@ test.describe('modal: canDismiss', () => {
test.beforeEach(async ({ page, skip }) => {
skip.rtl();
skip.mode('md');
await page.goto('/src/components/modal/test/canDismiss');
await page.goto('/src/components/modal/test/can-dismiss');
await page.click('#radio-card');
});
@ -151,7 +151,7 @@ test.describe('modal: canDismiss', () => {
await ionModalDidPresent.next();
const modalHeader = await page.locator('ion-modal #modal-header');
const modalHeader = page.locator('ion-modal #modal-header');
await dragElementBy(modalHeader, page, 0, 500);
await ionModalDidDismiss.next();
@ -164,10 +164,10 @@ test.describe('modal: canDismiss', () => {
await ionModalDidPresent.next();
const modalHeader = await page.locator('#modal-header');
const modalHeader = page.locator('#modal-header');
await dragElementBy(modalHeader, page, 0, 500);
const modal = await page.locator('ion-modal');
const modal = page.locator('ion-modal');
expect(modal).not.toBe(null);
});
test('should dismiss on swipe when canDismiss is Promise<true>', async ({ page }) => {
@ -179,7 +179,7 @@ test.describe('modal: canDismiss', () => {
await ionModalDidPresent.next();
const modalHeader = await page.locator('#modal-header');
const modalHeader = page.locator('#modal-header');
await dragElementBy(modalHeader, page, 0, 500);
await ionModalDidDismiss.next();
@ -193,12 +193,12 @@ test.describe('modal: canDismiss', () => {
await ionModalDidPresent.next();
const modalHeader = await page.locator('#modal-header');
const modalHeader = page.locator('#modal-header');
await dragElementBy(modalHeader, page, 0, 500);
await ionHandlerDone.next();
const modal = await page.locator('ion-modal');
const modal = page.locator('ion-modal');
expect(modal).not.toBe(null);
});
test('should dismiss when canDismiss is Action Sheet and user clicks confirm', async ({ page }) => {
@ -211,7 +211,7 @@ test.describe('modal: canDismiss', () => {
await ionModalDidPresent.next();
const modalHeader = await page.locator('#modal-header');
const modalHeader = page.locator('#modal-header');
await dragElementBy(modalHeader, page, 0, 500);
await ionActionSheetDidPresent.next();
@ -224,7 +224,7 @@ test.describe('modal: canDismiss', () => {
test.describe('sheet modal', () => {
test.beforeEach(async ({ page, skip }) => {
skip.rtl();
await page.goto('/src/components/modal/test/canDismiss');
await page.goto('/src/components/modal/test/can-dismiss');
await page.click('#radio-sheet');
});
test('should dismiss when canDismiss is true', async ({ page }) => {
@ -234,7 +234,7 @@ test.describe('modal: canDismiss', () => {
await ionModalDidPresent.next();
const modal = await page.locator('ion-modal');
const modal = page.locator('ion-modal');
const returnValue = await modal.evaluate((el: HTMLIonModalElement) => el.dismiss());
expect(returnValue).toBe(true);
@ -247,7 +247,7 @@ test.describe('modal: canDismiss', () => {
await ionModalDidPresent.next();
const modal = await page.locator('ion-modal');
const modal = page.locator('ion-modal');
const returnValue = await modal.evaluate((el: HTMLIonModalElement) => el.dismiss());
expect(returnValue).toBe(false);
@ -260,7 +260,7 @@ test.describe('modal: canDismiss', () => {
await ionModalDidPresent.next();
const modal = await page.locator('ion-modal');
const modal = page.locator('ion-modal');
const returnValue = await modal.evaluate((el: HTMLIonModalElement) => el.dismiss());
expect(returnValue).toBe(true);
@ -273,7 +273,7 @@ test.describe('modal: canDismiss', () => {
await ionModalDidPresent.next();
const modal = await page.locator('ion-modal');
const modal = page.locator('ion-modal');
const returnValue = await modal.evaluate((el: HTMLIonModalElement) => el.dismiss());
expect(returnValue).toBe(false);
@ -286,7 +286,7 @@ test.describe('modal: canDismiss', () => {
await ionModalDidPresent.next();
const modalHeader = await page.locator('#modal-header');
const modalHeader = page.locator('#modal-header');
await dragElementBy(modalHeader, page, 0, 500);
await ionModalDidDismiss.next();
@ -299,10 +299,10 @@ test.describe('modal: canDismiss', () => {
await ionModalDidPresent.next();
const modalHeader = await page.locator('#modal-header');
const modalHeader = page.locator('#modal-header');
await dragElementBy(modalHeader, page, 0, 500);
const modal = await page.locator('ion-modal');
const modal = page.locator('ion-modal');
expect(modal).not.toBe(null);
});
test('should dismiss on swipe when canDismiss is Promise<true>', async ({ page }) => {
@ -314,7 +314,7 @@ test.describe('modal: canDismiss', () => {
await ionModalDidPresent.next();
const modalHeader = await page.locator('#modal-header');
const modalHeader = page.locator('#modal-header');
await dragElementBy(modalHeader, page, 0, 500);
await ionModalDidDismiss.next();
@ -328,12 +328,12 @@ test.describe('modal: canDismiss', () => {
await ionModalDidPresent.next();
const modalHeader = await page.locator('#modal-header');
const modalHeader = page.locator('#modal-header');
await dragElementBy(modalHeader, page, 0, 500);
await ionHandlerDone.next();
const modal = await page.locator('ion-modal');
const modal = page.locator('ion-modal');
expect(modal).not.toBe(null);
});
test('should not dismiss on swipe when not attempting to close', async ({ page }) => {
@ -344,10 +344,10 @@ test.describe('modal: canDismiss', () => {
await ionModalDidPresent.next();
const modalHeader = await page.locator('#modal-header');
const modalHeader = page.locator('#modal-header');
await dragElementBy(modalHeader, page, 0, -500);
const modal = await page.locator('ion-modal');
const modal = page.locator('ion-modal');
expect(modal).not.toBe(null);
});
test('should hit the dismiss threshold when swiping', async ({ page }) => {
@ -359,7 +359,7 @@ test.describe('modal: canDismiss', () => {
await ionModalDidPresent.next();
const modalHeader = await page.locator('#modal-header');
const modalHeader = page.locator('#modal-header');
await dragElementBy(modalHeader, page, 0, 100);
await ionModalDidDismiss.next();
@ -374,7 +374,7 @@ test.describe('modal: canDismiss', () => {
await ionModalDidPresent.next();
const modalHeader = await page.locator('#modal-header');
const modalHeader = page.locator('#modal-header');
await dragElementBy(modalHeader, page, 0, 500);
await ionActionSheetDidPresent.next();
@ -388,7 +388,7 @@ test.describe('modal: canDismiss', () => {
test.beforeEach(async ({ page, skip }) => {
skip.mode('md');
skip.rtl();
await page.goto('/src/components/modal/test/canDismiss');
await page.goto('/src/components/modal/test/can-dismiss');
});
test('should pass data and role when calling dismiss', async ({ page }) => {
const ionModalDidPresent = await page.spyOnEvent('ionModalDidPresent');
@ -399,7 +399,7 @@ test.describe('modal: canDismiss', () => {
await ionModalDidPresent.next();
const modal = await page.locator('ion-modal');
const modal = page.locator('ion-modal');
await modal.evaluate((el: HTMLIonModalElement) => el.dismiss('my data', 'my role'));
await ionHandlerDone.next();
@ -415,7 +415,7 @@ test.describe('modal: canDismiss', () => {
await ionModalDidPresent.next();
const modalHeader = await page.locator('#modal-header');
const modalHeader = page.locator('#modal-header');
await dragElementBy(modalHeader, page, 0, 500);
await ionHandlerDone.next();

View File

@ -14,7 +14,7 @@ test.describe('card modal - with refresher', () => {
await page.click('#card');
await ionModalDidPresent.next();
const modal = await page.locator('ion-modal');
const modal = page.locator('ion-modal');
const content = (await page.$('ion-modal ion-content'))!;
await dragElementBy(content, page, 0, 500);

View File

@ -16,7 +16,7 @@ test.describe('card modal - scroll target', () => {
await page.click('#card');
await ionModalDidPresent.next();
const content = await page.locator('ion-modal .ion-content-scroll-host');
const content = page.locator('ion-modal .ion-content-scroll-host');
await dragElementBy(content, page, 0, 500);
await ionModalDidDismiss.next();
@ -27,7 +27,7 @@ test.describe('card modal - scroll target', () => {
await page.click('#card');
await ionModalDidPresent.next();
const modal = await page.locator('ion-modal');
const modal = page.locator('ion-modal');
const content = (await page.$('ion-modal .ion-content-scroll-host'))!;
await content.evaluate((el: HTMLElement) => (el.scrollTop = 500));
@ -44,7 +44,7 @@ test.describe('card modal - scroll target', () => {
await page.click('#card');
await ionModalDidPresent.next();
const content = await page.locator('ion-modal .ion-content-scroll-host');
const content = page.locator('ion-modal .ion-content-scroll-host');
await dragElementBy(content, page, 0, 20);
await expect(content).not.toHaveCSS('overflow', 'hidden');

View File

@ -82,7 +82,7 @@ test.describe('card modal', () => {
await cardModalPage.openModalByTrigger('#card');
await cardModalPage.swipeToCloseModal('ion-modal ion-content', false, 20);
const content = await page.locator('ion-modal ion-content');
const content = page.locator('ion-modal ion-content');
await expect(content).toHaveJSProperty('scrollY', true);
});
});
@ -157,7 +157,7 @@ test.describe('card modal', () => {
await cardModalPage.openModalByTrigger('#card');
await cardModalPage.swipeToCloseModal('ion-modal ion-content', false, 20);
const content = await page.locator('ion-modal ion-content');
const content = page.locator('ion-modal ion-content');
await expect(content).toHaveJSProperty('scrollY', true);
});
});

Some files were not shown because too many files have changed in this diff Show More