@tak-ps/vue-tabler 4.8.0 → 4.10.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/CHANGELOG.md +10 -0
- package/components/Badge.vue +136 -0
- package/components/input/Input.vue +141 -30
- package/lib.ts +1 -0
- package/package.json +1 -1
- package/test/Badge.spec.ts +52 -0
- package/test/Button.spec.ts +42 -0
package/CHANGELOG.md
CHANGED
|
@@ -10,6 +10,16 @@
|
|
|
10
10
|
|
|
11
11
|
## Version History
|
|
12
12
|
|
|
13
|
+
### v4.10.0
|
|
14
|
+
|
|
15
|
+
- :rocket: Improve Blur styling of TablerInput component
|
|
16
|
+
- :rocket: Improve Error styling of TablerInput component
|
|
17
|
+
- :rocket: Imrpove Error Blur styling of TablerInput component
|
|
18
|
+
|
|
19
|
+
### v4.9.0
|
|
20
|
+
|
|
21
|
+
- :tada: Introduce Tabler Badge
|
|
22
|
+
|
|
13
23
|
### v4.8.0
|
|
14
24
|
|
|
15
25
|
- :rocket: Allow setting button colour via prop
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<span
|
|
3
|
+
class='badge tabler-badge'
|
|
4
|
+
:style='badgeStyle'
|
|
5
|
+
>
|
|
6
|
+
<slot />
|
|
7
|
+
</span>
|
|
8
|
+
</template>
|
|
9
|
+
|
|
10
|
+
<script setup lang='ts'>
|
|
11
|
+
import { computed } from 'vue';
|
|
12
|
+
|
|
13
|
+
export interface BadgeProps {
|
|
14
|
+
backgroundColor?: string;
|
|
15
|
+
borderColor?: string;
|
|
16
|
+
textColor?: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const props = withDefaults(defineProps<BadgeProps>(), {
|
|
20
|
+
backgroundColor: 'rgba(34, 197, 94, 0.2)',
|
|
21
|
+
borderColor: undefined,
|
|
22
|
+
textColor: '#ffffff',
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
type ParsedColor = {
|
|
26
|
+
red: number;
|
|
27
|
+
green: number;
|
|
28
|
+
blue: number;
|
|
29
|
+
alpha?: number;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const HEX_COLOR = /^#([\da-f]{3}|[\da-f]{4}|[\da-f]{6}|[\da-f]{8})$/i;
|
|
33
|
+
const RGB_COLOR = /^rgba?\(([^)]+)\)$/i;
|
|
34
|
+
|
|
35
|
+
function clampChannel(value: number): number {
|
|
36
|
+
return Math.max(0, Math.min(255, Math.round(value)));
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function clampAlpha(value: number): number {
|
|
40
|
+
return Math.max(0, Math.min(1, value));
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function parseHexColor(value: string): ParsedColor | null {
|
|
44
|
+
const match = value.match(HEX_COLOR);
|
|
45
|
+
if (!match) return null;
|
|
46
|
+
|
|
47
|
+
const hex = match[1];
|
|
48
|
+
|
|
49
|
+
if (hex.length === 3 || hex.length === 4) {
|
|
50
|
+
const red = Number.parseInt(`${hex[0]}${hex[0]}`, 16);
|
|
51
|
+
const green = Number.parseInt(`${hex[1]}${hex[1]}`, 16);
|
|
52
|
+
const blue = Number.parseInt(`${hex[2]}${hex[2]}`, 16);
|
|
53
|
+
const alpha = hex.length === 4
|
|
54
|
+
? clampAlpha(Number.parseInt(`${hex[3]}${hex[3]}`, 16) / 255)
|
|
55
|
+
: undefined;
|
|
56
|
+
|
|
57
|
+
return { red, green, blue, alpha };
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const red = Number.parseInt(hex.slice(0, 2), 16);
|
|
61
|
+
const green = Number.parseInt(hex.slice(2, 4), 16);
|
|
62
|
+
const blue = Number.parseInt(hex.slice(4, 6), 16);
|
|
63
|
+
const alpha = hex.length === 8
|
|
64
|
+
? clampAlpha(Number.parseInt(hex.slice(6, 8), 16) / 255)
|
|
65
|
+
: undefined;
|
|
66
|
+
|
|
67
|
+
return { red, green, blue, alpha };
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function parseRgbColor(value: string): ParsedColor | null {
|
|
71
|
+
const match = value.match(RGB_COLOR);
|
|
72
|
+
if (!match) return null;
|
|
73
|
+
|
|
74
|
+
const parts = match[1].split(',').map((part) => part.trim());
|
|
75
|
+
if (parts.length < 3 || parts.length > 4) return null;
|
|
76
|
+
|
|
77
|
+
const red = Number(parts[0]);
|
|
78
|
+
const green = Number(parts[1]);
|
|
79
|
+
const blue = Number(parts[2]);
|
|
80
|
+
const alpha = parts[3] === undefined ? undefined : Number(parts[3]);
|
|
81
|
+
|
|
82
|
+
if ([red, green, blue, alpha].some((part) => part !== undefined && Number.isNaN(part))) {
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return {
|
|
87
|
+
red: clampChannel(red),
|
|
88
|
+
green: clampChannel(green),
|
|
89
|
+
blue: clampChannel(blue),
|
|
90
|
+
alpha: alpha === undefined ? undefined : clampAlpha(alpha),
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function parseColor(value: string): ParsedColor | null {
|
|
95
|
+
return parseHexColor(value) || parseRgbColor(value);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function darkenColor(value: string, amount = 0.2): string {
|
|
99
|
+
const parsed = parseColor(value);
|
|
100
|
+
if (!parsed) return value;
|
|
101
|
+
|
|
102
|
+
const factor = 1 - amount;
|
|
103
|
+
const next = {
|
|
104
|
+
red: clampChannel(parsed.red * factor),
|
|
105
|
+
green: clampChannel(parsed.green * factor),
|
|
106
|
+
blue: clampChannel(parsed.blue * factor),
|
|
107
|
+
alpha: parsed.alpha,
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
if (next.alpha === undefined) {
|
|
111
|
+
return `rgb(${next.red}, ${next.green}, ${next.blue})`;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return `rgba(${next.red}, ${next.green}, ${next.blue}, ${next.alpha})`;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const resolvedBorderColor = computed(() => {
|
|
118
|
+
if (props.borderColor) return props.borderColor;
|
|
119
|
+
return darkenColor(props.backgroundColor, 0.2);
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
const badgeStyle = computed(() => {
|
|
123
|
+
return {
|
|
124
|
+
backgroundColor: props.backgroundColor,
|
|
125
|
+
borderColor: resolvedBorderColor.value,
|
|
126
|
+
color: props.textColor,
|
|
127
|
+
};
|
|
128
|
+
});
|
|
129
|
+
</script>
|
|
130
|
+
|
|
131
|
+
<style scoped>
|
|
132
|
+
.tabler-badge {
|
|
133
|
+
border-style: solid;
|
|
134
|
+
border-width: 1px;
|
|
135
|
+
}
|
|
136
|
+
</style>
|
|
@@ -10,7 +10,12 @@
|
|
|
10
10
|
</TablerLabel>
|
|
11
11
|
<div class='col-12'>
|
|
12
12
|
<template v-if='!rows || rows <= 1'>
|
|
13
|
-
<div
|
|
13
|
+
<div
|
|
14
|
+
class='input-group input-group-flat'
|
|
15
|
+
:class='{
|
|
16
|
+
"tabler-input-group-invalid": errorstr && hasEndAdornment
|
|
17
|
+
}'
|
|
18
|
+
>
|
|
14
19
|
<span
|
|
15
20
|
v-if='icon || $slots.icon'
|
|
16
21
|
class='input-group-text'
|
|
@@ -43,7 +48,8 @@
|
|
|
43
48
|
:autofocus='autofocus'
|
|
44
49
|
:type='computed_type'
|
|
45
50
|
:class='{
|
|
46
|
-
"is-invalid": errorstr
|
|
51
|
+
"is-invalid": errorstr,
|
|
52
|
+
"tabler-input-with-end": hasEndAdornment
|
|
47
53
|
}'
|
|
48
54
|
class='form-control'
|
|
49
55
|
:placeholder='placeholder||label||""'
|
|
@@ -83,8 +89,8 @@
|
|
|
83
89
|
href='#'
|
|
84
90
|
class='link-secondary'
|
|
85
91
|
data-bs-toggle='tooltip'
|
|
86
|
-
aria-label='
|
|
87
|
-
data-bs-original-title='
|
|
92
|
+
aria-label='Hide Password'
|
|
93
|
+
data-bs-original-title='Hide Password'
|
|
88
94
|
>
|
|
89
95
|
<IconEyeOff
|
|
90
96
|
:size='20'
|
|
@@ -98,7 +104,6 @@
|
|
|
98
104
|
@click='current = ""'
|
|
99
105
|
>
|
|
100
106
|
<a
|
|
101
|
-
v-if='!passwordVisible'
|
|
102
107
|
href='#'
|
|
103
108
|
class='link-secondary'
|
|
104
109
|
data-bs-toggle='tooltip'
|
|
@@ -111,35 +116,69 @@
|
|
|
111
116
|
/>
|
|
112
117
|
</a>
|
|
113
118
|
</span>
|
|
114
|
-
<
|
|
115
|
-
v-if='
|
|
116
|
-
class='
|
|
117
|
-
|
|
118
|
-
|
|
119
|
+
<span
|
|
120
|
+
v-if='hasEndAdornment'
|
|
121
|
+
:class='errorstr ? "tabler-input-error-end" : "tabler-input-end"'
|
|
122
|
+
class='input-group-text'
|
|
123
|
+
>
|
|
124
|
+
<IconAlertCircle
|
|
125
|
+
v-if='errorstr'
|
|
126
|
+
:size='20'
|
|
127
|
+
stroke='1.5'
|
|
128
|
+
/>
|
|
129
|
+
<slot
|
|
130
|
+
v-else
|
|
131
|
+
name='end'
|
|
132
|
+
/>
|
|
133
|
+
</span>
|
|
119
134
|
</div>
|
|
135
|
+
<div
|
|
136
|
+
v-if='errorstr'
|
|
137
|
+
class='invalid-feedback d-block'
|
|
138
|
+
v-text='errorstr'
|
|
139
|
+
/>
|
|
120
140
|
</template>
|
|
121
141
|
<template v-else>
|
|
122
|
-
<
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
142
|
+
<div class='position-relative'>
|
|
143
|
+
<textarea
|
|
144
|
+
ref='textInput'
|
|
145
|
+
v-model='current'
|
|
146
|
+
:disabled='disabled'
|
|
147
|
+
:autofocus='autofocus'
|
|
148
|
+
:autocomplete='autocomplete'
|
|
149
|
+
:wrap='wrap'
|
|
150
|
+
:rows='rows'
|
|
151
|
+
:type='computed_type'
|
|
152
|
+
:class='{
|
|
153
|
+
"is-invalid": errorstr,
|
|
154
|
+
"tabler-input-with-end": hasEndAdornment
|
|
155
|
+
}'
|
|
156
|
+
class='form-control'
|
|
157
|
+
:placeholder='placeholder||label||""'
|
|
158
|
+
@keyup.enter='$emit("submit")'
|
|
159
|
+
@focus='$emit("focus")'
|
|
160
|
+
@blur='$emit("blur")'
|
|
161
|
+
/>
|
|
162
|
+
|
|
163
|
+
<div
|
|
164
|
+
v-if='hasEndAdornment'
|
|
165
|
+
:class='errorstr ? "tabler-input-error-end" : "tabler-input-end"'
|
|
166
|
+
class='tabler-input-textarea-end'
|
|
167
|
+
>
|
|
168
|
+
<IconAlertCircle
|
|
169
|
+
v-if='errorstr'
|
|
170
|
+
:size='20'
|
|
171
|
+
stroke='1.5'
|
|
172
|
+
/>
|
|
173
|
+
<slot
|
|
174
|
+
v-else
|
|
175
|
+
name='end'
|
|
176
|
+
/>
|
|
177
|
+
</div>
|
|
178
|
+
</div>
|
|
140
179
|
<div
|
|
141
180
|
v-if='errorstr'
|
|
142
|
-
class='invalid-feedback'
|
|
181
|
+
class='invalid-feedback d-block'
|
|
143
182
|
v-text='errorstr'
|
|
144
183
|
/>
|
|
145
184
|
</template>
|
|
@@ -148,9 +187,10 @@
|
|
|
148
187
|
</template>
|
|
149
188
|
|
|
150
189
|
<script setup lang="ts">
|
|
151
|
-
import { ref, computed, watch, onMounted } from 'vue'
|
|
190
|
+
import { ref, computed, watch, onMounted, useSlots } from 'vue'
|
|
152
191
|
import TablerLabel from '../internal/Label.vue'
|
|
153
192
|
import {
|
|
193
|
+
IconAlertCircle,
|
|
154
194
|
IconEye,
|
|
155
195
|
IconEyeOff,
|
|
156
196
|
IconCircleXFilled,
|
|
@@ -192,6 +232,8 @@ const props = withDefaults(defineProps<InputProps>(), {
|
|
|
192
232
|
error: ''
|
|
193
233
|
});
|
|
194
234
|
|
|
235
|
+
const slots = useSlots();
|
|
236
|
+
|
|
195
237
|
const emit = defineEmits<{
|
|
196
238
|
(e: 'blur'): void;
|
|
197
239
|
(e: 'focus'): void;
|
|
@@ -210,6 +252,10 @@ const errorstr = computed(() => {
|
|
|
210
252
|
return internal_error.value
|
|
211
253
|
})
|
|
212
254
|
|
|
255
|
+
const hasEndAdornment = computed(() => {
|
|
256
|
+
return Boolean(errorstr.value || !!slots.end)
|
|
257
|
+
})
|
|
258
|
+
|
|
213
259
|
const computed_type = computed(() => {
|
|
214
260
|
if (props.type === 'password' && passwordVisible.value) return 'text';
|
|
215
261
|
if (props.type === 'integer') return 'number'
|
|
@@ -271,4 +317,69 @@ input:autofill {
|
|
|
271
317
|
background-image calc(infinity * 1s) step-end allow-discrete,
|
|
272
318
|
color calc(infinity * 1s) step-end;
|
|
273
319
|
}
|
|
320
|
+
|
|
321
|
+
.tabler-input-with-end {
|
|
322
|
+
padding-right: 3rem;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
.tabler-input-with-end.is-invalid {
|
|
326
|
+
background-image: none;
|
|
327
|
+
padding-right: 3rem;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
.tabler-input-group-invalid > .form-control.is-invalid {
|
|
331
|
+
border-right: 0;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
.tabler-input-group-invalid:focus-within {
|
|
335
|
+
border-radius: var(--tblr-border-radius);
|
|
336
|
+
box-shadow: var(--tblr-shadow-input), 0 0 0 0.25rem rgba(var(--tblr-danger-rgb), 0.25);
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
.tabler-input-group-invalid:focus-within > .form-control.is-invalid {
|
|
340
|
+
border-color: var(--tblr-form-invalid-border-color, var(--tblr-danger));
|
|
341
|
+
box-shadow: none;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
.tabler-input-group-invalid > .form-control.is-invalid:focus {
|
|
345
|
+
border-color: var(--tblr-form-invalid-border-color, var(--tblr-danger)) !important;
|
|
346
|
+
box-shadow: none !important;
|
|
347
|
+
outline: 0;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
.tabler-input-group-invalid > .input-group-text:last-child {
|
|
351
|
+
border-color: var(--tblr-form-invalid-border-color, var(--tblr-danger)) !important;
|
|
352
|
+
border-left: 0;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
.tabler-input-group-invalid:focus-within > .input-group-text:last-child {
|
|
356
|
+
border-color: var(--tblr-form-invalid-border-color, var(--tblr-danger)) !important;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
.tabler-input-end {
|
|
360
|
+
color: var(--tblr-white);
|
|
361
|
+
opacity: 1;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
.tabler-input-error-end {
|
|
365
|
+
color: var(--tblr-form-invalid-border-color, var(--tblr-danger)) !important;
|
|
366
|
+
opacity: 1;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
.tabler-input-end :deep(a),
|
|
370
|
+
.tabler-input-end :deep(svg),
|
|
371
|
+
.tabler-input-error-end :deep(a),
|
|
372
|
+
.tabler-input-error-end :deep(svg) {
|
|
373
|
+
color: inherit;
|
|
374
|
+
opacity: 1;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
.tabler-input-textarea-end {
|
|
378
|
+
position: absolute;
|
|
379
|
+
right: 0.5rem;
|
|
380
|
+
top: 0.5rem;
|
|
381
|
+
z-index: 2;
|
|
382
|
+
display: flex;
|
|
383
|
+
align-items: center;
|
|
384
|
+
}
|
|
274
385
|
</style>
|
package/lib.ts
CHANGED
|
@@ -10,6 +10,7 @@ export { default as TablerEnum } from './components/input/Enum.vue';
|
|
|
10
10
|
|
|
11
11
|
export { default as TablerAlert } from './components/Alert.vue'
|
|
12
12
|
export { default as TablerInlineAlert } from './components/InlineAlert.vue'
|
|
13
|
+
export { default as TablerBadge } from './components/Badge.vue'
|
|
13
14
|
export { default as TablerButton } from './components/Button.vue'
|
|
14
15
|
export { default as TablerRefreshButton } from './components/RefreshButton.vue'
|
|
15
16
|
export { default as TablerIconButton } from './components/IconButton.vue'
|
package/package.json
CHANGED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
import { mount } from '@vue/test-utils'
|
|
3
|
+
import Badge from '../components/Badge.vue'
|
|
4
|
+
|
|
5
|
+
describe('TablerBadge', () => {
|
|
6
|
+
it('renders the default slot content', () => {
|
|
7
|
+
const wrapper = mount(Badge, {
|
|
8
|
+
slots: {
|
|
9
|
+
default: 'Allowed',
|
|
10
|
+
},
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
expect(wrapper.get('span').text().trim()).toBe('Allowed')
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
it('applies the default badge colors', () => {
|
|
17
|
+
const wrapper = mount(Badge)
|
|
18
|
+
const badge = wrapper.get('span').element as HTMLSpanElement
|
|
19
|
+
|
|
20
|
+
expect(badge.style.backgroundColor).toBe('rgba(34, 197, 94, 0.2)')
|
|
21
|
+
expect(badge.style.borderColor).toBe('rgba(27, 158, 75, 0.2)')
|
|
22
|
+
expect(badge.style.color).toBe('rgb(255, 255, 255)')
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
it('derives a darker border color from the provided background color', () => {
|
|
26
|
+
const wrapper = mount(Badge, {
|
|
27
|
+
props: {
|
|
28
|
+
backgroundColor: '#336699',
|
|
29
|
+
},
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
const badge = wrapper.get('span').element as HTMLSpanElement
|
|
33
|
+
|
|
34
|
+
expect(badge.style.backgroundColor).toBe('rgb(51, 102, 153)')
|
|
35
|
+
expect(badge.style.borderColor).toBe('rgb(41, 82, 122)')
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
it('prefers explicit border and text colors when provided', () => {
|
|
39
|
+
const wrapper = mount(Badge, {
|
|
40
|
+
props: {
|
|
41
|
+
backgroundColor: 'rgba(1, 2, 3, 0.4)',
|
|
42
|
+
borderColor: '#123456',
|
|
43
|
+
textColor: '#abcdef',
|
|
44
|
+
},
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
const badge = wrapper.get('span').element as HTMLSpanElement
|
|
48
|
+
|
|
49
|
+
expect(badge.style.borderColor).toBe('rgb(18, 52, 86)')
|
|
50
|
+
expect(badge.style.color).toBe('rgb(171, 205, 239)')
|
|
51
|
+
})
|
|
52
|
+
})
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
import { mount } from '@vue/test-utils'
|
|
3
|
+
import Button from '../components/Button.vue'
|
|
4
|
+
|
|
5
|
+
describe('TablerButton', () => {
|
|
6
|
+
it('renders the default slot content', () => {
|
|
7
|
+
const wrapper = mount(Button, {
|
|
8
|
+
slots: {
|
|
9
|
+
default: 'Click Me',
|
|
10
|
+
},
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
expect(wrapper.get('button').text().trim()).toBe('Click Me')
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
it('sets the disabled attribute when disabled is true', () => {
|
|
17
|
+
const wrapper = mount(Button, {
|
|
18
|
+
props: {
|
|
19
|
+
disabled: true,
|
|
20
|
+
},
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
expect(wrapper.get('button').attributes('disabled')).toBeDefined()
|
|
24
|
+
expect((wrapper.get('button').element as HTMLButtonElement).disabled).toBe(true)
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
it('applies the provided background color', () => {
|
|
28
|
+
const wrapper = mount(Button, {
|
|
29
|
+
props: {
|
|
30
|
+
color: 'red',
|
|
31
|
+
},
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
expect((wrapper.get('button').element as HTMLButtonElement).style.backgroundColor).toBe('red')
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
it('does not apply a background color when color is omitted', () => {
|
|
38
|
+
const wrapper = mount(Button)
|
|
39
|
+
|
|
40
|
+
expect((wrapper.get('button').element as HTMLButtonElement).style.backgroundColor).toBe('')
|
|
41
|
+
})
|
|
42
|
+
})
|