Skip to content

Commit 9d579b6

Browse files
committed
fix: tabs, finally 😅
1 parent 9a03f5f commit 9d579b6

File tree

11 files changed

+111
-111
lines changed

11 files changed

+111
-111
lines changed

‎.npmrc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
save-exact = true
1+
save-exact = true
2+
public-hoist-pattern[]=@types*

‎apps/website/src/routes/test/index.tsx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,20 +15,20 @@ export default component$(() => {
1515
<Tabs behavior="automatic">
1616
<h3 id="tablist-1">Danish Composers</h3>
1717
<TabList>
18-
<Tab for="maria">Maria Ahlefeldt</Tab>
19-
<Tab for="carl">Carl Andersen</Tab>
20-
<Tab for="ida">Ida Henriette da Fonseca</Tab>
18+
<Tab>Maria Ahlefeldt</Tab>
19+
<Tab>Carl Andersen</Tab>
20+
<Tab>Ida Henriette da Fonseca</Tab>
2121
</TabList>
22-
<TabPanel id="maria">
22+
<TabPanel>
2323
<p>
2424
Maria Theresia Ahlefeldt (16 January 1755 - 20 December 1810) was a
2525
...
2626
</p>
2727
</TabPanel>
28-
<TabPanel id="carl">
28+
<TabPanel>
2929
<p>Carl Joachim Andersen (29 April 1847 - 7 May 1909) was a ...</p>
3030
</TabPanel>
31-
<TabPanel id="ida">
31+
<TabPanel>
3232
<p>
3333
Ida Henriette da Fonseca (July 27, 1802 - July 6, 1858) was a ...
3434
</p>

‎package.json

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,12 @@
2727
"@floating-ui/dom": "1.0.10",
2828
"@jscutlery/semver": "^2.30.1",
2929
"@k11r/nx-cloudflare-wrangler": "^2.0.0",
30-
"@nx/workspace": "16.0.3",
31-
"@nx/linter": "16.0.3",
3230
"@nx/cypress": "16.0.3",
33-
"@nx/storybook": "16.0.3",
3431
"@nx/eslint-plugin": "16.0.3",
32+
"@nx/linter": "16.0.3",
33+
"@nx/storybook": "16.0.3",
3534
"@nx/vite": "16.0.3",
35+
"@nx/workspace": "16.0.3",
3636
"@storybook/addon-a11y": "7.0.8",
3737
"@storybook/addon-essentials": "7.0.8",
3838
"@storybook/addon-interactions": "7.0.8",
@@ -46,6 +46,7 @@
4646
"@storybook/testing-library": "0.1.0",
4747
"@types/eslint": "8.37.0",
4848
"@types/node": "18.16.1",
49+
"@types/testing-library__jest-dom": "5.14.5",
4950
"@typescript-eslint/eslint-plugin": "5.59.1",
5051
"@typescript-eslint/parser": "5.59.1",
5152
"@vitest/coverage-c8": "~0.28.5",

‎packages/kit-headless/.storybook/tsconfig.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
{
22
"extends": "../tsconfig.json",
33
"compilerOptions": {
4-
"emitDecoratorMetadata": true
4+
"emitDecoratorMetadata": true,
5+
"types": ["node", "jest", "@testing-library/jest-dom"]
56
},
67

78
"exclude": [

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

Lines changed: 21 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -7,28 +7,20 @@ import {
77
useComputed$,
88
useTask$,
99
$,
10-
useVisibleTask$,
11-
useSignal,
1210
} from '@builder.io/qwik';
1311
import { tabsContextId } from './tabs-context-id';
1412

1513
export interface TabProps {
16-
for: string;
1714
onClick?: PropFunction<() => void>;
1815
class?: string;
1916
selectedClassName?: string;
2017
}
2118

22-
// Tab button inside of a tab list
2319
export const Tab = component$((props: TabProps) => {
2420
const contextService = useContext(tabsContextId);
2521

2622
const uniqueId = useId();
2723

28-
const currentTabIndex = useSignal(0);
29-
30-
const isSelectedSignal = useSignal(false);
31-
3224
useTask$(({ cleanup }) => {
3325
contextService.tabsChanged$();
3426

@@ -37,24 +29,25 @@ export const Tab = component$((props: TabProps) => {
3729
});
3830
});
3931

40-
useTask$(({ track }) => {
41-
track(contextService.indexByTabId);
42-
console.log('contextService.indexByTabId', contextService.indexByTabId);
43-
currentTabIndex.value = contextService.indexByTabId[uniqueId];
32+
const isSelectedSignal = useComputed$(() => {
33+
return (
34+
contextService.selectedIndex.value ===
35+
contextService.tabsMap[uniqueId]?.index
36+
);
4437
});
4538

46-
useVisibleTask$(() => {
47-
console.log(
48-
'contextService.selectedIndex.value',
49-
contextService.selectedIndex.value
50-
);
51-
console.log('currentTabIndex.value', currentTabIndex.value);
52-
isSelectedSignal.value =
53-
contextService.selectedIndex.value === currentTabIndex.value;
39+
// TODO: Figure out a way to fix this shitty hack :)
40+
useTask$(({ track }) => {
41+
track(() => isSelectedSignal.value);
42+
43+
if (isSelectedSignal.value) {
44+
contextService.showTabs$();
45+
}
5446
});
5547

5648
const selectTab$ = $(() => {
57-
contextService.selectedIndex.value = currentTabIndex.value;
49+
contextService.selectedIndex.value =
50+
contextService.tabsMap[uniqueId]?.index || 0;
5851
});
5952

6053
const selectIfAutomatic$ = $(() => {
@@ -65,19 +58,19 @@ export const Tab = component$((props: TabProps) => {
6558

6659
return (
6760
<button
68-
data-for={props.for}
61+
id={'tab-' + uniqueId}
6962
data-tab-id={uniqueId}
7063
type="button"
7164
role="tab"
7265
onFocus$={selectIfAutomatic$}
7366
onMouseEnter$={selectIfAutomatic$}
7467
aria-selected={isSelectedSignal.value}
75-
aria-controls={'tabpanel-' + props.for}
76-
class={`${isSelectedSignal ? `selected ${props.selectedClassName}` : ''}${
77-
props.class ? ` ${props.class}` : ''
78-
}`}
79-
onClick$={async () => {
80-
await selectTab$();
68+
aria-controls={'tabpanel-' + contextService.tabsMap[uniqueId]?.tabPanelId}
69+
class={`${
70+
isSelectedSignal.value ? `selected ${props.selectedClassName}` : ''
71+
}${props.class ? ` ${props.class}` : ''}`}
72+
onClick$={() => {
73+
selectTab$();
8174
if (props.onClick) {
8275
props.onClick();
8376
}
Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
import { Signal, QRL } from '@builder.io/qwik';
22
import { Behavior } from './behavior.type';
3+
import { TabInfo } from './tabs';
34

45
export interface TabsContext {
56
selectedIndex: Signal<number>;
67
selectedTabId: Signal<string>;
7-
selectedTabPanelId: Signal<string>;
88
selectTab$: QRL<(tabId: string) => void>;
9+
showTabs$: QRL<() => void>;
910
tabsChanged$: QRL<() => void>;
10-
indexByTabId: { [key: string]: number };
11-
indexByTabPanelId: { [key: string]: number };
11+
tabsMap: { [key: string]: TabInfo };
12+
tabPanelsMap: { [key: string]: TabInfo };
1213
behavior: Behavior;
1314
}

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

Lines changed: 11 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
import {
22
component$,
33
useContext,
4-
useSignal,
5-
useVisibleTask$,
64
useId,
75
Slot,
86
useTask$,
@@ -11,19 +9,15 @@ import {
119
import { tabsContextId } from './tabs-context-id';
1210

1311
export interface TabPanelProps {
14-
id: string;
1512
class?: string;
1613
}
1714

18-
// Tab Panel implementation
1915
export const TabPanel = component$(({ ...props }: TabPanelProps) => {
2016
const contextService = useContext(tabsContextId);
21-
const uniqueId = useId();
17+
const panelUID = useId();
2218

23-
const currentPanelIndex = useSignal(0);
24-
25-
const isSelectedSignal = useComputed$(
26-
() => contextService.selectedIndex.value === currentPanelIndex.value
19+
const matchedTabId = useComputed$(
20+
() => contextService.tabPanelsMap[panelUID]?.index
2721
);
2822

2923
useTask$(({ cleanup }) => {
@@ -34,25 +28,25 @@ export const TabPanel = component$(({ ...props }: TabPanelProps) => {
3428
});
3529
});
3630

37-
useTask$(({ track }) => {
38-
track(contextService.indexByTabPanelId);
39-
currentPanelIndex.value = contextService.indexByTabPanelId[uniqueId];
31+
const isSelectedSignal = useComputed$(() => {
32+
return (
33+
contextService.selectedIndex.value ===
34+
contextService.tabPanelsMap[panelUID]?.index
35+
);
4036
});
4137

4238
return (
4339
<div
44-
data-tabpanel-id={uniqueId}
45-
id={'tabpanel-' + props.id}
40+
data-tabpanel-id={panelUID}
41+
id={'tabpanel-' + panelUID}
4642
role="tabpanel"
4743
tabIndex={0}
48-
aria-labelledby={`tab-${currentPanelIndex}`}
44+
aria-labelledby={`tab-${matchedTabId}`}
4945
class={`${isSelectedSignal.value ? 'is-hidden' : ''}${
5046
props.class ? ` ${props.class}` : ''
5147
}`}
5248
style={isSelectedSignal.value ? 'display: block' : 'display: none'}
5349
>
54-
<p>thisPanelIndex.value: {currentPanelIndex.value} </p>
55-
<p>contextService.selectedIndex: {contextService.selectedIndex} </p>
5650
<Slot />
5751
</div>
5852
);

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

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Meta, StoryObj } from 'storybook-framework-qwik';
2-
import { Tab, TabList, TabPanel, Tabs, TabsProps } from './tabs';
2+
import { Tab, TabList, TabPanel, Tabs, TabsProps } from './';
33
import { userEvent, within } from '@storybook/testing-library';
44
import { expect } from '@storybook/jest';
55

@@ -28,10 +28,12 @@ export const Primary: Story = {
2828
play: async ({ canvasElement }) => {
2929
const canvas = within(canvasElement);
3030

31-
const tab2 = canvas.getByRole('tab', { name: 'Tab 2' });
32-
await userEvent.click(tab2);
31+
const tabs = await canvas.findAllByRole('tab');
32+
await userEvent.click(tabs[1]);
3333

34-
// const tabPanel2 = canvas.getByRole('tabpanel');
35-
// await expect(tabPanel2).toContain('Panel 2');
34+
const tabPanel = await canvas.findByRole('tabpanel');
35+
36+
// await expect(tabPanel).toBeInTheDocument();
37+
// await expect(tabPanel).toContain('Panel 2');
3638
},
3739
};

0 commit comments

Comments
 (0)