@lumaui/angular 0.1.0 → 0.1.2
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 +24 -12
- package/fesm2022/lumaui-angular.mjs +127 -0
- package/fesm2022/lumaui-angular.mjs.map +1 -0
- package/package.json +18 -14
- package/types/lumaui-angular.d.ts +54 -0
- package/src/index.ts +0 -9
- package/src/lib/button/button.directive.spec.ts +0 -1350
- package/src/lib/button/button.directive.ts +0 -34
- package/src/lib/button/button.docs.md +0 -209
- package/src/lib/button/index.ts +0 -3
- package/src/lib/card/card-content.directive.ts +0 -13
- package/src/lib/card/card-description.directive.ts +0 -19
- package/src/lib/card/card-directives.spec.ts +0 -309
- package/src/lib/card/card-header.directive.ts +0 -12
- package/src/lib/card/card-title.directive.ts +0 -16
- package/src/lib/card/card.component.html +0 -5
- package/src/lib/card/card.component.spec.ts +0 -379
- package/src/lib/card/card.component.ts +0 -33
- package/src/lib/card/card.docs.md +0 -452
- package/src/lib/card/index.ts +0 -19
- package/src/test-setup.ts +0 -5
|
@@ -1,1350 +0,0 @@
|
|
|
1
|
-
import { Component, DebugElement } from '@angular/core';
|
|
2
|
-
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
|
3
|
-
|
|
4
|
-
import { ButtonDirective } from './button.directive';
|
|
5
|
-
import { By } from '@angular/platform-browser';
|
|
6
|
-
|
|
7
|
-
// ============================================================
|
|
8
|
-
// TEST HOST COMPONENTS
|
|
9
|
-
// ============================================================
|
|
10
|
-
|
|
11
|
-
@Component({
|
|
12
|
-
template: `
|
|
13
|
-
<button
|
|
14
|
-
lumaButton
|
|
15
|
-
[lmVariant]="lmVariant"
|
|
16
|
-
[lmSize]="lmSize"
|
|
17
|
-
[lmDisabled]="lmDisabled"
|
|
18
|
-
[lmType]="lmType"
|
|
19
|
-
>
|
|
20
|
-
Test Button
|
|
21
|
-
</button>
|
|
22
|
-
`,
|
|
23
|
-
imports: [ButtonDirective],
|
|
24
|
-
})
|
|
25
|
-
class ButtonTestHostComponent {
|
|
26
|
-
lmVariant: 'primary' | 'outline' | 'ghost' | 'danger' = 'primary';
|
|
27
|
-
lmSize: 'sm' | 'md' | 'lg' | 'full' = 'md';
|
|
28
|
-
lmDisabled = false;
|
|
29
|
-
lmType: 'button' | 'submit' | 'reset' = 'button';
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
@Component({
|
|
33
|
-
template: `<a lumaButton [lmVariant]="lmVariant" href="/test"
|
|
34
|
-
>Link Button</a
|
|
35
|
-
>`,
|
|
36
|
-
imports: [ButtonDirective],
|
|
37
|
-
})
|
|
38
|
-
class AnchorButtonTestHostComponent {
|
|
39
|
-
lmVariant: 'primary' | 'outline' | 'ghost' | 'danger' = 'primary';
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
@Component({
|
|
43
|
-
template: `<button lumaButton lmType="submit">Submit Button</button>`,
|
|
44
|
-
imports: [ButtonDirective],
|
|
45
|
-
})
|
|
46
|
-
class SubmitButtonTestHostComponent {}
|
|
47
|
-
|
|
48
|
-
@Component({
|
|
49
|
-
template: `<button lumaButton lmType="reset">Reset Button</button>`,
|
|
50
|
-
imports: [ButtonDirective],
|
|
51
|
-
})
|
|
52
|
-
class ResetButtonTestHostComponent {}
|
|
53
|
-
|
|
54
|
-
// ============================================================
|
|
55
|
-
// TOKEN DEFINITIONS
|
|
56
|
-
// ============================================================
|
|
57
|
-
|
|
58
|
-
const BUTTON_TOKENS = {
|
|
59
|
-
primary: {
|
|
60
|
-
bg: 'oklch(0.54 0.1 230)',
|
|
61
|
-
bgHover: 'oklch(0.49 0.09 230)',
|
|
62
|
-
bgActive: 'oklch(0.44 0.08 230)',
|
|
63
|
-
text: 'oklch(1 0 0)',
|
|
64
|
-
},
|
|
65
|
-
outline: {
|
|
66
|
-
border: 'oklch(0.5 0.01 0)',
|
|
67
|
-
borderHover: 'oklch(0.2 0.005 0)',
|
|
68
|
-
bgHover: 'oklch(0.96 0.01 230)',
|
|
69
|
-
text: 'oklch(0.2 0.005 0)',
|
|
70
|
-
},
|
|
71
|
-
ghost: {
|
|
72
|
-
bg: 'rgba(0, 0, 0, 0)',
|
|
73
|
-
bgHover: 'oklch(0.96 0.01 230)',
|
|
74
|
-
text: 'oklch(0.2 0.005 0)',
|
|
75
|
-
},
|
|
76
|
-
danger: {
|
|
77
|
-
bg: 'oklch(0.55 0.22 25)',
|
|
78
|
-
bgHover: 'oklch(0.50 0.20 25)',
|
|
79
|
-
bgActive: 'oklch(0.45 0.18 25)',
|
|
80
|
-
text: 'oklch(1 0 0)',
|
|
81
|
-
},
|
|
82
|
-
padding: {
|
|
83
|
-
xSm: '1rem',
|
|
84
|
-
xMd: '1.5rem',
|
|
85
|
-
xLg: '2rem',
|
|
86
|
-
ySm: '0.5rem',
|
|
87
|
-
yMd: '0.75rem',
|
|
88
|
-
yLg: '1rem',
|
|
89
|
-
},
|
|
90
|
-
radius: '10px',
|
|
91
|
-
focus: {
|
|
92
|
-
ringWidth: '2px',
|
|
93
|
-
ringColor: 'oklch(0.54 0.1 230 / 0.25)',
|
|
94
|
-
},
|
|
95
|
-
transition: {
|
|
96
|
-
duration: '200ms',
|
|
97
|
-
timing: 'ease-out',
|
|
98
|
-
},
|
|
99
|
-
} as const;
|
|
100
|
-
|
|
101
|
-
const DARK_TOKENS = {
|
|
102
|
-
primary: {
|
|
103
|
-
bg: 'oklch(0.64 0.12 230)',
|
|
104
|
-
bgHover: 'oklch(0.69 0.12 230)',
|
|
105
|
-
bgActive: 'oklch(0.74 0.13 230)',
|
|
106
|
-
text: 'oklch(0.1 0 0)',
|
|
107
|
-
},
|
|
108
|
-
outline: {
|
|
109
|
-
border: 'oklch(0.65 0.01 0)',
|
|
110
|
-
borderHover: 'oklch(0.98 0.002 0)',
|
|
111
|
-
bgHover: 'oklch(0.20 0.01 230)',
|
|
112
|
-
text: 'oklch(0.98 0.002 0)',
|
|
113
|
-
},
|
|
114
|
-
ghost: {
|
|
115
|
-
bgHover: 'oklch(0.20 0.01 230)',
|
|
116
|
-
text: 'oklch(0.98 0.002 0)',
|
|
117
|
-
},
|
|
118
|
-
danger: {
|
|
119
|
-
bg: 'oklch(0.60 0.24 25)',
|
|
120
|
-
bgHover: 'oklch(0.65 0.25 25)',
|
|
121
|
-
bgActive: 'oklch(0.70 0.26 25)',
|
|
122
|
-
text: 'oklch(0.1 0 0)',
|
|
123
|
-
},
|
|
124
|
-
focus: {
|
|
125
|
-
ringColor: 'oklch(0.64 0.12 230 / 0.25)',
|
|
126
|
-
},
|
|
127
|
-
} as const;
|
|
128
|
-
|
|
129
|
-
// ============================================================
|
|
130
|
-
// TOKEN SETUP/CLEANUP UTILITIES
|
|
131
|
-
// ============================================================
|
|
132
|
-
|
|
133
|
-
function setupButtonTokens(): void {
|
|
134
|
-
const root = document.documentElement;
|
|
135
|
-
|
|
136
|
-
// Primary variant
|
|
137
|
-
root.style.setProperty('--luma-button-primary-bg', BUTTON_TOKENS.primary.bg);
|
|
138
|
-
root.style.setProperty(
|
|
139
|
-
'--luma-button-primary-bg-hover',
|
|
140
|
-
BUTTON_TOKENS.primary.bgHover,
|
|
141
|
-
);
|
|
142
|
-
root.style.setProperty(
|
|
143
|
-
'--luma-button-primary-bg-active',
|
|
144
|
-
BUTTON_TOKENS.primary.bgActive,
|
|
145
|
-
);
|
|
146
|
-
root.style.setProperty(
|
|
147
|
-
'--luma-button-primary-text',
|
|
148
|
-
BUTTON_TOKENS.primary.text,
|
|
149
|
-
);
|
|
150
|
-
|
|
151
|
-
// Outline variant
|
|
152
|
-
root.style.setProperty(
|
|
153
|
-
'--luma-button-outline-border',
|
|
154
|
-
BUTTON_TOKENS.outline.border,
|
|
155
|
-
);
|
|
156
|
-
root.style.setProperty(
|
|
157
|
-
'--luma-button-outline-border-hover',
|
|
158
|
-
BUTTON_TOKENS.outline.borderHover,
|
|
159
|
-
);
|
|
160
|
-
root.style.setProperty(
|
|
161
|
-
'--luma-button-outline-bg-hover',
|
|
162
|
-
BUTTON_TOKENS.outline.bgHover,
|
|
163
|
-
);
|
|
164
|
-
root.style.setProperty(
|
|
165
|
-
'--luma-button-outline-text',
|
|
166
|
-
BUTTON_TOKENS.outline.text,
|
|
167
|
-
);
|
|
168
|
-
|
|
169
|
-
// Ghost variant
|
|
170
|
-
root.style.setProperty('--luma-button-ghost-bg', BUTTON_TOKENS.ghost.bg);
|
|
171
|
-
root.style.setProperty(
|
|
172
|
-
'--luma-button-ghost-bg-hover',
|
|
173
|
-
BUTTON_TOKENS.ghost.bgHover,
|
|
174
|
-
);
|
|
175
|
-
root.style.setProperty('--luma-button-ghost-text', BUTTON_TOKENS.ghost.text);
|
|
176
|
-
|
|
177
|
-
// Danger variant
|
|
178
|
-
root.style.setProperty('--luma-button-danger-bg', BUTTON_TOKENS.danger.bg);
|
|
179
|
-
root.style.setProperty(
|
|
180
|
-
'--luma-button-danger-bg-hover',
|
|
181
|
-
BUTTON_TOKENS.danger.bgHover,
|
|
182
|
-
);
|
|
183
|
-
root.style.setProperty(
|
|
184
|
-
'--luma-button-danger-bg-active',
|
|
185
|
-
BUTTON_TOKENS.danger.bgActive,
|
|
186
|
-
);
|
|
187
|
-
root.style.setProperty(
|
|
188
|
-
'--luma-button-danger-text',
|
|
189
|
-
BUTTON_TOKENS.danger.text,
|
|
190
|
-
);
|
|
191
|
-
|
|
192
|
-
// Padding
|
|
193
|
-
root.style.setProperty(
|
|
194
|
-
'--luma-button-padding-x-sm',
|
|
195
|
-
BUTTON_TOKENS.padding.xSm,
|
|
196
|
-
);
|
|
197
|
-
root.style.setProperty(
|
|
198
|
-
'--luma-button-padding-x-md',
|
|
199
|
-
BUTTON_TOKENS.padding.xMd,
|
|
200
|
-
);
|
|
201
|
-
root.style.setProperty(
|
|
202
|
-
'--luma-button-padding-x-lg',
|
|
203
|
-
BUTTON_TOKENS.padding.xLg,
|
|
204
|
-
);
|
|
205
|
-
root.style.setProperty(
|
|
206
|
-
'--luma-button-padding-y-sm',
|
|
207
|
-
BUTTON_TOKENS.padding.ySm,
|
|
208
|
-
);
|
|
209
|
-
root.style.setProperty(
|
|
210
|
-
'--luma-button-padding-y-md',
|
|
211
|
-
BUTTON_TOKENS.padding.yMd,
|
|
212
|
-
);
|
|
213
|
-
root.style.setProperty(
|
|
214
|
-
'--luma-button-padding-y-lg',
|
|
215
|
-
BUTTON_TOKENS.padding.yLg,
|
|
216
|
-
);
|
|
217
|
-
|
|
218
|
-
// Radius
|
|
219
|
-
root.style.setProperty('--luma-button-radius', BUTTON_TOKENS.radius);
|
|
220
|
-
|
|
221
|
-
// Focus
|
|
222
|
-
root.style.setProperty(
|
|
223
|
-
'--luma-button-focus-ring-width',
|
|
224
|
-
BUTTON_TOKENS.focus.ringWidth,
|
|
225
|
-
);
|
|
226
|
-
root.style.setProperty(
|
|
227
|
-
'--luma-button-focus-ring-color',
|
|
228
|
-
BUTTON_TOKENS.focus.ringColor,
|
|
229
|
-
);
|
|
230
|
-
|
|
231
|
-
// Transition
|
|
232
|
-
root.style.setProperty(
|
|
233
|
-
'--luma-button-transition-duration',
|
|
234
|
-
BUTTON_TOKENS.transition.duration,
|
|
235
|
-
);
|
|
236
|
-
root.style.setProperty(
|
|
237
|
-
'--luma-button-transition-timing',
|
|
238
|
-
BUTTON_TOKENS.transition.timing,
|
|
239
|
-
);
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
function cleanupButtonTokens(): void {
|
|
243
|
-
const root = document.documentElement;
|
|
244
|
-
const tokenNames = [
|
|
245
|
-
'--luma-button-primary-bg',
|
|
246
|
-
'--luma-button-primary-bg-hover',
|
|
247
|
-
'--luma-button-primary-bg-active',
|
|
248
|
-
'--luma-button-primary-text',
|
|
249
|
-
'--luma-button-outline-border',
|
|
250
|
-
'--luma-button-outline-border-hover',
|
|
251
|
-
'--luma-button-outline-bg-hover',
|
|
252
|
-
'--luma-button-outline-text',
|
|
253
|
-
'--luma-button-ghost-bg',
|
|
254
|
-
'--luma-button-ghost-bg-hover',
|
|
255
|
-
'--luma-button-ghost-text',
|
|
256
|
-
'--luma-button-danger-bg',
|
|
257
|
-
'--luma-button-danger-bg-hover',
|
|
258
|
-
'--luma-button-danger-bg-active',
|
|
259
|
-
'--luma-button-danger-text',
|
|
260
|
-
'--luma-button-padding-x-sm',
|
|
261
|
-
'--luma-button-padding-x-md',
|
|
262
|
-
'--luma-button-padding-x-lg',
|
|
263
|
-
'--luma-button-padding-y-sm',
|
|
264
|
-
'--luma-button-padding-y-md',
|
|
265
|
-
'--luma-button-padding-y-lg',
|
|
266
|
-
'--luma-button-radius',
|
|
267
|
-
'--luma-button-focus-ring-width',
|
|
268
|
-
'--luma-button-focus-ring-color',
|
|
269
|
-
'--luma-button-transition-duration',
|
|
270
|
-
'--luma-button-transition-timing',
|
|
271
|
-
];
|
|
272
|
-
|
|
273
|
-
tokenNames.forEach((name) => root.style.removeProperty(name));
|
|
274
|
-
root.classList.remove('dark');
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
function applyDarkTheme(): void {
|
|
278
|
-
const root = document.documentElement;
|
|
279
|
-
root.classList.add('dark');
|
|
280
|
-
|
|
281
|
-
// Primary
|
|
282
|
-
root.style.setProperty('--luma-button-primary-bg', DARK_TOKENS.primary.bg);
|
|
283
|
-
root.style.setProperty(
|
|
284
|
-
'--luma-button-primary-bg-hover',
|
|
285
|
-
DARK_TOKENS.primary.bgHover,
|
|
286
|
-
);
|
|
287
|
-
root.style.setProperty(
|
|
288
|
-
'--luma-button-primary-bg-active',
|
|
289
|
-
DARK_TOKENS.primary.bgActive,
|
|
290
|
-
);
|
|
291
|
-
root.style.setProperty(
|
|
292
|
-
'--luma-button-primary-text',
|
|
293
|
-
DARK_TOKENS.primary.text,
|
|
294
|
-
);
|
|
295
|
-
|
|
296
|
-
// Outline
|
|
297
|
-
root.style.setProperty(
|
|
298
|
-
'--luma-button-outline-border',
|
|
299
|
-
DARK_TOKENS.outline.border,
|
|
300
|
-
);
|
|
301
|
-
root.style.setProperty(
|
|
302
|
-
'--luma-button-outline-border-hover',
|
|
303
|
-
DARK_TOKENS.outline.borderHover,
|
|
304
|
-
);
|
|
305
|
-
root.style.setProperty(
|
|
306
|
-
'--luma-button-outline-bg-hover',
|
|
307
|
-
DARK_TOKENS.outline.bgHover,
|
|
308
|
-
);
|
|
309
|
-
root.style.setProperty(
|
|
310
|
-
'--luma-button-outline-text',
|
|
311
|
-
DARK_TOKENS.outline.text,
|
|
312
|
-
);
|
|
313
|
-
|
|
314
|
-
// Ghost
|
|
315
|
-
root.style.setProperty(
|
|
316
|
-
'--luma-button-ghost-bg-hover',
|
|
317
|
-
DARK_TOKENS.ghost.bgHover,
|
|
318
|
-
);
|
|
319
|
-
root.style.setProperty('--luma-button-ghost-text', DARK_TOKENS.ghost.text);
|
|
320
|
-
|
|
321
|
-
// Danger
|
|
322
|
-
root.style.setProperty('--luma-button-danger-bg', DARK_TOKENS.danger.bg);
|
|
323
|
-
root.style.setProperty(
|
|
324
|
-
'--luma-button-danger-bg-hover',
|
|
325
|
-
DARK_TOKENS.danger.bgHover,
|
|
326
|
-
);
|
|
327
|
-
root.style.setProperty(
|
|
328
|
-
'--luma-button-danger-bg-active',
|
|
329
|
-
DARK_TOKENS.danger.bgActive,
|
|
330
|
-
);
|
|
331
|
-
root.style.setProperty('--luma-button-danger-text', DARK_TOKENS.danger.text);
|
|
332
|
-
|
|
333
|
-
// Focus
|
|
334
|
-
root.style.setProperty(
|
|
335
|
-
'--luma-button-focus-ring-color',
|
|
336
|
-
DARK_TOKENS.focus.ringColor,
|
|
337
|
-
);
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
// ============================================================
|
|
341
|
-
// TEST SUITES
|
|
342
|
-
// ============================================================
|
|
343
|
-
|
|
344
|
-
describe('ButtonDirective', () => {
|
|
345
|
-
let fixture: ComponentFixture<ButtonTestHostComponent>;
|
|
346
|
-
let hostComponent: ButtonTestHostComponent;
|
|
347
|
-
let buttonElement: DebugElement;
|
|
348
|
-
let directive: ButtonDirective;
|
|
349
|
-
|
|
350
|
-
beforeEach(async () => {
|
|
351
|
-
await TestBed.configureTestingModule({
|
|
352
|
-
imports: [
|
|
353
|
-
ButtonDirective,
|
|
354
|
-
ButtonTestHostComponent,
|
|
355
|
-
SubmitButtonTestHostComponent,
|
|
356
|
-
ResetButtonTestHostComponent,
|
|
357
|
-
],
|
|
358
|
-
}).compileComponents();
|
|
359
|
-
|
|
360
|
-
fixture = TestBed.createComponent(ButtonTestHostComponent);
|
|
361
|
-
hostComponent = fixture.componentInstance;
|
|
362
|
-
buttonElement = fixture.debugElement.query(By.directive(ButtonDirective));
|
|
363
|
-
directive = buttonElement.injector.get(ButtonDirective);
|
|
364
|
-
|
|
365
|
-
setupButtonTokens();
|
|
366
|
-
// Note: Don't call detectChanges() here - let each test/describe handle it
|
|
367
|
-
// to avoid ExpressionChangedAfterItHasBeenCheckedError
|
|
368
|
-
});
|
|
369
|
-
|
|
370
|
-
afterEach(() => {
|
|
371
|
-
cleanupButtonTokens();
|
|
372
|
-
});
|
|
373
|
-
|
|
374
|
-
// ============================================================
|
|
375
|
-
// BASIC DIRECTIVE TESTS
|
|
376
|
-
// ============================================================
|
|
377
|
-
|
|
378
|
-
describe('Basic Directive Creation', () => {
|
|
379
|
-
it('should create the directive', () => {
|
|
380
|
-
expect(directive).toBeTruthy();
|
|
381
|
-
});
|
|
382
|
-
|
|
383
|
-
it('should apply as directive on button element', () => {
|
|
384
|
-
fixture.detectChanges();
|
|
385
|
-
expect(buttonElement.nativeElement.tagName).toBe('BUTTON');
|
|
386
|
-
});
|
|
387
|
-
|
|
388
|
-
it('should have signal-based inputs', () => {
|
|
389
|
-
expect(typeof directive.lmVariant).toBe('function');
|
|
390
|
-
expect(typeof directive.lmSize).toBe('function');
|
|
391
|
-
expect(typeof directive.lmDisabled).toBe('function');
|
|
392
|
-
expect(typeof directive.lmType).toBe('function');
|
|
393
|
-
});
|
|
394
|
-
|
|
395
|
-
it('should have computed classes signal', () => {
|
|
396
|
-
expect(typeof directive.classes).toBe('function');
|
|
397
|
-
expect(typeof directive.classes()).toBe('string');
|
|
398
|
-
});
|
|
399
|
-
|
|
400
|
-
it('should apply default variant (primary) when not specified', () => {
|
|
401
|
-
fixture.detectChanges();
|
|
402
|
-
expect(directive.lmVariant()).toBe('primary');
|
|
403
|
-
});
|
|
404
|
-
|
|
405
|
-
it('should apply default size (md) when not specified', () => {
|
|
406
|
-
fixture.detectChanges();
|
|
407
|
-
expect(directive.lmSize()).toBe('md');
|
|
408
|
-
});
|
|
409
|
-
|
|
410
|
-
it('should set type attribute to button by default', () => {
|
|
411
|
-
fixture.detectChanges();
|
|
412
|
-
expect(buttonElement.nativeElement.getAttribute('type')).toBe('button');
|
|
413
|
-
});
|
|
414
|
-
|
|
415
|
-
it('should maintain consistent class generation across multiple calls', () => {
|
|
416
|
-
fixture.detectChanges();
|
|
417
|
-
const classes1 = directive.classes();
|
|
418
|
-
const classes2 = directive.classes();
|
|
419
|
-
expect(classes1).toBe(classes2);
|
|
420
|
-
});
|
|
421
|
-
});
|
|
422
|
-
|
|
423
|
-
// ============================================================
|
|
424
|
-
// DESIGN TOKEN DEFINITION TESTS
|
|
425
|
-
// ============================================================
|
|
426
|
-
|
|
427
|
-
describe('Design Token Definition', () => {
|
|
428
|
-
describe('Primary Variant Tokens', () => {
|
|
429
|
-
it('should define --luma-button-primary-bg css variable', () => {
|
|
430
|
-
const value = getComputedStyle(document.documentElement)
|
|
431
|
-
.getPropertyValue('--luma-button-primary-bg')
|
|
432
|
-
.trim();
|
|
433
|
-
expect(value).toBe(BUTTON_TOKENS.primary.bg);
|
|
434
|
-
});
|
|
435
|
-
|
|
436
|
-
it('should define --luma-button-primary-bg-hover css variable', () => {
|
|
437
|
-
const value = getComputedStyle(document.documentElement)
|
|
438
|
-
.getPropertyValue('--luma-button-primary-bg-hover')
|
|
439
|
-
.trim();
|
|
440
|
-
expect(value).toBe(BUTTON_TOKENS.primary.bgHover);
|
|
441
|
-
});
|
|
442
|
-
|
|
443
|
-
it('should define --luma-button-primary-bg-active css variable', () => {
|
|
444
|
-
const value = getComputedStyle(document.documentElement)
|
|
445
|
-
.getPropertyValue('--luma-button-primary-bg-active')
|
|
446
|
-
.trim();
|
|
447
|
-
expect(value).toBe(BUTTON_TOKENS.primary.bgActive);
|
|
448
|
-
});
|
|
449
|
-
|
|
450
|
-
it('should define --luma-button-primary-text css variable', () => {
|
|
451
|
-
const value = getComputedStyle(document.documentElement)
|
|
452
|
-
.getPropertyValue('--luma-button-primary-text')
|
|
453
|
-
.trim();
|
|
454
|
-
expect(value).toBe(BUTTON_TOKENS.primary.text);
|
|
455
|
-
});
|
|
456
|
-
});
|
|
457
|
-
|
|
458
|
-
describe('Outline Variant Tokens', () => {
|
|
459
|
-
it('should define --luma-button-outline-border css variable', () => {
|
|
460
|
-
const value = getComputedStyle(document.documentElement)
|
|
461
|
-
.getPropertyValue('--luma-button-outline-border')
|
|
462
|
-
.trim();
|
|
463
|
-
expect(value).toBe(BUTTON_TOKENS.outline.border);
|
|
464
|
-
});
|
|
465
|
-
|
|
466
|
-
it('should define --luma-button-outline-border-hover css variable', () => {
|
|
467
|
-
const value = getComputedStyle(document.documentElement)
|
|
468
|
-
.getPropertyValue('--luma-button-outline-border-hover')
|
|
469
|
-
.trim();
|
|
470
|
-
expect(value).toBe(BUTTON_TOKENS.outline.borderHover);
|
|
471
|
-
});
|
|
472
|
-
|
|
473
|
-
it('should define --luma-button-outline-bg-hover css variable', () => {
|
|
474
|
-
const value = getComputedStyle(document.documentElement)
|
|
475
|
-
.getPropertyValue('--luma-button-outline-bg-hover')
|
|
476
|
-
.trim();
|
|
477
|
-
expect(value).toBe(BUTTON_TOKENS.outline.bgHover);
|
|
478
|
-
});
|
|
479
|
-
|
|
480
|
-
it('should define --luma-button-outline-text css variable', () => {
|
|
481
|
-
const value = getComputedStyle(document.documentElement)
|
|
482
|
-
.getPropertyValue('--luma-button-outline-text')
|
|
483
|
-
.trim();
|
|
484
|
-
expect(value).toBe(BUTTON_TOKENS.outline.text);
|
|
485
|
-
});
|
|
486
|
-
});
|
|
487
|
-
|
|
488
|
-
describe('Ghost Variant Tokens', () => {
|
|
489
|
-
it('should define --luma-button-ghost-bg css variable', () => {
|
|
490
|
-
const value = getComputedStyle(document.documentElement)
|
|
491
|
-
.getPropertyValue('--luma-button-ghost-bg')
|
|
492
|
-
.trim();
|
|
493
|
-
expect(value).toBe(BUTTON_TOKENS.ghost.bg);
|
|
494
|
-
});
|
|
495
|
-
|
|
496
|
-
it('should define --luma-button-ghost-bg-hover css variable', () => {
|
|
497
|
-
const value = getComputedStyle(document.documentElement)
|
|
498
|
-
.getPropertyValue('--luma-button-ghost-bg-hover')
|
|
499
|
-
.trim();
|
|
500
|
-
expect(value).toBe(BUTTON_TOKENS.ghost.bgHover);
|
|
501
|
-
});
|
|
502
|
-
|
|
503
|
-
it('should define --luma-button-ghost-text css variable', () => {
|
|
504
|
-
const value = getComputedStyle(document.documentElement)
|
|
505
|
-
.getPropertyValue('--luma-button-ghost-text')
|
|
506
|
-
.trim();
|
|
507
|
-
expect(value).toBe(BUTTON_TOKENS.ghost.text);
|
|
508
|
-
});
|
|
509
|
-
});
|
|
510
|
-
|
|
511
|
-
describe('Danger Variant Tokens', () => {
|
|
512
|
-
it('should define --luma-button-danger-bg css variable', () => {
|
|
513
|
-
const value = getComputedStyle(document.documentElement)
|
|
514
|
-
.getPropertyValue('--luma-button-danger-bg')
|
|
515
|
-
.trim();
|
|
516
|
-
expect(value).toBe(BUTTON_TOKENS.danger.bg);
|
|
517
|
-
});
|
|
518
|
-
|
|
519
|
-
it('should define --luma-button-danger-bg-hover css variable', () => {
|
|
520
|
-
const value = getComputedStyle(document.documentElement)
|
|
521
|
-
.getPropertyValue('--luma-button-danger-bg-hover')
|
|
522
|
-
.trim();
|
|
523
|
-
expect(value).toBe(BUTTON_TOKENS.danger.bgHover);
|
|
524
|
-
});
|
|
525
|
-
|
|
526
|
-
it('should define --luma-button-danger-bg-active css variable', () => {
|
|
527
|
-
const value = getComputedStyle(document.documentElement)
|
|
528
|
-
.getPropertyValue('--luma-button-danger-bg-active')
|
|
529
|
-
.trim();
|
|
530
|
-
expect(value).toBe(BUTTON_TOKENS.danger.bgActive);
|
|
531
|
-
});
|
|
532
|
-
|
|
533
|
-
it('should define --luma-button-danger-text css variable', () => {
|
|
534
|
-
const value = getComputedStyle(document.documentElement)
|
|
535
|
-
.getPropertyValue('--luma-button-danger-text')
|
|
536
|
-
.trim();
|
|
537
|
-
expect(value).toBe(BUTTON_TOKENS.danger.text);
|
|
538
|
-
});
|
|
539
|
-
});
|
|
540
|
-
|
|
541
|
-
describe('Dimension Tokens', () => {
|
|
542
|
-
it('should define --luma-button-padding-x-sm css variable', () => {
|
|
543
|
-
const value = getComputedStyle(document.documentElement)
|
|
544
|
-
.getPropertyValue('--luma-button-padding-x-sm')
|
|
545
|
-
.trim();
|
|
546
|
-
expect(value).toBe(BUTTON_TOKENS.padding.xSm);
|
|
547
|
-
});
|
|
548
|
-
|
|
549
|
-
it('should define --luma-button-padding-x-md css variable', () => {
|
|
550
|
-
const value = getComputedStyle(document.documentElement)
|
|
551
|
-
.getPropertyValue('--luma-button-padding-x-md')
|
|
552
|
-
.trim();
|
|
553
|
-
expect(value).toBe(BUTTON_TOKENS.padding.xMd);
|
|
554
|
-
});
|
|
555
|
-
|
|
556
|
-
it('should define --luma-button-padding-x-lg css variable', () => {
|
|
557
|
-
const value = getComputedStyle(document.documentElement)
|
|
558
|
-
.getPropertyValue('--luma-button-padding-x-lg')
|
|
559
|
-
.trim();
|
|
560
|
-
expect(value).toBe(BUTTON_TOKENS.padding.xLg);
|
|
561
|
-
});
|
|
562
|
-
|
|
563
|
-
it('should define --luma-button-padding-y-sm css variable', () => {
|
|
564
|
-
const value = getComputedStyle(document.documentElement)
|
|
565
|
-
.getPropertyValue('--luma-button-padding-y-sm')
|
|
566
|
-
.trim();
|
|
567
|
-
expect(value).toBe(BUTTON_TOKENS.padding.ySm);
|
|
568
|
-
});
|
|
569
|
-
|
|
570
|
-
it('should define --luma-button-padding-y-md css variable', () => {
|
|
571
|
-
const value = getComputedStyle(document.documentElement)
|
|
572
|
-
.getPropertyValue('--luma-button-padding-y-md')
|
|
573
|
-
.trim();
|
|
574
|
-
expect(value).toBe(BUTTON_TOKENS.padding.yMd);
|
|
575
|
-
});
|
|
576
|
-
|
|
577
|
-
it('should define --luma-button-padding-y-lg css variable', () => {
|
|
578
|
-
const value = getComputedStyle(document.documentElement)
|
|
579
|
-
.getPropertyValue('--luma-button-padding-y-lg')
|
|
580
|
-
.trim();
|
|
581
|
-
expect(value).toBe(BUTTON_TOKENS.padding.yLg);
|
|
582
|
-
});
|
|
583
|
-
|
|
584
|
-
it('should define --luma-button-radius css variable', () => {
|
|
585
|
-
const value = getComputedStyle(document.documentElement)
|
|
586
|
-
.getPropertyValue('--luma-button-radius')
|
|
587
|
-
.trim();
|
|
588
|
-
expect(value).toBe(BUTTON_TOKENS.radius);
|
|
589
|
-
});
|
|
590
|
-
});
|
|
591
|
-
|
|
592
|
-
describe('Focus Tokens', () => {
|
|
593
|
-
it('should define --luma-button-focus-ring-width css variable', () => {
|
|
594
|
-
const value = getComputedStyle(document.documentElement)
|
|
595
|
-
.getPropertyValue('--luma-button-focus-ring-width')
|
|
596
|
-
.trim();
|
|
597
|
-
expect(value).toBe(BUTTON_TOKENS.focus.ringWidth);
|
|
598
|
-
});
|
|
599
|
-
|
|
600
|
-
it('should define --luma-button-focus-ring-color css variable', () => {
|
|
601
|
-
const value = getComputedStyle(document.documentElement)
|
|
602
|
-
.getPropertyValue('--luma-button-focus-ring-color')
|
|
603
|
-
.trim();
|
|
604
|
-
expect(value).toBe(BUTTON_TOKENS.focus.ringColor);
|
|
605
|
-
});
|
|
606
|
-
});
|
|
607
|
-
|
|
608
|
-
describe('Transition Tokens', () => {
|
|
609
|
-
it('should define --luma-button-transition-duration css variable', () => {
|
|
610
|
-
const value = getComputedStyle(document.documentElement)
|
|
611
|
-
.getPropertyValue('--luma-button-transition-duration')
|
|
612
|
-
.trim();
|
|
613
|
-
expect(value).toBe(BUTTON_TOKENS.transition.duration);
|
|
614
|
-
});
|
|
615
|
-
|
|
616
|
-
it('should define --luma-button-transition-timing css variable', () => {
|
|
617
|
-
const value = getComputedStyle(document.documentElement)
|
|
618
|
-
.getPropertyValue('--luma-button-transition-timing')
|
|
619
|
-
.trim();
|
|
620
|
-
expect(value).toBe(BUTTON_TOKENS.transition.timing);
|
|
621
|
-
});
|
|
622
|
-
});
|
|
623
|
-
});
|
|
624
|
-
|
|
625
|
-
// ============================================================
|
|
626
|
-
// TOKEN CONSUMPTION TESTS
|
|
627
|
-
// ============================================================
|
|
628
|
-
// These tests verify that when a variant is selected, the corresponding
|
|
629
|
-
// tokens are accessible and the correct classes are applied.
|
|
630
|
-
// CSS variables are inherited from document.documentElement.
|
|
631
|
-
|
|
632
|
-
describe('Token Consumption', () => {
|
|
633
|
-
describe('Primary Variant', () => {
|
|
634
|
-
beforeEach(() => {
|
|
635
|
-
hostComponent.lmVariant = 'primary';
|
|
636
|
-
fixture.detectChanges();
|
|
637
|
-
});
|
|
638
|
-
|
|
639
|
-
it('should have access to --luma-button-primary-bg token', () => {
|
|
640
|
-
// Token is set on document root and inherited by all elements
|
|
641
|
-
const value = getComputedStyle(document.documentElement)
|
|
642
|
-
.getPropertyValue('--luma-button-primary-bg')
|
|
643
|
-
.trim();
|
|
644
|
-
expect(value).toBe(BUTTON_TOKENS.primary.bg);
|
|
645
|
-
});
|
|
646
|
-
|
|
647
|
-
it('should have access to --luma-button-primary-text token', () => {
|
|
648
|
-
const value = getComputedStyle(document.documentElement)
|
|
649
|
-
.getPropertyValue('--luma-button-primary-text')
|
|
650
|
-
.trim();
|
|
651
|
-
expect(value).toBe(BUTTON_TOKENS.primary.text);
|
|
652
|
-
});
|
|
653
|
-
|
|
654
|
-
it('should have access to --luma-button-primary-bg-hover token', () => {
|
|
655
|
-
const value = getComputedStyle(document.documentElement)
|
|
656
|
-
.getPropertyValue('--luma-button-primary-bg-hover')
|
|
657
|
-
.trim();
|
|
658
|
-
expect(value).toBe(BUTTON_TOKENS.primary.bgHover);
|
|
659
|
-
});
|
|
660
|
-
});
|
|
661
|
-
|
|
662
|
-
describe('Outline Variant', () => {
|
|
663
|
-
beforeEach(() => {
|
|
664
|
-
hostComponent.lmVariant = 'outline';
|
|
665
|
-
fixture.detectChanges();
|
|
666
|
-
});
|
|
667
|
-
|
|
668
|
-
it('should have access to --luma-button-outline-border token', () => {
|
|
669
|
-
const value = getComputedStyle(document.documentElement)
|
|
670
|
-
.getPropertyValue('--luma-button-outline-border')
|
|
671
|
-
.trim();
|
|
672
|
-
expect(value).toBe(BUTTON_TOKENS.outline.border);
|
|
673
|
-
});
|
|
674
|
-
|
|
675
|
-
it('should have access to --luma-button-outline-text token', () => {
|
|
676
|
-
const value = getComputedStyle(document.documentElement)
|
|
677
|
-
.getPropertyValue('--luma-button-outline-text')
|
|
678
|
-
.trim();
|
|
679
|
-
expect(value).toBe(BUTTON_TOKENS.outline.text);
|
|
680
|
-
});
|
|
681
|
-
|
|
682
|
-
it('should have access to --luma-button-outline-bg-hover token', () => {
|
|
683
|
-
const value = getComputedStyle(document.documentElement)
|
|
684
|
-
.getPropertyValue('--luma-button-outline-bg-hover')
|
|
685
|
-
.trim();
|
|
686
|
-
expect(value).toBe(BUTTON_TOKENS.outline.bgHover);
|
|
687
|
-
});
|
|
688
|
-
});
|
|
689
|
-
|
|
690
|
-
describe('Ghost Variant', () => {
|
|
691
|
-
beforeEach(() => {
|
|
692
|
-
hostComponent.lmVariant = 'ghost';
|
|
693
|
-
fixture.detectChanges();
|
|
694
|
-
});
|
|
695
|
-
|
|
696
|
-
it('should have access to --luma-button-ghost-bg token', () => {
|
|
697
|
-
const value = getComputedStyle(document.documentElement)
|
|
698
|
-
.getPropertyValue('--luma-button-ghost-bg')
|
|
699
|
-
.trim();
|
|
700
|
-
expect(value).toBe(BUTTON_TOKENS.ghost.bg);
|
|
701
|
-
});
|
|
702
|
-
|
|
703
|
-
it('should have access to --luma-button-ghost-text token', () => {
|
|
704
|
-
const value = getComputedStyle(document.documentElement)
|
|
705
|
-
.getPropertyValue('--luma-button-ghost-text')
|
|
706
|
-
.trim();
|
|
707
|
-
expect(value).toBe(BUTTON_TOKENS.ghost.text);
|
|
708
|
-
});
|
|
709
|
-
|
|
710
|
-
it('should have access to --luma-button-ghost-bg-hover token', () => {
|
|
711
|
-
const value = getComputedStyle(document.documentElement)
|
|
712
|
-
.getPropertyValue('--luma-button-ghost-bg-hover')
|
|
713
|
-
.trim();
|
|
714
|
-
expect(value).toBe(BUTTON_TOKENS.ghost.bgHover);
|
|
715
|
-
});
|
|
716
|
-
});
|
|
717
|
-
|
|
718
|
-
describe('Danger Variant', () => {
|
|
719
|
-
beforeEach(() => {
|
|
720
|
-
hostComponent.lmVariant = 'danger';
|
|
721
|
-
fixture.detectChanges();
|
|
722
|
-
});
|
|
723
|
-
|
|
724
|
-
it('should have access to --luma-button-danger-bg token', () => {
|
|
725
|
-
const value = getComputedStyle(document.documentElement)
|
|
726
|
-
.getPropertyValue('--luma-button-danger-bg')
|
|
727
|
-
.trim();
|
|
728
|
-
expect(value).toBe(BUTTON_TOKENS.danger.bg);
|
|
729
|
-
});
|
|
730
|
-
|
|
731
|
-
it('should have access to --luma-button-danger-text token', () => {
|
|
732
|
-
const value = getComputedStyle(document.documentElement)
|
|
733
|
-
.getPropertyValue('--luma-button-danger-text')
|
|
734
|
-
.trim();
|
|
735
|
-
expect(value).toBe(BUTTON_TOKENS.danger.text);
|
|
736
|
-
});
|
|
737
|
-
|
|
738
|
-
it('should have access to --luma-button-danger-bg-hover token', () => {
|
|
739
|
-
const value = getComputedStyle(document.documentElement)
|
|
740
|
-
.getPropertyValue('--luma-button-danger-bg-hover')
|
|
741
|
-
.trim();
|
|
742
|
-
expect(value).toBe(BUTTON_TOKENS.danger.bgHover);
|
|
743
|
-
});
|
|
744
|
-
});
|
|
745
|
-
|
|
746
|
-
describe('Dimension Tokens', () => {
|
|
747
|
-
it('should have access to --luma-button-padding-x-md token', () => {
|
|
748
|
-
hostComponent.lmSize = 'md';
|
|
749
|
-
fixture.detectChanges();
|
|
750
|
-
|
|
751
|
-
const value = getComputedStyle(document.documentElement)
|
|
752
|
-
.getPropertyValue('--luma-button-padding-x-md')
|
|
753
|
-
.trim();
|
|
754
|
-
expect(value).toBe(BUTTON_TOKENS.padding.xMd);
|
|
755
|
-
});
|
|
756
|
-
|
|
757
|
-
it('should have access to --luma-button-padding-y-md token', () => {
|
|
758
|
-
hostComponent.lmSize = 'md';
|
|
759
|
-
fixture.detectChanges();
|
|
760
|
-
|
|
761
|
-
const value = getComputedStyle(document.documentElement)
|
|
762
|
-
.getPropertyValue('--luma-button-padding-y-md')
|
|
763
|
-
.trim();
|
|
764
|
-
expect(value).toBe(BUTTON_TOKENS.padding.yMd);
|
|
765
|
-
});
|
|
766
|
-
|
|
767
|
-
it('should have access to --luma-button-radius token', () => {
|
|
768
|
-
fixture.detectChanges();
|
|
769
|
-
|
|
770
|
-
const value = getComputedStyle(document.documentElement)
|
|
771
|
-
.getPropertyValue('--luma-button-radius')
|
|
772
|
-
.trim();
|
|
773
|
-
expect(value).toBe(BUTTON_TOKENS.radius);
|
|
774
|
-
});
|
|
775
|
-
});
|
|
776
|
-
});
|
|
777
|
-
|
|
778
|
-
// ============================================================
|
|
779
|
-
// TOKEN OVERRIDE TESTS
|
|
780
|
-
// ============================================================
|
|
781
|
-
|
|
782
|
-
describe('Token Override', () => {
|
|
783
|
-
it('should respect custom radius token override', () => {
|
|
784
|
-
const customRadius = '20px';
|
|
785
|
-
document.documentElement.style.setProperty(
|
|
786
|
-
'--luma-button-radius',
|
|
787
|
-
customRadius,
|
|
788
|
-
);
|
|
789
|
-
fixture.detectChanges();
|
|
790
|
-
|
|
791
|
-
const value = getComputedStyle(document.documentElement)
|
|
792
|
-
.getPropertyValue('--luma-button-radius')
|
|
793
|
-
.trim();
|
|
794
|
-
expect(value).toBe(customRadius);
|
|
795
|
-
});
|
|
796
|
-
|
|
797
|
-
it('should respect custom padding-x override', () => {
|
|
798
|
-
const customPadding = '3rem';
|
|
799
|
-
document.documentElement.style.setProperty(
|
|
800
|
-
'--luma-button-padding-x-md',
|
|
801
|
-
customPadding,
|
|
802
|
-
);
|
|
803
|
-
fixture.detectChanges();
|
|
804
|
-
|
|
805
|
-
const value = getComputedStyle(document.documentElement)
|
|
806
|
-
.getPropertyValue('--luma-button-padding-x-md')
|
|
807
|
-
.trim();
|
|
808
|
-
expect(value).toBe(customPadding);
|
|
809
|
-
});
|
|
810
|
-
|
|
811
|
-
it('should respect custom padding-y override', () => {
|
|
812
|
-
const customPadding = '1.5rem';
|
|
813
|
-
document.documentElement.style.setProperty(
|
|
814
|
-
'--luma-button-padding-y-md',
|
|
815
|
-
customPadding,
|
|
816
|
-
);
|
|
817
|
-
fixture.detectChanges();
|
|
818
|
-
|
|
819
|
-
const value = getComputedStyle(document.documentElement)
|
|
820
|
-
.getPropertyValue('--luma-button-padding-y-md')
|
|
821
|
-
.trim();
|
|
822
|
-
expect(value).toBe(customPadding);
|
|
823
|
-
});
|
|
824
|
-
|
|
825
|
-
it('should respect custom primary background override', () => {
|
|
826
|
-
const customColor = 'oklch(0.6 0.15 250)';
|
|
827
|
-
document.documentElement.style.setProperty(
|
|
828
|
-
'--luma-button-primary-bg',
|
|
829
|
-
customColor,
|
|
830
|
-
);
|
|
831
|
-
hostComponent.lmVariant = 'primary';
|
|
832
|
-
fixture.detectChanges();
|
|
833
|
-
|
|
834
|
-
const value = getComputedStyle(document.documentElement)
|
|
835
|
-
.getPropertyValue('--luma-button-primary-bg')
|
|
836
|
-
.trim();
|
|
837
|
-
expect(value).toBe(customColor);
|
|
838
|
-
});
|
|
839
|
-
|
|
840
|
-
it('should respect custom focus ring width override', () => {
|
|
841
|
-
const customWidth = '4px';
|
|
842
|
-
document.documentElement.style.setProperty(
|
|
843
|
-
'--luma-button-focus-ring-width',
|
|
844
|
-
customWidth,
|
|
845
|
-
);
|
|
846
|
-
fixture.detectChanges();
|
|
847
|
-
|
|
848
|
-
const value = getComputedStyle(document.documentElement)
|
|
849
|
-
.getPropertyValue('--luma-button-focus-ring-width')
|
|
850
|
-
.trim();
|
|
851
|
-
expect(value).toBe(customWidth);
|
|
852
|
-
});
|
|
853
|
-
|
|
854
|
-
it('should respect custom transition duration override', () => {
|
|
855
|
-
const customDuration = '300ms';
|
|
856
|
-
document.documentElement.style.setProperty(
|
|
857
|
-
'--luma-button-transition-duration',
|
|
858
|
-
customDuration,
|
|
859
|
-
);
|
|
860
|
-
fixture.detectChanges();
|
|
861
|
-
|
|
862
|
-
const value = getComputedStyle(document.documentElement)
|
|
863
|
-
.getPropertyValue('--luma-button-transition-duration')
|
|
864
|
-
.trim();
|
|
865
|
-
expect(value).toBe(customDuration);
|
|
866
|
-
});
|
|
867
|
-
});
|
|
868
|
-
|
|
869
|
-
// ============================================================
|
|
870
|
-
// CLASS APPLICATION TESTS
|
|
871
|
-
// ============================================================
|
|
872
|
-
|
|
873
|
-
describe('Class Application', () => {
|
|
874
|
-
describe('Base Classes', () => {
|
|
875
|
-
beforeEach(() => {
|
|
876
|
-
fixture.detectChanges();
|
|
877
|
-
});
|
|
878
|
-
|
|
879
|
-
it('should apply inline-flex for layout', () => {
|
|
880
|
-
expect(directive.classes()).toContain('inline-flex');
|
|
881
|
-
});
|
|
882
|
-
|
|
883
|
-
it('should apply items-center for vertical alignment', () => {
|
|
884
|
-
expect(directive.classes()).toContain('items-center');
|
|
885
|
-
});
|
|
886
|
-
|
|
887
|
-
it('should apply justify-center for horizontal alignment', () => {
|
|
888
|
-
expect(directive.classes()).toContain('justify-center');
|
|
889
|
-
});
|
|
890
|
-
|
|
891
|
-
it('should apply font-medium for typography', () => {
|
|
892
|
-
expect(directive.classes()).toContain('font-medium');
|
|
893
|
-
});
|
|
894
|
-
|
|
895
|
-
it('should apply leading-snug for line height', () => {
|
|
896
|
-
expect(directive.classes()).toContain('leading-snug');
|
|
897
|
-
});
|
|
898
|
-
|
|
899
|
-
it('should remove default focus outline', () => {
|
|
900
|
-
expect(directive.classes()).toContain('focus:outline-none');
|
|
901
|
-
});
|
|
902
|
-
|
|
903
|
-
it('should apply focus-visible ring class', () => {
|
|
904
|
-
expect(directive.classes()).toContain(
|
|
905
|
-
'focus-visible:lm-ring-button-focus',
|
|
906
|
-
);
|
|
907
|
-
});
|
|
908
|
-
|
|
909
|
-
it('should apply transition class with CSS variables', () => {
|
|
910
|
-
expect(directive.classes()).toContain(
|
|
911
|
-
'transition-[color_var(--luma-button-transition-duration)_var(--luma-button-transition-timing)]',
|
|
912
|
-
);
|
|
913
|
-
});
|
|
914
|
-
});
|
|
915
|
-
|
|
916
|
-
describe('Primary Variant Classes', () => {
|
|
917
|
-
beforeEach(() => {
|
|
918
|
-
hostComponent.lmVariant = 'primary';
|
|
919
|
-
fixture.detectChanges();
|
|
920
|
-
});
|
|
921
|
-
|
|
922
|
-
it('should apply primary background class', () => {
|
|
923
|
-
expect(directive.classes()).toContain('lm-bg-button-primary-bg');
|
|
924
|
-
});
|
|
925
|
-
|
|
926
|
-
it('should apply primary text class', () => {
|
|
927
|
-
expect(directive.classes()).toContain('lm-text-button-primary-text');
|
|
928
|
-
});
|
|
929
|
-
|
|
930
|
-
it('should apply primary hover background class', () => {
|
|
931
|
-
expect(directive.classes()).toContain(
|
|
932
|
-
'hover:lm-bg-button-primary-bg-hover',
|
|
933
|
-
);
|
|
934
|
-
});
|
|
935
|
-
|
|
936
|
-
it('should apply primary active background class', () => {
|
|
937
|
-
expect(directive.classes()).toContain(
|
|
938
|
-
'active:lm-bg-button-primary-bg-active',
|
|
939
|
-
);
|
|
940
|
-
});
|
|
941
|
-
});
|
|
942
|
-
|
|
943
|
-
describe('Outline Variant Classes', () => {
|
|
944
|
-
beforeEach(() => {
|
|
945
|
-
hostComponent.lmVariant = 'outline';
|
|
946
|
-
fixture.detectChanges();
|
|
947
|
-
});
|
|
948
|
-
|
|
949
|
-
it('should apply transparent background', () => {
|
|
950
|
-
expect(directive.classes()).toContain('bg-transparent');
|
|
951
|
-
});
|
|
952
|
-
|
|
953
|
-
it('should apply outline text class', () => {
|
|
954
|
-
expect(directive.classes()).toContain('lm-text-button-outline-text');
|
|
955
|
-
});
|
|
956
|
-
|
|
957
|
-
it('should apply border class', () => {
|
|
958
|
-
expect(directive.classes()).toContain('border');
|
|
959
|
-
});
|
|
960
|
-
|
|
961
|
-
it('should apply outline border class', () => {
|
|
962
|
-
expect(directive.classes()).toContain(
|
|
963
|
-
'lm-border-button-outline-border',
|
|
964
|
-
);
|
|
965
|
-
});
|
|
966
|
-
|
|
967
|
-
it('should apply border hover class', () => {
|
|
968
|
-
expect(directive.classes()).toContain(
|
|
969
|
-
'hover:lm-border-button-outline-border-hover',
|
|
970
|
-
);
|
|
971
|
-
});
|
|
972
|
-
|
|
973
|
-
it('should apply background hover class', () => {
|
|
974
|
-
expect(directive.classes()).toContain(
|
|
975
|
-
'hover:lm-bg-button-outline-bg-hover',
|
|
976
|
-
);
|
|
977
|
-
});
|
|
978
|
-
});
|
|
979
|
-
|
|
980
|
-
describe('Ghost Variant Classes', () => {
|
|
981
|
-
beforeEach(() => {
|
|
982
|
-
hostComponent.lmVariant = 'ghost';
|
|
983
|
-
fixture.detectChanges();
|
|
984
|
-
});
|
|
985
|
-
|
|
986
|
-
it('should apply ghost background class', () => {
|
|
987
|
-
expect(directive.classes()).toContain('lm-bg-button-ghost-bg');
|
|
988
|
-
});
|
|
989
|
-
|
|
990
|
-
it('should apply ghost text class', () => {
|
|
991
|
-
expect(directive.classes()).toContain('lm-text-button-ghost-text');
|
|
992
|
-
});
|
|
993
|
-
|
|
994
|
-
it('should apply ghost hover background class', () => {
|
|
995
|
-
expect(directive.classes()).toContain(
|
|
996
|
-
'hover:lm-bg-button-ghost-bg-hover',
|
|
997
|
-
);
|
|
998
|
-
});
|
|
999
|
-
});
|
|
1000
|
-
|
|
1001
|
-
describe('Danger Variant Classes', () => {
|
|
1002
|
-
beforeEach(() => {
|
|
1003
|
-
hostComponent.lmVariant = 'danger';
|
|
1004
|
-
fixture.detectChanges();
|
|
1005
|
-
});
|
|
1006
|
-
|
|
1007
|
-
it('should apply danger background class', () => {
|
|
1008
|
-
expect(directive.classes()).toContain('lm-bg-button-danger-bg');
|
|
1009
|
-
});
|
|
1010
|
-
|
|
1011
|
-
it('should apply danger text class', () => {
|
|
1012
|
-
expect(directive.classes()).toContain('lm-text-button-danger-text');
|
|
1013
|
-
});
|
|
1014
|
-
|
|
1015
|
-
it('should apply danger hover background class', () => {
|
|
1016
|
-
expect(directive.classes()).toContain(
|
|
1017
|
-
'hover:lm-bg-button-danger-bg-hover',
|
|
1018
|
-
);
|
|
1019
|
-
});
|
|
1020
|
-
|
|
1021
|
-
it('should apply danger active background class', () => {
|
|
1022
|
-
expect(directive.classes()).toContain(
|
|
1023
|
-
'active:lm-bg-button-danger-bg-active',
|
|
1024
|
-
);
|
|
1025
|
-
});
|
|
1026
|
-
});
|
|
1027
|
-
|
|
1028
|
-
describe('Size Classes', () => {
|
|
1029
|
-
it('should apply small size classes', () => {
|
|
1030
|
-
hostComponent.lmSize = 'sm';
|
|
1031
|
-
fixture.detectChanges();
|
|
1032
|
-
|
|
1033
|
-
expect(directive.classes()).toContain(
|
|
1034
|
-
'px-[var(--luma-button-padding-x-sm)]',
|
|
1035
|
-
);
|
|
1036
|
-
expect(directive.classes()).toContain(
|
|
1037
|
-
'py-[var(--luma-button-padding-y-sm)]',
|
|
1038
|
-
);
|
|
1039
|
-
expect(directive.classes()).toContain('text-sm');
|
|
1040
|
-
expect(directive.classes()).toContain('lm-rounded-button');
|
|
1041
|
-
});
|
|
1042
|
-
|
|
1043
|
-
it('should apply medium size classes', () => {
|
|
1044
|
-
hostComponent.lmSize = 'md';
|
|
1045
|
-
fixture.detectChanges();
|
|
1046
|
-
|
|
1047
|
-
expect(directive.classes()).toContain(
|
|
1048
|
-
'px-[var(--luma-button-padding-x-md)]',
|
|
1049
|
-
);
|
|
1050
|
-
expect(directive.classes()).toContain(
|
|
1051
|
-
'py-[var(--luma-button-padding-y-md)]',
|
|
1052
|
-
);
|
|
1053
|
-
expect(directive.classes()).toContain('lm-text-size-base');
|
|
1054
|
-
expect(directive.classes()).toContain('lm-rounded-button');
|
|
1055
|
-
});
|
|
1056
|
-
|
|
1057
|
-
it('should apply large size classes', () => {
|
|
1058
|
-
hostComponent.lmSize = 'lg';
|
|
1059
|
-
fixture.detectChanges();
|
|
1060
|
-
|
|
1061
|
-
expect(directive.classes()).toContain(
|
|
1062
|
-
'px-[var(--luma-button-padding-x-lg)]',
|
|
1063
|
-
);
|
|
1064
|
-
expect(directive.classes()).toContain(
|
|
1065
|
-
'py-[var(--luma-button-padding-y-lg)]',
|
|
1066
|
-
);
|
|
1067
|
-
expect(directive.classes()).toContain('text-lg');
|
|
1068
|
-
expect(directive.classes()).toContain('lm-rounded-button');
|
|
1069
|
-
});
|
|
1070
|
-
|
|
1071
|
-
it('should apply full size classes', () => {
|
|
1072
|
-
hostComponent.lmSize = 'full';
|
|
1073
|
-
fixture.detectChanges();
|
|
1074
|
-
|
|
1075
|
-
expect(directive.classes()).toContain('w-full');
|
|
1076
|
-
});
|
|
1077
|
-
|
|
1078
|
-
it('should apply compound variant for full size with primary', () => {
|
|
1079
|
-
hostComponent.lmSize = 'full';
|
|
1080
|
-
hostComponent.lmVariant = 'primary';
|
|
1081
|
-
fixture.detectChanges();
|
|
1082
|
-
|
|
1083
|
-
expect(directive.classes()).toContain(
|
|
1084
|
-
'px-[var(--luma-button-padding-x-md)]',
|
|
1085
|
-
);
|
|
1086
|
-
expect(directive.classes()).toContain(
|
|
1087
|
-
'py-[var(--luma-button-padding-y-md)]',
|
|
1088
|
-
);
|
|
1089
|
-
});
|
|
1090
|
-
});
|
|
1091
|
-
});
|
|
1092
|
-
|
|
1093
|
-
// ============================================================
|
|
1094
|
-
// DISABLED STATE TESTS
|
|
1095
|
-
// ============================================================
|
|
1096
|
-
|
|
1097
|
-
describe('Disabled State', () => {
|
|
1098
|
-
describe('when disabled', () => {
|
|
1099
|
-
beforeEach(() => {
|
|
1100
|
-
hostComponent.lmDisabled = true;
|
|
1101
|
-
fixture.detectChanges();
|
|
1102
|
-
});
|
|
1103
|
-
|
|
1104
|
-
it('should apply disabled opacity class', () => {
|
|
1105
|
-
expect(directive.classes()).toContain('disabled:opacity-50');
|
|
1106
|
-
});
|
|
1107
|
-
|
|
1108
|
-
it('should apply disabled cursor class', () => {
|
|
1109
|
-
expect(directive.classes()).toContain('disabled:cursor-not-allowed');
|
|
1110
|
-
});
|
|
1111
|
-
|
|
1112
|
-
it('should set disabled attribute on element', () => {
|
|
1113
|
-
expect(buttonElement.nativeElement.hasAttribute('disabled')).toBe(true);
|
|
1114
|
-
});
|
|
1115
|
-
|
|
1116
|
-
it('should reflect disabled input signal', () => {
|
|
1117
|
-
expect(directive.lmDisabled()).toBe(true);
|
|
1118
|
-
});
|
|
1119
|
-
});
|
|
1120
|
-
|
|
1121
|
-
describe('when enabled', () => {
|
|
1122
|
-
beforeEach(() => {
|
|
1123
|
-
hostComponent.lmDisabled = false;
|
|
1124
|
-
fixture.detectChanges();
|
|
1125
|
-
});
|
|
1126
|
-
|
|
1127
|
-
it('should not have disabled attribute', () => {
|
|
1128
|
-
expect(buttonElement.nativeElement.hasAttribute('disabled')).toBe(
|
|
1129
|
-
false,
|
|
1130
|
-
);
|
|
1131
|
-
});
|
|
1132
|
-
|
|
1133
|
-
it('should reflect enabled state in signal', () => {
|
|
1134
|
-
expect(directive.lmDisabled()).toBe(false);
|
|
1135
|
-
});
|
|
1136
|
-
});
|
|
1137
|
-
});
|
|
1138
|
-
|
|
1139
|
-
// ============================================================
|
|
1140
|
-
// ACCESSIBILITY TESTS
|
|
1141
|
-
// ============================================================
|
|
1142
|
-
|
|
1143
|
-
describe('Accessibility', () => {
|
|
1144
|
-
it('should have button element by default', () => {
|
|
1145
|
-
fixture.detectChanges();
|
|
1146
|
-
expect(buttonElement.nativeElement.tagName).toBe('BUTTON');
|
|
1147
|
-
});
|
|
1148
|
-
|
|
1149
|
-
it('should apply focus-visible ring for keyboard navigation', () => {
|
|
1150
|
-
const classes = directive.classes();
|
|
1151
|
-
expect(classes).toContain('focus-visible:lm-ring-button-focus');
|
|
1152
|
-
});
|
|
1153
|
-
|
|
1154
|
-
it('should set type attribute correctly', () => {
|
|
1155
|
-
fixture.detectChanges();
|
|
1156
|
-
expect(buttonElement.nativeElement.getAttribute('type')).toBe('button');
|
|
1157
|
-
});
|
|
1158
|
-
|
|
1159
|
-
it('should allow submit type', () => {
|
|
1160
|
-
// Use dedicated test host to avoid ExpressionChangedAfterItHasBeenCheckedError
|
|
1161
|
-
const submitFixture = TestBed.createComponent(
|
|
1162
|
-
SubmitButtonTestHostComponent,
|
|
1163
|
-
);
|
|
1164
|
-
submitFixture.detectChanges();
|
|
1165
|
-
const submitButton = submitFixture.debugElement.query(
|
|
1166
|
-
By.directive(ButtonDirective),
|
|
1167
|
-
);
|
|
1168
|
-
expect(submitButton.nativeElement.getAttribute('type')).toBe('submit');
|
|
1169
|
-
});
|
|
1170
|
-
|
|
1171
|
-
it('should allow reset type', () => {
|
|
1172
|
-
// Use dedicated test host to avoid ExpressionChangedAfterItHasBeenCheckedError
|
|
1173
|
-
const resetFixture = TestBed.createComponent(
|
|
1174
|
-
ResetButtonTestHostComponent,
|
|
1175
|
-
);
|
|
1176
|
-
resetFixture.detectChanges();
|
|
1177
|
-
const resetButton = resetFixture.debugElement.query(
|
|
1178
|
-
By.directive(ButtonDirective),
|
|
1179
|
-
);
|
|
1180
|
-
expect(resetButton.nativeElement.getAttribute('type')).toBe('reset');
|
|
1181
|
-
});
|
|
1182
|
-
|
|
1183
|
-
it('should propagate disabled state to DOM', () => {
|
|
1184
|
-
hostComponent.lmDisabled = true;
|
|
1185
|
-
fixture.detectChanges();
|
|
1186
|
-
expect(buttonElement.nativeElement.hasAttribute('disabled')).toBe(true);
|
|
1187
|
-
});
|
|
1188
|
-
});
|
|
1189
|
-
|
|
1190
|
-
// ============================================================
|
|
1191
|
-
// INPUT REACTIVITY TESTS
|
|
1192
|
-
// ============================================================
|
|
1193
|
-
|
|
1194
|
-
describe('Input Reactivity', () => {
|
|
1195
|
-
it('should apply primary variant classes', () => {
|
|
1196
|
-
hostComponent.lmVariant = 'primary';
|
|
1197
|
-
fixture.detectChanges();
|
|
1198
|
-
expect(directive.classes()).toContain('lm-bg-button-primary');
|
|
1199
|
-
});
|
|
1200
|
-
|
|
1201
|
-
it('should apply outline variant classes', () => {
|
|
1202
|
-
hostComponent.lmVariant = 'outline';
|
|
1203
|
-
fixture.detectChanges();
|
|
1204
|
-
expect(directive.classes()).toContain('bg-transparent');
|
|
1205
|
-
expect(directive.classes()).toContain('lm-border-button-outline-border');
|
|
1206
|
-
});
|
|
1207
|
-
|
|
1208
|
-
it('should apply sm size classes', () => {
|
|
1209
|
-
hostComponent.lmSize = 'sm';
|
|
1210
|
-
fixture.detectChanges();
|
|
1211
|
-
expect(directive.classes()).toContain(
|
|
1212
|
-
'px-[var(--luma-button-padding-x-sm)]',
|
|
1213
|
-
);
|
|
1214
|
-
});
|
|
1215
|
-
|
|
1216
|
-
it('should apply lg size classes', () => {
|
|
1217
|
-
hostComponent.lmSize = 'lg';
|
|
1218
|
-
fixture.detectChanges();
|
|
1219
|
-
expect(directive.classes()).toContain(
|
|
1220
|
-
'px-[var(--luma-button-padding-x-lg)]',
|
|
1221
|
-
);
|
|
1222
|
-
});
|
|
1223
|
-
});
|
|
1224
|
-
|
|
1225
|
-
// ============================================================
|
|
1226
|
-
// DARK THEME TESTS
|
|
1227
|
-
// ============================================================
|
|
1228
|
-
|
|
1229
|
-
describe('Dark Theme', () => {
|
|
1230
|
-
beforeEach(() => {
|
|
1231
|
-
applyDarkTheme();
|
|
1232
|
-
});
|
|
1233
|
-
|
|
1234
|
-
it('should have access to dark theme primary background', () => {
|
|
1235
|
-
hostComponent.lmVariant = 'primary';
|
|
1236
|
-
fixture.detectChanges();
|
|
1237
|
-
|
|
1238
|
-
const value = getComputedStyle(document.documentElement)
|
|
1239
|
-
.getPropertyValue('--luma-button-primary-bg')
|
|
1240
|
-
.trim();
|
|
1241
|
-
expect(value).toBe(DARK_TOKENS.primary.bg);
|
|
1242
|
-
});
|
|
1243
|
-
|
|
1244
|
-
it('should have access to dark theme primary text', () => {
|
|
1245
|
-
hostComponent.lmVariant = 'primary';
|
|
1246
|
-
fixture.detectChanges();
|
|
1247
|
-
|
|
1248
|
-
const value = getComputedStyle(document.documentElement)
|
|
1249
|
-
.getPropertyValue('--luma-button-primary-text')
|
|
1250
|
-
.trim();
|
|
1251
|
-
expect(value).toBe(DARK_TOKENS.primary.text);
|
|
1252
|
-
});
|
|
1253
|
-
|
|
1254
|
-
it('should have access to dark theme outline border', () => {
|
|
1255
|
-
hostComponent.lmVariant = 'outline';
|
|
1256
|
-
fixture.detectChanges();
|
|
1257
|
-
|
|
1258
|
-
const value = getComputedStyle(document.documentElement)
|
|
1259
|
-
.getPropertyValue('--luma-button-outline-border')
|
|
1260
|
-
.trim();
|
|
1261
|
-
expect(value).toBe(DARK_TOKENS.outline.border);
|
|
1262
|
-
});
|
|
1263
|
-
|
|
1264
|
-
it('should have access to dark theme danger background', () => {
|
|
1265
|
-
hostComponent.lmVariant = 'danger';
|
|
1266
|
-
fixture.detectChanges();
|
|
1267
|
-
|
|
1268
|
-
const value = getComputedStyle(document.documentElement)
|
|
1269
|
-
.getPropertyValue('--luma-button-danger-bg')
|
|
1270
|
-
.trim();
|
|
1271
|
-
expect(value).toBe(DARK_TOKENS.danger.bg);
|
|
1272
|
-
});
|
|
1273
|
-
|
|
1274
|
-
it('should have access to dark theme focus ring color', () => {
|
|
1275
|
-
fixture.detectChanges();
|
|
1276
|
-
|
|
1277
|
-
const value = getComputedStyle(document.documentElement)
|
|
1278
|
-
.getPropertyValue('--luma-button-focus-ring-color')
|
|
1279
|
-
.trim();
|
|
1280
|
-
expect(value).toBe(DARK_TOKENS.focus.ringColor);
|
|
1281
|
-
});
|
|
1282
|
-
|
|
1283
|
-
it('should have dark class on document element', () => {
|
|
1284
|
-
expect(document.documentElement.classList.contains('dark')).toBe(true);
|
|
1285
|
-
});
|
|
1286
|
-
});
|
|
1287
|
-
});
|
|
1288
|
-
|
|
1289
|
-
// ============================================================
|
|
1290
|
-
// ANCHOR ELEMENT TESTS
|
|
1291
|
-
// ============================================================
|
|
1292
|
-
|
|
1293
|
-
describe('ButtonDirective on Anchor Element', () => {
|
|
1294
|
-
let fixture: ComponentFixture<AnchorButtonTestHostComponent>;
|
|
1295
|
-
let anchorElement: DebugElement;
|
|
1296
|
-
let directive: ButtonDirective;
|
|
1297
|
-
|
|
1298
|
-
beforeEach(async () => {
|
|
1299
|
-
await TestBed.configureTestingModule({
|
|
1300
|
-
imports: [ButtonDirective, AnchorButtonTestHostComponent],
|
|
1301
|
-
}).compileComponents();
|
|
1302
|
-
|
|
1303
|
-
fixture = TestBed.createComponent(AnchorButtonTestHostComponent);
|
|
1304
|
-
anchorElement = fixture.debugElement.query(By.directive(ButtonDirective));
|
|
1305
|
-
directive = anchorElement.injector.get(ButtonDirective);
|
|
1306
|
-
|
|
1307
|
-
setupButtonTokens();
|
|
1308
|
-
await fixture.whenStable();
|
|
1309
|
-
});
|
|
1310
|
-
|
|
1311
|
-
afterEach(() => {
|
|
1312
|
-
cleanupButtonTokens();
|
|
1313
|
-
});
|
|
1314
|
-
|
|
1315
|
-
it('should create directive on anchor element', () => {
|
|
1316
|
-
expect(directive).toBeTruthy();
|
|
1317
|
-
expect(anchorElement.nativeElement.tagName).toBe('A');
|
|
1318
|
-
});
|
|
1319
|
-
|
|
1320
|
-
it('should apply same base classes as button element', () => {
|
|
1321
|
-
fixture.detectChanges();
|
|
1322
|
-
|
|
1323
|
-
const classes = directive.classes();
|
|
1324
|
-
expect(classes).toContain('inline-flex');
|
|
1325
|
-
expect(classes).toContain('items-center');
|
|
1326
|
-
expect(classes).toContain('justify-center');
|
|
1327
|
-
});
|
|
1328
|
-
|
|
1329
|
-
it('should apply variant classes on anchor element', () => {
|
|
1330
|
-
fixture.detectChanges();
|
|
1331
|
-
|
|
1332
|
-
const classes = directive.classes();
|
|
1333
|
-
expect(classes).toContain('lm-bg-button-primary');
|
|
1334
|
-
expect(classes).toContain('lm-text-button-primary-text');
|
|
1335
|
-
});
|
|
1336
|
-
|
|
1337
|
-
it('should preserve href attribute', () => {
|
|
1338
|
-
fixture.detectChanges();
|
|
1339
|
-
expect(anchorElement.nativeElement.getAttribute('href')).toBe('/test');
|
|
1340
|
-
});
|
|
1341
|
-
|
|
1342
|
-
it('should have access to tokens on anchor element', () => {
|
|
1343
|
-
fixture.detectChanges();
|
|
1344
|
-
|
|
1345
|
-
const value = getComputedStyle(document.documentElement)
|
|
1346
|
-
.getPropertyValue('--luma-button-primary-bg')
|
|
1347
|
-
.trim();
|
|
1348
|
-
expect(value).toBe(BUTTON_TOKENS.primary.bg);
|
|
1349
|
-
});
|
|
1350
|
-
});
|