Skip to content

Commit 1f986de

Browse files
authored
Merge pull request #350 from shairez/pr-tabs-final-selectedIndex-attempt
2 parents 8f09ebc + 4bde8e8 commit 1f986de

File tree

19 files changed

+569
-332
lines changed

19 files changed

+569
-332
lines changed

.github/actions/test/action.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,9 @@ runs:
2626
shell: bash
2727
run: npx nx build headless
2828

29-
- name: Build storybook
30-
shell: bash
31-
run: npx nx build-storybook headless
29+
# - name: Build storybook
30+
# shell: bash
31+
# run: npx nx build-storybook headless
3232

3333
- name: Run Cypress component tests
3434
uses: cypress-io/github-action@v5

.npmrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
save-exact = true
2+
auto-install-peers=true
23
public-hoist-pattern[]=@types*

apps/website/src/components/preview-code-example/preview-code-example.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@ import { Tab, TabList, TabPanel, Tabs } from '@qwik-ui/headless';
44
export const PreviewCodeExample = component$(() => {
55
return (
66
<Tabs class="mb-12">
7-
<TabList class="flex rounded-t-xl p-4 bg-blue-200 dark:bg-indigo-900">
8-
<Tab class="px-4 py-2 rounded-lg hover:bg-[var(--qwik-light-blue)] dark:hover:bg-[var(--qwik-dark-purple)]">
7+
<TabList class="flex rounded-t-xl px-4 bg-blue-200 dark:bg-indigo-900">
8+
<Tab class="px-4 py-2 hover:bg-[var(--qwik-light-blue)] dark:hover:bg-[var(--qwik-dark-purple)]">
99
Preview
1010
</Tab>
11-
<Tab class="px-4 py-2 rounded-lg hover:bg-[var(--qwik-light-blue)] dark:hover:bg-[var(--qwik-dark-purple)]">
11+
<Tab class="px-4 py-2 hover:bg-[var(--qwik-light-blue)] dark:hover:bg-[var(--qwik-dark-purple)]">
1212
Code
1313
</Tab>
1414
</TabList>
@@ -17,7 +17,7 @@ export const PreviewCodeExample = component$(() => {
1717
<Slot name="actualComponent" />
1818
</section>
1919
</TabPanel>
20-
<TabPanel class="rounded-b-xl p-12 bg-slate-900">
20+
<TabPanel class="rounded-b-xl p-12 bg-slate-900">
2121
<section class="overflow-auto">
2222
<Slot name="codeExample" />
2323
</section>

apps/website/src/routes/docs/headless/(components)/tabs/examples.tsx

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,38 @@ export const Example01 = component$(() => {
3939
</PreviewCodeExample>
4040
);
4141
});
42+
43+
export const VerticalTabsExample = component$(() => {
44+
return (
45+
<PreviewCodeExample>
46+
<div q:slot="actualComponent" class="tabs-example mr-auto">
47+
<Tabs vertical>
48+
<h3 id="tablist-1">Danish Composers</h3>
49+
<TabList class="flex flex-col w-fit">
50+
<Tab>Maria Ahlefeldt</Tab>
51+
<Tab>Carl Andersen</Tab>
52+
<Tab>Ida Henriette da Fonseca</Tab>
53+
</TabList>
54+
<TabPanel>
55+
<p>
56+
Maria Theresia Ahlefeldt (16 January 1755 - 20 December 1810) was
57+
a ...
58+
</p>
59+
</TabPanel>
60+
<TabPanel>
61+
<p>Carl Joachim Andersen (29 April 1847 - 7 May 1909) was a ...</p>
62+
</TabPanel>
63+
<TabPanel>
64+
<p>
65+
Ida Henriette da Fonseca (July 27, 1802 - July 6, 1858) was a ...
66+
</p>
67+
</TabPanel>
68+
</Tabs>
69+
</div>
70+
71+
<div q:slot="codeExample">
72+
<Slot />
73+
</div>
74+
</PreviewCodeExample>
75+
);
76+
});
Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
.tabs-example [role='tab'] {
22
width: 250px;
3-
background: lightgreen;
3+
background: #333;
44
}
55

66
.tabs-example [role='tab'].selected {
7-
background: magenta;
7+
background: gray;
88
}
99

1010
.tabs-example [role='tablist'] {
1111
border: 1px solid;
12+
color: white;
13+
margin-bottom: 1rem;
1214
}

apps/website/src/routes/docs/headless/(components)/tabs/index.mdx

Lines changed: 43 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ title: Qwik UI | Tabs
33
---
44

55
import { Tab, TabList, TabPanel, Tabs } from '@qwik-ui/headless';
6-
import { Example01 } from './examples';
6+
import { Example01, VerticalTabsExample } from './examples';
77
import { CodeExample } from '../../../../../components/code-example/code-example';
88
import { KeyboardInteractionTable } from '../../../../../components/keyboard-interaction-table/keyboard-interaction-table';
99
import { APITable } from '../../../../../components/api-table/api-table';
@@ -80,6 +80,34 @@ import { APITable } from '../../../../../components/api-table/api-table';
8080

8181
</CodeExample>
8282

83+
## Vertical
84+
85+
by default, the tabs are horizontal, but you can adjust the underlying behavior to be vertical by setting the `vertical` prop to true.
86+
87+
<VerticalTabsExample>
88+
```tsx
89+
<Tabs vertical>
90+
<h3 id="tablist-1">Danish Composers</h3>
91+
<TabList>
92+
<Tab>Maria Ahlefeldt</Tab>
93+
<Tab>Carl Andersen</Tab>
94+
<Tab>Ida Henriette da Fonseca</Tab>
95+
</TabList>
96+
<TabPanel>
97+
<p>
98+
Maria Theresia Ahlefeldt (16 January 1755 – 20 December 1810) was a ...
99+
</p>
100+
</TabPanel>
101+
<TabPanel>
102+
<p>Carl Joachim Andersen (29 April 1847 – 7 May 1909) was a ...</p>
103+
</TabPanel>
104+
<TabPanel>
105+
<p>Ida Henriette da Fonseca (July 27, 1802 – July 6, 1858) was a ...</p>
106+
</TabPanel>
107+
</Tabs>
108+
```
109+
</VerticalTabsExample>
110+
83111
## Accessibility
84112

85113
### Keyboard interaction
@@ -111,7 +139,6 @@ import { APITable } from '../../../../../components/api-table/api-table';
111139
description: 'In "vertical mode", moves focus to the previous tab.',
112140
},
113141
{
114-
/* {
115142
keyTitle: 'Home',
116143
description: 'Moves focus to the first tab.',
117144
},
@@ -126,7 +153,6 @@ import { APITable } from '../../../../../components/api-table/api-table';
126153
{
127154
keyTitle: 'PageDown',
128155
description: 'Moves focus to the first tab.',
129-
}, */
130156
},
131157
]}
132158
/>
@@ -143,24 +169,22 @@ import { APITable } from '../../../../../components/api-table/api-table';
143169
description:
144170
'Toggle between automatic or manual. The automatic behavior moves between tabs when hover. The manual behavior moves between tabs on click.',
145171
},
172+
{
173+
name: 'selectedIndex',
174+
type: 'number',
175+
description: 'A way to set the selected index programmatically',
176+
},
146177
{
147178
name: 'vertical',
148179
type: 'boolean',
149180
description:
150181
'If set to true, the behavior of UpArrow and DownArrow will navigate between tabs vertically instead of horizontally.',
151182
},
152-
]}
153-
/>
154-
155-
### TabList
156-
157-
<APITable
158-
propDescriptors={[
159183
{
160-
name: 'labelledBy',
161-
type: 'string',
184+
name: 'onSelectedIndexChange$',
185+
type: '(index: number) => void',
162186
description:
163-
'The aria-labelledby for this tablist. Mainly for accessibility purpose.',
187+
'An event hook that getting notified whenever the selected index changes',
164188
},
165189
]}
166190
/>
@@ -176,8 +200,13 @@ import { APITable } from '../../../../../components/api-table/api-table';
176200
},
177201
{
178202
name: 'onClick',
179-
type: 'PropFunction<() => void>',
203+
type: '(event: QwikMouseEvent) => void',
180204
description: 'A custom click handler to wire to tab click event',
181205
},
206+
{
207+
name: 'disabled',
208+
type: 'boolean',
209+
description: 'Set the disabled state of a specific tab',
210+
},
182211
]}
183212
/>

package.json

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
"commit": "git-cz",
1212
"format:fix": "pretty-quick --staged",
1313
"prepare": "husky install",
14+
"build:headless": "nx build headless",
1415
"test:headless": "nx component-test headless",
1516
"test:headless:ci": "nx component-test-ci headless",
1617
"story:build:headless": "nx build-storybook headless",
@@ -91,7 +92,7 @@
9192
"react-dom": "18.2.0",
9293
"sass": "1.58.1",
9394
"storybook": "7.0.0-rc.7",
94-
"storybook-framework-qwik": "0.2.0",
95+
"storybook-framework-qwik": "0.2.2",
9596
"tailwindcss": "^3.2.6",
9697
"ts-node": "10.9.1",
9798
"typescript": "5.0.4",
@@ -115,5 +116,10 @@
115116
],
116117
"dependencies": {
117118
"tslib": "^2.3.0"
119+
},
120+
"pnpm": {
121+
"patchedDependencies": {
122+
123+
}
118124
}
119125
}

packages/kit-headless/src/components/tabs/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,6 @@ export * from './behavior.type';
22
export * from './tabs-context.type';
33
export * from './tabs-context-id';
44
export * from './tabs';
5-
export * from './tabs-panel';
5+
export * from './tab-panel';
66
export * from './tabs-list';
77
export * from './tab';
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import {
2+
component$,
3+
useContext,
4+
useId,
5+
Slot,
6+
useTask$,
7+
useSignal,
8+
useVisibleTask$,
9+
QwikIntrinsicElements,
10+
} from '@builder.io/qwik';
11+
import { tabsContextId } from './tabs-context-id';
12+
import { isBrowser, isServer } from '@builder.io/qwik/build';
13+
14+
export type TabPanelProps = QwikIntrinsicElements['div'];
15+
16+
export const TabPanel = component$(({ ...props }: TabPanelProps) => {
17+
const contextService = useContext(tabsContextId);
18+
const isSelectedSig = useSignal(false);
19+
const serverAssignedIndexSig = useSignal<number | undefined>(undefined);
20+
const matchedTabIdSig = useSignal<string | undefined>(undefined);
21+
22+
const panelUID = useId();
23+
24+
useTask$(async function initIndexTask({ cleanup }) {
25+
if (isServer) {
26+
serverAssignedIndexSig.value =
27+
await contextService.getNextServerAssignedPanelIndex$();
28+
}
29+
if (isBrowser) {
30+
contextService.reIndexTabs$();
31+
}
32+
cleanup(() => {
33+
contextService.reIndexTabs$();
34+
});
35+
});
36+
37+
useTask$(async function isSelectedPanelTask({ track }) {
38+
const isSelected = await track(() =>
39+
contextService.isPanelSelected$(panelUID)
40+
);
41+
42+
if (isServer) {
43+
isSelectedSig.value = await contextService.isIndexSelected$(
44+
serverAssignedIndexSig.value
45+
);
46+
return;
47+
}
48+
49+
isSelectedSig.value = isSelected;
50+
});
51+
52+
useVisibleTask$(async function matchedPanelIdTask({ track }) {
53+
matchedTabIdSig.value = await track(() =>
54+
contextService.getMatchedTabId$(panelUID)
55+
);
56+
});
57+
58+
return (
59+
<div
60+
data-tabpanel-id={panelUID}
61+
id={'tabpanel-' + panelUID}
62+
role="tabpanel"
63+
tabIndex={0}
64+
hidden={isSelectedSig.value ? (null as unknown as undefined) : true}
65+
aria-labelledby={`tab-${matchedTabIdSig.value}`}
66+
class={`${isSelectedSig.value ? '' : 'is-hidden'}${
67+
props.class ? ` ${props.class}` : ''
68+
}`}
69+
style={isSelectedSig.value ? 'display: block' : 'display: none'}
70+
>
71+
<Slot />
72+
</div>
73+
);
74+
});

0 commit comments

Comments
 (0)