ripple 0.2.151 → 0.2.153

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.
Files changed (67) hide show
  1. package/README.md +3 -3
  2. package/package.json +5 -5
  3. package/src/compiler/phases/1-parse/index.js +1 -1
  4. package/src/compiler/phases/3-transform/client/index.js +37 -16
  5. package/src/compiler/phases/3-transform/server/index.js +43 -25
  6. package/src/runtime/internal/client/events.js +5 -1
  7. package/src/runtime/internal/client/index.js +2 -1
  8. package/src/runtime/internal/client/render.js +18 -15
  9. package/src/runtime/internal/client/runtime.js +78 -10
  10. package/src/runtime/internal/server/index.js +51 -11
  11. package/src/server/index.js +1 -1
  12. package/tests/client/array/array.derived.test.ripple +61 -33
  13. package/tests/client/array/array.iteration.test.ripple +3 -1
  14. package/tests/client/array/array.mutations.test.ripple +19 -15
  15. package/tests/client/array/array.static.test.ripple +115 -104
  16. package/tests/client/array/array.to-methods.test.ripple +3 -3
  17. package/tests/client/basic/basic.attributes.test.ripple +110 -57
  18. package/tests/client/basic/basic.collections.test.ripple +41 -22
  19. package/tests/client/basic/basic.errors.test.ripple +12 -6
  20. package/tests/client/basic/basic.events.test.ripple +51 -33
  21. package/tests/client/basic/basic.reactivity.test.ripple +120 -56
  22. package/tests/client/basic/basic.rendering.test.ripple +49 -19
  23. package/tests/client/basic/basic.styling.test.ripple +2 -2
  24. package/tests/client/basic/basic.utilities.test.ripple +1 -1
  25. package/tests/client/boundaries.test.ripple +70 -58
  26. package/tests/client/compiler/compiler.assignments.test.ripple +32 -4
  27. package/tests/client/compiler/compiler.attributes.test.ripple +46 -46
  28. package/tests/client/compiler/compiler.basic.test.ripple +18 -15
  29. package/tests/client/compiler/compiler.tracked-access.test.ripple +53 -42
  30. package/tests/client/compiler/compiler.typescript.test.ripple +1 -2
  31. package/tests/client/composite/composite.dynamic-components.test.ripple +6 -6
  32. package/tests/client/composite/composite.generics.test.ripple +39 -36
  33. package/tests/client/composite/composite.props.test.ripple +4 -3
  34. package/tests/client/composite/composite.reactivity.test.ripple +112 -27
  35. package/tests/client/composite/composite.render.test.ripple +9 -8
  36. package/tests/client/computed-properties.test.ripple +24 -24
  37. package/tests/client/context.test.ripple +11 -9
  38. package/tests/client/date.test.ripple +3 -1
  39. package/tests/client/dynamic-elements.test.ripple +103 -78
  40. package/tests/client/for.test.ripple +27 -17
  41. package/tests/client/head.test.ripple +42 -6
  42. package/tests/client/html.test.ripple +42 -32
  43. package/tests/client/input-value.test.ripple +4 -4
  44. package/tests/client/map.test.ripple +140 -141
  45. package/tests/client/media-query.test.ripple +31 -31
  46. package/tests/client/object.test.ripple +148 -112
  47. package/tests/client/portal.test.ripple +29 -15
  48. package/tests/client/ref.test.ripple +9 -3
  49. package/tests/client/set.test.ripple +111 -111
  50. package/tests/client/tracked-expression.test.ripple +16 -17
  51. package/tests/client/url/url.derived.test.ripple +19 -9
  52. package/tests/client/url/url.parsing.test.ripple +24 -8
  53. package/tests/client/url/url.partial-removal.test.ripple +12 -4
  54. package/tests/client/url/url.reactivity.test.ripple +63 -25
  55. package/tests/client/url/url.serialization.test.ripple +18 -6
  56. package/tests/client/url-search-params/url-search-params.derived.test.ripple +10 -6
  57. package/tests/client/url-search-params/url-search-params.iteration.test.ripple +3 -1
  58. package/tests/client/url-search-params/url-search-params.mutation.test.ripple +26 -14
  59. package/tests/client/url-search-params/url-search-params.tracked-url.test.ripple +3 -1
  60. package/tests/server/await.test.ripple +23 -22
  61. package/tests/server/basic.test.ripple +1 -1
  62. package/tests/server/compiler.test.ripple +3 -7
  63. package/tests/server/composite.test.ripple +38 -36
  64. package/tests/server/for.test.ripple +9 -5
  65. package/tests/server/if.test.ripple +1 -1
  66. package/tests/server/streaming-ssr.test.ripple +67 -0
  67. package/types/server.d.ts +5 -4
@@ -4,7 +4,7 @@ import { track, flushSync } from 'ripple';
4
4
  describe('basic client > attribute rendering', () => {
5
5
  it('render static attributes', () => {
6
6
  component Basic() {
7
- <div class='foo' id='bar' style='color: red;'>{'Hello World'}</div>
7
+ <div class="foo" id="bar" style="color: red;">{'Hello World'}</div>
8
8
  }
9
9
 
10
10
  render(Basic);
@@ -16,7 +16,13 @@ describe('basic client > attribute rendering', () => {
16
16
  component Basic() {
17
17
  let active = track(false);
18
18
 
19
- <button onClick={() => { @active = !@active }}>{'Toggle'}</button>
19
+ <button
20
+ onClick={() => {
21
+ @active = !@active;
22
+ }}
23
+ >
24
+ {'Toggle'}
25
+ </button>
20
26
  <div class={@active ? 'active' : 'inactive'}>{'Dynamic Class'}</div>
21
27
 
22
28
  <style>
@@ -31,7 +37,9 @@ describe('basic client > attribute rendering', () => {
31
37
  const button = container.querySelector('button');
32
38
  const div = container.querySelector('div');
33
39
 
34
- expect(Array.from(div.classList).some(className => className.startsWith('ripple-'))).toBe(true);
40
+ expect(Array.from(div.classList).some((className) => className.startsWith('ripple-'))).toBe(
41
+ true,
42
+ );
35
43
  expect(div.classList.contains('inactive')).toBe(true);
36
44
 
37
45
  button.click();
@@ -46,24 +54,26 @@ describe('basic client > attribute rendering', () => {
46
54
 
47
55
  it('render class attribute with array, nested array, nested object', () => {
48
56
  component Basic() {
49
- <div class={[
50
- 'foo',
51
- 'bar',
52
- true && 'baz',
53
- false && 'aaa',
54
- null && 'bbb',
55
- [
56
- 'ccc',
57
- 'ddd',
58
- { eee: true, fff: false }
59
- ]
60
- ]}>
57
+ <div
58
+ class={[
59
+ 'foo',
60
+ 'bar',
61
+ true && 'baz',
62
+ false && 'aaa',
63
+ null && 'bbb',
64
+ [
65
+ 'ccc',
66
+ 'ddd',
67
+ {eee: true, fff: false},
68
+ ],
69
+ ]}
70
+ >
61
71
  {'Class Array'}
62
72
  </div>
63
73
 
64
74
  <style>
65
75
  .foo {
66
- color: red;
76
+ color: red;
67
77
  }
68
78
  </style>
69
79
  }
@@ -72,7 +82,9 @@ describe('basic client > attribute rendering', () => {
72
82
 
73
83
  const div = container.querySelector('div');
74
84
 
75
- expect(Array.from(div.classList).some(className => className.startsWith('ripple-'))).toBe(true);
85
+ expect(Array.from(div.classList).some((className) => className.startsWith('ripple-'))).toBe(
86
+ true,
87
+ );
76
88
  expect(div.classList.contains('foo')).toBe(true);
77
89
  expect(div.classList.contains('bar')).toBe(true);
78
90
  expect(div.classList.contains('baz')).toBe(true);
@@ -88,8 +100,14 @@ describe('basic client > attribute rendering', () => {
88
100
  component Basic() {
89
101
  let active = track(false);
90
102
 
91
- <button onClick={() => { @active = !@active }}>{'Toggle'}</button>
92
- <div class={{ active: @active, inactive: !@active }}>{'Dynamic Class'}</div>
103
+ <button
104
+ onClick={() => {
105
+ @active = !@active;
106
+ }}
107
+ >
108
+ {'Toggle'}
109
+ </button>
110
+ <div class={{active: @active, inactive: !@active}}>{'Dynamic Class'}</div>
93
111
 
94
112
  <style>
95
113
  .active {
@@ -103,7 +121,9 @@ describe('basic client > attribute rendering', () => {
103
121
  const button = container.querySelector('button');
104
122
  const div = container.querySelector('div');
105
123
 
106
- expect(Array.from(div.classList).some(className => className.startsWith('ripple-'))).toBe(true);
124
+ expect(Array.from(div.classList).some((className) => className.startsWith('ripple-'))).toBe(
125
+ true,
126
+ );
107
127
  expect(div.classList.contains('inactive')).toBe(true);
108
128
  expect(div.classList.contains('active')).toBe(false);
109
129
 
@@ -123,7 +143,13 @@ describe('basic client > attribute rendering', () => {
123
143
  component Basic() {
124
144
  let count = track(0);
125
145
 
126
- <button onClick={() => { @count++ }}>{'Increment'}</button>
146
+ <button
147
+ onClick={() => {
148
+ @count++;
149
+ }}
150
+ >
151
+ {'Increment'}
152
+ </button>
127
153
  <div id={`item-${@count}`}>{'Dynamic ID'}</div>
128
154
  }
129
155
 
@@ -149,7 +175,13 @@ describe('basic client > attribute rendering', () => {
149
175
  component Basic() {
150
176
  let color = track('red');
151
177
 
152
- <button onClick={() => { @color = @color === 'red' ? 'blue' : 'red' }}>{'Change Color'}</button>
178
+ <button
179
+ onClick={() => {
180
+ @color = @color === 'red' ? 'blue' : 'red';
181
+ }}
182
+ >
183
+ {'Change Color'}
184
+ </button>
153
185
  <div style={`color: ${@color}; font-weight: bold;`}>{'Dynamic Style'}</div>
154
186
  }
155
187
 
@@ -172,11 +204,14 @@ describe('basic client > attribute rendering', () => {
172
204
  component Basic() {
173
205
  let color = track('red');
174
206
 
175
- <button onClick={() => { @color = @color === 'red' ? 'blue' : 'red' }}>{'Change Color'}</button>
176
- <div style={{
177
- color: @color,
178
- fontWeight: 'bold',
179
- }}>{'Dynamic Style'}</div>
207
+ <button
208
+ onClick={() => {
209
+ @color = @color === 'red' ? 'blue' : 'red';
210
+ }}
211
+ >
212
+ {'Change Color'}
213
+ </button>
214
+ <div style={{color: @color, fontWeight: 'bold'}}>{'Dynamic Style'}</div>
180
215
  }
181
216
 
182
217
  render(Basic);
@@ -203,7 +238,7 @@ describe('basic client > attribute rendering', () => {
203
238
  }
204
239
 
205
240
  <button onClick={toggleColor}>{'Change Color'}</button>
206
- <div style={@style}>{'Dynamic Style'}</div>
241
+ <div {@style}>{'Dynamic Style'}</div>
207
242
  }
208
243
 
209
244
  render(Basic);
@@ -230,7 +265,7 @@ describe('basic client > attribute rendering', () => {
230
265
  }
231
266
 
232
267
  <button onClick={toggleColor}>{'Change Color'}</button>
233
- <div style={{ ...@style }}>{'Dynamic Style'}</div>
268
+ <div style={{...@style}}>{'Dynamic Style'}</div>
234
269
  }
235
270
 
236
271
  render(Basic);
@@ -271,7 +306,7 @@ describe('basic client > attribute rendering', () => {
271
306
 
272
307
  it('render spread props without duplication', () => {
273
308
  component App() {
274
- const checkBoxProp = {name:'car'}
309
+ const checkBoxProp = { name: 'car' };
275
310
 
276
311
  <div>
277
312
  <input {...checkBoxProp} type="checkbox" id="vehicle1" value="Bike" />
@@ -299,11 +334,15 @@ describe('basic client > attribute rendering', () => {
299
334
  let disabled = track(false);
300
335
  let checked = track(false);
301
336
 
302
- <button onClick={() => {
303
- @disabled = !@disabled;
304
- @checked = !@checked;
305
- }}>{'Toggle'}</button>
306
- <input type='checkbox' disabled={@disabled} checked={@checked} />
337
+ <button
338
+ onClick={() => {
339
+ @disabled = !@disabled;
340
+ @checked = !@checked;
341
+ }}
342
+ >
343
+ {'Toggle'}
344
+ </button>
345
+ <input type="checkbox" {@disabled} {@checked} />
307
346
  }
308
347
 
309
348
  render(Basic);
@@ -328,11 +367,15 @@ describe('basic client > attribute rendering', () => {
328
367
 
329
368
  <button
330
369
  onClick={() => {
331
- @theme = @theme === 'light' ? 'dark' : 'light';
332
- @size = @size === 'medium' ? 'large' : 'medium';
333
- }}
334
- >{'Toggle Theme & Size'}</button>
335
- <div class={`theme-${@theme} size-${@size}`} data-theme={@theme} data-size={@size}>{'Multiple Dynamic Attributes'}</div>
370
+ @theme = @theme === 'light' ? 'dark' : 'light';
371
+ @size = @size === 'medium' ? 'large' : 'medium';
372
+ }}
373
+ >
374
+ {'Toggle Theme & Size'}
375
+ </button>
376
+ <div class={`theme-${@theme} size-${@size}`} data-theme={@theme} data-size={@size}>
377
+ {'Multiple Dynamic Attributes'}
378
+ </div>
336
379
  }
337
380
 
338
381
  render(Basic);
@@ -357,14 +400,20 @@ describe('basic client > attribute rendering', () => {
357
400
  let showTitle = track(false);
358
401
  let showAria = track(false);
359
402
 
360
- <button onClick={() => {
361
- @showTitle = !@showTitle;
362
- @showAria = !@showAria;
363
- }}>{'Toggle Attributes'}</button>
403
+ <button
404
+ onClick={() => {
405
+ @showTitle = !@showTitle;
406
+ @showAria = !@showAria;
407
+ }}
408
+ >
409
+ {'Toggle Attributes'}
410
+ </button>
364
411
  <div
365
412
  title={@showTitle ? 'This is a title' : null}
366
413
  aria-label={@showAria ? 'Accessible label' : null}
367
- >{'Conditional Attributes'}</div>
414
+ >
415
+ {'Conditional Attributes'}
416
+ </div>
368
417
  }
369
418
 
370
419
  render(Basic);
@@ -390,20 +439,24 @@ describe('basic client > attribute rendering', () => {
390
439
 
391
440
  it('render spread attributes', () => {
392
441
  component Basic() {
393
- let attrs = track({
394
- class: 'initial',
395
- id: 'test-1'
396
- });
442
+ let attrs = track(
443
+ {
444
+ class: 'initial',
445
+ id: 'test-1',
446
+ },
447
+ );
397
448
 
398
449
  <button
399
450
  onClick={() => {
400
- @attrs = {
401
- class: 'updated',
402
- id: 'test-2',
403
- 'data-extra': 'value'
404
- };
405
- }}
406
- >{'Update Attributes'}</button>
451
+ @attrs = {
452
+ class: 'updated',
453
+ id: 'test-2',
454
+ 'data-extra': 'value',
455
+ };
456
+ }}
457
+ >
458
+ {'Update Attributes'}
459
+ </button>
407
460
  <div {...@attrs}>{'Spread Attributes'}</div>
408
461
  }
409
462
 
@@ -448,7 +501,7 @@ describe('basic client > attribute rendering', () => {
448
501
  it('handles boolean attributes with no prop value provides', () => {
449
502
  component App() {
450
503
  <div class="container">
451
- <button onClick={() => console.log("clicked!")} disabled>{"Button"}</button>
504
+ <button onClick={() => console.log('clicked!')} disabled>{'Button'}</button>
452
505
  <input type="checkbox" checked />
453
506
  </div>
454
507
  }
@@ -4,16 +4,22 @@ import { TRACKED_ARRAY } from '../../../src/runtime/internal/client/constants.js
4
4
  describe('basic client > collections', () => {
5
5
  it('renders with simple reactive objects', () => {
6
6
  component Basic() {
7
- let user = track({
8
- name: 'John',
9
- age: 25
10
- });
11
-
12
- <div class='name'>{@user.name}</div>
13
- <div class='age'>{@user.age}</div>
14
- <button onClick={() => {
15
- @user = {...@user, name: 'Jane', age: 30}
16
- }}>{'Update User'}</button>
7
+ let user = track(
8
+ {
9
+ name: 'John',
10
+ age: 25,
11
+ },
12
+ );
13
+
14
+ <div class="name">{@user.name}</div>
15
+ <div class="age">{@user.age}</div>
16
+ <button
17
+ onClick={() => {
18
+ @user = { ...@user, name: 'Jane', age: 30 };
19
+ }}
20
+ >
21
+ {'Update User'}
22
+ </button>
17
23
  }
18
24
 
19
25
  render(Basic);
@@ -34,17 +40,23 @@ describe('basic client > collections', () => {
34
40
 
35
41
  it('renders with nested reactive objects', () => {
36
42
  component Basic() {
37
- let user = track({
38
- name: track('John'),
39
- age: track(25)
40
- });
41
-
42
- <div class='name'>{@user.@name}</div>
43
- <div class='age'>{@user.@age}</div>
44
- <button onClick={() => {
45
- @user.@name = 'Jane';
46
- @user.@age = 30;
47
- }}>{'Update User'}</button>
43
+ let user = track(
44
+ {
45
+ name: track('John'),
46
+ age: track(25),
47
+ },
48
+ );
49
+
50
+ <div class="name">{@user.@name}</div>
51
+ <div class="age">{@user.@age}</div>
52
+ <button
53
+ onClick={() => {
54
+ @user.@name = 'Jane';
55
+ @user.@age = 30;
56
+ }}
57
+ >
58
+ {'Update User'}
59
+ </button>
48
60
  }
49
61
 
50
62
  render(Basic);
@@ -71,7 +83,14 @@ describe('basic client > collections', () => {
71
83
  <pre>{array[0]}</pre>
72
84
  <pre>{TRACKED_ARRAY in array}</pre>
73
85
 
74
- <button onClick={() => { array.push(array.length + 1); array[0] = array[0] + 1 }}>{'Add'}</button>
86
+ <button
87
+ onClick={() => {
88
+ array.push(array.length + 1);
89
+ array[0] = array[0] + 1;
90
+ }}
91
+ >
92
+ {'Add'}
93
+ </button>
75
94
  }
76
95
 
77
96
  render(App);
@@ -19,9 +19,9 @@ describe('basic client > errors', () => {
19
19
  <div>
20
20
  <button onClick={triggerError}>{'Trigger Error'}</button>
21
21
  if (@hasError) {
22
- <div class='error'>{'Error caught: ' + @errorMessage}</div>
22
+ <div class="error">{'Error caught: ' + @errorMessage}</div>
23
23
  } else {
24
- <div class='success'>{'No error'}</div>
24
+ <div class="success">{'No error'}</div>
25
25
  }
26
26
  </div>
27
27
  }
@@ -78,7 +78,9 @@ describe('basic client > errors', () => {
78
78
 
79
79
  render(Basic);
80
80
 
81
- expect(error).toBe('Assignments or updates to tracked values are not allowed during computed "track(() => ...)" evaluation');
81
+ expect(error).toBe(
82
+ 'Assignments or updates to tracked values are not allowed during computed "track(() => ...)" evaluation',
83
+ );
82
84
  });
83
85
 
84
86
  it('errors on mutating tracked value inside untrack() in computed track() evaluation', () => {
@@ -100,10 +102,12 @@ describe('basic client > errors', () => {
100
102
 
101
103
  render(Basic);
102
104
 
103
- expect(error).toBe('Assignments or updates to tracked values are not allowed during computed "track(() => ...)" evaluation');
105
+ expect(error).toBe(
106
+ 'Assignments or updates to tracked values are not allowed during computed "track(() => ...)" evaluation',
107
+ );
104
108
  });
105
109
 
106
- it("errors on mutating a tracked variable in track() getter", () => {
110
+ it('errors on mutating a tracked variable in track() getter', () => {
107
111
  component Basic() {
108
112
  let count = track(0);
109
113
 
@@ -121,7 +125,9 @@ describe('basic client > errors', () => {
121
125
 
122
126
  render(Basic);
123
127
 
124
- expect(error).toBe('Assignments or updates to tracked values are not allowed during computed "track(() => ...)" evaluation');
128
+ expect(error).toBe(
129
+ 'Assignments or updates to tracked values are not allowed during computed "track(() => ...)" evaluation',
130
+ );
125
131
  });
126
132
 
127
133
  it('should throw error for await in client-side control-flow statements', () => {
@@ -8,11 +8,17 @@ describe('basic client > events', () => {
8
8
  let clickCount = track(0);
9
9
 
10
10
  <button
11
- onFocus={() => { @focusCount++ }}
12
- onClick={() => { @clickCount++ }}
13
- >{'Test Button'}</button>
14
- <div class='focus-count'>{@focusCount}</div>
15
- <div class='click-count'>{@clickCount}</div>
11
+ onFocus={() => {
12
+ @focusCount++;
13
+ }}
14
+ onClick={() => {
15
+ @clickCount++;
16
+ }}
17
+ >
18
+ {'Test Button'}
19
+ </button>
20
+ <div class="focus-count">{@focusCount}</div>
21
+ <div class="click-count">{@clickCount}</div>
16
22
  }
17
23
 
18
24
  render(Basic);
@@ -35,10 +41,20 @@ describe('basic client > events', () => {
35
41
  let captureClicks = track(0);
36
42
  let bubbleClicks = track(0);
37
43
 
38
- <div onClickCapture={() => { @captureClicks++ }}>
39
- <button onClick={() => { @bubbleClicks++ }}>{'Click me'}</button>
40
- <div class='capture-count'>{@captureClicks}</div>
41
- <div class='bubble-count'>{@bubbleClicks}</div>
44
+ <div
45
+ onClickCapture={() => {
46
+ @captureClicks++;
47
+ }}
48
+ >
49
+ <button
50
+ onClick={() => {
51
+ @bubbleClicks++;
52
+ }}
53
+ >
54
+ {'Click me'}
55
+ </button>
56
+ <div class="capture-count">{@captureClicks}</div>
57
+ <div class="bubble-count">{@bubbleClicks}</div>
42
58
  </div>
43
59
  }
44
60
 
@@ -61,20 +77,20 @@ describe('basic client > events', () => {
61
77
 
62
78
  const minus = {
63
79
  onClick() {
64
- @count--
65
- }
66
- }
80
+ @count--;
81
+ },
82
+ };
67
83
 
68
84
  const plus = {
69
85
  onClick() {
70
- @count++
71
- }
72
- }
86
+ @count++;
87
+ },
88
+ };
73
89
 
74
90
  <div>
75
- <button {...minus} class='minus'>{'-'}</button>
76
- <span class='count'>{@count}</span>
77
- <button {...plus} class='plus'>{'+'}</button>
91
+ <button {...minus} class="minus">{'-'}</button>
92
+ <span class="count">{@count}</span>
93
+ <button {...plus} class="plus">{'+'}</button>
78
94
  </div>
79
95
  }
80
96
 
@@ -110,18 +126,20 @@ describe('basic client > events', () => {
110
126
  let focusCount = track(0);
111
127
 
112
128
  const mixedHandler = {
113
- onClick() { // Delegated event
114
- @clickCount++
129
+ onClick() {
130
+ // Delegated event
131
+ @clickCount++;
132
+ },
133
+ onFocus() {
134
+ // Non-delegated event
135
+ @focusCount++;
115
136
  },
116
- onFocus() { // Non-delegated event
117
- @focusCount++
118
- }
119
- }
137
+ };
120
138
 
121
139
  <div>
122
- <button {...mixedHandler} class='mixed-button'>{'Test'}</button>
123
- <span class='click-count'>{@clickCount}</span>
124
- <span class='focus-count'>{@focusCount}</span>
140
+ <button {...mixedHandler} class="mixed-button">{'Test'}</button>
141
+ <span class="click-count">{@clickCount}</span>
142
+ <span class="focus-count">{@focusCount}</span>
125
143
  </div>
126
144
  }
127
145
 
@@ -169,13 +187,13 @@ describe('basic client > events', () => {
169
187
  @isEven = true;
170
188
  };
171
189
 
172
- <div class='counter'>{@counter}</div>
173
- <div class='parity'>{@isEven ? 'Even' : 'Odd'}</div>
174
- <div class='history-count'>{@history.length}</div>
190
+ <div class="counter">{@counter}</div>
191
+ <div class="parity">{@isEven ? 'Even' : 'Odd'}</div>
192
+ <div class="history-count">{@history.length}</div>
175
193
 
176
- <button class='inc-btn' onClick={handleIncrement}>{'+'}</button>
177
- <button class='dec-btn' onClick={handleDecrement}>{'-'}</button>
178
- <button class='reset-btn' onClick={handleReset}>{'Reset'}</button>
194
+ <button class="inc-btn" onClick={handleIncrement}>{'+'}</button>
195
+ <button class="dec-btn" onClick={handleDecrement}>{'-'}</button>
196
+ <button class="reset-btn" onClick={handleReset}>{'Reset'}</button>
179
197
  }
180
198
 
181
199
  render(Basic);