Skip to content

Commit b7f8002

Browse files
rhamiltoclaude
andauthored
fix(ResponsiveActions): prevent empty dropdown when all actions are persistent or pinned (#889)
Fixes #888 When all ResponsiveAction components had isPersistent or isPinned props, an empty kebab dropdown was still being rendered. Root Cause: The hasAdditionalOptions prop on OverflowMenuControl was forcing the dropdown control to always show, even when there were no regular overflow actions (only persistent/pinned actions that don't need a persistent dropdown). Changes: - Conditionally set hasAdditionalOptions based on presence of regular actions - Track hasRegularActions boolean to distinguish action types - Only set hasAdditionalOptions={true} when regular actions are present - Added early return guard to prevent rendering empty OverflowMenu - Added tests for persistent-only, pinned-only, and mixed action scenarios Behavior: - Regular actions: hasAdditionalOptions={true} → dropdown always renders - Only persistent: hasAdditionalOptions={false} → no dropdown - Only pinned: hasAdditionalOptions={false} → dropdown managed by OverflowMenu - Mixed persistent/pinned: hasAdditionalOptions={false} → dropdown managed by OverflowMenu Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
1 parent 24a7dff commit b7f8002

File tree

3 files changed

+176
-1
lines changed

3 files changed

+176
-1
lines changed

packages/module/src/ResponsiveActions/ResponsiveActions.test.tsx

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,47 @@ describe('ResponsiveActions component', () => {
1414
</ResponsiveActions>);
1515
expect(container).toMatchSnapshot();
1616
});
17+
18+
test('ResponsiveActions with only isPersistent actions', () => {
19+
const { container } = render(
20+
<ResponsiveActions breakpoint="lg">
21+
<ResponsiveAction isPersistent>Persistent action 1</ResponsiveAction>
22+
<ResponsiveAction isPersistent>Persistent action 2</ResponsiveAction>
23+
</ResponsiveActions>);
24+
25+
// Should not have dropdown control when only persistent actions
26+
const dropdownControl = container.querySelector('[data-ouia-component-id="ResponsiveActions-menu-control"]');
27+
expect(dropdownControl).toBeNull();
28+
29+
// Should have persistent actions
30+
const buttons = container.querySelectorAll('button');
31+
expect(buttons).toHaveLength(2);
32+
});
33+
34+
test('ResponsiveActions with only isPinned actions', () => {
35+
const { container } = render(
36+
<ResponsiveActions breakpoint="lg">
37+
<ResponsiveAction isPinned>Pinned action 1</ResponsiveAction>
38+
<ResponsiveAction isPinned>Pinned action 2</ResponsiveAction>
39+
</ResponsiveActions>);
40+
41+
// Should have pinned actions as buttons
42+
const buttons = container.querySelectorAll('button');
43+
expect(buttons).toHaveLength(2);
44+
expect(container).toMatchSnapshot();
45+
});
46+
47+
test('ResponsiveActions with mix of isPersistent and isPinned actions', () => {
48+
const { container } = render(
49+
<ResponsiveActions breakpoint="lg">
50+
<ResponsiveAction isPersistent>Persistent action</ResponsiveAction>
51+
<ResponsiveAction isPinned>Pinned action</ResponsiveAction>
52+
</ResponsiveActions>);
53+
54+
// Should have both persistent and pinned actions as buttons
55+
const buttons = container.querySelectorAll('button');
56+
expect(buttons).toHaveLength(2);
57+
expect(container).toMatchSnapshot();
58+
});
1759
});
1860
});

packages/module/src/ResponsiveActions/ResponsiveActions.tsx

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ export const ResponsiveActions: FunctionComponent<ResponsiveActionsProps> = ({ o
2121
const persistentActions: ReactNode[] = [];
2222
const pinnedActions: ReactNode[] = [];
2323
const dropdownItems: ReactNode[] = [];
24+
let hasRegularActions = false;
2425

2526
Children.forEach(children, (child, index) => {
2627
if (isValidElement<ResponsiveActionProps>(child)) {
@@ -34,7 +35,11 @@ export const ResponsiveActions: FunctionComponent<ResponsiveActionsProps> = ({ o
3435
</Button>
3536
</OverflowMenuItem>
3637
);
38+
} else {
39+
// Track if there are any regular (non-persistent, non-pinned) actions
40+
hasRegularActions = true;
3741
}
42+
3843
if (!isPersistent) {
3944
dropdownItems.push(
4045
<OverflowMenuDropdownItem key={key} onClick={onClick} isShared={isPinned} ouiaId={`${ouiaId}-action-${key}`} isDisabled={actionProps.isDisabled}>
@@ -45,6 +50,11 @@ export const ResponsiveActions: FunctionComponent<ResponsiveActionsProps> = ({ o
4550
}
4651
});
4752

53+
// Only render OverflowMenu if there are actions to display
54+
if (persistentActions.length === 0 && pinnedActions.length === 0 && dropdownItems.length === 0) {
55+
return null;
56+
}
57+
4858
return (
4959
<OverflowMenu breakpoint={breakpoint} data-ouia-component-id={`${ouiaId}-menu`} {...props}>
5060
{persistentActions.length > 0 ? (
@@ -62,7 +72,7 @@ export const ResponsiveActions: FunctionComponent<ResponsiveActionsProps> = ({ o
6272
</OverflowMenuContent>
6373
) : null}
6474
{dropdownItems.length > 0 && (
65-
<OverflowMenuControl hasAdditionalOptions data-ouia-component-id={`${ouiaId}-menu-control`}>
75+
<OverflowMenuControl hasAdditionalOptions={hasRegularActions} data-ouia-component-id={`${ouiaId}-menu-control`}>
6676
<Dropdown
6777
ouiaId={`${ouiaId}-menu-dropdown`}
6878
onSelect={() => setIsOpen(false)}

packages/module/src/ResponsiveActions/__snapshots__/ResponsiveActions.test.tsx.snap

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,3 +99,126 @@ exports[`ResponsiveActions component should render correctly ResponsiveActions 1
9999
</div>
100100
</div>
101101
`;
102+
103+
exports[`ResponsiveActions component should render correctly ResponsiveActions with mix of isPersistent and isPinned actions 1`] = `
104+
<div>
105+
<div
106+
class="pf-v6-c-overflow-menu"
107+
data-ouia-component-id="ResponsiveActions-menu"
108+
>
109+
<div
110+
class="pf-v6-c-overflow-menu__content"
111+
>
112+
<div
113+
class="pf-v6-c-overflow-menu__group pf-m-button-group"
114+
data-ouia-component-id="ResponsiveActions-menu-persistent-group"
115+
>
116+
<div
117+
class="pf-v6-c-overflow-menu__item"
118+
>
119+
120+
<button
121+
class="pf-v6-c-button pf-m-primary"
122+
data-ouia-component-id="ResponsiveActions-action-0"
123+
data-ouia-component-type="PF6/Button"
124+
data-ouia-safe="true"
125+
type="button"
126+
>
127+
<span
128+
class="pf-v6-c-button__text"
129+
>
130+
Persistent action
131+
</span>
132+
</button>
133+
134+
</div>
135+
</div>
136+
</div>
137+
<div
138+
class="pf-v6-c-overflow-menu__content"
139+
>
140+
<div
141+
class="pf-v6-c-overflow-menu__group pf-m-button-group"
142+
data-ouia-component-id="ResponsiveActions-menu-pinned-group"
143+
>
144+
<div
145+
class="pf-v6-c-overflow-menu__item"
146+
>
147+
148+
<button
149+
class="pf-v6-c-button pf-m-primary"
150+
data-ouia-component-id="ResponsiveActions-action-1"
151+
data-ouia-component-type="PF6/Button"
152+
data-ouia-safe="true"
153+
type="button"
154+
>
155+
<span
156+
class="pf-v6-c-button__text"
157+
>
158+
Pinned action
159+
</span>
160+
</button>
161+
162+
</div>
163+
</div>
164+
</div>
165+
</div>
166+
</div>
167+
`;
168+
169+
exports[`ResponsiveActions component should render correctly ResponsiveActions with only isPinned actions 1`] = `
170+
<div>
171+
<div
172+
class="pf-v6-c-overflow-menu"
173+
data-ouia-component-id="ResponsiveActions-menu"
174+
>
175+
<div
176+
class="pf-v6-c-overflow-menu__content"
177+
>
178+
<div
179+
class="pf-v6-c-overflow-menu__group pf-m-button-group"
180+
data-ouia-component-id="ResponsiveActions-menu-pinned-group"
181+
>
182+
<div
183+
class="pf-v6-c-overflow-menu__item"
184+
>
185+
186+
<button
187+
class="pf-v6-c-button pf-m-primary"
188+
data-ouia-component-id="ResponsiveActions-action-0"
189+
data-ouia-component-type="PF6/Button"
190+
data-ouia-safe="true"
191+
type="button"
192+
>
193+
<span
194+
class="pf-v6-c-button__text"
195+
>
196+
Pinned action 1
197+
</span>
198+
</button>
199+
200+
</div>
201+
<div
202+
class="pf-v6-c-overflow-menu__item"
203+
>
204+
205+
<button
206+
class="pf-v6-c-button pf-m-primary"
207+
data-ouia-component-id="ResponsiveActions-action-1"
208+
data-ouia-component-type="PF6/Button"
209+
data-ouia-safe="true"
210+
type="button"
211+
>
212+
<span
213+
class="pf-v6-c-button__text"
214+
>
215+
Pinned action 2
216+
</span>
217+
</button>
218+
219+
</div>
220+
</div>
221+
</div>
222+
</div>
223+
</div>
224+
`;

0 commit comments

Comments
 (0)