From ff6ea9c6c9fe068e7d12f6e49ead19e6fe18de7f Mon Sep 17 00:00:00 2001 From: Milind More Date: Fri, 31 Jul 2026 15:07:45 +0530 Subject: [PATCH 1/7] fix: update carousel tab indexing logic, adjust mobile responsive styles, and default selected index to 0 --- .../carousel-tab-list/__tests__/view.test.ts | 7 +++++++ .../carousel/carousel-tab-list/style.scss | 20 ++++++++++++++----- src/blocks/carousel/carousel-tab-list/view.ts | 7 ++++++- src/blocks/carousel/save.tsx | 2 +- src/blocks/carousel/view.ts | 6 +++++- 5 files changed, 34 insertions(+), 8 deletions(-) diff --git a/src/blocks/carousel/carousel-tab-list/__tests__/view.test.ts b/src/blocks/carousel/carousel-tab-list/__tests__/view.test.ts index d1523ce..7f89d4e 100644 --- a/src/blocks/carousel/carousel-tab-list/__tests__/view.test.ts +++ b/src/blocks/carousel/carousel-tab-list/__tests__/view.test.ts @@ -209,6 +209,13 @@ describe( 'getTabTabIndex (roving tabindex)', () => { ); expect( storeConfig!.callbacks.getTabTabIndex() ).toBe( '-1' ); } ); + + it( 'returns "0" for the first tab when selectedIndex is -1 or uninitialized', () => { + ( getContext as jest.Mock ).mockReturnValue( + mockContext( { selectedIndex: -1, snap: { index: 0 } } ), + ); + expect( storeConfig!.callbacks.getTabTabIndex() ).toBe( '0' ); + } ); } ); describe( 'getKeyFeatureDotText', () => { diff --git a/src/blocks/carousel/carousel-tab-list/style.scss b/src/blocks/carousel/carousel-tab-list/style.scss index 1753b0a..3c69ba7 100644 --- a/src/blocks/carousel/carousel-tab-list/style.scss +++ b/src/blocks/carousel/carousel-tab-list/style.scss @@ -45,11 +45,8 @@ &:focus-visible { - /* Visible by default (WCAG 2.4.7). Use currentColor unconditionally - * because --rt-tab-border-color defaults to transparent. */ - outline-color: currentcolor; - outline-style: solid; - outline-width: 2px; + /* Visible by default (WCAG 2.4.7). High contrast outline for keyboard navigation */ + outline: 2px solid var(--wp-admin-theme-color, #007cba); outline-offset: 2px; } @@ -61,9 +58,22 @@ } } +@media (max-width: 1024px) { + + .wp-block-rt-carousel-carousel-tab-list { + flex-direction: column; + + &__tab { + width: 100%; + box-sizing: border-box; + } + } +} + @media (prefers-reduced-motion: reduce) { .wp-block-rt-carousel-carousel-tab-list__tab { transition: none; } } + diff --git a/src/blocks/carousel/carousel-tab-list/view.ts b/src/blocks/carousel/carousel-tab-list/view.ts index 4da4c14..34a62a8 100644 --- a/src/blocks/carousel/carousel-tab-list/view.ts +++ b/src/blocks/carousel/carousel-tab-list/view.ts @@ -94,8 +94,13 @@ store( 'rt-carousel/carousel', { * inactive tabs are focusable only via arrow keys (tabindex=-1). */ getTabTabIndex: (): string => { const context = getContext< TabContext >(); - const isActive = context.selectedIndex === context.snap?.index; + const selectedIndex = + typeof context.selectedIndex === 'number' && context.selectedIndex >= 0 + ? context.selectedIndex + : 0; + const isActive = selectedIndex === ( context.snap?.index ?? 0 ); return isActive ? '0' : '-1'; }, }, } as CarouselStore ); + diff --git a/src/blocks/carousel/save.tsx b/src/blocks/carousel/save.tsx index 686eca1..e05edd6 100644 --- a/src/blocks/carousel/save.tsx +++ b/src/blocks/carousel/save.tsx @@ -56,7 +56,7 @@ export default function Save( { : false, isPlaying: !! autoplay, // Initially true if autoplay is enabled timerIterationId: 0, - selectedIndex: -1, + selectedIndex: 0, scrollSnaps: [], canScrollPrev: false, canScrollNext: false, diff --git a/src/blocks/carousel/view.ts b/src/blocks/carousel/view.ts index 0f93cf2..f2cbb3c 100644 --- a/src/blocks/carousel/view.ts +++ b/src/blocks/carousel/view.ts @@ -285,7 +285,11 @@ store( 'rt-carousel/carousel', { const { snap } = context as CarouselContext & { snap?: { index?: number }; }; - return context.selectedIndex === snap?.index; + const selectedIndex = + typeof context.selectedIndex === 'number' && context.selectedIndex >= 0 + ? context.selectedIndex + : 0; + return selectedIndex === snap?.index; }, getDotLabel: () => { const context = getContext(); From f898d9c32089e7d2746fff2ceebe48e220727775 Mon Sep 17 00:00:00 2001 From: Milind More Date: Fri, 31 Jul 2026 15:25:57 +0530 Subject: [PATCH 2/7] style: implement horizontal scrolling for carousel tabs on mobile devices --- src/blocks/carousel/carousel-tab-list/style.scss | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/blocks/carousel/carousel-tab-list/style.scss b/src/blocks/carousel/carousel-tab-list/style.scss index 3c69ba7..c9d3c31 100644 --- a/src/blocks/carousel/carousel-tab-list/style.scss +++ b/src/blocks/carousel/carousel-tab-list/style.scss @@ -61,11 +61,20 @@ @media (max-width: 1024px) { .wp-block-rt-carousel-carousel-tab-list { - flex-direction: column; + max-width: 100%; + /* stylelint-disable-next-line declaration-no-important */ + flex-wrap: nowrap !important; + overflow-x: auto; + -webkit-overflow-scrolling: touch; + scrollbar-width: none; + + &::-webkit-scrollbar { + display: none; + } &__tab { - width: 100%; - box-sizing: border-box; + flex: 0 0 auto; + white-space: nowrap; } } } From cd661374b85b61a159faa1758a9587f872584c7e Mon Sep 17 00:00:00 2001 From: Milind More Date: Fri, 31 Jul 2026 18:40:10 +0530 Subject: [PATCH 3/7] fix: handle undefined snap index to prevent incorrect tab focus state in carousel blocks --- .../carousel/carousel-tab-list/__tests__/view.test.ts | 9 +++++++++ src/blocks/carousel/carousel-tab-list/view.ts | 6 +++++- src/blocks/carousel/view.ts | 5 ++++- 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/blocks/carousel/carousel-tab-list/__tests__/view.test.ts b/src/blocks/carousel/carousel-tab-list/__tests__/view.test.ts index 7f89d4e..c700933 100644 --- a/src/blocks/carousel/carousel-tab-list/__tests__/view.test.ts +++ b/src/blocks/carousel/carousel-tab-list/__tests__/view.test.ts @@ -216,6 +216,15 @@ describe( 'getTabTabIndex (roving tabindex)', () => { ); expect( storeConfig!.callbacks.getTabTabIndex() ).toBe( '0' ); } ); + + it( 'returns "-1" when snap or snap.index is undefined', () => { + ( getContext as jest.Mock ).mockReturnValue( { + carouselId: 'c1', + selectedIndex: 0, + snap: undefined, + } ); + expect( storeConfig!.callbacks.getTabTabIndex() ).toBe( '-1' ); + } ); } ); describe( 'getKeyFeatureDotText', () => { diff --git a/src/blocks/carousel/carousel-tab-list/view.ts b/src/blocks/carousel/carousel-tab-list/view.ts index 34a62a8..7fd58cc 100644 --- a/src/blocks/carousel/carousel-tab-list/view.ts +++ b/src/blocks/carousel/carousel-tab-list/view.ts @@ -94,11 +94,15 @@ store( 'rt-carousel/carousel', { * inactive tabs are focusable only via arrow keys (tabindex=-1). */ getTabTabIndex: (): string => { const context = getContext< TabContext >(); + const snapIndex = context.snap?.index; + if ( typeof snapIndex !== 'number' ) { + return '-1'; + } const selectedIndex = typeof context.selectedIndex === 'number' && context.selectedIndex >= 0 ? context.selectedIndex : 0; - const isActive = selectedIndex === ( context.snap?.index ?? 0 ); + const isActive = selectedIndex === snapIndex; return isActive ? '0' : '-1'; }, }, diff --git a/src/blocks/carousel/view.ts b/src/blocks/carousel/view.ts index f2cbb3c..0aa7328 100644 --- a/src/blocks/carousel/view.ts +++ b/src/blocks/carousel/view.ts @@ -285,11 +285,14 @@ store( 'rt-carousel/carousel', { const { snap } = context as CarouselContext & { snap?: { index?: number }; }; + if ( typeof snap?.index !== 'number' ) { + return false; + } const selectedIndex = typeof context.selectedIndex === 'number' && context.selectedIndex >= 0 ? context.selectedIndex : 0; - return selectedIndex === snap?.index; + return selectedIndex === snap.index; }, getDotLabel: () => { const context = getContext(); From 71f713038801ee02124cb1947d6a766a186c5cbe Mon Sep 17 00:00:00 2001 From: Milind More Date: Fri, 31 Jul 2026 18:42:33 +0530 Subject: [PATCH 4/7] style: update carousel tab list styles to support flex layout and remove important override --- src/blocks/carousel/carousel-tab-list/style.scss | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/blocks/carousel/carousel-tab-list/style.scss b/src/blocks/carousel/carousel-tab-list/style.scss index c9d3c31..44f0c66 100644 --- a/src/blocks/carousel/carousel-tab-list/style.scss +++ b/src/blocks/carousel/carousel-tab-list/style.scss @@ -60,10 +60,10 @@ @media (max-width: 1024px) { - .wp-block-rt-carousel-carousel-tab-list { + .wp-block-rt-carousel-carousel-tab-list, + .wp-block-rt-carousel-carousel-tab-list.is-layout-flex { max-width: 100%; - /* stylelint-disable-next-line declaration-no-important */ - flex-wrap: nowrap !important; + flex-wrap: nowrap; overflow-x: auto; -webkit-overflow-scrolling: touch; scrollbar-width: none; @@ -72,7 +72,7 @@ display: none; } - &__tab { + .wp-block-rt-carousel-carousel-tab-list__tab { flex: 0 0 auto; white-space: nowrap; } From 943eeda38c56bad0c785291901b906de7e6daef2 Mon Sep 17 00:00:00 2001 From: Milind More Date: Fri, 31 Jul 2026 18:43:52 +0530 Subject: [PATCH 5/7] style: update carousel tab list scrollbar to use thin visible styling instead of hidden --- src/blocks/carousel/carousel-tab-list/style.scss | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/blocks/carousel/carousel-tab-list/style.scss b/src/blocks/carousel/carousel-tab-list/style.scss index 44f0c66..36afc8b 100644 --- a/src/blocks/carousel/carousel-tab-list/style.scss +++ b/src/blocks/carousel/carousel-tab-list/style.scss @@ -66,10 +66,21 @@ flex-wrap: nowrap; overflow-x: auto; -webkit-overflow-scrolling: touch; - scrollbar-width: none; + scrollbar-width: thin; + scrollbar-color: rgba(0, 0, 0, 0.2) transparent; + padding-bottom: 4px; &::-webkit-scrollbar { - display: none; + height: 4px; + } + + &::-webkit-scrollbar-track { + background: transparent; + } + + &::-webkit-scrollbar-thumb { + background-color: rgba(0, 0, 0, 0.2); + border-radius: 2px; } .wp-block-rt-carousel-carousel-tab-list__tab { From 7680ff9e872241edc42ff34466c5cdd03b5b6004 Mon Sep 17 00:00:00 2001 From: Milind More Date: Fri, 31 Jul 2026 18:44:55 +0530 Subject: [PATCH 6/7] test: clarify tab index behavior for uninitialized selectedIndex in carousel-tab-list tests --- .../carousel/carousel-tab-list/__tests__/view.test.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/blocks/carousel/carousel-tab-list/__tests__/view.test.ts b/src/blocks/carousel/carousel-tab-list/__tests__/view.test.ts index c700933..0ce4cf5 100644 --- a/src/blocks/carousel/carousel-tab-list/__tests__/view.test.ts +++ b/src/blocks/carousel/carousel-tab-list/__tests__/view.test.ts @@ -210,13 +210,20 @@ describe( 'getTabTabIndex (roving tabindex)', () => { expect( storeConfig!.callbacks.getTabTabIndex() ).toBe( '-1' ); } ); - it( 'returns "0" for the first tab when selectedIndex is -1 or uninitialized', () => { + it( 'returns "0" for the first tab when selectedIndex is -1', () => { ( getContext as jest.Mock ).mockReturnValue( mockContext( { selectedIndex: -1, snap: { index: 0 } } ), ); expect( storeConfig!.callbacks.getTabTabIndex() ).toBe( '0' ); } ); + it( 'returns "0" for the first tab when selectedIndex is undefined (uninitialized)', () => { + ( getContext as jest.Mock ).mockReturnValue( + mockContext( { selectedIndex: undefined as unknown as number, snap: { index: 0 } } ), + ); + expect( storeConfig!.callbacks.getTabTabIndex() ).toBe( '0' ); + } ); + it( 'returns "-1" when snap or snap.index is undefined', () => { ( getContext as jest.Mock ).mockReturnValue( { carouselId: 'c1', From 6d9ed7a6def42dc1b6fa01209d911d387dd91fee Mon Sep 17 00:00:00 2001 From: Milind More Date: Fri, 31 Jul 2026 18:46:06 +0530 Subject: [PATCH 7/7] refactor: split border shorthand into individual properties and fix comment formatting in carousel tab list styles --- src/blocks/carousel/carousel-tab-list/style.scss | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/blocks/carousel/carousel-tab-list/style.scss b/src/blocks/carousel/carousel-tab-list/style.scss index 36afc8b..ef29650 100644 --- a/src/blocks/carousel/carousel-tab-list/style.scss +++ b/src/blocks/carousel/carousel-tab-list/style.scss @@ -32,7 +32,9 @@ align-items: center; justify-content: center; padding: 0.5em 1em; - border: var(--rt-tab-border-width) var(--rt-tab-border-style) var(--rt-tab-border-color); + border-width: var(--rt-tab-border-width); + border-style: var(--rt-tab-border-style); + border-color: var(--rt-tab-border-color); background: var(--rt-tab-inactive-bg); color: var(--rt-tab-inactive-color); cursor: pointer; @@ -45,7 +47,8 @@ &:focus-visible { - /* Visible by default (WCAG 2.4.7). High contrast outline for keyboard navigation */ + /* Visible by default (WCAG 2.4.7). High contrast outline for keyboard + * navigation. */ outline: 2px solid var(--wp-admin-theme-color, #007cba); outline-offset: 2px; }