ngwr 15.4.0 → 15.5.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/comment/styles/_index.scss +270 -0
- package/dialog/styles/_index.scss +6 -0
- package/fesm2022/ngwr-comment-testing.mjs +60 -0
- package/fesm2022/ngwr-comment-testing.mjs.map +1 -0
- package/fesm2022/ngwr-comment.mjs +516 -0
- package/fesm2022/ngwr-comment.mjs.map +1 -0
- package/fesm2022/ngwr-dialog-testing.mjs +14 -0
- package/fesm2022/ngwr-dialog-testing.mjs.map +1 -1
- package/fesm2022/ngwr-dialog.mjs +293 -134
- package/fesm2022/ngwr-dialog.mjs.map +1 -1
- package/fesm2022/ngwr-drawer.mjs +2 -0
- package/fesm2022/ngwr-drawer.mjs.map +1 -1
- package/fesm2022/ngwr-editor.mjs +1 -1
- package/fesm2022/ngwr-hotkey.mjs +86 -6
- package/fesm2022/ngwr-hotkey.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-ar.mjs +19 -0
- package/fesm2022/ngwr-i18n-ar.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-cs.mjs +19 -0
- package/fesm2022/ngwr-i18n-cs.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-de.mjs +19 -0
- package/fesm2022/ngwr-i18n-de.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-en.mjs +19 -0
- package/fesm2022/ngwr-i18n-en.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-es.mjs +19 -0
- package/fesm2022/ngwr-i18n-es.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-fr.mjs +19 -0
- package/fesm2022/ngwr-i18n-fr.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-he.mjs +19 -0
- package/fesm2022/ngwr-i18n-he.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-hi.mjs +19 -0
- package/fesm2022/ngwr-i18n-hi.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-id.mjs +19 -0
- package/fesm2022/ngwr-i18n-id.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-it.mjs +19 -0
- package/fesm2022/ngwr-i18n-it.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-ja.mjs +19 -0
- package/fesm2022/ngwr-i18n-ja.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-ko.mjs +19 -0
- package/fesm2022/ngwr-i18n-ko.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-nl.mjs +19 -0
- package/fesm2022/ngwr-i18n-nl.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-pl.mjs +19 -0
- package/fesm2022/ngwr-i18n-pl.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-pt.mjs +19 -0
- package/fesm2022/ngwr-i18n-pt.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-ru.mjs +19 -0
- package/fesm2022/ngwr-i18n-ru.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-sv.mjs +19 -0
- package/fesm2022/ngwr-i18n-sv.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-tr.mjs +19 -0
- package/fesm2022/ngwr-i18n-tr.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-uk.mjs +19 -0
- package/fesm2022/ngwr-i18n-uk.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-vi.mjs +19 -0
- package/fesm2022/ngwr-i18n-vi.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-zh-tw.mjs +19 -0
- package/fesm2022/ngwr-i18n-zh-tw.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-zh.mjs +19 -0
- package/fesm2022/ngwr-i18n-zh.mjs.map +1 -1
- package/fesm2022/ngwr-input-number-testing.mjs.map +1 -1
- package/fesm2022/ngwr-input-number.mjs +109 -12
- package/fesm2022/ngwr-input-number.mjs.map +1 -1
- package/fesm2022/ngwr-keyboard.mjs +78 -18
- package/fesm2022/ngwr-keyboard.mjs.map +1 -1
- package/fesm2022/ngwr-platform.mjs +134 -2
- package/fesm2022/ngwr-platform.mjs.map +1 -1
- package/fesm2022/ngwr-schema-form.mjs +2 -1
- package/fesm2022/ngwr-schema-form.mjs.map +1 -1
- package/fesm2022/ngwr-select.mjs.map +1 -1
- package/fesm2022/ngwr-storage.mjs +54 -33
- package/fesm2022/ngwr-storage.mjs.map +1 -1
- package/fesm2022/ngwr-table.mjs +90 -34
- package/fesm2022/ngwr-table.mjs.map +1 -1
- package/fesm2022/ngwr-transfer.mjs +1 -1
- package/fesm2022/ngwr-utils-router.mjs +165 -0
- package/fesm2022/ngwr-utils-router.mjs.map +1 -0
- package/fesm2022/ngwr-utils.mjs +311 -3
- package/fesm2022/ngwr-utils.mjs.map +1 -1
- package/fesm2022/ngwr-version.mjs +1 -1
- package/fesm2022/ngwr-version.mjs.map +1 -1
- package/i18n/ar.json +19 -0
- package/i18n/cs.json +19 -0
- package/i18n/de.json +19 -0
- package/i18n/en.json +19 -0
- package/i18n/es.json +19 -0
- package/i18n/fr.json +19 -0
- package/i18n/he.json +19 -0
- package/i18n/hi.json +19 -0
- package/i18n/id.json +19 -0
- package/i18n/it.json +19 -0
- package/i18n/ja.json +19 -0
- package/i18n/ko.json +19 -0
- package/i18n/nl.json +19 -0
- package/i18n/pl.json +19 -0
- package/i18n/pt.json +19 -0
- package/i18n/ru.json +19 -0
- package/i18n/sv.json +19 -0
- package/i18n/tr.json +19 -0
- package/i18n/uk.json +19 -0
- package/i18n/vi.json +19 -0
- package/i18n/zh-TW.json +19 -0
- package/i18n/zh.json +19 -0
- package/llms-full.txt +30 -14
- package/package.json +14 -1
- package/schematics/use/symbol-map.json +2 -0
- package/skills/ngwr/SKILL.md +1 -1
- package/skills/ngwr/references/catalog.md +3 -0
- package/table/styles/_index.scss +17 -0
- package/types/ngwr-badge.d.ts +2 -2
- package/types/ngwr-button.d.ts +1 -1
- package/types/ngwr-comment-testing.d.ts +90 -0
- package/types/ngwr-comment.d.ts +304 -0
- package/types/ngwr-dialog-testing.d.ts +7 -0
- package/types/ngwr-dialog.d.ts +109 -6
- package/types/ngwr-divider.d.ts +1 -1
- package/types/ngwr-drawer.d.ts +1 -1
- package/types/ngwr-hotkey.d.ts +60 -3
- package/types/ngwr-image-cropper.d.ts +1 -1
- package/types/ngwr-input-number-testing.d.ts +5 -0
- package/types/ngwr-input-number.d.ts +91 -12
- package/types/ngwr-keyboard.d.ts +28 -1
- package/types/ngwr-platform.d.ts +73 -7
- package/types/ngwr-popconfirm.d.ts +2 -2
- package/types/ngwr-progress.d.ts +1 -1
- package/types/ngwr-select.d.ts +19 -13
- package/types/ngwr-skeleton.d.ts +1 -1
- package/types/ngwr-storage.d.ts +33 -3
- package/types/ngwr-table.d.ts +145 -44
- package/types/ngwr-utils-router.d.ts +95 -0
- package/types/ngwr-utils.d.ts +262 -2
- package/types/ngwr-version.d.ts +1 -1
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
@use '../../theme/styles' as theme;
|
|
2
|
+
|
|
3
|
+
// Styles for the entry points this component's own markup renders.
|
|
4
|
+
//
|
|
5
|
+
// v15 removed the `@use 'ngwr'` umbrella: a consumer writes one `@use` per
|
|
6
|
+
// component they place, and never learns about the ones a component places
|
|
7
|
+
// INSIDE itself. Sass deduplicates `@use` within a compilation, so an app that
|
|
8
|
+
// also loads one of these by hand still emits it once.
|
|
9
|
+
@use '../../button/styles' as button-styles; // WrButton
|
|
10
|
+
@use '../../textarea/styles' as textarea-styles; // WrTextarea
|
|
11
|
+
|
|
12
|
+
.wr-comment {
|
|
13
|
+
--wr-comment-gap: var(--wr-space-md);
|
|
14
|
+
--wr-comment-head-gap: var(--wr-space-sm);
|
|
15
|
+
--wr-comment-body-gap: var(--wr-space-xs);
|
|
16
|
+
--wr-comment-actions-gap: var(--wr-space-sm);
|
|
17
|
+
--wr-comment-font-family: var(--wr-font-family-base);
|
|
18
|
+
--wr-comment-font-size: var(--wr-text-sm);
|
|
19
|
+
--wr-comment-line-height: var(--wr-text-sm-leading);
|
|
20
|
+
--wr-comment-author-color: var(--wr-color-on-surface);
|
|
21
|
+
--wr-comment-author-font-weight: var(--wr-font-weight-semibold);
|
|
22
|
+
--wr-comment-time-color: var(--wr-color-on-surface-muted);
|
|
23
|
+
--wr-comment-time-font-size: var(--wr-text-xs);
|
|
24
|
+
--wr-comment-body-color: var(--wr-color-on-surface);
|
|
25
|
+
// Replies: how far one level steps in, and the room between them.
|
|
26
|
+
--wr-comment-indent: var(--wr-space-xl);
|
|
27
|
+
--wr-comment-replies-gap: var(--wr-space-lg);
|
|
28
|
+
--wr-comment-replies-margin-top: var(--wr-space-lg);
|
|
29
|
+
// Bubble variant. The reader's own side is a filled intent with its
|
|
30
|
+
// `-contrast` label; the other side is a neutral fill with surface text.
|
|
31
|
+
--wr-comment-bubble-bg: var(--wr-color-fill);
|
|
32
|
+
--wr-comment-bubble-color: var(--wr-color-on-surface);
|
|
33
|
+
--wr-comment-bubble-time-color: var(--wr-color-on-surface-muted);
|
|
34
|
+
--wr-comment-bubble-radius: var(--wr-border-radius-lg);
|
|
35
|
+
--wr-comment-bubble-padding-y: var(--wr-space-sm);
|
|
36
|
+
--wr-comment-bubble-padding-x: var(--wr-space-md);
|
|
37
|
+
--wr-comment-bubble-max-width: 80%;
|
|
38
|
+
|
|
39
|
+
display: flex;
|
|
40
|
+
flex-wrap: wrap;
|
|
41
|
+
align-items: flex-start;
|
|
42
|
+
gap: var(--wr-comment-gap);
|
|
43
|
+
// Claim the full row even when nested in a flex-row demo wrapper.
|
|
44
|
+
flex: 1 1 100%;
|
|
45
|
+
width: 100%;
|
|
46
|
+
min-width: 0;
|
|
47
|
+
font-family: var(--wr-comment-font-family);
|
|
48
|
+
font-size: var(--wr-comment-font-size);
|
|
49
|
+
line-height: var(--wr-comment-line-height);
|
|
50
|
+
color: var(--wr-comment-body-color);
|
|
51
|
+
|
|
52
|
+
&__avatar {
|
|
53
|
+
flex: 0 0 auto;
|
|
54
|
+
display: flex;
|
|
55
|
+
|
|
56
|
+
&:empty {
|
|
57
|
+
display: none;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
&__main {
|
|
62
|
+
flex: 1 1 0;
|
|
63
|
+
display: flex;
|
|
64
|
+
flex-direction: column;
|
|
65
|
+
gap: var(--wr-comment-body-gap);
|
|
66
|
+
min-width: 0;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
&__head {
|
|
70
|
+
display: flex;
|
|
71
|
+
flex-wrap: wrap;
|
|
72
|
+
align-items: baseline;
|
|
73
|
+
gap: var(--wr-comment-head-gap);
|
|
74
|
+
min-width: 0;
|
|
75
|
+
|
|
76
|
+
&:empty {
|
|
77
|
+
display: none;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
&__author {
|
|
82
|
+
color: var(--wr-comment-author-color);
|
|
83
|
+
font-weight: var(--wr-comment-author-font-weight);
|
|
84
|
+
overflow-wrap: anywhere;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
&__time {
|
|
88
|
+
color: var(--wr-comment-time-color);
|
|
89
|
+
font-size: var(--wr-comment-time-font-size);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
&__body {
|
|
93
|
+
min-width: 0;
|
|
94
|
+
// A pasted URL or a token with no break opportunity would otherwise push
|
|
95
|
+
// the row past its column.
|
|
96
|
+
overflow-wrap: anywhere;
|
|
97
|
+
// Text from the composer holds the lines Shift+Enter made.
|
|
98
|
+
white-space: pre-line;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
&__actions {
|
|
102
|
+
display: flex;
|
|
103
|
+
flex-wrap: wrap;
|
|
104
|
+
align-items: center;
|
|
105
|
+
gap: var(--wr-comment-actions-gap);
|
|
106
|
+
|
|
107
|
+
&:empty {
|
|
108
|
+
display: none;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
&__replies {
|
|
113
|
+
flex: 1 1 100%;
|
|
114
|
+
display: flex;
|
|
115
|
+
flex-direction: column;
|
|
116
|
+
gap: var(--wr-comment-replies-gap);
|
|
117
|
+
margin-top: var(--wr-comment-replies-margin-top);
|
|
118
|
+
padding-inline-start: var(--wr-comment-indent);
|
|
119
|
+
min-width: 0;
|
|
120
|
+
|
|
121
|
+
&:empty {
|
|
122
|
+
display: none;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// The third level and deeper sit flush with their parent: each step takes
|
|
126
|
+
// the indent plus an avatar column, and a phone has no body width left to
|
|
127
|
+
// give after two.
|
|
128
|
+
.wr-comment__replies .wr-comment__replies {
|
|
129
|
+
padding-inline-start: 0;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// One step on a phone: the second level is flush too.
|
|
134
|
+
@media (max-width: 480px) {
|
|
135
|
+
&__replies .wr-comment__replies {
|
|
136
|
+
padding-inline-start: 0;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Bubble variant: the body is a chat bubble, the head sits inside it, and the
|
|
141
|
+
// whole row leans to one inline side. Logical properties and `row-reverse`
|
|
142
|
+
// only, so `dir="rtl"` mirrors the log on its own.
|
|
143
|
+
&--bubble {
|
|
144
|
+
flex-wrap: nowrap;
|
|
145
|
+
|
|
146
|
+
.wr-comment__main {
|
|
147
|
+
flex: 0 1 auto;
|
|
148
|
+
max-width: var(--wr-comment-bubble-max-width);
|
|
149
|
+
gap: 0;
|
|
150
|
+
padding: var(--wr-comment-bubble-padding-y) var(--wr-comment-bubble-padding-x);
|
|
151
|
+
background: var(--wr-comment-bubble-bg);
|
|
152
|
+
color: var(--wr-comment-bubble-color);
|
|
153
|
+
@include theme.smooth-br(var(--wr-comment-bubble-radius));
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
.wr-comment__head {
|
|
157
|
+
gap: var(--wr-comment-head-gap);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
.wr-comment__author {
|
|
161
|
+
color: inherit;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
.wr-comment__time {
|
|
165
|
+
color: var(--wr-comment-bubble-time-color);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
.wr-comment__actions {
|
|
169
|
+
margin-top: var(--wr-comment-body-gap);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
&--end {
|
|
174
|
+
--wr-comment-bubble-bg: var(--wr-color-primary);
|
|
175
|
+
--wr-comment-bubble-color: var(--wr-color-primary-contrast);
|
|
176
|
+
--wr-comment-bubble-time-color: var(--wr-color-primary-contrast);
|
|
177
|
+
|
|
178
|
+
flex-direction: row-reverse;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
.wr-comment-composer {
|
|
183
|
+
--wr-comment-composer-bg: var(--wr-color-surface);
|
|
184
|
+
--wr-comment-composer-border: var(--wr-border-base);
|
|
185
|
+
--wr-comment-composer-ring: transparent;
|
|
186
|
+
--wr-comment-composer-radius: var(--wr-control-radius-md);
|
|
187
|
+
--wr-comment-composer-gap: var(--wr-space-xs);
|
|
188
|
+
--wr-comment-composer-padding: var(--wr-space-xs);
|
|
189
|
+
// 36px: one row of the md field (20px line, 8px padding a side) less its
|
|
190
|
+
// border, so the button sits inside the row it sends.
|
|
191
|
+
--wr-comment-composer-send-size: 2.25rem;
|
|
192
|
+
|
|
193
|
+
display: flex;
|
|
194
|
+
align-items: flex-end;
|
|
195
|
+
gap: var(--wr-comment-composer-gap);
|
|
196
|
+
// Claim the full row even when nested in a flex-row demo wrapper.
|
|
197
|
+
flex: 1 1 100%;
|
|
198
|
+
width: 100%;
|
|
199
|
+
min-width: 0;
|
|
200
|
+
padding: var(--wr-comment-composer-padding);
|
|
201
|
+
background: var(--wr-comment-composer-bg);
|
|
202
|
+
border: 1px solid var(--wr-comment-composer-border);
|
|
203
|
+
@include theme.smooth-br(var(--wr-comment-composer-radius));
|
|
204
|
+
box-shadow: 0 0 0 3px var(--wr-comment-composer-ring);
|
|
205
|
+
transition:
|
|
206
|
+
border-color var(--wr-transition-base),
|
|
207
|
+
box-shadow var(--wr-transition-base);
|
|
208
|
+
|
|
209
|
+
// The frame carries the focus state, so the field inside draws none of its
|
|
210
|
+
// own: no border, no ring, no fill, in every state.
|
|
211
|
+
&:focus-within {
|
|
212
|
+
--wr-comment-composer-border: var(--wr-color-primary);
|
|
213
|
+
--wr-comment-composer-ring: color-mix(in srgb, var(--wr-color-primary) 15%, transparent);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
&--disabled {
|
|
217
|
+
--wr-comment-composer-bg: var(--wr-color-fill);
|
|
218
|
+
|
|
219
|
+
cursor: not-allowed;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
&--readonly {
|
|
223
|
+
--wr-comment-composer-bg: var(--wr-color-fill);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
&__field {
|
|
227
|
+
flex: 1 1 auto;
|
|
228
|
+
min-width: 0;
|
|
229
|
+
|
|
230
|
+
&.wr-textarea,
|
|
231
|
+
&.wr-textarea--focused,
|
|
232
|
+
&.wr-textarea--disabled,
|
|
233
|
+
&.wr-textarea--readonly {
|
|
234
|
+
--wr-textarea-bg: transparent;
|
|
235
|
+
--wr-textarea-border: transparent;
|
|
236
|
+
--wr-textarea-ring: transparent;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
&__actions {
|
|
241
|
+
flex: 0 0 auto;
|
|
242
|
+
display: flex;
|
|
243
|
+
align-items: center;
|
|
244
|
+
gap: var(--wr-comment-composer-gap);
|
|
245
|
+
|
|
246
|
+
&:empty {
|
|
247
|
+
display: none;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
&__send {
|
|
252
|
+
flex: 0 0 auto;
|
|
253
|
+
width: var(--wr-comment-composer-send-size);
|
|
254
|
+
height: var(--wr-comment-composer-send-size);
|
|
255
|
+
padding: 0;
|
|
256
|
+
|
|
257
|
+
@include theme.touch-target;
|
|
258
|
+
|
|
259
|
+
.wr-icon__svg {
|
|
260
|
+
display: block;
|
|
261
|
+
width: 1.125rem;
|
|
262
|
+
height: 1.125rem;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// The glyph points toward the inline end, so it turns with the direction.
|
|
266
|
+
[dir='rtl'] & .wr-icon__svg {
|
|
267
|
+
transform: scaleX(-1);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
@use '../../theme/styles' as theme;
|
|
2
2
|
|
|
3
|
+
// Styles for the entry point this component's own markup renders: `confirm()`
|
|
4
|
+
// mounts two `wr-btn`, and a consumer who writes `@use 'ngwr/dialog'` never
|
|
5
|
+
// learns about them. Sass deduplicates `@use`, so an app that also loads the
|
|
6
|
+
// button by hand still emits it once.
|
|
7
|
+
@use '../../button/styles' as button-styles; // WrButton
|
|
8
|
+
|
|
3
9
|
// CDK Overlay base styles are usually loaded once by the consumer.
|
|
4
10
|
// We re-emit the minimal `cdk-overlay-*` rules so a standalone `@use
|
|
5
11
|
// 'ngwr/dialog'` works without manually pulling CDK's prebuilt CSS.
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { ComponentHarness, HarnessPredicate, TestKey } from "@angular/cdk/testing";
|
|
2
|
+
import { WrTextareaHarness } from "ngwr/textarea/testing";
|
|
3
|
+
/**
|
|
4
|
+
* @license
|
|
5
|
+
*
|
|
6
|
+
* Use of this source code is governed by an MIT-style license that can be
|
|
7
|
+
* found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE
|
|
8
|
+
*/
|
|
9
|
+
var WrCommentComposerHarness = class WrCommentComposerHarness extends ComponentHarness {
|
|
10
|
+
static hostSelector = "wr-comment-composer";
|
|
11
|
+
static with(options = {}) {
|
|
12
|
+
return new HarnessPredicate(WrCommentComposerHarness, options).addOption("value", options.value, (harness, value) => HarnessPredicate.stringMatches(harness.getValue(), value)).addOption("placeholder", options.placeholder, (harness, placeholder) => HarnessPredicate.stringMatches(harness.getPlaceholder(), placeholder)).addOption("disabled", options.disabled, async (harness, disabled) => await harness.isDisabled() === disabled);
|
|
13
|
+
}
|
|
14
|
+
field = this.locatorFor(WrTextareaHarness);
|
|
15
|
+
native = this.locatorFor("textarea.wr-textarea__native");
|
|
16
|
+
sendButton = this.locatorFor("button.wr-comment-composer__send");
|
|
17
|
+
async getField() {
|
|
18
|
+
return this.field();
|
|
19
|
+
}
|
|
20
|
+
async getValue() {
|
|
21
|
+
return (await this.field()).getValue();
|
|
22
|
+
}
|
|
23
|
+
async setValue(value) {
|
|
24
|
+
return (await this.field()).setValue(value);
|
|
25
|
+
}
|
|
26
|
+
async getPlaceholder() {
|
|
27
|
+
return (await this.field()).getPlaceholder();
|
|
28
|
+
}
|
|
29
|
+
async getSendLabel() {
|
|
30
|
+
return (await this.sendButton()).getAttribute("aria-label");
|
|
31
|
+
}
|
|
32
|
+
async isDisabled() {
|
|
33
|
+
return (await this.field()).isDisabled();
|
|
34
|
+
}
|
|
35
|
+
async isReadonly() {
|
|
36
|
+
return (await this.field()).isReadonly();
|
|
37
|
+
}
|
|
38
|
+
async isSending() {
|
|
39
|
+
return await (await this.host()).getAttribute("aria-busy") === "true";
|
|
40
|
+
}
|
|
41
|
+
async canSend() {
|
|
42
|
+
return !await (await this.sendButton()).getProperty("disabled");
|
|
43
|
+
}
|
|
44
|
+
async send() {
|
|
45
|
+
if (!await this.canSend()) throw new Error("WrCommentComposerHarness.send(): the send button is disabled - the draft is empty, or the composer is disabled, read-only or sending. Type something first, or assert canSend().");
|
|
46
|
+
return (await this.sendButton()).click();
|
|
47
|
+
}
|
|
48
|
+
async pressEnter() {
|
|
49
|
+
return (await this.native()).sendKeys(TestKey.ENTER);
|
|
50
|
+
}
|
|
51
|
+
async focus() {
|
|
52
|
+
return (await this.field()).focus();
|
|
53
|
+
}
|
|
54
|
+
async isFocused() {
|
|
55
|
+
return (await this.field()).isFocused();
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
export { WrCommentComposerHarness };
|
|
59
|
+
|
|
60
|
+
//# sourceMappingURL=ngwr-comment-testing.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ngwr-comment-testing.mjs","names":[],"sources":["../../../projects/lib/comment/testing/wr-comment-composer-harness.ts"],"sourcesContent":["/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { ComponentHarness, HarnessPredicate, TestKey } from '@angular/cdk/testing';\n\nimport { WrTextareaHarness } from 'ngwr/textarea/testing';\n\nimport type { WrCommentComposerHarnessFilters } from './types';\n\n/**\n * Test harness for `<wr-comment-composer>`: a chat composer, which is a\n * `<wr-textarea>` with a send button beside it.\n *\n * The field is driven through {@link WrTextareaHarness}, handed out by\n * {@link getField}, rather than re-queried here: one harness per element, so\n * the two cannot drift. What this one adds is the sending half - whether a\n * send would go through, and the two ways a user triggers one.\n *\n * Nothing here answers \"did it send\". The composer emits the text through an\n * output and keeps no record, so the spec listens to `(send)` on its own host.\n * What IS readable afterwards is the draft: with `clearOnSend` the field is\n * empty once the text is out, and {@link getValue} says so.\n *\n * There is no IME method. jsdom fires no composition events, and a harness\n * that set `isComposing` by hand would be testing its own flag; a spec that\n * needs that path dispatches the keydown itself.\n *\n * @example\n * ```ts\n * const composer = await loader.getHarness(WrCommentComposerHarness.with({ placeholder: 'Message' }));\n *\n * await composer.setValue('On my way');\n * await composer.pressEnter();\n * expect(await composer.getValue()).toBe('');\n * ```\n *\n * @see https://ngwr.dev/guides/testing\n */\nexport class WrCommentComposerHarness extends ComponentHarness {\n static hostSelector = 'wr-comment-composer';\n\n /** Build a predicate that narrows the query - pass to `getHarness` / `getAllHarnesses`. */\n static with(options: WrCommentComposerHarnessFilters = {}): HarnessPredicate<WrCommentComposerHarness> {\n return new HarnessPredicate(WrCommentComposerHarness, options)\n .addOption('value', options.value, (harness, value) => HarnessPredicate.stringMatches(harness.getValue(), value))\n .addOption('placeholder', options.placeholder, (harness, placeholder) =>\n HarnessPredicate.stringMatches(harness.getPlaceholder(), placeholder)\n )\n .addOption('disabled', options.disabled, async (harness, disabled) => (await harness.isDisabled()) === disabled);\n }\n\n private readonly field = this.locatorFor(WrTextareaHarness);\n // The one thing read past the field's harness: a keystroke has to land on\n // the native element, and `WrTextareaHarness` types values, not keys.\n private readonly native = this.locatorFor('textarea.wr-textarea__native');\n private readonly sendButton = this.locatorFor('button.wr-comment-composer__send');\n\n /** The field itself, for everything `WrTextareaHarness` already answers. */\n async getField(): Promise<WrTextareaHarness> {\n return this.field();\n }\n\n /** The draft currently in the field. */\n async getValue(): Promise<string> {\n return (await this.field()).getValue();\n }\n\n /**\n * Type a draft in, through the field's own harness, so a disabled or\n * read-only composer throws the same way the field would.\n */\n async setValue(value: string): Promise<void> {\n return (await this.field()).setValue(value);\n }\n\n /** The field's placeholder. */\n async getPlaceholder(): Promise<string> {\n return (await this.field()).getPlaceholder();\n }\n\n /** The send button's accessible name - `sendLabel`, or the catalog's `comment.send`. */\n async getSendLabel(): Promise<string | null> {\n return (await this.sendButton()).getAttribute('aria-label');\n }\n\n /** Whether the field refuses input - the native element's own `disabled`. */\n async isDisabled(): Promise<boolean> {\n return (await this.field()).isDisabled();\n }\n\n /** Whether the field shows its draft but refuses edits. */\n async isReadonly(): Promise<boolean> {\n return (await this.field()).isReadonly();\n }\n\n /** Whether a send is in flight, from the host's `aria-busy`. */\n async isSending(): Promise<boolean> {\n return (await (await this.host()).getAttribute('aria-busy')) === 'true';\n }\n\n /**\n * Whether a send would go through right now, from the send button's own\n * `disabled`: it is off for whitespace-only text and under `disabled`,\n * `readonly` and `sending` alike, which is the same gate Enter checks.\n */\n async canSend(): Promise<boolean> {\n return !(await (await this.sendButton()).getProperty<boolean>('disabled'));\n }\n\n /**\n * Click the send button. Throws when a send could not go through, rather than\n * clicking a disabled button and reporting nothing: assert {@link canSend}\n * instead where that is the point.\n */\n async send(): Promise<void> {\n if (!(await this.canSend())) {\n throw new Error(\n 'WrCommentComposerHarness.send(): the send button is disabled - the draft is empty, or the composer is ' +\n 'disabled, read-only or sending. Type something first, or assert canSend().'\n );\n }\n return (await this.sendButton()).click();\n }\n\n /**\n * Press Enter in the field, exactly as a user would: with `enterSends` it\n * sends, and a Shift+Enter is not this method. Nothing is thrown when the\n * send is refused, because that refusal is behaviour worth asserting.\n */\n async pressEnter(): Promise<void> {\n return (await this.native()).sendKeys(TestKey.ENTER);\n }\n\n /** Move keyboard focus into the field. */\n async focus(): Promise<void> {\n return (await this.field()).focus();\n }\n\n /** Whether the field holds focus. */\n async isFocused(): Promise<boolean> {\n return (await this.field()).isFocused();\n }\n}\n"],"mappings":";;;;;;;;AA0CA,IAAa,2BAAb,MAAa,iCAAiC,iBAAgB;CAC5D,OAAO,eAAe;CAGtB,OAAO,KAAK,UAA2C,CAAA,GAAE;EACvD,OAAO,IAAI,iBAAiB,0BAA0B,OAAO,CAAC,CAC3D,UAAU,SAAS,QAAQ,QAAQ,SAAS,UAAU,iBAAiB,cAAc,QAAQ,SAAQ,GAAI,KAAK,CAAC,CAAC,CAChH,UAAU,eAAe,QAAQ,cAAc,SAAS,gBACvD,iBAAiB,cAAc,QAAQ,eAAc,GAAI,WAAW,CAAC,CACtE,CACA,UAAU,YAAY,QAAQ,UAAU,OAAO,SAAS,aAAc,MAAM,QAAQ,WAAU,MAAQ,QAAQ;CACnH;CAEiB,QAAQ,KAAK,WAAW,iBAAiB;CAGzC,SAAS,KAAK,WAAW,8BAA8B;CACvD,aAAa,KAAK,WAAW,kCAAkC;CAGhF,MAAM,WAAQ;EACZ,OAAO,KAAK,MAAK;CACnB;CAGA,MAAM,WAAQ;EACZ,QAAQ,MAAM,KAAK,MAAK,EAAA,CAAI,SAAQ;CACtC;CAMA,MAAM,SAAS,OAAa;EAC1B,QAAQ,MAAM,KAAK,MAAK,EAAA,CAAI,SAAS,KAAK;CAC5C;CAGA,MAAM,iBAAc;EAClB,QAAQ,MAAM,KAAK,MAAK,EAAA,CAAI,eAAc;CAC5C;CAGA,MAAM,eAAY;EAChB,QAAQ,MAAM,KAAK,WAAU,EAAA,CAAI,aAAa,YAAY;CAC5D;CAGA,MAAM,aAAU;EACd,QAAQ,MAAM,KAAK,MAAK,EAAA,CAAI,WAAU;CACxC;CAGA,MAAM,aAAU;EACd,QAAQ,MAAM,KAAK,MAAK,EAAA,CAAI,WAAU;CACxC;CAGA,MAAM,YAAS;EACb,OAAQ,OAAO,MAAM,KAAK,KAAI,EAAA,CAAI,aAAa,WAAW,MAAO;CACnE;CAOA,MAAM,UAAO;EACX,OAAO,CAAE,OAAO,MAAM,KAAK,WAAU,EAAA,CAAI,YAAqB,UAAU;CAC1E;CAOA,MAAM,OAAI;EACR,IAAI,CAAE,MAAM,KAAK,QAAO,GACtB,MAAM,IAAI,MACR,kLAC8E;EAGlF,QAAQ,MAAM,KAAK,WAAU,EAAA,CAAI,MAAK;CACxC;CAOA,MAAM,aAAU;EACd,QAAQ,MAAM,KAAK,OAAM,EAAA,CAAI,SAAS,QAAQ,KAAK;CACrD;CAGA,MAAM,QAAK;EACT,QAAQ,MAAM,KAAK,MAAK,EAAA,CAAI,MAAK;CACnC;CAGA,MAAM,YAAS;EACb,QAAQ,MAAM,KAAK,MAAK,EAAA,CAAI,UAAS;CACvC"}
|