@sapudex/web-components 2.61.0 → 2.62.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cypress/specs/AccordionSpotlight.cy.tsx +43 -2
- package/cypress/specs/Carousel.cy.tsx +88 -0
- package/cypress/specs/CountrySelector.cy.tsx +233 -6
- package/cypress/specs/FilterMenu.cy.tsx +39 -45
- package/cypress/specs/List.cy.tsx +624 -10
- package/cypress/specs/ListItem.cy.tsx +50 -0
- package/cypress/specs/SelectBox.cy.tsx +239 -131
- package/cypress/specs/SelectBoxAccessibility.cy.tsx +3109 -0
- package/cypress/specs/TagGroup.cy.tsx +168 -0
- package/dist/AccordionSpotlight.d.ts +7 -2
- package/dist/AccordionSpotlight.js +31 -6
- package/dist/AccordionSpotlight.js.map +1 -1
- package/dist/AccordionSpotlightItem.d.ts +3 -1
- package/dist/AccordionSpotlightItem.js +3 -1
- package/dist/AccordionSpotlightItem.js.map +1 -1
- package/dist/Carousel.d.ts +1 -0
- package/dist/Carousel.js +19 -3
- package/dist/Carousel.js.map +1 -1
- package/dist/CountrySelector.js +1 -3
- package/dist/CountrySelector.js.map +1 -1
- package/dist/List.d.ts +2 -0
- package/dist/List.js +22 -4
- package/dist/List.js.map +1 -1
- package/dist/ListGroupHeader.js +5 -1
- package/dist/ListGroupHeader.js.map +1 -1
- package/dist/ListGroupHeaderBase.d.ts +2 -0
- package/dist/ListGroupHeaderBase.js +15 -2
- package/dist/ListGroupHeaderBase.js.map +1 -1
- package/dist/ListGroupHeaderTemplate.js +14 -22
- package/dist/ListGroupHeaderTemplate.js.map +1 -1
- package/dist/ListItem.d.ts +1 -2
- package/dist/ListItem.js +14 -12
- package/dist/ListItem.js.map +1 -1
- package/dist/ListItemBase.d.ts +5 -0
- package/dist/ListItemBase.js +11 -1
- package/dist/ListItemBase.js.map +1 -1
- package/dist/ListItemTemplate.js +1 -1
- package/dist/ListItemTemplate.js.map +1 -1
- package/dist/ListSelectAllTemplate.js +1 -1
- package/dist/ListSelectAllTemplate.js.map +1 -1
- package/dist/ListTemplate.js +2 -1
- package/dist/ListTemplate.js.map +1 -1
- package/dist/Search.d.ts +13 -3
- package/dist/Search.js +36 -11
- package/dist/Search.js.map +1 -1
- package/dist/SearchableListBase.d.ts +2 -0
- package/dist/SearchableListBase.js +13 -3
- package/dist/SearchableListBase.js.map +1 -1
- package/dist/SearchableListBaseTemplate.js +42 -33
- package/dist/SearchableListBaseTemplate.js.map +1 -1
- package/dist/SelectBase.d.ts +18 -33
- package/dist/SelectBase.js +124 -139
- package/dist/SelectBase.js.map +1 -1
- package/dist/SelectBox.d.ts +3 -0
- package/dist/SelectBox.js +19 -7
- package/dist/SelectBox.js.map +1 -1
- package/dist/SelectBoxTemplate.js +3 -3
- package/dist/SelectBoxTemplate.js.map +1 -1
- package/dist/SideNavigationItem.js +2 -0
- package/dist/SideNavigationItem.js.map +1 -1
- package/dist/SideNavigationItemTemplate.d.ts +2 -0
- package/dist/SideNavigationItemTemplate.js +15 -0
- package/dist/SideNavigationItemTemplate.js.map +1 -0
- package/dist/SideNavigationMobile.js +1 -2
- package/dist/SideNavigationMobile.js.map +1 -1
- package/dist/SideNavigationSubItem.js +2 -1
- package/dist/SideNavigationSubItem.js.map +1 -1
- package/dist/SideNavigationSubItemTemplate.js +1 -2
- package/dist/SideNavigationSubItemTemplate.js.map +1 -1
- package/dist/TagGroup.d.ts +3 -0
- package/dist/TagGroup.js +31 -0
- package/dist/TagGroup.js.map +1 -1
- package/dist/TagGroupTemplate.js +1 -1
- package/dist/TagGroupTemplate.js.map +1 -1
- package/dist/assets/{InputSuggestions-1OnUn6pM.js → InputSuggestions-BPKrPqPb.js} +1 -1
- package/dist/assets/{ListItemStandardExpandableTextTemplate-CoBAMp_v.js → ListItemStandardExpandableTextTemplate-DkdOcCxC.js} +1 -1
- package/dist/assets/{bundle.esm-BqaVWFKf.js → bundle.esm-17Yt5gDr.js} +232 -233
- package/dist/css/themes/AccordionSpotlight.css +1 -1
- package/dist/css/themes/ListGroupHeader.css +1 -1
- package/dist/css/themes/SideNavigationItem.css +1 -1
- package/dist/css/themes/SideNavigationMobile.css +1 -1
- package/dist/css/themes/SideNavigationSubItem.css +1 -1
- package/dist/css/themes/Table.css +1 -1
- package/dist/custom-elements-internal.json +7 -7
- package/dist/custom-elements.json +7 -7
- package/dist/generated/i18n/i18n-defaults.d.ts +2 -1
- package/dist/generated/i18n/i18n-defaults.js +2 -1
- package/dist/generated/i18n/i18n-defaults.js.map +1 -1
- package/dist/generated/themes/AccordionSpotlight.css.d.ts +1 -1
- package/dist/generated/themes/AccordionSpotlight.css.js +1 -1
- package/dist/generated/themes/AccordionSpotlight.css.js.map +1 -1
- package/dist/generated/themes/ListGroupHeader.css.d.ts +1 -1
- package/dist/generated/themes/ListGroupHeader.css.js +1 -1
- package/dist/generated/themes/ListGroupHeader.css.js.map +1 -1
- package/dist/generated/themes/SideNavigationItem.css.d.ts +1 -1
- package/dist/generated/themes/SideNavigationItem.css.js +1 -1
- package/dist/generated/themes/SideNavigationItem.css.js.map +1 -1
- package/dist/generated/themes/SideNavigationMobile.css.d.ts +1 -1
- package/dist/generated/themes/SideNavigationMobile.css.js +1 -1
- package/dist/generated/themes/SideNavigationMobile.css.js.map +1 -1
- package/dist/generated/themes/SideNavigationSubItem.css.d.ts +1 -1
- package/dist/generated/themes/SideNavigationSubItem.css.js +1 -1
- package/dist/generated/themes/SideNavigationSubItem.css.js.map +1 -1
- package/dist/generated/themes/Table.css.d.ts +1 -1
- package/dist/generated/themes/Table.css.js +1 -1
- package/dist/generated/themes/Table.css.js.map +1 -1
- package/dist/messagebundle.properties +3 -0
- package/dist/vscode.html-custom-data.json +4 -4
- package/dist/web-types.json +9 -9
- package/package.json +1 -1
- package/src/AccordionSpotlight.ts +35 -6
- package/src/AccordionSpotlightItem.ts +3 -1
- package/src/Carousel.ts +22 -3
- package/src/CountrySelector.ts +1 -3
- package/src/List.ts +21 -4
- package/src/ListGroupHeader.ts +5 -1
- package/src/ListGroupHeaderBase.ts +16 -2
- package/src/ListGroupHeaderTemplate.tsx +29 -47
- package/src/ListItem.ts +14 -13
- package/src/ListItemBase.ts +14 -1
- package/src/ListItemTemplate.tsx +4 -4
- package/src/ListSelectAllTemplate.tsx +10 -8
- package/src/ListTemplate.tsx +14 -10
- package/src/Search.ts +48 -13
- package/src/SearchableListBase.ts +15 -3
- package/src/SearchableListBaseTemplate.tsx +51 -36
- package/src/SelectBase.ts +130 -144
- package/src/SelectBox.ts +21 -7
- package/src/SelectBoxTemplate.tsx +10 -9
- package/src/SideNavigationItem.ts +2 -0
- package/src/SideNavigationItemTemplate.tsx +97 -0
- package/src/SideNavigationMobile.ts +1 -2
- package/src/SideNavigationSubItem.ts +2 -1
- package/src/SideNavigationSubItemTemplate.tsx +7 -6
- package/src/TagGroup.ts +32 -0
- package/src/TagGroupTemplate.tsx +1 -1
- package/src/generated/i18n/i18n-defaults.ts +2 -2
- package/src/generated/themes/AccordionSpotlight.css.ts +1 -1
- package/src/generated/themes/ListGroupHeader.css.ts +1 -1
- package/src/generated/themes/SideNavigationItem.css.ts +1 -1
- package/src/generated/themes/SideNavigationMobile.css.ts +1 -1
- package/src/generated/themes/SideNavigationSubItem.css.ts +1 -1
- package/src/generated/themes/Table.css.ts +1 -1
- package/src/i18n/messagebundle.properties +3 -0
- package/src/themes/AccordionSpotlight.css +9 -9
- package/src/themes/ListGroupHeader.css +2 -43
- package/src/themes/SideNavigationItem.css +56 -6
- package/src/themes/SideNavigationMobile.css +61 -15
- package/src/themes/SideNavigationSubItem.css +14 -109
- package/src/themes/Table.css +6 -1
- package/dist/css/themes/SideNavigationMobileDropdown.css +0 -1
- package/dist/generated/themes/SideNavigationMobileDropdown.css.d.ts +0 -2
- package/dist/generated/themes/SideNavigationMobileDropdown.css.js +0 -8
- package/dist/generated/themes/SideNavigationMobileDropdown.css.js.map +0 -1
- package/src/generated/themes/SideNavigationMobileDropdown.css.ts +0 -9
- package/src/themes/SideNavigationMobileDropdown.css +0 -36
|
@@ -362,18 +362,27 @@ describe("List - collapsible group header uses tree pattern", () => {
|
|
|
362
362
|
.find(".udex-list__group-content").should("have.attr", "role", "treeitem");
|
|
363
363
|
});
|
|
364
364
|
|
|
365
|
-
|
|
365
|
+
// Updated: the header owns a same-shadow sibling group without including its leaves in the visible label.
|
|
366
|
+
it("SingleSelect collapsible header: owns the sibling content group without nesting its slot", () => {
|
|
366
367
|
cy.mount(collapsibleListSingleSelect);
|
|
367
368
|
cy.get("#group-header-collapsible").shadow()
|
|
368
|
-
.find(".udex-list__group-list > .udex-list__group-content
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
369
|
+
.find(".udex-list__group-list > .udex-list__group-content.udex-list__group-header-wrap[role='treeitem']")
|
|
370
|
+
.should(UDExHeader => {
|
|
371
|
+
const UDExRow = UDExHeader[0];
|
|
372
|
+
const UDExShadow = UDExRow.getRootNode() as ShadowRoot;
|
|
373
|
+
const UDExOwnedId = UDExRow.getAttribute("aria-owns");
|
|
374
|
+
expect(UDExOwnedId).to.match(/^\S+$/);
|
|
375
|
+
expect(UDExRow.getAttribute("aria-controls")).to.equal(UDExOwnedId);
|
|
376
|
+
const UDExGroups = UDExShadow.querySelectorAll(`[id="${UDExOwnedId}"]`);
|
|
377
|
+
expect(UDExGroups).to.have.length(1);
|
|
378
|
+
const UDExGroup = UDExGroups[0];
|
|
379
|
+
expect(UDExGroup.matches(".udex-list__group-list__content[role=group]")).to.equal(true);
|
|
380
|
+
expect(UDExGroup.getRootNode()).to.equal(UDExShadow);
|
|
381
|
+
expect(UDExGroup.parentElement).to.equal(UDExRow.parentElement);
|
|
382
|
+
expect(UDExRow.nextElementSibling).to.equal(UDExGroup);
|
|
383
|
+
expect(UDExRow.querySelector("slot, [role=group], [udex-list-item]")).to.equal(null);
|
|
384
|
+
expect(UDExGroup.querySelector("slot")!.assignedElements()).to.have.length(2);
|
|
385
|
+
});
|
|
377
386
|
});
|
|
378
387
|
|
|
379
388
|
it("child items expose accessible-role=\"treeitem\" on the host", () => {
|
|
@@ -448,6 +457,59 @@ describe("List - flat MultiSelect selection announcement", () => {
|
|
|
448
457
|
});
|
|
449
458
|
});
|
|
450
459
|
|
|
460
|
+
describe("List component - accessible role attribute reflection", () => {
|
|
461
|
+
it("preserves the default listbox role for standalone lists", () => {
|
|
462
|
+
cy.mount(<UDExList><UDExListItem>Item 1</UDExListItem></UDExList>);
|
|
463
|
+
|
|
464
|
+
cy.get("[udex-list]").should("have.attr", "accessible-role", "listbox");
|
|
465
|
+
cy.get("[udex-list]").shadow().find("#udex-list").should("have.attr", "role", "listbox");
|
|
466
|
+
});
|
|
467
|
+
|
|
468
|
+
it("reflects an empty role configuration when the host owns the role", () => {
|
|
469
|
+
cy.mount(<UDExList role="tree" accessibleRole=""><UDExListItem>Item 1</UDExListItem></UDExList>);
|
|
470
|
+
|
|
471
|
+
cy.get("[udex-list]").should("have.attr", "role", "tree").and("have.attr", "accessible-role", "");
|
|
472
|
+
cy.get("[udex-list]").shadow().find("#udex-list").should("have.attr", "role", "none");
|
|
473
|
+
});
|
|
474
|
+
|
|
475
|
+
it("updates the inner role when an explicit attribute is set", () => {
|
|
476
|
+
cy.mount(<UDExList><UDExListItem>Item 1</UDExListItem></UDExList>);
|
|
477
|
+
|
|
478
|
+
cy.get("[udex-list]").shadow().find("#udex-list").should("have.attr", "role", "listbox");
|
|
479
|
+
cy.get("[udex-list]").invoke("attr", "accessible-role", "tree");
|
|
480
|
+
cy.get("[udex-list]").shadow().find("#udex-list").should("have.attr", "role", "tree");
|
|
481
|
+
});
|
|
482
|
+
});
|
|
483
|
+
|
|
484
|
+
describe("ListItem - removing an explicit accessible role", () => {
|
|
485
|
+
(["SingleSelect", "MultiSelect", "SingleContent"] as const).forEach(UDExMode => {
|
|
486
|
+
it(`${UDExMode}: restores fallback roles and selection semantics after removeAttr`, () => {
|
|
487
|
+
cy.mount(<UDExListItem mode={UDExMode} accessibleRole="treeitem">Item 1</UDExListItem>);
|
|
488
|
+
const UDExRole = UDExMode === "SingleContent" ? "listitem" : "option";
|
|
489
|
+
const UDExRow = () => cy.get("[udex-list-item]").shadow().find("[part=item]");
|
|
490
|
+
|
|
491
|
+
UDExRow().should("have.attr", "role", "treeitem");
|
|
492
|
+
cy.get("[udex-list-item]").should("have.prop", "effectiveRole", "treeitem");
|
|
493
|
+
// Attribute removal resets accessibleRole to null, not an empty string.
|
|
494
|
+
cy.get("[udex-list-item]").invoke("removeAttr", "accessible-role");
|
|
495
|
+
cy.get("[udex-list-item]").should(UDExItem => {
|
|
496
|
+
expect(UDExItem).not.to.have.attr("accessible-role");
|
|
497
|
+
expect(UDExItem).to.have.prop("roleItem", UDExRole);
|
|
498
|
+
expect(UDExItem).to.have.prop("effectiveRole", "option");
|
|
499
|
+
});
|
|
500
|
+
UDExRow().should(UDExItem => {
|
|
501
|
+
expect(UDExItem).to.have.attr("role", UDExRole);
|
|
502
|
+
expect(UDExItem).not.to.have.attr("aria-checked");
|
|
503
|
+
if (UDExMode === "SingleContent") {
|
|
504
|
+
expect(UDExItem).not.to.have.attr("aria-selected");
|
|
505
|
+
} else {
|
|
506
|
+
expect(UDExItem).to.have.attr("aria-selected", "false");
|
|
507
|
+
}
|
|
508
|
+
});
|
|
509
|
+
});
|
|
510
|
+
});
|
|
511
|
+
});
|
|
512
|
+
|
|
451
513
|
describe("List component - SingleContent accessible name", () => {
|
|
452
514
|
it("uses accessible-name as aria-label when only accessible-name is set", () => {
|
|
453
515
|
cy.mount(
|
|
@@ -547,3 +609,555 @@ describe("List component - selection modes keep listbox/option role", () => {
|
|
|
547
609
|
});
|
|
548
610
|
});
|
|
549
611
|
});
|
|
612
|
+
|
|
613
|
+
describe("List - standalone grouped MultiSelect keyboard regressions", () => {
|
|
614
|
+
const UDExHeaderRow = (UDExId = "keyboard-group-1") =>
|
|
615
|
+
cy.get(`[udex-list-group-header]#${UDExId}`).shadow()
|
|
616
|
+
.find("[udex-list-item]#group-heading").shadow().find("[part=item]");
|
|
617
|
+
const UDExLeafRow = (UDExId: string) =>
|
|
618
|
+
cy.get(`[udex-list-item]#${UDExId}`).shadow().find("[part=item]");
|
|
619
|
+
|
|
620
|
+
const UDExAssertRovingTabStop = (UDExActiveId: string) => {
|
|
621
|
+
cy.get("[udex-list]#keyboard-list").find("[udex-list-group-header]").each(UDExGroup => {
|
|
622
|
+
UDExHeaderRow(UDExGroup[0].id)
|
|
623
|
+
.should("have.attr", "tabindex", UDExGroup[0].id === UDExActiveId ? "0" : "-1");
|
|
624
|
+
});
|
|
625
|
+
cy.get("[udex-list]#keyboard-list").find("[udex-list-item]").each(UDExItem => {
|
|
626
|
+
UDExLeafRow(UDExItem[0].id)
|
|
627
|
+
.should("have.attr", "tabindex", UDExItem[0].id === UDExActiveId ? "0" : "-1");
|
|
628
|
+
});
|
|
629
|
+
};
|
|
630
|
+
|
|
631
|
+
beforeEach(() => {
|
|
632
|
+
cy.mount(
|
|
633
|
+
<div id="list-keyboard-fixture">
|
|
634
|
+
<button id="before-list" type="button">Before list</button>
|
|
635
|
+
<UDExList id="keyboard-list" mode="MultiSelect" accessible-name="Grouped choices">
|
|
636
|
+
<UDExListGroupHeader id="keyboard-group-1" label="First group">
|
|
637
|
+
<UDExListItem id="keyboard-alpha" label="Alpha" />
|
|
638
|
+
<UDExListItem id="keyboard-disabled" label="Unavailable" disabled />
|
|
639
|
+
<UDExListItem id="keyboard-beta" label="Beta" />
|
|
640
|
+
</UDExListGroupHeader>
|
|
641
|
+
<UDExListGroupHeader id="keyboard-group-2" label="Second group">
|
|
642
|
+
<UDExListItem id="keyboard-gamma" label="Gamma" />
|
|
643
|
+
</UDExListGroupHeader>
|
|
644
|
+
</UDExList>
|
|
645
|
+
<button id="after-list" type="button">After list</button>
|
|
646
|
+
</div>
|
|
647
|
+
);
|
|
648
|
+
cy.get("#list-keyboard-fixture").then(UDExFixture => {
|
|
649
|
+
UDExFixture[0].addEventListener("selection-change", cy.stub().as("selectionChange"));
|
|
650
|
+
});
|
|
651
|
+
|
|
652
|
+
// Native entry is essential: focusing a row directly masks a missing tabindex.
|
|
653
|
+
UDExHeaderRow().should("exist");
|
|
654
|
+
cy.get("#before-list").realClick().should("be.focused");
|
|
655
|
+
cy.realPress("Tab");
|
|
656
|
+
UDExHeaderRow().should("be.focused");
|
|
657
|
+
});
|
|
658
|
+
|
|
659
|
+
it("Tab reaches the first actual shadow header row with option semantics", () => {
|
|
660
|
+
cy.get("[udex-list]#keyboard-list").shadow().find("#udex-list")
|
|
661
|
+
.should("have.attr", "role", "listbox");
|
|
662
|
+
UDExHeaderRow().should("have.attr", "role", "option");
|
|
663
|
+
UDExHeaderRow().should("have.attr", "aria-selected", "false");
|
|
664
|
+
UDExHeaderRow().find("[part=title]").should("have.text", "First group");
|
|
665
|
+
cy.get("@selectionChange").should("not.have.been.called");
|
|
666
|
+
});
|
|
667
|
+
|
|
668
|
+
it("ArrowDown visits enabled children, skips the disabled child and reaches the next header", () => {
|
|
669
|
+
cy.realPress("ArrowDown");
|
|
670
|
+
UDExLeafRow("keyboard-alpha").should("be.focused");
|
|
671
|
+
cy.realPress("ArrowDown");
|
|
672
|
+
UDExLeafRow("keyboard-beta").should("be.focused");
|
|
673
|
+
UDExLeafRow("keyboard-disabled").should("have.attr", "aria-disabled", "true");
|
|
674
|
+
UDExLeafRow("keyboard-disabled").should("have.attr", "tabindex", "-1");
|
|
675
|
+
cy.realPress("ArrowDown");
|
|
676
|
+
UDExHeaderRow("keyboard-group-2").should("be.focused");
|
|
677
|
+
cy.realPress("ArrowDown");
|
|
678
|
+
UDExLeafRow("keyboard-gamma").should("be.focused");
|
|
679
|
+
cy.get("@selectionChange").should("not.have.been.called");
|
|
680
|
+
});
|
|
681
|
+
|
|
682
|
+
it("ArrowUp returns through headers and enabled children while skipping the disabled child", () => {
|
|
683
|
+
cy.realPress("End");
|
|
684
|
+
UDExLeafRow("keyboard-gamma").should("be.focused");
|
|
685
|
+
cy.realPress("ArrowUp");
|
|
686
|
+
UDExHeaderRow("keyboard-group-2").should("be.focused");
|
|
687
|
+
cy.realPress("ArrowUp");
|
|
688
|
+
UDExLeafRow("keyboard-beta").should("be.focused");
|
|
689
|
+
cy.realPress("ArrowUp");
|
|
690
|
+
UDExLeafRow("keyboard-alpha").should("be.focused");
|
|
691
|
+
cy.realPress("ArrowUp");
|
|
692
|
+
UDExHeaderRow().should("be.focused");
|
|
693
|
+
cy.get("@selectionChange").should("not.have.been.called");
|
|
694
|
+
});
|
|
695
|
+
|
|
696
|
+
it("Home and End move between the first header and last enabled leaf without selecting", () => {
|
|
697
|
+
cy.realPress("End");
|
|
698
|
+
UDExLeafRow("keyboard-gamma").should("be.focused");
|
|
699
|
+
cy.realPress("Home");
|
|
700
|
+
UDExHeaderRow().should("be.focused");
|
|
701
|
+
cy.get("@selectionChange").should("not.have.been.called");
|
|
702
|
+
});
|
|
703
|
+
|
|
704
|
+
it("keeps one roving row tabindex and lets Tab leave the list", () => {
|
|
705
|
+
UDExAssertRovingTabStop("keyboard-group-1");
|
|
706
|
+
cy.realPress("End");
|
|
707
|
+
UDExLeafRow("keyboard-gamma").should("be.focused");
|
|
708
|
+
UDExAssertRovingTabStop("keyboard-gamma");
|
|
709
|
+
cy.realPress("Tab");
|
|
710
|
+
cy.get("#after-list").should("be.focused");
|
|
711
|
+
});
|
|
712
|
+
|
|
713
|
+
(["Space", "Enter"] as const).forEach(UDExKey => {
|
|
714
|
+
it(`${UDExKey} toggles the focused leaf and bubbles its selection-change event`, () => {
|
|
715
|
+
cy.realPress("ArrowDown");
|
|
716
|
+
UDExLeafRow("keyboard-alpha").should("be.focused");
|
|
717
|
+
cy.realPress(UDExKey);
|
|
718
|
+
UDExLeafRow("keyboard-alpha").should("have.attr", "aria-selected", "true");
|
|
719
|
+
UDExLeafRow("keyboard-beta").should("have.attr", "aria-selected", "false");
|
|
720
|
+
UDExLeafRow("keyboard-disabled").should("have.attr", "aria-selected", "false");
|
|
721
|
+
cy.get("@selectionChange").should("have.been.calledOnce")
|
|
722
|
+
.its("firstCall.args.0.detail.item.id").should("equal", "keyboard-alpha");
|
|
723
|
+
|
|
724
|
+
cy.realPress(UDExKey);
|
|
725
|
+
UDExLeafRow("keyboard-alpha").should("have.attr", "aria-selected", "false");
|
|
726
|
+
UDExLeafRow("keyboard-alpha").should("be.focused");
|
|
727
|
+
cy.get("@selectionChange").should("have.been.calledTwice")
|
|
728
|
+
.its("lastCall.args.0.detail.item.id").should("equal", "keyboard-alpha");
|
|
729
|
+
});
|
|
730
|
+
|
|
731
|
+
it(`${UDExKey} toggles only the focused group's enabled children`, () => {
|
|
732
|
+
cy.realPress(UDExKey);
|
|
733
|
+
UDExLeafRow("keyboard-alpha").should("have.attr", "aria-selected", "true");
|
|
734
|
+
UDExLeafRow("keyboard-beta").should("have.attr", "aria-selected", "true");
|
|
735
|
+
UDExLeafRow("keyboard-disabled").should("have.attr", "aria-selected", "false");
|
|
736
|
+
UDExLeafRow("keyboard-gamma").should("have.attr", "aria-selected", "false");
|
|
737
|
+
UDExHeaderRow().should("be.focused");
|
|
738
|
+
|
|
739
|
+
cy.realPress(UDExKey);
|
|
740
|
+
UDExLeafRow("keyboard-alpha").should("have.attr", "aria-selected", "false");
|
|
741
|
+
UDExLeafRow("keyboard-beta").should("have.attr", "aria-selected", "false");
|
|
742
|
+
UDExHeaderRow().should("be.focused");
|
|
743
|
+
});
|
|
744
|
+
});
|
|
745
|
+
});
|
|
746
|
+
|
|
747
|
+
describe("List - SingleSelect collapsible header names and ownership", () => {
|
|
748
|
+
const UDExHeaderRow = (UDExIndex = 0) =>
|
|
749
|
+
cy.get("[udex-list-group-header]").eq(UDExIndex).shadow()
|
|
750
|
+
.find(".udex-list__group-content.udex-list__group-header-wrap");
|
|
751
|
+
|
|
752
|
+
[false, true].forEach(UDExRichLabel => {
|
|
753
|
+
["", "Digital Builders"].forEach(UDExSupportingText => {
|
|
754
|
+
it(`names a ${UDExRichLabel ? "rich" : "plain"} heading ${UDExSupportingText ? "with" : "without"} supporting text, excluding leaves and the chevron`, () => {
|
|
755
|
+
cy.mount(
|
|
756
|
+
<UDExList mode="SingleSelect">
|
|
757
|
+
<UDExListGroupHeader
|
|
758
|
+
collapsible
|
|
759
|
+
label={UDExRichLabel ? "<b>Programming</b> Languages" : "Programming Languages"}
|
|
760
|
+
supporting-text={UDExSupportingText}>
|
|
761
|
+
<UDExListItem label="JavaScript" />
|
|
762
|
+
<UDExListItem label="Python" />
|
|
763
|
+
</UDExListGroupHeader>
|
|
764
|
+
</UDExList>
|
|
765
|
+
);
|
|
766
|
+
|
|
767
|
+
UDExHeaderRow().find("#group-heading").should("have.text", "Programming Languages");
|
|
768
|
+
if (UDExRichLabel) {
|
|
769
|
+
UDExHeaderRow().find("#group-heading b").should("have.text", "Programming");
|
|
770
|
+
}
|
|
771
|
+
if (UDExSupportingText) {
|
|
772
|
+
UDExHeaderRow().find("#group-supporting-text").should("have.text", UDExSupportingText);
|
|
773
|
+
} else {
|
|
774
|
+
UDExHeaderRow().find("#group-supporting-text").should("not.exist");
|
|
775
|
+
}
|
|
776
|
+
UDExHeaderRow().find("[udex-button].udex-list__group-heading__icon").should("exist");
|
|
777
|
+
UDExHeaderRow().should(UDExHeader => {
|
|
778
|
+
expect(UDExHeader).to.have.attr("role", "treeitem");
|
|
779
|
+
expect(UDExHeader).to.have.attr("tabindex", "0");
|
|
780
|
+
expect(UDExHeader).to.have.attr("aria-labelledby", UDExSupportingText
|
|
781
|
+
? "group-heading group-supporting-text" : "group-heading");
|
|
782
|
+
// Resolve the name sources in this shadow root, not via document-global IDs.
|
|
783
|
+
const UDExShadow = UDExHeader[0].getRootNode() as ShadowRoot;
|
|
784
|
+
const UDExName = UDExHeader.attr("aria-labelledby")!.split(" ").map(UDExId => {
|
|
785
|
+
const UDExLabels = UDExShadow.querySelectorAll(`[id="${UDExId}"]`);
|
|
786
|
+
expect(UDExLabels).to.have.length(1);
|
|
787
|
+
expect(UDExHeader[0].contains(UDExLabels[0])).to.equal(true);
|
|
788
|
+
return UDExLabels[0].textContent!.trim();
|
|
789
|
+
}).join(" ");
|
|
790
|
+
expect(UDExName).to.equal(
|
|
791
|
+
["Programming Languages", UDExSupportingText].filter(Boolean).join(" ")
|
|
792
|
+
);
|
|
793
|
+
expect(UDExHeader[0].querySelector(".udex-list__header")!.textContent!
|
|
794
|
+
.replace(/\s+/g, " ").trim()).to.equal(UDExName);
|
|
795
|
+
expect(UDExHeader[0].querySelector("slot, [role=group], [udex-list-item]")).to.equal(null);
|
|
796
|
+
expect(UDExHeader.text()).not.to.contain("JavaScript");
|
|
797
|
+
expect(UDExHeader.text()).not.to.contain("Python");
|
|
798
|
+
});
|
|
799
|
+
});
|
|
800
|
+
});
|
|
801
|
+
});
|
|
802
|
+
|
|
803
|
+
it("keeps unique same-shadow ownership and leaf focus through keyboard collapse and expansion", () => {
|
|
804
|
+
cy.mount(
|
|
805
|
+
<div>
|
|
806
|
+
<button id="before-owned-list" type="button">Before list</button>
|
|
807
|
+
<UDExList mode="SingleSelect" accessible-name="Grouped choices">
|
|
808
|
+
<UDExListGroupHeader collapsible label="First group">
|
|
809
|
+
<UDExListItem id="owned-leaf" label="Alpha" />
|
|
810
|
+
</UDExListGroupHeader>
|
|
811
|
+
<UDExListGroupHeader collapsible collapsed label="Second group">
|
|
812
|
+
<UDExListItem label="Beta" />
|
|
813
|
+
</UDExListGroupHeader>
|
|
814
|
+
</UDExList>
|
|
815
|
+
</div>
|
|
816
|
+
);
|
|
817
|
+
|
|
818
|
+
UDExHeaderRow().should("have.attr", "aria-expanded", "true");
|
|
819
|
+
UDExHeaderRow(1).should("have.attr", "aria-expanded", "false");
|
|
820
|
+
UDExHeaderRow().invoke("attr", "aria-owns").then(UDExFirstId => {
|
|
821
|
+
UDExHeaderRow(1).invoke("attr", "aria-owns").then(UDExSecondId => {
|
|
822
|
+
expect(UDExSecondId).to.match(/^\S+$/).and.not.equal(UDExFirstId);
|
|
823
|
+
const UDExAssertOwnership = () => {
|
|
824
|
+
cy.get("[udex-list-group-header]").each((UDExHost, UDExIndex) => {
|
|
825
|
+
cy.wrap(UDExHost).shadow().find(".udex-list__group-content").should(UDExHeader => {
|
|
826
|
+
const UDExRow = UDExHeader[0];
|
|
827
|
+
const UDExOwnedId = UDExRow.getAttribute("aria-owns");
|
|
828
|
+
expect(UDExOwnedId).to.equal([UDExFirstId, UDExSecondId][UDExIndex]);
|
|
829
|
+
expect(UDExRow.getAttribute("aria-controls")).to.equal(UDExOwnedId);
|
|
830
|
+
const UDExShadow = UDExHost[0].shadowRoot!;
|
|
831
|
+
expect(UDExRow.getRootNode()).to.equal(UDExShadow);
|
|
832
|
+
const UDExGroups = UDExShadow.querySelectorAll(`[id="${UDExOwnedId}"]`);
|
|
833
|
+
expect(UDExGroups).to.have.length(1);
|
|
834
|
+
expect(UDExGroups[0].matches(".udex-list__group-list__content[role=group]")).to.equal(true);
|
|
835
|
+
expect(UDExRow.nextElementSibling).to.equal(UDExGroups[0]);
|
|
836
|
+
expect(UDExRow.querySelector("slot, [role=group]")).to.equal(null);
|
|
837
|
+
expect(UDExGroups[0].querySelector("slot")!.assignedElements()).to.have.length(1);
|
|
838
|
+
});
|
|
839
|
+
});
|
|
840
|
+
};
|
|
841
|
+
|
|
842
|
+
UDExAssertOwnership();
|
|
843
|
+
cy.get("#before-owned-list").realClick().should("be.focused");
|
|
844
|
+
cy.realPress("Tab");
|
|
845
|
+
UDExHeaderRow().should("be.focused");
|
|
846
|
+
cy.realPress("ArrowLeft");
|
|
847
|
+
UDExHeaderRow().should("have.attr", "aria-expanded", "false");
|
|
848
|
+
UDExHeaderRow().should("be.focused");
|
|
849
|
+
cy.get("[udex-list-item]#owned-leaf").should("have.attr", "collapsed");
|
|
850
|
+
UDExAssertOwnership();
|
|
851
|
+
|
|
852
|
+
cy.realPress("ArrowDown");
|
|
853
|
+
UDExHeaderRow(1).should("be.focused");
|
|
854
|
+
cy.realPress("ArrowUp");
|
|
855
|
+
UDExHeaderRow().should("be.focused");
|
|
856
|
+
cy.realPress("ArrowRight");
|
|
857
|
+
UDExHeaderRow().should("have.attr", "aria-expanded", "true");
|
|
858
|
+
UDExHeaderRow().should("be.focused");
|
|
859
|
+
cy.get("[udex-list-item]#owned-leaf").should("not.have.attr", "collapsed");
|
|
860
|
+
UDExAssertOwnership();
|
|
861
|
+
cy.realPress("ArrowDown");
|
|
862
|
+
cy.get("[udex-list-item]#owned-leaf").shadow().find("[part=item]").should("be.focused");
|
|
863
|
+
cy.realPress("ArrowUp");
|
|
864
|
+
UDExHeaderRow().should("be.focused");
|
|
865
|
+
});
|
|
866
|
+
});
|
|
867
|
+
});
|
|
868
|
+
|
|
869
|
+
it("omits inner ownership and duplicate semantics in SelectBox-style host-ARIA mode", () => {
|
|
870
|
+
cy.mount(
|
|
871
|
+
<UDExList mode="SingleSelect">
|
|
872
|
+
<UDExListGroupHeader collapsible label="Host-owned group" _useHostAriaRole={true}>
|
|
873
|
+
<UDExListItem label="Alpha" />
|
|
874
|
+
</UDExListGroupHeader>
|
|
875
|
+
</UDExList>
|
|
876
|
+
);
|
|
877
|
+
|
|
878
|
+
UDExHeaderRow().should(UDExHeader => {
|
|
879
|
+
expect(UDExHeader).to.have.attr("role", "presentation");
|
|
880
|
+
["aria-owns", "aria-controls", "aria-expanded", "aria-labelledby", "tabindex"].forEach(UDExAttribute => {
|
|
881
|
+
expect(UDExHeader).not.to.have.attr(UDExAttribute);
|
|
882
|
+
});
|
|
883
|
+
});
|
|
884
|
+
cy.get("[udex-list-group-header]").shadow()
|
|
885
|
+
.find(".udex-list__group-list > .udex-list__group-list__content[role=group] slot").should("exist");
|
|
886
|
+
});
|
|
887
|
+
});
|
|
888
|
+
|
|
889
|
+
describe("List - MultiSelect header host-ARIA Boolean binding regressions", () => {
|
|
890
|
+
const UDExLayouts = [
|
|
891
|
+
{ name: "noncollapsible", collapsible: false, collapsed: false },
|
|
892
|
+
{ name: "collapsible expanded", collapsible: true, collapsed: false },
|
|
893
|
+
{ name: "collapsible collapsed", collapsible: true, collapsed: true },
|
|
894
|
+
];
|
|
895
|
+
|
|
896
|
+
UDExLayouts.forEach(UDExLayout => {
|
|
897
|
+
[false, true].forEach(UDExHostAriaMode => {
|
|
898
|
+
// Updated: the nested host must remain transparent; only its actual row exposes tree state.
|
|
899
|
+
it(`${UDExLayout.name}: propagates _useHostAriaRole=${UDExHostAriaMode} to the nested header row`, () => {
|
|
900
|
+
cy.mount(
|
|
901
|
+
<div>
|
|
902
|
+
<button id="before-binding-list" type="button">Before list</button>
|
|
903
|
+
<UDExList mode="MultiSelect">
|
|
904
|
+
<UDExListGroupHeader
|
|
905
|
+
label="Binding group"
|
|
906
|
+
collapsible={UDExLayout.collapsible}
|
|
907
|
+
collapsed={UDExLayout.collapsed}
|
|
908
|
+
_useHostAriaRole={UDExHostAriaMode}>
|
|
909
|
+
<UDExListItem label="Child" />
|
|
910
|
+
</UDExListGroupHeader>
|
|
911
|
+
</UDExList>
|
|
912
|
+
</div>
|
|
913
|
+
);
|
|
914
|
+
|
|
915
|
+
cy.get("[udex-list-group-header]").should("have.prop", "_useHostAriaRole", UDExHostAriaMode);
|
|
916
|
+
const UDExNestedHeader = () => cy.get("[udex-list-group-header]").shadow().find("[udex-list-item]#group-heading");
|
|
917
|
+
UDExNestedHeader().should(UDExHeader => {
|
|
918
|
+
expect(UDExHeader).to.have.prop("_useHostAriaRole", UDExHostAriaMode);
|
|
919
|
+
expect(UDExHeader).to.have.attr("role", "none");
|
|
920
|
+
expect(UDExHeader[0].getAttributeNames().filter(UDExAttribute => UDExAttribute.startsWith("aria-")),
|
|
921
|
+
"presentational host omits all ARIA naming/state attributes").to.deep.equal([]);
|
|
922
|
+
// A false Boolean must be absent, never the present attribute string "false".
|
|
923
|
+
if (UDExHostAriaMode) {
|
|
924
|
+
expect(UDExHeader).to.have.attr("_use-host-aria-role");
|
|
925
|
+
} else {
|
|
926
|
+
expect(UDExHeader).not.to.have.attr("_use-host-aria-role");
|
|
927
|
+
}
|
|
928
|
+
});
|
|
929
|
+
UDExNestedHeader().shadow().find("[part=item]").should(UDExRow => {
|
|
930
|
+
if (UDExHostAriaMode) {
|
|
931
|
+
expect(UDExRow).to.have.attr("role", "presentation");
|
|
932
|
+
["tabindex", "aria-selected", "aria-checked", "aria-expanded"].forEach(UDExAttribute => {
|
|
933
|
+
expect(UDExRow).not.to.have.attr(UDExAttribute);
|
|
934
|
+
});
|
|
935
|
+
} else {
|
|
936
|
+
expect(UDExRow).to.have.attr("role", UDExLayout.collapsible ? "treeitem" : "option");
|
|
937
|
+
expect(UDExRow).to.have.attr("tabindex", "0");
|
|
938
|
+
if (UDExLayout.collapsible) {
|
|
939
|
+
expect(UDExRow).to.have.attr("aria-expanded", String(!UDExLayout.collapsed));
|
|
940
|
+
expect(UDExRow).to.have.attr("aria-checked", "false");
|
|
941
|
+
expect(UDExRow).not.to.have.attr("aria-selected");
|
|
942
|
+
} else {
|
|
943
|
+
expect(UDExRow).not.to.have.attr("aria-expanded");
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
});
|
|
947
|
+
|
|
948
|
+
if (!UDExHostAriaMode) {
|
|
949
|
+
cy.get("#before-binding-list").realClick().should("be.focused");
|
|
950
|
+
cy.realPress("Tab");
|
|
951
|
+
UDExNestedHeader().shadow().find("[part=item]").should("be.focused");
|
|
952
|
+
}
|
|
953
|
+
});
|
|
954
|
+
});
|
|
955
|
+
});
|
|
956
|
+
});
|
|
957
|
+
|
|
958
|
+
describe("List - grouped SingleContent regressions", () => {
|
|
959
|
+
const UDExGroupedList = (
|
|
960
|
+
UDExMode: "SingleContent" | "SingleSelect" | "MultiSelect" = "SingleContent",
|
|
961
|
+
UDExMixed = false
|
|
962
|
+
) => (
|
|
963
|
+
<div id="sc-grouped-fixture">
|
|
964
|
+
<button id="before-sc-grouped" type="button">Before list</button>
|
|
965
|
+
<UDExList id="sc-grouped-list" mode={UDExMode} accessible-name="Grouped information">
|
|
966
|
+
<UDExListGroupHeader id="sc-grouped-expanded" collapsible label="First group" supportingText="Details">
|
|
967
|
+
<UDExListItem id="sc-grouped-alpha" label="Alpha" />
|
|
968
|
+
</UDExListGroupHeader>
|
|
969
|
+
<UDExListGroupHeader id="sc-grouped-second" collapsible={!UDExMixed} collapsed={!UDExMixed} label="Second group">
|
|
970
|
+
<UDExListItem label="Beta" />
|
|
971
|
+
</UDExListGroupHeader>
|
|
972
|
+
{UDExMixed && <UDExListItem label="Ungrouped item" />}
|
|
973
|
+
</UDExList>
|
|
974
|
+
<button id="after-sc-grouped" type="button">After list</button>
|
|
975
|
+
</div>
|
|
976
|
+
);
|
|
977
|
+
const UDExInnerList = () => cy.get("#sc-grouped-list").shadow().find("#udex-list");
|
|
978
|
+
const UDExHeaderRow = () => cy.get("#sc-grouped-expanded").shadow().find(".udex-list__group-content");
|
|
979
|
+
const UDExSelectionSemantics = [
|
|
980
|
+
'[role="tree"]', '[role="treeitem"]', '[role="listbox"]', '[role="option"]',
|
|
981
|
+
"[aria-selected]", "[aria-checked]", "[aria-multiselectable]", "[aria-owns]",
|
|
982
|
+
].join(", ");
|
|
983
|
+
|
|
984
|
+
const UDExAssertContentList = () => {
|
|
985
|
+
UDExInnerList().should("have.attr", "role", "list");
|
|
986
|
+
cy.get("#sc-grouped-list").shadow().find(UDExSelectionSemantics).should("not.exist");
|
|
987
|
+
cy.get("#sc-grouped-list").find("[udex-list-group-header]").each(UDExGroup => {
|
|
988
|
+
cy.wrap(UDExGroup).should(UDExHost => {
|
|
989
|
+
expect(UDExHost).to.have.prop("_treeMode", false);
|
|
990
|
+
expect(UDExHost).not.to.have.attr("_list-managed-tree-mode");
|
|
991
|
+
});
|
|
992
|
+
cy.wrap(UDExGroup).shadow().find(".udex-list__group").should("have.attr", "role", "listitem");
|
|
993
|
+
cy.wrap(UDExGroup).shadow().find('.udex-list__group[role="listitem"] [role="list"] > slot')
|
|
994
|
+
.should("have.length", 1).should(UDExSlot => {
|
|
995
|
+
expect((UDExSlot[0] as HTMLSlotElement).assignedElements())
|
|
996
|
+
.to.deep.equal(Array.from(UDExGroup[0].children));
|
|
997
|
+
});
|
|
998
|
+
cy.wrap(UDExGroup).shadow().find(UDExSelectionSemantics).should("not.exist");
|
|
999
|
+
});
|
|
1000
|
+
cy.get("#sc-grouped-list").find("[udex-list-item]").each(UDExItem => {
|
|
1001
|
+
cy.wrap(UDExItem).should(UDExHost => {
|
|
1002
|
+
expect(UDExHost).to.have.prop("mode", "SingleContent");
|
|
1003
|
+
expect(UDExHost).to.have.prop("accessibleRole", "");
|
|
1004
|
+
expect(UDExHost).not.to.have.attr("_list-managed-role");
|
|
1005
|
+
});
|
|
1006
|
+
cy.wrap(UDExItem).shadow().find("[part=item]").should("have.attr", "role", "listitem");
|
|
1007
|
+
cy.wrap(UDExItem).shadow().find(UDExSelectionSemantics).should("not.exist");
|
|
1008
|
+
});
|
|
1009
|
+
};
|
|
1010
|
+
|
|
1011
|
+
[false, true].forEach(UDExMixed => {
|
|
1012
|
+
it(`${UDExMixed ? "mixed groups and an ungrouped leaf" : "expanded and collapsed groups"} use nested lists without selection semantics`, () => {
|
|
1013
|
+
cy.mount(UDExGroupedList("SingleContent", UDExMixed));
|
|
1014
|
+
UDExAssertContentList();
|
|
1015
|
+
UDExInnerList().should("have.attr", "aria-label", "Grouped information");
|
|
1016
|
+
UDExHeaderRow().should(UDExHeader => {
|
|
1017
|
+
const UDExRow = UDExHeader[0];
|
|
1018
|
+
const UDExShadow = UDExRow.getRootNode() as ShadowRoot;
|
|
1019
|
+
expect(UDExHeader).to.have.attr("role", "button");
|
|
1020
|
+
expect(UDExHeader).to.have.attr("aria-expanded", "true");
|
|
1021
|
+
expect(UDExHeader).to.have.attr("aria-labelledby", "group-heading group-supporting-text");
|
|
1022
|
+
const UDExLabels = UDExRow.getAttribute("aria-labelledby")!.split(" ")
|
|
1023
|
+
.map(UDExId => UDExShadow.getElementById(UDExId)!.textContent!.trim());
|
|
1024
|
+
expect(UDExLabels.join(" ")).to.equal("First group Details");
|
|
1025
|
+
const UDExContent = UDExShadow.getElementById(UDExRow.getAttribute("aria-controls")!);
|
|
1026
|
+
expect(UDExContent).to.equal(UDExRow.nextElementSibling);
|
|
1027
|
+
expect(UDExContent!.getAttribute("role")).to.equal("list");
|
|
1028
|
+
expect(UDExRow.querySelector("slot, [role=list]")).to.equal(null);
|
|
1029
|
+
});
|
|
1030
|
+
if (!UDExMixed) {
|
|
1031
|
+
cy.get("#sc-grouped-second").shadow().find(".udex-list__group-content")
|
|
1032
|
+
.should("have.attr", "role", "button").and("have.attr", "aria-expanded", "false");
|
|
1033
|
+
}
|
|
1034
|
+
});
|
|
1035
|
+
});
|
|
1036
|
+
|
|
1037
|
+
it("Tab enters the collapse button; Enter, Space and Left/Right toggle it without selecting or losing focus", () => {
|
|
1038
|
+
cy.mount(UDExGroupedList());
|
|
1039
|
+
cy.get("#sc-grouped-fixture").then(UDExFixture => {
|
|
1040
|
+
UDExFixture[0].addEventListener("selection-change", cy.stub().as("scSelectionChange"));
|
|
1041
|
+
});
|
|
1042
|
+
UDExHeaderRow().should("have.attr", "role", "button");
|
|
1043
|
+
cy.get("#before-sc-grouped").realClick().should("be.focused");
|
|
1044
|
+
cy.realPress("Tab");
|
|
1045
|
+
UDExHeaderRow().should("be.focused");
|
|
1046
|
+
|
|
1047
|
+
(["Enter", "Space", "ArrowLeft"] as const).forEach(UDExKey => {
|
|
1048
|
+
cy.realPress(UDExKey);
|
|
1049
|
+
UDExHeaderRow().should("have.attr", "aria-expanded", "false");
|
|
1050
|
+
UDExHeaderRow().should("be.focused");
|
|
1051
|
+
cy.get("#sc-grouped-expanded").should("have.attr", "collapsed");
|
|
1052
|
+
cy.get("#sc-grouped-alpha").should("have.attr", "collapsed");
|
|
1053
|
+
cy.realPress(UDExKey === "ArrowLeft" ? "ArrowRight" : UDExKey);
|
|
1054
|
+
UDExHeaderRow().should("have.attr", "aria-expanded", "true");
|
|
1055
|
+
UDExHeaderRow().should("be.focused");
|
|
1056
|
+
cy.get("#sc-grouped-expanded").should("not.have.attr", "collapsed");
|
|
1057
|
+
cy.get("#sc-grouped-alpha").should("not.have.attr", "collapsed");
|
|
1058
|
+
});
|
|
1059
|
+
cy.realPress("Tab");
|
|
1060
|
+
cy.get("#sc-grouped-second").shadow().find(".udex-list__group-content").should("be.focused");
|
|
1061
|
+
cy.realPress("Tab");
|
|
1062
|
+
cy.get("#after-sc-grouped").should("be.focused");
|
|
1063
|
+
cy.get("@scSelectionChange").should("not.have.been.called");
|
|
1064
|
+
UDExAssertContentList();
|
|
1065
|
+
});
|
|
1066
|
+
|
|
1067
|
+
(["SingleSelect", "MultiSelect"] as const).forEach(UDExMode => {
|
|
1068
|
+
it(`${UDExMode} -> SingleContent -> ${UDExMode} clears List-owned tree semantics and rebuilds them`, () => {
|
|
1069
|
+
cy.mount(UDExGroupedList(UDExMode));
|
|
1070
|
+
const UDExAssertSelectionTree = () => {
|
|
1071
|
+
UDExInnerList().should("have.attr", "role", "tree");
|
|
1072
|
+
cy.get("#sc-grouped-list").find("[udex-list-group-header]").each(UDExGroup => {
|
|
1073
|
+
cy.wrap(UDExGroup).should(UDExHost => {
|
|
1074
|
+
expect(UDExHost).to.have.prop("_treeMode", true);
|
|
1075
|
+
expect(UDExHost).to.have.attr("_list-managed-tree-mode");
|
|
1076
|
+
});
|
|
1077
|
+
cy.wrap(UDExGroup).shadow().find(".udex-list__group").should("have.attr", "role", "none");
|
|
1078
|
+
cy.wrap(UDExGroup).shadow().find('[role="group"] > slot').should("have.length", 1);
|
|
1079
|
+
if (UDExMode === "MultiSelect") {
|
|
1080
|
+
cy.wrap(UDExGroup).shadow().find("[udex-list-item]#group-heading").shadow()
|
|
1081
|
+
.find("[part=item]").should("have.attr", "role", "treeitem");
|
|
1082
|
+
} else {
|
|
1083
|
+
cy.wrap(UDExGroup).shadow().find(".udex-list__group-content").should("have.attr", "role", "treeitem");
|
|
1084
|
+
}
|
|
1085
|
+
});
|
|
1086
|
+
cy.get("#sc-grouped-list").find("[udex-list-item]").each(UDExItem => {
|
|
1087
|
+
cy.wrap(UDExItem).should(UDExHost => {
|
|
1088
|
+
expect(UDExHost).to.have.prop("accessibleRole", "treeitem");
|
|
1089
|
+
expect(UDExHost).to.have.attr("_list-managed-role");
|
|
1090
|
+
});
|
|
1091
|
+
cy.wrap(UDExItem).shadow().find("[part=item]").should("have.attr", "role", "treeitem")
|
|
1092
|
+
.and("have.attr", UDExMode === "MultiSelect" ? "aria-checked" : "aria-selected", "false");
|
|
1093
|
+
});
|
|
1094
|
+
};
|
|
1095
|
+
UDExAssertSelectionTree();
|
|
1096
|
+
cy.get("#sc-grouped-list").invoke("prop", "mode", "SingleContent");
|
|
1097
|
+
UDExAssertContentList();
|
|
1098
|
+
UDExHeaderRow().should("have.attr", "role", "button");
|
|
1099
|
+
cy.get("#sc-grouped-list").invoke("prop", "mode", UDExMode);
|
|
1100
|
+
UDExAssertSelectionTree();
|
|
1101
|
+
});
|
|
1102
|
+
});
|
|
1103
|
+
|
|
1104
|
+
it("clears List-owned tree modes when the last collapsible group is removed", () => {
|
|
1105
|
+
cy.mount(UDExGroupedList("MultiSelect", true));
|
|
1106
|
+
UDExInnerList().should("have.attr", "role", "tree");
|
|
1107
|
+
cy.get("#sc-grouped-list").find("[udex-list-group-header]")
|
|
1108
|
+
.should("have.length", 2).each(UDExGroup => {
|
|
1109
|
+
cy.wrap(UDExGroup).should("have.attr", "_list-managed-tree-mode");
|
|
1110
|
+
});
|
|
1111
|
+
cy.get("#sc-grouped-expanded").invoke("prop", "collapsible", false).should("not.have.attr", "collapsible");
|
|
1112
|
+
// Re-render the List after changing its slotted group's configuration.
|
|
1113
|
+
cy.get("#sc-grouped-list").invoke("prop", "accessibleName", "Updated grouped information");
|
|
1114
|
+
UDExInnerList().should("have.attr", "role", "listbox");
|
|
1115
|
+
UDExInnerList().should("have.attr", "aria-multiselectable", "true");
|
|
1116
|
+
cy.get("#sc-grouped-list").find("[udex-list-group-header]").each(UDExGroup => {
|
|
1117
|
+
cy.wrap(UDExGroup).should(UDExHost => {
|
|
1118
|
+
expect(UDExHost).to.have.prop("_treeMode", false);
|
|
1119
|
+
expect(UDExHost).not.to.have.attr("_list-managed-tree-mode");
|
|
1120
|
+
});
|
|
1121
|
+
cy.wrap(UDExGroup).shadow().find("[udex-list-item]#group-heading").shadow()
|
|
1122
|
+
.find("[part=item]").should("have.attr", "role", "option");
|
|
1123
|
+
});
|
|
1124
|
+
cy.get("#sc-grouped-list").find("[udex-list-item]").each(UDExItem => {
|
|
1125
|
+
cy.wrap(UDExItem).should("not.have.attr", "_list-managed-role");
|
|
1126
|
+
cy.wrap(UDExItem).should("have.prop", "accessibleRole", "");
|
|
1127
|
+
cy.wrap(UDExItem).shadow().find("[part=item]").should("have.attr", "role", "option");
|
|
1128
|
+
});
|
|
1129
|
+
});
|
|
1130
|
+
|
|
1131
|
+
it("preserves externally supplied tree mode and host-owned roles in SingleContent", () => {
|
|
1132
|
+
cy.mount(
|
|
1133
|
+
<UDExList id="sc-host-tree" mode="SingleContent" role="tree" accessibleRole="">
|
|
1134
|
+
<UDExListGroupHeader collapsible label="Host-owned group" role="treeitem" aria-label="Host-owned group"
|
|
1135
|
+
aria-expanded="true" _treeMode={true} _useHostAriaRole={true}>
|
|
1136
|
+
<UDExListItem label="Alpha" role="treeitem" accessibleRole="treeitem" _useHostAriaRole={true} />
|
|
1137
|
+
</UDExListGroupHeader>
|
|
1138
|
+
</UDExList>
|
|
1139
|
+
);
|
|
1140
|
+
cy.get("#sc-host-tree").should("have.attr", "role", "tree");
|
|
1141
|
+
cy.get("#sc-host-tree").shadow().find("#udex-list").should("have.attr", "role", "none");
|
|
1142
|
+
cy.get("#sc-host-tree").find("[udex-list-group-header]").should(UDExGroup => {
|
|
1143
|
+
expect(UDExGroup).to.have.prop("_treeMode", true);
|
|
1144
|
+
expect(UDExGroup).to.have.prop("_useHostAriaRole", true);
|
|
1145
|
+
expect(UDExGroup).to.have.attr("role", "treeitem");
|
|
1146
|
+
expect(UDExGroup).to.have.attr("aria-expanded", "true");
|
|
1147
|
+
expect(UDExGroup).not.to.have.attr("_list-managed-tree-mode");
|
|
1148
|
+
}).shadow().find(".udex-list__group-content").should(UDExHeader => {
|
|
1149
|
+
expect(UDExHeader).to.have.attr("role", "presentation");
|
|
1150
|
+
["tabindex", "aria-expanded", "aria-controls", "aria-labelledby", "aria-owns"].forEach(UDExAttribute => {
|
|
1151
|
+
expect(UDExHeader).not.to.have.attr(UDExAttribute);
|
|
1152
|
+
});
|
|
1153
|
+
});
|
|
1154
|
+
cy.get("#sc-host-tree").find("[udex-list-group-header]").shadow()
|
|
1155
|
+
.find('.udex-list__group[role="presentation"] [role="group"] > slot').should("have.length", 1);
|
|
1156
|
+
cy.get("#sc-host-tree").find("[udex-list-item]").should(UDExItem => {
|
|
1157
|
+
expect(UDExItem).to.have.attr("role", "treeitem");
|
|
1158
|
+
expect(UDExItem).to.have.prop("accessibleRole", "treeitem");
|
|
1159
|
+
expect(UDExItem).to.have.prop("_useHostAriaRole", true);
|
|
1160
|
+
expect(UDExItem).not.to.have.attr("_list-managed-role");
|
|
1161
|
+
}).shadow().find("[part=item]").should("have.attr", "role", "presentation");
|
|
1162
|
+
});
|
|
1163
|
+
});
|