Skip to content

Commit 8d71d8b

Browse files
author
TanmayRanaware
committed
Merge branch 'master' into unit-test-mixin
2 parents 241ae5d + cd1b034 commit 8d71d8b

File tree

4 files changed

+164
-133
lines changed

4 files changed

+164
-133
lines changed

README.md

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,21 +6,20 @@
66
![Discord](https://img.shields.io/discord/1392597664184537230)
77

88

9-
Rimmel is a powerful, fast and lightweight JavaScript UI library for creating web applications with event streams.
9+
Rimmel is a powerful, fast and lightweight JavaScript UI library for creating web applications using reactive streams.
1010

11-
It implements [RML](https://github.com/ReactiveHTML/reactive-markup), the Reactive Markup which makes your HTML work with Streams in a seamless way.
11+
It implements [RML](https://github.com/ReactiveHTML/reactive-markup), the Reactive Markup which makes your HTML work with streams in a seamless way.
1212

1313
## To Busy to read?, Check [TL;DR 📜](./QUICKSTART.md)<br>
1414

1515
## Getting started
1616
If you are new to reactive streams, there is a [3m crash-course](https://medium.com/@fourtyeighthours/the-mostly-inaccurate-crash-course-for-reactive-ui-development-w-rxjs-ddbb7e5e526e) tailored for UI development with Rimmel, arguably the simplest RxJS introduction around to get you started.
1717

18-
If you are new to the functional/reactive programming mindset, this [interactive tutorial](https://reactivex.io/learnrx/) may be an especially useful introduction to map/reduce in the reactive world.
18+
If you are new to the reactive and functional programming paradigms, this [interactive tutorial](https://reactivex.io/learnrx/) may be an especially useful introduction.
1919

2020
If you come from Angular, check out [this page](./docs/migrating/angular.md)<br>
2121
If you come from React, check out [this page](./docs/migrating/react.md)<br>
2222

23-
2423
## Hello World 👋🌏🏖️😎
2524
Let's jump straight in. The "Hello World" for reactive user interfaces is the classic click counter: one button, you click it, ze counts it.
2625

rollup.config.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ import { join } from 'path';
44
import typescript from '@rollup/plugin-typescript';
55
import { visualizer } from 'rollup-plugin-visualizer';
66

7-
const getTSConfig = async (path) => {
8-
const tsConfig = (await import('./tsconfig.json', { assert: { type: 'json' } })).default;
7+
import tsConfig from './tsconfig.json' with { type: 'json' };
98

9+
const getTSConfig = (path) => {
1010
tsConfig.compilerOptions.outDir = path;
1111
tsConfig.compilerOptions.declarationDir = join(path, 'types');
1212
return tsConfig.compilerOptions;
@@ -23,7 +23,7 @@ export default [
2323
nodeResolve({ preferBuiltins: true }),
2424
// json(),
2525
typescript({
26-
...await getTSConfig('dist/globaljs'),
26+
...getTSConfig('dist/globaljs'),
2727
sourceMap: true,
2828
outDir: 'dist/globaljs',
2929
declaration: false,
@@ -58,7 +58,7 @@ export default [
5858
nodeResolve({ preferBuiltins: true }),
5959
json(),
6060
typescript({
61-
...await getTSConfig('dist/esm'),
61+
...getTSConfig('dist/esm'),
6262
}),
6363
visualizer({ filename: 'bundle-stats-esm.html' }),
6464
],
@@ -87,7 +87,7 @@ export default [
8787
nodeResolve({ preferBuiltins: true }),
8888
json(),
8989
typescript({
90-
...await getTSConfig('dist/ssr'),
90+
...getTSConfig('dist/ssr'),
9191
sourceMap: true,
9292
outDir: 'dist/ssr',
9393
declaration: true,

src/sources/swap-source.test.ts

Lines changed: 147 additions & 116 deletions
Original file line numberDiff line numberDiff line change
@@ -1,126 +1,157 @@
11
import type { Observable } from 'rxjs';
22

33
import { Subject } from 'rxjs';
4-
import { MockElement, MockEvent } from '../test-support';
54
import { Swap, swap } from './swap-source';
5+
import { MockElement, MockEvent } from '../test-support';
66

77
describe('Swap Event Adapter', () => {
8+
it('Swaps a value from an element with a static string', () => {
9+
const oldValue = 'old data';
10+
const newValue = 'new data';
11+
const el = MockElement({
12+
tagName: "INPUT",
13+
type: 'text',
14+
value: oldValue,
15+
});
16+
const eventData = MockEvent('input', {
17+
target: el as HTMLInputElement
18+
});
19+
const handlerSpy = jest.fn();
20+
const source = Swap(newValue, handlerSpy);
21+
22+
source.next(eventData);
23+
24+
expect(handlerSpy).toHaveBeenCalledWith(oldValue);
25+
expect(el.value).toEqual(newValue);
26+
})
27+
})
28+
29+
it('Swaps a value from an element with empty string by default', () => {
30+
const oldValue = 'old data';
31+
const el = MockElement({
32+
tagName: 'INPUT',
33+
type: 'text',
34+
value: oldValue,
35+
});
36+
const eventData = MockEvent('input', {
37+
target: el as HTMLInputElement
38+
});
39+
const handlerSpy = jest.fn();
40+
const source = Swap(undefined, handlerSpy);
41+
42+
source.next(eventData);
43+
44+
expect(handlerSpy).toHaveBeenCalledWith(oldValue);
45+
expect(el.value).toEqual('');
46+
});
847

9-
it('Swaps a value in an element with a static string', () => {
10-
const oldValue = 'old data';
11-
const newValue = 'new data';
12-
13-
const el = MockElement({
14-
tagName: 'INPUT',
15-
type: 'text',
16-
value: oldValue,
17-
});
18-
19-
const eventData = MockEvent('input', {
20-
target: el as HTMLInputElement
21-
});
22-
23-
const handlerSpy = jest.fn();
24-
const source = Swap(newValue)(handlerSpy);
25-
source.next(eventData);
26-
27-
expect(handlerSpy).toHaveBeenCalledWith(oldValue);
28-
expect(el.value).toEqual(newValue);
29-
});
30-
31-
it('Swaps a value in an element using a function', () => {
32-
const oldValue = 'old data';
33-
34-
const el = MockElement({
35-
tagName: 'INPUT',
36-
type: 'text',
37-
value: oldValue,
38-
});
39-
40-
const eventData = MockEvent('input', {
41-
target: el as HTMLInputElement
42-
});
43-
44-
const replaceFn = (v: string) => v.toUpperCase();
45-
46-
const handlerSpy = jest.fn();
47-
const source = Swap(replaceFn)(handlerSpy);
48-
source.next(eventData);
49-
50-
expect(handlerSpy).toHaveBeenCalledWith(oldValue);
51-
expect(el.value).toEqual('OLD DATA');
52-
});
53-
54-
it('Swaps a value in an element with empty string by default', () => {
55-
const oldValue = 'old data';
56-
57-
const el = MockElement({
58-
tagName: 'INPUT',
59-
type: 'text',
60-
value: oldValue,
61-
});
62-
63-
const eventData = MockEvent('input', {
64-
target: el as HTMLInputElement
65-
});
66-
67-
const handlerSpy = jest.fn();
68-
const source = Swap()(handlerSpy);
69-
source.next(eventData);
70-
71-
expect(handlerSpy).toHaveBeenCalledWith(oldValue);
72-
expect(el.value).toEqual('');
73-
});
74-
48+
it('Swaps a value using a function that transforms based on the old value', () => {
49+
const oldValue = '5';
50+
const replacementFn = (v: string) => String(Number(v) * 2);
51+
const el = MockElement({
52+
tagName: 'INPUT',
53+
type: 'text',
54+
value: oldValue,
55+
});
56+
const eventData = MockEvent('input', {
57+
target: el as HTMLInputElement
58+
});
59+
const handlerSpy = jest.fn();
60+
const source = Swap(replacementFn, handlerSpy);
61+
62+
source.next(eventData);
63+
64+
expect(handlerSpy).toHaveBeenCalledWith(oldValue);
65+
expect(el.value).toEqual('10');
7566
});
7667

7768
describe('swap Event Operator', () => {
78-
79-
it('Swaps and emits a value from an element with static string', () => {
80-
const oldValue = 'old data';
81-
const newValue = 'new data';
82-
83-
const el = MockElement({
84-
tagName: 'INPUT',
85-
type: 'text',
86-
value: oldValue,
87-
});
88-
89-
const eventData = MockEvent('input', {
90-
target: el as HTMLInputElement
91-
});
92-
93-
const handlerSpy = jest.fn();
94-
const pipeline = new Subject<typeof eventData>().pipe(swap(newValue)) as Observable<string> & Subject<typeof eventData>;
95-
pipeline.subscribe(x => handlerSpy(x));
96-
pipeline.next(eventData);
97-
98-
expect(handlerSpy).toHaveBeenCalledWith(oldValue);
99-
expect(el.value).toEqual(newValue);
100-
});
101-
102-
it('Swaps and emits a value from an element using function', () => {
103-
const oldValue = 'old data';
104-
105-
const el = MockElement({
106-
tagName: 'INPUT',
107-
type: 'text',
108-
value: oldValue,
109-
});
110-
111-
const eventData = MockEvent('input', {
112-
target: el as HTMLInputElement
113-
});
114-
115-
const replaceFn = (v: string) => v.toUpperCase();
116-
117-
const handlerSpy = jest.fn();
118-
const pipeline = new Subject<typeof eventData>().pipe(swap(replaceFn)) as Observable<string> & Subject<typeof eventData>;
119-
pipeline.subscribe(x => handlerSpy(x));
120-
pipeline.next(eventData);
121-
122-
expect(handlerSpy).toHaveBeenCalledWith(oldValue);
123-
expect(el.value).toEqual('OLD DATA');
124-
});
125-
126-
});
69+
it('Swaps and emits a value from an element with a static string', () => {
70+
const oldValue = 'old data';
71+
const newValue = 'new data';
72+
const el = MockElement({
73+
tagName: 'INPUT',
74+
type: 'text',
75+
value: oldValue,
76+
});
77+
const eventData = MockEvent('input', {
78+
target: el as HTMLInputElement
79+
});
80+
const handlerSpy = jest.fn();
81+
const pipeline = new Subject<typeof eventData>().pipe(swap(newValue)) as Observable<string> & Subject<typeof eventData>;
82+
83+
pipeline.subscribe(x => handlerSpy(x));
84+
pipeline.next(eventData);
85+
86+
expect(handlerSpy).toHaveBeenCalledWith(oldValue);
87+
expect(el.value).toEqual(newValue);
88+
});
89+
90+
it('Swaps and emits a value from an element with empty string', () => {
91+
const oldValue = 'old data';
92+
const el = MockElement({
93+
tagName: 'INPUT',
94+
type: 'text',
95+
value: oldValue,
96+
});
97+
const eventData = MockEvent('input', {
98+
target: el as HTMLInputElement
99+
});
100+
const handlerSpy = jest.fn();
101+
const pipeline = new Subject<typeof eventData>().pipe(swap('')) as Observable<string> & Subject<typeof eventData>;
102+
103+
pipeline.subscribe(x => handlerSpy(x));
104+
pipeline.next(eventData);
105+
106+
expect(handlerSpy).toHaveBeenCalledWith(oldValue);
107+
expect(el.value).toEqual('');
108+
});
109+
110+
111+
112+
it('Swaps a value using a function that generates new value from old', () => {
113+
const oldValue = 'test';
114+
const replacementFn = (v: string) => `${v}_modified`;
115+
const el = MockElement({
116+
tagName: 'INPUT',
117+
type: 'text',
118+
value: oldValue,
119+
});
120+
const eventData = MockEvent('input', {
121+
target: el as HTMLInputElement
122+
});
123+
const handlerSpy = jest.fn();
124+
const pipeline = new Subject<typeof eventData>().pipe(swap(replacementFn)) as Observable<string> & Subject<typeof eventData>;
125+
126+
pipeline.subscribe(x => handlerSpy(x));
127+
pipeline.next(eventData);
128+
129+
expect(handlerSpy).toHaveBeenCalledWith(oldValue);
130+
expect(el.value).toEqual('test_modified');
131+
});
132+
133+
it('Handles multiple swap operations in sequence', () => {
134+
const values = ['first', 'second', 'third'];
135+
const el = MockElement({
136+
tagName: 'INPUT',
137+
type: 'text',
138+
value: values[0],
139+
});
140+
const handlerSpy = jest.fn();
141+
const pipeline = new Subject<Event>().pipe(swap('replacement')) as Observable<string> & Subject<Event>;
142+
143+
pipeline.subscribe(x => handlerSpy(x));
144+
145+
values.forEach(val => {
146+
el.value = val;
147+
const eventData = MockEvent('input', { target: el as HTMLInputElement });
148+
pipeline.next(eventData);
149+
});
150+
151+
expect(handlerSpy).toHaveBeenCalledTimes(3);
152+
expect(handlerSpy).toHaveBeenNthCalledWith(1, 'first');
153+
expect(handlerSpy).toHaveBeenNthCalledWith(2, 'second');
154+
expect(handlerSpy).toHaveBeenNthCalledWith(3, 'third');
155+
expect(el.value).toEqual('replacement');
156+
});
157+
});

src/sources/swap-source.ts

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,11 @@ import { curry } from '../utils/curry';
77
import { EventListenerFunction } from '../types/dom';
88

99
/**
10-
* An Event Source Operator that "cuts" the value of the underlying <input> element
11-
* and resets it to the provided value or empty otherwise
12-
* @param handler A handler function or observer to send events to
13-
* @returns EventSource<string>
10+
* An Event Operator that swaps the value of the underlying <input> element
11+
* with the provided replacement (or empty string by default) and emits the previous value.
12+
* This operator mutates the element's value as a side effect.
13+
* @param replacement A string or function used to compute the new value
14+
* @returns OperatorFunction<Event, string>
1415
*/
1516
export const swap = <E extends Event>(replacement: string | Function) =>
1617
map((e: E) => {
@@ -22,10 +23,10 @@ export const swap = <E extends Event>(replacement: string | Function) =>
2223
;
2324

2425
/**
25-
* An Event Source that "cuts" the value of the underlying &lt;input&gt; element
26-
* and resets it to the provided value or empty otherwise
27-
* @param replacement A new value to swap the current element's value with
28-
* @param source A handler function or observer to send events to
26+
* An Event Adapter that swaps the value of the underlying &lt;input&gt; element
27+
* with the provided replacement (or empty string by default) and emits the previous value to the given target.
28+
* @param replacement A new value or function to compute the element's next value
29+
* @param source A handler function or observer to send emitted values to
2930
* @returns EventSource<string>
3031
*/
3132
export const Swap =

0 commit comments

Comments
 (0)