@api-client/ui 0.5.6 → 0.5.8
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/.cursor/rules/html-and-css-best-practices.mdc +63 -0
- package/.cursor/rules/lit-best-practices.mdc +78 -0
- package/.github/instructions/html-and-css-best-practices.instructions.md +70 -0
- package/.github/instructions/lit-best-practices.instructions.md +86 -0
- package/build/src/elements/currency/currency-picker.d.ts +10 -0
- package/build/src/elements/currency/currency-picker.d.ts.map +1 -0
- package/build/src/elements/currency/currency-picker.js +27 -0
- package/build/src/elements/currency/currency-picker.js.map +1 -0
- package/build/src/elements/currency/internals/Picker.d.ts +311 -0
- package/build/src/elements/currency/internals/Picker.d.ts.map +1 -0
- package/build/src/elements/currency/internals/Picker.js +857 -0
- package/build/src/elements/currency/internals/Picker.js.map +1 -0
- package/build/src/elements/currency/internals/Picker.styles.d.ts +3 -0
- package/build/src/elements/currency/internals/Picker.styles.d.ts.map +1 -0
- package/build/src/elements/currency/internals/Picker.styles.js +58 -0
- package/build/src/elements/currency/internals/Picker.styles.js.map +1 -0
- package/build/src/elements/mention-textarea/internals/MentionTextArea.d.ts +216 -0
- package/build/src/elements/mention-textarea/internals/MentionTextArea.d.ts.map +1 -0
- package/build/src/elements/mention-textarea/internals/MentionTextArea.js +1037 -0
- package/build/src/elements/mention-textarea/internals/MentionTextArea.js.map +1 -0
- package/build/src/elements/mention-textarea/internals/MentionTextArea.styles.d.ts +3 -0
- package/build/src/elements/mention-textarea/internals/MentionTextArea.styles.d.ts.map +1 -0
- package/build/src/elements/mention-textarea/internals/MentionTextArea.styles.js +274 -0
- package/build/src/elements/mention-textarea/internals/MentionTextArea.styles.js.map +1 -0
- package/build/src/elements/mention-textarea/ui-mention-textarea.d.ts +13 -0
- package/build/src/elements/mention-textarea/ui-mention-textarea.d.ts.map +1 -0
- package/build/src/elements/mention-textarea/ui-mention-textarea.js +28 -0
- package/build/src/elements/mention-textarea/ui-mention-textarea.js.map +1 -0
- package/build/src/md/button/internals/base.d.ts +1 -0
- package/build/src/md/button/internals/base.d.ts.map +1 -1
- package/build/src/md/button/internals/base.js +7 -0
- package/build/src/md/button/internals/base.js.map +1 -1
- package/build/src/md/chip/internals/Chip.styles.d.ts.map +1 -1
- package/build/src/md/chip/internals/Chip.styles.js +2 -0
- package/build/src/md/chip/internals/Chip.styles.js.map +1 -1
- package/build/src/md/date-picker/internals/DatePicker.styles.d.ts.map +1 -1
- package/build/src/md/date-picker/internals/DatePicker.styles.js +73 -0
- package/build/src/md/date-picker/internals/DatePicker.styles.js.map +1 -1
- package/build/src/md/date-picker/internals/DatePickerCalendar.d.ts +164 -51
- package/build/src/md/date-picker/internals/DatePickerCalendar.d.ts.map +1 -1
- package/build/src/md/date-picker/internals/DatePickerCalendar.js +660 -368
- package/build/src/md/date-picker/internals/DatePickerCalendar.js.map +1 -1
- package/build/src/md/date-picker/ui-date-picker-input.d.ts +65 -13
- package/build/src/md/date-picker/ui-date-picker-input.d.ts.map +1 -1
- package/build/src/md/date-picker/ui-date-picker-input.js +143 -76
- package/build/src/md/date-picker/ui-date-picker-input.js.map +1 -1
- package/build/src/md/date-picker/ui-date-picker-modal-input.d.ts +76 -17
- package/build/src/md/date-picker/ui-date-picker-modal-input.d.ts.map +1 -1
- package/build/src/md/date-picker/ui-date-picker-modal-input.js +192 -127
- package/build/src/md/date-picker/ui-date-picker-modal-input.js.map +1 -1
- package/build/src/md/date-picker/ui-date-picker-modal.d.ts +63 -15
- package/build/src/md/date-picker/ui-date-picker-modal.d.ts.map +1 -1
- package/build/src/md/date-picker/ui-date-picker-modal.js +143 -64
- package/build/src/md/date-picker/ui-date-picker-modal.js.map +1 -1
- package/demo/elements/currency/index.html +91 -0
- package/demo/elements/currency/index.ts +272 -0
- package/demo/elements/index.html +6 -0
- package/demo/elements/mention-textarea/index.html +19 -0
- package/demo/elements/mention-textarea/index.ts +205 -0
- package/demo/md/date-picker/date-picker.ts +138 -103
- package/package.json +2 -2
- package/src/elements/currency/currency-picker.ts +14 -0
- package/src/elements/currency/internals/Picker.styles.ts +58 -0
- package/src/elements/currency/internals/Picker.ts +846 -0
- package/src/elements/mention-textarea/internals/MentionTextArea.styles.ts +274 -0
- package/src/elements/mention-textarea/internals/MentionTextArea.ts +1036 -0
- package/src/elements/mention-textarea/ui-mention-textarea.ts +18 -0
- package/src/md/button/internals/base.ts +7 -0
- package/src/md/chip/internals/Chip.styles.ts +2 -0
- package/src/md/date-picker/internals/DatePicker.styles.ts +73 -0
- package/src/md/date-picker/internals/DatePickerCalendar.ts +643 -309
- package/src/md/date-picker/ui-date-picker-input.ts +110 -49
- package/src/md/date-picker/ui-date-picker-modal-input.ts +168 -99
- package/src/md/date-picker/ui-date-picker-modal.ts +136 -53
- package/test/README.md +3 -2
- package/test/elements/currency/CurrencyPicker.accessibility.test.ts +328 -0
- package/test/elements/currency/CurrencyPicker.core.test.ts +318 -0
- package/test/elements/currency/CurrencyPicker.integration.test.ts +482 -0
- package/test/elements/currency/CurrencyPicker.test.ts +486 -0
- package/test/elements/mention-textarea/MentionTextArea.basic.test.ts +63 -0
- package/test/elements/mention-textarea/MentionTextArea.test.ts +321 -0
- package/tsconfig.json +1 -1
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
import { fixture, assert, html } from '@open-wc/testing'
|
|
2
|
+
import CurrencyPicker from '../../../src/elements/currency/internals/Picker.js'
|
|
3
|
+
import '../../../src/elements/currency/currency-picker.js'
|
|
4
|
+
|
|
5
|
+
describe('CurrencyPicker - Core Tests', () => {
|
|
6
|
+
async function basicFixture(): Promise<CurrencyPicker> {
|
|
7
|
+
return fixture(html`<currency-picker></currency-picker>`)
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
async function multiSelectFixture(): Promise<CurrencyPicker> {
|
|
11
|
+
return fixture(html`<currency-picker multi></currency-picker>`)
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
async function allowedCurrenciesFixture(): Promise<CurrencyPicker> {
|
|
15
|
+
return fixture(html`<currency-picker .allowedCurrencies="${['USD', 'EUR', 'GBP']}"></currency-picker>`)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
async function requiredFixture(): Promise<CurrencyPicker> {
|
|
19
|
+
return fixture(html`<currency-picker required></currency-picker>`)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
describe('Basic Functionality', () => {
|
|
23
|
+
let picker: CurrencyPicker
|
|
24
|
+
|
|
25
|
+
beforeEach(async () => {
|
|
26
|
+
picker = await basicFixture()
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
it('should create element', () => {
|
|
30
|
+
assert.instanceOf(picker, CurrencyPicker)
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
it('should have default properties', () => {
|
|
34
|
+
assert.deepEqual(picker.selected, [])
|
|
35
|
+
assert.deepEqual(picker.allowedCurrencies, [])
|
|
36
|
+
assert.equal(picker.label, 'Add Currency')
|
|
37
|
+
assert.isFalse(picker.multi)
|
|
38
|
+
assert.isFalse(picker.required)
|
|
39
|
+
assert.isFalse(picker.disabled)
|
|
40
|
+
assert.isTrue(picker.showErrors)
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
it('should render currency selector', () => {
|
|
44
|
+
const selector = picker.shadowRoot?.querySelector('md-outlined-select')
|
|
45
|
+
assert.exists(selector)
|
|
46
|
+
assert.equal(selector?.getAttribute('label'), 'Add Currency')
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
it('should populate currency options', async () => {
|
|
50
|
+
await picker.updateComplete
|
|
51
|
+
const options = picker.shadowRoot?.querySelectorAll('md-select-option')
|
|
52
|
+
assert.exists(options)
|
|
53
|
+
assert.isAtLeast(options?.length || 0, 25) // Should have popular currencies
|
|
54
|
+
})
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
describe('Currency Selection', () => {
|
|
58
|
+
let picker: CurrencyPicker
|
|
59
|
+
|
|
60
|
+
beforeEach(async () => {
|
|
61
|
+
picker = await basicFixture()
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
it('should select a currency', async () => {
|
|
65
|
+
picker.selected = ['USD']
|
|
66
|
+
await picker.updateComplete
|
|
67
|
+
assert.deepEqual(picker.selected, ['USD'])
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
it('should get selected currencies', async () => {
|
|
71
|
+
// Test single-select mode (default)
|
|
72
|
+
picker.selected = ['USD', 'EUR']
|
|
73
|
+
await picker.updateComplete
|
|
74
|
+
|
|
75
|
+
const currencies = picker.getSelectedCurrencies()
|
|
76
|
+
assert.equal(currencies.length, 1, 'Should only allow one selection in single-select mode')
|
|
77
|
+
assert.equal(currencies[0].code, 'USD')
|
|
78
|
+
|
|
79
|
+
// Test multi-select mode
|
|
80
|
+
picker.multi = true
|
|
81
|
+
picker.selected = ['USD', 'EUR']
|
|
82
|
+
await picker.updateComplete
|
|
83
|
+
|
|
84
|
+
const currenciesMulti = picker.getSelectedCurrencies()
|
|
85
|
+
assert.equal(currenciesMulti.length, 2)
|
|
86
|
+
assert.equal(currenciesMulti[0].code, 'USD')
|
|
87
|
+
assert.equal(currenciesMulti[1].code, 'EUR')
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
it('should clear selection', async () => {
|
|
91
|
+
picker.selected = ['USD', 'EUR']
|
|
92
|
+
await picker.updateComplete
|
|
93
|
+
|
|
94
|
+
picker.clearSelection()
|
|
95
|
+
await picker.updateComplete
|
|
96
|
+
|
|
97
|
+
assert.deepEqual(picker.selected, [])
|
|
98
|
+
})
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
describe('Multi-select Mode', () => {
|
|
102
|
+
let picker: CurrencyPicker
|
|
103
|
+
|
|
104
|
+
beforeEach(async () => {
|
|
105
|
+
picker = await multiSelectFixture()
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
it('should allow multiple selections', async () => {
|
|
109
|
+
picker.selected = ['USD', 'EUR', 'GBP']
|
|
110
|
+
await picker.updateComplete
|
|
111
|
+
|
|
112
|
+
assert.equal(picker.selected.length, 3)
|
|
113
|
+
assert.include(picker.selected, 'USD')
|
|
114
|
+
assert.include(picker.selected, 'EUR')
|
|
115
|
+
assert.include(picker.selected, 'GBP')
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
it('should render chips for selected currencies', async () => {
|
|
119
|
+
picker.selected = ['USD', 'EUR']
|
|
120
|
+
await picker.updateComplete
|
|
121
|
+
|
|
122
|
+
const chipSet = picker.shadowRoot?.querySelector('ui-chip-set')
|
|
123
|
+
const chips = picker.shadowRoot?.querySelectorAll('ui-chip')
|
|
124
|
+
|
|
125
|
+
assert.exists(chipSet)
|
|
126
|
+
assert.equal(chips?.length, 2)
|
|
127
|
+
})
|
|
128
|
+
})
|
|
129
|
+
|
|
130
|
+
describe('Allowed Currencies', () => {
|
|
131
|
+
let picker: CurrencyPicker
|
|
132
|
+
|
|
133
|
+
beforeEach(async () => {
|
|
134
|
+
picker = await allowedCurrenciesFixture()
|
|
135
|
+
})
|
|
136
|
+
|
|
137
|
+
it('should filter options based on allowed currencies', async () => {
|
|
138
|
+
await picker.updateComplete
|
|
139
|
+
|
|
140
|
+
const options = picker.shadowRoot?.querySelectorAll('md-select-option[value]')
|
|
141
|
+
const optionValues = Array.from(options || [])
|
|
142
|
+
.map((option) => option.getAttribute('value'))
|
|
143
|
+
.filter(Boolean)
|
|
144
|
+
|
|
145
|
+
// Should only have USD, EUR, GBP (plus empty option)
|
|
146
|
+
assert.isAtMost(optionValues.length, 3)
|
|
147
|
+
assert.include(optionValues, 'USD')
|
|
148
|
+
assert.include(optionValues, 'EUR')
|
|
149
|
+
assert.include(optionValues, 'GBP')
|
|
150
|
+
})
|
|
151
|
+
})
|
|
152
|
+
|
|
153
|
+
describe('Validation and Error Handling', () => {
|
|
154
|
+
let picker: CurrencyPicker
|
|
155
|
+
|
|
156
|
+
beforeEach(async () => {
|
|
157
|
+
picker = await requiredFixture()
|
|
158
|
+
})
|
|
159
|
+
|
|
160
|
+
it('should show error state in UI when showErrors is true', async () => {
|
|
161
|
+
picker.showErrors = true
|
|
162
|
+
|
|
163
|
+
// First set a value, then clear it to trigger validation
|
|
164
|
+
picker.selected = ['USD']
|
|
165
|
+
await picker.updateComplete
|
|
166
|
+
|
|
167
|
+
picker.selected = [] // This should trigger required validation error
|
|
168
|
+
await picker.updateComplete
|
|
169
|
+
|
|
170
|
+
const errorElement = picker.shadowRoot?.querySelector('.error')
|
|
171
|
+
const selectElement = picker.shadowRoot?.querySelector('md-outlined-select')
|
|
172
|
+
const isInvalid = !picker.validity.valid
|
|
173
|
+
const hasAriaInvalid = selectElement?.getAttribute('aria-invalid') === 'true'
|
|
174
|
+
|
|
175
|
+
assert.exists(errorElement, 'Error element should be displayed')
|
|
176
|
+
assert.isTrue(isInvalid, 'Component should be in invalid state')
|
|
177
|
+
assert.isTrue(hasAriaInvalid, 'Select element should have aria-invalid=true')
|
|
178
|
+
assert.isTrue(picker.matches(':invalid'), 'Has the :invalid pseudo-class')
|
|
179
|
+
})
|
|
180
|
+
|
|
181
|
+
it('should not show error UI when showErrors is false', async () => {
|
|
182
|
+
picker.showErrors = false
|
|
183
|
+
picker.selected = [] // Directly set to empty to trigger required validation error
|
|
184
|
+
await picker.updateComplete
|
|
185
|
+
|
|
186
|
+
const errorElement = picker.shadowRoot?.querySelector('.error')
|
|
187
|
+
assert.isNull(errorElement)
|
|
188
|
+
})
|
|
189
|
+
|
|
190
|
+
it('should validate invalid currency codes', async () => {
|
|
191
|
+
picker.selected = ['INVALID_CODE']
|
|
192
|
+
await picker.updateComplete
|
|
193
|
+
|
|
194
|
+
// Should filter out invalid codes
|
|
195
|
+
assert.notInclude(picker.selected, 'INVALID_CODE')
|
|
196
|
+
})
|
|
197
|
+
|
|
198
|
+
it('should prevent multiple selections in single-select mode', async () => {
|
|
199
|
+
picker.multi = false
|
|
200
|
+
picker.selected = ['USD', 'EUR'] // Multiple selections in single-select mode
|
|
201
|
+
await picker.updateComplete
|
|
202
|
+
|
|
203
|
+
// Should only keep one selection when multi=false
|
|
204
|
+
assert.equal(picker.selected.length, 1)
|
|
205
|
+
assert.equal(picker.selected[0], 'USD') // Should keep the first one
|
|
206
|
+
})
|
|
207
|
+
})
|
|
208
|
+
|
|
209
|
+
describe('Form Integration', () => {
|
|
210
|
+
it('should be form-associated', () => {
|
|
211
|
+
assert.isTrue(CurrencyPicker.formAssociated)
|
|
212
|
+
})
|
|
213
|
+
|
|
214
|
+
it('should handle form state restoration', async () => {
|
|
215
|
+
// Test single-select mode (default)
|
|
216
|
+
const pickerSingle = await basicFixture()
|
|
217
|
+
pickerSingle.formStateRestoreCallback('USD,EUR')
|
|
218
|
+
await pickerSingle.updateComplete
|
|
219
|
+
|
|
220
|
+
assert.deepEqual(pickerSingle.selected, ['USD'], 'Single-select should only restore first value')
|
|
221
|
+
|
|
222
|
+
// Test multi-select mode
|
|
223
|
+
const pickerMulti = await multiSelectFixture()
|
|
224
|
+
pickerMulti.formStateRestoreCallback('USD,EUR')
|
|
225
|
+
await pickerMulti.updateComplete
|
|
226
|
+
|
|
227
|
+
assert.deepEqual(pickerMulti.selected, ['USD', 'EUR'], 'Multi-select should restore all values')
|
|
228
|
+
})
|
|
229
|
+
|
|
230
|
+
it('should handle form reset', async () => {
|
|
231
|
+
const picker = await basicFixture()
|
|
232
|
+
picker.selected = ['USD', 'EUR']
|
|
233
|
+
await picker.updateComplete
|
|
234
|
+
|
|
235
|
+
picker.formResetCallback()
|
|
236
|
+
await picker.updateComplete
|
|
237
|
+
|
|
238
|
+
assert.deepEqual(picker.selected, [])
|
|
239
|
+
})
|
|
240
|
+
})
|
|
241
|
+
|
|
242
|
+
describe('Accessibility', () => {
|
|
243
|
+
let picker: CurrencyPicker
|
|
244
|
+
|
|
245
|
+
beforeEach(async () => {
|
|
246
|
+
picker = await basicFixture()
|
|
247
|
+
})
|
|
248
|
+
|
|
249
|
+
it('should have proper ARIA attributes', async () => {
|
|
250
|
+
await picker.updateComplete
|
|
251
|
+
|
|
252
|
+
const container = picker.shadowRoot?.querySelector('.currency-picker')
|
|
253
|
+
const select = picker.shadowRoot?.querySelector('md-outlined-select')
|
|
254
|
+
|
|
255
|
+
assert.equal(container?.getAttribute('role'), 'group')
|
|
256
|
+
assert.exists(container?.getAttribute('aria-label'))
|
|
257
|
+
assert.exists(select?.getAttribute('aria-invalid'))
|
|
258
|
+
})
|
|
259
|
+
|
|
260
|
+
it('should update ARIA label based on selection', async () => {
|
|
261
|
+
picker.selected = ['USD']
|
|
262
|
+
await picker.updateComplete
|
|
263
|
+
|
|
264
|
+
const container = picker.shadowRoot?.querySelector('.currency-picker')
|
|
265
|
+
const ariaLabel = container?.getAttribute('aria-label')
|
|
266
|
+
|
|
267
|
+
assert.include(ariaLabel || '', 'USD selected')
|
|
268
|
+
})
|
|
269
|
+
|
|
270
|
+
it('should have error announcements', async () => {
|
|
271
|
+
picker.showErrors = true
|
|
272
|
+
picker.required = true
|
|
273
|
+
picker.clearSelection()
|
|
274
|
+
await picker.updateComplete
|
|
275
|
+
|
|
276
|
+
const errorElement = picker.shadowRoot?.querySelector('.error')
|
|
277
|
+
assert.equal(errorElement?.getAttribute('role'), 'alert')
|
|
278
|
+
assert.equal(errorElement?.getAttribute('aria-live'), 'polite')
|
|
279
|
+
})
|
|
280
|
+
})
|
|
281
|
+
|
|
282
|
+
describe('Edge Cases', () => {
|
|
283
|
+
let picker: CurrencyPicker
|
|
284
|
+
|
|
285
|
+
beforeEach(async () => {
|
|
286
|
+
picker = await basicFixture()
|
|
287
|
+
})
|
|
288
|
+
|
|
289
|
+
it('should handle empty arrays gracefully', async () => {
|
|
290
|
+
picker.selected = []
|
|
291
|
+
picker.allowedCurrencies = []
|
|
292
|
+
await picker.updateComplete
|
|
293
|
+
|
|
294
|
+
assert.deepEqual(picker.selected, [])
|
|
295
|
+
assert.deepEqual(picker.getSelectedCurrencies(), [])
|
|
296
|
+
})
|
|
297
|
+
|
|
298
|
+
it('should handle disabled state', async () => {
|
|
299
|
+
picker.disabled = true
|
|
300
|
+
await picker.updateComplete
|
|
301
|
+
|
|
302
|
+
const select = picker.shadowRoot?.querySelector('md-outlined-select')
|
|
303
|
+
assert.isTrue(select?.hasAttribute('disabled'))
|
|
304
|
+
})
|
|
305
|
+
|
|
306
|
+
it('should handle disconnection/reconnection', async () => {
|
|
307
|
+
picker.selected = ['USD']
|
|
308
|
+
await picker.updateComplete
|
|
309
|
+
|
|
310
|
+
// Disconnect and reconnect
|
|
311
|
+
picker.remove()
|
|
312
|
+
document.body.appendChild(picker)
|
|
313
|
+
await picker.updateComplete
|
|
314
|
+
|
|
315
|
+
assert.deepEqual(picker.selected, ['USD'])
|
|
316
|
+
})
|
|
317
|
+
})
|
|
318
|
+
})
|