Skip to content

Commit 8af235c

Browse files
committed
WIP: refactoring to clean it up
1 parent 1a6ae5a commit 8af235c

File tree

5 files changed

+82
-33
lines changed

5 files changed

+82
-33
lines changed

packages/kit-headless/src/components/tabs/tab.tsx

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -26,29 +26,35 @@ export const Tab = component$((props: TabProps) => {
2626
const serverAssignedIndexSig = useSignal<number | undefined>(undefined);
2727
const uniqueId = useId();
2828

29-
useTask$(({ cleanup }) => {
29+
useTask$(async ({ cleanup }) => {
3030
if (isServer) {
3131
serverAssignedIndexSig.value =
32-
contextService.lastAssignedTabIndexSig.value;
33-
contextService.lastAssignedTabIndexSig.value++;
32+
await contextService.getNextServerAssignedTabIndex$();
3433
}
3534
if (isBrowser) {
36-
contextService.onTabsChanged$();
35+
contextService.reIndexTabs$();
3736
}
3837
cleanup(() => {
39-
contextService.onTabsChanged$();
38+
contextService.reIndexTabs$();
4039
});
4140
});
4241

4342
useTask$(({ track }) => {
4443
track(() => props.disabled);
4544

46-
if (props.disabled && contextService.tabsMap[uniqueId]) {
47-
contextService.tabsMap[uniqueId].disabled = true;
45+
if (props.disabled) {
46+
contextService.updateTabState$();
47+
}
48+
});
49+
50+
const currentTabIndexSig = useComputed$(() => {
51+
if (isServer) {
52+
return serverAssignedIndexSig.value;
4853
}
54+
return;
4955
});
5056

51-
const isSelectedSignal = useComputed$(() => {
57+
const isSelectedSignalSig = useComputed$(() => {
5258
if (isServer) {
5359
return (
5460
serverAssignedIndexSig.value === contextService.selectedIndexSig.value
@@ -92,11 +98,11 @@ export const Tab = component$((props: TabProps) => {
9298
aria-disabled={props.disabled}
9399
onFocus$={selectIfAutomatic$}
94100
onMouseEnter$={selectIfAutomatic$}
95-
aria-selected={isSelectedSignal.value}
96-
tabIndex={isSelectedSignal.value ? 0 : -1}
101+
aria-selected={isSelectedSignalSig.value}
102+
tabIndex={isSelectedSignalSig.value ? 0 : -1}
97103
aria-controls={'tabpanel-' + matchedTabPanelId.value}
98104
class={`${
99-
isSelectedSignal.value
105+
isSelectedSignalSig.value
100106
? `selected ${props.selectedClassName || ''}`
101107
: ''
102108
}${props.class ? ` ${props.class}` : ''}`}

packages/kit-headless/src/components/tabs/tabs-context.type.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,11 @@ import { KeyCode } from '../../utils/key-code.type';
55

66
export interface TabsContext {
77
selectTab$: QRL<(tabId: string) => void>;
8-
showTabs$: QRL<() => void>;
9-
onTabsChanged$: QRL<() => void>;
8+
setSelectedIndex$: QRL<(index: number) => void>;
9+
getNextServerAssignedTabIndex$: QRL<() => number>;
10+
getNextServerAssignedPanelIndex$: QRL<() => number>;
11+
updateTabState$: QRL<(tabIndex: number, state: Partial<TabInfo>) => number>;
12+
reIndexTabs$: QRL<() => void>;
1013
onTabKeyDown$: QRL<(key: KeyCode, tabId: string) => void>;
1114
selectedIndexSig: Signal<number>;
1215
selectedTabIdSig: Signal<string>;

packages/kit-headless/src/components/tabs/tabs-panel.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,10 @@ export const TabPanel = component$(({ ...props }: TabPanelProps) => {
3232
contextService.lastAssignedPanelIndexSig.value++;
3333
}
3434
if (isBrowser) {
35-
contextService.onTabsChanged$();
35+
contextService.reIndexTabs$();
3636
}
3737
cleanup(() => {
38-
contextService.onTabsChanged$();
38+
contextService.reIndexTabs$();
3939
});
4040
});
4141

packages/kit-headless/src/components/tabs/tabs.spec.tsx

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { component$, useSignal, useStore } from '@builder.io/qwik';
1+
import { component$, useSignal, useStore, $, QRL } from '@builder.io/qwik';
22
import { Tab } from './tab';
33
import { Tabs } from './tabs';
44
import { TabList } from './tabs-list';
@@ -19,10 +19,17 @@ const ThreeTabsComponent = component$(
1919
disabledIndex,
2020
}: ThreeTabsCompProps) => {
2121
const isMiddleDisabledSignal = useSignal(isMiddleDisabled);
22-
22+
const selectedIndexDisplayValueSig = useSignal<number>();
23+
const onSelectedIndexChange$ = $((index: number) => {
24+
selectedIndexDisplayValueSig.value = index;
25+
});
2326
return (
2427
<>
25-
<Tabs data-testid="tabs" vertical={isVertical}>
28+
<Tabs
29+
data-testid="tabs"
30+
vertical={isVertical}
31+
onSelectedIndexChange$={onSelectedIndexChange$}
32+
>
2633
<TabList
2734
style={{
2835
display: 'flex',
@@ -43,6 +50,12 @@ const ThreeTabsComponent = component$(
4350

4451
<br />
4552

53+
{selectedIndexDisplayValueSig.value !== undefined && (
54+
<div data-testid="selected-index-from-event">
55+
Selected index from event: {selectedIndexDisplayValueSig.value}
56+
</div>
57+
)}
58+
4659
{showDisableButton && (
4760
<button
4861
onClick$={() =>
@@ -169,6 +182,16 @@ describe('Tabs', () => {
169182
cy.findByRole('tabpanel').should('contain', 'Dynamic Tab 2 Panel');
170183
});
171184

185+
it.only(`GIVEN 3 tabs
186+
WHEN clicking the middle one
187+
THEN onSelectedIndexChange should be called`, () => {
188+
cy.mount(<ThreeTabsComponent />);
189+
190+
cy.findByRole('tab', { name: /Tab 2/i }).click();
191+
192+
cy.findByTestId('selected-index-from-event').should('contain.text', 1);
193+
});
194+
172195
describe('Dynamic Tabs', () => {
173196
it(`GIVEN 3 tabs,
174197
WHEN removing the last one dynamically

packages/kit-headless/src/components/tabs/tabs.tsx

Lines changed: 32 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
useVisibleTask$,
88
useStore,
99
useTask$,
10+
QRL,
1011
} from '@builder.io/qwik';
1112
import { tabsContextId } from './tabs-context-id';
1213
import { TabsContext } from './tabs-context.type';
@@ -36,6 +37,7 @@ export interface TabsProps {
3637
class?: string;
3738
selectedIndex?: number;
3839
vertical?: boolean;
40+
onSelectedIndexChange$?: QRL<(index: number) => void>;
3941
}
4042

4143
export interface TabPair {
@@ -65,29 +67,42 @@ export const Tabs = component$((props: TabsProps) => {
6567

6668
const selectedTabIdSig = useSignal<string>('');
6769
const reIndexTabsSig = useSignal(true);
68-
const showTabsSig = useSignal(false);
69-
const tabPairs = useStore<TabPair[]>([]);
70+
71+
const tabPairsList = useStore<TabPair[]>([]);
7072

7173
const tabsMap = useStore<{ [key: string]: TabInfo }>({});
7274

7375
const tabPanelsMap = useStore<{ [key: string]: TabInfo }>({});
7476

75-
const onTabsChanged$ = $(() => {
77+
const reIndexTabs$ = $(() => {
7678
reIndexTabsSig.value = true;
7779
});
7880

7981
const selectTab$ = $((tabId: string) => {
8082
selectedTabIdSig.value = tabId;
8183
});
8284

83-
const showTabs$ = $(() => {
84-
showTabsSig.value = true;
85+
const updateTabState$ = $((tabIndex: number, state: Partial<TabInfo>) => {
86+
const prevState = tabPairsList[tabIndex];
87+
tabPairsList[tabIndex] = { ...prevState, ...state };
88+
});
89+
90+
const getNextServerAssignedTabIndex$ = $(() => {
91+
lastAssignedTabIndexSig.value++;
92+
return lastAssignedTabIndexSig.value;
8593
});
8694

95+
const getNextServerAssignedPanelIndex$ = $(() => {
96+
lastAssignedPanelIndexSig.value++;
97+
return lastAssignedPanelIndexSig.value;
98+
});
99+
100+
const setSelectedIndex$ = $((index: number) => {});
101+
87102
const onTabKeyDown$ = $((key: KeyCode, tabId: string) => {
88103
const tabsRootElement = ref.value;
89104

90-
const enabledTabs = tabPairs.filter((tabPair) => {
105+
const enabledTabs = tabPairsList.filter((tabPair) => {
91106
return !tabsMap[tabPair.tabId].disabled;
92107
});
93108
const currentFocusedTabIndex = enabledTabs.findIndex(
@@ -100,7 +115,7 @@ export const Tabs = component$((props: TabsProps) => {
100115
) {
101116
let nextTabId = enabledTabs[0].tabId;
102117

103-
if (currentFocusedTabIndex < tabPairs.length - 1) {
118+
if (currentFocusedTabIndex < tabPairsList.length - 1) {
104119
nextTabId = enabledTabs[currentFocusedTabIndex + 1].tabId;
105120
}
106121
tabsRootElement
@@ -139,8 +154,10 @@ export const Tabs = component$((props: TabsProps) => {
139154

140155
const contextService: TabsContext = {
141156
selectTab$,
142-
showTabs$,
143-
onTabsChanged$,
157+
setSelectedIndex$,
158+
getNextServerAssignedTabIndex$,
159+
getNextServerAssignedPanelIndex$,
160+
reIndexTabs$,
144161
onTabKeyDown$,
145162
selectedTabIdSig,
146163
selectedIndexSig,
@@ -183,11 +200,11 @@ export const Tabs = component$((props: TabsProps) => {
183200

184201
// See if the deleted index was the last one
185202
let lastTabWasSelectedPreviously = false;
186-
if (selectedIndexSig.value === tabPairs.length - 1) {
203+
if (selectedIndexSig.value === tabPairsList.length - 1) {
187204
lastTabWasSelectedPreviously = true;
188205
}
189206

190-
tabPairs.length = 0;
207+
tabPairsList.length = 0;
191208

192209
let deletedTabId: string | undefined = undefined;
193210

@@ -216,7 +233,7 @@ export const Tabs = component$((props: TabsProps) => {
216233
}
217234
const tabPanelId = tabPanelElement.getAttribute('data-tabpanel-id');
218235
if (tabId && tabPanelId) {
219-
tabPairs.push({ tabId, tabPanelId });
236+
tabPairsList.push({ tabId, tabPanelId });
220237

221238
tabsMap[tabId] = {
222239
tabId,
@@ -239,11 +256,11 @@ export const Tabs = component$((props: TabsProps) => {
239256
}
240257
});
241258

242-
if (tabPairs.length > 0) {
259+
if (tabPairsList.length > 0) {
243260
if (lastTabWasSelectedPreviously && deletedTabId) {
244-
selectedIndexSig.value = tabPairs.length - 1;
261+
selectedIndexSig.value = tabPairsList.length - 1;
245262
}
246-
selectedTabIdSig.value = tabPairs[selectedIndexSig.value].tabId;
263+
selectedTabIdSig.value = tabPairsList[selectedIndexSig.value].tabId;
247264
}
248265
}
249266
});

0 commit comments

Comments
 (0)