mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-17 02:31:34 +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 {
|
||||
FabButton as IonFabButton
|
||||
} 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-chip', 'ion-chip-button'] },
|
||||
{ components: ['ion-datetime', 'ion-picker', 'ion-picker-column', 'ion-picker-controller'] },
|
||||
{ components: ['ion-events'] },
|
||||
{ components: ['ion-fab', 'ion-fab-list'] },
|
||||
{ components: ['ion-fab-button'] },
|
||||
{ components: ['ion-gesture'], priority: 'low' },
|
||||
|
Reference in New Issue
Block a user