Skip to content

Commit

Permalink
chore(): sync feature-6.2 with main
Browse files Browse the repository at this point in the history
  • Loading branch information
liamdebeasi committed Jul 27, 2022
2 parents 4997331 + 57a21ad commit 4d10ae6
Show file tree
Hide file tree
Showing 8 changed files with 237 additions and 17 deletions.
14 changes: 7 additions & 7 deletions core/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
"@stencil/angular-output-target": "^0.4.0",
"@stencil/react-output-target": "^0.2.1",
"@stencil/sass": "^1.5.2",
"@stencil/vue-output-target": "^0.6.1",
"@stencil/vue-output-target": "^0.6.2",
"@types/jest": "^26.0.20",
"@types/node": "^14.6.0",
"@types/swiper": "5.4.0",
Expand Down
7 changes: 7 additions & 0 deletions core/src/components/datetime/datetime.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1128,6 +1128,13 @@ export class Datetime implements ComponentInterface {

this.initializeListeners();

/**
* The month/year picker from the date interface
* should be closed as it is not available in non-date
* interfaces.
*/
this.showMonthAndYear = false;

raf(() => {
this.ionRender.emit();
});
Expand Down
21 changes: 21 additions & 0 deletions core/src/components/datetime/test/presentation/datetime.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,27 @@ test.describe('datetime: presentation', () => {

expect(ionChangeSpy.length).toBe(1);
});

test('switching presentation should close month/year picker', async ({ page }, testInfo) => {
await test.skip(testInfo.project.metadata.rtl === true, 'This feature does not have RTL specific behaviors.');

await page.setContent(`
<ion-datetime presentation="date"></ion-datetime>
`);

await page.waitForSelector('.datetime-ready');

const datetime = page.locator('ion-datetime');
const monthYearButton = page.locator('ion-datetime .calendar-month-year');
await monthYearButton.click();

await expect(datetime).toHaveClass(/show-month-and-year/);

await datetime.evaluate((el: HTMLIonDatetimeElement) => (el.presentation = 'time'));
await page.waitForChanges();

await expect(datetime).not.toHaveClass(/show-month-and-year/);
});
});

test.describe('datetime: presentation: time', () => {
Expand Down
20 changes: 16 additions & 4 deletions core/src/components/nav/nav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,10 @@ export class Nav implements NavOutlet {
this.swipeGestureChanged();
}

connectedCallback() {
this.destroyed = false;
}

disconnectedCallback() {
for (const view of this.views) {
lifecycle(view.element!, LIFECYCLE_WILL_UNLOAD);
Expand Down Expand Up @@ -879,9 +883,13 @@ export class Nav implements NavOutlet {
leavingView: ViewController | undefined,
opts: NavOptions
): NavResult {
const cleanupView = hasCompleted ? enteringView : leavingView;
if (cleanupView) {
this.cleanup(cleanupView);
/**
* If the transition did not complete, the leavingView will still be the active
* view on the stack. Otherwise unmount all the views after the enteringView.
*/
const activeView = hasCompleted ? enteringView : leavingView;
if (activeView) {
this.unmountInactiveViews(activeView);
}

return {
Expand Down Expand Up @@ -944,9 +952,13 @@ export class Nav implements NavOutlet {
}

/**
* Unmounts all inactive views after the specified active view.
*
* DOM WRITE
*
* @param activeView The view that is actively visible in the stack. Used to calculate which views to unmount.
*/
private cleanup(activeView: ViewController) {
private unmountInactiveViews(activeView: ViewController) {
// ok, cleanup time!! Destroy all of the views that are
// INACTIVE and come after the active view
// only do this if the views exist, though
Expand Down
103 changes: 103 additions & 0 deletions core/src/components/nav/test/modal-navigation/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="UTF-8" />
<title>Nav - Modal Navigation</title>
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"
/>
<link href="../../../../../css/ionic.bundle.css" rel="stylesheet" />
<link href="../../../../../scripts/testing/styles.css" rel="stylesheet" />
<script src="../../../../../scripts/testing/scripts.js"></script>
<script nomodule src="../../../../../dist/ionic/ionic.js"></script>
<script type="module" src="../../../../../dist/ionic/ionic.esm.js"></script>
</head>

<body>
<ion-app>
<ion-header>
<ion-toolbar>
<ion-title>Modal Navigation</ion-title>
</ion-toolbar>
</ion-header>
<ion-content>
<ion-button id="openModal">Open Modal</ion-button>
<ion-modal trigger="openModal">
<ion-header>
<ion-toolbar>
<ion-title>Modal</ion-title>
<ion-buttons slot="end">
<ion-button onclick="dismiss()"> Close </ion-button>
</ion-buttons>
</ion-toolbar>
</ion-header>
<ion-content>
<ion-nav></ion-nav>
</ion-content>
</ion-modal>
</ion-content>
</ion-app>

<script>
const modal = document.querySelector('ion-modal');
const nav = document.querySelector('ion-nav');

modal.addEventListener('willPresent', () => {
nav.setRoot('page-one');
});

const dismiss = () => modal.dismiss();

const navigate = (component, componentProps) => {
nav.push(component, componentProps);
};

const navigateBack = () => {
nav.pop();
};

const navigateToRoot = () => {
nav.popToRoot();
};

class PageOne extends HTMLElement {
connectedCallback() {
this.innerHTML = `
<ion-content class="ion-padding">
<h1>Page One</h1>
<ion-button id="goto-page-two" onclick="navigate('page-two')">Go to Page Two</ion-button>
</ion-content>
`;
}
}

class PageTwo extends HTMLElement {
connectedCallback() {
this.innerHTML = `
<ion-content class="ion-padding">
<h1>Page Two</h1>
<ion-button id="goto-page-three" onclick="navigate('page-three')">Go to Page Three</ion-button>
</ion-content>
`;
}
}

class PageThree extends HTMLElement {
connectedCallback() {
this.innerHTML = `
<ion-content class="ion-padding">
<h1>Page Three</h1>
<ion-button id="go-back" onclick="navigateBack()">Go Back</ion-button>
<ion-button id="goto-root"onclick="navigateToRoot()">Go to Root</ion-button>
</ion-content>
`;
}
}

customElements.define('page-one', PageOne);
customElements.define('page-two', PageTwo);
customElements.define('page-three', PageThree);
</script>
</body>
</html>
77 changes: 77 additions & 0 deletions core/src/components/nav/test/modal-navigation/nav.e2e.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { expect } from '@playwright/test';
import type { E2EPage } from '@utils/test/playwright';
import { test } from '@utils/test/playwright';

test.describe('nav: modal-navigation', () => {
test.beforeEach(async ({ page }) => {
await page.goto(`/src/components/nav/test/modal-navigation`);
await openModal(page);
});

test('should render the root page', async ({ page }) => {
const pageOne = page.locator('page-one');
const pageOneHeading = page.locator('page-one h1');

await expect(pageOne).toBeVisible();
await expect(pageOneHeading).toHaveText('Page One');
});

test('should push to the next page', async ({ page }) => {
await page.click('#goto-page-two');

const pageTwo = page.locator('page-two');
const pageTwoHeading = page.locator('page-two h1');

await expect(pageTwo).toBeVisible();
await expect(pageTwoHeading).toHaveText('Page Two');
});

test('should pop to the previous page', async ({ page }) => {
await page.click('#goto-page-two');
await page.click('#goto-page-three');

const pageThree = page.locator('page-three');
const pageThreeHeading = page.locator('page-three h1');

await expect(pageThree).toBeVisible();
await expect(pageThreeHeading).toHaveText('Page Three');

await page.click('#go-back');

const pageTwo = page.locator('page-two');
const pageTwoHeading = page.locator('page-two h1');

// Verifies the leavingView was unmounted
await expect(pageThree).toHaveCount(0);
await expect(pageTwo).toBeVisible();
await expect(pageTwoHeading).toHaveText('Page Two');
});

test.describe('popping to the root', () => {
test('should render the root page', async ({ page }) => {
const pageTwo = page.locator('page-two');
const pageThree = page.locator('page-three');

await page.click('#goto-page-two');
await page.click('#goto-page-three');

await page.click('#goto-root');

const pageOne = page.locator('page-one');
const pageOneHeading = page.locator('page-one h1');

// Verifies all views besides the root were unmounted
await expect(pageTwo).toHaveCount(0);
await expect(pageThree).toHaveCount(0);

await expect(pageOne).toBeVisible();
await expect(pageOneHeading).toHaveText('Page One');
});
});
});

const openModal = async (page: E2EPage) => {
const ionModalDidPresent = await page.spyOnEvent('ionModalDidPresent');
await page.click('#openModal');
await ionModalDidPresent.next();
};
10 changes: 5 additions & 5 deletions packages/vue/src/vue-component-lib/utils.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { VNode, defineComponent, getCurrentInstance, h, inject, ref, Ref } from 'vue';

export interface InputProps {
modelValue?: string | boolean;
export interface InputProps<T> {
modelValue?: T;
}

const UPDATE_VALUE_EVENT = 'update:modelValue';
Expand Down Expand Up @@ -49,7 +49,7 @@ const getElementClasses = (ref: Ref<HTMLElement | undefined>, componentClasses:
* @prop externalModelUpdateEvent - The external event to fire from your Vue component when modelUpdateEvent fires. This is used for ensuring that v-model references have been
* correctly updated when a user's event callback fires.
*/
export const defineContainer = <Props>(
export const defineContainer = <Props, VModelType=string|number|boolean>(
name: string,
defineCustomElement: any,
componentProps: string[] = [],
Expand All @@ -67,7 +67,7 @@ export const defineContainer = <Props>(
defineCustomElement();
}

const Container = defineComponent<Props & InputProps>((props: any, { attrs, slots, emit }) => {
const Container = defineComponent<Props & InputProps<VModelType>>((props: any, { attrs, slots, emit }) => {
let modelPropValue = props[modelProp];
const containerRef = ref<HTMLElement>();
const classes = new Set(getComponentClasses(attrs.class));
Expand All @@ -76,7 +76,7 @@ export const defineContainer = <Props>(
if (vnode.el) {
const eventsNames = Array.isArray(modelUpdateEvent) ? modelUpdateEvent : [modelUpdateEvent];
eventsNames.forEach((eventName: string) => {
vnode.el.addEventListener(eventName.toLowerCase(), (e: Event) => {
vnode.el!.addEventListener(eventName.toLowerCase(), (e: Event) => {
modelPropValue = (e?.target as any)[modelProp];
emit(UPDATE_VALUE_EVENT, modelPropValue);

Expand Down

0 comments on commit 4d10ae6

Please sign in to comment.