feat(custom-elements): add experimental custom elements build (#22863)

Co-authored-by: Liam DeBeasi <liamdebeasi@icloud.com>
This commit is contained in:
Adam Bradley 2021-02-11 11:08:00 -06:00 committed by GitHub
parent 19d63f6243
commit 0de75afbef
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
61 changed files with 5430 additions and 7613 deletions

2
.gitignore vendored
View File

@ -59,10 +59,10 @@ prerender-static.html
angular/css/
packages/react/css/
packages/vue/css/
core/components/
core/css/
core/hydrate/
core/loader/
core/www/
.stencil/
angular/build/
core/components/

View File

@ -10,6 +10,7 @@ const rootDir = path.join(__dirname, '../');
const packages = [
'core',
'core/components',
'docs',
'angular',
'packages/react',

View File

@ -7,7 +7,25 @@ const fs = require('fs');
[
// core
{
files: ['../core/dist/index.js', '../core/dist/ionic/index.esm.js']
files: [
'../core/css/core.css',
'../core/css/core.css.map',
'../core/css/normalize.css',
'../core/css/normalize.css.map',
'../core/components/index.js',
'../core/components/index.d.ts',
'../core/components/package.json',
'../core/dist/index.js',
'../core/dist/ionic/index.esm.js',
]
},
// hydrate
{
files: [
'../core/hydrate/index.js',
'../core/hydrate/index.d.ts',
'../core/hydrate/package.json'
]
},
// angular
{

View File

@ -1,6 +1,7 @@
import { Location } from '@angular/common';
import { Attribute, ComponentFactoryResolver, ComponentRef, Directive, ElementRef, EventEmitter, Injector, NgZone, OnDestroy, OnInit, Optional, Output, SkipSelf, ViewContainerRef } from '@angular/core';
import { ActivatedRoute, ChildrenOutletContexts, OutletContext, PRIMARY_OUTLET, Router } from '@angular/router';
import { componentOnReady } from '@ionic/core';
import { BehaviorSubject, Observable } from 'rxjs';
import { distinctUntilChanged, filter, switchMap } from 'rxjs/operators';
@ -96,13 +97,12 @@ export class IonRouterOutlet implements OnDestroy, OnInit {
this.activateWith(context.route, context.resolver || null);
}
}
if ((this.nativeEl as any).componentOnReady) {
this.nativeEl.componentOnReady().then(() => {
if (this._swipeGesture === undefined) {
this.swipeGesture = this.config.getBoolean('swipeBackEnabled', (this.nativeEl as any).mode === 'ios');
}
});
}
new Promise(resolve => componentOnReady(this.nativeEl, resolve)).then(() => {
if (this._swipeGesture === undefined) {
this.swipeGesture = this.config.getBoolean('swipeBackEnabled', (this.nativeEl as any).mode === 'ios');
}
});
}
get isActivated(): boolean {

View File

@ -278,8 +278,8 @@ export class IonHeader {
}
export declare interface IonIcon extends Components.IonIcon {
}
@ProxyCmp({ inputs: ["ariaLabel", "color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "size", "src"] })
@Component({ selector: "ion-icon", changeDetection: ChangeDetectionStrategy.OnPush, template: "<ng-content></ng-content>", inputs: ["ariaLabel", "color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "size", "src"] })
@ProxyCmp({ inputs: ["ariaHidden", "ariaLabel", "color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] })
@Component({ selector: "ion-icon", changeDetection: ChangeDetectionStrategy.OnPush, template: "<ng-content></ng-content>", inputs: ["ariaHidden", "ariaLabel", "color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] })
export class IonIcon {
protected el: HTMLElement;
constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) {

View File

@ -10,6 +10,6 @@ export interface IonicWindow extends Window {
}
export interface HTMLStencilElement extends HTMLElement {
componentOnReady(): Promise<this>;
forceUpdate(): void;
componentOnReady?(): Promise<this>;
forceUpdate?(): void;
}

View File

@ -40,6 +40,24 @@ The `@ionic/core` package can by used in simple HTML, or by vanilla JavaScript w
* [@ionic/angular](https://www.npmjs.com/package/@ionic/angular)
## Custom Elements Build (Experimental)
In addition to the default, self lazy-loading components built by Stencil, this package also comes with each component exported as a stand-alone custom element within `@ionic/core/components`. Each component extends `HTMLElement`, and does not lazy-load itself. Instead, this package is useful for projects already using a bundler such as Webpack or Rollup. While all components are available to be imported, the custom elements build also ensures bundlers only import what's used, and tree-shakes any unused components.
Below is an example of importing `ion-toggle`, and initializing Ionic so it's able to correctly load the "mode", such as Material Design or iOS. Additionally, the `initialize({...})` function can receive the Ionic config.
```typescript
import { IonBadge } from "@ionic/core/components/ion-badge";
import { initialize } from "@ionic/core/components";
initialize();
customElements.define("ion-badge", IonBadge);
```
Notice how `IonBadge` is imported from `@ionic/core/components/ion-badge` rather than just `@ionic/core/components`. Additionally, the `initialize` function is imported from `@ionic/core/components` rather than `@ionic/core`. All of this helps to ensure bundlers do not pull in more code than is needed.
## How to contribute
[Check out the CONTRIBUTE guide](CONTRIBUTING.md)

12652
core/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -24,24 +24,26 @@
"collection": "dist/collection/collection-manifest.json",
"types": "dist/types/interface.d.ts",
"files": [
"dist/",
"components/",
"css/",
"dist/",
"hydrate/",
"loader/"
],
"dependencies": {
"ionicons": "^5.1.2",
"@stencil/core": "^2.4.0",
"ionicons": "^5.5.0",
"tslib": "^1.10.0"
},
"devDependencies": {
"@jest/core": "^26.6.3",
"@rollup/plugin-node-resolve": "^8.4.0",
"@rollup/plugin-virtual": "^2.0.3",
"@stencil/core": "2.1.2",
"@stencil/sass": "1.3.2",
"@stencil/vue-output-target": "0.3.0",
"@types/jest": "^26.0.10",
"@types/jest": "^26.0.20",
"@types/node": "^14.6.0",
"@types/puppeteer": "3.0.1",
"@types/puppeteer": "5.4.3",
"@types/swiper": "5.4.0",
"aws-sdk": "^2.738.0",
"clean-css-cli": "^4.1.11",
@ -51,7 +53,7 @@
"jest-cli": "^26.4.1",
"np": "^6.4.0",
"pixelmatch": "4.0.2",
"puppeteer": "^5.2.1",
"puppeteer": "^7.0.1",
"rollup": "^2.26.4",
"sass": "^1.26.10",
"stylelint": "^13.6.1",

View File

@ -0,0 +1,2 @@
export * from './index';
export * from '../dist/types/interface';

View File

@ -0,0 +1,9 @@
{
"name": "@ionic/core/components",
"version": "0.0.0",
"description": "Ionic Components exported as custom elements, extending HTMLElement.",
"main": "./index.js",
"types": "./custom-elements.d.ts",
"private": true,
"sideEffects": false
}

View File

@ -4,15 +4,15 @@ const virtual = require('@rollup/plugin-virtual');
const fs = require('fs');
async function main() {
const input = process.argv[2] || getInput();
const result = await check(input);
const relative = path.relative(process.cwd(), input);
const input = process.argv[2] || getMainEntry();
const result = await check(input);
const relative = path.relative(process.cwd(), input);
if (result.shaken) {
console.error(`Success! ${relative} is fully tree-shakeable`);
} else {
error(`Failed to tree-shake ${relative}`);
}
if (result.shaken) {
console.error(`Success! ${relative} is fully tree-shakeable`);
} else {
error(`Failed to tree-shake ${relative}`);
};
}
function error(msg) {
@ -20,10 +20,10 @@ function error(msg) {
process.exit(1);
}
function getInput() {
if (!fs.existsSync('package.json')) {
error(`Could not find package.json`);
}
function getMainEntry() {
if (!fs.existsSync('package.json')) {
error(`Could not find package.json`);
}
const pkg = JSON.parse(fs.readFileSync('package.json'), 'utf-8');
@ -38,20 +38,20 @@ function getInput() {
}
function resolve(file) {
if (isDirectory(file)) {
return ifExists(`${file}/index.js`) || ifExists(`${file}/index.cjs.js`);
}
if (isDirectory(file)) {
return ifExists(`${file}/index.cjs.js`) || ifExists(`${file}/index.js`);
}
return ifExists(file) || ifExists(`${file}.js`) || ifExists(`${file}.cjs.js`);
return ifExists(file) || ifExists(`${file}.cjs.js`) || ifExists(`${file}.js`);
}
function isDirectory(file) {
try {
const stats = fs.statSync(file);
return stats.isDirectory();
} catch (err) {
return false;
}
try {
const stats = fs.statSync(file);
return stats.isDirectory();
} catch (err) {
return false;
}
}
function ifExists(file) {
@ -67,7 +67,7 @@ async function check(input) {
virtual({
__agadoo__: `import ${JSON.stringify(resolved)}`,
tslib: `
const noop = () => {};
const noop = () => {};
export const __awaiter = noop;
export const __extends = noop;
export const __generator = noop;

View File

@ -71,7 +71,7 @@ export const testActionSheetAlert = async (
const alert = await page.find('ion-alert');
await alert.waitForVisible();
await page.waitFor(250);
await page.waitForTimeout(250);
screenshotCompares.push(await page.compareScreenshot(`alert open`));

View File

@ -20,7 +20,7 @@ test('datetime/picker: focus trap', async () => {
expect(datetime).not.toBe(null);
// TODO fix
await page.waitFor(250);
await page.waitForTimeout(250);
await page.keyboard.press('Tab');
@ -54,7 +54,7 @@ test('datetime: basic', async () => {
const picker = await page.find('ion-picker');
await picker.waitForVisible();
await page.waitFor(250);
await page.waitForTimeout(250);
compare = await page.compareScreenshot('should open custom picker');
expect(compare).toMatchScreenshot();
@ -70,7 +70,7 @@ test('datetime: basic-rtl', async () => {
const picker = await page.find('ion-picker');
await picker.waitForVisible();
await page.waitFor(250);
await page.waitForTimeout(250);
const compare = await page.compareScreenshot('should open custom picker');
expect(compare).toMatchScreenshot();

View File

@ -13,14 +13,14 @@ test('datetime: standalone', async () => {
const picker = await page.find('ion-picker');
await picker.waitForVisible();
await page.waitFor(250);
await page.waitForTimeout(250);
compare = await page.compareScreenshot('should open basic picker');
expect(compare).toMatchScreenshot();
const octoberOpt = await page.find({ text: 'October' });
await octoberOpt.click();
await page.waitFor(500);
await page.waitForTimeout(500);
compare = await page.compareScreenshot('should click "October" option');
expect(compare).toMatchScreenshot();

View File

@ -20,7 +20,7 @@ export const testFab = async (
const fab = await getFabComponent(page, selector);
await fab.click();
await page.waitFor(250);
await page.waitForTimeout(250);
await ensureFabState(fab, 'active');
@ -29,7 +29,7 @@ export const testFab = async (
const fabButton = await getFabButton(fab);
await fabButton.click();
await page.waitFor(250);
await page.waitForTimeout(250);
await ensureFabState(fab, 'inactive');

View File

@ -209,7 +209,14 @@ export class ItemSliding implements ComponentInterface {
this.leftOptions = this.rightOptions = undefined;
for (let i = 0; i < options.length; i++) {
const option = await options.item(i).componentOnReady();
const item = options.item(i);
/**
* We cannot use the componentOnReady helper
* util here since we need to wait for all of these items
* to be ready before we set `this.sides` and `this.optsDirty`.
*/
const option = ((item as any).componentOnReady !== undefined) ? await item.componentOnReady() : item;
const side = isEndSide(option.side) ? 'end' : 'start';

View File

@ -47,5 +47,5 @@ const deleteItemSliding = async (item: any, page: any, id: string) => {
// Wait for element to be removed from DOM
await page.waitForSelector(id, { hidden: true });
await page.waitFor(1000);
await page.waitForTimeout(1000);
};

View File

@ -42,7 +42,7 @@ export const openItemSliding = async (id: string, page: any, rtl = false) => {
await page.mouse.up();
// Add a timeout to make sure the item is open
await page.waitFor(2000);
await page.waitForTimeout(2000);
} catch (err) {
throw err;
}
@ -54,5 +54,5 @@ export const closeItemSliding = async (page: any) => {
await page.mouse.move(0, 0);
await page.mouse.down();
await page.mouse.up();
await page.waitFor(1000);
await page.waitForTimeout(1000);
};

View File

@ -11,7 +11,7 @@ test('item: inputs', async () => {
page,
'{"date":"","select":"n64","toggle":"","input":"","input2":"","checkbox":"","range":"10"}'
);
await page.waitFor(100);
await page.waitForTimeout(100);
// Default case, enabled and no value
let compare = await page.compareScreenshot();
@ -21,13 +21,13 @@ test('item: inputs', async () => {
const disableToggle = await page.find('#btnDisabled');
await disableToggle.waitForVisible();
await disableToggle.click();
await page.waitFor(300);
await page.waitForTimeout(300);
// check form
await page.click('#submit');
await page.waitFor(100);
await page.waitForTimeout(100);
await checkFormResult(page, '{}');
await page.waitFor(100);
await page.waitForTimeout(100);
// screenshot
compare = await page.compareScreenshot('should disable all');
@ -36,7 +36,7 @@ test('item: inputs', async () => {
// Reenable and set some value
await disableToggle.click();
await page.click('#btnSomeValue');
await page.waitFor(100);
await page.waitForTimeout(100);
// check form
await page.click('#submit');
@ -44,28 +44,28 @@ test('item: inputs', async () => {
page,
'{"date":"2016-12-09","select":"nes","toggle":"on","input":"Some text","input2":"Some text","checkbox":"on","range":"20"}'
);
await page.waitFor(100);
await page.waitForTimeout(100);
compare = await page.compareScreenshot('should reenable and set value');
expect(compare).toMatchScreenshot();
// Set "null"
await page.click('#btnNullValue');
await page.waitFor(100);
await page.waitForTimeout(100);
compare = await page.compareScreenshot('should set null');
expect(compare).toMatchScreenshot();
// Set "empty"
await page.click('#btnEmptyValue');
await page.waitFor(100);
await page.waitForTimeout(100);
compare = await page.compareScreenshot('should set empty');
expect(compare).toMatchScreenshot();
// Set "empty"
await page.click('#btnEmptyValue');
await page.waitFor(100);
await page.waitForTimeout(100);
compare = await page.compareScreenshot('should set empty');
expect(compare).toMatchScreenshot();
@ -73,7 +73,7 @@ test('item: inputs', async () => {
// Test multiple
await page.click('#checkbox-start');
await page.click('#datetime-end');
await page.waitFor(300);
await page.waitForTimeout(300);
compare = await page.compareScreenshot(
'should check checkbox and open datepicker'
@ -81,7 +81,7 @@ test('item: inputs', async () => {
expect(compare).toMatchScreenshot();
await page.click('#button-end');
await page.waitFor(100);
await page.waitForTimeout(100);
compare = await page.compareScreenshot('should change button color to red');
expect(compare).toMatchScreenshot();

View File

@ -11,7 +11,7 @@
<script nomodule src="../../../../../dist/ionic/ionic.js"></script>
<script type="module" src="../../../../../dist/ionic/ionic.esm.js"></script></head>
<script type="module">
import { loadingController } from '../../../../dist/ionic/index.esm.js';
import { loadingController } from '../../../../../dist/ionic/index.esm.js';
window.loadingController = loadingController;
</script>
<body>

View File

@ -25,12 +25,12 @@ export const testMenu = async (
const menu = await page.find(selector);
await menu.callMethod('open');
await page.waitFor(1000);
await page.waitForTimeout(1000);
screenshotCompares.push(await page.compareScreenshot());
await menu.callMethod('close');
await page.waitFor(250);
await page.waitForTimeout(250);
screenshotCompares.push(await page.compareScreenshot('dismiss'));

View File

@ -28,7 +28,7 @@ export const testModal = async (
let modal = await page.find('ion-modal');
await modal.waitForVisible();
await page.waitFor(100);
await page.waitForTimeout(100);
screenshotCompares.push(await page.compareScreenshot());

View File

@ -11,13 +11,13 @@ test.skip('nav: basic', async () => {
expect(await page.compareScreenshot()).toMatchScreenshot();
page.click('page-one ion-button.next');
await page.waitFor(navChanged);
await page.waitForTimeout(navChanged);
page.click('page-two ion-button.next');
await page.waitFor(navChanged);
await page.waitForTimeout(navChanged);
page.click('page-three ion-back-button');
await page.waitFor(navChanged);
await page.waitForTimeout(navChanged);
page.click('page-two ion-back-button');
await page.waitFor(navChanged);
await page.waitForTimeout(navChanged);
expect(await page.compareScreenshot('stack traversal')).toMatchScreenshot();
});

View File

@ -12,17 +12,17 @@ test.skip('nav: nested', async () => {
expect(await page.compareScreenshot()).toMatchScreenshot();
await page.click('page-one ion-button.next');
await page.waitFor(navChanged);
await page.waitForTimeout(navChanged);
await page.click('page-two-one ion-button.next');
await page.waitFor(navChanged);
await page.waitForTimeout(navChanged);
await page.click('page-two-two ion-button.next');
await page.waitFor(navChanged);
await page.waitForTimeout(navChanged);
await page.click('page-three ion-back-button');
await page.waitFor(navChanged);
await page.waitForTimeout(navChanged);
await page.click('page-two-two ion-back-button');
await page.waitFor(navChanged);
await page.waitForTimeout(navChanged);
await page.click('page-two-one ion-back-button');
await page.waitFor(navChanged);
await page.waitForTimeout(navChanged);
expect(await page.compareScreenshot('stack traversal')).toMatchScreenshot();
});

View File

@ -11,17 +11,17 @@ test.skip('nav: routing', async () => {
expect(await page.compareScreenshot()).toMatchScreenshot();
await page.click('page-root ion-button.next');
await page.waitFor(navChanged);
await page.waitForTimeout(navChanged);
await page.click('page-one ion-button.next');
await page.waitFor(navChanged);
await page.waitForTimeout(navChanged);
await page.click('page-two ion-button.next');
await page.waitFor(navChanged);
await page.waitForTimeout(navChanged);
await page.click('page-three ion-back-button');
await page.waitFor(navChanged);
await page.waitForTimeout(navChanged);
await page.click('page-two ion-back-button');
await page.waitFor(navChanged);
await page.waitForTimeout(navChanged);
await page.click('page-one ion-back-button');
await page.waitFor(navChanged);
await page.waitForTimeout(navChanged);
expect(await page.compareScreenshot('stack traversal')).toMatchScreenshot();
});

View File

@ -18,7 +18,7 @@ export const testPickerColumn = async (
const openButton = await page.find(selector);
await openButton.click();
await page.waitFor(250);
await page.waitForTimeout(250);
screenshotCompares.push(await page.compareScreenshot());

View File

@ -127,7 +127,7 @@
});
customElements.whenDefined('ion-radio-group')
.then(() => group.componentOnReady())
.then(() => group.componentOnReady && group.componentOnReady())
.then(() => {
valueEl.textContent = group.value;
});

View File

@ -3,7 +3,7 @@ import { Component, ComponentInterface, Element, Event, EventEmitter, Host, Meth
import { getIonMode } from '../../global/ionic-global';
import { Animation, Gesture, GestureDetail, RefresherEventDetail } from '../../interface';
import { getTimeGivenProgression } from '../../utils/animation/cubic-bezier';
import { clamp, getElementRoot, raf } from '../../utils/helpers';
import { clamp, componentOnReady, getElementRoot, raf } from '../../utils/helpers';
import { hapticImpact } from '../../utils/native/haptic';
import { createPullingAnimation, createSnapBackAnimation, getRefresherAnimationType, handleScrollWhilePulling, handleScrollWhileRefreshing, setSpinnerOpacity, shouldUseNativeRefresher, transitionEndAsync, translateElement } from './refresher.utils';
@ -415,7 +415,7 @@ export class Refresher implements ComponentInterface {
return;
}
await contentEl.componentOnReady();
await new Promise(resolve => componentOnReady(contentEl, resolve));
this.scrollEl = await contentEl.getScrollElement();
this.backgroundContentEl = getElementRoot(contentEl).querySelector('#background-content') as HTMLElement;

View File

@ -1,6 +1,7 @@
import { writeTask } from '@stencil/core';
import { createAnimation } from '../../utils/animation/animation';
import { componentOnReady } from '../../utils/helpers';
import { isPlatform } from '../../utils/platform';
// MD Native Refresher
@ -170,7 +171,7 @@ export const shouldUseNativeRefresher = async (referenceEl: HTMLIonRefresherElem
const refresherContent = referenceEl.querySelector('ion-refresher-content');
if (!refresherContent) { return Promise.resolve(false); }
await refresherContent.componentOnReady();
await new Promise(resolve => componentOnReady(refresherContent, resolve));
const pullingSpinner = referenceEl.querySelector('ion-refresher-content .refresher-pulling ion-spinner');
const refreshingSpinner = referenceEl.querySelector('ion-refresher-content .refresher-refreshing ion-spinner');

View File

@ -54,7 +54,7 @@ test('reorder: interactive', async () => {
const moveItem = async (id: string, page: pd.E2EPage, direction: 'up' | 'down' = 'up', numberOfSpaces = 1, ...parentSelectors: string[]) => {
try {
await moveReorderItem(`#${id}`, page, direction, numberOfSpaces, ...parentSelectors);
await page.waitFor(50);
await page.waitForTimeout(50);
} catch (err) {
throw err;
}

View File

@ -1,4 +1,5 @@
import { AnimationBuilder, NavOutletElement, RouteChain, RouteID, RouterDirection } from '../../../interface';
import { componentOnReady } from '../../../utils/helpers';
import { ROUTER_INTENT_NONE } from './constants';
@ -18,7 +19,7 @@ export const writeNavState = async (
if (index >= chain.length || !outlet) {
return changed;
}
await outlet.componentOnReady();
await new Promise(resolve => componentOnReady(outlet, resolve));
const route = chain[index];
const result = await outlet.setRouteId(route.id, route.params, direction, animation);

View File

@ -11,7 +11,7 @@ test('searchbar: basic', async () => {
let searchbar = await page.find('#basic');
await searchbar.callMethod('setFocus');
await page.waitFor(250);
await page.waitForTimeout(250);
searchbar = await page.find('#basic');
expect(searchbar).toHaveClass('searchbar-has-focus');
@ -21,7 +21,7 @@ test('searchbar: basic', async () => {
searchbar = await page.find('#noCancel');
await searchbar.callMethod('setFocus');
await page.waitFor(250);
await page.waitForTimeout(250);
searchbar = await page.find('#noCancel');
expect(searchbar).toHaveClass('searchbar-has-focus');
@ -43,7 +43,7 @@ test('searchbar:rtl: basic', async () => {
let searchbar = await page.find('#basic');
await searchbar.callMethod('setFocus');
await page.waitFor(250);
await page.waitForTimeout(250);
searchbar = await page.find('#basic');
expect(searchbar).toHaveClass('searchbar-has-focus');
@ -53,7 +53,7 @@ test('searchbar:rtl: basic', async () => {
searchbar = await page.find('#noCancel');
await searchbar.callMethod('setFocus');
await page.waitFor(250);
await page.waitForTimeout(250);
searchbar = await page.find('#noCancel');
expect(searchbar).toHaveClass('searchbar-has-focus');

View File

@ -5,7 +5,7 @@ test('segment: colors', async () => {
url: '/src/components/segment/test/colors?ionic:_testing=true'
});
await page.waitFor(250);
await page.waitForTimeout(250);
const compare = await page.compareScreenshot();
expect(compare).toMatchScreenshot();
@ -16,7 +16,7 @@ test('segment:rtl: colors', async () => {
url: '/src/components/segment/test/colors?ionic:_testing=true&rtl=true'
});
await page.waitFor(250);
await page.waitForTimeout(250);
const compare = await page.compareScreenshot();
expect(compare).toMatchScreenshot();

View File

@ -154,7 +154,9 @@
for (var i = 0; i < customIconSegments.length; i++) {
const customIconSegment = customIconSegments[i];
await customIconSegment.componentOnReady();
if (customIconSegment.componentOnReady) {
await customIconSegment.componentOnReady();
}
addIconClass(customIconSegment, customIconSegment.value);
customIconSegment.addEventListener('ionChange', function (ev) {

View File

@ -5,7 +5,7 @@ test('segment: toolbar', async () => {
url: '/src/components/segment/test/toolbar?ionic:_testing=true'
});
await page.waitFor(250);
await page.waitForTimeout(250);
const compare = await page.compareScreenshot();
expect(compare).toMatchScreenshot();
@ -16,7 +16,7 @@ test('segment:rtl: toolbar', async () => {
url: '/src/components/segment/test/toolbar?ionic:_testing=true&rtl=true'
});
await page.waitFor(250);
await page.waitForTimeout(250);
const compare = await page.compareScreenshot();
expect(compare).toMatchScreenshot();

View File

@ -14,7 +14,7 @@ test('select: basic', async () => {
let alert = await page.find('ion-alert');
await alert.waitForVisible();
await page.waitFor(250);
await page.waitForTimeout(250);
compares.push(await page.compareScreenshot('should open gender single select'));
@ -26,7 +26,7 @@ test('select: basic', async () => {
let actionSheet = await page.find('ion-action-sheet');
await actionSheet.waitForVisible();
await page.waitFor(250);
await page.waitForTimeout(250);
compares.push(await page.compareScreenshot('should open skittles action sheet select'));
@ -38,7 +38,7 @@ test('select: basic', async () => {
alert = await page.find('ion-alert');
await alert.waitForVisible();
await page.waitFor(250);
await page.waitForTimeout(250);
compares.push(await page.compareScreenshot('should open custom alert select'));
@ -50,7 +50,7 @@ test('select: basic', async () => {
const popover = await page.find('ion-popover');
await popover.waitForVisible();
await page.waitFor(250);
await page.waitForTimeout(250);
compares.push(await page.compareScreenshot('should open custom popover select'));
@ -62,7 +62,7 @@ test('select: basic', async () => {
actionSheet = await page.find('ion-action-sheet');
await actionSheet.waitForVisible();
await page.waitFor(250);
await page.waitForTimeout(250);
compares.push(await page.compareScreenshot('should open custom action sheet select'));

View File

@ -13,7 +13,7 @@ test('select: multiple-value', async () => {
const alert = await page.find('ion-alert');
await alert.waitForVisible();
await page.waitFor(250);
await page.waitForTimeout(250);
compare = await page.compareScreenshot('should open toppings multiple select');
expect(compare).toMatchScreenshot();

View File

@ -13,7 +13,7 @@ test('select: single-value', async () => {
const alert = await page.find('ion-alert');
await alert.waitForVisible();
await page.waitFor(250);
await page.waitForTimeout(250);
compare = await page.compareScreenshot('should open gender single select');
expect(compare).toMatchScreenshot();

View File

@ -168,7 +168,7 @@
</style>
<script>
customElements.whenDefined('ion-select-option').then(() => {
customElements.whenDefined('ion-select-option').then(async () => {
let selects = document.querySelectorAll('ion-select');
selects.forEach(function (select) {
select.addEventListener("ionChange", function () {
@ -176,7 +176,7 @@
});
});
function setResults(select) {
async function setResults(select) {
if (select.id) {
var resultsEl = document.getElementById(select.id + 'Result');
if (resultsEl) {
@ -205,9 +205,11 @@
year.appendChild(option);
}
year.componentOnReady().then(() => {
year.value = 1994;
});
if (year.componentOnReady) {
await year.componentOnReady();
}
year.value = 1994;
setResults(year);

View File

@ -1,6 +1,7 @@
import { Component, ComponentInterface, Element, Event, EventEmitter, Host, Method, Prop, Watch, h } from '@stencil/core';
import { getIonMode } from '../../global/ionic-global';
import { componentOnReady } from '../../utils/helpers'
import { SwiperInterface, SwiperOptions } from './swiper/swiper-interface';
@ -146,12 +147,12 @@ export class Slides implements ComponentInterface {
subtree: true
});
this.el.componentOnReady().then(() => {
componentOnReady(this.el, () => {
if (!this.didInit) {
this.didInit = true;
this.initSwiper();
}
});
})
}
}
@ -530,6 +531,6 @@ export class Slides implements ComponentInterface {
const waitForSlides = (el: HTMLElement) => {
return Promise.all(
Array.from(el.querySelectorAll('ion-slide')).map(s => s.componentOnReady())
Array.from(el.querySelectorAll('ion-slide')).map(s => new Promise(resolve => componentOnReady(s, resolve)))
);
};

View File

@ -14,10 +14,10 @@ test('slides: prevent-default', async () => {
const button = await page.find('#changeBackgroundButton');
const contentWithBackground = await page.find('#contentWithBackground');
await page.waitFor(500);
await page.waitForTimeout(500);
await scroller.click();
await page.waitFor(500);
await page.waitForTimeout(500);
screenshotCompares.push(await page.compareScreenshot('scroll down button'));

View File

@ -15,7 +15,7 @@ test('tab-bar: custom', async () => {
await page.keyboard.press('Tab');
screenshotCompares.push(await page.compareScreenshot('tab-bar: custom tabbed'));
await page.waitFor(10000);
await page.waitForTimeout(10000);
for (const screenshotCompare of screenshotCompares) {
expect(screenshotCompare).toMatchScreenshot();
}

View File

@ -73,7 +73,9 @@
<script>
customElements.whenDefined('ion-tabs').then(async () => {
const tabs = document.querySelector('ion-tabs');
await tabs.componentOnReady();
if (tabs.componentOnReady) {
await tabs.componentOnReady();
}
await tabs.select('tab-three');
});
</script>

View File

@ -9,7 +9,7 @@ test('textarea: basic', async () => {
compares.push(await page.compareScreenshot());
await page.waitFor(250);
await page.waitForTimeout(250);
compares.push(await page.compareScreenshot('value changed'));
for (const compare of compares) {

View File

@ -20,7 +20,7 @@ export const testToast = async (
await button.waitForVisible();
await button.click();
await page.waitFor(250);
await page.waitForTimeout(250);
let toast = await page.find('ion-toast');
await toast.waitForVisible();

View File

@ -4,7 +4,7 @@ test('virtual-scroll: basic', async () => {
const page = await newE2EPage({
url: '/src/components/virtual-scroll/test/basic?ionic:_testing=true'
});
await page.waitFor(300);
await page.waitForTimeout(300);
const compare = await page.compareScreenshot();
expect(compare).toMatchScreenshot();

View File

@ -4,7 +4,7 @@ test('virtual-scroll: cards', async () => {
const page = await newE2EPage({
url: '/src/components/virtual-scroll/test/cards?ionic:_testing=true'
});
await page.waitFor(300);
await page.waitForTimeout(300);
const compare = await page.compareScreenshot();
expect(compare).toMatchScreenshot();

View File

@ -1,6 +1,7 @@
import { Component, ComponentInterface, Element, FunctionalComponent, Host, Listen, Method, Prop, State, Watch, forceUpdate, h, readTask, writeTask } from '@stencil/core';
import { Cell, DomRenderFn, FooterHeightFn, HeaderFn, HeaderHeightFn, ItemHeightFn, ItemRenderFn, VirtualNode } from '../../interface';
import { componentOnReady } from '../../utils/helpers';
import { CELL_TYPE_FOOTER, CELL_TYPE_HEADER, CELL_TYPE_ITEM } from './constants';
import { Range, calcCells, calcHeightIndex, doRender, findCellIndex, getRange, getShouldUpdate, getViewport, inplaceUpdate, positionForIndex, resizeBuffer, updateVDom } from './virtual-scroll-utils';
@ -322,8 +323,8 @@ export class VirtualScroll implements ComponentInterface {
this.setCellHeight(cell, height);
}
};
if (node && node.componentOnReady) {
node.componentOnReady().then(update);
if (node) {
componentOnReady(node, update);
} else {
update();
}

View File

@ -1,6 +1,6 @@
import { getMode, setMode } from '@stencil/core';
import { Mode } from '../interface';
import { IonicConfig, Mode } from '../interface';
import { isPlatform, setupPlatforms } from '../utils/platform';
import { config, configFromSession, configFromURL, saveConfig } from './config';
@ -13,8 +13,10 @@ export const getIonMode = (ref?: any): Mode => {
return (ref && getMode(ref)) || defaultMode;
};
export default () => {
const doc = document;
export const initialize = (userConfig: IonicConfig = {}) => {
if (typeof (window as any) === 'undefined') { return; }
const doc = window.document;
const win = window;
Context.config = config;
const Ionic = (win as any).Ionic = (win as any).Ionic || {};
@ -28,7 +30,8 @@ export default () => {
...configFromSession(win),
persistConfig: false,
...Ionic.config,
...configFromURL(win)
...configFromURL(win),
...userConfig
};
config.reset(configObj);
@ -70,3 +73,5 @@ export default () => {
return defaultMode;
});
};
export default initialize;

View File

@ -5,6 +5,8 @@ export { iosTransitionAnimation } from './utils/transition/ios.transition';
export { mdTransitionAnimation } from './utils/transition/md.transition';
export { getTimeGivenProgression } from './utils/animation/cubic-bezier';
export { createGesture } from './utils/gesture';
export { initialize } from './global/ionic-global';
export { componentOnReady } from './utils/helpers';
export { isPlatform, Platforms, getPlatforms } from './utils/platform';
export { IonicSafeString } from './utils/sanitization';
export { IonicConfig, getMode, setupConfig } from './utils/config';

View File

@ -75,6 +75,8 @@ export interface StyleEventDetail {
[styleName: string]: boolean;
}
export { NavComponentWithProps } from "./components/nav/nav-interface";
declare module "./components" {
export namespace Components {
export interface IonIcon extends IoniconsComponents.IonIcon{}

View File

@ -1,5 +1,7 @@
import { ComponentRef, FrameworkDelegate } from '../interface';
import { componentOnReady } from './helpers';
export const attachComponent = async (
delegate: FrameworkDelegate | undefined,
container: Element,
@ -26,9 +28,8 @@ export const attachComponent = async (
}
container.appendChild(el);
if (el.componentOnReady) {
await el.componentOnReady();
}
await new Promise(resolve => componentOnReady(el, resolve));
return el;
};

View File

@ -5,6 +5,14 @@ import { Side } from '../interface';
declare const __zone_symbol__requestAnimationFrame: any;
declare const requestAnimationFrame: any;
export const componentOnReady = (el: any, callback: any) => {
if (el.componentOnReady) {
el.componentOnReady().then(callback);
} else {
callback();
}
}
/**
* Elements inside of web components sometimes need to inherit global attributes
* set on the host. For example, the inner input in `ion-input` should inherit

View File

@ -1,5 +1,5 @@
import { Config } from '../../interface';
import { componentOnReady } from '../helpers';
import { enableHideCaretOnScroll } from './hacks/hide-caret';
import { enableInputBlurring } from './hacks/input-blurring';
@ -24,9 +24,8 @@ export const startInputShims = (config: Config) => {
const scrollAssistMap = new WeakMap<HTMLElement, () => void>();
const registerInput = async (componentEl: HTMLElement) => {
if ((componentEl as any).componentOnReady) {
await (componentEl as any).componentOnReady();
}
await new Promise(resolve => componentOnReady(componentEl, resolve));
const inputRoot = componentEl.shadowRoot || componentEl;
const inputEl = inputRoot.querySelector('input') || inputRoot.querySelector('textarea');
const scrollEl = componentEl.closest('ion-content');

View File

@ -1,5 +1,6 @@
import { AnimationBuilder, BackButtonEvent, MenuI } from '../../interface';
import { MENU_BACK_BUTTON_PRIORITY } from '../hardware-back-button';
import { componentOnReady } from '../helpers';
import { menuOverlayAnimation } from './animations/overlay';
import { menuPushAnimation } from './animations/push';
@ -199,7 +200,7 @@ const createMenuController = () => {
const waitUntilReady = () => {
return Promise.all(
Array.from(document.querySelectorAll('ion-menu'))
.map(menu => menu.componentOnReady())
.map(menu => new Promise(resolve => componentOnReady(menu, resolve)))
);
};

View File

@ -3,7 +3,7 @@ import { getIonMode } from '../global/ionic-global';
import { ActionSheetOptions, AlertOptions, Animation, AnimationBuilder, BackButtonEvent, HTMLIonOverlayElement, IonicConfig, LoadingOptions, ModalOptions, OverlayInterface, PickerOptions, PopoverOptions, ToastOptions } from '../interface';
import { OVERLAY_BACK_BUTTON_PRIORITY } from './hardware-back-button';
import { addEventListener, getElementRoot, removeEventListener } from './helpers';
import { addEventListener, componentOnReady, getElementRoot, removeEventListener } from './helpers';
let lastId = 0;
@ -57,7 +57,7 @@ export const createOverlay = <T extends HTMLIonOverlayElement>(tagName: string,
// append the overlay element to the document body
getAppRoot(document).appendChild(element);
return element.componentOnReady() as any;
return new Promise(resolve => componentOnReady(element, resolve));
});
}
return Promise.resolve() as any;

View File

@ -1,5 +1,7 @@
import { readTask, writeTask } from '@stencil/core';
import { componentOnReady } from './helpers';
export const startStatusTap = () => {
const win = window;
win.addEventListener('statusTap', () => {
@ -12,7 +14,7 @@ export const startStatusTap = () => {
}
const contentEl = el.closest('ion-content');
if (contentEl) {
contentEl.componentOnReady().then(() => {
new Promise(resolve => componentOnReady(contentEl, resolve)).then(() => {
writeTask(() => contentEl.scrollToTop(300));
});
}

View File

@ -148,7 +148,9 @@
await actionSheet.present();
const picker = document.querySelector('ion-picker');
await picker.componentOnReady();
if (picker.componentOnReady) {
await picker.componentOnReady();
}
picker.buttons = [
{ text: "Cancel", role: "cancel" },

View File

@ -2,6 +2,7 @@ import { Build, writeTask } from '@stencil/core';
import { LIFECYCLE_DID_ENTER, LIFECYCLE_DID_LEAVE, LIFECYCLE_WILL_ENTER, LIFECYCLE_WILL_LEAVE } from '../../components/nav/constants';
import { Animation, AnimationBuilder, NavDirection, NavOptions } from '../../interface';
import { componentOnReady } from '../helpers';
const iosTransitionAnimation = () => import('./ios.transition');
const mdTransitionAnimation = () => import('./md.transition');
@ -178,8 +179,8 @@ export const lifecycle = (el: HTMLElement | undefined, eventName: string) => {
};
const shallowReady = (el: Element | undefined): Promise<any> => {
if (el && (el as any).componentOnReady) {
return (el as any).componentOnReady();
if (el) {
return new Promise(resolve => componentOnReady(el, resolve));
}
return Promise.resolve();
};

View File

@ -111,9 +111,16 @@ export const config: Config = {
type: 'dist',
esmLoaderPath: '../loader'
},
// {
// type: 'dist-custom-elements-bundle',
// },
{
type: 'dist-custom-elements',
dir: 'components',
copy: [{
src: '../scripts/custom-elements',
dest: 'components',
warn: true
}],
includeGlobalScripts: false
},
{
type: 'docs-readme',
strict: true