@instructure/ui-tabs 11.6.0 → 11.6.1-snapshot-129
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +48 -308
- package/es/Tabs/{Panel → v1/Panel}/index.js +2 -2
- package/es/Tabs/{Tab → v1/Tab}/index.js +2 -2
- package/es/Tabs/{index.js → v1/index.js} +2 -2
- package/es/Tabs/v2/Panel/index.js +126 -0
- package/es/Tabs/v2/Panel/props.js +26 -0
- package/es/Tabs/v2/Panel/styles.js +81 -0
- package/es/Tabs/v2/Tab/index.js +118 -0
- package/es/Tabs/v2/Tab/props.js +26 -0
- package/es/Tabs/v2/Tab/styles.js +145 -0
- package/es/Tabs/v2/index.js +396 -0
- package/es/Tabs/v2/props.js +26 -0
- package/es/Tabs/v2/styles.js +145 -0
- package/es/{index.js → exports/a.js} +3 -3
- package/es/exports/b.js +26 -0
- package/lib/Tabs/v1/Panel/index.js +132 -0
- package/lib/Tabs/v1/Tab/index.js +125 -0
- package/lib/Tabs/v1/index.js +410 -0
- package/lib/Tabs/{Panel → v2/Panel}/index.js +3 -4
- package/lib/Tabs/v2/Panel/props.js +31 -0
- package/lib/Tabs/v2/Panel/styles.js +87 -0
- package/lib/Tabs/{Tab → v2/Tab}/index.js +3 -4
- package/lib/Tabs/v2/Tab/props.js +31 -0
- package/lib/Tabs/v2/Tab/styles.js +151 -0
- package/lib/Tabs/{index.js → v2/index.js} +5 -6
- package/lib/Tabs/v2/props.js +31 -0
- package/lib/Tabs/v2/styles.js +151 -0
- package/lib/{index.js → exports/a.js} +4 -4
- package/lib/exports/b.js +26 -0
- package/package.json +46 -24
- package/src/Tabs/{Panel → v1/Panel}/index.tsx +2 -2
- package/src/Tabs/{Tab → v1/Tab}/index.tsx +3 -3
- package/src/Tabs/{Tab → v1/Tab}/props.ts +1 -1
- package/src/Tabs/{index.tsx → v1/index.tsx} +3 -3
- package/src/Tabs/{props.ts → v1/props.ts} +1 -1
- package/src/Tabs/v2/Panel/index.tsx +138 -0
- package/src/Tabs/v2/Panel/props.ts +100 -0
- package/src/Tabs/v2/Panel/styles.ts +92 -0
- package/src/Tabs/v2/README.md +559 -0
- package/src/Tabs/v2/Tab/index.tsx +123 -0
- package/src/Tabs/v2/Tab/props.ts +80 -0
- package/src/Tabs/v2/Tab/styles.ts +161 -0
- package/src/Tabs/v2/index.tsx +547 -0
- package/src/Tabs/v2/props.ts +126 -0
- package/src/Tabs/v2/styles.ts +156 -0
- package/src/{index.ts → exports/a.ts} +6 -6
- package/src/exports/b.ts +31 -0
- package/tsconfig.build.tsbuildinfo +1 -1
- package/types/Tabs/v1/Panel/index.d.ts.map +1 -0
- package/types/Tabs/v1/Panel/props.d.ts.map +1 -0
- package/types/Tabs/v1/Panel/styles.d.ts.map +1 -0
- package/types/Tabs/v1/Panel/theme.d.ts.map +1 -0
- package/types/Tabs/{Tab → v1/Tab}/index.d.ts +1 -1
- package/types/Tabs/v1/Tab/index.d.ts.map +1 -0
- package/types/Tabs/{Tab → v1/Tab}/props.d.ts +1 -1
- package/types/Tabs/v1/Tab/props.d.ts.map +1 -0
- package/types/Tabs/v1/Tab/styles.d.ts.map +1 -0
- package/types/Tabs/v1/Tab/theme.d.ts.map +1 -0
- package/types/Tabs/{index.d.ts → v1/index.d.ts} +1 -1
- package/types/Tabs/v1/index.d.ts.map +1 -0
- package/types/Tabs/{props.d.ts → v1/props.d.ts} +1 -1
- package/types/Tabs/v1/props.d.ts.map +1 -0
- package/types/Tabs/v1/styles.d.ts.map +1 -0
- package/types/Tabs/v1/theme.d.ts.map +1 -0
- package/types/Tabs/v2/Panel/index.d.ts +46 -0
- package/types/Tabs/v2/Panel/index.d.ts.map +1 -0
- package/types/Tabs/v2/Panel/props.d.ts +46 -0
- package/types/Tabs/v2/Panel/props.d.ts.map +1 -0
- package/types/Tabs/v2/Panel/styles.d.ts +19 -0
- package/types/Tabs/v2/Panel/styles.d.ts.map +1 -0
- package/types/Tabs/v2/Tab/index.d.ts +43 -0
- package/types/Tabs/v2/Tab/index.d.ts.map +1 -0
- package/types/Tabs/v2/Tab/props.d.ts +33 -0
- package/types/Tabs/v2/Tab/props.d.ts.map +1 -0
- package/types/Tabs/v2/Tab/styles.d.ts +20 -0
- package/types/Tabs/v2/Tab/styles.d.ts.map +1 -0
- package/types/Tabs/v2/index.d.ts +80 -0
- package/types/Tabs/v2/index.d.ts.map +1 -0
- package/types/Tabs/v2/props.d.ts +68 -0
- package/types/Tabs/v2/props.d.ts.map +1 -0
- package/types/Tabs/v2/styles.d.ts +19 -0
- package/types/Tabs/v2/styles.d.ts.map +1 -0
- package/types/exports/a.d.ts +7 -0
- package/types/exports/a.d.ts.map +1 -0
- package/types/exports/b.d.ts +7 -0
- package/types/exports/b.d.ts.map +1 -0
- package/types/Tabs/Panel/index.d.ts.map +0 -1
- package/types/Tabs/Panel/props.d.ts.map +0 -1
- package/types/Tabs/Panel/styles.d.ts.map +0 -1
- package/types/Tabs/Panel/theme.d.ts.map +0 -1
- package/types/Tabs/Tab/index.d.ts.map +0 -1
- package/types/Tabs/Tab/props.d.ts.map +0 -1
- package/types/Tabs/Tab/styles.d.ts.map +0 -1
- package/types/Tabs/Tab/theme.d.ts.map +0 -1
- package/types/Tabs/index.d.ts.map +0 -1
- package/types/Tabs/props.d.ts.map +0 -1
- package/types/Tabs/styles.d.ts.map +0 -1
- package/types/Tabs/theme.d.ts.map +0 -1
- package/types/index.d.ts +0 -7
- package/types/index.d.ts.map +0 -1
- /package/es/Tabs/{Panel → v1/Panel}/props.js +0 -0
- /package/es/Tabs/{Panel → v1/Panel}/styles.js +0 -0
- /package/es/Tabs/{Panel → v1/Panel}/theme.js +0 -0
- /package/es/Tabs/{Tab → v1/Tab}/props.js +0 -0
- /package/es/Tabs/{Tab → v1/Tab}/styles.js +0 -0
- /package/es/Tabs/{Tab → v1/Tab}/theme.js +0 -0
- /package/es/Tabs/{props.js → v1/props.js} +0 -0
- /package/es/Tabs/{styles.js → v1/styles.js} +0 -0
- /package/es/Tabs/{theme.js → v1/theme.js} +0 -0
- /package/lib/Tabs/{Panel → v1/Panel}/props.js +0 -0
- /package/lib/Tabs/{Panel → v1/Panel}/styles.js +0 -0
- /package/lib/Tabs/{Panel → v1/Panel}/theme.js +0 -0
- /package/lib/Tabs/{Tab → v1/Tab}/props.js +0 -0
- /package/lib/Tabs/{Tab → v1/Tab}/styles.js +0 -0
- /package/lib/Tabs/{Tab → v1/Tab}/theme.js +0 -0
- /package/lib/Tabs/{props.js → v1/props.js} +0 -0
- /package/lib/Tabs/{styles.js → v1/styles.js} +0 -0
- /package/lib/Tabs/{theme.js → v1/theme.js} +0 -0
- /package/src/Tabs/{Panel → v1/Panel}/props.ts +0 -0
- /package/src/Tabs/{Panel → v1/Panel}/styles.ts +0 -0
- /package/src/Tabs/{Panel → v1/Panel}/theme.ts +0 -0
- /package/src/Tabs/{README.md → v1/README.md} +0 -0
- /package/src/Tabs/{Tab → v1/Tab}/styles.ts +0 -0
- /package/src/Tabs/{Tab → v1/Tab}/theme.ts +0 -0
- /package/src/Tabs/{styles.ts → v1/styles.ts} +0 -0
- /package/src/Tabs/{theme.ts → v1/theme.ts} +0 -0
- /package/types/Tabs/{Panel → v1/Panel}/index.d.ts +0 -0
- /package/types/Tabs/{Panel → v1/Panel}/props.d.ts +0 -0
- /package/types/Tabs/{Panel → v1/Panel}/styles.d.ts +0 -0
- /package/types/Tabs/{Panel → v1/Panel}/theme.d.ts +0 -0
- /package/types/Tabs/{Tab → v1/Tab}/styles.d.ts +0 -0
- /package/types/Tabs/{Tab → v1/Tab}/theme.d.ts +0 -0
- /package/types/Tabs/{styles.d.ts → v1/styles.d.ts} +0 -0
- /package/types/Tabs/{theme.d.ts → v1/theme.d.ts} +0 -0
|
@@ -0,0 +1,559 @@
|
|
|
1
|
+
---
|
|
2
|
+
describes: Tabs
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
`<Tabs />` is an accessible tabbed navigation component. Use the TAB key to focus the component and arrow keys to navigate between panels of content. To set a default panel that should be selected on initial render, set the `selected` prop on that `<Tabs.Panel>`.
|
|
6
|
+
|
|
7
|
+
```js
|
|
8
|
+
---
|
|
9
|
+
type: example
|
|
10
|
+
---
|
|
11
|
+
const Example = () => {
|
|
12
|
+
const [selectedIndex, setSelectedIndex] = useState(0)
|
|
13
|
+
|
|
14
|
+
const handleTabChange = (event, { index }) => {
|
|
15
|
+
setSelectedIndex(index)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
return (
|
|
19
|
+
<Tabs
|
|
20
|
+
margin="large auto"
|
|
21
|
+
padding="medium"
|
|
22
|
+
onRequestTabChange={handleTabChange}
|
|
23
|
+
>
|
|
24
|
+
<Tabs.Panel
|
|
25
|
+
id="tabA"
|
|
26
|
+
tabIndex={-1}
|
|
27
|
+
renderTitle="Tab A"
|
|
28
|
+
textAlign="center"
|
|
29
|
+
padding="large"
|
|
30
|
+
isSelected={selectedIndex === 0}
|
|
31
|
+
>
|
|
32
|
+
<Button>Focus Me</Button>
|
|
33
|
+
</Tabs.Panel>
|
|
34
|
+
<Tabs.Panel id="tabB" renderTitle="Disabled Tab" isDisabled>
|
|
35
|
+
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod
|
|
36
|
+
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim
|
|
37
|
+
veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea
|
|
38
|
+
commodo consequat. Duis aute irure dolor in reprehenderit in voluptate
|
|
39
|
+
velit esse cillum dolore eu fugiat nulla pariatur.
|
|
40
|
+
</Tabs.Panel>
|
|
41
|
+
<Tabs.Panel
|
|
42
|
+
id="tabC"
|
|
43
|
+
renderTitle="Tab C"
|
|
44
|
+
isSelected={selectedIndex === 2}
|
|
45
|
+
tabIndex={0}
|
|
46
|
+
>
|
|
47
|
+
Sed ut perspiciatis unde omnis iste natus error sit voluptatem
|
|
48
|
+
accusantium doloremque laudantium. Totam rem aperiam, eaque ipsa quae ab
|
|
49
|
+
illo inventore veritatis et quasi architecto beatae vitae dicta sunt
|
|
50
|
+
explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut
|
|
51
|
+
odit aut fugit, sed quia consequuntur magni dolores.
|
|
52
|
+
</Tabs.Panel>
|
|
53
|
+
<Tabs.Panel
|
|
54
|
+
id="tabD"
|
|
55
|
+
renderTitle="Tab D"
|
|
56
|
+
isSelected={selectedIndex === 3}
|
|
57
|
+
tabIndex={0}
|
|
58
|
+
>
|
|
59
|
+
At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis
|
|
60
|
+
praesentium voluptatum deleniti atque corrupti. Quos dolores et quas
|
|
61
|
+
molestias excepturi sint occaecati cupiditate non provident, similique
|
|
62
|
+
sunt in culpa. Qui officia deserunt mollitia animi, id est laborum et
|
|
63
|
+
dolorum fuga.
|
|
64
|
+
</Tabs.Panel>
|
|
65
|
+
</Tabs>
|
|
66
|
+
)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
render(<Example />)
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Secondary Tabs
|
|
73
|
+
|
|
74
|
+
```js
|
|
75
|
+
---
|
|
76
|
+
type: example
|
|
77
|
+
---
|
|
78
|
+
const Example = () => {
|
|
79
|
+
const [selectedIndex, setSelectedIndex] = useState(2)
|
|
80
|
+
|
|
81
|
+
const handleTabChange = (event, { index }) => {
|
|
82
|
+
setSelectedIndex(index)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return (
|
|
86
|
+
<Tabs
|
|
87
|
+
variant="secondary"
|
|
88
|
+
onRequestTabChange={handleTabChange}
|
|
89
|
+
minHeight="10rem"
|
|
90
|
+
maxHeight="10rem"
|
|
91
|
+
>
|
|
92
|
+
<Tabs.Panel renderTitle="First Tab" isSelected={selectedIndex === 0} tabIndex={0}>
|
|
93
|
+
Hello World
|
|
94
|
+
</Tabs.Panel>
|
|
95
|
+
<Tabs.Panel renderTitle="Disabled Tab" isDisabled>
|
|
96
|
+
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod
|
|
97
|
+
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim
|
|
98
|
+
veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea
|
|
99
|
+
commodo consequat. Duis aute irure dolor in reprehenderit in voluptate
|
|
100
|
+
velit esse cillum dolore eu fugiat nulla pariatur.
|
|
101
|
+
</Tabs.Panel>
|
|
102
|
+
<Tabs.Panel renderTitle="Third Tab" isSelected={selectedIndex === 2} tabIndex={0}>
|
|
103
|
+
Sed ut perspiciatis unde omnis iste natus error sit voluptatem
|
|
104
|
+
accusantium doloremque laudantium. Totam rem aperiam, eaque ipsa quae ab
|
|
105
|
+
illo inventore veritatis et quasi architecto beatae vitae dicta sunt
|
|
106
|
+
explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut
|
|
107
|
+
odit aut fugit, sed quia consequuntur magni dolores.
|
|
108
|
+
</Tabs.Panel>
|
|
109
|
+
<Tabs.Panel renderTitle="Fourth Tab" isSelected={selectedIndex === 3} tabIndex={0}>
|
|
110
|
+
At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis
|
|
111
|
+
praesentium voluptatum deleniti atque corrupti. Quos dolores et quas
|
|
112
|
+
molestias excepturi sint occaecati cupiditate non provident, similique
|
|
113
|
+
sunt in culpa. Qui officia deserunt mollitia animi, id est laborum et
|
|
114
|
+
dolorum fuga.
|
|
115
|
+
</Tabs.Panel>
|
|
116
|
+
</Tabs>
|
|
117
|
+
)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
render(<Example />)
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### Handling Tab overflow
|
|
124
|
+
|
|
125
|
+
By default, `<Tabs />` will stack each individual `<Tab />` if there isn't enough horizontal
|
|
126
|
+
space to display them all inline. For a more compact tab navigation, set `tabOverflow` to
|
|
127
|
+
`scroll`, which allows the Tabs to scroll horizontally.
|
|
128
|
+
|
|
129
|
+
```js
|
|
130
|
+
---
|
|
131
|
+
type: example
|
|
132
|
+
---
|
|
133
|
+
const Example = () => {
|
|
134
|
+
const [selectedIndex, setSelectedIndex] = useState(4)
|
|
135
|
+
|
|
136
|
+
const handleTabChange = (event, { index }) => {
|
|
137
|
+
setSelectedIndex(index)
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return (
|
|
141
|
+
<Tabs
|
|
142
|
+
margin="large auto"
|
|
143
|
+
padding="medium"
|
|
144
|
+
onRequestTabChange={handleTabChange}
|
|
145
|
+
tabOverflow="scroll"
|
|
146
|
+
maxWidth="20rem"
|
|
147
|
+
>
|
|
148
|
+
<Tabs.Panel
|
|
149
|
+
id="tabA"
|
|
150
|
+
renderTitle="Tab A"
|
|
151
|
+
isSelected={selectedIndex === 0}
|
|
152
|
+
tabIndex={0}
|
|
153
|
+
>
|
|
154
|
+
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
|
155
|
+
</Tabs.Panel>
|
|
156
|
+
<Tabs.Panel
|
|
157
|
+
id="tabB"
|
|
158
|
+
renderTitle="Tab B"
|
|
159
|
+
isSelected={selectedIndex === 1}
|
|
160
|
+
tabIndex={0}
|
|
161
|
+
>
|
|
162
|
+
Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
|
|
163
|
+
</Tabs.Panel>
|
|
164
|
+
<Tabs.Panel
|
|
165
|
+
id="tabC"
|
|
166
|
+
renderTitle="Tab C"
|
|
167
|
+
isSelected={selectedIndex === 2}
|
|
168
|
+
tabIndex={0}
|
|
169
|
+
>
|
|
170
|
+
Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris.
|
|
171
|
+
</Tabs.Panel>
|
|
172
|
+
<Tabs.Panel
|
|
173
|
+
id="tabD"
|
|
174
|
+
renderTitle="Tab D"
|
|
175
|
+
isSelected={selectedIndex === 3}
|
|
176
|
+
tabIndex={0}
|
|
177
|
+
>
|
|
178
|
+
Duis aute irure dolor in reprehenderit in voluptate velit esse.
|
|
179
|
+
</Tabs.Panel>
|
|
180
|
+
<Tabs.Panel
|
|
181
|
+
id="tabE"
|
|
182
|
+
renderTitle="Tab E"
|
|
183
|
+
isSelected={selectedIndex === 4}
|
|
184
|
+
tabIndex={0}
|
|
185
|
+
>
|
|
186
|
+
Excepteur sint occaecat cupidatat non proident, sunt in culpa.
|
|
187
|
+
</Tabs.Panel>
|
|
188
|
+
<Tabs.Panel
|
|
189
|
+
id="tabF"
|
|
190
|
+
renderTitle="Tab F"
|
|
191
|
+
isSelected={selectedIndex === 5}
|
|
192
|
+
tabIndex={0}
|
|
193
|
+
>
|
|
194
|
+
Sed ut perspiciatis unde omnis iste natus error sit voluptatem.
|
|
195
|
+
</Tabs.Panel>
|
|
196
|
+
<Tabs.Panel
|
|
197
|
+
id="tabG"
|
|
198
|
+
renderTitle="Tab G"
|
|
199
|
+
isSelected={selectedIndex === 6}
|
|
200
|
+
tabIndex={0}
|
|
201
|
+
>
|
|
202
|
+
Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit.
|
|
203
|
+
</Tabs.Panel>
|
|
204
|
+
</Tabs>
|
|
205
|
+
)
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
render(<Example />)
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
### Controlling the size and the spacing
|
|
212
|
+
|
|
213
|
+
To restrict the width of `<Tabs />`, use the `maxWidth` prop. Add space around the entire component using the `margin` prop. Adjust the padding around the panel content via `padding` (default is `small`) on each `<Tabs.Panel>`.
|
|
214
|
+
|
|
215
|
+
Set the height of the Tabs component with the `fixHeight` property (set to '100%' to fill out it's parent element). You can also restrict the height of the **panels** using the `minHeight` and `maxHeight` properties (they don't work if you set `fixHeight` on the whole Tabs component).
|
|
216
|
+
|
|
217
|
+
Finally, switch the text alignment of the panel content with `textAlign`.
|
|
218
|
+
|
|
219
|
+
```js
|
|
220
|
+
---
|
|
221
|
+
type: example
|
|
222
|
+
---
|
|
223
|
+
const Example = () => {
|
|
224
|
+
const [selectedIndex, setSelectedIndex] = useState(0)
|
|
225
|
+
const [heightOption, setHeightOption] = useState('fixHeight: 100%')
|
|
226
|
+
|
|
227
|
+
const heightOptions = {
|
|
228
|
+
['fixHeight: 100%']: { fixHeight: '100%' },
|
|
229
|
+
['fixHeight: 15rem']: { fixHeight: '15rem' },
|
|
230
|
+
['minHeight: 17rem']: { minHeight: '17rem' },
|
|
231
|
+
['maxHeight: 10rem']: { maxHeight: '10rem' }
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
const handleTabChange = (event, { index }) => {
|
|
235
|
+
setSelectedIndex(index)
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
const handleHeightOptionSelect = (e, heightOption) => {
|
|
239
|
+
setHeightOption(heightOption)
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
const containerProps = {
|
|
243
|
+
as: 'div',
|
|
244
|
+
...(heightOption.includes('fixHeight') && {
|
|
245
|
+
height: '22rem',
|
|
246
|
+
withVisualDebug: true
|
|
247
|
+
})
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
return (
|
|
251
|
+
<>
|
|
252
|
+
<View display="block" margin="none none medium">
|
|
253
|
+
<RadioInputGroup
|
|
254
|
+
name="tabsHeightOptions"
|
|
255
|
+
defaultValue="fixHeight: 100%"
|
|
256
|
+
description={
|
|
257
|
+
<ScreenReaderContent>Tabs height selector</ScreenReaderContent>
|
|
258
|
+
}
|
|
259
|
+
variant="toggle"
|
|
260
|
+
onChange={handleHeightOptionSelect}
|
|
261
|
+
>
|
|
262
|
+
{Object.keys(heightOptions).map((heightOption) => (
|
|
263
|
+
<RadioInput
|
|
264
|
+
key={heightOption}
|
|
265
|
+
label={heightOption}
|
|
266
|
+
value={heightOption}
|
|
267
|
+
/>
|
|
268
|
+
))}
|
|
269
|
+
</RadioInputGroup>
|
|
270
|
+
</View>
|
|
271
|
+
|
|
272
|
+
<View {...containerProps}>
|
|
273
|
+
<Tabs
|
|
274
|
+
margin="large auto"
|
|
275
|
+
padding="medium"
|
|
276
|
+
onRequestTabChange={handleTabChange}
|
|
277
|
+
{...heightOptions[heightOption]}
|
|
278
|
+
>
|
|
279
|
+
<Tabs.Panel
|
|
280
|
+
id="tabA"
|
|
281
|
+
tabIndex={-1}
|
|
282
|
+
renderTitle="Tab A"
|
|
283
|
+
textAlign="center"
|
|
284
|
+
padding="large"
|
|
285
|
+
isSelected={selectedIndex === 0}
|
|
286
|
+
>
|
|
287
|
+
<Button>Focus Me</Button>
|
|
288
|
+
</Tabs.Panel>
|
|
289
|
+
<Tabs.Panel id="tabB" renderTitle="Disabled Tab" isDisabled>
|
|
290
|
+
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do
|
|
291
|
+
eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim
|
|
292
|
+
ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
|
|
293
|
+
aliquip ex ea commodo consequat. Duis aute irure dolor in
|
|
294
|
+
reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
|
|
295
|
+
pariatur.
|
|
296
|
+
</Tabs.Panel>
|
|
297
|
+
<Tabs.Panel
|
|
298
|
+
id="tabC"
|
|
299
|
+
renderTitle="Tab C"
|
|
300
|
+
isSelected={selectedIndex === 2}
|
|
301
|
+
tabIndex={0}
|
|
302
|
+
>
|
|
303
|
+
Sed ut perspiciatis unde omnis iste natus error sit voluptatem
|
|
304
|
+
accusantium doloremque laudantium. Totam rem aperiam, eaque ipsa quae
|
|
305
|
+
ab illo inventore veritatis et quasi architecto beatae vitae dicta
|
|
306
|
+
sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit
|
|
307
|
+
aspernatur aut odit aut fugit, sed quia consequuntur magni dolores.
|
|
308
|
+
</Tabs.Panel>
|
|
309
|
+
<Tabs.Panel
|
|
310
|
+
id="tabD"
|
|
311
|
+
renderTitle="Tab D"
|
|
312
|
+
isSelected={selectedIndex === 3}
|
|
313
|
+
tabIndex={0}
|
|
314
|
+
>
|
|
315
|
+
At vero eos et accusamus et iusto odio dignissimos ducimus qui
|
|
316
|
+
blanditiis praesentium voluptatum deleniti atque corrupti. Quos
|
|
317
|
+
dolores et quas molestias excepturi sint occaecati cupiditate non
|
|
318
|
+
provident, similique sunt in culpa. Qui officia deserunt mollitia
|
|
319
|
+
animi, id est laborum et dolorum fuga.
|
|
320
|
+
</Tabs.Panel>
|
|
321
|
+
</Tabs>
|
|
322
|
+
</View>
|
|
323
|
+
</>
|
|
324
|
+
)
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
render(<Example />)
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
### Support for dynamic content with active panel
|
|
331
|
+
|
|
332
|
+
Marking one of the `<Tabs.Panel>` as `active` will render that panel's content in all the panels. This is useful for dynamic content rendering: the panel area can be used as a container, what routing libraries, such as React Router, can use to render their children elements into.
|
|
333
|
+
|
|
334
|
+
```js
|
|
335
|
+
---
|
|
336
|
+
type: example
|
|
337
|
+
---
|
|
338
|
+
const Outlet = () => {
|
|
339
|
+
const [show, setShow] = useState(false)
|
|
340
|
+
|
|
341
|
+
useEffect(() => {
|
|
342
|
+
const timer = setTimeout(() => setShow(true), 2000)
|
|
343
|
+
return () => clearTimeout(timer)
|
|
344
|
+
}, [])
|
|
345
|
+
|
|
346
|
+
return (
|
|
347
|
+
<div>
|
|
348
|
+
<Heading level="h1" as="h1" margin="0 0 x-small">
|
|
349
|
+
{show ? 'Hello Developer' : 'Simulating network call...'}
|
|
350
|
+
</Heading>
|
|
351
|
+
{show ? (
|
|
352
|
+
<div>
|
|
353
|
+
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do
|
|
354
|
+
eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad
|
|
355
|
+
minim veniam, quis nostrud exercitation ullamco laboris nisi ut
|
|
356
|
+
aliquip ex ea commodo consequat. Duis aute irure dolor in
|
|
357
|
+
reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
|
|
358
|
+
pariatur.
|
|
359
|
+
</div>
|
|
360
|
+
) : (
|
|
361
|
+
<Spinner renderTitle="Loading" size="medium" />
|
|
362
|
+
)}
|
|
363
|
+
</div>
|
|
364
|
+
)
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
const Example = () => {
|
|
368
|
+
const [selectedIndex, setSelectedIndex] = useState(0)
|
|
369
|
+
|
|
370
|
+
const handleTabChange = (event, { index }) => {
|
|
371
|
+
setSelectedIndex(index)
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
return (
|
|
375
|
+
<Tabs
|
|
376
|
+
margin="large auto"
|
|
377
|
+
padding="medium"
|
|
378
|
+
onRequestTabChange={handleTabChange}
|
|
379
|
+
>
|
|
380
|
+
<Tabs.Panel
|
|
381
|
+
id="tabA"
|
|
382
|
+
renderTitle="Tab A"
|
|
383
|
+
textAlign="center"
|
|
384
|
+
padding="large"
|
|
385
|
+
isSelected={selectedIndex === 0}
|
|
386
|
+
active
|
|
387
|
+
tabIndex={0}
|
|
388
|
+
>
|
|
389
|
+
<Outlet />
|
|
390
|
+
</Tabs.Panel>
|
|
391
|
+
<Tabs.Panel id="tabB" renderTitle="Disabled Tab" isDisabled />
|
|
392
|
+
<Tabs.Panel
|
|
393
|
+
id="tabC"
|
|
394
|
+
renderTitle="Tab C"
|
|
395
|
+
isSelected={selectedIndex === 2}
|
|
396
|
+
/>
|
|
397
|
+
<Tabs.Panel
|
|
398
|
+
id="tabD"
|
|
399
|
+
renderTitle="Tab D"
|
|
400
|
+
isSelected={selectedIndex === 3}
|
|
401
|
+
/>
|
|
402
|
+
</Tabs>
|
|
403
|
+
)
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
render(<Example />)
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
### Persisting the selected tab
|
|
410
|
+
|
|
411
|
+
If you need to persist the rendered content of the tabpanels between tabbing, you can set the `unmountOnExit` prop to `false` on the `<Tabs.Panel>` component. It works case by case, so you can set it to `false` only on the tabpanels you want to persist.
|
|
412
|
+
|
|
413
|
+
```js
|
|
414
|
+
---
|
|
415
|
+
type: example
|
|
416
|
+
---
|
|
417
|
+
const Counter = () => {
|
|
418
|
+
const [counter, setCounter] = useState(0)
|
|
419
|
+
|
|
420
|
+
const handleIncrement = () => {
|
|
421
|
+
setCounter(counter + 1)
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
return (
|
|
425
|
+
<div>
|
|
426
|
+
<Button onClick={handleIncrement}>Increment</Button>
|
|
427
|
+
<hr />
|
|
428
|
+
<Text>{counter}</Text>
|
|
429
|
+
</div>
|
|
430
|
+
)
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
const Example = () => {
|
|
434
|
+
const [selectedIndex, setSelectedIndex] = useState(0)
|
|
435
|
+
|
|
436
|
+
const handleTabChange = (event, { index }) => {
|
|
437
|
+
setSelectedIndex(index)
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
return (
|
|
441
|
+
<Tabs
|
|
442
|
+
margin="large auto"
|
|
443
|
+
padding="medium"
|
|
444
|
+
onRequestTabChange={handleTabChange}
|
|
445
|
+
>
|
|
446
|
+
<Tabs.Panel
|
|
447
|
+
id="tabA"
|
|
448
|
+
tabIndex={-1}
|
|
449
|
+
renderTitle="I will persist"
|
|
450
|
+
textAlign="center"
|
|
451
|
+
padding="large"
|
|
452
|
+
isSelected={selectedIndex === 0}
|
|
453
|
+
unmountOnExit={false}
|
|
454
|
+
>
|
|
455
|
+
<Counter />
|
|
456
|
+
</Tabs.Panel>
|
|
457
|
+
<Tabs.Panel
|
|
458
|
+
id="tabB"
|
|
459
|
+
tabIndex={-1}
|
|
460
|
+
renderTitle="I will unmount"
|
|
461
|
+
isSelected={selectedIndex === 1}
|
|
462
|
+
textAlign="center"
|
|
463
|
+
padding="large"
|
|
464
|
+
>
|
|
465
|
+
<Counter />
|
|
466
|
+
</Tabs.Panel>
|
|
467
|
+
<Tabs.Panel
|
|
468
|
+
id="tabC"
|
|
469
|
+
renderTitle="Tab C"
|
|
470
|
+
isSelected={selectedIndex === 2}
|
|
471
|
+
tabIndex={0}
|
|
472
|
+
>
|
|
473
|
+
Tab C
|
|
474
|
+
</Tabs.Panel>
|
|
475
|
+
<Tabs.Panel
|
|
476
|
+
id="tabD"
|
|
477
|
+
renderTitle="Tab D"
|
|
478
|
+
isSelected={selectedIndex === 3}
|
|
479
|
+
tabIndex={0}
|
|
480
|
+
>
|
|
481
|
+
Tab D
|
|
482
|
+
</Tabs.Panel>
|
|
483
|
+
</Tabs>
|
|
484
|
+
)
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
render(<Example />)
|
|
488
|
+
```
|
|
489
|
+
|
|
490
|
+
### Managing focus with tabIndex
|
|
491
|
+
|
|
492
|
+
**Best practice:** For text-only panels, set `tabIndex={0}` to include the panel in the keyboard tab sequence—this ensures screen reader users can navigate to and read the content. For panels containing interactive elements (buttons, inputs, links), leave `tabIndex` unset so keyboard users tab directly to the controls without stopping on the panel container first.
|
|
493
|
+
|
|
494
|
+
```js
|
|
495
|
+
---
|
|
496
|
+
type: example
|
|
497
|
+
---
|
|
498
|
+
const Example = () => {
|
|
499
|
+
const [selectedIndex, setSelectedIndex] = useState(0)
|
|
500
|
+
|
|
501
|
+
const handleTabChange = (event, { index }) => {
|
|
502
|
+
setSelectedIndex(index)
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
return (
|
|
506
|
+
<Tabs
|
|
507
|
+
margin="large auto"
|
|
508
|
+
padding="medium"
|
|
509
|
+
onRequestTabChange={handleTabChange}
|
|
510
|
+
>
|
|
511
|
+
<Tabs.Panel
|
|
512
|
+
id="tabA"
|
|
513
|
+
renderTitle="Panel with button"
|
|
514
|
+
textAlign="center"
|
|
515
|
+
padding="large"
|
|
516
|
+
isSelected={selectedIndex === 0}
|
|
517
|
+
tabIndex={-1}
|
|
518
|
+
>
|
|
519
|
+
<Button>Focus Me First</Button>
|
|
520
|
+
</Tabs.Panel>
|
|
521
|
+
<Tabs.Panel
|
|
522
|
+
id="tabB"
|
|
523
|
+
renderTitle="Panel with text only"
|
|
524
|
+
isSelected={selectedIndex === 1}
|
|
525
|
+
tabIndex={0}
|
|
526
|
+
>
|
|
527
|
+
This panel only contains text, so tabIndex is set to 0 to include it in the tab sequence.
|
|
528
|
+
</Tabs.Panel>
|
|
529
|
+
</Tabs>
|
|
530
|
+
)
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
render(<Example />)
|
|
534
|
+
```
|
|
535
|
+
|
|
536
|
+
### Guidelines
|
|
537
|
+
|
|
538
|
+
```js
|
|
539
|
+
---
|
|
540
|
+
type: embed
|
|
541
|
+
---
|
|
542
|
+
<Guidelines>
|
|
543
|
+
<Figure recommendation="yes" title="Do">
|
|
544
|
+
<Figure.Item>Title should be a single row above content</Figure.Item>
|
|
545
|
+
<Figure.Item>TabButton content should be succinct, preferably one word</Figure.Item>
|
|
546
|
+
<Figure.Item>Use title case</Figure.Item>
|
|
547
|
+
<Figure.Item>Use default variant when a single Tabs component exists on the page</Figure.Item>
|
|
548
|
+
<Figure.Item>Ensure each Tablist.Panel content is mutually exclusive of the others’</Figure.Item>
|
|
549
|
+
</Figure>
|
|
550
|
+
<Figure recommendation="no" title="Don't">
|
|
551
|
+
<Figure.Item>Exceed 20 characters including spaces for the title</Figure.Item>
|
|
552
|
+
<Figure.Item>Exceed 5 tabs</Figure.Item>
|
|
553
|
+
<Figure.Item>Use avatars, pills, icons, etc in the TabButton</Figure.Item>
|
|
554
|
+
<Figure.Item>Use `tabOverflow="scroll"` with `secondary` Tabs</Figure.Item>
|
|
555
|
+
<Figure.Item>Nest tabbed content within a tab</Figure.Item>
|
|
556
|
+
<Figure.Item>Align tabs to the center of the page</Figure.Item>
|
|
557
|
+
</Figure>
|
|
558
|
+
</Guidelines>
|
|
559
|
+
```
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* The MIT License (MIT)
|
|
3
|
+
*
|
|
4
|
+
* Copyright (c) 2015 - present Instructure, Inc.
|
|
5
|
+
*
|
|
6
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
|
+
* of this software and associated documentation files (the "Software"), to deal
|
|
8
|
+
* in the Software without restriction, including without limitation the rights
|
|
9
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
10
|
+
* copies of the Software, and to permit persons to whom the Software is
|
|
11
|
+
* furnished to do so, subject to the following conditions:
|
|
12
|
+
*
|
|
13
|
+
* The above copyright notice and this permission notice shall be included in all
|
|
14
|
+
* copies or substantial portions of the Software.
|
|
15
|
+
*
|
|
16
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
17
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
18
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
19
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
20
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
21
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
22
|
+
* SOFTWARE.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
import { Component } from 'react'
|
|
26
|
+
|
|
27
|
+
import { passthroughProps, callRenderProp } from '@instructure/ui-react-utils'
|
|
28
|
+
import { View } from '@instructure/ui-view/latest'
|
|
29
|
+
import type { ViewOwnProps } from '@instructure/ui-view/latest'
|
|
30
|
+
|
|
31
|
+
import { withStyle } from '@instructure/emotion'
|
|
32
|
+
|
|
33
|
+
import generateStyle from './styles'
|
|
34
|
+
|
|
35
|
+
import type { TabsTabProps } from './props'
|
|
36
|
+
import { allowedProps } from './props'
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
---
|
|
40
|
+
parent: Tabs
|
|
41
|
+
id: Tabs.Tab
|
|
42
|
+
---
|
|
43
|
+
**/
|
|
44
|
+
@withStyle(generateStyle)
|
|
45
|
+
class Tab extends Component<TabsTabProps> {
|
|
46
|
+
static readonly componentId = 'Tabs.Tab'
|
|
47
|
+
|
|
48
|
+
static allowedProps = allowedProps
|
|
49
|
+
|
|
50
|
+
static defaultProps = {
|
|
51
|
+
variant: 'default',
|
|
52
|
+
isDisabled: false,
|
|
53
|
+
isSelected: false
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
componentDidMount() {
|
|
57
|
+
this.props.makeStyles?.()
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
componentDidUpdate() {
|
|
61
|
+
this.props.makeStyles?.()
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
handleClick = (event: React.MouseEvent<ViewOwnProps>) => {
|
|
65
|
+
const { onClick, index, id, isDisabled } = this.props
|
|
66
|
+
|
|
67
|
+
if (isDisabled) {
|
|
68
|
+
return
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (typeof onClick === 'function') {
|
|
72
|
+
onClick(event, { index, id })
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
handleKeyDown = (event: React.KeyboardEvent<ViewOwnProps>) => {
|
|
77
|
+
const { onKeyDown, index, id, isDisabled } = this.props
|
|
78
|
+
|
|
79
|
+
if (isDisabled) {
|
|
80
|
+
return
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (typeof onKeyDown === 'function') {
|
|
84
|
+
onKeyDown(event, { index, id })
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
render() {
|
|
89
|
+
const {
|
|
90
|
+
id,
|
|
91
|
+
variant,
|
|
92
|
+
isSelected,
|
|
93
|
+
isDisabled,
|
|
94
|
+
controls,
|
|
95
|
+
children,
|
|
96
|
+
styles,
|
|
97
|
+
...props
|
|
98
|
+
} = this.props
|
|
99
|
+
|
|
100
|
+
return (
|
|
101
|
+
<View
|
|
102
|
+
{...passthroughProps(props)}
|
|
103
|
+
as="div"
|
|
104
|
+
role="tab"
|
|
105
|
+
id={id}
|
|
106
|
+
onClick={this.handleClick}
|
|
107
|
+
onKeyDown={this.handleKeyDown}
|
|
108
|
+
css={styles?.tab}
|
|
109
|
+
aria-selected={isSelected ? 'true' : undefined}
|
|
110
|
+
aria-disabled={isDisabled ? 'true' : undefined}
|
|
111
|
+
aria-controls={controls}
|
|
112
|
+
tabIndex={isSelected && !isDisabled ? 0 : undefined}
|
|
113
|
+
position="relative"
|
|
114
|
+
focusPosition="inset"
|
|
115
|
+
>
|
|
116
|
+
{callRenderProp(children)}
|
|
117
|
+
</View>
|
|
118
|
+
)
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export default Tab
|
|
123
|
+
export { Tab }
|