@sprucelabs/spruce-heartwood-utils 38.17.4 → 38.17.6
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.
- package/README.md +117 -33
- package/build/app/SkillViewEmitter.d.ts +167 -0
- package/build/app/SkillViewEmitter.js +69 -0
- package/build/esm/__tests__/support/MockRemoteViewControllerFactory.js +13 -29
- package/build/esm/__tests__/support/heartwoodEventFaker.js +20 -31
- package/build/esm/__tests__/support/remoteVcAssert.js +51 -63
- package/build/esm/app/SkillViewEmitter.d.ts +167 -0
- package/build/esm/app/SkillViewEmitter.js +65 -0
- package/build/esm/components/DelayedPlacer.js +11 -15
- package/build/esm/components/Settings.js +1 -2
- package/build/esm/components/Sizer.js +12 -30
- package/build/esm/components/animation/DelayedPlacerExport.js +2 -14
- package/build/esm/components/animation/SimpleEmitter.js +3 -6
- package/build/esm/components/animation/SizerExport.js +6 -27
- package/build/esm/devices/HeartwoodDevice.js +34 -52
- package/build/esm/errors/SpruceError.js +1 -1
- package/build/esm/hooks/queueShow.js +6 -7
- package/build/esm/index-testing.d.ts +6 -0
- package/build/esm/index-testing.js +4 -0
- package/build/esm/index-web.d.ts +11 -0
- package/build/esm/index-web.js +7 -0
- package/build/esm/theming/ThemeManager.js +55 -82
- package/build/esm/theming/loadActiveThemeForOrg.js +6 -17
- package/build/esm/views/CardRegistrar.js +46 -58
- package/build/esm/views/RemoteViewControllerFactory.js +60 -76
- package/build/index-testing.d.ts +6 -0
- package/build/index-testing.js +15 -0
- package/build/index-web.d.ts +11 -0
- package/build/index-web.js +33 -0
- package/package.json +33 -14
- package/docs/animation/README.md +0 -1697
- package/docs/animation/animation-emitter.md +0 -78
- package/docs/animation/delayed-placer.md +0 -156
- package/docs/animation/queue-show.md +0 -309
- package/docs/animation/settings.md +0 -117
- package/docs/animation/size-util.md +0 -102
- package/docs/animation/sizer.md +0 -161
- package/docs/bundle-optimization.md +0 -100
- package/docs/conferenceStage/plan.md +0 -167
- package/docs/heartwood-utils.md +0 -709
- package/docs/theme_prompt.md +0 -76
- package/docs/utilities/getDeviceOrientation.md +0 -131
- package/docs/utilities/skillViewState.md +0 -140
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @sprucelabs/spruce-heartwood-utils
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Heartwood integration toolkit for Spruce skills. Use this package to load remote cards, register and render cross-skill views, apply shared theme behavior, drive animation/layout primitives, and test the full flow with built-in mocks.
|
|
4
4
|
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -14,16 +14,99 @@ This package is designed for use inside a Spruce skill. It expects `@sprucelabs/
|
|
|
14
14
|
|
|
15
15
|
---
|
|
16
16
|
|
|
17
|
+
## Project Pitch
|
|
18
|
+
|
|
19
|
+
Building a Spruce experience usually means combining:
|
|
20
|
+
- browser/runtime view infrastructure
|
|
21
|
+
- test utilities for remote card/event flows
|
|
22
|
+
- animation and layout primitives used in Heartwood views
|
|
23
|
+
|
|
24
|
+
`@sprucelabs/spruce-heartwood-utils` packages those into one module with explicit entrypoints so you can keep browser bundles clean while still getting first-class testing helpers.
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## What You Get
|
|
29
|
+
|
|
30
|
+
- Remote view controller infrastructure:
|
|
31
|
+
`RemoteViewControllerFactoryImpl`, `CardRegistrar`, shared remote VC types
|
|
32
|
+
- Theming and plugins:
|
|
33
|
+
`loadActiveThemeForOrg`, `AutoLogoutPlugin`
|
|
34
|
+
- Test harness pieces:
|
|
35
|
+
`remoteVcAssert`, `fakeGetViews`, `MockRemoteViewControllerFactory`, `SpyAutoLogoutPlugin`
|
|
36
|
+
- Animation/layout primitives:
|
|
37
|
+
`Sizer`, `DelayedPlacer`, queue/show helpers, emitters, settings utilities
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## Import Paths
|
|
42
|
+
|
|
43
|
+
- `@sprucelabs/spruce-heartwood-utils/web` for browser-safe runtime imports (recommended for Vite/Bun/ESM apps)
|
|
44
|
+
- `@sprucelabs/spruce-heartwood-utils/testing` for test helpers and mocks
|
|
45
|
+
- `@sprucelabs/spruce-heartwood-utils` keeps the legacy mixed export surface for existing consumers
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## Get Started
|
|
50
|
+
|
|
51
|
+
### Web Only
|
|
52
|
+
|
|
53
|
+
Use this when you are shipping runtime/browser code and do not need test helpers in that code path.
|
|
54
|
+
|
|
55
|
+
```ts
|
|
56
|
+
import {
|
|
57
|
+
CardRegistrar,
|
|
58
|
+
RemoteViewControllerFactoryImpl,
|
|
59
|
+
AutoLogoutPlugin,
|
|
60
|
+
loadActiveThemeForOrg,
|
|
61
|
+
Sizer,
|
|
62
|
+
SimpleEmitter,
|
|
63
|
+
} from '@sprucelabs/spruce-heartwood-utils/web'
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Tests Only
|
|
67
|
+
|
|
68
|
+
Use this in test suites where you need to fake remote views and assert event-driven card registration.
|
|
69
|
+
|
|
70
|
+
```ts
|
|
71
|
+
import {
|
|
72
|
+
remoteVcAssert,
|
|
73
|
+
fakeGetViews,
|
|
74
|
+
MockRemoteViewControllerFactory,
|
|
75
|
+
SpyAutoLogoutPlugin,
|
|
76
|
+
} from '@sprucelabs/spruce-heartwood-utils/testing'
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Web + Tests
|
|
80
|
+
|
|
81
|
+
Use split imports so runtime code stays browser-safe while tests get all mocks/helpers.
|
|
82
|
+
|
|
83
|
+
```ts
|
|
84
|
+
import {
|
|
85
|
+
CardRegistrar,
|
|
86
|
+
RemoteViewControllerFactoryImpl,
|
|
87
|
+
} from '@sprucelabs/spruce-heartwood-utils/web'
|
|
88
|
+
import {
|
|
89
|
+
remoteVcAssert,
|
|
90
|
+
MockRemoteViewControllerFactory,
|
|
91
|
+
} from '@sprucelabs/spruce-heartwood-utils/testing'
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
17
96
|
## Table of Contents
|
|
18
97
|
|
|
19
|
-
1. [
|
|
20
|
-
2. [
|
|
21
|
-
3. [
|
|
22
|
-
4. [
|
|
23
|
-
5. [
|
|
24
|
-
6. [
|
|
25
|
-
7. [
|
|
26
|
-
8. [
|
|
98
|
+
1. [Project Pitch](#project-pitch)
|
|
99
|
+
2. [What You Get](#what-you-get)
|
|
100
|
+
3. [Import Paths](#import-paths)
|
|
101
|
+
4. [Get Started](#get-started)
|
|
102
|
+
5. [Remote View Controllers](#remote-view-controllers)
|
|
103
|
+
6. [CardRegistrar](#cardregistrar)
|
|
104
|
+
7. [Types](#types)
|
|
105
|
+
8. [Theming](#theming)
|
|
106
|
+
9. [Plugins](#plugins)
|
|
107
|
+
10. [Test Utilities](#test-utilities)
|
|
108
|
+
11. [Device & Layout Utilities](#device--layout-utilities)
|
|
109
|
+
12. [Animation](#animation)
|
|
27
110
|
- [Quick Start](#quick-start)
|
|
28
111
|
- [Required CSS](#required-css)
|
|
29
112
|
- [System Architecture](#system-architecture)
|
|
@@ -49,7 +132,7 @@ Fetches compiled ViewController source from the `heartwood.get-skill-views` even
|
|
|
49
132
|
import {
|
|
50
133
|
RemoteViewControllerFactoryImpl,
|
|
51
134
|
RemoteViewControllerFactory,
|
|
52
|
-
} from '@sprucelabs/spruce-heartwood-utils'
|
|
135
|
+
} from '@sprucelabs/spruce-heartwood-utils/web'
|
|
53
136
|
```
|
|
54
137
|
|
|
55
138
|
**Injecting a mock factory in tests:**
|
|
@@ -77,7 +160,7 @@ const vc = await remoteVcFactory.RemoteController(
|
|
|
77
160
|
The interface that both `RemoteViewControllerFactoryImpl` and `MockRemoteViewControllerFactory` implement. Use this type for any field that may hold the real factory or a test mock:
|
|
78
161
|
|
|
79
162
|
```ts
|
|
80
|
-
import { RemoteViewControllerFactory } from '@sprucelabs/spruce-heartwood-utils'
|
|
163
|
+
import { RemoteViewControllerFactory } from '@sprucelabs/spruce-heartwood-utils/web'
|
|
81
164
|
|
|
82
165
|
private remoteVcFactory: RemoteViewControllerFactory
|
|
83
166
|
```
|
|
@@ -89,7 +172,7 @@ private remoteVcFactory: RemoteViewControllerFactory
|
|
|
89
172
|
Options passed to `RemoteViewControllerFactoryImpl.Factory()`.
|
|
90
173
|
|
|
91
174
|
```ts
|
|
92
|
-
import { RemoteFactoryOptions } from '@sprucelabs/spruce-heartwood-utils'
|
|
175
|
+
import { RemoteFactoryOptions } from '@sprucelabs/spruce-heartwood-utils/web'
|
|
93
176
|
|
|
94
177
|
interface RemoteFactoryOptions {
|
|
95
178
|
connectToApi: () => Promise<MercuryClient>
|
|
@@ -104,7 +187,7 @@ interface RemoteFactoryOptions {
|
|
|
104
187
|
A narrowed subset of `ViewControllerFactory` containing only what the remote factory needs. Use this type when accepting or storing a factory reference to avoid a full `ViewControllerFactory` dependency:
|
|
105
188
|
|
|
106
189
|
```ts
|
|
107
|
-
import { VcFactoryForRemoteFactory } from '@sprucelabs/spruce-heartwood-utils'
|
|
190
|
+
import { VcFactoryForRemoteFactory } from '@sprucelabs/spruce-heartwood-utils/web'
|
|
108
191
|
|
|
109
192
|
private views: VcFactoryForRemoteFactory
|
|
110
193
|
```
|
|
@@ -121,7 +204,7 @@ import {
|
|
|
121
204
|
CardRegistrarOptions,
|
|
122
205
|
CardFetchOptions,
|
|
123
206
|
EachCardHandler,
|
|
124
|
-
} from '@sprucelabs/spruce-heartwood-utils'
|
|
207
|
+
} from '@sprucelabs/spruce-heartwood-utils/web'
|
|
125
208
|
```
|
|
126
209
|
|
|
127
210
|
### `CardRegistrar.Registrar(options)`
|
|
@@ -203,7 +286,7 @@ type CardFetchOptions<Contract, Name> = {
|
|
|
203
286
|
The shape of a card loaded from another skill. Extends `ViewController<Card>` with an optional `load()` method so you can forward load arguments after the card is rendered.
|
|
204
287
|
|
|
205
288
|
```ts
|
|
206
|
-
import { RemoteDashboardCard } from '@sprucelabs/spruce-heartwood-utils'
|
|
289
|
+
import { RemoteDashboardCard } from '@sprucelabs/spruce-heartwood-utils/web'
|
|
207
290
|
|
|
208
291
|
interface RemoteDashboardCard extends ViewController<Card> {
|
|
209
292
|
load?(options: SkillViewControllerLoadOptions): Promise<void>
|
|
@@ -235,7 +318,7 @@ cards: [
|
|
|
235
318
|
Fetches the active Heartwood theme for an organization so your skill can apply the same visual style.
|
|
236
319
|
|
|
237
320
|
```ts
|
|
238
|
-
import { loadActiveThemeForOrg } from '@sprucelabs/spruce-heartwood-utils'
|
|
321
|
+
import { loadActiveThemeForOrg } from '@sprucelabs/spruce-heartwood-utils/web'
|
|
239
322
|
```
|
|
240
323
|
|
|
241
324
|
**Signature:**
|
|
@@ -264,7 +347,7 @@ const theme = await loadActiveThemeForOrg(client, organizationId)
|
|
|
264
347
|
Sends `enableAutoLogout` / `disableAutoLogout` commands to the native Heartwood device layer. Register it with your skill's `ViewControllerFactory` to give your skill views control over session timeout behavior.
|
|
265
348
|
|
|
266
349
|
```ts
|
|
267
|
-
import { AutoLogoutPlugin } from '@sprucelabs/spruce-heartwood-utils'
|
|
350
|
+
import { AutoLogoutPlugin } from '@sprucelabs/spruce-heartwood-utils/web'
|
|
268
351
|
|
|
269
352
|
const plugin = factory.BuildPlugin(AutoLogoutPlugin)
|
|
270
353
|
|
|
@@ -279,7 +362,7 @@ plugin.disableAutoLogout() // cancel a pending auto-logout
|
|
|
279
362
|
Use this type when you want to reference the plugin without coupling to the concrete class — for example, when declaring a field that holds either the real plugin or a spy:
|
|
280
363
|
|
|
281
364
|
```ts
|
|
282
|
-
import type { AutoLogoutViewPlugin } from '@sprucelabs/spruce-heartwood-utils'
|
|
365
|
+
import type { AutoLogoutViewPlugin } from '@sprucelabs/spruce-heartwood-utils/web'
|
|
283
366
|
|
|
284
367
|
// Field declaration — works with both AutoLogoutPlugin and SpyAutoLogoutPlugin:
|
|
285
368
|
private autoLogoutPlugin: AutoLogoutViewPlugin
|
|
@@ -301,7 +384,7 @@ interface AutoLogoutViewPlugin extends ViewControllerPlugin {
|
|
|
301
384
|
A test double that records calls to `enableAutoLogout` and `disableAutoLogout`. Swap it in during test setup to assert your skill calls the plugin correctly:
|
|
302
385
|
|
|
303
386
|
```ts
|
|
304
|
-
import { SpyAutoLogoutPlugin } from '@sprucelabs/spruce-heartwood-utils'
|
|
387
|
+
import { SpyAutoLogoutPlugin } from '@sprucelabs/spruce-heartwood-utils/testing'
|
|
305
388
|
|
|
306
389
|
protected async beforeEach() {
|
|
307
390
|
const factory = this.views.getFactory()
|
|
@@ -341,7 +424,7 @@ Use these in your skill's test suite to verify that your skill correctly registe
|
|
|
341
424
|
High-level assertion helper. Verifies that your skill view emits the expected registration event and that the returned cards are rendered.
|
|
342
425
|
|
|
343
426
|
```ts
|
|
344
|
-
import { remoteVcAssert } from '@sprucelabs/spruce-heartwood-utils'
|
|
427
|
+
import { remoteVcAssert } from '@sprucelabs/spruce-heartwood-utils/testing'
|
|
345
428
|
```
|
|
346
429
|
|
|
347
430
|
**Basic assertion — confirm your skill view emits the event and renders cards:**
|
|
@@ -387,7 +470,7 @@ await remoteVcAssert.assertSkillViewRendersRemoteCards({
|
|
|
387
470
|
### `AssertRendersRemoteCardsOptions<Svc>`
|
|
388
471
|
|
|
389
472
|
```ts
|
|
390
|
-
import { AssertRendersRemoteCardsOptions } from '@sprucelabs/spruce-heartwood-utils'
|
|
473
|
+
import { AssertRendersRemoteCardsOptions } from '@sprucelabs/spruce-heartwood-utils/testing'
|
|
391
474
|
|
|
392
475
|
interface AssertRendersRemoteCardsOptions<Svc extends SkillViewController = SkillViewController> {
|
|
393
476
|
svc: Svc
|
|
@@ -410,7 +493,7 @@ interface AssertRendersRemoteCardsOptions<Svc extends SkillViewController = Skil
|
|
|
410
493
|
Intercepts `heartwood.get-skill-views::v2021_02_11` and returns stub ViewController source. Useful when you need manual control over what remote views are returned in a test — `remoteVcAssert` uses this internally, but you can call it directly for custom scenarios.
|
|
411
494
|
|
|
412
495
|
```ts
|
|
413
|
-
import { fakeGetViews } from '@sprucelabs/spruce-heartwood-utils'
|
|
496
|
+
import { fakeGetViews } from '@sprucelabs/spruce-heartwood-utils/testing'
|
|
414
497
|
|
|
415
498
|
// Stub two remote views:
|
|
416
499
|
await fakeGetViews.fakeGetViews(['your-skill.card-a', 'your-skill.card-b'])
|
|
@@ -439,7 +522,8 @@ A drop-in replacement for the real remote factory. Controls exactly which VC cla
|
|
|
439
522
|
import {
|
|
440
523
|
MockRemoteViewControllerFactory,
|
|
441
524
|
RemoteViewControllerFactoryImpl,
|
|
442
|
-
} from '@sprucelabs/spruce-heartwood-utils'
|
|
525
|
+
} from '@sprucelabs/spruce-heartwood-utils/web'
|
|
526
|
+
import { MockDroppedInControllers } from '@sprucelabs/spruce-heartwood-utils/testing'
|
|
443
527
|
```
|
|
444
528
|
|
|
445
529
|
**Setup — inject the mock before your skill view creates its factory:**
|
|
@@ -497,7 +581,7 @@ if (mockFactory.hasController('your-skill.some-card')) { ... }
|
|
|
497
581
|
Type for the map of card IDs to VC classes held by `MockRemoteViewControllerFactory`:
|
|
498
582
|
|
|
499
583
|
```ts
|
|
500
|
-
import { MockDroppedInControllers } from '@sprucelabs/spruce-heartwood-utils'
|
|
584
|
+
import { MockDroppedInControllers } from '@sprucelabs/spruce-heartwood-utils/testing'
|
|
501
585
|
|
|
502
586
|
type MockDroppedInControllers = Record<
|
|
503
587
|
string,
|
|
@@ -596,7 +680,7 @@ import {
|
|
|
596
680
|
AnimationEmitter,
|
|
597
681
|
SizerProps,
|
|
598
682
|
DelayedPlacerProps,
|
|
599
|
-
} from '@sprucelabs/spruce-heartwood-utils'
|
|
683
|
+
} from '@sprucelabs/spruce-heartwood-utils/web'
|
|
600
684
|
```
|
|
601
685
|
|
|
602
686
|
---
|
|
@@ -692,7 +776,7 @@ Pass the **same emitter instance** to `Sizer` and `DelayedPlacer` when they are
|
|
|
692
776
|
The interface `Sizer` and `DelayedPlacer` use to communicate layout-change events. Pass an instance as the `emitter` prop to coordinate components. The package exports `SimpleEmitter` — a lightweight in-memory implementation you can use directly:
|
|
693
777
|
|
|
694
778
|
```ts
|
|
695
|
-
import { SimpleEmitter } from '@sprucelabs/spruce-heartwood-utils'
|
|
779
|
+
import { SimpleEmitter } from '@sprucelabs/spruce-heartwood-utils/web'
|
|
696
780
|
|
|
697
781
|
const emitter = new SimpleEmitter()
|
|
698
782
|
```
|
|
@@ -720,7 +804,7 @@ interface AnimationEmitter {
|
|
|
720
804
|
2. **Create an emitter** — one per component tree, stable across renders:
|
|
721
805
|
|
|
722
806
|
```ts
|
|
723
|
-
import { SimpleEmitter } from '@sprucelabs/spruce-heartwood-utils'
|
|
807
|
+
import { SimpleEmitter } from '@sprucelabs/spruce-heartwood-utils/web'
|
|
724
808
|
|
|
725
809
|
const emitter = useMemo(() => new SimpleEmitter(), [])
|
|
726
810
|
```
|
|
@@ -990,7 +1074,7 @@ Wraps children in a div whose `height` is set via inline style to match the meas
|
|
|
990
1074
|
2. **Wrap your content** — `shouldHideOverflow` clips children during the height transition:
|
|
991
1075
|
|
|
992
1076
|
```tsx
|
|
993
|
-
import { Sizer, SimpleEmitter } from '@sprucelabs/spruce-heartwood-utils'
|
|
1077
|
+
import { Sizer, SimpleEmitter } from '@sprucelabs/spruce-heartwood-utils/web'
|
|
994
1078
|
|
|
995
1079
|
const emitter = useMemo(() => new SimpleEmitter(), [])
|
|
996
1080
|
|
|
@@ -1058,7 +1142,7 @@ sizer.current?.hideOverflow(): void // re-apply overflow: hidden
|
|
|
1058
1142
|
#### Usage
|
|
1059
1143
|
|
|
1060
1144
|
```tsx
|
|
1061
|
-
import { Sizer, SimpleEmitter } from '@sprucelabs/spruce-heartwood-utils'
|
|
1145
|
+
import { Sizer, SimpleEmitter } from '@sprucelabs/spruce-heartwood-utils/web'
|
|
1062
1146
|
|
|
1063
1147
|
// Simplest — just clip overflow during height transition:
|
|
1064
1148
|
<Sizer shouldHideOverflow>
|
|
@@ -1120,7 +1204,7 @@ When `isEnabled={false}`, children render inline with no wrapper.
|
|
|
1120
1204
|
2. **Wrap your content** — `isEnabled`, `className`, and `isFocused` are all required:
|
|
1121
1205
|
|
|
1122
1206
|
```tsx
|
|
1123
|
-
import { DelayedPlacer, SimpleEmitter } from '@sprucelabs/spruce-heartwood-utils'
|
|
1207
|
+
import { DelayedPlacer, SimpleEmitter } from '@sprucelabs/spruce-heartwood-utils/web'
|
|
1124
1208
|
|
|
1125
1209
|
const emitter = useMemo(() => new SimpleEmitter(), [])
|
|
1126
1210
|
|
|
@@ -1190,7 +1274,7 @@ delayedPlacer.current?.placeRightAway(): void // re-measure and re-place immedi
|
|
|
1190
1274
|
|
|
1191
1275
|
```tsx
|
|
1192
1276
|
import React, { useRef, useMemo } from 'react'
|
|
1193
|
-
import { DelayedPlacer, SimpleEmitter } from '@sprucelabs/spruce-heartwood-utils'
|
|
1277
|
+
import { DelayedPlacer, SimpleEmitter } from '@sprucelabs/spruce-heartwood-utils/web'
|
|
1194
1278
|
|
|
1195
1279
|
function YourPanel({ isFocused }: { isFocused: () => boolean }) {
|
|
1196
1280
|
const placerRef = useRef<React.ElementRef<typeof DelayedPlacer>>(null)
|
|
@@ -1282,7 +1366,7 @@ A complete example showing all three systems working together in a card componen
|
|
|
1282
1366
|
import React, { useMemo } from 'react'
|
|
1283
1367
|
import {
|
|
1284
1368
|
Sizer, DelayedPlacer, queueShow, Settings, SimpleEmitter,
|
|
1285
|
-
} from '@sprucelabs/spruce-heartwood-utils'
|
|
1369
|
+
} from '@sprucelabs/spruce-heartwood-utils/web'
|
|
1286
1370
|
|
|
1287
1371
|
function YourCard({ isPlaced, isFocused }: { isPlaced: boolean; isFocused: () => boolean }) {
|
|
1288
1372
|
const emitter = useMemo(() => new SimpleEmitter(), [])
|
|
@@ -1321,7 +1405,7 @@ queueShow removes .hidden from each element with a 40ms stagger
|
|
|
1321
1405
|
**Test setup for animation components:**
|
|
1322
1406
|
|
|
1323
1407
|
```ts
|
|
1324
|
-
import { Settings, stopQueue } from '@sprucelabs/spruce-heartwood-utils'
|
|
1408
|
+
import { Settings, stopQueue } from '@sprucelabs/spruce-heartwood-utils/web'
|
|
1325
1409
|
import { skillViewState } from '@sprucelabs/spruce-heartwood-utils/build/components/skillViews/skillViewState'
|
|
1326
1410
|
|
|
1327
1411
|
protected async beforeEach() {
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { AbstractEventEmitter } from '@sprucelabs/mercury-event-emitter';
|
|
2
|
+
import { EventContract, EventContractEmitPayloads, MercuryEventEmitter } from '@sprucelabs/mercury-types';
|
|
3
|
+
declare const contract: {
|
|
4
|
+
eventSignatures: {
|
|
5
|
+
'did-change-orientation': {};
|
|
6
|
+
'did-resize': {};
|
|
7
|
+
'did-render': {};
|
|
8
|
+
'did-resize-content': {};
|
|
9
|
+
'did-place-cards': {};
|
|
10
|
+
'will-change-route': {};
|
|
11
|
+
'did-render-dialog': {};
|
|
12
|
+
'did-keydown': {
|
|
13
|
+
emitPayloadSchema: {
|
|
14
|
+
id: string;
|
|
15
|
+
fields: {
|
|
16
|
+
e: {
|
|
17
|
+
type: "raw";
|
|
18
|
+
isRequired: true;
|
|
19
|
+
options: {
|
|
20
|
+
valueType: string;
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
};
|
|
26
|
+
'did-scroll': {
|
|
27
|
+
emitPayloadSchema: {
|
|
28
|
+
id: string;
|
|
29
|
+
fields: {
|
|
30
|
+
scrollTop: {
|
|
31
|
+
type: "number";
|
|
32
|
+
isRequired: true;
|
|
33
|
+
};
|
|
34
|
+
skillViewNode: {
|
|
35
|
+
type: "raw";
|
|
36
|
+
isRequired: true;
|
|
37
|
+
options: {
|
|
38
|
+
valueType: string;
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
};
|
|
42
|
+
};
|
|
43
|
+
};
|
|
44
|
+
'connection-status-change': {
|
|
45
|
+
emitPayloadSchema: {
|
|
46
|
+
id: string;
|
|
47
|
+
fields: {
|
|
48
|
+
payload: {
|
|
49
|
+
type: "schema";
|
|
50
|
+
isRequired: true;
|
|
51
|
+
options: {
|
|
52
|
+
schema: {
|
|
53
|
+
id: string;
|
|
54
|
+
fields: {
|
|
55
|
+
status: {
|
|
56
|
+
type: "select";
|
|
57
|
+
isRequired: true;
|
|
58
|
+
options: {
|
|
59
|
+
choices: ({
|
|
60
|
+
readonly label: "Connecting";
|
|
61
|
+
readonly value: "connecting";
|
|
62
|
+
} | {
|
|
63
|
+
readonly label: "Connected";
|
|
64
|
+
readonly value: "connected";
|
|
65
|
+
} | {
|
|
66
|
+
readonly label: "Disconnected";
|
|
67
|
+
readonly value: "disconnected";
|
|
68
|
+
})[];
|
|
69
|
+
};
|
|
70
|
+
};
|
|
71
|
+
};
|
|
72
|
+
};
|
|
73
|
+
};
|
|
74
|
+
};
|
|
75
|
+
};
|
|
76
|
+
};
|
|
77
|
+
};
|
|
78
|
+
};
|
|
79
|
+
};
|
|
80
|
+
export type SkillViewEventContract = typeof contract;
|
|
81
|
+
export type SkillViewEvents = SkillViewEventContract['eventSignatures'];
|
|
82
|
+
export type SkillViewEmitPayloads = EventContractEmitPayloads<SkillViewEventContract>;
|
|
83
|
+
export type SkillViewEmitter = MercuryEventEmitter<SkillViewEventContract>;
|
|
84
|
+
export default class SkillViewEmitterImpl<Contract extends EventContract = SkillViewEventContract> extends AbstractEventEmitter<Contract> implements MercuryEventEmitter<Contract> {
|
|
85
|
+
private static instance;
|
|
86
|
+
private constructor();
|
|
87
|
+
static Emitter(): SkillViewEmitterImpl<{
|
|
88
|
+
eventSignatures: {
|
|
89
|
+
'did-change-orientation': {};
|
|
90
|
+
'did-resize': {};
|
|
91
|
+
'did-render': {};
|
|
92
|
+
'did-resize-content': {};
|
|
93
|
+
'did-place-cards': {};
|
|
94
|
+
'will-change-route': {};
|
|
95
|
+
'did-render-dialog': {};
|
|
96
|
+
'did-keydown': {
|
|
97
|
+
emitPayloadSchema: {
|
|
98
|
+
id: string;
|
|
99
|
+
fields: {
|
|
100
|
+
e: {
|
|
101
|
+
type: "raw";
|
|
102
|
+
isRequired: true;
|
|
103
|
+
options: {
|
|
104
|
+
valueType: string;
|
|
105
|
+
};
|
|
106
|
+
};
|
|
107
|
+
};
|
|
108
|
+
};
|
|
109
|
+
};
|
|
110
|
+
'did-scroll': {
|
|
111
|
+
emitPayloadSchema: {
|
|
112
|
+
id: string;
|
|
113
|
+
fields: {
|
|
114
|
+
scrollTop: {
|
|
115
|
+
type: "number";
|
|
116
|
+
isRequired: true;
|
|
117
|
+
};
|
|
118
|
+
skillViewNode: {
|
|
119
|
+
type: "raw";
|
|
120
|
+
isRequired: true;
|
|
121
|
+
options: {
|
|
122
|
+
valueType: string;
|
|
123
|
+
};
|
|
124
|
+
};
|
|
125
|
+
};
|
|
126
|
+
};
|
|
127
|
+
};
|
|
128
|
+
'connection-status-change': {
|
|
129
|
+
emitPayloadSchema: {
|
|
130
|
+
id: string;
|
|
131
|
+
fields: {
|
|
132
|
+
payload: {
|
|
133
|
+
type: "schema";
|
|
134
|
+
isRequired: true;
|
|
135
|
+
options: {
|
|
136
|
+
schema: {
|
|
137
|
+
id: string;
|
|
138
|
+
fields: {
|
|
139
|
+
status: {
|
|
140
|
+
type: "select";
|
|
141
|
+
isRequired: true;
|
|
142
|
+
options: {
|
|
143
|
+
choices: ({
|
|
144
|
+
readonly label: "Connecting";
|
|
145
|
+
readonly value: "connecting";
|
|
146
|
+
} | {
|
|
147
|
+
readonly label: "Connected";
|
|
148
|
+
readonly value: "connected";
|
|
149
|
+
} | {
|
|
150
|
+
readonly label: "Disconnected";
|
|
151
|
+
readonly value: "disconnected";
|
|
152
|
+
})[];
|
|
153
|
+
};
|
|
154
|
+
};
|
|
155
|
+
};
|
|
156
|
+
};
|
|
157
|
+
};
|
|
158
|
+
};
|
|
159
|
+
};
|
|
160
|
+
};
|
|
161
|
+
};
|
|
162
|
+
};
|
|
163
|
+
}>;
|
|
164
|
+
static reset(): void;
|
|
165
|
+
static getInstance(): SkillViewEmitter;
|
|
166
|
+
}
|
|
167
|
+
export {};
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const mercury_client_1 = require("@sprucelabs/mercury-client");
|
|
4
|
+
const mercury_event_emitter_1 = require("@sprucelabs/mercury-event-emitter");
|
|
5
|
+
const mercury_types_1 = require("@sprucelabs/mercury-types");
|
|
6
|
+
const schema_1 = require("@sprucelabs/schema");
|
|
7
|
+
const contract = (0, mercury_types_1.buildEventContract)({
|
|
8
|
+
eventSignatures: {
|
|
9
|
+
...mercury_client_1.connectionStatusContract.eventSignatures,
|
|
10
|
+
'did-change-orientation': {},
|
|
11
|
+
'did-resize': {},
|
|
12
|
+
'did-render': {},
|
|
13
|
+
'did-resize-content': {},
|
|
14
|
+
'did-place-cards': {},
|
|
15
|
+
'will-change-route': {},
|
|
16
|
+
'did-render-dialog': {},
|
|
17
|
+
'did-keydown': {
|
|
18
|
+
emitPayloadSchema: (0, schema_1.buildSchema)({
|
|
19
|
+
id: 'didKeydownEmitPayload',
|
|
20
|
+
fields: {
|
|
21
|
+
e: {
|
|
22
|
+
type: 'raw',
|
|
23
|
+
isRequired: true,
|
|
24
|
+
options: {
|
|
25
|
+
valueType: 'KeyboardEvent',
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
}),
|
|
30
|
+
},
|
|
31
|
+
'did-scroll': {
|
|
32
|
+
emitPayloadSchema: (0, schema_1.buildSchema)({
|
|
33
|
+
id: 'didScrollEmitPayload',
|
|
34
|
+
fields: {
|
|
35
|
+
scrollTop: {
|
|
36
|
+
type: 'number',
|
|
37
|
+
isRequired: true,
|
|
38
|
+
},
|
|
39
|
+
skillViewNode: {
|
|
40
|
+
type: 'raw',
|
|
41
|
+
isRequired: true,
|
|
42
|
+
options: {
|
|
43
|
+
valueType: 'HTMLDivElement',
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
}),
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
class SkillViewEmitterImpl extends mercury_event_emitter_1.AbstractEventEmitter {
|
|
52
|
+
constructor(contract) {
|
|
53
|
+
super(contract);
|
|
54
|
+
}
|
|
55
|
+
static Emitter() {
|
|
56
|
+
return new this(contract);
|
|
57
|
+
}
|
|
58
|
+
static reset() {
|
|
59
|
+
this.instance = null;
|
|
60
|
+
}
|
|
61
|
+
static getInstance() {
|
|
62
|
+
if (!this.instance) {
|
|
63
|
+
this.instance = this.Emitter();
|
|
64
|
+
}
|
|
65
|
+
return this.instance;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
exports.default = SkillViewEmitterImpl;
|
|
69
|
+
//# sourceMappingURL=SkillViewEmitter.js.map
|
|
@@ -1,12 +1,3 @@
|
|
|
1
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
-
});
|
|
9
|
-
};
|
|
10
1
|
import { vcAssert, } from '@sprucelabs/heartwood-view-controllers';
|
|
11
2
|
import { assert } from '@sprucelabs/test-utils';
|
|
12
3
|
class MockRemoteViewControllerFactory {
|
|
@@ -45,8 +36,7 @@ class MockRemoteViewControllerFactory {
|
|
|
45
36
|
}
|
|
46
37
|
}
|
|
47
38
|
assertSkillViewRendersRemoteCard(vc, name, id) {
|
|
48
|
-
|
|
49
|
-
const Class = (_a = MockRemoteViewControllerFactory.controllers) === null || _a === void 0 ? void 0 : _a[name];
|
|
39
|
+
const Class = MockRemoteViewControllerFactory.controllers?.[name];
|
|
50
40
|
assert.isTruthy(Class, `You did not drop in a remote card with the name '${name}'! Try MockRemoteViewControllerFactory.dropInRemoteController('${name}', Class) in your beforeEach().`);
|
|
51
41
|
const cards = vcAssert.assertSkillViewRendersCards(vc);
|
|
52
42
|
const cardsWithNoControllers = cards.filter((c) => !c);
|
|
@@ -92,7 +82,7 @@ class MockRemoteViewControllerFactory {
|
|
|
92
82
|
try {
|
|
93
83
|
this.assertFetchedRemoteController(id);
|
|
94
84
|
}
|
|
95
|
-
catch
|
|
85
|
+
catch {
|
|
96
86
|
return;
|
|
97
87
|
}
|
|
98
88
|
assert.fail(`You fetched the controller with the id '${id}' but you should not have.`);
|
|
@@ -107,23 +97,17 @@ class MockRemoteViewControllerFactory {
|
|
|
107
97
|
assert.isEqualDeep(this.constructorOptionsById[id], expected, `The constructor options for the remote card with id '${id}' do not match the expected options. Make sure the card you are rendering has an id using this.views.Controller('card', { id })`);
|
|
108
98
|
}
|
|
109
99
|
hasController(name) {
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
});
|
|
122
|
-
}
|
|
123
|
-
loadViewsForNamespace(namespace) {
|
|
124
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
125
|
-
this.loadedNamespaces[namespace] = true;
|
|
126
|
-
});
|
|
100
|
+
return this.loadedControllers[name] ?? false;
|
|
101
|
+
}
|
|
102
|
+
async RemoteController(id, options) {
|
|
103
|
+
assert.isTruthy(MockRemoteViewControllerFactory.controllers?.[id], `Could not find a remote card with the id '${id}'! Try MockRemoteViewControllerFactory.dropInRemoteController('${id}', Class) in your beforeEach().`);
|
|
104
|
+
this.loadedControllers[id] = true;
|
|
105
|
+
this.constructorOptionsById[options.id ?? id] = options;
|
|
106
|
+
this.lastRemoteCostructorOptions = options;
|
|
107
|
+
return this.views.Controller(id, options);
|
|
108
|
+
}
|
|
109
|
+
async loadViewsForNamespace(namespace) {
|
|
110
|
+
this.loadedNamespaces[namespace] = true;
|
|
127
111
|
}
|
|
128
112
|
assertLoadedViewsForNamespace(namespace) {
|
|
129
113
|
assert.isTrue(this.loadedNamespaces[namespace], `You did not load views for the namespace '${namespace}'. Try removeVc.loadViewsForNamespace('${namespace}').`);
|