@api-client/ui 0.5.16 → 0.5.18

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 (23) 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/CodeEditor.d.ts +20 -1
  6. package/build/src/elements/code-editor/internals/CodeEditor.d.ts.map +1 -1
  7. package/build/src/elements/code-editor/internals/CodeEditor.js +67 -2
  8. package/build/src/elements/code-editor/internals/CodeEditor.js.map +1 -1
  9. package/build/src/elements/code-editor/internals/CodeEditor.styles.d.ts.map +1 -1
  10. package/build/src/elements/code-editor/internals/CodeEditor.styles.js +8 -2
  11. package/build/src/elements/code-editor/internals/CodeEditor.styles.js.map +1 -1
  12. package/build/src/elements/code-editor/internals/types.d.ts +4 -0
  13. package/build/src/elements/code-editor/internals/types.d.ts.map +1 -1
  14. package/build/src/elements/code-editor/internals/types.js.map +1 -1
  15. package/demo/elements/code-editor/CodeEditorDemo.ts +8 -0
  16. package/package.json +1 -1
  17. package/src/elements/code-editor/code-editor.ts +6 -1
  18. package/src/elements/code-editor/internals/CodeEditor.styles.ts +8 -2
  19. package/src/elements/code-editor/internals/CodeEditor.ts +55 -2
  20. package/src/elements/code-editor/internals/types.ts +5 -0
  21. package/test/elements/autocomplete/autocomplete-input.spec.ts +71 -70
  22. package/test/elements/code-editor/code-editor.accessibility.test.ts +325 -0
  23. package/test/elements/code-editor/code-editor.test.ts +577 -0
@@ -101,6 +101,12 @@ class CodeEditorDemo extends DemoPage {
101
101
  this.editorValue = target.value
102
102
  }
103
103
 
104
+ private handleFunctionDocumentation(event: CustomEvent<{ functionSchema: FunctionSchema }>): void {
105
+ const { functionSchema } = event.detail
106
+ this.output = `Function documentation requested for: ${functionSchema.name}`
107
+ console.log('Function documentation:', functionSchema)
108
+ }
109
+
104
110
  private handleChange(event: Event): void {
105
111
  const target = event.target as HTMLElement & { value: string }
106
112
  this.output = `Editor content changed: ${target.value}`
@@ -125,6 +131,8 @@ class CodeEditorDemo extends DemoPage {
125
131
  @suggestion-insert=${this.handleSuggestionInsert}
126
132
  @input=${this.handleInput}
127
133
  @change=${this.handleChange}
134
+ show-documentation
135
+ @function-documentation="${this.handleFunctionDocumentation}"
128
136
  ></code-editor>
129
137
 
130
138
  ${this.output ? html`<p><strong>Output:</strong> ${this.output}</p>` : ''}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@api-client/ui",
3
- "version": "0.5.16",
3
+ "version": "0.5.18",
4
4
  "description": "Internal UI component library for the API Client ecosystem.",
5
5
  "license": "UNLICENSED",
6
6
  "main": "build/src/index.js",
@@ -15,5 +15,10 @@ declare global {
15
15
  }
16
16
 
17
17
  export default CodeEditorElement
18
- export type { Suggestion, FunctionInsertEvent, SuggestionInsertEvent } from './internals/types.js'
18
+ export type {
19
+ Suggestion,
20
+ FunctionInsertEvent,
21
+ SuggestionInsertEvent,
22
+ FunctionDocumentationEvent,
23
+ } from './internals/types.js'
19
24
  export type { FunctionSchema, FunctionParameter } from '@pawel-up/jexl/schemas/types.js'
@@ -117,8 +117,8 @@ export default css`
117
117
  border: none;
118
118
  }
119
119
 
120
- .cm-focused .cm-cursor {
121
- border-left-color: var(--md-sys-color-primary);
120
+ .cm-focused .cm-content[contenteditable='true'] {
121
+ caret-color: var(--md-sys-color-primary);
122
122
  }
123
123
 
124
124
  ::selection {
@@ -186,6 +186,12 @@ export default css`
186
186
  .param-description {
187
187
  color: var(--md-sys-color-on-surface-variant);
188
188
  }
189
+
190
+ .documentation {
191
+ margin-top: 8px;
192
+ padding-top: 8px;
193
+ border-top: 1px solid var(--md-sys-color-outline-variant);
194
+ }
189
195
  }
190
196
 
191
197
  /* Responsive */
@@ -27,9 +27,11 @@ import {
27
27
  import type { FunctionSchema } from '@pawel-up/jexl/schemas/types.js'
28
28
  import { getTypeStringFromSchema } from '@pawel-up/jexl/schemas/utils.js'
29
29
  import { functionLinter } from './Linter.js'
30
- import type { FunctionInsertEvent, Suggestion, SuggestionInsertEvent } from './types.js'
30
+ import type { FunctionInsertEvent, Suggestion, SuggestionInsertEvent, FunctionDocumentationEvent } from './types.js'
31
31
  import { SuggestionMatchDecorator } from './SuggestionMatchDecorator.js'
32
32
 
33
+ import '../../../md/button/ui-button.js'
34
+
33
35
  /**
34
36
  * A CodeMirror 6 based editor component that supports function autocomplete and suggestion placeholders.
35
37
  *
@@ -42,6 +44,7 @@ import { SuggestionMatchDecorator } from './SuggestionMatchDecorator.js'
42
44
  *
43
45
  * @fires function-insert - When a function is inserted
44
46
  * @fires suggestion-insert - When a suggestion is inserted
47
+ * @fires function-documentation - When documentation is requested for a function
45
48
  * @fires input - When the editor content changes
46
49
  * @fires change - When the editor loses focus and content has changed
47
50
  */
@@ -57,42 +60,49 @@ export default class CodeEditor extends LitElement {
57
60
 
58
61
  /**
59
62
  * The label text displayed as placeholder/floating label
63
+ * @attribute
60
64
  */
61
65
  @property({ type: String })
62
66
  accessor label = ''
63
67
 
64
68
  /**
65
69
  * Supporting text displayed below the editor
70
+ * @attribute
66
71
  */
67
72
  @property({ type: String, attribute: 'supporting-text' })
68
73
  accessor supportingText = ''
69
74
 
70
75
  /**
71
76
  * Whether the component is disabled
77
+ * @attribute
72
78
  */
73
79
  @property({ type: Boolean, reflect: true })
74
80
  accessor disabled = false
75
81
 
76
82
  /**
77
83
  * Whether the component is in an invalid state
84
+ * @attribute
78
85
  */
79
86
  @property({ type: Boolean, reflect: true })
80
87
  accessor invalid = false
81
88
 
82
89
  /**
83
90
  * The name attribute for form integration
91
+ * @attribute
84
92
  */
85
93
  @property({ type: String })
86
94
  accessor name = ''
87
95
 
88
96
  /**
89
97
  * Whether the input is required
98
+ * @attribute
90
99
  */
91
100
  @property({ type: Boolean })
92
101
  accessor required = false
93
102
 
94
103
  /**
95
104
  * Placeholder text shown when editor is empty
105
+ * @attribute
96
106
  */
97
107
  @property({ type: String })
98
108
  accessor placeholder = ''
@@ -139,10 +149,18 @@ export default class CodeEditor extends LitElement {
139
149
 
140
150
  /**
141
151
  * Programming language for syntax highlighting
152
+ * @attribute
142
153
  */
143
154
  @property({ type: String })
144
155
  accessor language = 'javascript'
145
156
 
157
+ /**
158
+ * Whether to show documentation links/buttons in function tooltips and autocomplete
159
+ * @attribute show-documentation
160
+ */
161
+ @property({ type: Boolean, attribute: 'show-documentation' })
162
+ accessor showDocumentation = false
163
+
146
164
  @query('.editor-container')
147
165
  private accessor editorContainer!: HTMLDivElement
148
166
 
@@ -231,6 +249,9 @@ export default class CodeEditor extends LitElement {
231
249
  }
232
250
  if (update.focusChanged) {
233
251
  this.handleFocusChange(update.view.hasFocus)
252
+ } else if (update.view.hasFocus !== this.isEditorFocus) {
253
+ // If focus state changed without focusChanged event, update manually
254
+ this.handleFocusChange(update.view.hasFocus)
234
255
  }
235
256
  }),
236
257
  this.createPlaceholderPlugin(),
@@ -405,7 +426,7 @@ export default class CodeEditor extends LitElement {
405
426
  * Creates a styled HTML element to display function documentation.
406
427
  * This is used for both hover tooltips and autocomplete info panels.
407
428
  */
408
- private createFunctionInfoElement(fn: FunctionSchema): HTMLElement {
429
+ protected createFunctionInfoElement(fn: FunctionSchema): HTMLElement {
409
430
  const container = document.createElement('div')
410
431
  container.className = 'function-info' // for styling
411
432
 
@@ -466,6 +487,27 @@ export default class CodeEditor extends LitElement {
466
487
  container.appendChild(returnsContainer)
467
488
  }
468
489
 
490
+ // Add documentation button if showDocumentation is enabled
491
+ if (this.showDocumentation) {
492
+ const docContainer = document.createElement('div')
493
+ docContainer.className = 'documentation'
494
+
495
+ const docButton = document.createElement('ui-button')
496
+ docButton.type = 'button'
497
+ docButton.className = 'documentation-button'
498
+ docButton.textContent = 'Show documentation'
499
+ docButton.setAttribute('aria-label', `Show documentation for ${fn.name} function`)
500
+
501
+ docButton.addEventListener('click', (event) => {
502
+ event.preventDefault()
503
+ event.stopPropagation()
504
+ this.dispatchFunctionDocumentation(fn)
505
+ })
506
+
507
+ docContainer.appendChild(docButton)
508
+ container.appendChild(docContainer)
509
+ }
510
+
469
511
  return container
470
512
  }
471
513
 
@@ -554,6 +596,17 @@ export default class CodeEditor extends LitElement {
554
596
  this.dispatchEvent(event)
555
597
  }
556
598
 
599
+ /**
600
+ * Dispatch function documentation event
601
+ */
602
+ private dispatchFunctionDocumentation(functionSchema: FunctionSchema): void {
603
+ const event = new CustomEvent<FunctionDocumentationEvent>('function-documentation', {
604
+ detail: { functionSchema },
605
+ bubbles: true,
606
+ })
607
+ this.dispatchEvent(event)
608
+ }
609
+
557
610
  /**
558
611
  * Focus the editor
559
612
  */
@@ -26,3 +26,8 @@ export interface SuggestionInsertEvent {
26
26
  /** The position where the suggestion was inserted */
27
27
  position: number
28
28
  }
29
+
30
+ export interface FunctionDocumentationEvent {
31
+ /** The function schema for which documentation is requested */
32
+ functionSchema: FunctionSchema
33
+ }
@@ -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