ripple 0.2.79 → 0.2.81

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -18,6 +18,7 @@ export type Dependency = {
18
18
  };
19
19
 
20
20
  export type Tracked = {
21
+ a: { get?: Function, set?: Function };
21
22
  b: Block;
22
23
  c: number;
23
24
  f: number;
@@ -25,6 +26,7 @@ export type Tracked = {
25
26
  };
26
27
 
27
28
  export type Derived = {
29
+ a: { get?: Function, set?: Function };
28
30
  b: Block;
29
31
  blocks: null | Block[];
30
32
  c: number;
@@ -114,6 +114,10 @@ export function spread_attrs(attrs, css_hash) {
114
114
 
115
115
  if (typeof value === 'function') continue;
116
116
 
117
+ if (is_tracked_object(value)) {
118
+ value = get(value);
119
+ }
120
+
117
121
  if (name === 'class' && css_hash) {
118
122
  value = (value == null ? '' : value) + ' ' + css_hash;
119
123
  }
@@ -1,6 +1,6 @@
1
1
  // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2
2
 
3
- exports[`basic > basic operations 1`] = `
3
+ exports[`basic client > basic operations 1`] = `
4
4
  <div>
5
5
  <div>
6
6
  0
@@ -18,25 +18,7 @@ exports[`basic > basic operations 1`] = `
18
18
  </div>
19
19
  `;
20
20
 
21
- exports[`basic > correctly renders adjacent text nodes 1`] = `
22
- <div>
23
- <div>
24
- 11
25
- </div>
26
- <div>
27
- 11
28
- </div>
29
- <div>
30
- truetrue
31
- </div>
32
- <div>
33
- truetrue
34
- </div>
35
-
36
- </div>
37
- `;
38
-
39
- exports[`basic > handles boolean attributes with no prop value provides 1`] = `
21
+ exports[`basic client > handles boolean attributes with no prop value provides 1`] = `
40
22
  <div>
41
23
  <div
42
24
  class="container"
@@ -55,7 +37,7 @@ exports[`basic > handles boolean attributes with no prop value provides 1`] = `
55
37
  </div>
56
38
  `;
57
39
 
58
- exports[`basic > render semi-dynamic text 1`] = `
40
+ exports[`basic client > render semi-dynamic text 1`] = `
59
41
  <div>
60
42
  <div>
61
43
  Hello World
@@ -64,7 +46,7 @@ exports[`basic > render semi-dynamic text 1`] = `
64
46
  </div>
65
47
  `;
66
48
 
67
- exports[`basic > render spread props without duplication 1`] = `
49
+ exports[`basic client > render spread props without duplication 1`] = `
68
50
  <div>
69
51
  <div>
70
52
  <input
@@ -78,7 +60,7 @@ exports[`basic > render spread props without duplication 1`] = `
78
60
  </div>
79
61
  `;
80
62
 
81
- exports[`basic > render static attributes 1`] = `
63
+ exports[`basic client > render static attributes 1`] = `
82
64
  <div>
83
65
  <div
84
66
  class="foo"
@@ -91,7 +73,7 @@ exports[`basic > render static attributes 1`] = `
91
73
  </div>
92
74
  `;
93
75
 
94
- exports[`basic > render static text 1`] = `
76
+ exports[`basic client > render static text 1`] = `
95
77
  <div>
96
78
  <div>
97
79
  Hello World
@@ -100,7 +82,7 @@ exports[`basic > render static text 1`] = `
100
82
  </div>
101
83
  `;
102
84
 
103
- exports[`basic > renders simple JS expression logic correctly 1`] = `
85
+ exports[`basic client > renders simple JS expression logic correctly 1`] = `
104
86
  <div>
105
87
  <div>
106
88
  {"0":"Test"}
@@ -112,7 +94,7 @@ exports[`basic > renders simple JS expression logic correctly 1`] = `
112
94
  </div>
113
95
  `;
114
96
 
115
- exports[`basic > should handle lexical scopes correctly 1`] = `
97
+ exports[`basic client > should handle lexical scopes correctly 1`] = `
116
98
  <div>
117
99
  <section>
118
100
  Nested scope variable
@@ -2,7 +2,7 @@ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
2
2
  import { mount, flushSync, effect, track } from 'ripple';
3
3
  import { compile } from 'ripple/compiler';
4
4
 
5
- describe('basic', () => {
5
+ describe('basic client', () => {
6
6
  let container;
7
7
 
8
8
  function render(component) {
@@ -1276,7 +1276,12 @@ describe('basic', () => {
1276
1276
  let logs = [];
1277
1277
 
1278
1278
  component App() {
1279
- let count = track(0);
1279
+ let count = track(0, {
1280
+ set(v) {
1281
+ logs.push('inside setter');
1282
+ return v;
1283
+ }
1284
+ });
1280
1285
  let name = track('Click Me');
1281
1286
 
1282
1287
  function buttonRef(el) {
@@ -1289,7 +1294,7 @@ describe('basic', () => {
1289
1294
  <Child
1290
1295
  class="my-button"
1291
1296
  onClick={() => @name === 'Click Me' ? @name = 'Clicked' : @name = 'Click Me'}
1292
- count:={() => @count, (v) => {logs.push('inside setter'); @count++}}
1297
+ count={@count}
1293
1298
  {ref buttonRef}
1294
1299
  >{@name}</Child>;
1295
1300
  }
@@ -663,5 +663,41 @@ describe('composite components', () => {
663
663
 
664
664
  render(App);
665
665
  expect(container).toMatchSnapshot();
666
- })
666
+ });
667
+
668
+ it('handles dynamic component switching', () => {
669
+ component Child1() {
670
+ <div>{"I am child 1"}</div>
671
+ }
672
+
673
+ component Child2() {
674
+ <div>{"I am child 2"}</div>
675
+ }
676
+
677
+ component App() {
678
+ let thing = track(() => Child1)
679
+
680
+ <div id="container">
681
+ <@thing />
682
+ </div>
683
+
684
+ <button onClick={() => @thing = @thing === Child1 ? Child2 : Child1}>{"Change Child"}</button>
685
+ }
686
+
687
+ render(App);
688
+ flushSync();
689
+
690
+ expect(container.querySelector('#container').textContent).toBe('I am child 1');
691
+
692
+ const button = container.querySelector('button');
693
+ button.click();
694
+ flushSync();
695
+
696
+ expect(container.querySelector('#container').textContent).toBe('I am child 2');
697
+
698
+ button.click();
699
+ flushSync();
700
+
701
+ expect(container.querySelector('#container').textContent).toBe('I am child 1');
702
+ });
667
703
  });
@@ -0,0 +1,15 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { render } from 'ripple/server';
3
+
4
+ describe('basic client', () => {
5
+ it('render static text', async () => {
6
+ component Basic() {
7
+ <div>{'Hello World'}</div>
8
+ }
9
+
10
+ const { head, body } = await render(Basic);
11
+
12
+ expect(head).toBe('');
13
+ expect(body).toBe('<div>Hello World</div>');
14
+ });
15
+ });
package/types/index.d.ts CHANGED
@@ -89,13 +89,13 @@ type RestKeys<T, K extends readonly (keyof T)[]> = Expand<Omit<T, K[number]>>;
89
89
  type SplitResult<T extends Props, K extends readonly (keyof T)[]> =
90
90
  [...PickKeys<T, K>, UnwrapTracked<RestKeys<T, K>>];
91
91
 
92
- type TrackOptions = { split?: readonly (string | number | symbol)[] };
92
+ type TrackOptions<T> = { split?: readonly (string | number | symbol)[], get?: (v: T) => T, set?: (v: T) => T };
93
93
 
94
94
  export declare function track<V>(value?: V | (() => V)): Tracked<V>;
95
95
 
96
96
  export declare function track<V extends Props, const K extends readonly (keyof V)[]>(
97
97
  value: V,
98
- options: TrackOptions
98
+ options: TrackOptions<V>
99
99
  ): SplitResult<V, K>;
100
100
 
101
101
  export function on<Type extends keyof WindowEventMap>(
package/types/server.d.ts CHANGED
@@ -0,0 +1,4 @@
1
+
2
+ export declare async function render(
3
+ component: () => void,
4
+ ): { head: string; body: string };
@@ -1,146 +0,0 @@
1
- import { describe, it, expect, beforeEach, afterEach } from 'vitest';
2
- import { mount, flushSync, effect, track } from 'ripple';
3
-
4
- describe('prop accessors', () => {
5
- let container;
6
-
7
- function render(component) {
8
- mount(component, {
9
- target: container
10
- });
11
- }
12
-
13
- beforeEach(() => {
14
- container = document.createElement('div');
15
- document.body.appendChild(container);
16
- });
17
-
18
- afterEach(() => {
19
- document.body.removeChild(container);
20
- container = null;
21
- });
22
-
23
- it('should render a basic prop accessor on a composite component', () => {
24
- const logs = [];
25
-
26
- component Child(props) {
27
- effect(() => {
28
- logs.push('App effect', props.@foo);
29
- });
30
-
31
- <button onClick={() => props.@foo++}>{"Increment foo"}</button>
32
- }
33
-
34
- component App(props) {
35
- let foo = track(0);
36
-
37
- <Child {foo} />
38
-
39
- <div>{"parent foo: " + @foo}</div>
40
-
41
- <button onClick={() => { @foo++; }}>{"Increment parent foo"}</button>
42
- }
43
-
44
- render(App);
45
- flushSync();
46
-
47
- expect(container.querySelectorAll('div')[0].textContent).toBe('parent foo: 0');
48
- expect(logs).toEqual(['App effect', 0]);
49
-
50
- const [button, button2] = container.querySelectorAll('button');
51
-
52
- button.click();
53
- flushSync();
54
-
55
- expect(container.querySelectorAll('div')[0].textContent).toBe('parent foo: 1');
56
- expect(logs).toEqual(['App effect', 0, 'App effect', 1]);
57
-
58
- button2.click();
59
- flushSync();
60
-
61
- expect(container.querySelectorAll('div')[0].textContent).toBe('parent foo: 2');
62
- expect(logs).toEqual(['App effect', 0, 'App effect', 1, 'App effect', 2]);
63
- });
64
-
65
- // Note: Disabled this test for further investigation.
66
- it.skip('should render a basic prop accessor on a composite component #2', () => {
67
- const logs = [];
68
-
69
- component Child({ foo }) {
70
- effect(() => {
71
- logs.push('App effect', props.foo);
72
- });
73
-
74
- <button onClick={() => props.foo++}>{"Increment foo"}</button>
75
- }
76
-
77
- component App() {
78
- let foo = track(0);
79
-
80
- <Child foo:={() => {
81
- return @foo;
82
- }, v => {
83
- // do not update parent
84
- }} />
85
-
86
- <div>{"parent foo: " + @foo}</div>
87
-
88
- <button onClick={() => { foo++ }}>{"Increment parent foo"}</button>
89
- }
90
-
91
- render(App);
92
- flushSync();
93
-
94
- expect(container.querySelectorAll('div')[0].textContent).toBe('parent foo: 0');
95
- expect(logs).toEqual(['App effect', 0]);
96
-
97
- const [button, button2] = container.querySelectorAll('button');
98
-
99
- button.click();
100
- flushSync();
101
-
102
- expect(container.querySelectorAll('div')[0].textContent).toBe('parent foo: 0');
103
- expect(logs).toEqual(['App effect', 0]);
104
-
105
- button2.click();
106
- flushSync();
107
-
108
- expect(container.querySelectorAll('div')[0].textContent).toBe('parent foo: 1');
109
- expect(logs).toEqual(['App effect', 0, 'App effect', 1]);
110
-
111
- button2.click();
112
- flushSync();
113
-
114
- expect(container.querySelectorAll('div')[0].textContent).toBe('parent foo: 2');
115
- expect(logs).toEqual(['App effect', 0, 'App effect', 1, 'App effect', 2]);
116
- });
117
-
118
- it('handles a simple getter prop accessor with no setter', () =>{
119
- component Parent() {
120
- let value = track(123);
121
-
122
- <Child value:={() => @value} />
123
-
124
- <button onClick={() => { @value++ }}>{"Increment value"}</button>
125
- }
126
-
127
- component Child({ value }) {
128
- <div>{value}</div>
129
- }
130
-
131
- render(Parent);
132
-
133
- expect(container.querySelector('div').textContent).toBe('123');
134
-
135
- const button = container.querySelector('button');
136
- button.click();
137
- flushSync();
138
-
139
- expect(container.querySelector('div').textContent).toBe('124');
140
-
141
- button.click();
142
- flushSync();
143
-
144
- expect(container.querySelector('div').textContent).toBe('125');
145
- });
146
- });