@shimmer-from-structure/angular 2.2.3 → 2.3.0
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 +124 -4
- package/ng-package.json +8 -0
- package/package.json +29 -19
- package/src/public-api.ts +9 -0
- package/src/shimmer-config.service.test.ts +88 -0
- package/{shimmer-config.service.d.ts → src/shimmer-config.service.ts} +20 -7
- package/src/shimmer.component.test.ts +175 -0
- package/src/shimmer.component.ts +277 -0
- package/src/test/setup.ts +52 -0
- package/src/types.ts +34 -0
- package/tsconfig.eslint.json +5 -0
- package/tsconfig.json +17 -0
- package/vitest.config.ts +12 -0
- package/fesm2022/shimmer-from-structure-angular.mjs +0 -298
- package/fesm2022/shimmer-from-structure-angular.mjs.map +0 -1
- package/index.d.ts +0 -5
- package/public-api.d.ts +0 -3
- package/shimmer.component.d.ts +0 -48
- package/types.d.ts +0 -28
package/README.md
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
# ✨ Shimmer From Structure
|
|
2
2
|
|
|
3
|
-
A **React, Vue, Svelte &
|
|
3
|
+
A **React, Vue, Svelte, Angular & SolidJS** shimmer/skeleton library that **automatically adapts to your component's runtime structure**. Unlike traditional shimmer libraries that require pre-defined skeleton structures, this library analyzes your actual component's DOM at runtime and generates a shimmer effect that perfectly matches its layout.
|
|
4
4
|
|
|
5
5
|

|
|
6
6
|

|
|
7
7
|

|
|
8
8
|

|
|
9
|
+

|
|
9
10
|
|
|
10
11
|

|
|
11
12
|
|
|
@@ -19,7 +20,7 @@ Traditional shimmer libraries require you to:
|
|
|
19
20
|
|
|
20
21
|
**Shimmer From Structure** eliminates all of that:
|
|
21
22
|
|
|
22
|
-
- ✅ **Works with React, Vue, Svelte &
|
|
23
|
+
- ✅ **Works with React, Vue, Svelte, Angular & SolidJS** - Simple, framework-specific adapters
|
|
23
24
|
- ✅ Automatically measures your component's structure at runtime
|
|
24
25
|
- ✅ Generates shimmer effects that match actual dimensions
|
|
25
26
|
- ✅ Zero maintenance - works with any layout changes
|
|
@@ -44,7 +45,7 @@ Shimmer From Structure provides dedicated packages for **React and Vue**.
|
|
|
44
45
|
|
|
45
46
|
### React
|
|
46
47
|
|
|
47
|
-
React support is built
|
|
48
|
+
React support is built into the main package for backward compatibility:
|
|
48
49
|
|
|
49
50
|
```javascript
|
|
50
51
|
// React projects (or @shimmer-from-structure/react)
|
|
@@ -78,6 +79,15 @@ Angular support requires importing from the specific adapter:
|
|
|
78
79
|
import { ShimmerComponent } from '@shimmer-from-structure/angular';
|
|
79
80
|
```
|
|
80
81
|
|
|
82
|
+
### SolidJS
|
|
83
|
+
|
|
84
|
+
SolidJS support requires importing from the specific adapter:
|
|
85
|
+
|
|
86
|
+
```tsx
|
|
87
|
+
// SolidJS projects
|
|
88
|
+
import { Shimmer } from '@shimmer-from-structure/solid';
|
|
89
|
+
```
|
|
90
|
+
|
|
81
91
|
---
|
|
82
92
|
|
|
83
93
|
# 📖 Basic Usage
|
|
@@ -174,6 +184,29 @@ export class UserCardComponent {
|
|
|
174
184
|
}
|
|
175
185
|
```
|
|
176
186
|
|
|
187
|
+
## SolidJS
|
|
188
|
+
|
|
189
|
+
### Static Content
|
|
190
|
+
|
|
191
|
+
```tsx
|
|
192
|
+
import { createSignal } from 'solid-js';
|
|
193
|
+
import { Shimmer } from '@shimmer-from-structure/solid';
|
|
194
|
+
|
|
195
|
+
function UserCard() {
|
|
196
|
+
const [isLoading, setIsLoading] = createSignal(true);
|
|
197
|
+
|
|
198
|
+
return (
|
|
199
|
+
<Shimmer loading={isLoading()}>
|
|
200
|
+
<div class="card">
|
|
201
|
+
<img src="avatar.jpg" class="avatar" />
|
|
202
|
+
<h2>John Doe</h2>
|
|
203
|
+
<p>Software Engineer</p>
|
|
204
|
+
</div>
|
|
205
|
+
</Shimmer>
|
|
206
|
+
);
|
|
207
|
+
}
|
|
208
|
+
```
|
|
209
|
+
|
|
177
210
|
---
|
|
178
211
|
|
|
179
212
|
### Dynamic Content with `templateProps`
|
|
@@ -287,6 +320,31 @@ export class AppComponent {
|
|
|
287
320
|
}
|
|
288
321
|
```
|
|
289
322
|
|
|
323
|
+
**SolidJS**
|
|
324
|
+
|
|
325
|
+
```tsx
|
|
326
|
+
import { createSignal } from 'solid-js';
|
|
327
|
+
import { Shimmer } from '@shimmer-from-structure/solid';
|
|
328
|
+
import { UserCard } from './UserCard';
|
|
329
|
+
|
|
330
|
+
function App() {
|
|
331
|
+
const [loading, setLoading] = createSignal(true);
|
|
332
|
+
const [user, setUser] = createSignal(null);
|
|
333
|
+
|
|
334
|
+
const userTemplate = {
|
|
335
|
+
name: 'Loading...',
|
|
336
|
+
role: 'Loading role...',
|
|
337
|
+
avatar: 'placeholder.jpg',
|
|
338
|
+
};
|
|
339
|
+
|
|
340
|
+
return (
|
|
341
|
+
<Shimmer loading={loading()} templateProps={{ user: userTemplate }}>
|
|
342
|
+
<UserCard user={user() || userTemplate} />
|
|
343
|
+
</Shimmer>
|
|
344
|
+
);
|
|
345
|
+
}
|
|
346
|
+
```
|
|
347
|
+
|
|
290
348
|
The `templateProps` object is spread onto the first child component when loading, allowing it to render with mock data for measurement.
|
|
291
349
|
|
|
292
350
|
## 🎨 API Reference
|
|
@@ -378,6 +436,24 @@ The `templateProps` object is spread onto the first child component when loading
|
|
|
378
436
|
</shimmer>
|
|
379
437
|
```
|
|
380
438
|
|
|
439
|
+
**SolidJS**
|
|
440
|
+
|
|
441
|
+
```tsx
|
|
442
|
+
<Shimmer
|
|
443
|
+
loading={isLoading()}
|
|
444
|
+
shimmerColor="rgba(255, 255, 255, 0.2)"
|
|
445
|
+
backgroundColor="rgba(255, 255, 255, 0.1)"
|
|
446
|
+
duration={2}
|
|
447
|
+
fallbackBorderRadius={8}
|
|
448
|
+
templateProps={{
|
|
449
|
+
user: userTemplate,
|
|
450
|
+
settings: settingsTemplate,
|
|
451
|
+
}}
|
|
452
|
+
>
|
|
453
|
+
<MyComponent user={user()} settings={settings()} />
|
|
454
|
+
</Shimmer>
|
|
455
|
+
```
|
|
456
|
+
|
|
381
457
|
## 🔧 How It Works
|
|
382
458
|
|
|
383
459
|
1. **Visible Container Rendering**: When `loading={true}`, your component renders with transparent text but **visible container backgrounds**
|
|
@@ -702,6 +778,27 @@ bootstrapApplication(AppComponent, {
|
|
|
702
778
|
});
|
|
703
779
|
```
|
|
704
780
|
|
|
781
|
+
### SolidJS (ShimmerProvider)
|
|
782
|
+
|
|
783
|
+
```tsx
|
|
784
|
+
import { Shimmer, ShimmerProvider } from '@shimmer-from-structure/solid';
|
|
785
|
+
|
|
786
|
+
function App() {
|
|
787
|
+
return (
|
|
788
|
+
<ShimmerProvider
|
|
789
|
+
config={{
|
|
790
|
+
shimmerColor: 'rgba(56, 189, 248, 0.4)',
|
|
791
|
+
backgroundColor: 'rgba(56, 189, 248, 0.1)',
|
|
792
|
+
duration: 2.5,
|
|
793
|
+
fallbackBorderRadius: 8,
|
|
794
|
+
}}
|
|
795
|
+
>
|
|
796
|
+
<Dashboard />
|
|
797
|
+
</ShimmerProvider>
|
|
798
|
+
);
|
|
799
|
+
}
|
|
800
|
+
```
|
|
801
|
+
|
|
705
802
|
---
|
|
706
803
|
|
|
707
804
|
Components inside the provider automatically inherit values. You can still override them locally:
|
|
@@ -746,6 +843,16 @@ Components inside the provider automatically inherit values. You can still overr
|
|
|
746
843
|
<shimmer [loading]="true" [duration]="0.5"><app-fast-card /></shimmer>
|
|
747
844
|
```
|
|
748
845
|
|
|
846
|
+
**SolidJS**
|
|
847
|
+
|
|
848
|
+
```tsx
|
|
849
|
+
<!-- Inherits blue theme from provider -->
|
|
850
|
+
<Shimmer loading={true()}><UserCard /></Shimmer>
|
|
851
|
+
|
|
852
|
+
<!-- Overrides provider settings -->
|
|
853
|
+
<Shimmer loading={true()} duration={0.5}><FastCard /></Shimmer>
|
|
854
|
+
```
|
|
855
|
+
|
|
749
856
|
### Accessing Config in Hooks/Composables
|
|
750
857
|
|
|
751
858
|
If you need to access the current configuration in your own components:
|
|
@@ -794,6 +901,17 @@ export class MyComponent {
|
|
|
794
901
|
}
|
|
795
902
|
```
|
|
796
903
|
|
|
904
|
+
**SolidJS**
|
|
905
|
+
|
|
906
|
+
```tsx
|
|
907
|
+
import { useShimmerConfig } from '@shimmer-from-structure/solid';
|
|
908
|
+
|
|
909
|
+
function MyComponent() {
|
|
910
|
+
const config = useShimmerConfig();
|
|
911
|
+
return <div style={{ background: config.backgroundColor }}>...</div>;
|
|
912
|
+
}
|
|
913
|
+
```
|
|
914
|
+
|
|
797
915
|
## Best Practices
|
|
798
916
|
|
|
799
917
|
### 1. Use `templateProps` for Dynamic Data
|
|
@@ -890,9 +1008,10 @@ This library is organized as a monorepo with four packages:
|
|
|
890
1008
|
| `@shimmer-from-structure/vue` | Vue 3 adapter | 3.89 kB |
|
|
891
1009
|
| `@shimmer-from-structure/svelte` | Svelte adapter | 4.60 kB |
|
|
892
1010
|
| `@shimmer-from-structure/angular` | Angular adapter | 6.83 kB |
|
|
1011
|
+
| `@shimmer-from-structure/solid` | SolidJS adapter | 4.01 kB |
|
|
893
1012
|
| `shimmer-from-structure` | Main package (React backward compatibility) | 0.93 kB |
|
|
894
1013
|
|
|
895
|
-
The core package contains all DOM measurement logic, while React, Vue, Svelte and
|
|
1014
|
+
The core package contains all DOM measurement logic, while React, Vue, Svelte, Angular and SolidJS packages are thin wrappers that provide framework-specific APIs.
|
|
896
1015
|
|
|
897
1016
|
## 🚧 Roadmap
|
|
898
1017
|
|
|
@@ -902,6 +1021,7 @@ The core package contains all DOM measurement logic, while React, Vue, Svelte an
|
|
|
902
1021
|
- [x] **Vue.js adapter**
|
|
903
1022
|
- [x] **Svelte adapter**
|
|
904
1023
|
- [x] **Angular adapter**
|
|
1024
|
+
- [x] **SolidJS adapter**
|
|
905
1025
|
- [ ] Better async component support
|
|
906
1026
|
- [ ] Customizable shimmer direction (vertical, diagonal)
|
|
907
1027
|
- [ ] React Native support
|
package/ng-package.json
ADDED
package/package.json
CHANGED
|
@@ -1,14 +1,36 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shimmer-from-structure/angular",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.3.0",
|
|
4
4
|
"description": "Angular adapter for shimmer-from-structure",
|
|
5
5
|
"peerDependencies": {
|
|
6
|
-
"@angular/
|
|
7
|
-
"@angular/
|
|
6
|
+
"@angular/common": "^19.0.0 || ^20.0.0 || ^21.0.0",
|
|
7
|
+
"@angular/core": "^19.0.0 || ^20.0.0 || ^21.0.0"
|
|
8
8
|
},
|
|
9
9
|
"dependencies": {
|
|
10
|
-
"@shimmer-from-structure/core": "*"
|
|
11
|
-
|
|
10
|
+
"@shimmer-from-structure/core": "*"
|
|
11
|
+
},
|
|
12
|
+
"devDependencies": {
|
|
13
|
+
"@analogjs/vite-plugin-angular": "^2.2.3",
|
|
14
|
+
"@angular/common": "^19.0.0",
|
|
15
|
+
"@angular/compiler": "^19.0.0",
|
|
16
|
+
"@angular/compiler-cli": "^19.0.0",
|
|
17
|
+
"@angular/core": "^19.0.0",
|
|
18
|
+
"@angular/platform-browser": "^19.0.0",
|
|
19
|
+
"@angular/platform-browser-dynamic": "^19.0.0",
|
|
20
|
+
"@testing-library/dom": "^10.4.1",
|
|
21
|
+
"jsdom": "^27.4.0",
|
|
22
|
+
"ng-packagr": "^19.0.0",
|
|
23
|
+
"rxjs": "~7.8.0",
|
|
24
|
+
"typescript": "~5.6.0",
|
|
25
|
+
"vite": "^7.3.1",
|
|
26
|
+
"vitest": "^4.0.17",
|
|
27
|
+
"zone.js": "~0.15.0"
|
|
28
|
+
},
|
|
29
|
+
"scripts": {
|
|
30
|
+
"build": "ng-packagr -p ng-package.json",
|
|
31
|
+
"test": "vitest run",
|
|
32
|
+
"format": "prettier --write .",
|
|
33
|
+
"prepublishOnly": "cp ../../README.md ."
|
|
12
34
|
},
|
|
13
35
|
"keywords": [
|
|
14
36
|
"angular",
|
|
@@ -22,17 +44,5 @@
|
|
|
22
44
|
"directory": "packages/angular"
|
|
23
45
|
},
|
|
24
46
|
"author": "Olebogeng Mbedzi",
|
|
25
|
-
"license": "MIT"
|
|
26
|
-
|
|
27
|
-
"typings": "index.d.ts",
|
|
28
|
-
"exports": {
|
|
29
|
-
"./package.json": {
|
|
30
|
-
"default": "./package.json"
|
|
31
|
-
},
|
|
32
|
-
".": {
|
|
33
|
-
"types": "./index.d.ts",
|
|
34
|
-
"default": "./fesm2022/shimmer-from-structure-angular.mjs"
|
|
35
|
-
}
|
|
36
|
-
},
|
|
37
|
-
"sideEffects": false
|
|
38
|
-
}
|
|
47
|
+
"license": "MIT"
|
|
48
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// Public API Surface
|
|
2
|
+
export { ShimmerComponent } from './shimmer.component';
|
|
3
|
+
export {
|
|
4
|
+
SHIMMER_CONFIG,
|
|
5
|
+
provideShimmerConfig,
|
|
6
|
+
injectShimmerConfig,
|
|
7
|
+
shimmerDefaults,
|
|
8
|
+
} from './shimmer-config.service';
|
|
9
|
+
export type { ShimmerInputs, ShimmerConfig, ShimmerContextValue, ElementInfo } from './types';
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { TestBed } from '@angular/core/testing';
|
|
3
|
+
import {
|
|
4
|
+
provideShimmerConfig,
|
|
5
|
+
injectShimmerConfig,
|
|
6
|
+
shimmerDefaults,
|
|
7
|
+
SHIMMER_CONFIG,
|
|
8
|
+
} from './shimmer-config.service';
|
|
9
|
+
|
|
10
|
+
describe('shimmer-config.service', () => {
|
|
11
|
+
describe('shimmerDefaults', () => {
|
|
12
|
+
it('exports default values', () => {
|
|
13
|
+
expect(shimmerDefaults).toBeDefined();
|
|
14
|
+
expect(shimmerDefaults.shimmerColor).toBeDefined();
|
|
15
|
+
expect(shimmerDefaults.backgroundColor).toBeDefined();
|
|
16
|
+
expect(shimmerDefaults.duration).toBeDefined();
|
|
17
|
+
expect(shimmerDefaults.fallbackBorderRadius).toBeDefined();
|
|
18
|
+
});
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
describe('provideShimmerConfig', () => {
|
|
22
|
+
it('creates a provider object', () => {
|
|
23
|
+
const config = { shimmerColor: '#fff', duration: 2 };
|
|
24
|
+
const provider = provideShimmerConfig(config);
|
|
25
|
+
|
|
26
|
+
expect(provider.provide).toBe(SHIMMER_CONFIG);
|
|
27
|
+
expect(provider.useValue).toBe(config);
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
describe('injectShimmerConfig', () => {
|
|
32
|
+
it('returns defaults when no provider is configured', () => {
|
|
33
|
+
TestBed.configureTestingModule({});
|
|
34
|
+
|
|
35
|
+
TestBed.runInInjectionContext(() => {
|
|
36
|
+
const config = injectShimmerConfig();
|
|
37
|
+
|
|
38
|
+
expect(config.shimmerColor).toBe(shimmerDefaults.shimmerColor);
|
|
39
|
+
expect(config.backgroundColor).toBe(shimmerDefaults.backgroundColor);
|
|
40
|
+
expect(config.duration).toBe(shimmerDefaults.duration);
|
|
41
|
+
expect(config.fallbackBorderRadius).toBe(shimmerDefaults.fallbackBorderRadius);
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('merges provided config with defaults', () => {
|
|
46
|
+
TestBed.configureTestingModule({
|
|
47
|
+
providers: [
|
|
48
|
+
provideShimmerConfig({
|
|
49
|
+
shimmerColor: 'rgba(255, 255, 255, 0.8)',
|
|
50
|
+
duration: 3,
|
|
51
|
+
}),
|
|
52
|
+
],
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
TestBed.runInInjectionContext(() => {
|
|
56
|
+
const config = injectShimmerConfig();
|
|
57
|
+
|
|
58
|
+
expect(config.shimmerColor).toBe('rgba(255, 255, 255, 0.8)');
|
|
59
|
+
expect(config.duration).toBe(3);
|
|
60
|
+
// Should use defaults for non-provided values
|
|
61
|
+
expect(config.backgroundColor).toBe(shimmerDefaults.backgroundColor);
|
|
62
|
+
expect(config.fallbackBorderRadius).toBe(shimmerDefaults.fallbackBorderRadius);
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('returns fully resolved ShimmerContextValue', () => {
|
|
67
|
+
TestBed.configureTestingModule({
|
|
68
|
+
providers: [
|
|
69
|
+
provideShimmerConfig({
|
|
70
|
+
shimmerColor: '#aaa',
|
|
71
|
+
backgroundColor: '#bbb',
|
|
72
|
+
duration: 1.5,
|
|
73
|
+
fallbackBorderRadius: 10,
|
|
74
|
+
}),
|
|
75
|
+
],
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
TestBed.runInInjectionContext(() => {
|
|
79
|
+
const config = injectShimmerConfig();
|
|
80
|
+
|
|
81
|
+
expect(config.shimmerColor).toBe('#aaa');
|
|
82
|
+
expect(config.backgroundColor).toBe('#bbb');
|
|
83
|
+
expect(config.duration).toBe(1.5);
|
|
84
|
+
expect(config.fallbackBorderRadius).toBe(10);
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
});
|
|
@@ -1,11 +1,13 @@
|
|
|
1
|
-
import { InjectionToken } from '@angular/core';
|
|
1
|
+
import { InjectionToken, inject } from '@angular/core';
|
|
2
2
|
import type { ShimmerConfig, ShimmerContextValue } from '@shimmer-from-structure/core';
|
|
3
3
|
import { shimmerDefaults } from '@shimmer-from-structure/core';
|
|
4
|
+
|
|
4
5
|
/**
|
|
5
6
|
* Injection token for global shimmer configuration.
|
|
6
7
|
* Use `provideShimmerConfig()` to configure in your app.
|
|
7
8
|
*/
|
|
8
|
-
export
|
|
9
|
+
export const SHIMMER_CONFIG = new InjectionToken<ShimmerConfig>('SHIMMER_CONFIG');
|
|
10
|
+
|
|
9
11
|
/**
|
|
10
12
|
* Provider function for shimmer configuration.
|
|
11
13
|
* Use in your app's providers array.
|
|
@@ -22,14 +24,25 @@ export declare const SHIMMER_CONFIG: InjectionToken<ShimmerConfig>;
|
|
|
22
24
|
* });
|
|
23
25
|
* ```
|
|
24
26
|
*/
|
|
25
|
-
export
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
export function provideShimmerConfig(config: ShimmerConfig) {
|
|
28
|
+
return { provide: SHIMMER_CONFIG, useValue: config };
|
|
29
|
+
}
|
|
30
|
+
|
|
29
31
|
/**
|
|
30
32
|
* Inject and resolve shimmer configuration.
|
|
31
33
|
* Merges injected config with defaults.
|
|
32
34
|
* Returns fully resolved ShimmerContextValue with all properties defined.
|
|
33
35
|
*/
|
|
34
|
-
export
|
|
36
|
+
export function injectShimmerConfig(): ShimmerContextValue {
|
|
37
|
+
const config = inject(SHIMMER_CONFIG, { optional: true }) ?? {};
|
|
38
|
+
|
|
39
|
+
return {
|
|
40
|
+
shimmerColor: config.shimmerColor ?? shimmerDefaults.shimmerColor,
|
|
41
|
+
backgroundColor: config.backgroundColor ?? shimmerDefaults.backgroundColor,
|
|
42
|
+
duration: config.duration ?? shimmerDefaults.duration,
|
|
43
|
+
fallbackBorderRadius: config.fallbackBorderRadius ?? shimmerDefaults.fallbackBorderRadius,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Re-export defaults for testing and reference
|
|
35
48
|
export { shimmerDefaults };
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach } from 'vitest';
|
|
2
|
+
import { Component, NO_ERRORS_SCHEMA, signal, ChangeDetectorRef } from '@angular/core';
|
|
3
|
+
import { TestBed, ComponentFixture } from '@angular/core/testing';
|
|
4
|
+
import { By } from '@angular/platform-browser';
|
|
5
|
+
import { ShimmerComponent } from './shimmer.component';
|
|
6
|
+
import { provideShimmerConfig, SHIMMER_CONFIG } from './shimmer-config.service';
|
|
7
|
+
|
|
8
|
+
// Test wrapper component for content projection
|
|
9
|
+
@Component({
|
|
10
|
+
selector: 'test-host',
|
|
11
|
+
standalone: true,
|
|
12
|
+
imports: [ShimmerComponent],
|
|
13
|
+
template: `
|
|
14
|
+
<shimmer [loading]="loading">
|
|
15
|
+
<div class="test-content" style="width: 100px; height: 50px;">Content</div>
|
|
16
|
+
</shimmer>
|
|
17
|
+
`,
|
|
18
|
+
schemas: [NO_ERRORS_SCHEMA],
|
|
19
|
+
})
|
|
20
|
+
class TestHostComponent {
|
|
21
|
+
loading = true;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
describe('ShimmerComponent', () => {
|
|
25
|
+
let fixture: ComponentFixture<TestHostComponent>;
|
|
26
|
+
let hostComponent: TestHostComponent;
|
|
27
|
+
|
|
28
|
+
beforeEach(async () => {
|
|
29
|
+
await TestBed.configureTestingModule({
|
|
30
|
+
imports: [TestHostComponent, ShimmerComponent],
|
|
31
|
+
}).compileComponents();
|
|
32
|
+
|
|
33
|
+
fixture = TestBed.createComponent(TestHostComponent);
|
|
34
|
+
hostComponent = fixture.componentInstance;
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
// Helper to wait for async operations
|
|
38
|
+
const wait = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms || 50));
|
|
39
|
+
|
|
40
|
+
it('renders children normally when loading=false', async () => {
|
|
41
|
+
fixture.detectChanges();
|
|
42
|
+
const shimmerEl = fixture.debugElement.query(By.directive(ShimmerComponent));
|
|
43
|
+
const shimmerInstance = shimmerEl.componentInstance;
|
|
44
|
+
|
|
45
|
+
Object.defineProperty(shimmerInstance, 'loading', { value: signal(false) });
|
|
46
|
+
shimmerEl.injector.get(ChangeDetectorRef).detectChanges();
|
|
47
|
+
|
|
48
|
+
fixture.detectChanges();
|
|
49
|
+
await wait(0);
|
|
50
|
+
fixture.detectChanges();
|
|
51
|
+
|
|
52
|
+
const content = fixture.nativeElement.querySelector('.test-content');
|
|
53
|
+
expect(content).toBeTruthy();
|
|
54
|
+
expect(content.textContent).toBe('Content');
|
|
55
|
+
|
|
56
|
+
// Should not have the measure container when not loading
|
|
57
|
+
const measureContainer = fixture.nativeElement.querySelector('.shimmer-measure-container');
|
|
58
|
+
expect(measureContainer).toBeFalsy();
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it('renders shimmer structure when loading=true', async () => {
|
|
62
|
+
hostComponent.loading = true;
|
|
63
|
+
fixture.detectChanges();
|
|
64
|
+
await wait(0);
|
|
65
|
+
fixture.detectChanges();
|
|
66
|
+
|
|
67
|
+
// Should render the measure container
|
|
68
|
+
const measureContainer = fixture.nativeElement.querySelector('.shimmer-measure-container');
|
|
69
|
+
expect(measureContainer).toBeTruthy();
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it('applies transparent text style to measure container', async () => {
|
|
73
|
+
hostComponent.loading = true;
|
|
74
|
+
fixture.detectChanges();
|
|
75
|
+
await wait(0);
|
|
76
|
+
fixture.detectChanges();
|
|
77
|
+
|
|
78
|
+
// Check that the measure container has the class
|
|
79
|
+
const measureContainer = fixture.nativeElement.querySelector('.shimmer-measure-container');
|
|
80
|
+
expect(measureContainer).toBeTruthy();
|
|
81
|
+
expect(measureContainer.classList.contains('shimmer-measure-container')).toBe(true);
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
describe('ShimmerComponent with config provider', () => {
|
|
86
|
+
let fixture: ComponentFixture<TestHostComponent>;
|
|
87
|
+
const wait = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms || 50));
|
|
88
|
+
|
|
89
|
+
beforeEach(async () => {
|
|
90
|
+
await TestBed.configureTestingModule({
|
|
91
|
+
imports: [TestHostComponent, ShimmerComponent],
|
|
92
|
+
providers: [
|
|
93
|
+
provideShimmerConfig({
|
|
94
|
+
shimmerColor: 'rgba(255, 0, 0, 0.5)',
|
|
95
|
+
backgroundColor: '#ff0000',
|
|
96
|
+
duration: 2,
|
|
97
|
+
fallbackBorderRadius: 8,
|
|
98
|
+
}),
|
|
99
|
+
],
|
|
100
|
+
}).compileComponents();
|
|
101
|
+
|
|
102
|
+
fixture = TestBed.createComponent(TestHostComponent);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it('uses provided config values', async () => {
|
|
106
|
+
fixture.componentInstance.loading = true;
|
|
107
|
+
fixture.detectChanges();
|
|
108
|
+
await wait(0);
|
|
109
|
+
fixture.detectChanges();
|
|
110
|
+
|
|
111
|
+
// The config should be injected - we can verify by checking the SHIMMER_CONFIG token
|
|
112
|
+
const config = TestBed.inject(SHIMMER_CONFIG);
|
|
113
|
+
expect(config.shimmerColor).toBe('rgba(255, 0, 0, 0.5)');
|
|
114
|
+
expect(config.backgroundColor).toBe('#ff0000');
|
|
115
|
+
expect(config.duration).toBe(2);
|
|
116
|
+
expect(config.fallbackBorderRadius).toBe(8);
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
describe('ShimmerComponent input overrides', () => {
|
|
121
|
+
const wait = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms || 50));
|
|
122
|
+
|
|
123
|
+
@Component({
|
|
124
|
+
selector: 'test-override-host',
|
|
125
|
+
standalone: true,
|
|
126
|
+
imports: [ShimmerComponent],
|
|
127
|
+
template: `
|
|
128
|
+
<shimmer
|
|
129
|
+
[loading]="true"
|
|
130
|
+
[shimmerColor]="'#00ff00'"
|
|
131
|
+
[backgroundColor]="'#0000ff'"
|
|
132
|
+
[duration]="3"
|
|
133
|
+
[fallbackBorderRadius]="12"
|
|
134
|
+
>
|
|
135
|
+
<div>Content</div>
|
|
136
|
+
</shimmer>
|
|
137
|
+
`,
|
|
138
|
+
schemas: [NO_ERRORS_SCHEMA],
|
|
139
|
+
})
|
|
140
|
+
class TestOverrideHostComponent {}
|
|
141
|
+
|
|
142
|
+
beforeEach(async () => {
|
|
143
|
+
await TestBed.configureTestingModule({
|
|
144
|
+
imports: [TestOverrideHostComponent, ShimmerComponent],
|
|
145
|
+
providers: [
|
|
146
|
+
provideShimmerConfig({
|
|
147
|
+
shimmerColor: 'rgba(255, 0, 0, 0.5)',
|
|
148
|
+
duration: 1,
|
|
149
|
+
}),
|
|
150
|
+
],
|
|
151
|
+
}).compileComponents();
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
it('component inputs override provider config', async () => {
|
|
155
|
+
const fixture = TestBed.createComponent(TestOverrideHostComponent);
|
|
156
|
+
fixture.detectChanges();
|
|
157
|
+
|
|
158
|
+
// Manual override for input signals
|
|
159
|
+
const shimmer = fixture.debugElement.children[0].componentInstance as ShimmerComponent;
|
|
160
|
+
Object.defineProperty(shimmer, 'shimmerColor', { value: signal('#00ff00') });
|
|
161
|
+
Object.defineProperty(shimmer, 'backgroundColor', { value: signal('#0000ff') });
|
|
162
|
+
Object.defineProperty(shimmer, 'duration', { value: signal(3) });
|
|
163
|
+
Object.defineProperty(shimmer, 'fallbackBorderRadius', { value: signal(12) });
|
|
164
|
+
|
|
165
|
+
fixture.detectChanges();
|
|
166
|
+
await wait(0);
|
|
167
|
+
fixture.detectChanges();
|
|
168
|
+
|
|
169
|
+
// The shimmer component should use input values over config
|
|
170
|
+
expect(shimmer.resolvedShimmerColor()).toBe('#00ff00');
|
|
171
|
+
expect(shimmer.resolvedBackgroundColor()).toBe('#0000ff');
|
|
172
|
+
expect(shimmer.resolvedDuration()).toBe(3);
|
|
173
|
+
expect(shimmer.resolvedFallbackBorderRadius()).toBe(12);
|
|
174
|
+
});
|
|
175
|
+
});
|