Skip to content

Commit fea13af

Browse files
fix: don't hijack arrow keys from form fields inside Content Slider
The slider keydown handler claimed ArrowLeft/ArrowRight unconditionally, breaking caret movement for interactive inner blocks (form inputs, textareas, selects, contenteditable) used as slides. Skip navigation when focus is inside such an element. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 9a69c0b commit fea13af

2 files changed

Lines changed: 52 additions & 6 deletions

File tree

src/blocks/frontend/content-slider/index.js

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -101,14 +101,25 @@ const initSlider = ( slider ) => {
101101
{ passive: true }
102102
);
103103

104+
// Arrow keys navigate slides, but inner controls (form fields, editable
105+
// content, selects) need them for their own caret/option movement, so the
106+
// slider only claims the keys when focus is not inside such an element.
107+
const isFromInteractiveElement = ( target ) => {
108+
const el = target?.closest?.( 'input, textarea, select, [contenteditable=""], [contenteditable="true"]' );
109+
return Boolean( el );
110+
};
111+
104112
slider.addEventListener( 'keydown', ( event ) => {
105-
if ( 'ArrowLeft' === event.key ) {
106-
event.preventDefault();
107-
goTo( current - 1 );
108-
} else if ( 'ArrowRight' === event.key ) {
109-
event.preventDefault();
110-
goTo( current + 1 );
113+
if ( 'ArrowLeft' !== event.key && 'ArrowRight' !== event.key ) {
114+
return;
111115
}
116+
117+
if ( isFromInteractiveElement( event.target ) ) {
118+
return;
119+
}
120+
121+
event.preventDefault();
122+
goTo( 'ArrowLeft' === event.key ? current - 1 : current + 1 );
112123
});
113124

114125
let timer = null;

src/blocks/test/e2e/blocks/content-slider.spec.js

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,41 @@ test.describe( 'Content Slider Block', () => {
125125
await expect( dots.nth( 0 ) ).toHaveClass( /o-content-dot--active/ );
126126
});
127127

128+
test( 'arrow keys inside a form field edit text instead of navigating slides', async({ editor, page }) => {
129+
130+
// A form field as a slide needs Left/Right for its own caret movement;
131+
// the slider must not hijack those keys when focus is inside the field.
132+
await editor.insertBlock({
133+
name: BLOCK,
134+
attributes: { loop: true },
135+
innerBlocks: [
136+
{
137+
name: 'themeisle-blocks/form',
138+
innerBlocks: [
139+
{ name: 'themeisle-blocks/form-input', attributes: { label: 'Name', type: 'text' } }
140+
]
141+
},
142+
{ name: 'core/paragraph', attributes: { content: 'Second slide' } }
143+
]
144+
});
145+
await publishAndViewPost({ editor, page });
146+
147+
const dots = page.locator( '.o-content-dots .o-content-dot' );
148+
await expect( dots.nth( 0 ) ).toHaveClass( /o-content-dot--active/ );
149+
150+
const input = page.locator( '.o-content-track input[type="text"]' ).first();
151+
await input.fill( 'abc' );
152+
153+
// Caret sits after "abc"; ArrowLeft should move it within the field.
154+
await input.press( 'ArrowLeft' );
155+
156+
// The slide did not change (the key was not hijacked by the slider).
157+
await expect( dots.nth( 0 ) ).toHaveClass( /o-content-dot--active/ );
158+
159+
// The caret moved left inside the field instead of being suppressed.
160+
expect( await input.evaluate( el => el.selectionStart ) ).toBe( 2 );
161+
});
162+
128163
test( 'showArrows and showDots toggles hide chrome', async({ editor, page }) => {
129164
await editor.insertBlock( sliderWithSlides( 2, { showArrows: false, showDots: false }) );
130165
await publishAndViewPost({ editor, page });

0 commit comments

Comments
 (0)