Skip to content

Commit 9b15294

Browse files
committed
refactor: update tabs demo/doc
1 parent a390887 commit 9b15294

File tree

8 files changed

+78
-15
lines changed

8 files changed

+78
-15
lines changed

apps/showcase/__store__/index.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1251,6 +1251,10 @@ export const Store: Record<string, Record<string, Record<string, { component: Re
12511251
'component': React.lazy(() => import('demo/styled/listbox/filter-demo')),
12521252
'filePath': 'demo/styled/listbox/filter-demo.tsx',
12531253
},
1254+
'focus-behavior-demo': {
1255+
'component': React.lazy(() => import('demo/styled/listbox/focus-behavior-demo')),
1256+
'filePath': 'demo/styled/listbox/focus-behavior-demo.tsx',
1257+
},
12541258
'invalid-demo': {
12551259
'component': React.lazy(() => import('demo/styled/listbox/invalid-demo')),
12561260
'filePath': 'demo/styled/listbox/invalid-demo.tsx',

apps/showcase/assets/styles/layout/_designer.css

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@
103103
}
104104
}
105105

106-
.p-tabs {
106+
/*.p-tabs {
107107
.p-tablist-tab-list {
108108
background: transparent;
109109
border-style: solid;
@@ -135,12 +135,12 @@
135135
136136
.p-tab-active {
137137
background: transparent;
138-
border-color: var(--high-contrast-text-color);
138+
border-color: transparent;
139139
color: var(--high-contrast-text-color);
140140
}
141141
142142
.p-tablist-active-bar {
143-
display: none;
143+
display: block;
144144
inset-block-end: -1px;
145145
height: 1px;
146146
background: var(--high-contrast-text-color);
@@ -246,4 +246,5 @@
246246
box-shadow: none;
247247
}
248248
}
249+
*/
249250
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
'use client';
2+
import type { ListboxRootValueChangeEvent } from '@primereact/types/shared/listbox';
3+
import { Button } from '@primereact/ui/button';
4+
import { Listbox } from '@primereact/ui/listbox';
5+
import * as React from 'react';
6+
7+
const cities = [
8+
{ name: 'New York', code: 'NY' },
9+
{ name: 'Rome', code: 'RM' },
10+
{ name: 'London', code: 'LDN' },
11+
{ name: 'Istanbul', code: 'IST' },
12+
{ name: 'Paris', code: 'PRS' }
13+
];
14+
15+
export default function FocusBehaviorDemo() {
16+
const [selectedCity, setSelectedCity] = React.useState<string | null>(null);
17+
const [autoOptionFocus, setAutoOptionFocus] = React.useState(true);
18+
const [selectOnFocus, setSelectOnFocus] = React.useState(false);
19+
const [focusOnHover, setFocusOnHover] = React.useState(true);
20+
21+
return (
22+
<div className="space-y-4">
23+
<div className="flex flex-wrap gap-2 justify-center">
24+
<Button size="small" severity={autoOptionFocus ? 'primary' : 'secondary'} onClick={() => setAutoOptionFocus((prev) => !prev)}>
25+
autoOptionFocus: {String(autoOptionFocus)}
26+
</Button>
27+
<Button size="small" severity={selectOnFocus ? 'primary' : 'secondary'} onClick={() => setSelectOnFocus((prev) => !prev)}>
28+
selectOnFocus: {String(selectOnFocus)}
29+
</Button>
30+
<Button size="small" severity={focusOnHover ? 'primary' : 'secondary'} onClick={() => setFocusOnHover((prev) => !prev)}>
31+
focusOnHover: {String(focusOnHover)}
32+
</Button>
33+
</div>
34+
35+
<div className="flex justify-center">
36+
<Listbox.Root
37+
value={selectedCity}
38+
onValueChange={(e: ListboxRootValueChangeEvent) => setSelectedCity(e.value as string | null)}
39+
options={cities}
40+
optionLabel="name"
41+
optionValue="code"
42+
autoOptionFocus={autoOptionFocus}
43+
selectOnFocus={selectOnFocus}
44+
focusOnHover={focusOnHover}
45+
className="w-full md:w-56"
46+
>
47+
<Listbox.List />
48+
</Listbox.Root>
49+
</div>
50+
</div>
51+
);
52+
}

apps/showcase/docs/styled/components/listbox/features.mdx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,12 @@ Use the `value` and `onValueChange` properties to control the selected value. Th
3535

3636
<DocDemoViewer name="listbox:controlled-demo" />
3737

38+
### Focus Behavior
39+
40+
Use `autoOptionFocus` to control initial focused option behavior, `selectOnFocus` to select options while navigating with focus, and `focusOnHover` to move focus with mouse hover when the component is focused.
41+
42+
<DocDemoViewer name="listbox:focus-behavior-demo" />
43+
3844
### Option
3945

4046
Use the `Listbox.Option` component to define options manually. Each option requires a `uKey` or `index` prop for identification and the `optionKey` property on the root specifies the corresponding field from the data.

apps/showcase/docs/styled/components/tabs/features.mdx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,11 @@ import { Tabs } from '@primereact/ui/tabs';
1515
```tsx
1616
<Tabs.Root>
1717
<Tabs.List>
18-
<Tabs.Tab value="tab1">Tab 1</Tabs.Tab>
18+
<Tabs.Tab />
1919
<Tabs.Indicator />
2020
</Tabs.List>
2121
<Tabs.Panels>
22-
<Tabs.Panel value="tab1">Tab 1 Content</Tabs.Panel>
22+
<Tabs.Panel />
2323
</Tabs.Panels>
2424
</Tabs.Root>
2525
```
@@ -66,13 +66,13 @@ Use custom markup inside `Tabs.Tab` and `Tabs.Panel` to build richer tab content
6666

6767
### Screen Reader
6868

69-
`Tabs.List` acts as the tablist container. `Tabs.Tab` elements use the `tab` role and expose selected and disabled state via ARIA attributes. `Tabs.Panel` uses the `tabpanel` role for associated content.
69+
`Tabs.Tab` elements use the `tab` role and expose selected and disabled state via ARIA attributes. `Tabs.Panel` uses the `tabpanel` role and links back to its related tab with `aria-labelledby`.
7070

7171
### Tab Keyboard Support
7272

7373
| Key | Function |
7474
| ------------- | ---------------------------------------------------------------------------------------------------- |
75-
| `tab` | Moves focus through the header. |
75+
| `tab` | Moves focus to the active tab and then proceeds to the next focusable element. |
7676
| `enter` | Activates the focused tab header. |
7777
| `space` | Activates the focused tab header. |
7878
| `right arrow` | Moves focus to the next header. If focus is on the last header, moves focus to the first header. |

packages/@primereact/headless/src/tabs/useTabs.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,12 +43,12 @@ export const useTabs = withHeadless({
4343

4444
if (!tabs) return;
4545

46-
const activeTab = findSingle(tabs, '[data-pc-section="tab"][data-p-active="true"]');
46+
const activeTab = findSingle(tabs, '[data-slot="tab"][data-p-active="true"]');
4747

4848
if (!activeTab) return;
4949

5050
// Get elements and their offsets
51-
const scrollContainer = tabs.querySelector('[data-pc-section="content"]') as HTMLElement;
51+
const scrollContainer = tabs.querySelector('[data-slot="content"]') as HTMLElement;
5252

5353
if (!scrollContainer) return;
5454

@@ -82,7 +82,7 @@ export const useTabs = withHeadless({
8282

8383
const focusTab = (tabElement: HTMLElement | null, direction: 'next' | 'previous' | 'first' | 'last'): void => {
8484
const findTab = (listElement: HTMLElement): HTMLElement | null => {
85-
return findSingle(listElement, '[data-pc-section="tab"]') as HTMLElement | null;
85+
return findSingle(listElement, '[data-slot="tab"]') as HTMLElement | null;
8686
};
8787

8888
const findAdjacentTab = (listElement: HTMLElement, direction: 'next' | 'previous', selfCheck = false): HTMLElement | null => {
@@ -101,11 +101,11 @@ export const useTabs = withHeadless({
101101
};
102102

103103
const findBoundaryTab = (boundary: 'first' | 'last'): HTMLElement | null => {
104-
const listElement = findSingle(elementRef?.current as HTMLElement, '[data-pc-section="tablist"]') as HTMLElement | null;
104+
const listElement = findSingle(elementRef?.current as HTMLElement, '[data-slot="tabList"]') as HTMLElement | null;
105105

106106
if (!listElement) return null;
107107

108-
const tabSelector = '[data-pc-section="tab"]';
108+
const tabSelector = '[data-slot="tab"]';
109109
let targetChild: HTMLElement | null = null;
110110

111111
if (boundary === 'first') {

packages/primereact/src/tabs/list/TabsList.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ export const TabsList = withComponent({
148148
type="button"
149149
className={tabs?.cx('prevButton')}
150150
aria-label={props.prevButtonAriaLabel}
151-
tabIndex={tabs?.props.tabindex}
151+
tabIndex={tabs?.props.tabIndex}
152152
style={{
153153
zIndex: 50
154154
}}
@@ -169,7 +169,7 @@ export const TabsList = withComponent({
169169
type="button"
170170
className={tabs?.cx('nextButton')}
171171
aria-label={props.nextButtonAriaLabel}
172-
tabIndex={tabs?.props.tabindex}
172+
tabIndex={tabs?.props.tabIndex}
173173
onClick={onNextButtonClick}
174174
{...ptmi('nextButton')}
175175
style={{

packages/primereact/src/tabs/tab/TabsTab.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ export const TabsTab = withComponent({
2222
const rootProps = mergeProps(
2323
{
2424
className: tabs?.cx('tab', { active, disabled: props.disabled }),
25-
tabIndex: active ? tabs?.props.tabindex : -1,
25+
tabIndex: active ? tabs?.props.tabIndex : -1,
2626
disabled: props.disabled,
2727
role: 'tab',
2828
'data-p-active': active,

0 commit comments

Comments
 (0)