mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-18 03:00:58 +08:00
feature(events): add ion-events pub/sub component
This commit is contained in:
30
packages/core/src/components.d.ts
vendored
30
packages/core/src/components.d.ts
vendored
@ -766,6 +766,36 @@ declare global {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
import {
|
||||||
|
Events as IonEvents
|
||||||
|
} from './components/events/events';
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLIonEventsElement extends IonEvents, HTMLElement {
|
||||||
|
}
|
||||||
|
var HTMLIonEventsElement: {
|
||||||
|
prototype: HTMLIonEventsElement;
|
||||||
|
new (): HTMLIonEventsElement;
|
||||||
|
};
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"ion-events": HTMLIonEventsElement;
|
||||||
|
}
|
||||||
|
interface ElementTagNameMap {
|
||||||
|
"ion-events": HTMLIonEventsElement;
|
||||||
|
}
|
||||||
|
namespace JSX {
|
||||||
|
interface IntrinsicElements {
|
||||||
|
"ion-events": JSXElements.IonEventsAttributes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
namespace JSXElements {
|
||||||
|
export interface IonEventsAttributes extends HTMLAttributes {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
FabButton as IonFabButton
|
FabButton as IonFabButton
|
||||||
} from './components/fab-button/fab-button';
|
} from './components/fab-button/fab-button';
|
||||||
|
52
packages/core/src/components/events/events.tsx
Normal file
52
packages/core/src/components/events/events.tsx
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
import { Component, Listen, Method } from '@stencil/core';
|
||||||
|
|
||||||
|
const subscriptions = new Map<string, ((event?: any) => void)[]>();
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
tag: 'ion-events'
|
||||||
|
})
|
||||||
|
export class Events {
|
||||||
|
|
||||||
|
@Method()
|
||||||
|
subscribe(topic: string, handler: (event?: any) => void) {
|
||||||
|
const handlers = subscriptions.get(topic) || [];
|
||||||
|
handlers.push(handler);
|
||||||
|
subscriptions.set(topic, handlers);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Method()
|
||||||
|
unsubscribe(topic: string, handler: (event?: any) => void) {
|
||||||
|
const handlers = subscriptions.get(topic) || [];
|
||||||
|
const newHandlers = handlers.filter(fun => fun !== handler);
|
||||||
|
subscriptions.set(topic, newHandlers);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Method()
|
||||||
|
publish(topic: string, event?: any) {
|
||||||
|
return publishImpl(topic, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Listen('window:online')
|
||||||
|
@Listen('window:offline')
|
||||||
|
@Listen('window:orientationchange')
|
||||||
|
online(event: Event) {
|
||||||
|
return publishImpl(`app:${event.type}`, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Listen('window:statusTap')
|
||||||
|
statusTap(event: Event) {
|
||||||
|
console.log('TODO: Finish the status tap: ', event);
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// make this method async just to give the browser a chance to chill and do what it needs to do before firing this off
|
||||||
|
function publishImpl(topic: string, event: any): Promise<any> {
|
||||||
|
return Promise.resolve().then(() => {
|
||||||
|
const handlers = subscriptions.get(topic) || [];
|
||||||
|
for (const handler of handlers) {
|
||||||
|
handler(event);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
22
packages/core/src/components/events/readme.md
Normal file
22
packages/core/src/components/events/readme.md
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
# ion-events
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<!-- Auto Generated Below -->
|
||||||
|
|
||||||
|
|
||||||
|
## Methods
|
||||||
|
|
||||||
|
#### publish()
|
||||||
|
|
||||||
|
|
||||||
|
#### subscribe()
|
||||||
|
|
||||||
|
|
||||||
|
#### unsubscribe()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
----------------------------------------------
|
||||||
|
|
||||||
|
*Built by [StencilJS](https://stenciljs.com/)*
|
55
packages/core/src/components/events/test/basic/e2e.js
Normal file
55
packages/core/src/components/events/test/basic/e2e.js
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const { By, until } = require('selenium-webdriver');
|
||||||
|
const { register, Page, platforms } = require('../../../../../scripts/e2e');
|
||||||
|
const expect = require('chai').expect;
|
||||||
|
|
||||||
|
class E2ETestPage extends Page {
|
||||||
|
constructor(driver, platform) {
|
||||||
|
super(driver, `http://localhost:3333/src/components/events/test/basic?ionicplatform=${platform}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
platforms.forEach(platform => {
|
||||||
|
describe('events/basic', () => {
|
||||||
|
|
||||||
|
register('should init', driver => {
|
||||||
|
const page = new E2ETestPage(driver, platform);
|
||||||
|
return page.navigate();
|
||||||
|
});
|
||||||
|
|
||||||
|
register('subscribers should receive event on or shortly after button click', async (driver, testContext) => {
|
||||||
|
|
||||||
|
testContext.timeout(1000);
|
||||||
|
const page = new E2ETestPage(driver, platform);
|
||||||
|
|
||||||
|
// go to page two
|
||||||
|
const publishButtonSelector = 'ion-button.publish.hydrated';
|
||||||
|
const publishButton = await getElement(driver, publishButtonSelector);
|
||||||
|
publishButton.click();
|
||||||
|
await wait(300);
|
||||||
|
|
||||||
|
const secretOneElement = await getElement(driver, '.secret-one');
|
||||||
|
const secretOneText = await secretOneElement.getText();
|
||||||
|
expect(secretOneText).to.equal('Taco');
|
||||||
|
|
||||||
|
const secretTwoElement = await getElement(driver, '.secret-two');
|
||||||
|
const secretTwoText = await secretTwoElement.getText();
|
||||||
|
expect(secretTwoText).to.equal('Burrito');
|
||||||
|
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
async function getElement(driver, selector) {
|
||||||
|
driver.wait(until.elementLocated(By.css(selector)));
|
||||||
|
const element = driver.findElement(By.css(selector));
|
||||||
|
await driver.wait(until.elementIsVisible(driver.findElement(By.css(selector))));
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
|
||||||
|
function wait(duration) {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
setTimeout(resolve, duration);
|
||||||
|
})
|
||||||
|
}
|
66
packages/core/src/components/events/test/basic/index.html
Normal file
66
packages/core/src/components/events/test/basic/index.html
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html dir="ltr">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Events</title>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover">
|
||||||
|
<script src="/dist/ionic.js"></script>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body onload="init()">
|
||||||
|
<ion-events></ion-events>
|
||||||
|
<ion-app>
|
||||||
|
<ion-page>
|
||||||
|
<ion-header>
|
||||||
|
<ion-toolbar>
|
||||||
|
<ion-title>Events</ion-title>
|
||||||
|
</ion-toolbar>
|
||||||
|
</ion-header>
|
||||||
|
|
||||||
|
<ion-content padding>
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<div>
|
||||||
|
The first secret is: <span class="secret-one">TBD</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
The second secret is: <span class="secret-two">TBD</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<ion-button class="publish">Publish the data</ion-button>
|
||||||
|
</ion-content>
|
||||||
|
|
||||||
|
</ion-page>
|
||||||
|
</ion-app>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
async function runTest() {
|
||||||
|
const ionEvents = document.querySelector('ion-events');
|
||||||
|
await ionEvents.componentOnReady();
|
||||||
|
|
||||||
|
ionEvents.subscribe('secretOne', (secretOne) => {
|
||||||
|
const span = document.querySelector('.secret-one');
|
||||||
|
span.textContent = secretOne;
|
||||||
|
});
|
||||||
|
|
||||||
|
ionEvents.subscribe('secretTwo', (secretTwo) => {
|
||||||
|
const span = document.querySelector('.secret-two');
|
||||||
|
span.textContent = secretTwo;
|
||||||
|
});
|
||||||
|
|
||||||
|
const publishButton = document.querySelector('ion-button .publish');
|
||||||
|
publishButton.addEventListener('click', async () => {
|
||||||
|
await ionEvents.publish('secretOne', 'Taco');
|
||||||
|
await ionEvents.publish('secretTwo', 'Burrito');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function init() {
|
||||||
|
setTimeout(runTest(), 100);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
@ -14,6 +14,7 @@ exports.config = {
|
|||||||
{ components: ['ion-checkbox'] },
|
{ components: ['ion-checkbox'] },
|
||||||
{ components: ['ion-chip', 'ion-chip-button'] },
|
{ components: ['ion-chip', 'ion-chip-button'] },
|
||||||
{ components: ['ion-datetime', 'ion-picker', 'ion-picker-column', 'ion-picker-controller'] },
|
{ components: ['ion-datetime', 'ion-picker', 'ion-picker-column', 'ion-picker-controller'] },
|
||||||
|
{ components: ['ion-events'] },
|
||||||
{ components: ['ion-fab', 'ion-fab-list'] },
|
{ components: ['ion-fab', 'ion-fab-list'] },
|
||||||
{ components: ['ion-fab-button'] },
|
{ components: ['ion-fab-button'] },
|
||||||
{ components: ['ion-gesture'], priority: 'low' },
|
{ components: ['ion-gesture'], priority: 'low' },
|
||||||
|
Reference in New Issue
Block a user