Skip to content

Commit bb29dbd

Browse files
DaniGuardiolaDaniGuardiolatyxlaciampojasmussen
authored andcommitted
Tabs: tweak sizing and overflow behavior of TabList (#64371)
* Tweak sizing and overflow behavior of TabList. * Add size and overflow playground story. * Add "scroll into view" behavior to selected tabs. * fit-content only on horizontal orientation * Reduce specificity of `fit-content` to make it easier to override. * centered label only when orientation is horizontal * Remove unused file. * Fix inspector controls tabs. * Fix font library modal tabs. * Fix style-book tabs. * fix typo * Add changelog entry. * fix emotion being weird ugh * Prevent unwanted focusable container in Firefox. * Add fade effect. * Fix IntersectionObserver logic. * Feature detect IntersectionObserver to prevent tests from failing. * Add a bit of tolerance for scroll state detection. * Fix vertical indicator. * Better handling of vertical overflow. * Add a bit of scroll margin for better "scroll into view" experience. * Horizontal fade should only happen on horizontal direction. * Adjust for offset parent scroll state in `getElementOffsetRect`. * Better "scroll into view" positioning heuristics ("nearest"). * Invert use of before and after to remove z-index and fix related issues. * Make vertical indicator light blue as discussed. * Undo most overrides in pattern/media vertical tabs. * Clean up outdated styles previously needed for label wrapping. * Revert vertical indicator changes and some indicator patterns/media tabs styles * Revert vertical indicator bug fix * Add changelog entry * Remove outdated style. * Address feedback * Fix scroll bug * Improve automatic tab scrolling behavior. * Tweaks to prevent unit test failure and minor cleanup. * Undo unnecessary changes. * Improved story * Fix scroll jumping bug. * Scroll to active tab instead of selected (support `selectOnMove=false`). * Fix minor visual glitch with overflow fade out indicators. * Misc tweaks * Fix. * Fix changelog * Fix changelog but it's actually true * Fix changelog * Make Story Book tabs nicer. * Temp fix for scrollbar issue in Style Book tabs. * Fix scroll bug and clean up a little. * Simplify and clean up a bit more. * Fix merge issues. * Fix merge issues again. * Make inserter patterns/media changes more minimal * Fix outdated comment * Fix another typo in comment. * Minor cleanup. * Fix bad automatic merge. * ugh, fix again Co-authored-by: DaniGuardiola <[email protected]> Co-authored-by: tyxla <[email protected]> Co-authored-by: ciampo <[email protected]> Co-authored-by: jasmussen <[email protected]> Co-authored-by: jameskoster <[email protected]> Co-authored-by: afercia <[email protected]>
1 parent 8635e98 commit bb29dbd

File tree

15 files changed

+346
-185
lines changed

15 files changed

+346
-185
lines changed

packages/block-editor/src/components/inserter/style.scss

Lines changed: 0 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -257,39 +257,11 @@ $block-inserter-tabs-height: 44px;
257257
svg {
258258
fill: var(--wp-admin-theme-color);
259259
}
260-
261-
&::after {
262-
content: "";
263-
display: block;
264-
outline: none;
265-
position: absolute;
266-
top: 0;
267-
bottom: 0;
268-
left: 0;
269-
right: 0;
270-
border-radius: $radius-small;
271-
opacity: 0.04;
272-
background: var(--wp-admin-theme-color);
273-
height: 100%;
274-
}
275-
}
276-
277-
&:focus-visible,
278-
&:focus:not(:disabled) {
279-
border-radius: $radius-small;
280-
box-shadow: 0 0 0 var(--wp-admin-border-width-focus) var(--wp-admin-theme-color);
281-
// Windows high contrast mode.
282-
outline: 2px solid transparent;
283-
outline-offset: 0;
284260
}
285261

286262
&::before {
287263
display: none;
288264
}
289-
290-
&::after {
291-
display: none;
292-
}
293265
}
294266
}
295267

packages/block-editor/src/components/inserter/tabs.js

Lines changed: 0 additions & 78 deletions
This file was deleted.
Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,3 @@
1-
.show-icon-labels {
2-
.block-editor-block-inspector__tabs [role="tablist"] {
3-
.components-button {
4-
justify-content: center;
5-
}
6-
}
1+
.block-editor-block-inspector__tabs [role="tablist"] {
2+
width: 100%;
73
}

packages/components/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
### Enhancements
1616

17+
- `Tabs`: handle horizontal overflow and large tab lists gracefully ([#64371](https://github.com/WordPress/gutenberg/pull/64371)).
1718
- `BorderBoxControl`: promote to stable ([#65586](https://github.com/WordPress/gutenberg/pull/65586)).
1819
- `MenuGroup`: Simplify the MenuGroup styles within dropdown menus. ([#65561](https://github.com/WordPress/gutenberg/pull/65561)).
1920
- `DatePicker`: Use compact button size. ([#65653](https://github.com/WordPress/gutenberg/pull/65653)).

packages/components/src/tabs/stories/index.story.tsx

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,112 @@ const Template: StoryFn< typeof Tabs > = ( props ) => {
7070

7171
export const Default = Template.bind( {} );
7272

73+
export const SizeAndOverflowPlayground: StoryFn< typeof Tabs > = ( props ) => {
74+
const [ fullWidth, setFullWidth ] = useState( false );
75+
return (
76+
<div>
77+
<div style={ { maxWidth: '40rem', marginBottom: '1rem' } }>
78+
<p>
79+
This story helps understand how the TabList component
80+
behaves under different conditions. The container below
81+
(with the dotted red border) can be horizontally resized,
82+
and it has a bit of padding to be out of the way of the
83+
TabList.
84+
</p>
85+
<p>
86+
The button will toggle between full width (adding{ ' ' }
87+
<code>width: 100%</code>) and the default width.
88+
</p>
89+
<p>Try the following:</p>
90+
<ul>
91+
<li>
92+
<strong>Small container</strong> that causes tabs to
93+
overflow with scroll.
94+
</li>
95+
<li>
96+
<strong>Large container</strong> that exceeds the normal
97+
width of the tabs.
98+
<ul>
99+
<li>
100+
<strong>
101+
With <code>width: 100%</code>
102+
</strong>{ ' ' }
103+
set on the TabList (tabs fill up the space).
104+
</li>
105+
<li>
106+
<strong>
107+
Without <code>width: 100%</code>
108+
</strong>{ ' ' }
109+
(defaults to <code>auto</code>) set on the
110+
TabList (tabs take up space proportional to
111+
their content).
112+
</li>
113+
</ul>
114+
</li>
115+
</ul>
116+
</div>
117+
<Button
118+
style={ { marginBottom: '1rem' } }
119+
variant="primary"
120+
onClick={ () => setFullWidth( ! fullWidth ) }
121+
>
122+
{ fullWidth
123+
? 'Remove width: 100% from TabList'
124+
: 'Set width: 100% in TabList' }
125+
</Button>
126+
<Tabs { ...props }>
127+
<div
128+
style={ {
129+
width: '20rem',
130+
border: '2px dotted red',
131+
padding: '1rem',
132+
resize: 'horizontal',
133+
overflow: 'auto',
134+
} }
135+
>
136+
<Tabs.TabList
137+
style={ {
138+
maxWidth: '100%',
139+
width: fullWidth ? '100%' : undefined,
140+
} }
141+
>
142+
<Tabs.Tab tabId="tab1">
143+
Label with multiple words
144+
</Tabs.Tab>
145+
<Tabs.Tab tabId="tab2">Short</Tabs.Tab>
146+
<Tabs.Tab tabId="tab3">
147+
Hippopotomonstrosesquippedaliophobia
148+
</Tabs.Tab>
149+
<Tabs.Tab tabId="tab4">Tab 4</Tabs.Tab>
150+
<Tabs.Tab tabId="tab5">Tab 5</Tabs.Tab>
151+
</Tabs.TabList>
152+
</div>
153+
<Tabs.TabPanel tabId="tab1">
154+
<p>Selected tab: Tab 1</p>
155+
<p>(Label with multiple words)</p>
156+
</Tabs.TabPanel>
157+
<Tabs.TabPanel tabId="tab2">
158+
<p>Selected tab: Tab 2</p>
159+
<p>(Short)</p>
160+
</Tabs.TabPanel>
161+
<Tabs.TabPanel tabId="tab3">
162+
<p>Selected tab: Tab 3</p>
163+
<p>(Hippopotomonstrosesquippedaliophobia)</p>
164+
</Tabs.TabPanel>
165+
<Tabs.TabPanel tabId="tab4">
166+
<p>Selected tab: Tab 4</p>
167+
</Tabs.TabPanel>
168+
<Tabs.TabPanel tabId="tab5">
169+
<p>Selected tab: Tab 5</p>
170+
</Tabs.TabPanel>
171+
</Tabs>
172+
</div>
173+
);
174+
};
175+
SizeAndOverflowPlayground.args = {
176+
defaultTabId: 'tab4',
177+
};
178+
73179
const VerticalTemplate: StoryFn< typeof Tabs > = ( props ) => {
74180
return (
75181
<Tabs orientation="vertical" { ...props }>

packages/components/src/tabs/styles.ts

Lines changed: 54 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -16,32 +16,40 @@ export const TabListWrapper = styled.div`
1616
align-items: stretch;
1717
flex-direction: row;
1818
text-align: center;
19+
overflow-x: auto;
1920
2021
&[aria-orientation='vertical'] {
2122
flex-direction: column;
2223
text-align: start;
2324
}
2425
25-
@media not ( prefers-reduced-motion ) {
26-
&.is-animation-enabled::after {
27-
transition-property: transform;
28-
transition-duration: 0.2s;
29-
transition-timing-function: ease-out;
30-
}
26+
:where( [aria-orientation='horizontal'] ) {
27+
width: fit-content;
3128
}
29+
3230
--direction-factor: 1;
33-
--direction-origin-x: left;
31+
--direction-start: left;
32+
--direction-end: right;
3433
--indicator-start: var( --indicator-left );
3534
&:dir( rtl ) {
3635
--direction-factor: -1;
37-
--direction-origin-x: right;
36+
--direction-start: right;
37+
--direction-end: left;
3838
--indicator-start: var( --indicator-right );
3939
}
40-
&::after {
40+
41+
@media not ( prefers-reduced-motion ) {
42+
&.is-animation-enabled::before {
43+
transition-property: transform;
44+
transition-duration: 0.2s;
45+
transition-timing-function: ease-out;
46+
}
47+
}
48+
&::before {
4149
content: '';
4250
position: absolute;
4351
pointer-events: none;
44-
transform-origin: var( --direction-origin-x ) top;
52+
transform-origin: var( --direction-start ) top;
4553
4654
// Windows high contrast mode.
4755
outline: 2px solid transparent;
@@ -52,7 +60,31 @@ export const TabListWrapper = styled.div`
5260
when scaling in the transform, see: https://stackoverflow.com/a/52159123 */
5361
--antialiasing-factor: 100;
5462
&:not( [aria-orientation='vertical'] ) {
55-
&::after {
63+
--fade-width: 4rem;
64+
--fade-gradient-base: transparent 0%, black var( --fade-width );
65+
--fade-gradient-composed: var( --fade-gradient-base ), black 60%,
66+
transparent 50%;
67+
&.is-overflowing-first {
68+
mask-image: linear-gradient(
69+
to var( --direction-end ),
70+
var( --fade-gradient-base )
71+
);
72+
}
73+
&.is-overflowing-last {
74+
mask-image: linear-gradient(
75+
to var( --direction-start ),
76+
var( --fade-gradient-base )
77+
);
78+
}
79+
&.is-overflowing-first.is-overflowing-last {
80+
mask-image: linear-gradient(
81+
to right,
82+
var( --fade-gradient-composed )
83+
),
84+
linear-gradient( to left, var( --fade-gradient-composed ) );
85+
}
86+
87+
&::before {
5688
bottom: 0;
5789
height: 0;
5890
width: calc( var( --antialiasing-factor ) * 1px );
@@ -71,8 +103,7 @@ export const TabListWrapper = styled.div`
71103
${ COLORS.theme.accent };
72104
}
73105
}
74-
&[aria-orientation='vertical']::after {
75-
z-index: -1;
106+
&[aria-orientation='vertical']::before {
76107
top: 0;
77108
left: 0;
78109
width: 100%;
@@ -87,14 +118,14 @@ export const TabListWrapper = styled.div`
87118

88119
export const Tab = styled( Ariakit.Tab )`
89120
& {
121+
scroll-margin: 24px;
122+
flex-grow: 1;
123+
flex-shrink: 0;
90124
display: inline-flex;
91125
align-items: center;
92126
position: relative;
93127
border-radius: 0;
94-
min-height: ${ space(
95-
12
96-
) }; // Avoid fixed height to allow for long strings that go in multiple lines.
97-
height: auto;
128+
height: ${ space( 12 ) };
98129
background: transparent;
99130
border: none;
100131
box-shadow: none;
@@ -104,7 +135,6 @@ export const Tab = styled( Ariakit.Tab )`
104135
margin-left: 0;
105136
font-weight: 500;
106137
text-align: inherit;
107-
hyphens: auto;
108138
color: ${ COLORS.theme.foreground };
109139
110140
&[aria-disabled='true'] {
@@ -123,7 +153,7 @@ export const Tab = styled( Ariakit.Tab )`
123153
}
124154
125155
// Focus.
126-
&::before {
156+
&::after {
127157
content: '';
128158
position: absolute;
129159
top: ${ space( 3 ) };
@@ -146,7 +176,7 @@ export const Tab = styled( Ariakit.Tab )`
146176
}
147177
}
148178
149-
&:focus-visible::before {
179+
&:focus-visible::after {
150180
opacity: 1;
151181
}
152182
}
@@ -156,6 +186,10 @@ export const Tab = styled( Ariakit.Tab )`
156186
10
157187
) }; // Avoid fixed height to allow for long strings that go in multiple lines.
158188
}
189+
190+
[aria-orientation='horizontal'] & {
191+
justify-content: center;
192+
}
159193
`;
160194

161195
export const TabPanel = styled( Ariakit.TabPanel )`

0 commit comments

Comments
 (0)