Skip to content

Commit 349b591

Browse files
committed
test(ui5-li): add edge case tests for edit mode keyboard navigation
10 new tests covering: - F2/F7 no-op on items with no focusable elements - F2 no-op in Single/Multiple selection modes (radio/checkbox tabindex=-1) - Tab exit to growing button in edit mode - Shift+Tab from first item's first inner element exits list - F7 position memory clamping across items with different element counts - Edit mode cleared after focusout, navigation mode restored on re-entry - Arrow Down from item level uses standard navigation (not edit transfer) - Tab chain across items clears source item's edit mode via focusout
1 parent 3d583ff commit 349b591

1 file changed

Lines changed: 243 additions & 0 deletions

File tree

packages/main/cypress/specs/List.cy.tsx

Lines changed: 243 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3271,3 +3271,246 @@ describe("Edit mode (F2) with Delete selection mode", () => {
32713271
cy.get("#before").should("be.focused");
32723272
});
32733273
});
3274+
3275+
describe("Edit mode edge cases", () => {
3276+
it("F2 is a no-op on items with no focusable elements", () => {
3277+
cy.mount(
3278+
<div>
3279+
<List>
3280+
<ListItemStandard>Item 1</ListItemStandard>
3281+
</List>
3282+
<button id="after">After</button>
3283+
</div>
3284+
);
3285+
3286+
cy.get("[ui5-li]").first().realClick();
3287+
cy.get("[ui5-li]").first().should("be.focused");
3288+
3289+
cy.realPress("F2");
3290+
cy.get("[ui5-li]").first().should("be.focused");
3291+
3292+
cy.realPress("Tab");
3293+
cy.get("#after").should("be.focused");
3294+
});
3295+
3296+
it("F7 is a no-op on items with no focusable elements", () => {
3297+
cy.mount(
3298+
<List>
3299+
<ListItemStandard>Item 1</ListItemStandard>
3300+
<ListItemStandard>Item 2</ListItemStandard>
3301+
</List>
3302+
);
3303+
3304+
cy.get("[ui5-li]").first().realClick();
3305+
cy.get("[ui5-li]").first().should("be.focused");
3306+
3307+
cy.realPress("F7");
3308+
cy.get("[ui5-li]").first().should("be.focused");
3309+
});
3310+
3311+
it("Tab from last item in edit mode focuses growing button when growing=Button", () => {
3312+
cy.mount(
3313+
<div>
3314+
<List selectionMode="Delete" growing="Button">
3315+
<ListItemStandard>Item 1</ListItemStandard>
3316+
<ListItemStandard>Item 2</ListItemStandard>
3317+
</List>
3318+
<button id="after">After</button>
3319+
</div>
3320+
);
3321+
3322+
cy.get("[ui5-li]").last().realClick();
3323+
cy.realPress("F2");
3324+
3325+
cy.get("[ui5-li]").last()
3326+
.shadow()
3327+
.find("[ui5-button]")
3328+
.should("be.focused");
3329+
3330+
cy.realPress("Tab");
3331+
cy.get("[ui5-list]")
3332+
.shadow()
3333+
.find("[id$='growing-btn']")
3334+
.should("be.focused");
3335+
});
3336+
3337+
it("Shift+Tab from first item's first inner element exits the list", () => {
3338+
cy.mount(
3339+
<div>
3340+
<button id="before">Before</button>
3341+
<List selectionMode="Delete">
3342+
<ListItemStandard>Item 1</ListItemStandard>
3343+
<ListItemStandard>Item 2</ListItemStandard>
3344+
</List>
3345+
</div>
3346+
);
3347+
3348+
cy.get("[ui5-li]").first().realClick();
3349+
cy.realPress("F2");
3350+
3351+
cy.get("[ui5-li]").first()
3352+
.shadow()
3353+
.find("[ui5-button]")
3354+
.should("be.focused");
3355+
3356+
cy.realPress(["Shift", "Tab"]);
3357+
cy.get("#before").should("be.focused");
3358+
});
3359+
3360+
it("F2 is a no-op in Single selection mode (radio button has tabindex=-1)", () => {
3361+
cy.mount(
3362+
<div>
3363+
<List selectionMode="Single">
3364+
<ListItemStandard>Item 1</ListItemStandard>
3365+
<ListItemStandard>Item 2</ListItemStandard>
3366+
</List>
3367+
<button id="after">After</button>
3368+
</div>
3369+
);
3370+
3371+
cy.get("[ui5-li]").first().realClick();
3372+
cy.get("[ui5-li]").first().should("be.focused");
3373+
3374+
cy.realPress("F2");
3375+
cy.get("[ui5-li]").first().should("be.focused");
3376+
3377+
cy.realPress("Tab");
3378+
cy.get("#after").should("be.focused");
3379+
});
3380+
3381+
it("F2 is a no-op in Multiple selection mode (checkbox has tabindex=-1)", () => {
3382+
cy.mount(
3383+
<div>
3384+
<List selectionMode="Multiple">
3385+
<ListItemStandard>Item 1</ListItemStandard>
3386+
<ListItemStandard>Item 2</ListItemStandard>
3387+
</List>
3388+
<button id="after">After</button>
3389+
</div>
3390+
);
3391+
3392+
cy.get("[ui5-li]").first().realClick();
3393+
cy.get("[ui5-li]").first().should("be.focused");
3394+
3395+
cy.realPress("F2");
3396+
cy.get("[ui5-li]").first().should("be.focused");
3397+
3398+
cy.realPress("Tab");
3399+
cy.get("#after").should("be.focused");
3400+
});
3401+
3402+
it("F7 position memory clamps when navigating to item with fewer elements", () => {
3403+
cy.mount(
3404+
<List selectionMode="Delete">
3405+
<ListItemCustom>
3406+
<Button id="btn1">Action 1</Button>
3407+
<Button id="btn2">Action 2</Button>
3408+
</ListItemCustom>
3409+
<ListItemStandard>Item 2</ListItemStandard>
3410+
</List>
3411+
);
3412+
3413+
cy.get("[ui5-li-custom]").realClick();
3414+
cy.realPress("F7");
3415+
cy.get("#btn1").should("be.focused");
3416+
3417+
cy.realPress("Tab");
3418+
cy.get("#btn2").should("be.focused");
3419+
3420+
cy.realPress("F7");
3421+
cy.get("[ui5-li-custom]").should("be.focused");
3422+
3423+
cy.realPress("ArrowDown");
3424+
cy.get("[ui5-li]").should("be.focused");
3425+
3426+
cy.realPress("F7");
3427+
cy.get("[ui5-li]")
3428+
.shadow()
3429+
.find("[ui5-button]")
3430+
.should("be.focused");
3431+
});
3432+
3433+
it("edit mode is cleared after focusout and re-entering the list restores navigation mode", () => {
3434+
cy.mount(
3435+
<div>
3436+
<button id="outside">Outside</button>
3437+
<List selectionMode="Delete">
3438+
<ListItemStandard>Item 1</ListItemStandard>
3439+
<ListItemStandard>Item 2</ListItemStandard>
3440+
</List>
3441+
<button id="after">After</button>
3442+
</div>
3443+
);
3444+
3445+
cy.get("[ui5-li]").first().realClick();
3446+
cy.realPress("F2");
3447+
3448+
cy.get("[ui5-li]").first()
3449+
.shadow()
3450+
.find("[ui5-button]")
3451+
.should("be.focused");
3452+
3453+
cy.get("#outside").realClick();
3454+
cy.get("#outside").should("be.focused");
3455+
3456+
cy.get("[ui5-li]").first().realClick();
3457+
cy.get("[ui5-li]").first().should("be.focused");
3458+
3459+
cy.realPress("Tab");
3460+
cy.get("#after").should("be.focused");
3461+
});
3462+
3463+
it("Arrow Down from item level uses standard navigation, not edit mode transfer", () => {
3464+
cy.mount(
3465+
<List selectionMode="Delete">
3466+
<ListItemStandard>Item 1</ListItemStandard>
3467+
<ListItemStandard>Item 2</ListItemStandard>
3468+
<ListItemStandard>Item 3</ListItemStandard>
3469+
</List>
3470+
);
3471+
3472+
cy.get("[ui5-li]").first().realClick();
3473+
cy.get("[ui5-li]").first().should("be.focused");
3474+
3475+
cy.realPress("ArrowDown");
3476+
cy.get("[ui5-li]").eq(1).should("be.focused");
3477+
3478+
cy.realPress("ArrowDown");
3479+
cy.get("[ui5-li]").eq(2).should("be.focused");
3480+
});
3481+
3482+
it("Tab chains across items and source item returns to navigation mode after focusout", () => {
3483+
cy.mount(
3484+
<div>
3485+
<List selectionMode="Delete">
3486+
<ListItemStandard>Item 1</ListItemStandard>
3487+
<ListItemStandard>Item 2</ListItemStandard>
3488+
</List>
3489+
<button id="after">After</button>
3490+
</div>
3491+
);
3492+
3493+
cy.get("[ui5-li]").first().realClick();
3494+
cy.realPress("F2");
3495+
3496+
cy.get("[ui5-li]").first()
3497+
.shadow()
3498+
.find("[ui5-button]")
3499+
.should("be.focused");
3500+
3501+
cy.realPress("Tab");
3502+
cy.get("[ui5-li]").eq(1)
3503+
.shadow()
3504+
.find("[ui5-button]")
3505+
.should("be.focused");
3506+
3507+
cy.realPress("Tab");
3508+
cy.get("#after").should("be.focused");
3509+
3510+
cy.get("[ui5-li]").first().realClick();
3511+
cy.get("[ui5-li]").first().should("be.focused");
3512+
3513+
cy.realPress("Tab");
3514+
cy.get("#after").should("be.focused");
3515+
});
3516+
});

0 commit comments

Comments
 (0)