Skip to content

Commit ed6bf71

Browse files
Rémi Van KeisbelckRémi Van Keisbelck
authored andcommitted
Merge branch 'release/2.2.0'
2 parents 8b32827 + 788a7d5 commit ed6bf71

10 files changed

Lines changed: 281 additions & 202 deletions

File tree

CHANGELOG.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,18 @@
11
# Changelog
22

3+
## v2.1.0 (03/05/2021)
4+
5+
#### closed
6+
7+
- [**closed**] Expose UpdatedPiped [#63](https://github.com/vankeisb/react-tea-cup/pull/63)
8+
- [**closed**] Router handles URLs that ends with / [#62](https://github.com/vankeisb/react-tea-cup/pull/62)
9+
10+
#### dependencies
11+
12+
- [**dependencies**] Bump ssri from 6.0.1 to 6.0.2 [#61](https://github.com/vankeisb/react-tea-cup/pull/61)
13+
14+
---
15+
316
## v2.0.1 (21/04/2021)
417

518
#### closed

core/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "tea-cup-core",
3-
"version": "2.1.0",
3+
"version": "2.2.0",
44
"description": "react-tea-cup core classes and utilities (Maybe etc)",
55
"author": "Rémi Van Keisbelck <remi@rvkb.com>",
66
"license": "MIT",

samples/README.md

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,20 @@ test everything. They are not meant to be "good practises".
77

88
# make sure tea-cup is compiled
99
cd react-tea-cup
10-
npm install
11-
npm run compile
10+
yarn install
11+
cd core
12+
yarn run compile
13+
cd ../tea-cup
14+
yarn run compile
1215

13-
# build and run the samples
14-
cd samples
15-
npm install
16-
npm start
16+
# run the samples
17+
# With React 16 (default)
18+
cd ../samples
19+
yarn start
20+
21+
# With React 17
22+
cd ../samples
23+
yarn upgrade react@^17.0.1 react-dom@^17.0.1 @types/react@^17.0.1 @types/react-dom@^17.0.1
24+
yarn start
1725

1826
The samples app is made with `create-react-app`. See the scripts in `package.json`.

samples/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@
1111
"react": "^16.7.0",
1212
"react-dom": "^16.7.0",
1313
"react-scripts": "3.4.3",
14-
"react-tea-cup": "2.1.0",
15-
"tea-cup-core": "2.1.0"
14+
"react-tea-cup": "2.2.0",
15+
"tea-cup-core": "2.2.0"
1616
},
1717
"scripts": {
1818
"start": "react-scripts start",

samples/src/App.tsx

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ import * as Sful from './Samples/StatefulInView';
5858
import * as Rest from './Samples/Rest';
5959
import * as TimeSample from './Samples/TimeSample';
6060
import * as EventsSample from './Samples/EventsSample';
61+
import * as SelectSample from './Samples/SelectSample';
6162
import * as PortsSample from './Samples/PortsSample';
6263
import { appSamplePorts } from "./Samples/PortsSample";
6364

@@ -166,6 +167,7 @@ interface Samples {
166167
readonly rest: Rest.Model;
167168
readonly time: TimeSample.Model;
168169
readonly events: EventsSample.Model;
170+
readonly select: SelectSample.Model;
169171
readonly ports: PortsSample.Model;
170172
}
171173

@@ -180,6 +182,7 @@ type Msg =
180182
| { type: 'rest'; child: Rest.Msg }
181183
| { type: 'timeSample'; child: TimeSample.Msg }
182184
| { type: 'eventsSample'; child: EventsSample.Msg }
185+
| { type: 'selectSample'; child: SelectSample.Msg }
183186
| { type: 'portsSample'; child: PortsSample.Msg }
184187
| { type: 'urlChange'; location: Location }
185188
| { type: 'newUrl'; url: string }
@@ -199,6 +202,7 @@ function initSamples(): [Model, Cmd<Msg>] {
199202
const rest = Rest.init();
200203
const time = TimeSample.init();
201204
const events = EventsSample.init();
205+
const select = SelectSample.init();
202206
const ports = PortsSample.init();
203207
return [
204208
{
@@ -214,6 +218,7 @@ function initSamples(): [Model, Cmd<Msg>] {
214218
rest: rest[0],
215219
time: time[0],
216220
events: events[0],
221+
select: select[0],
217222
ports: ports[0],
218223
},
219224
},
@@ -228,6 +233,7 @@ function initSamples(): [Model, Cmd<Msg>] {
228233
rest[1].map(mapRest),
229234
time[1].map(mapTimeSample),
230235
events[1].map(mapEventsSample),
236+
select[1].map(mapSelectSample),
231237
ports[1].map(mapPortsSample),
232238
]),
233239
];
@@ -350,6 +356,13 @@ function mapEventsSample(m: EventsSample.Msg): Msg {
350356
};
351357
}
352358

359+
function mapSelectSample(m: SelectSample.Msg): Msg {
360+
return {
361+
type: 'selectSample',
362+
child: m,
363+
};
364+
}
365+
353366
function mapPortsSample(m: PortsSample.Msg): Msg {
354367
return {
355368
type: 'portsSample',
@@ -402,6 +415,7 @@ function viewHome(dispatch: Dispatcher<Msg>) {
402415
</a>
403416
.
404417
</p>
418+
<pre>React version: {React.version}</pre>
405419
</div>
406420
);
407421
}
@@ -576,6 +590,8 @@ function viewSamples(dispatch: Dispatcher<Msg>, samples: Samples) {
576590
{TimeSample.view(map(dispatch, mapTimeSample), samples.time)}
577591
<h2>Events</h2>
578592
{EventsSample.view(map(dispatch, mapEventsSample), samples.events)}
593+
<h2>Select</h2>
594+
{SelectSample.view(map(dispatch, mapSelectSample), samples.select)}
579595
<h2>Ports</h2>
580596
{PortsSample.view(map(dispatch, mapPortsSample), samples.ports)}
581597
<button onClick={() => {
@@ -657,6 +673,12 @@ function update(msg: Msg, model: Model): [Model, Cmd<Msg>] {
657673
return [{ ...s, events: macEvents[0] }, macEvents[1].map(mapEventsSample)];
658674
});
659675

676+
case 'selectSample':
677+
return mapSample((s: Samples) => {
678+
const macSelect = SelectSample.update(msg.child, s.select);
679+
return [{ ...s, select: macSelect[0] }, macSelect[1].map(mapSelectSample)];
680+
});
681+
660682
case 'urlChange':
661683
return init(msg.location);
662684

@@ -672,13 +694,14 @@ function update(msg: Msg, model: Model): [Model, Cmd<Msg>] {
672694
function subscriptions(model: Model): Sub<Msg> {
673695
switch (model.tag) {
674696
case 'samples':
675-
const { counter, parentChild, raf, time, events } = model.samples;
697+
const { counter, parentChild, raf, time, events, select } = model.samples;
676698
return Sub.batch([
677699
Counter.subscriptions(counter).map(mapCounter),
678700
ParentChild.subscriptions(parentChild).map(mapParentChild),
679701
Raf.subscriptions(raf).map(mapRaf),
680702
TimeSample.subscriptions(time).map(mapTimeSample),
681703
EventsSample.subscriptions(events).map(mapEventsSample),
704+
SelectSample.subscriptions(select).map(mapSelectSample),
682705
PortsSample.subscriptions().map(mapPortsSample),
683706
]);
684707
default:
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
/*
2+
* MIT License
3+
*
4+
* Copyright (c) 2019 Rémi Van Keisbelck
5+
*
6+
* Permission is hereby granted, free of charge, to any person obtaining a copy
7+
* of this software and associated documentation files (the "Software"), to deal
8+
* in the Software without restriction, including without limitation the rights
9+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
* copies of the Software, and to permit persons to whom the Software is
11+
* furnished to do so, subject to the following conditions:
12+
*
13+
* The above copyright notice and this permission notice shall be included in all
14+
* copies or substantial portions of the Software.
15+
*
16+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22+
* SOFTWARE.
23+
*
24+
*/
25+
26+
import * as React from 'react';
27+
import {Cmd, Dispatcher, just, Maybe, noCmd, nothing, Sub} from 'tea-cup-core';
28+
import {DocumentEvents} from "react-tea-cup";
29+
30+
export type Model = {
31+
selected: Maybe<string>
32+
mouseUpCounter: number
33+
};
34+
35+
export type Msg = {
36+
type: 'selected',
37+
value: string
38+
}
39+
| { type: 'mouse-up' };
40+
41+
export function init(): [Model, Cmd<Msg>] {
42+
return noCmd({
43+
selected: nothing,
44+
mouseUpCounter: 0
45+
});
46+
}
47+
48+
export function view(dispatch: Dispatcher<Msg>, model: Model) {
49+
const value = model.selected.withDefault("select me");
50+
console.log("rendering value", value)
51+
return (
52+
<div className="select">
53+
<p><em>(Use me with Firefox, too.)</em></p>
54+
<select value={value}
55+
onChange={e => {
56+
console.log("FW onChange", e.target.value)
57+
dispatch({type: 'selected', value: e.target.value});
58+
}}
59+
>
60+
<option value={"select me"}>Select me...</option>
61+
{Array.of(1, 2, 3, 4, 5)
62+
.map(i =>
63+
<option
64+
key={i}
65+
value={i}>
66+
{`Option ${i}`}
67+
</option>)
68+
}
69+
</select>
70+
<br/>
71+
<p>{`Saw ${model.mouseUpCounter} mouse up events.`}</p>
72+
</div>
73+
);
74+
}
75+
76+
export function update(msg: Msg, model: Model): [Model, Cmd<Msg>] {
77+
switch (msg.type) {
78+
case 'selected': {
79+
const model1: Model = {
80+
...model,
81+
selected: msg.value !== 'select me' ? just(msg.value) : nothing
82+
};
83+
return [model1, Cmd.none()];
84+
}
85+
case "mouse-up": {
86+
const model1: Model = {
87+
...model,
88+
mouseUpCounter: model.mouseUpCounter + 1
89+
};
90+
return [model1, Cmd.none()];
91+
}
92+
}
93+
}
94+
95+
const documentEvents = new DocumentEvents<Msg>();
96+
97+
export function subscriptions(model: Model): Sub<Msg> {
98+
return Sub.batch([
99+
documentEvents.on('mouseup', (e: MouseEvent) => (
100+
{
101+
type: 'mouse-up',
102+
} as Msg
103+
), {passive: true, capture: false}),
104+
]);
105+
}

tea-cup/package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "react-tea-cup",
3-
"version": "2.1.0",
3+
"version": "2.2.0",
44
"description": "Put some TEA in your React.",
55
"author": "Rémi Van Keisbelck <remi@rvkb.com>",
66
"license": "MIT",
@@ -21,8 +21,8 @@
2121
},
2222
"dependencies": {},
2323
"peerDependencies": {
24-
"react": "^16.7.0",
25-
"tea-cup-core": "^2.1.0"
24+
"react": "^16.7.0 || ^17.0.1",
25+
"tea-cup-core": "^2.2.0"
2626
},
2727
"devDependencies": {
2828
"@types/jsdom": "^16.2.5",

tea-cup/src/TeaCup/DocumentEvents.test.tsx

Lines changed: 19 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -51,30 +51,30 @@ describe('DocumentEvents Test', () => {
5151
expect(addSpy.mock.calls.length).toBe(1);
5252
});
5353

54-
it('second sub adds no listener', () => {
54+
it('second sub adds another listener', () => {
5555
const sub = documentEvents.on('click', (e) => 'clicked');
5656
sub.init(() => ({}));
5757

5858
expect(addSpy.mock.calls.length).toBe(1);
5959

60-
const sub2 = documentEvents.on('click', (e) => 'clicked2');
60+
const sub2 = documentEvents.on('click', (e) => 'clicked2', true);
6161
sub2.init(() => ({}));
6262

63-
expect(addSpy.mock.calls.length).toBe(1);
63+
expect(addSpy.mock.calls.length).toBe(2);
6464
});
6565

66-
it('last sub removes listener', () => {
66+
it('all subs remove listeners', () => {
6767
const sub = documentEvents.on('click', (e) => 'clicked');
6868
sub.init(() => ({}));
6969

70-
const sub2 = documentEvents.on('click', (e) => 'clicked2');
70+
const sub2 = documentEvents.on('click', (e) => 'clicked2', { capture: true });
7171
sub2.init(() => ({}));
7272

7373
sub.release();
74-
expect(removeSpy.mock.calls.length).toBe(0);
74+
expect(removeSpy.mock.calls.length).toBe(1);
7575

7676
sub2.release();
77-
expect(removeSpy.mock.calls.length).toBe(1);
77+
expect(removeSpy.mock.calls.length).toBe(2);
7878
});
7979

8080
it('sub receives event from listener', () => {
@@ -93,23 +93,30 @@ describe('DocumentEvents Test', () => {
9393
expect(msgs).toEqual(['clicked1']);
9494
});
9595

96-
it('two subs receive events from listener', () => {
96+
it('each sub receive events its listener', () => {
9797
const msgs: string[] = [];
9898
const collectMsgs = (msg: string): void => {
9999
msgs.push(msg);
100100
};
101+
const msgs2: string[] = [];
102+
const collectMsgs2 = (msg: string): void => {
103+
msgs2.push(msg);
104+
};
101105

102-
const sub = documentEvents.on('click', (e) => 'clicked1');
106+
const sub = documentEvents.on('click', (e) => 'clicked');
103107
const sub2 = documentEvents.on('click', (e) => 'clicked2');
104108

105109
sub.init(collectMsgs);
106-
sub2.init(collectMsgs);
110+
sub2.init(collectMsgs2);
107111

108-
expect(addSpy.mock.calls.length).toBe(1);
112+
expect(addSpy.mock.calls.length).toBe(2);
109113
const listener = addSpy.mock.calls[0][1];
114+
const listener2 = addSpy.mock.calls[1][1];
110115

111116
listener({ event: 'event' });
112-
expect(msgs).toEqual(['clicked1', 'clicked2']);
117+
expect(msgs).toEqual(['clicked']);
118+
listener2({ event: 'event' });
119+
expect(msgs2).toEqual(['clicked2']);
113120
});
114121

115122
it('sub stops receiving events from listener', () => {
@@ -132,16 +139,4 @@ describe('DocumentEvents Test', () => {
132139
expect(msgs).toEqual(['clicked1', 'clicked1']);
133140
});
134141

135-
it('release removes all listeners', () => {
136-
const sub = documentEvents.on('click', (e) => 'clicked1');
137-
sub.init(() => ({}));
138-
const sub2 = documentEvents.on('click', (e) => 'clicked2');
139-
sub2.init(() => ({}));
140-
const sub3 = documentEvents.on('mousemove', (e) => 'moved3');
141-
sub3.init(() => ({}));
142-
expect(addSpy.mock.calls.length).toBe(2);
143-
144-
documentEvents.release();
145-
expect(removeSpy.mock.calls.length).toBe(2);
146-
});
147142
});

0 commit comments

Comments
 (0)