@api-client/ui 0.5.15 → 0.5.17

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.
Files changed (34) hide show
  1. package/.github/instructions/lit-best-practices.instructions.md +1 -0
  2. package/build/src/elements/code-editor/code-editor.d.ts +1 -1
  3. package/build/src/elements/code-editor/code-editor.d.ts.map +1 -1
  4. package/build/src/elements/code-editor/code-editor.js.map +1 -1
  5. package/build/src/elements/code-editor/internals/{PlaceholderWidget.d.ts → ChipWidget.d.ts} +1 -1
  6. package/build/src/elements/code-editor/internals/ChipWidget.d.ts.map +1 -0
  7. package/build/src/elements/code-editor/internals/{PlaceholderWidget.js → ChipWidget.js} +2 -2
  8. package/build/src/elements/code-editor/internals/ChipWidget.js.map +1 -0
  9. package/build/src/elements/code-editor/internals/CodeEditor.d.ts +11 -1
  10. package/build/src/elements/code-editor/internals/CodeEditor.d.ts.map +1 -1
  11. package/build/src/elements/code-editor/internals/CodeEditor.js +58 -8
  12. package/build/src/elements/code-editor/internals/CodeEditor.js.map +1 -1
  13. package/build/src/elements/code-editor/internals/CodeEditor.styles.d.ts.map +1 -1
  14. package/build/src/elements/code-editor/internals/CodeEditor.styles.js +8 -2
  15. package/build/src/elements/code-editor/internals/CodeEditor.styles.js.map +1 -1
  16. package/build/src/elements/code-editor/internals/SuggestionMatchDecorator.d.ts.map +1 -1
  17. package/build/src/elements/code-editor/internals/SuggestionMatchDecorator.js +4 -4
  18. package/build/src/elements/code-editor/internals/SuggestionMatchDecorator.js.map +1 -1
  19. package/build/src/elements/code-editor/internals/types.d.ts +4 -0
  20. package/build/src/elements/code-editor/internals/types.d.ts.map +1 -1
  21. package/build/src/elements/code-editor/internals/types.js.map +1 -1
  22. package/demo/elements/code-editor/CodeEditorDemo.ts +53 -83
  23. package/package.json +1 -1
  24. package/src/elements/code-editor/code-editor.ts +6 -1
  25. package/src/elements/code-editor/internals/{PlaceholderWidget.ts → ChipWidget.ts} +1 -1
  26. package/src/elements/code-editor/internals/CodeEditor.styles.ts +8 -2
  27. package/src/elements/code-editor/internals/CodeEditor.ts +54 -8
  28. package/src/elements/code-editor/internals/SuggestionMatchDecorator.ts +4 -5
  29. package/src/elements/code-editor/internals/types.ts +5 -0
  30. package/test/elements/autocomplete/autocomplete-input.spec.ts +71 -70
  31. package/test/elements/code-editor/code-editor.accessibility.test.ts +325 -0
  32. package/test/elements/code-editor/code-editor.test.ts +576 -0
  33. package/build/src/elements/code-editor/internals/PlaceholderWidget.d.ts.map +0 -1
  34. package/build/src/elements/code-editor/internals/PlaceholderWidget.js.map +0 -1
@@ -1,5 +1,4 @@
1
- /* eslint-disable @typescript-eslint/no-unused-expressions */
2
- import { fixture, expect, html, oneEvent, nextFrame, aTimeout, assert } from '@open-wc/testing'
1
+ import { fixture, assert, html, oneEvent, nextFrame, aTimeout } from '@open-wc/testing'
3
2
  import sinon from 'sinon'
4
3
 
5
4
  import { AutocompleteInput } from '../../../src/elements/autocomplete/autocomplete-input.js'
@@ -67,10 +66,10 @@ describe('AutocompleteInput', () => {
67
66
  const input = getSlottedInput(el)
68
67
  const suggestions = getSlottedSuggestions(el)
69
68
 
70
- expect(input).to.exist
71
- expect(input?.id).to.equal('test-input')
72
- expect(suggestions).to.exist
73
- expect(suggestions?.popover).to.equal('manual')
69
+ assert.ok(input)
70
+ assert.equal(input?.id, 'test-input')
71
+ assert.ok(suggestions)
72
+ assert.equal(suggestions?.popover, 'manual')
74
73
  })
75
74
 
76
75
  it('generates an ID for the input if not provided and sets up anchor names', async () => {
@@ -86,20 +85,20 @@ describe('AutocompleteInput', () => {
86
85
  const input = getSlottedInput(el)!
87
86
  const suggestions = getSlottedSuggestions(el)!
88
87
 
89
- expect(input.id).to.match(/^autocomplete-input-/)
88
+ assert.match(input.id, /^autocomplete-input-/)
90
89
  // @ts-expect-error _inputId is a protected member
91
90
  const internalInputId = el.inputId
92
- expect(input.style.getPropertyValue('anchor-name')).to.equal(`--${internalInputId}`, 'input has anchor name')
91
+ assert.equal(input.style.getPropertyValue('anchor-name'), `--${internalInputId}`, 'input has anchor name')
93
92
  const anchorValue = getComputedStyle(suggestions).getPropertyValue('position-anchor')
94
- expect(anchorValue).to.equal(`--${internalInputId}`, 'suggestions element has position-anchor')
93
+ assert.equal(anchorValue, `--${internalInputId}`, 'suggestions element has position-anchor')
95
94
  })
96
95
 
97
96
  it('popover is initially closed', async () => {
98
97
  const el = await basicFixture()
99
98
  await el.updateComplete
100
99
  const suggestions = getSlottedSuggestions(el)
101
- expect(suggestions?.matches(':popover-open')).to.be.false
102
- expect(el.opened).to.be.false
100
+ assert.isFalse(suggestions?.matches(':popover-open'))
101
+ assert.isFalse(el.opened)
103
102
  })
104
103
  })
105
104
 
@@ -113,8 +112,8 @@ describe('AutocompleteInput', () => {
113
112
  input.focus()
114
113
  await nextFrame() // Allow focus event to propagate and popover to open
115
114
 
116
- expect(suggestions.matches(':popover-open')).to.be.true
117
- expect(el.opened).to.be.true
115
+ assert.isTrue(suggestions.matches(':popover-open'))
116
+ assert.isTrue(el.opened)
118
117
  })
119
118
 
120
119
  it('does not open on input focus without suggestions', async () => {
@@ -126,7 +125,7 @@ describe('AutocompleteInput', () => {
126
125
  input.focus()
127
126
  await nextFrame()
128
127
 
129
- expect(suggestions.matches(':popover-open')).to.be.false
128
+ assert.isFalse(suggestions.matches(':popover-open'))
130
129
  })
131
130
 
132
131
  it('does not open on input focus if suggestions element is missing', async () => {
@@ -137,7 +136,7 @@ describe('AutocompleteInput', () => {
137
136
  input.focus()
138
137
  await nextFrame()
139
138
  // No suggestions ref, so opened should be false
140
- expect(el.opened).to.be.false
139
+ assert.isFalse(el.opened)
141
140
  })
142
141
 
143
142
  it('closes on input blur', async () => {
@@ -148,15 +147,15 @@ describe('AutocompleteInput', () => {
148
147
 
149
148
  input.focus()
150
149
  await nextFrame()
151
- expect(suggestions.matches(':popover-open')).to.be.true
150
+ assert.isTrue(suggestions.matches(':popover-open'))
152
151
 
153
152
  input.blur()
154
153
  // Blur handler uses requestAnimationFrame
155
154
  await aTimeout(0) // Wait for rAF
156
155
  await nextFrame() // Wait for potential re-render
157
156
 
158
- expect(suggestions.matches(':popover-open')).to.be.false
159
- expect(el.opened).to.be.false
157
+ assert.isFalse(suggestions.matches(':popover-open'))
158
+ assert.isFalse(el.opened)
160
159
  })
161
160
 
162
161
  it('closes on Escape key', async () => {
@@ -167,12 +166,12 @@ describe('AutocompleteInput', () => {
167
166
 
168
167
  input.focus()
169
168
  await nextFrame()
170
- expect(suggestions.matches(':popover-open')).to.be.true
169
+ assert.isTrue(suggestions.matches(':popover-open'))
171
170
 
172
171
  input.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape', bubbles: true, composed: true }))
173
172
  await nextFrame()
174
173
 
175
- expect(suggestions.matches(':popover-open')).to.be.false
174
+ assert.isFalse(suggestions.matches(':popover-open'))
176
175
  })
177
176
  })
178
177
 
@@ -195,7 +194,7 @@ describe('AutocompleteInput', () => {
195
194
  it('shows all items for empty query', () => {
196
195
  input.value = ''
197
196
  input.dispatchEvent(new Event('input', { bubbles: true, composed: true }))
198
- items.forEach((item) => expect(item.hidden).to.be.false)
197
+ items.forEach((item) => assert.isFalse(item.hidden))
199
198
  })
200
199
 
201
200
  it('filters items based on data-value', async () => {
@@ -203,9 +202,9 @@ describe('AutocompleteInput', () => {
203
202
  input.dispatchEvent(new Event('input', { bubbles: true, composed: true }))
204
203
  await nextFrame()
205
204
 
206
- expect(items[0].hidden).to.be.false // Apple
207
- expect(items[1].hidden).to.be.true // Banana
208
- expect(items[2].hidden).to.be.true // Cherry
205
+ assert.isFalse(items[0].hidden) // Apple
206
+ assert.isTrue(items[1].hidden) // Banana
207
+ assert.isTrue(items[2].hidden) // Cherry
209
208
  })
210
209
 
211
210
  it('filters items based on textContent if data-value is not present or does not match', async () => {
@@ -218,9 +217,9 @@ describe('AutocompleteInput', () => {
218
217
  input.dispatchEvent(new Event('input', { bubbles: true, composed: true }))
219
218
  await nextFrame()
220
219
 
221
- expect(items[0].hidden).to.be.false // Pineapple (via textContent)
222
- expect(items[1].hidden).to.be.true // Banana
223
- expect(items[2].hidden).to.be.true // Cherry
220
+ assert.isFalse(items[0].hidden) // Pineapple (via textContent)
221
+ assert.isTrue(items[1].hidden) // Banana
222
+ assert.isTrue(items[2].hidden) // Cherry
224
223
  })
225
224
 
226
225
  it('filters items based on data-index fields', async () => {
@@ -228,9 +227,9 @@ describe('AutocompleteInput', () => {
228
227
  input.dispatchEvent(new Event('input', { bubbles: true, composed: true }))
229
228
  await nextFrame()
230
229
 
231
- expect(items[0].hidden).to.be.true // Apple
232
- expect(items[1].hidden).to.be.true // Banana
233
- expect(items[2].hidden).to.be.false // Cherry
230
+ assert.isTrue(items[0].hidden) // Apple
231
+ assert.isTrue(items[1].hidden) // Banana
232
+ assert.isFalse(items[2].hidden) // Cherry
234
233
  })
235
234
 
236
235
  it('is case-insensitive', async () => {
@@ -238,9 +237,9 @@ describe('AutocompleteInput', () => {
238
237
  input.dispatchEvent(new Event('input', { bubbles: true, composed: true }))
239
238
  await nextFrame()
240
239
 
241
- expect(items[0].hidden).to.be.true // Apple
242
- expect(items[1].hidden).to.be.false // Banana
243
- expect(items[2].hidden).to.be.true // Cherry
240
+ assert.isTrue(items[0].hidden) // Apple
241
+ assert.isFalse(items[1].hidden) // Banana
242
+ assert.isTrue(items[2].hidden) // Cherry
244
243
  })
245
244
 
246
245
  it('closes popover if no items match', async () => {
@@ -249,8 +248,8 @@ describe('AutocompleteInput', () => {
249
248
  await nextFrame()
250
249
  await aTimeout(0) // filterSuggestions might call closeSuggestions, which might be async
251
250
 
252
- items.forEach((item) => expect(item.hidden).to.be.true)
253
- expect(suggestionsBox.matches(':popover-open')).to.be.false
251
+ items.forEach((item) => assert.isTrue(item.hidden))
252
+ assert.isFalse(suggestionsBox.matches(':popover-open'))
254
253
  })
255
254
  })
256
255
 
@@ -258,6 +257,7 @@ describe('AutocompleteInput', () => {
258
257
  let el: AutocompleteInput
259
258
  let input: HTMLInputElement
260
259
  let suggestionsBox: MdListbox
260
+ let notifySelectSpy: sinon.SinonSpy
261
261
 
262
262
  beforeEach(async () => {
263
263
  el = await basicFixture()
@@ -268,7 +268,7 @@ describe('AutocompleteInput', () => {
268
268
  // We spy on its methods.
269
269
  sinon.spy(suggestionsBox, 'highlightNext')
270
270
  sinon.spy(suggestionsBox, 'highlightPrevious')
271
- sinon.spy(suggestionsBox, 'notifySelect')
271
+ notifySelectSpy = sinon.spy(suggestionsBox, 'notifySelect')
272
272
  })
273
273
 
274
274
  afterEach(() => {
@@ -276,14 +276,14 @@ describe('AutocompleteInput', () => {
276
276
  })
277
277
 
278
278
  it('ArrowDown highlights next item and opens popover if closed', async () => {
279
- expect(suggestionsBox.matches(':popover-open')).to.be.false
279
+ assert.isFalse(suggestionsBox.matches(':popover-open'))
280
280
  input.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowDown', bubbles: true, composed: true }))
281
281
  // handleKeydown uses rAF if popover was closed
282
282
  await aTimeout(0) // for rAF
283
283
  await nextFrame() // for popover to open and highlight
284
284
 
285
- expect(suggestionsBox.matches(':popover-open')).to.be.true
286
- expect(suggestionsBox.highlightNext).to.have.been.calledOnce
285
+ assert.isTrue(suggestionsBox.matches(':popover-open'))
286
+ assert.isTrue((suggestionsBox.highlightNext as sinon.SinonSpy).calledOnce)
287
287
  })
288
288
 
289
289
  it('ArrowUp highlights previous item', async () => {
@@ -291,7 +291,7 @@ describe('AutocompleteInput', () => {
291
291
  await nextFrame()
292
292
  input.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowUp', bubbles: true, composed: true }))
293
293
  await nextFrame()
294
- expect(suggestionsBox.highlightPrevious).to.have.been.calledOnce
294
+ assert.isTrue((suggestionsBox.highlightPrevious as sinon.SinonSpy).calledOnce)
295
295
  })
296
296
 
297
297
  it('Enter selects highlighted item and dispatches event', async () => {
@@ -307,13 +307,14 @@ describe('AutocompleteInput', () => {
307
307
  input.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter', bubbles: true, composed: true }))
308
308
 
309
309
  const event = await eventPromise
310
- expect(suggestionsBox.notifySelect).to.have.been.calledWith(items[0])
311
- expect(event.detail.item).to.equal(items[0])
312
- expect(suggestionsBox.matches(':popover-open')).to.be.false // Popover should close
310
+
311
+ assert.isTrue((suggestionsBox.notifySelect as sinon.SinonSpy).calledWith(items[0]))
312
+ assert.equal(event.detail.item, items[0])
313
+ assert.isFalse(suggestionsBox.matches(':popover-open')) // Popover should close
313
314
  })
314
315
 
315
316
  it('Enter does nothing if popover closed or no item highlighted', async () => {
316
- expect(suggestionsBox.matches(':popover-open')).to.be.false
317
+ assert.isFalse(suggestionsBox.matches(':popover-open'))
317
318
  suggestionsBox.highlightListItem = null // Ensure no item is highlighted
318
319
 
319
320
  const spy = sinon.spy()
@@ -322,8 +323,8 @@ describe('AutocompleteInput', () => {
322
323
  input.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter', bubbles: true, composed: true }))
323
324
  await nextFrame()
324
325
 
325
- expect(spy).not.to.have.been.called
326
- expect(suggestionsBox.notifySelect).not.to.have.been.called
326
+ assert.isFalse(spy.called)
327
+ assert.isFalse(notifySelectSpy.called)
327
328
  })
328
329
  })
329
330
 
@@ -351,8 +352,8 @@ describe('AutocompleteInput', () => {
351
352
  )
352
353
 
353
354
  const event = await eventPromise
354
- expect(event.detail.item).to.equal(items[1])
355
- expect(suggestionsBox.matches(':popover-open')).to.be.false // Popover should close
355
+ assert.equal(event.detail.item, items[1])
356
+ assert.isFalse(suggestionsBox.matches(':popover-open')) // Popover should close
356
357
  })
357
358
  })
358
359
 
@@ -369,8 +370,8 @@ describe('AutocompleteInput', () => {
369
370
  await el.updateComplete // Ensure component processes the new input
370
371
 
371
372
  const slottedInput = getSlottedInput(el)
372
- expect(slottedInput).to.equal(inputEl)
373
- expect(inputEl.id).to.match(/^autocomplete-input-/)
373
+ assert.equal(slottedInput, inputEl)
374
+ assert.match(inputEl.id, /^autocomplete-input-/)
374
375
  })
375
376
 
376
377
  it('handles dynamically added suggestions', async () => {
@@ -387,10 +388,10 @@ describe('AutocompleteInput', () => {
387
388
  await el.updateComplete
388
389
 
389
390
  const slottedSuggestions = getSlottedSuggestions(el)
390
- expect(slottedSuggestions).to.equal(suggestionsEl)
391
- expect(suggestionsEl.popover).to.equal('manual')
391
+ assert.equal(slottedSuggestions, suggestionsEl)
392
+ assert.equal(suggestionsEl.popover, 'manual')
392
393
  const anchorValue = getComputedStyle(suggestionsEl).getPropertyValue('position-anchor')
393
- expect(anchorValue).to.equal(`--${input.id}`)
394
+ assert.equal(anchorValue, `--${input.id}`)
394
395
  })
395
396
 
396
397
  it('re-filters when suggestion items change (via itemschange event)', async () => {
@@ -404,8 +405,8 @@ describe('AutocompleteInput', () => {
404
405
  input.value = 'ban' // Should show "Banana"
405
406
  input.dispatchEvent(new Event('input', { bubbles: true, composed: true }))
406
407
  await nextFrame()
407
- expect(items[0].hidden).to.be.true // Apple
408
- expect(items[1].hidden).to.be.false // Banana
408
+ assert.isTrue(items[0].hidden) // Apple
409
+ assert.isFalse(items[1].hidden) // Banana
409
410
 
410
411
  // Add a new item that matches "ban"
411
412
  const newItem = document.createElement('ui-list-item') as MdListItem
@@ -418,9 +419,9 @@ describe('AutocompleteInput', () => {
418
419
  await nextFrame() // Allow handler to run
419
420
 
420
421
  items = getSuggestionItems(suggestionsBox) // Re-fetch items
421
- expect(items.find((i) => i.dataset.value === 'bandana')?.hidden).to.be.false
422
- expect(items.find((i) => i.dataset.value === 'banana')?.hidden).to.be.false
423
- expect(items.find((i) => i.dataset.value === 'apple')?.hidden).to.be.true
422
+ assert.isFalse(items.find((i) => i.dataset.value === 'bandana')?.hidden)
423
+ assert.isFalse(items.find((i) => i.dataset.value === 'banana')?.hidden)
424
+ assert.isTrue(items.find((i) => i.dataset.value === 'apple')?.hidden)
424
425
  })
425
426
  })
426
427
 
@@ -431,18 +432,18 @@ describe('AutocompleteInput', () => {
431
432
  const input = getSlottedInput(el)!
432
433
  const suggestions = getSlottedSuggestions(el)!
433
434
 
434
- expect(el.opened).to.be.false
435
+ assert.isFalse(el.opened)
435
436
 
436
437
  input.focus()
437
438
  await nextFrame()
438
- expect(el.opened).to.be.true
439
- expect(suggestions.matches(':popover-open')).to.be.true
439
+ assert.isTrue(el.opened)
440
+ assert.isTrue(suggestions.matches(':popover-open'))
440
441
 
441
442
  // @ts-expect-error protected method
442
443
  el.closeSuggestions()
443
444
  await nextFrame()
444
- expect(el.opened).to.be.false
445
- expect(suggestions.matches(':popover-open')).to.be.false
445
+ assert.isFalse(el.opened)
446
+ assert.isFalse(suggestions.matches(':popover-open'))
446
447
  })
447
448
  })
448
449
 
@@ -503,7 +504,7 @@ describe('AutocompleteInput', () => {
503
504
  await el.updateComplete // For positionArea state change and re-render
504
505
  await nextFrame() // For DOM to reflect changes
505
506
 
506
- expect(el.positionArea).to.equal('bottom')
507
+ assert.equal(el.positionArea, 'bottom')
507
508
  })
508
509
 
509
510
  it('positions at top when insufficient space below but sufficient space above', async () => {
@@ -516,7 +517,7 @@ describe('AutocompleteInput', () => {
516
517
  await el.updateComplete
517
518
  await nextFrame()
518
519
 
519
- expect(el.positionArea).to.equal('top')
520
+ assert.equal(el.positionArea, 'top')
520
521
  })
521
522
 
522
523
  it('positions at top when insufficient space below and more space above', async () => {
@@ -535,7 +536,7 @@ describe('AutocompleteInput', () => {
535
536
  await el.updateComplete
536
537
  await nextFrame()
537
538
 
538
- expect(el.positionArea).to.equal('top')
539
+ assert.equal(el.positionArea, 'top')
539
540
  })
540
541
 
541
542
  it('positions at bottom when insufficient space below and also insufficient (or less) space above', async () => {
@@ -552,7 +553,7 @@ describe('AutocompleteInput', () => {
552
553
  await el.updateComplete
553
554
  await nextFrame()
554
555
 
555
- expect(el.positionArea).to.equal('bottom')
556
+ assert.equal(el.positionArea, 'bottom')
556
557
  })
557
558
 
558
559
  it('uses fallback threshold of 150px if popover scrollHeight is 0', async () => {
@@ -574,7 +575,7 @@ describe('AutocompleteInput', () => {
574
575
  await el.updateComplete
575
576
  await nextFrame()
576
577
 
577
- expect(el.positionArea).to.equal('top')
578
+ assert.equal(el.positionArea, 'top')
578
579
  scrollHeightStub.restore()
579
580
  })
580
581
 
@@ -603,8 +604,8 @@ describe('AutocompleteInput', () => {
603
604
  await el.updateComplete
604
605
  await nextFrame()
605
606
 
606
- expect(el.positionArea).to.equal('top')
607
- expect(getBoundingClientRectStub.called).to.be.true
607
+ assert.equal(el.positionArea, 'top')
608
+ assert.isTrue(getBoundingClientRectStub.called)
608
609
  })
609
610
  })
610
611