Skip to content

Commit 85a512a

Browse files
fix: sidebar text visibility in icon mode when expanded
The sidebar was not showing text labels and menu text properly when expanded in icon mode. This was caused by unconditional `group-data-[collapsible=icon]` classes that didn't check the `data-state` attribute. Changes: 1. Replaced complex Tailwind group-data classes in sidebarMenuButtonVariants with simpler semantic class names (`sidebar-menu-button-icon-mode`, `sidebar-menu-button-icon-mode-lg`) 2. Added comprehensive CSS rules that check BOTH data-collapsible AND data-state attributes 3. When expanded (data-state="expanded"): - Labels have opacity: 1 (fully visible) - Menu buttons use full width (100%) instead of being constrained to 2rem - All text content is properly displayed 4. When collapsed (data-state="collapsed"): - Labels have opacity: 0 (hidden) - Menu buttons constrained to 2rem x 2rem (icon only) - Text content hidden This fix ensures that all text (group labels, menu button labels, sub-menu items) is visible when the sidebar is expanded, resolving the issue where text was not showing after expansion. Agent-Logs-Url: https://github.com/objectstack-ai/objectui/sessions/90957339-73b0-4709-b11e-3b9cc8f855d7 Co-authored-by: xuyushun441-sys <255036401+xuyushun441-sys@users.noreply.github.com>
1 parent f94eb3f commit 85a512a

File tree

3 files changed

+172
-38
lines changed

3 files changed

+172
-38
lines changed
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
import { test, expect } from '@playwright/test';
2+
import { waitForReactMount, CONSOLE_BASE } from './helpers';
3+
4+
/**
5+
* Sidebar text visibility tests
6+
*
7+
* These tests validate that the sidebar displays text correctly
8+
* when toggled between collapsed (icon mode) and expanded states.
9+
*/
10+
11+
test.describe('Sidebar Text Visibility', () => {
12+
test('should show all text labels when sidebar is expanded in icon mode', async ({ page }) => {
13+
await page.goto(`${CONSOLE_BASE}/`);
14+
await waitForReactMount(page);
15+
16+
// Wait for sidebar to be visible
17+
const sidebar = page.locator('[data-sidebar="sidebar"]').first();
18+
await expect(sidebar).toBeVisible();
19+
20+
// Find the sidebar toggle button
21+
const toggleButton = page.locator('[data-sidebar="trigger"]').first();
22+
await expect(toggleButton).toBeVisible();
23+
24+
// Get the parent sidebar element that has data-state attribute
25+
const sidebarGroup = page.locator('.group[data-collapsible="icon"]').first();
26+
27+
// First, collapse the sidebar if it's expanded
28+
let currentState = await sidebarGroup.getAttribute('data-state');
29+
if (currentState === 'expanded') {
30+
await toggleButton.click();
31+
await page.waitForTimeout(300); // Wait for animation
32+
currentState = await sidebarGroup.getAttribute('data-state');
33+
expect(currentState).toBe('collapsed');
34+
}
35+
36+
// Now expand the sidebar
37+
await toggleButton.click();
38+
await page.waitForTimeout(300); // Wait for animation
39+
40+
// Verify sidebar is expanded
41+
currentState = await sidebarGroup.getAttribute('data-state');
42+
expect(currentState).toBe('expanded');
43+
44+
// Check that group labels are visible
45+
const groupLabels = page.locator('[data-sidebar="group-label"]');
46+
const labelCount = await groupLabels.count();
47+
48+
if (labelCount > 0) {
49+
for (let i = 0; i < labelCount; i++) {
50+
const label = groupLabels.nth(i);
51+
52+
// Check opacity
53+
const opacity = await label.evaluate((el) => {
54+
return window.getComputedStyle(el).opacity;
55+
});
56+
expect(parseFloat(opacity), `Group label ${i} should be fully visible (opacity: 1)`).toBe(1);
57+
58+
// Check display
59+
const display = await label.evaluate((el) => {
60+
return window.getComputedStyle(el).display;
61+
});
62+
expect(display, `Group label ${i} should not be hidden`).not.toBe('none');
63+
}
64+
}
65+
66+
// Check that menu button text is visible
67+
const menuButtons = page.locator('[data-sidebar="menu-button"]');
68+
const buttonCount = await menuButtons.count();
69+
70+
if (buttonCount > 0) {
71+
for (let i = 0; i < buttonCount; i++) {
72+
const button = menuButtons.nth(i);
73+
74+
// Check that button has proper width (not constrained to icon size)
75+
const width = await button.evaluate((el) => {
76+
return window.getComputedStyle(el).width;
77+
});
78+
79+
// In expanded mode, button should not be constrained to 2rem (32px)
80+
const widthPx = parseFloat(width);
81+
expect(widthPx, `Menu button ${i} should be wider than icon size (${widthPx}px)`).toBeGreaterThan(32);
82+
}
83+
}
84+
85+
// Take a screenshot for visual verification
86+
await page.screenshot({
87+
path: '/tmp/sidebar-expanded.png',
88+
fullPage: false
89+
});
90+
91+
console.log(`Found ${labelCount} group labels and ${buttonCount} menu buttons`);
92+
});
93+
94+
test('should hide text labels when sidebar is collapsed in icon mode', async ({ page }) => {
95+
await page.goto(`${CONSOLE_BASE}/`);
96+
await waitForReactMount(page);
97+
98+
const sidebar = page.locator('[data-sidebar="sidebar"]').first();
99+
await expect(sidebar).toBeVisible();
100+
101+
const toggleButton = page.locator('[data-sidebar="trigger"]').first();
102+
await expect(toggleButton).toBeVisible();
103+
104+
const sidebarGroup = page.locator('.group[data-collapsible="icon"]').first();
105+
106+
// Expand first if needed
107+
let currentState = await sidebarGroup.getAttribute('data-state');
108+
if (currentState === 'collapsed') {
109+
await toggleButton.click();
110+
await page.waitForTimeout(300);
111+
}
112+
113+
// Now collapse
114+
await toggleButton.click();
115+
await page.waitForTimeout(300);
116+
117+
currentState = await sidebarGroup.getAttribute('data-state');
118+
expect(currentState).toBe('collapsed');
119+
120+
// Check that group labels are hidden
121+
const groupLabels = page.locator('[data-sidebar="group-label"]');
122+
const labelCount = await groupLabels.count();
123+
124+
if (labelCount > 0) {
125+
for (let i = 0; i < labelCount; i++) {
126+
const label = groupLabels.nth(i);
127+
128+
const opacity = await label.evaluate((el) => {
129+
return window.getComputedStyle(el).opacity;
130+
});
131+
expect(parseFloat(opacity), `Group label ${i} should be hidden (opacity: 0) when collapsed`).toBe(0);
132+
}
133+
}
134+
135+
// Take a screenshot
136+
await page.screenshot({
137+
path: '/tmp/sidebar-collapsed.png',
138+
fullPage: false
139+
});
140+
});
141+
});

packages/components/src/index.css

Lines changed: 29 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,8 @@ body {
158158
color: hsl(var(--foreground));
159159
}
160160

161-
/* Sidebar group-data variants - explicit rules for Tailwind 4 compatibility */
161+
/* Sidebar explicit rules for Tailwind 4 compatibility */
162+
162163
/* Icon mode: collapsed state = icon width, expanded state = full width */
163164
.group[data-collapsible="icon"][data-state="collapsed"] .group-data-\[collapsible\=icon\]\:w-\[--sidebar-width-icon\] {
164165
width: var(--sidebar-width-icon);
@@ -184,66 +185,58 @@ body {
184185
width: var(--sidebar-width);
185186
}
186187

187-
/* Labels and actions visibility - only hide when BOTH icon mode AND collapsed */
188+
/* Labels visibility - only hide when BOTH icon mode AND collapsed */
188189
.group[data-collapsible="icon"][data-state="collapsed"] .group-data-\[collapsible\=icon\]\:opacity-0 {
189190
opacity: 0;
190191
}
191192

193+
.group[data-collapsible="icon"][data-state="expanded"] .group-data-\[collapsible\=icon\]\:opacity-0 {
194+
opacity: 1;
195+
}
196+
192197
.group[data-collapsible="icon"][data-state="collapsed"] .group-data-\[collapsible\=icon\]\:-mt-8 {
193198
margin-top: -2rem;
194199
}
195200

201+
.group[data-collapsible="icon"][data-state="expanded"] .group-data-\[collapsible\=icon\]\:-mt-8 {
202+
margin-top: 0;
203+
}
204+
196205
.group[data-collapsible="icon"][data-state="collapsed"] .group-data-\[collapsible\=icon\]\:hidden {
197206
display: none;
198207
}
199208

209+
.group[data-collapsible="icon"][data-state="expanded"] .group-data-\[collapsible\=icon\]\:hidden {
210+
display: block;
211+
}
212+
200213
.group[data-collapsible="icon"][data-state="collapsed"] .group-data-\[collapsible\=icon\]\:overflow-hidden {
201214
overflow: hidden;
202215
}
203216

204-
/* Menu button size - only constrain size when collapsed */
205-
.group[data-collapsible="icon"][data-state="collapsed"] .group-data-\[collapsible\=icon\]\:\!size-8 {
206-
width: 2rem !important;
207-
height: 2rem !important;
217+
.group[data-collapsible="icon"][data-state="expanded"] .group-data-\[collapsible\=icon\]\:overflow-hidden {
218+
overflow: visible;
208219
}
209220

210-
.group[data-collapsible="icon"][data-state="collapsed"] .group-data-\[collapsible\=icon\]\:\!p-2 {
221+
/* Menu button behavior - collapsed = icon only, expanded = full width */
222+
.group[data-collapsible="icon"][data-state="collapsed"] .sidebar-menu-button-icon-mode {
223+
width: 2rem !important;
224+
height: 2rem !important;
211225
padding: 0.5rem !important;
212226
}
213227

214-
.group[data-collapsible="icon"][data-state="collapsed"] .group-data-\[collapsible\=icon\]\:\!p-0 {
215-
padding: 0 !important;
228+
.group[data-collapsible="icon"][data-state="expanded"] .sidebar-menu-button-icon-mode {
229+
width: 100%;
230+
height: auto;
231+
padding: 0.5rem;
216232
}
217233

218-
/* When expanded, ensure labels are visible and menu buttons are normal size */
219-
.group[data-collapsible="icon"][data-state="expanded"] .group-data-\[collapsible\=icon\]\:opacity-0 {
220-
opacity: 1 !important;
221-
}
222-
223-
.group[data-collapsible="icon"][data-state="expanded"] .group-data-\[collapsible\=icon\]\:-mt-8 {
224-
margin-top: 0 !important;
225-
}
226-
227-
.group[data-collapsible="icon"][data-state="expanded"] .group-data-\[collapsible\=icon\]\:hidden {
228-
display: block !important;
229-
}
230-
231-
.group[data-collapsible="icon"][data-state="expanded"] .group-data-\[collapsible\=icon\]\:overflow-hidden {
232-
overflow: visible !important;
233-
}
234-
235-
/* CRITICAL: Reset size constraint in expanded state - use width: 100% to fill container */
236-
.group[data-collapsible="icon"][data-state="expanded"] .group-data-\[collapsible\=icon\]\:\!size-8 {
237-
width: 100% !important;
238-
height: auto !important;
239-
}
240-
241-
.group[data-collapsible="icon"][data-state="expanded"] .group-data-\[collapsible\=icon\]\:\!p-2 {
242-
padding: 0.5rem !important;
234+
.group[data-collapsible="icon"][data-state="collapsed"] .sidebar-menu-button-icon-mode-lg {
235+
padding: 0 !important;
243236
}
244237

245-
.group[data-collapsible="icon"][data-state="expanded"] .group-data-\[collapsible\=icon\]\:\!p-0 {
246-
padding: 0 !important;
238+
.group[data-collapsible="icon"][data-state="expanded"] .sidebar-menu-button-icon-mode-lg {
239+
padding: 0.5rem;
247240
}
248241

249242
/* Offcanvas mode: always collapsed = 0 width */

packages/components/src/ui/sidebar.tsx

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)