@api-client/ui 0.5.39 → 0.5.41
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/build/src/elements/contextual-menu/{internals/ContextualMenu.d.ts → ContextualMenu.d.ts} +11 -6
- package/build/src/elements/contextual-menu/ContextualMenu.d.ts.map +1 -0
- package/build/src/elements/contextual-menu/{internals/ContextualMenu.js → ContextualMenu.js} +25 -3
- package/build/src/elements/contextual-menu/ContextualMenu.js.map +1 -0
- package/build/src/elements/contextual-menu/internals/types.d.ts +16 -7
- package/build/src/elements/contextual-menu/internals/types.d.ts.map +1 -1
- package/build/src/elements/contextual-menu/internals/types.js.map +1 -1
- package/build/tsconfig.tsbuildinfo +1 -0
- package/package.json +1 -1
- package/src/elements/contextual-menu/{internals/ContextualMenu.ts → ContextualMenu.ts} +31 -7
- package/src/elements/contextual-menu/internals/types.ts +17 -7
- package/.aiexclude +0 -3
- package/.cursor/rules/html-and-css-best-practices.mdc +0 -63
- package/.cursor/rules/lit-best-practices.mdc +0 -89
- package/.editorconfig +0 -29
- package/.github/CONTRIBUTING.md +0 -24
- package/.github/instructions/html-and-css-best-practices.instructions.md +0 -70
- package/.github/instructions/lit-best-practices.instructions.md +0 -90
- package/.github/release.yml +0 -14
- package/.github/stale.yml +0 -23
- package/.github/workflows/auto-release.yml +0 -182
- package/.github/workflows/release.yml +0 -82
- package/.prettierrc.js +0 -14
- package/.vscode/settings.json +0 -18
- package/RELEASE.md +0 -163
- package/RELEASE_SETUP.md +0 -235
- package/build/src/demo/DemoPage.d.ts +0 -81
- package/build/src/demo/DemoPage.d.ts.map +0 -1
- package/build/src/demo/DemoPage.js +0 -175
- package/build/src/demo/DemoPage.js.map +0 -1
- package/build/src/demo/DemoStyles.d.ts +0 -3
- package/build/src/demo/DemoStyles.d.ts.map +0 -1
- package/build/src/demo/DemoStyles.js +0 -60
- package/build/src/demo/DemoStyles.js.map +0 -1
- package/build/src/elements/contextual-menu/internals/ContextualMenu.d.ts.map +0 -1
- package/build/src/elements/contextual-menu/internals/ContextualMenu.js.map +0 -1
- package/build/test/elements/navigation/Navigation.test.d.ts +0 -3
- package/build/test/elements/navigation/Navigation.test.d.ts.map +0 -1
- package/build/test/elements/navigation/Navigation.test.js +0 -113
- package/build/test/elements/navigation/Navigation.test.js.map +0 -1
- package/commitlint.config.cjs +0 -2
- package/demo/elements/authorization/AuthPlugin.js +0 -57
- package/demo/elements/authorization/AuthProxy.js +0 -215
- package/demo/elements/authorization/api-key.html +0 -27
- package/demo/elements/authorization/api-key.ts +0 -44
- package/demo/elements/authorization/basic.html +0 -27
- package/demo/elements/authorization/basic.ts +0 -43
- package/demo/elements/authorization/bearer.html +0 -27
- package/demo/elements/authorization/bearer.ts +0 -43
- package/demo/elements/authorization/env.js +0 -8
- package/demo/elements/authorization/index.html +0 -44
- package/demo/elements/authorization/ntlm.html +0 -27
- package/demo/elements/authorization/ntlm.ts +0 -43
- package/demo/elements/authorization/oauth-authorize.html +0 -75
- package/demo/elements/authorization/oauth-authorize.ts +0 -40
- package/demo/elements/authorization/oauth-error.html +0 -18
- package/demo/elements/authorization/oauth-error.ts +0 -10
- package/demo/elements/authorization/oauth-popup.html +0 -36
- package/demo/elements/authorization/oauth2.html +0 -27
- package/demo/elements/authorization/oauth2.ts +0 -100
- package/demo/elements/authorization/oidc.html +0 -27
- package/demo/elements/authorization/oidc.ts +0 -139
- package/demo/elements/authorization/private.crt +0 -31
- package/demo/elements/authorization/private.csr +0 -28
- package/demo/elements/authorization/private.key +0 -51
- package/demo/elements/authorization/private.pem +0 -31
- package/demo/elements/authorization/redirect.html +0 -20
- package/demo/elements/authorization/ssl-commands.sh +0 -30
- package/demo/elements/authorization/ssl.conf +0 -24
- package/demo/elements/autocomplete/index.html +0 -64
- package/demo/elements/autocomplete/index.ts +0 -171
- package/demo/elements/code-editor/CodeEditorDemo.ts +0 -173
- package/demo/elements/code-editor/index.html +0 -19
- package/demo/elements/context-menu/DemoIcons.ts +0 -21
- package/demo/elements/context-menu/basic.html +0 -25
- package/demo/elements/context-menu/basic.ts +0 -119
- package/demo/elements/context-menu/custom-data.html +0 -25
- package/demo/elements/context-menu/custom-data.ts +0 -62
- package/demo/elements/context-menu/demo.css +0 -28
- package/demo/elements/context-menu/enabled-state.html +0 -25
- package/demo/elements/context-menu/enabled-state.ts +0 -73
- package/demo/elements/context-menu/icons.html +0 -25
- package/demo/elements/context-menu/icons.ts +0 -64
- package/demo/elements/context-menu/index.html +0 -43
- package/demo/elements/context-menu/nested.html +0 -25
- package/demo/elements/context-menu/nestedt.ts +0 -152
- package/demo/elements/context-menu/no-execute.html +0 -25
- package/demo/elements/context-menu/no-execute.ts +0 -134
- package/demo/elements/context-menu/radio-menu.html +0 -25
- package/demo/elements/context-menu/radio-menu.ts +0 -83
- package/demo/elements/context-menu/separators.html +0 -25
- package/demo/elements/context-menu/separators.ts +0 -172
- package/demo/elements/currency/index.html +0 -91
- package/demo/elements/currency/index.ts +0 -352
- package/demo/elements/environment/environment-editor.html +0 -20
- package/demo/elements/environment/environment-editor.ts +0 -49
- package/demo/elements/environment/index.html +0 -33
- package/demo/elements/environment/server-editor.html +0 -20
- package/demo/elements/environment/server-editor.ts +0 -67
- package/demo/elements/environment/variables-editor.html +0 -20
- package/demo/elements/environment/variables-editor.ts +0 -94
- package/demo/elements/har/har-viewer.html +0 -20
- package/demo/elements/har/har-viewer.ts +0 -76
- package/demo/elements/har/har1.har +0 -3044
- package/demo/elements/har/har2.json +0 -439
- package/demo/elements/har/index.html +0 -26
- package/demo/elements/highlight/example.md +0 -27
- package/demo/elements/highlight/index.html +0 -31
- package/demo/elements/highlight/marked-highlight.html +0 -132
- package/demo/elements/highlight/marked-highlight.ts +0 -22
- package/demo/elements/highlight/prism-highlight.html +0 -62
- package/demo/elements/highlight/prism-highlight.ts +0 -17
- package/demo/elements/http/body-editor.html +0 -17
- package/demo/elements/http/body-editor.ts +0 -115
- package/demo/elements/http/headers.html +0 -17
- package/demo/elements/http/headers.ts +0 -59
- package/demo/elements/http/http-assertions.html +0 -20
- package/demo/elements/http/http-assertions.ts +0 -89
- package/demo/elements/http/http-flows.html +0 -23
- package/demo/elements/http/http-flows.ts +0 -89
- package/demo/elements/http/index.html +0 -45
- package/demo/elements/http/request-editor.html +0 -26
- package/demo/elements/http/request-editor.ts +0 -197
- package/demo/elements/http/request-log.html +0 -16
- package/demo/elements/http/request-log.ts +0 -136
- package/demo/elements/http/url-editing.html +0 -17
- package/demo/elements/http/url-editing.ts +0 -112
- package/demo/elements/icons/index.html +0 -81
- package/demo/elements/icons/index.ts +0 -52
- package/demo/elements/index.html +0 -72
- package/demo/elements/mention-textarea/index.html +0 -19
- package/demo/elements/mention-textarea/index.ts +0 -205
- package/demo/elements/navigation/navigation-item.html +0 -49
- package/demo/elements/navigation/navigation-item.ts +0 -131
- package/demo/elements/navigation/navigation.html +0 -20
- package/demo/elements/navigation/navigation.ts +0 -45
- package/demo/elements/project/index.html +0 -29
- package/demo/elements/project/project-run-report.html +0 -20
- package/demo/elements/project/project-run-report.ts +0 -132
- package/demo/elements/project/request-editor.html +0 -23
- package/demo/elements/project/request-editor.ts +0 -232
- package/demo/elements/user/user-avatar.html +0 -17
- package/demo/elements/user/user-avatar.ts +0 -60
- package/demo/env.js +0 -4
- package/demo/index.html +0 -34
- package/demo/layout/index.html +0 -94
- package/demo/layout/index.ts +0 -190
- package/demo/md/DemoStyles.ts +0 -61
- package/demo/md/UiDemoPage.ts +0 -6
- package/demo/md/buttons/button.html +0 -121
- package/demo/md/buttons/button.ts +0 -246
- package/demo/md/buttons/group.html +0 -36
- package/demo/md/buttons/group.ts +0 -171
- package/demo/md/checkbox/index.html +0 -39
- package/demo/md/checkbox/index.ts +0 -220
- package/demo/md/chip/chip.html +0 -70
- package/demo/md/chip/chip.ts +0 -219
- package/demo/md/chip/pawel6c9a.jpg +0 -0
- package/demo/md/collapse/CustomDetail.ts +0 -89
- package/demo/md/collapse/collapse.html +0 -21
- package/demo/md/collapse/collapse.ts +0 -78
- package/demo/md/date-picker/date-picker.ts +0 -336
- package/demo/md/date-picker/index.html +0 -171
- package/demo/md/dialog/confirm-dialog.html +0 -49
- package/demo/md/dialog/confirm-dialog.ts +0 -121
- package/demo/md/dialog/dialog.html +0 -25
- package/demo/md/dialog/dialog.ts +0 -468
- package/demo/md/dropdown-list/index.html +0 -31
- package/demo/md/dropdown-list/index.ts +0 -158
- package/demo/md/icon-button/index.html +0 -122
- package/demo/md/icon-button/index.ts +0 -132
- package/demo/md/index.html +0 -73
- package/demo/md/inputs/input.html +0 -73
- package/demo/md/inputs/input.ts +0 -278
- package/demo/md/inputs/radio.html +0 -39
- package/demo/md/inputs/radio.ts +0 -156
- package/demo/md/inputs/switch.html +0 -45
- package/demo/md/inputs/switch.ts +0 -144
- package/demo/md/list/list.html +0 -65
- package/demo/md/list/list.ts +0 -204
- package/demo/md/listbox/listbox.html +0 -31
- package/demo/md/listbox/listbox.ts +0 -27
- package/demo/md/menu/index.html +0 -19
- package/demo/md/menu/index.ts +0 -514
- package/demo/md/notification/snack.html +0 -21
- package/demo/md/notification/snack.ts +0 -70
- package/demo/md/progress/progress.html +0 -46
- package/demo/md/progress/progress.ts +0 -161
- package/demo/md/segmented-button/index.html +0 -21
- package/demo/md/segmented-button/index.ts +0 -55
- package/demo/md/select/index.html +0 -16
- package/demo/md/select/index.ts +0 -217
- package/demo/md/tabs/tabs.html +0 -40
- package/demo/md/tabs/tabs.ts +0 -214
- package/demo/oauth-popup.html +0 -36
- package/demo/page.css +0 -8
- package/demo/resources/calendar-month.png +0 -0
- package/demo/resources/favorite.png +0 -0
- package/demo/resources/fingerprint.png +0 -0
- package/demo/resources/home-work.png +0 -0
- package/demo/resources/mood.png +0 -0
- package/demo/resources/print.png +0 -0
- package/demo/resources/stars.png +0 -0
- package/demo/resources/theaters.png +0 -0
- package/demo/tsconfig.json +0 -4
- package/eslint.config.js +0 -97
- package/scripts/copy-assets.js +0 -21
- package/scripts/release.js +0 -66
- package/src/demo/DemoPage.ts +0 -169
- package/src/demo/DemoStyles.ts +0 -60
- package/test/README.md +0 -375
- package/test/contextual-menu/ContextMenu.test.ts +0 -760
- package/test/contextual-menu/ContextMenuElement.test.ts +0 -569
- package/test/core/activity.spec.ts +0 -413
- package/test/core/activity_manager.spec.ts +0 -544
- package/test/core/application.spec.ts +0 -218
- package/test/core/fragment.spec.ts +0 -565
- package/test/core/fragment_manager.spec.ts +0 -404
- package/test/core/live_data.spec.ts +0 -558
- package/test/core/renderer.spec.ts +0 -113
- package/test/dom-assertions.test.ts +0 -182
- package/test/elements/MonacoSetup.ts +0 -65
- package/test/elements/authorization/basic-method.test.ts +0 -177
- package/test/elements/authorization/bearer-method.test.ts +0 -143
- package/test/elements/authorization/ntlm-method.test.ts +0 -219
- package/test/elements/authorization/oauth2-client-credentials-method.test.ts +0 -334
- package/test/elements/authorization/oauth2-code-method.test.ts +0 -320
- package/test/elements/authorization/oauth2-custom-grant-method.test.ts +0 -255
- package/test/elements/authorization/oauth2-device-code-method.test.ts +0 -371
- package/test/elements/authorization/oauth2-implicit-method.test.ts +0 -407
- package/test/elements/authorization/oauth2-jwt-method.test.ts +0 -217
- package/test/elements/authorization/oauth2-password-method.test.ts +0 -275
- package/test/elements/authorization/openid-method.test.ts +0 -591
- package/test/elements/autocomplete/autocomplete-input.spec.ts +0 -646
- package/test/elements/code-editor/code-editor.accessibility.test.ts +0 -298
- package/test/elements/code-editor/code-editor.test.ts +0 -574
- package/test/elements/currency/CurrencyPicker.accessibility.test.ts +0 -328
- package/test/elements/currency/CurrencyPicker.core.test.ts +0 -318
- package/test/elements/currency/CurrencyPicker.integration.test.ts +0 -482
- package/test/elements/currency/CurrencyPicker.test.ts +0 -486
- package/test/elements/data-table/DataTable.browser.test.ts +0 -649
- package/test/elements/har/HarUtils.test.ts +0 -45
- package/test/elements/har/HarViewerElement.test.ts +0 -687
- package/test/elements/har/test-data/har1.har +0 -3044
- package/test/elements/highlight/MarkedHighlightElement.test.ts +0 -452
- package/test/elements/highlight/PrismHighlightElement.test.ts +0 -79
- package/test/elements/highlight/PrismHighlighter.test.ts +0 -94
- package/test/elements/highlight/remoteSanitization.md +0 -1
- package/test/elements/highlight/test.md +0 -3
- package/test/elements/highlight/test1.md +0 -3
- package/test/elements/highlight/test2.md +0 -1
- package/test/elements/http/BodyFormdataEditorElement.test.ts +0 -482
- package/test/elements/http/BodyMultipartEditorElement.test.ts +0 -658
- package/test/elements/http/BodyRawEditorElement.test.ts +0 -90
- package/test/elements/http/CertificateAdd.test.ts +0 -457
- package/test/elements/http/HttpAssertions.test.ts +0 -994
- package/test/elements/http/HttpFlows.test.ts +0 -502
- package/test/elements/http/UrlEncodeUtils.test.ts +0 -202
- package/test/elements/layout/SplitItem.test.ts +0 -440
- package/test/elements/layout/SplitLayoutManager.test.ts +0 -1501
- package/test/elements/layout/SplitPanel.test.ts +0 -1109
- package/test/elements/mention-textarea/MentionTextArea.basic.test.ts +0 -114
- package/test/elements/mention-textarea/MentionTextArea.test.ts +0 -613
- package/test/elements/navigation/Navigation.test.ts +0 -120
- package/test/env.ts +0 -15
- package/test/events/EventTypes.test.ts +0 -363
- package/test/events/EventsTestHelpers.ts +0 -16
- package/test/helpers/TestUtils.ts +0 -243
- package/test/helpers/UiMock.ts +0 -185
- package/test/lib/Dom.test.ts +0 -231
- package/test/md/button/UiButton.test.ts +0 -347
- package/test/md/button/UiIconButton.test.ts +0 -155
- package/test/md/chip/UiChip.test.ts +0 -219
- package/test/md/collapse/UiCollapse.test.ts +0 -250
- package/test/md/collapse/flex-layout.test.ts +0 -105
- package/test/md/date-time/DateTime.test.ts +0 -348
- package/test/md/dialog/UiConfirmDialog.test.ts +0 -131
- package/test/md/dialog/UiDialog.test.ts +0 -759
- package/test/md/menu/Menu.test.ts +0 -855
- package/test/md/menu/MenuIntegration.test.ts +0 -426
- package/test/md/menu/MenuItem.test.ts +0 -652
- package/test/md/menu/SubMenu.test.ts +0 -410
- package/test/md/progress/UiCircularProgressElement.test.ts +0 -481
- package/test/md/progress/UiProgressElement.test.ts +0 -117
- package/test/md/progress/UiRangeElement.test.ts +0 -156
- package/test/md/select/Select.test.ts +0 -925
- package/test/plugins/takeScreenshotPlugin.js +0 -35
- package/test/setup.test.ts +0 -217
- package/test/setup.ts +0 -117
- package/test/tsconfig.json +0 -7
- package/web-dev-server.config.js +0 -21
- package/web-test-runner.config.js +0 -90
|
@@ -1,925 +0,0 @@
|
|
|
1
|
-
import { assert, fixture, html, nextFrame, oneEvent } from '@open-wc/testing'
|
|
2
|
-
import sinon from 'sinon'
|
|
3
|
-
import UiSelect from '../../../src/md/select/internals/Select.js'
|
|
4
|
-
import UiOption from '../../../src/md/select/internals/Option.js'
|
|
5
|
-
import type { UiSelectChangeEvent } from '../../../src/md/select/internals/Select.js'
|
|
6
|
-
|
|
7
|
-
import '../../../src/md/select/ui-select.js'
|
|
8
|
-
import '../../../src/md/select/ui-option.js'
|
|
9
|
-
import '../../../src/md/icons/ui-icon.js'
|
|
10
|
-
|
|
11
|
-
describe('md', () => {
|
|
12
|
-
describe('UiSelect', () => {
|
|
13
|
-
async function basicFixture(): Promise<UiSelect> {
|
|
14
|
-
return fixture(html`
|
|
15
|
-
<ui-select label="Select an option">
|
|
16
|
-
<ui-option value="apple">Apple</ui-option>
|
|
17
|
-
<ui-option value="banana">Banana</ui-option>
|
|
18
|
-
<ui-option value="cherry">Cherry</ui-option>
|
|
19
|
-
</ui-select>
|
|
20
|
-
`)
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
async function withValueFixture(): Promise<UiSelect> {
|
|
24
|
-
return fixture(html`
|
|
25
|
-
<ui-select label="Select an option" value="banana">
|
|
26
|
-
<ui-option value="apple">Apple</ui-option>
|
|
27
|
-
<ui-option value="banana">Banana</ui-option>
|
|
28
|
-
<ui-option value="cherry">Cherry</ui-option>
|
|
29
|
-
</ui-select>
|
|
30
|
-
`)
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
async function requiredFixture(): Promise<UiSelect> {
|
|
34
|
-
return fixture(html`
|
|
35
|
-
<ui-select label="Required field" required>
|
|
36
|
-
<ui-option value="apple">Apple</ui-option>
|
|
37
|
-
<ui-option value="banana">Banana</ui-option>
|
|
38
|
-
</ui-select>
|
|
39
|
-
`)
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
async function disabledFixture(): Promise<UiSelect> {
|
|
43
|
-
return fixture(html`
|
|
44
|
-
<ui-select label="Disabled select" disabled>
|
|
45
|
-
<ui-option value="apple">Apple</ui-option>
|
|
46
|
-
<ui-option value="banana">Banana</ui-option>
|
|
47
|
-
</ui-select>
|
|
48
|
-
`)
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
async function formFixture(): Promise<HTMLFormElement> {
|
|
52
|
-
return fixture(html`
|
|
53
|
-
<form>
|
|
54
|
-
<input name="text" value="test" />
|
|
55
|
-
<ui-select name="fruit" label="Select fruit">
|
|
56
|
-
<ui-option value="apple">Apple</ui-option>
|
|
57
|
-
<ui-option value="banana">Banana</ui-option>
|
|
58
|
-
</ui-select>
|
|
59
|
-
</form>
|
|
60
|
-
`)
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
async function invalidFixture(): Promise<UiSelect> {
|
|
64
|
-
return fixture(html`
|
|
65
|
-
<ui-select label="Invalid select" invalid invalidText="This field is required">
|
|
66
|
-
<ui-option value="apple">Apple</ui-option>
|
|
67
|
-
<ui-option value="banana">Banana</ui-option>
|
|
68
|
-
</ui-select>
|
|
69
|
-
`)
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
async function emptyFixture(): Promise<UiSelect> {
|
|
73
|
-
return fixture(html`<ui-select label="Empty select"></ui-select>`)
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
async function selectedAttributeFixture(): Promise<UiSelect> {
|
|
77
|
-
return fixture(html`
|
|
78
|
-
<ui-select label="Select with pre-selected option">
|
|
79
|
-
<ui-option value="apple">Apple</ui-option>
|
|
80
|
-
<ui-option value="banana" selected>Banana</ui-option>
|
|
81
|
-
<ui-option value="cherry">Cherry</ui-option>
|
|
82
|
-
</ui-select>
|
|
83
|
-
`)
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
async function multipleSelectedFixture(): Promise<UiSelect> {
|
|
87
|
-
return fixture(html`
|
|
88
|
-
<ui-select label="Select with multiple selected">
|
|
89
|
-
<ui-option value="apple" selected>Apple</ui-option>
|
|
90
|
-
<ui-option value="banana" selected>Banana</ui-option>
|
|
91
|
-
<ui-option value="cherry">Cherry</ui-option>
|
|
92
|
-
</ui-select>
|
|
93
|
-
`)
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
async function selectedWithValueFixture(): Promise<UiSelect> {
|
|
97
|
-
return fixture(html`
|
|
98
|
-
<ui-select label="Select with both value and selected" value="cherry">
|
|
99
|
-
<ui-option value="apple">Apple</ui-option>
|
|
100
|
-
<ui-option value="banana" selected>Banana</ui-option>
|
|
101
|
-
<ui-option value="cherry">Cherry</ui-option>
|
|
102
|
-
</ui-select>
|
|
103
|
-
`)
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
async function dynamicOptionsFixture(): Promise<UiSelect> {
|
|
107
|
-
return fixture(html` <ui-select label="Dynamic options"> </ui-select> `)
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
describe('Basic functionality', () => {
|
|
111
|
-
it('should create select element', async () => {
|
|
112
|
-
const element = await basicFixture()
|
|
113
|
-
assert.instanceOf(element, UiSelect)
|
|
114
|
-
assert.equal(element.tagName.toLowerCase(), 'ui-select')
|
|
115
|
-
})
|
|
116
|
-
|
|
117
|
-
it('has formAssociated set', async () => {
|
|
118
|
-
assert.isTrue(UiSelect.formAssociated)
|
|
119
|
-
})
|
|
120
|
-
|
|
121
|
-
it('should have correct default properties', async () => {
|
|
122
|
-
const element = await basicFixture()
|
|
123
|
-
assert.isUndefined(element.value)
|
|
124
|
-
assert.isUndefined(element.name)
|
|
125
|
-
assert.equal(element.label, 'Select an option')
|
|
126
|
-
assert.isFalse(element.required)
|
|
127
|
-
assert.isUndefined(element.invalid)
|
|
128
|
-
assert.isUndefined(element.invalidText)
|
|
129
|
-
assert.isFalse(element.disabled)
|
|
130
|
-
assert.isFalse(element.open)
|
|
131
|
-
assert.isNull(element.selectedItem)
|
|
132
|
-
assert.equal(element.renderValue, '')
|
|
133
|
-
})
|
|
134
|
-
|
|
135
|
-
it('should set correct ARIA attributes', async () => {
|
|
136
|
-
const element = await basicFixture()
|
|
137
|
-
assert.equal(element.getAttribute('role'), 'combobox')
|
|
138
|
-
assert.equal(element.getAttribute('aria-haspopup'), 'listbox')
|
|
139
|
-
assert.equal(element.getAttribute('aria-controls'), 'menu')
|
|
140
|
-
assert.equal(element.getAttribute('aria-label'), 'Select an option')
|
|
141
|
-
assert.equal(element.tabIndex, 0)
|
|
142
|
-
})
|
|
143
|
-
|
|
144
|
-
it('should not have tabindex when disabled', async () => {
|
|
145
|
-
const element = await disabledFixture()
|
|
146
|
-
assert.equal(element.tabIndex, -1)
|
|
147
|
-
})
|
|
148
|
-
})
|
|
149
|
-
|
|
150
|
-
describe('Value and selection', () => {
|
|
151
|
-
it('should set value and select corresponding option', async () => {
|
|
152
|
-
const element = await basicFixture()
|
|
153
|
-
element.value = 'banana'
|
|
154
|
-
await element.updateComplete
|
|
155
|
-
// We need it here because the observer also awaits updateComplete
|
|
156
|
-
await nextFrame()
|
|
157
|
-
|
|
158
|
-
assert.equal(element.value, 'banana')
|
|
159
|
-
assert.isNotNull(element.selectedItem)
|
|
160
|
-
assert.equal(element.selectedItem!.value, 'banana')
|
|
161
|
-
assert.equal(element.renderValue, 'Banana')
|
|
162
|
-
})
|
|
163
|
-
|
|
164
|
-
it('should initialize with preselected value', async () => {
|
|
165
|
-
const element = await withValueFixture()
|
|
166
|
-
await element.updateComplete
|
|
167
|
-
|
|
168
|
-
assert.equal(element.value, 'banana')
|
|
169
|
-
assert.isNotNull(element.selectedItem)
|
|
170
|
-
assert.equal(element.selectedItem!.value, 'banana')
|
|
171
|
-
assert.equal(element.renderValue, 'Banana')
|
|
172
|
-
})
|
|
173
|
-
|
|
174
|
-
it('should handle invalid value gracefully', async () => {
|
|
175
|
-
const element = await basicFixture()
|
|
176
|
-
element.value = 'nonexistent'
|
|
177
|
-
await element.updateComplete
|
|
178
|
-
|
|
179
|
-
assert.equal(element.value, 'nonexistent')
|
|
180
|
-
assert.isNull(element.selectedItem)
|
|
181
|
-
assert.equal(element.renderValue, '')
|
|
182
|
-
})
|
|
183
|
-
|
|
184
|
-
it('should clear selection when value is undefined', async () => {
|
|
185
|
-
const element = await withValueFixture()
|
|
186
|
-
await element.updateComplete
|
|
187
|
-
|
|
188
|
-
element.value = undefined
|
|
189
|
-
await element.updateComplete
|
|
190
|
-
// We need it here because the observer also awaits updateComplete
|
|
191
|
-
await nextFrame()
|
|
192
|
-
|
|
193
|
-
assert.isUndefined(element.value, 'the value should be undefined')
|
|
194
|
-
assert.isNull(element.selectedItem, 'selected item should be null')
|
|
195
|
-
assert.equal(element.renderValue, '')
|
|
196
|
-
})
|
|
197
|
-
|
|
198
|
-
it('should discover selected attribute on options', async () => {
|
|
199
|
-
const element = await selectedAttributeFixture()
|
|
200
|
-
await element.updateComplete
|
|
201
|
-
|
|
202
|
-
assert.equal(element.value, 'banana')
|
|
203
|
-
assert.isNotNull(element.selectedItem)
|
|
204
|
-
assert.equal(element.selectedItem!.value, 'banana')
|
|
205
|
-
assert.equal(element.renderValue, 'Banana')
|
|
206
|
-
})
|
|
207
|
-
|
|
208
|
-
it('should handle multiple selected options by selecting the first one', async () => {
|
|
209
|
-
const element = await multipleSelectedFixture()
|
|
210
|
-
await element.updateComplete
|
|
211
|
-
await nextFrame()
|
|
212
|
-
|
|
213
|
-
// When multiple options have selected attribute, only the first one should be selected
|
|
214
|
-
assert.equal(element.value, 'apple')
|
|
215
|
-
assert.isNotNull(element.selectedItem)
|
|
216
|
-
assert.equal(element.selectedItem!.value, 'apple') // First selected item
|
|
217
|
-
assert.equal(element.renderValue, 'Apple') // Display value should match the first selected item
|
|
218
|
-
})
|
|
219
|
-
|
|
220
|
-
it('should prioritize value over selected attribute', async () => {
|
|
221
|
-
const element = await selectedWithValueFixture()
|
|
222
|
-
await element.updateComplete
|
|
223
|
-
|
|
224
|
-
assert.equal(element.value, 'cherry')
|
|
225
|
-
assert.isNotNull(element.selectedItem)
|
|
226
|
-
assert.equal(element.selectedItem!.value, 'cherry')
|
|
227
|
-
assert.equal(element.renderValue, 'Cherry')
|
|
228
|
-
})
|
|
229
|
-
|
|
230
|
-
it('should update value when options change dynamically', async () => {
|
|
231
|
-
const element = await dynamicOptionsFixture()
|
|
232
|
-
await element.updateComplete
|
|
233
|
-
|
|
234
|
-
// Initially, there are no options, so value should be undefined
|
|
235
|
-
assert.isUndefined(element.value)
|
|
236
|
-
|
|
237
|
-
// Add options dynamically
|
|
238
|
-
element.innerHTML = `
|
|
239
|
-
<ui-option value="apple">Apple</ui-option>
|
|
240
|
-
<ui-option value="banana" selected>Banana</ui-option>
|
|
241
|
-
<ui-option value="cherry">Cherry</ui-option>
|
|
242
|
-
`
|
|
243
|
-
await element.updateComplete
|
|
244
|
-
await nextFrame()
|
|
245
|
-
|
|
246
|
-
// The value should now reflect the selected option
|
|
247
|
-
assert.equal(element.value, 'banana')
|
|
248
|
-
assert.isNotNull(element.selectedItem)
|
|
249
|
-
assert.equal(element.selectedItem!.value, 'banana')
|
|
250
|
-
assert.equal(element.renderValue, 'Banana')
|
|
251
|
-
})
|
|
252
|
-
})
|
|
253
|
-
|
|
254
|
-
describe('Form integration', () => {
|
|
255
|
-
it('should have no form when not in form', async () => {
|
|
256
|
-
const element = await basicFixture()
|
|
257
|
-
assert.isNull(element.form)
|
|
258
|
-
})
|
|
259
|
-
|
|
260
|
-
it('should have form when in a form', async () => {
|
|
261
|
-
const form = await formFixture()
|
|
262
|
-
const select = form.querySelector('ui-select')!
|
|
263
|
-
assert.ok(select.form, 'has a form')
|
|
264
|
-
assert.isTrue(select.form === form, 'has the parent form')
|
|
265
|
-
})
|
|
266
|
-
|
|
267
|
-
it('should participate in form submission', async () => {
|
|
268
|
-
const form = await formFixture()
|
|
269
|
-
const select = form.querySelector('ui-select')!
|
|
270
|
-
select.value = 'apple'
|
|
271
|
-
await select.updateComplete
|
|
272
|
-
|
|
273
|
-
const formData = new FormData(form)
|
|
274
|
-
assert.equal(formData.get('fruit'), 'apple')
|
|
275
|
-
})
|
|
276
|
-
|
|
277
|
-
it('should update form value on selection change', async () => {
|
|
278
|
-
const form = await formFixture()
|
|
279
|
-
const select = form.querySelector('ui-select')!
|
|
280
|
-
|
|
281
|
-
// Simulate selection of 'banana'
|
|
282
|
-
const bananaOption = select.querySelector('ui-option[value="banana"]') as UiOption
|
|
283
|
-
select.handleSelect(new CustomEvent('select', { detail: { item: bananaOption } }))
|
|
284
|
-
await select.updateComplete
|
|
285
|
-
|
|
286
|
-
const formData = new FormData(form)
|
|
287
|
-
assert.equal(formData.get('fruit'), 'banana', 'Form data should be "banana" after selection')
|
|
288
|
-
})
|
|
289
|
-
|
|
290
|
-
it('should clear form value when value is set to undefined', async () => {
|
|
291
|
-
const form = await formFixture()
|
|
292
|
-
const select = form.querySelector('ui-select')!
|
|
293
|
-
select.value = 'apple'
|
|
294
|
-
await select.updateComplete
|
|
295
|
-
|
|
296
|
-
let formData = new FormData(form)
|
|
297
|
-
assert.equal(formData.get('fruit'), 'apple', 'Form data should be "apple" initially')
|
|
298
|
-
|
|
299
|
-
select.value = undefined
|
|
300
|
-
await select.updateComplete
|
|
301
|
-
|
|
302
|
-
formData = new FormData(form)
|
|
303
|
-
assert.isNull(formData.get('fruit'), 'Form data should be null after clearing value')
|
|
304
|
-
})
|
|
305
|
-
|
|
306
|
-
it('should not submit when no value selected', async () => {
|
|
307
|
-
const form = await formFixture()
|
|
308
|
-
const formData = new FormData(form)
|
|
309
|
-
assert.isNull(formData.get('fruit'))
|
|
310
|
-
})
|
|
311
|
-
|
|
312
|
-
it('should reset value on form reset', async () => {
|
|
313
|
-
const form = await formFixture()
|
|
314
|
-
const select = form.querySelector('ui-select')!
|
|
315
|
-
select.value = 'apple'
|
|
316
|
-
await select.updateComplete
|
|
317
|
-
|
|
318
|
-
select.formResetCallback()
|
|
319
|
-
assert.isUndefined(select.value)
|
|
320
|
-
})
|
|
321
|
-
|
|
322
|
-
it('should restore state', async () => {
|
|
323
|
-
const element = await basicFixture()
|
|
324
|
-
element.formStateRestoreCallback('cherry')
|
|
325
|
-
assert.equal(element.value, 'cherry')
|
|
326
|
-
|
|
327
|
-
element.formStateRestoreCallback(null)
|
|
328
|
-
assert.isUndefined(element.value)
|
|
329
|
-
})
|
|
330
|
-
})
|
|
331
|
-
|
|
332
|
-
describe('Validation', () => {
|
|
333
|
-
it('should be valid by default', async () => {
|
|
334
|
-
const element = await basicFixture()
|
|
335
|
-
assert.isTrue(element.checkValidity())
|
|
336
|
-
assert.isTrue(element.validity.valid)
|
|
337
|
-
assert.equal(element.validationMessage, '')
|
|
338
|
-
})
|
|
339
|
-
|
|
340
|
-
it('should be invalid when required and no value', async () => {
|
|
341
|
-
const element = await requiredFixture()
|
|
342
|
-
element.validate()
|
|
343
|
-
await element.updateComplete
|
|
344
|
-
|
|
345
|
-
assert.isFalse(element.checkValidity())
|
|
346
|
-
assert.isFalse(element.validity.valid)
|
|
347
|
-
assert.isTrue(element.validity.valueMissing)
|
|
348
|
-
assert.equal(element.validationMessage, 'Please select an item.')
|
|
349
|
-
assert.isTrue(element.invalid)
|
|
350
|
-
assert.equal(element.invalidText, 'Please select an item.')
|
|
351
|
-
})
|
|
352
|
-
|
|
353
|
-
it('should be valid when required and has value', async () => {
|
|
354
|
-
const element = await requiredFixture()
|
|
355
|
-
element.value = 'apple'
|
|
356
|
-
await element.updateComplete
|
|
357
|
-
|
|
358
|
-
assert.isTrue(element.checkValidity())
|
|
359
|
-
assert.isTrue(element.validity.valid)
|
|
360
|
-
assert.equal(element.validationMessage, '')
|
|
361
|
-
assert.isFalse(element.invalid)
|
|
362
|
-
})
|
|
363
|
-
|
|
364
|
-
it('should display invalid state', async () => {
|
|
365
|
-
const element = await invalidFixture()
|
|
366
|
-
assert.isTrue(element.invalid)
|
|
367
|
-
assert.equal(element.invalidText, 'This field is required')
|
|
368
|
-
})
|
|
369
|
-
})
|
|
370
|
-
|
|
371
|
-
describe('Keyboard interaction', () => {
|
|
372
|
-
it('should open dropdown on Enter key', async () => {
|
|
373
|
-
const element = await basicFixture()
|
|
374
|
-
assert.isFalse(element.open)
|
|
375
|
-
|
|
376
|
-
element.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter' }))
|
|
377
|
-
await nextFrame()
|
|
378
|
-
|
|
379
|
-
assert.isTrue(element.open)
|
|
380
|
-
})
|
|
381
|
-
|
|
382
|
-
it('should open dropdown on Space key', async () => {
|
|
383
|
-
const element = await basicFixture()
|
|
384
|
-
assert.isFalse(element.open)
|
|
385
|
-
|
|
386
|
-
element.dispatchEvent(new KeyboardEvent('keydown', { key: ' ' }))
|
|
387
|
-
await nextFrame()
|
|
388
|
-
|
|
389
|
-
assert.isTrue(element.open)
|
|
390
|
-
})
|
|
391
|
-
|
|
392
|
-
it('should open dropdown on ArrowDown key', async () => {
|
|
393
|
-
const element = await basicFixture()
|
|
394
|
-
assert.isFalse(element.open)
|
|
395
|
-
|
|
396
|
-
element.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowDown' }))
|
|
397
|
-
await nextFrame()
|
|
398
|
-
|
|
399
|
-
assert.isTrue(element.open)
|
|
400
|
-
})
|
|
401
|
-
|
|
402
|
-
it('should open dropdown on ArrowUp key', async () => {
|
|
403
|
-
const element = await basicFixture()
|
|
404
|
-
assert.isFalse(element.open)
|
|
405
|
-
|
|
406
|
-
element.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowUp' }))
|
|
407
|
-
await nextFrame()
|
|
408
|
-
|
|
409
|
-
assert.isTrue(element.open)
|
|
410
|
-
})
|
|
411
|
-
|
|
412
|
-
it('should close dropdown on Escape key', async () => {
|
|
413
|
-
const element = await basicFixture()
|
|
414
|
-
element.open = true
|
|
415
|
-
await element.updateComplete
|
|
416
|
-
|
|
417
|
-
element.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape' }))
|
|
418
|
-
await nextFrame()
|
|
419
|
-
|
|
420
|
-
assert.isFalse(element.open)
|
|
421
|
-
})
|
|
422
|
-
|
|
423
|
-
it('should not respond to keyboard when disabled', async () => {
|
|
424
|
-
const element = await disabledFixture()
|
|
425
|
-
element.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter' }))
|
|
426
|
-
await nextFrame()
|
|
427
|
-
|
|
428
|
-
assert.isFalse(element.open)
|
|
429
|
-
})
|
|
430
|
-
|
|
431
|
-
it('should handle Tab key when menu is open', async () => {
|
|
432
|
-
const element = await basicFixture()
|
|
433
|
-
element.open = true
|
|
434
|
-
await element.updateComplete
|
|
435
|
-
|
|
436
|
-
element.dispatchEvent(new KeyboardEvent('keydown', { key: 'Tab' }))
|
|
437
|
-
await element.updateComplete
|
|
438
|
-
|
|
439
|
-
assert.isFalse(element.open)
|
|
440
|
-
})
|
|
441
|
-
})
|
|
442
|
-
|
|
443
|
-
describe('Mouse interaction', () => {
|
|
444
|
-
it('should open dropdown on click', async () => {
|
|
445
|
-
const element = await basicFixture()
|
|
446
|
-
assert.isFalse(element.open)
|
|
447
|
-
|
|
448
|
-
element.click()
|
|
449
|
-
await nextFrame()
|
|
450
|
-
|
|
451
|
-
assert.isTrue(element.open)
|
|
452
|
-
})
|
|
453
|
-
|
|
454
|
-
it('should not respond to click when disabled', async () => {
|
|
455
|
-
const element = await disabledFixture()
|
|
456
|
-
element.click()
|
|
457
|
-
await nextFrame()
|
|
458
|
-
|
|
459
|
-
assert.isFalse(element.open)
|
|
460
|
-
})
|
|
461
|
-
|
|
462
|
-
it('should close on blur when focus leaves component', async () => {
|
|
463
|
-
const element = await basicFixture()
|
|
464
|
-
element.open = true
|
|
465
|
-
await element.updateComplete
|
|
466
|
-
|
|
467
|
-
// Simulate blur event with no related target (focus leaving completely)
|
|
468
|
-
element.dispatchEvent(new FocusEvent('blur', { relatedTarget: null }))
|
|
469
|
-
await nextFrame()
|
|
470
|
-
|
|
471
|
-
assert.isFalse(element.open)
|
|
472
|
-
})
|
|
473
|
-
|
|
474
|
-
it('should not close on blur when focus moves to menu', async () => {
|
|
475
|
-
const element = await basicFixture()
|
|
476
|
-
element.open = true
|
|
477
|
-
await element.updateComplete
|
|
478
|
-
|
|
479
|
-
const menu = element.shadowRoot!.querySelector('.menu')!
|
|
480
|
-
// Simulate blur event with menu as related target
|
|
481
|
-
element.dispatchEvent(new FocusEvent('blur', { relatedTarget: menu as HTMLElement }))
|
|
482
|
-
await nextFrame()
|
|
483
|
-
|
|
484
|
-
assert.isTrue(element.open)
|
|
485
|
-
})
|
|
486
|
-
})
|
|
487
|
-
|
|
488
|
-
describe('Selection events', () => {
|
|
489
|
-
it('should dispatch change event when selection changes', async () => {
|
|
490
|
-
const element = await basicFixture()
|
|
491
|
-
element.open = true
|
|
492
|
-
await element.updateComplete
|
|
493
|
-
|
|
494
|
-
const changePromise = oneEvent(element, 'change')
|
|
495
|
-
|
|
496
|
-
// Simulate option selection
|
|
497
|
-
const option = element.querySelector('ui-option[value="apple"]') as UiOption
|
|
498
|
-
element.handleSelect(new CustomEvent('select', { detail: { item: option } }))
|
|
499
|
-
|
|
500
|
-
const changeEvent = (await changePromise) as CustomEvent<UiSelectChangeEvent>
|
|
501
|
-
assert.equal(changeEvent.detail.value, 'apple')
|
|
502
|
-
assert.equal(changeEvent.detail.item, option)
|
|
503
|
-
assert.isFalse(changeEvent.bubbles)
|
|
504
|
-
assert.isTrue(changeEvent.composed)
|
|
505
|
-
assert.isFalse(element.open) // Should close after selection
|
|
506
|
-
})
|
|
507
|
-
|
|
508
|
-
it('should dispatch open event when opening', async () => {
|
|
509
|
-
const element = await basicFixture()
|
|
510
|
-
await element.updateComplete
|
|
511
|
-
|
|
512
|
-
const openPromise = oneEvent(element, 'open')
|
|
513
|
-
element.open = true
|
|
514
|
-
await element.updateComplete
|
|
515
|
-
|
|
516
|
-
const openEvent = await openPromise
|
|
517
|
-
assert.isFalse(openEvent.bubbles)
|
|
518
|
-
assert.isTrue(openEvent.composed)
|
|
519
|
-
})
|
|
520
|
-
|
|
521
|
-
it('should dispatch close event when closing', async () => {
|
|
522
|
-
const element = await basicFixture()
|
|
523
|
-
element.open = true
|
|
524
|
-
await element.updateComplete
|
|
525
|
-
|
|
526
|
-
const closePromise = oneEvent(element, 'close')
|
|
527
|
-
element.open = false
|
|
528
|
-
await element.updateComplete
|
|
529
|
-
|
|
530
|
-
const closeEvent = await closePromise
|
|
531
|
-
assert.isFalse(closeEvent.bubbles)
|
|
532
|
-
assert.isTrue(closeEvent.composed)
|
|
533
|
-
})
|
|
534
|
-
})
|
|
535
|
-
|
|
536
|
-
describe('Menu integration', () => {
|
|
537
|
-
it('should highlight selected item when menu opens', async () => {
|
|
538
|
-
const element = await withValueFixture()
|
|
539
|
-
await element.updateComplete
|
|
540
|
-
|
|
541
|
-
// Check that menu shows popover when opened
|
|
542
|
-
element.open = true
|
|
543
|
-
await element.updateComplete
|
|
544
|
-
|
|
545
|
-
const menu = element.shadowRoot!.querySelector('ui-menu')!
|
|
546
|
-
assert.isTrue(menu.matches(':popover-open'))
|
|
547
|
-
})
|
|
548
|
-
|
|
549
|
-
it('should highlight first item when no selection and menu opens', async () => {
|
|
550
|
-
const element = await basicFixture()
|
|
551
|
-
await element.updateComplete
|
|
552
|
-
|
|
553
|
-
// Check that menu opens correctly
|
|
554
|
-
element.open = true
|
|
555
|
-
await element.updateComplete
|
|
556
|
-
|
|
557
|
-
const menu = element.shadowRoot!.querySelector('ui-menu')!
|
|
558
|
-
assert.isTrue(menu.matches(':popover-open'))
|
|
559
|
-
})
|
|
560
|
-
|
|
561
|
-
it('should handle menu close event', async () => {
|
|
562
|
-
const element = await basicFixture()
|
|
563
|
-
element.open = true
|
|
564
|
-
await element.updateComplete
|
|
565
|
-
|
|
566
|
-
element.handleMenuClose()
|
|
567
|
-
assert.isFalse(element.open)
|
|
568
|
-
})
|
|
569
|
-
|
|
570
|
-
it('should handle highlight change events', async () => {
|
|
571
|
-
const element = await basicFixture()
|
|
572
|
-
const option = element.querySelector('ui-option') as UiOption
|
|
573
|
-
option.id = 'test-option'
|
|
574
|
-
|
|
575
|
-
element.handleHighlightChange(
|
|
576
|
-
new CustomEvent('highlightchange', {
|
|
577
|
-
detail: { item: option },
|
|
578
|
-
})
|
|
579
|
-
)
|
|
580
|
-
|
|
581
|
-
await element.updateComplete
|
|
582
|
-
|
|
583
|
-
// Check that the highlight change was processed
|
|
584
|
-
// by verifying the DOM reflects the active descendant
|
|
585
|
-
const container = element.shadowRoot!.querySelector('.ui-select')!
|
|
586
|
-
assert.equal(container.getAttribute('aria-activedescendant'), 'test-option')
|
|
587
|
-
})
|
|
588
|
-
|
|
589
|
-
it('should clear aria-activedescendant when no item highlighted', async () => {
|
|
590
|
-
const element = await basicFixture()
|
|
591
|
-
element.handleHighlightChange(
|
|
592
|
-
new CustomEvent('highlightchange', {
|
|
593
|
-
detail: { item: null },
|
|
594
|
-
})
|
|
595
|
-
)
|
|
596
|
-
|
|
597
|
-
// Check that aria-activedescendant is cleared
|
|
598
|
-
const container = element.shadowRoot!.querySelector('.ui-select')!
|
|
599
|
-
assert.equal(container.getAttribute('aria-activedescendant'), '')
|
|
600
|
-
})
|
|
601
|
-
})
|
|
602
|
-
|
|
603
|
-
describe('Accessibility', () => {
|
|
604
|
-
it('should update aria-expanded when opening/closing', async () => {
|
|
605
|
-
const element = await basicFixture()
|
|
606
|
-
await element.updateComplete
|
|
607
|
-
|
|
608
|
-
element.open = true
|
|
609
|
-
await element.updateComplete
|
|
610
|
-
assert.equal(element.getAttribute('aria-expanded'), 'true')
|
|
611
|
-
|
|
612
|
-
element.open = false
|
|
613
|
-
await element.updateComplete
|
|
614
|
-
assert.equal(element.getAttribute('aria-expanded'), 'false')
|
|
615
|
-
})
|
|
616
|
-
|
|
617
|
-
it('should set aria-label from label property', async () => {
|
|
618
|
-
const element = await basicFixture()
|
|
619
|
-
element.label = 'Test label'
|
|
620
|
-
await element.updateComplete
|
|
621
|
-
|
|
622
|
-
assert.equal(element.getAttribute('aria-label'), 'Test label')
|
|
623
|
-
})
|
|
624
|
-
|
|
625
|
-
it('should remove aria-label when label is cleared', async () => {
|
|
626
|
-
const element = await basicFixture()
|
|
627
|
-
element.label = undefined
|
|
628
|
-
await element.updateComplete
|
|
629
|
-
|
|
630
|
-
assert.isFalse(element.hasAttribute('aria-label'))
|
|
631
|
-
})
|
|
632
|
-
|
|
633
|
-
it('should support aria-activedescendant', async () => {
|
|
634
|
-
const element = await basicFixture()
|
|
635
|
-
const option = element.querySelector('ui-option') as UiOption
|
|
636
|
-
option.id = 'test-id'
|
|
637
|
-
|
|
638
|
-
// Simulate highlight change to set aria-activedescendant
|
|
639
|
-
element.handleHighlightChange(
|
|
640
|
-
new CustomEvent('highlightchange', {
|
|
641
|
-
detail: { item: option },
|
|
642
|
-
})
|
|
643
|
-
)
|
|
644
|
-
await element.updateComplete
|
|
645
|
-
|
|
646
|
-
const container = element.shadowRoot!.querySelector('.ui-select')!
|
|
647
|
-
assert.equal(container.getAttribute('aria-activedescendant'), 'test-id')
|
|
648
|
-
})
|
|
649
|
-
})
|
|
650
|
-
|
|
651
|
-
describe('Edge cases', () => {
|
|
652
|
-
it('should handle empty option list', async () => {
|
|
653
|
-
const element = await emptyFixture()
|
|
654
|
-
element.value = 'nonexistent'
|
|
655
|
-
await element.updateComplete
|
|
656
|
-
|
|
657
|
-
assert.equal(element.value, 'nonexistent')
|
|
658
|
-
assert.isNull(element.selectedItem)
|
|
659
|
-
assert.equal(element.renderValue, '')
|
|
660
|
-
})
|
|
661
|
-
|
|
662
|
-
it('should handle setting open before menu is rendered', async () => {
|
|
663
|
-
const element = await basicFixture()
|
|
664
|
-
await element.updateComplete
|
|
665
|
-
|
|
666
|
-
// This should not throw an error even with timing issues
|
|
667
|
-
element.open = true
|
|
668
|
-
await element.updateComplete
|
|
669
|
-
|
|
670
|
-
assert.isTrue(element.open)
|
|
671
|
-
|
|
672
|
-
element.open = false
|
|
673
|
-
await element.updateComplete
|
|
674
|
-
|
|
675
|
-
assert.isFalse(element.open)
|
|
676
|
-
})
|
|
677
|
-
|
|
678
|
-
it('should maintain focus during interaction', async () => {
|
|
679
|
-
const element = await basicFixture()
|
|
680
|
-
const focusSpy = sinon.spy(element, 'focus')
|
|
681
|
-
|
|
682
|
-
// Simulate selection
|
|
683
|
-
const option = element.querySelector('ui-option[value="apple"]') as UiOption
|
|
684
|
-
element.handleSelect(new CustomEvent('select', { detail: { item: option } }))
|
|
685
|
-
|
|
686
|
-
assert.isTrue(focusSpy.called)
|
|
687
|
-
})
|
|
688
|
-
|
|
689
|
-
it('should handle selection with prevented event', async () => {
|
|
690
|
-
const element = await basicFixture()
|
|
691
|
-
element.open = true
|
|
692
|
-
await element.updateComplete
|
|
693
|
-
|
|
694
|
-
const event = new CustomEvent('select', {
|
|
695
|
-
detail: { item: element.querySelector('ui-option') as UiOption },
|
|
696
|
-
})
|
|
697
|
-
const stopPropagationSpy = sinon.spy(event, 'stopPropagation')
|
|
698
|
-
|
|
699
|
-
element.handleSelect(event)
|
|
700
|
-
assert.isTrue(stopPropagationSpy.called)
|
|
701
|
-
})
|
|
702
|
-
})
|
|
703
|
-
|
|
704
|
-
describe('Rendering', () => {
|
|
705
|
-
it('should render with correct classes', async () => {
|
|
706
|
-
const element = await basicFixture()
|
|
707
|
-
await element.updateComplete
|
|
708
|
-
|
|
709
|
-
const container = element.shadowRoot!.querySelector('.ui-select')!
|
|
710
|
-
assert.isTrue(container.classList.contains('ui-select'))
|
|
711
|
-
assert.isFalse(container.classList.contains('open'))
|
|
712
|
-
assert.isFalse(container.classList.contains('disabled'))
|
|
713
|
-
})
|
|
714
|
-
|
|
715
|
-
it('should render with open class when open', async () => {
|
|
716
|
-
const element = await basicFixture()
|
|
717
|
-
element.open = true
|
|
718
|
-
await element.updateComplete
|
|
719
|
-
|
|
720
|
-
const container = element.shadowRoot!.querySelector('.ui-select')!
|
|
721
|
-
assert.isTrue(container.classList.contains('open'))
|
|
722
|
-
})
|
|
723
|
-
|
|
724
|
-
it('should render with disabled class when disabled', async () => {
|
|
725
|
-
const element = await disabledFixture()
|
|
726
|
-
await element.updateComplete
|
|
727
|
-
|
|
728
|
-
const container = element.shadowRoot!.querySelector('.ui-select')!
|
|
729
|
-
assert.isTrue(container.classList.contains('disabled'))
|
|
730
|
-
})
|
|
731
|
-
|
|
732
|
-
it('should render focus ring', async () => {
|
|
733
|
-
const element = await basicFixture()
|
|
734
|
-
await element.updateComplete
|
|
735
|
-
|
|
736
|
-
const focusRing = element.shadowRoot!.querySelector('md-focus-ring')!
|
|
737
|
-
assert.isNotNull(focusRing)
|
|
738
|
-
assert.equal(focusRing.getAttribute('part'), 'focus-ring')
|
|
739
|
-
})
|
|
740
|
-
|
|
741
|
-
it('should render text field with correct properties', async () => {
|
|
742
|
-
const element = await withValueFixture()
|
|
743
|
-
await element.updateComplete
|
|
744
|
-
|
|
745
|
-
const textField = element.shadowRoot!.querySelector('ui-outlined-text-field')!
|
|
746
|
-
assert.equal(textField.label, 'Select an option')
|
|
747
|
-
// The text field should show the render value of the selected option
|
|
748
|
-
assert.equal(textField.value, element.renderValue)
|
|
749
|
-
assert.isTrue(textField.hasAttribute('readonly'))
|
|
750
|
-
assert.equal(textField.tabIndex, -1)
|
|
751
|
-
assert.isTrue(textField.hasAttribute('inert'))
|
|
752
|
-
assert.equal(textField.getAttribute('aria-hidden'), 'true')
|
|
753
|
-
})
|
|
754
|
-
|
|
755
|
-
it('should render dropdown icon', async () => {
|
|
756
|
-
const element = await basicFixture()
|
|
757
|
-
await element.updateComplete
|
|
758
|
-
|
|
759
|
-
const icon = element.shadowRoot!.querySelector('ui-icon[slot="suffix"]')!
|
|
760
|
-
assert.equal(icon.textContent!.trim(), 'arrow_drop_down')
|
|
761
|
-
})
|
|
762
|
-
|
|
763
|
-
it('should render menu with correct attributes', async () => {
|
|
764
|
-
const element = await basicFixture()
|
|
765
|
-
await element.updateComplete
|
|
766
|
-
|
|
767
|
-
const menu = element.shadowRoot!.querySelector('ui-menu')!
|
|
768
|
-
assert.equal(menu.id, 'menu')
|
|
769
|
-
assert.equal(menu.getAttribute('popover'), 'auto')
|
|
770
|
-
assert.equal(menu.getAttribute('selector'), 'ui-option')
|
|
771
|
-
})
|
|
772
|
-
|
|
773
|
-
it('should slot options correctly', async () => {
|
|
774
|
-
const element = await basicFixture()
|
|
775
|
-
await element.updateComplete
|
|
776
|
-
|
|
777
|
-
const options = element.querySelectorAll('ui-option')
|
|
778
|
-
assert.equal(options.length, 3)
|
|
779
|
-
assert.equal(options[0].value, 'apple')
|
|
780
|
-
assert.equal(options[1].value, 'banana')
|
|
781
|
-
assert.equal(options[2].value, 'cherry')
|
|
782
|
-
})
|
|
783
|
-
})
|
|
784
|
-
|
|
785
|
-
describe('Selected attribute discovery', () => {
|
|
786
|
-
it('should discover and set value from selected attribute on initialization', async () => {
|
|
787
|
-
const element = await selectedAttributeFixture()
|
|
788
|
-
await element.updateComplete
|
|
789
|
-
await nextFrame()
|
|
790
|
-
|
|
791
|
-
assert.equal(element.value, 'banana', 'value should be set from selected option')
|
|
792
|
-
assert.isNotNull(element.selectedItem, 'selected item should not be null')
|
|
793
|
-
assert.equal(element.selectedItem!.value, 'banana', 'selected item should be the banana option')
|
|
794
|
-
assert.equal(element.renderValue, 'Banana', 'render value should match selected option')
|
|
795
|
-
})
|
|
796
|
-
|
|
797
|
-
it('should handle case with no selected options', async () => {
|
|
798
|
-
const element = await basicFixture()
|
|
799
|
-
await element.updateComplete
|
|
800
|
-
await nextFrame()
|
|
801
|
-
|
|
802
|
-
assert.isUndefined(element.value, 'value should be undefined when no options are selected')
|
|
803
|
-
assert.isNull(element.selectedItem, 'selected item should be null when no options are selected')
|
|
804
|
-
assert.equal(element.renderValue, '', 'render value should be empty when no options are selected')
|
|
805
|
-
})
|
|
806
|
-
|
|
807
|
-
it('should select the first option when multiple options have selected attribute', async () => {
|
|
808
|
-
const element = await multipleSelectedFixture()
|
|
809
|
-
await element.updateComplete
|
|
810
|
-
await nextFrame()
|
|
811
|
-
|
|
812
|
-
assert.equal(element.value, 'apple', 'should select the first option with selected attribute')
|
|
813
|
-
assert.isNotNull(element.selectedItem, 'selected item should not be null')
|
|
814
|
-
assert.equal(element.selectedItem!.value, 'apple', 'selected item should be the first selected option')
|
|
815
|
-
assert.equal(element.renderValue, 'Apple', 'render value should match first selected option')
|
|
816
|
-
})
|
|
817
|
-
|
|
818
|
-
it('should prioritize explicit value property over selected attribute', async () => {
|
|
819
|
-
const element = await selectedWithValueFixture()
|
|
820
|
-
await element.updateComplete
|
|
821
|
-
await nextFrame()
|
|
822
|
-
|
|
823
|
-
assert.equal(element.value, 'cherry', 'value property should take precedence')
|
|
824
|
-
assert.isNotNull(element.selectedItem, 'selected item should not be null')
|
|
825
|
-
assert.equal(element.selectedItem!.value, 'cherry', 'selected item should match value property')
|
|
826
|
-
assert.equal(element.renderValue, 'Cherry', 'render value should match value property')
|
|
827
|
-
})
|
|
828
|
-
|
|
829
|
-
it('should rediscover selected options when value is cleared', async () => {
|
|
830
|
-
const element = await selectedAttributeFixture()
|
|
831
|
-
await element.updateComplete
|
|
832
|
-
await nextFrame()
|
|
833
|
-
|
|
834
|
-
// First, verify it's initialized correctly
|
|
835
|
-
assert.equal(element.value, 'banana')
|
|
836
|
-
|
|
837
|
-
// Now clear the value and it should fall back to selected attribute
|
|
838
|
-
element.value = undefined
|
|
839
|
-
await element.updateComplete
|
|
840
|
-
await nextFrame()
|
|
841
|
-
|
|
842
|
-
assert.equal(element.value, 'banana', 'should rediscover selected option when value is cleared')
|
|
843
|
-
assert.isNotNull(element.selectedItem, 'selected item should not be null')
|
|
844
|
-
assert.equal(element.selectedItem!.value, 'banana', 'selected item should be rediscovered')
|
|
845
|
-
})
|
|
846
|
-
|
|
847
|
-
it('should handle dynamically added options with selected attribute', async () => {
|
|
848
|
-
const element = await dynamicOptionsFixture()
|
|
849
|
-
await element.updateComplete
|
|
850
|
-
await nextFrame()
|
|
851
|
-
|
|
852
|
-
// Initially no options
|
|
853
|
-
assert.isUndefined(element.value)
|
|
854
|
-
assert.isNull(element.selectedItem)
|
|
855
|
-
|
|
856
|
-
// Add options dynamically
|
|
857
|
-
element.innerHTML = `
|
|
858
|
-
<ui-option value="apple">Apple</ui-option>
|
|
859
|
-
<ui-option value="banana" selected>Banana</ui-option>
|
|
860
|
-
<ui-option value="cherry">Cherry</ui-option>
|
|
861
|
-
`
|
|
862
|
-
|
|
863
|
-
await element.updateComplete
|
|
864
|
-
await nextFrame()
|
|
865
|
-
|
|
866
|
-
assert.equal(element.value, 'banana', 'should discover selected option from dynamically added content')
|
|
867
|
-
assert.isNotNull(element.selectedItem, 'selected item should not be null')
|
|
868
|
-
assert.equal(
|
|
869
|
-
(element.selectedItem as UiOption)!.value,
|
|
870
|
-
'banana',
|
|
871
|
-
'selected item should be the dynamically added selected option'
|
|
872
|
-
)
|
|
873
|
-
})
|
|
874
|
-
|
|
875
|
-
it('should update when selected attribute is added to existing option', async () => {
|
|
876
|
-
const element = await basicFixture()
|
|
877
|
-
await element.updateComplete
|
|
878
|
-
await nextFrame()
|
|
879
|
-
|
|
880
|
-
// Initially no selection
|
|
881
|
-
assert.isUndefined(element.value)
|
|
882
|
-
assert.isNull(element.selectedItem)
|
|
883
|
-
|
|
884
|
-
// Add selected attribute to an option
|
|
885
|
-
const bananaOption = element.querySelector('ui-option[value="banana"]') as UiOption
|
|
886
|
-
bananaOption.selected = true
|
|
887
|
-
|
|
888
|
-
// Trigger setCurrentOption manually since we changed the DOM
|
|
889
|
-
await (element as UiSelect & { setCurrentOption(): Promise<void> }).setCurrentOption()
|
|
890
|
-
await element.updateComplete
|
|
891
|
-
await nextFrame()
|
|
892
|
-
|
|
893
|
-
assert.equal(element.value, 'banana', 'should discover newly selected option')
|
|
894
|
-
assert.isNotNull(element.selectedItem, 'selected item should not be null')
|
|
895
|
-
assert.equal(
|
|
896
|
-
(element.selectedItem as UiOption)!.value,
|
|
897
|
-
'banana',
|
|
898
|
-
'selected item should be the newly selected option'
|
|
899
|
-
)
|
|
900
|
-
})
|
|
901
|
-
|
|
902
|
-
it('should clear selection when selected attribute is removed from all options', async () => {
|
|
903
|
-
const element = await selectedAttributeFixture()
|
|
904
|
-
await element.updateComplete
|
|
905
|
-
await nextFrame()
|
|
906
|
-
|
|
907
|
-
// Initially has selection
|
|
908
|
-
assert.equal(element.value, 'banana')
|
|
909
|
-
|
|
910
|
-
// Remove selected attribute
|
|
911
|
-
const bananaOption = element.querySelector('ui-option[value="banana"]') as UiOption
|
|
912
|
-
bananaOption.selected = false
|
|
913
|
-
|
|
914
|
-
// Clear the value to trigger rediscovery
|
|
915
|
-
element.value = undefined
|
|
916
|
-
await element.updateComplete
|
|
917
|
-
await nextFrame()
|
|
918
|
-
|
|
919
|
-
assert.isUndefined(element.value, 'value should be undefined when no options are selected')
|
|
920
|
-
assert.isNull(element.selectedItem, 'selected item should be null when no options are selected')
|
|
921
|
-
assert.equal(element.renderValue, '', 'render value should be empty when no options are selected')
|
|
922
|
-
})
|
|
923
|
-
})
|
|
924
|
-
})
|
|
925
|
-
})
|