chore(): resolve merge conflicts

This commit is contained in:
Liam DeBeasi
2021-04-23 11:41:46 -04:00
120 changed files with 21206 additions and 2546 deletions

View File

@ -1,15 +1,15 @@
{
"name": "@ionic/vue",
"version": "5.6.3",
"version": "5.6.5",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@ionic/vue",
"version": "5.6.3",
"version": "5.6.5",
"license": "MIT",
"dependencies": {
"@ionic/core": "5.6.2",
"@ionic/core": "5.6.4",
"ionicons": "^5.1.2"
},
"devDependencies": {
@ -53,13 +53,13 @@
}
},
"node_modules/@ionic/core": {
"version": "5.6.2",
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-5.6.2.tgz",
"integrity": "sha512-hnwd6ln0IZUVfFu2ilZK03b6EdQFqEWiTkL5kayq2gjB3BK/u1IEtV3C9fdwc8NJKopGwdbdQnujj6VhYPzV3Q==",
"version": "5.6.4",
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-5.6.4.tgz",
"integrity": "sha512-fxCV/+0ibiaiBn1dsrrOmlLGJlTkqiG6IVXdLpPKimGdFLjy56olDvB5trlz9J5C/nHc7vR5MIiYC0qdTyX7og==",
"dependencies": {
"@stencil/core": "^2.4.0",
"ionicons": "^5.5.1",
"tslib": "^1.10.0"
"tslib": "^2.1.0"
}
},
"node_modules/@ionic/core/node_modules/@stencil/core": {
@ -74,6 +74,11 @@
"npm": ">=6.0.0"
}
},
"node_modules/@ionic/core/node_modules/tslib": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.2.0.tgz",
"integrity": "sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w=="
},
"node_modules/@stencil/core": {
"version": "1.17.3",
"resolved": "https://registry.npmjs.org/@stencil/core/-/core-1.17.3.tgz",
@ -536,7 +541,8 @@
"node_modules/tslib": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
"dev": true
},
"node_modules/typescript": {
"version": "4.0.5",
@ -627,19 +633,24 @@
}
},
"@ionic/core": {
"version": "5.6.2",
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-5.6.2.tgz",
"integrity": "sha512-hnwd6ln0IZUVfFu2ilZK03b6EdQFqEWiTkL5kayq2gjB3BK/u1IEtV3C9fdwc8NJKopGwdbdQnujj6VhYPzV3Q==",
"version": "5.6.4",
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-5.6.4.tgz",
"integrity": "sha512-fxCV/+0ibiaiBn1dsrrOmlLGJlTkqiG6IVXdLpPKimGdFLjy56olDvB5trlz9J5C/nHc7vR5MIiYC0qdTyX7og==",
"requires": {
"@stencil/core": "^2.4.0",
"ionicons": "^5.5.1",
"tslib": "^1.10.0"
"tslib": "^2.1.0"
},
"dependencies": {
"@stencil/core": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/@stencil/core/-/core-2.4.0.tgz",
"integrity": "sha512-gU6+Yyd6O0KrCSS/O6j8KKqmRo+/Dcs2fI0+APCpbAWK+nqhwDISpdnSEfGDCLMoAC08XOZCycBRk2K1VGnEcg=="
},
"tslib": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.2.0.tgz",
"integrity": "sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w=="
}
}
},
@ -1050,7 +1061,8 @@
"tslib": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
"dev": true
},
"typescript": {
"version": "4.0.5",

View File

@ -1,6 +1,6 @@
{
"name": "@ionic/vue",
"version": "5.6.3",
"version": "5.6.5",
"description": "Vue specific wrapper for @ionic/core",
"scripts": {
"lint": "echo add linter",
@ -57,7 +57,7 @@
"vue-router": "^4.0.0-rc.4"
},
"dependencies": {
"@ionic/core": "5.6.3",
"@ionic/core": "5.6.5",
"ionicons": "^5.1.2"
},
"vetur": {

View File

@ -40,16 +40,31 @@ export const IonRouterOutlet = defineComponent({
// The base url for this router outlet
let parentOutletPath: string;
watch(matchedRouteRef, (currentValue, previousValue) => {
/**
* We need to watch the route object
* to listen for navigation changes.
* Previously we had watched matchedRouteRef,
* but if you had a /page/:id route, going from
* page/1 to page/2 would not cause this callback
* to fire since the matchedRouteRef was the same.
*/
watch([route, matchedRouteRef], ([currentRoute, currentMatchedRouteRef], [_, previousMatchedRouteRef]) => {
/**
* We need to make sure that we are not re-rendering
* the same view if navigation changes in a sub-outlet.
* This is mainly for tabs when outlet 1 renders ion-tabs
* and outlet 2 renders the individual tab view. We don't
* want outlet 1 creating a new ion-tabs instance every time
* we switch tabs.
* If the matched route ref has changed,
* then we need to set up a new view item.
* If the matched route ref has not changed,
* it is possible that this is a parameterized URL
* change such as /page/1 to /page/2. In that case,
* we can assume that the `route` object has changed,
* but we should only set up a new view item in this outlet
* if that last matched view item matches our current matched
* view item otherwise if we had this in a nested outlet the
* parent outlet would re-render as well as the child page.
*/
if (currentValue !== previousValue) {
if (
currentMatchedRouteRef !== previousMatchedRouteRef ||
currentRoute.matched[currentRoute.matched.length - 1] === currentMatchedRouteRef
) {
setupViewItem(matchedRouteRef);
}
});

View File

@ -11,30 +11,53 @@ interface Tab {
ref: VNode
}
const isTabButton = (child: any) => child.type?.name === 'IonTabButton';
const getTabs = (nodes: VNode[]) => {
let tabs: VNode[] = [];
nodes.forEach((node: VNode) => {
if (isTabButton(node)) {
tabs.push(node);
} else if (Array.isArray(node.children) && node.children.length > 1) {
const childTabs = getTabs(node.children as VNode[]);
tabs = [...tabs, ...childTabs];
}
});
return tabs;
}
export const IonTabBar = defineComponent({
name: 'IonTabBar',
props: {
_tabsWillChange: { type: Function, default: () => {} },
_tabsDidChange: { type: Function, default: () => {} }
},
mounted() {
const ionRouter: any = inject('navManager');
const tabState: TabState = {
data() {
return {
tabState: {
activeTab: undefined,
tabs: {}
};
const currentInstance = getCurrentInstance();
const isTabButton = (child: any) => child.type?.name === 'IonTabButton';
/**
* For each tab, we need to keep track of its
* base href as well as any child page that
* is active in its stack so that when we go back
* to a tab from another tab, we can correctly
* show any child pages if necessary.
*/
const children = (currentInstance.subTree.children || []) as VNode[];
children.forEach((child: VNode) => {
if (isTabButton(child)) {
},
tabVnodes: []
}
},
updated() {
this.setupTabState(inject('navManager'));
},
methods: {
setupTabState(ionRouter: any) {
/**
* For each tab, we need to keep track of its
* base href as well as any child page that
* is active in its stack so that when we go back
* to a tab from another tab, we can correctly
* show any child pages if necessary.
*/
const tabState: TabState = this.$data.tabState;
const currentInstance = getCurrentInstance();
const tabs = this.$data.tabVnodes = getTabs((currentInstance.subTree.children || []) as VNode[]);
tabs.forEach(child => {
tabState.tabs[child.props.tab] = {
originalHref: child.props.href,
currentHref: child.props.href,
@ -47,12 +70,15 @@ export const IonTabBar = defineComponent({
* ion-tab-bar is managing for it.
*/
child.component.props._getTabState = () => tabState;
}
});
});
const checkActiveTab = (currentRoute: any) => {
const childNodes = (currentInstance.subTree.children || []) as VNode[];
const { tabs, activeTab: prevActiveTab } = tabState;
this.checkActiveTab(ionRouter);
},
checkActiveTab(ionRouter: any) {
const currentRoute = ionRouter.getCurrentRouteInfo();
const childNodes = this.$data.tabVnodes;
const { tabs, activeTab: prevActiveTab } = this.$data.tabState;
const tabState = this.$data.tabState;
const tabKeys = Object.keys(tabs);
const activeTab = tabKeys
.find(key => {
@ -66,21 +92,19 @@ export const IonTabBar = defineComponent({
* it in the tabs state.
*/
childNodes.forEach((child: VNode) => {
if (isTabButton(child)) {
const tab = tabs[child.props.tab];
if (!tab || (tab.originalHref !== child.props.href)) {
const tab = tabs[child.props.tab];
if (!tab || (tab.originalHref !== child.props.href)) {
tabs[child.props.tab] = {
originalHref: child.props.href,
currentHref: child.props.href,
ref: child
}
tabs[child.props.tab] = {
originalHref: child.props.href,
currentHref: child.props.href,
ref: child
}
}
});
if (activeTab && prevActiveTab) {
const prevHref = tabState.tabs[prevActiveTab].currentHref;
const prevHref = this.$data.tabState.tabs[prevActiveTab].currentHref;
/**
* If the tabs change or the url changes,
* update the currentHref for the active tab.
@ -128,10 +152,14 @@ export const IonTabBar = defineComponent({
tabBar.selectedTab = tabState.activeTab = '';
}
}
};
}
},
mounted() {
const ionRouter: any = inject('navManager');
ionRouter.registerHistoryChangeListener(checkActiveTab.bind(this));
checkActiveTab(ionRouter.getCurrentRouteInfo());
this.setupTabState(ionRouter);
ionRouter.registerHistoryChangeListener(() => this.checkActiveTab(ionRouter));
},
setup(_, { slots }) {
return () => {

View File

@ -1,37 +1,72 @@
import { defineComponent, h, ref, VNode, getCurrentInstance } from 'vue';
import { defineComponent, h, ref, VNode, getCurrentInstance, ComponentInternalInstance } from 'vue';
import { needsKebabCase } from '../utils';
export interface OverlayProps {
isOpen?: boolean;
}
/**
* Make sure we only
* warn user about each
* event at most once.
*/
let willPresentWarn = false;
let didPresentWarn = false;
let willDismissWarn = false;
let didDismissWarn = false;
const checkForDeprecatedListeners = (instance: ComponentInternalInstance) => {
const props = instance.vnode.props;
if (!willPresentWarn && props.onOnWillPresent !== undefined) {
console.warn('[@ionic/vue Deprecation]: @onWillPresent has been deprecated in favor of @willPresent and will be removed in Ionic Vue v6.0.');
willPresentWarn = true;
}
if (!didPresentWarn && props.onOnDidPresent !== undefined) {
console.warn('[@ionic/vue Deprecation]: @onDidPresent has been deprecated in favor of @didPresent and will be removed in Ionic Vue v6.0.');
didPresentWarn = true;
}
if (!willDismissWarn && props.onOnWillDismiss !== undefined) {
console.warn('[@ionic/vue Deprecation]: @onWillDismiss has been deprecated in favor of @willDismiss and will be removed in Ionic Vue v6.0.');
willDismissWarn = true;
}
if (!didDismissWarn && props.onOnDidDismiss !== undefined) {
console.warn('[@ionic/vue Deprecation]: @onDidDismiss has been deprecated in favor of @didDismiss and will be removed in Ionic Vue v6.0.');
didDismissWarn = true;
}
}
export const defineOverlayContainer = <Props extends object>(name: string, componentProps: string[] = [], controller: any) => {
/**
* Vue 3.0.6 fixed a bug where events on custom elements
* were always converted to lower case, so "ionRefresh"
* became "ionRefresh". We need to account for the old
* became "ionrefresh". We need to account for the old
* issue as well as the new behavior where "ionRefresh"
* is converted to "ion-refresh".
* See https://github.com/vuejs/vue-next/pull/2847
*/
const eventPrefix = name.toLowerCase().split('-').join('');
const lowerCaseListeners = [
{ componentEv: `${eventPrefix}willpresent`, frameworkEv: 'onWillPresent' },
{ componentEv: `${eventPrefix}didpresent`, frameworkEv: 'onDidPresent' },
{ componentEv: `${eventPrefix}willdismiss`, frameworkEv: 'onWillDismiss' },
{ componentEv: `${eventPrefix}diddismiss`, frameworkEv: 'onDidDismiss' },
{ componentEv: `${eventPrefix}willpresent`, frameworkEv: 'willPresent', deprecatedEv: 'onWillPresent' },
{ componentEv: `${eventPrefix}didpresent`, frameworkEv: 'didPresent', deprecatedEv: 'onDidPresent' },
{ componentEv: `${eventPrefix}willdismiss`, frameworkEv: 'willDismiss', deprecatedEv: 'onWillDismiss' },
{ componentEv: `${eventPrefix}diddismiss`, frameworkEv: 'didDismiss', deprecatedEv: 'onDidDismiss' },
];
const kebabCaseListeners = [
{ componentEv: `${name}-will-present`, frameworkEv: 'onWillPresent' },
{ componentEv: `${name}-did-present`, frameworkEv: 'onDidPresent' },
{ componentEv: `${name}-will-dismiss`, frameworkEv: 'onWillDismiss' },
{ componentEv: `${name}-did-dismiss`, frameworkEv: 'onDidDismiss' },
{ componentEv: `${name}-will-present`, frameworkEv: 'willPresent', deprecatedEv: 'onWillPresent' },
{ componentEv: `${name}-did-present`, frameworkEv: 'didPresent', deprecatedEv: 'onDidPresent' },
{ componentEv: `${name}-will-dismiss`, frameworkEv: 'willDismiss', deprecatedEv: 'onWillDismiss' },
{ componentEv: `${name}-did-dismiss`, frameworkEv: 'didDismiss', deprecatedEv: 'onDidDismiss' },
];
const Container = defineComponent<Props & OverlayProps>((props, { slots, emit }) => {
const instance = getCurrentInstance();
const adjustedEventListeners = needsKebabCase(instance.appContext.app.version) ? kebabCaseListeners : lowerCaseListeners;
checkForDeprecatedListeners(instance);
const overlay = ref();
const onVnodeMounted = async () => {
const isOpen = props.isOpen;
@ -85,9 +120,21 @@ export const defineOverlayContainer = <Props extends object>(name: string, compo
return;
}
/**
* When supporting both the "on" prefixed and non-"on" prefixed
* events, there seems to be an issue where the handlers are
* getting passed as props. This should be resolved when we drop
* support for the "on" prefixed listeners.
*/
const restOfProps = { ...(props as any) };
delete restOfProps.onWillPresent;
delete restOfProps.onDidPresent;
delete restOfProps.onWillDismiss;
delete restOfProps.onDidDismiss;
const component = slots.default && slots.default()[0];
overlay.value = controller.create({
...props,
...restOfProps,
component
});
@ -96,6 +143,7 @@ export const defineOverlayContainer = <Props extends object>(name: string, compo
adjustedEventListeners.forEach(eventListener => {
overlay.value.addEventListener(eventListener.componentEv, () => {
emit(eventListener.frameworkEv);
emit(eventListener.deprecatedEv);
});
})
@ -118,7 +166,10 @@ export const defineOverlayContainer = <Props extends object>(name: string, compo
Container.displayName = name;
Container.props = [...componentProps, 'isOpen'];
Container.emits = [...lowerCaseListeners.map(ev => ev.frameworkEv), ...kebabCaseListeners.map(ev => ev.frameworkEv)];
Container.emits = [
'willPresent', 'didPresent', 'willDismiss', 'didDismiss',
'onWillPresent', 'onDidPresent', 'onWillDismiss', 'onDidDismiss'
];
return Container;
}

View File

@ -2,5 +2,6 @@
"pluginsFile": "tests/e2e/plugins/index.js",
"includeShadowDom": true,
"video": false,
"screenshotOnRunFailure": false
"screenshotOnRunFailure": false,
"defaultCommandTimeout": 10000
}

View File

@ -46,7 +46,8 @@ const routes: Array<RouteRecordRaw> = [
},
{
path: '/routing/:id',
component: () => import('@/views/RoutingParameter.vue')
component: () => import('@/views/RoutingParameter.vue'),
props: true
},
{
path: '/routing/:id/view',
@ -71,7 +72,8 @@ const routes: Array<RouteRecordRaw> = [
},
{
path: ':id',
component: () => import('@/views/Folder.vue')
component: () => import('@/views/Folder.vue'),
props: true
}
]
},

View File

@ -5,19 +5,19 @@
<ion-buttons slot="start">
<ion-menu-button></ion-menu-button>
</ion-buttons>
<ion-title>{{ folder }}</ion-title>
<ion-title>{{ $props.id }}</ion-title>
</ion-toolbar>
</ion-header>
<ion-content :fullscreen="true">
<ion-header collapse="condense">
<ion-toolbar>
<ion-title size="large">{{ folder }}</ion-title>
<ion-title size="large">{{ $props.id }}</ion-title>
</ion-toolbar>
</ion-header>
<div id="container">
<strong class="capitalize">{{ folder }}</strong>
<strong class="capitalize">{{ $props.id }}</strong>
<p>Explore <a target="_blank" rel="noopener noreferrer" href="https://ionicframework.com/docs/components">UI Components</a></p>
</div>
</ion-content>
@ -26,8 +26,6 @@
<script lang="ts">
import { IonButtons, IonContent, IonHeader, IonMenuButton, IonPage, IonTitle, IonToolbar } from '@ionic/vue';
import { useRoute } from 'vue-router';
import { ref, computed, watch } from 'vue';
export default {
name: 'Folder',
@ -40,16 +38,8 @@ export default {
IonTitle,
IonToolbar,
},
setup() {
const route = useRoute();
const folder = ref(route.params.id || 'Inbox');
const matchedFolder = computed(() => route.params.id);
watch(matchedFolder, () => {
folder.value = matchedFolder.value as string;
})
return { folder }
props: {
id: { type: String, default: 'Inbox' }
}
}
</script>

View File

@ -76,7 +76,7 @@
<ion-action-sheet
:is-open="isActionSheetOpen"
:buttons="actionSheetButtons"
@onDidDismiss="setActionSheetRef(false)"
@didDismiss="setActionSheetRef(false)"
>
</ion-action-sheet>
@ -84,7 +84,7 @@
:is-open="isAlertOpen"
header="Alert!"
:buttons="alertButtons"
@onDidDismiss="setAlertRef(false)"
@didDismiss="setAlertRef(false)"
>
</ion-alert>
@ -93,17 +93,17 @@
:duration="2000"
message="Loading"
:backdrop-dismiss="true"
@onDidDismiss="setLoadingRef(false)"
@didDismiss="setLoadingRef(false)"
>
</ion-loading>
<ion-modal
:is-open="isModalOpen"
:componentProps="overlayProps"
@onWillPresent="onModalWillPresent"
@onDidPresent="onModalDidPresent"
@onWillDismiss="onModalWillDismiss"
@onDidDismiss="onModalDidDismiss"
@willPresent="onModalWillPresent"
@didPresent="onModalDidPresent"
@willDismiss="onModalWillDismiss"
@didDismiss="onModalDidDismiss"
>
<ModalContent></ModalContent>
</ion-modal>
@ -112,7 +112,7 @@
:is-open="isPopoverOpen"
:componentProps="overlayProps"
:event="popoverEvent"
@onDidDismiss="setPopoverRef(false)"
@didDismiss="setPopoverRef(false)"
>
<PopoverContent></PopoverContent>
</ion-popover>
@ -122,7 +122,7 @@
:duration="2000"
message="Toast"
:buttons="toastButtons"
@onDidDismiss="setToastRef(false)"
@didDismiss="setToastRef(false)"
>
</ion-toast>
</ion-content>

View File

@ -18,8 +18,11 @@
<ion-button id="parameter-view" :router-link="'/routing/' + $route.params.id + '/view'">Go to Single View</ion-button>
<ion-button router-link="/routing/abc">Go to Parameter Page ABC</ion-button>
<ion-button router-link="/routing/xyz">Go to Parameter Page XYZ</ion-button>
<div class="ion-padding" id="parameter-value">
{{ $route.params.id }}
{{ $props.id }}
</div>
</ion-content>
</ion-page>
@ -39,6 +42,9 @@ import {
import { defineComponent } from 'vue';
export default defineComponent({
props: {
id: { type: String, default: 'my default' }
},
components: {
IonButton,
IonBackButton,

View File

@ -3,20 +3,17 @@
<ion-content>
<ion-tabs id="tabs">
<ion-tab-bar slot="bottom">
<ion-tab-button tab="tab1" href="/tabs/tab1">
<ion-icon :icon="triangle" />
<ion-label>Tab 1</ion-label>
<ion-tab-button
v-for="tab in tabs"
:tab="'tab' + tab.id"
:href="'/tabs/tab' + tab.id"
:key="tab.id"
>
<ion-icon :icon="tab.icon" />
<ion-label>Tab {{ tab.id }}</ion-label>
</ion-tab-button>
<ion-tab-button tab="tab2" href="/tabs/tab2">
<ion-icon :icon="ellipse" />
<ion-label>Tab 2</ion-label>
</ion-tab-button>
<ion-tab-button tab="tab3" href="/tabs/tab3">
<ion-icon :icon="square" />
<ion-label>Tab 3</ion-label>
</ion-tab-button>
<ion-button id="add-tab" @click="addTab()">Add Tab</ion-button>
</ion-tab-bar>
</ion-tabs>
</ion-content>
@ -24,18 +21,33 @@
</template>
<script lang="ts">
import { IonTabBar, IonTabButton, IonTabs, IonContent, IonLabel, IonIcon, IonPage } from '@ionic/vue';
import { ellipse, square, triangle } from 'ionicons/icons';
import { IonButton, IonTabBar, IonTabButton, IonTabs, IonContent, IonLabel, IonIcon, IonPage } from '@ionic/vue';
import { ellipse, square, triangle, shield } from 'ionicons/icons';
import { useRouter } from 'vue-router';
import { ref, defineComponent } from 'vue';
export default {
export default defineComponent({
name: 'Tabs',
components: { IonContent, IonLabel, IonTabs, IonTabBar, IonTabButton, IonIcon, IonPage },
components: { IonButton, IonContent, IonLabel, IonTabs, IonTabBar, IonTabButton, IonIcon, IonPage },
setup() {
return {
ellipse,
square,
triangle,
const tabs = ref([
{ id: 1, icon: triangle },
{ id: 2, icon: ellipse },
{ id: 3, icon: square }
])
const router = useRouter();
const addTab = () => {
router.addRoute({ path: '/tabs/tab4', component: () => import('@/views/Tab4.vue') });
tabs.value = [
...tabs.value,
{
id: 4,
icon: shield
}
]
}
return { tabs, addTab }
}
}
});
</script>

View File

@ -1,6 +1,7 @@
describe('Nested', () => {
beforeEach(() => {
cy.visit('http://localhost:8080/nested');
cy.ionPageVisible('nestedchild');
});
it('should show first page', () => {

View File

@ -203,6 +203,71 @@ describe('Tabs', () => {
cy.get('ion-tab-button#tab-button-tab1').should('not.have.class', 'tab-selected');
});
// Verifies fix for https://github.com/ionic-team/ionic-framework/issues/23101
it('should return to previous tab instance when using the ion-back-button', () => {
cy.visit('http://localhost:8080/tabs/tab1');
cy.get('#tabs-secondary').click();
cy.ionPageHidden('tabs');
cy.ionPageVisible('tab1-secondary');
cy.get('ion-tab-button#tab-button-tab2-secondary').click();
cy.ionPageHidden('tab1-secondary');
cy.ionPageVisible('tab2-secondary');
cy.get('ion-tab-button#tab-button-tab1-secondary').click();
cy.ionPageHidden('tab2-secondary');
cy.ionPageVisible('tab1-secondary');
cy.ionBackClick('tab1-secondary');
cy.ionPageDoesNotExist('tabs-secondary');
cy.ionPageVisible('tab1');
});
// Verifies fix for https://github.com/ionic-team/ionic-framework/issues/23087
it('should return to correct view and url when going back from child page after switching tabs', () => {
cy.visit('http://localhost:8080/tabs/tab1');
cy.get('#child-one').click();
cy.ionPageHidden('tab1');
cy.ionPageVisible('tab1childone');
cy.get('ion-tab-button#tab-button-tab2').click();
cy.ionPageHidden('tab1childone');
cy.ionPageVisible('tab2');
cy.get('ion-tab-button#tab-button-tab1').click();
cy.ionPageHidden('tab2');
cy.ionPageVisible('tab1childone');
cy.ionBackClick('tab1childone');
cy.ionPageDoesNotExist('tab1childone');
cy.ionPageVisible('tab1');
cy.url().should('include', '/tabs/tab1');
});
// Verifies fix for https://github.com/ionic-team/ionic-framework/issues/22847
it('should support dynamic tabs', () => {
cy.visit('http://localhost:8080/tabs/tab1');
cy.ionPageVisible('tab1');
cy.get('ion-tab-button').its('length').should('equal', 3);
cy.get('ion-tab-button#tab-button-tab1').should('have.class', 'tab-selected');
cy.get('#add-tab').click();
cy.get('ion-tab-button').its('length').should('equal', 4);
cy.get('ion-tab-button#tab-button-tab4').click();
cy.ionPageVisible('tab4');
cy.ionPageHidden('tab1');
cy.get('ion-tab-button#tab-button-tab1').should('not.have.class', 'tab-selected');
cy.get('ion-tab-button#tab-button-tab4').should('have.class', 'tab-selected');
});
})
describe('Tabs - Swipe to Go Back', () => {
@ -214,11 +279,15 @@ describe('Tabs - Swipe to Go Back', () => {
cy.ionPageVisible('tab1')
});
it('should swipe and abort', () => {
// TODO: Flaky if test runner is slow
// Delays between gesture movements
// cause swipe back gesture to think
// velocity is higher than it actually is
/*it('should swipe and abort', () => {
cy.ionSwipeToGoBack();
cy.ionPageHidden('home');
cy.ionPageVisible('tab1');
});
});*/
it('should swipe and go back to home', () => {
cy.ionSwipeToGoBack(true);

View File

@ -47,12 +47,6 @@ Cypress.Commands.add('ionPageVisible', (pageId) => {
.should('have.length', 1)
})
Cypress.Commands.add('ionPageInvisible', (pageId) => {
cy.get(`div.ion-page[data-pageid=${pageId}]`)
.should('have.class', 'ion-page-invisible')
.should('have.length', 1)
})
Cypress.Commands.add('ionPageHidden', (pageId) => {
cy.get(`div.ion-page[data-pageid=${pageId}]`)
.should('have.class', 'ion-page-hidden')

View File

@ -358,4 +358,39 @@ describe('Routing', () => {
const cmpAgain = wrapper.findComponent(Page1);
expect(cmpAgain.props()).toEqual({ title: 'abc Title' });
});
// Verifies fix for https://github.com/ionic-team/ionic-framework/pull/23189
it('should update props on a parameterized url', async () => {
const Page = {
props: {
id: { type: String, default: 'Default ID' }
},
components: { IonPage },
template: `<ion-page>{{ $props.id }}</ion-page>`
}
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes: [
{ path: '/page/:id', component: Page, props: true },
{ path: '/', redirect: '/page/1' }
]
});
router.push('/');
await router.isReady();
const wrapper = mount(App, {
global: {
plugins: [router, IonicVue]
}
});
const page = wrapper.findComponent(Page);
expect(page.props()).toEqual({ id: '1' });
router.push('/page/2');
await waitForRouter();
expect(page.props()).toEqual({ id: '2' });
});
});