Skip to content

Commit 1bfc081

Browse files
authored
feat: Custom svg icons for runtime drawers' headerActions (#4108)
1 parent beb057b commit 1bfc081

File tree

5 files changed

+73
-24
lines changed

5 files changed

+73
-24
lines changed

src/app-layout/__tests__/runtime-drawers.test.tsx

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1220,6 +1220,34 @@ describe('toolbar mode only features', () => {
12201220
findDrawerHeaderActionById('add', renderProps)!.click();
12211221
expect(onHeaderActionClick).toHaveBeenCalledWith({ id: 'add' });
12221222
});
1223+
1224+
test('propagates iconSvg / pressedIconSvg as html content for headerActions', async () => {
1225+
registerDrawer({
1226+
...drawerDefaults,
1227+
id: 'global-drawer',
1228+
headerActions: [
1229+
{
1230+
type: 'icon-button',
1231+
id: 'add',
1232+
iconSvg: `<rect data-testid="custom-icon" />`,
1233+
text: 'Add',
1234+
},
1235+
{
1236+
type: 'icon-toggle-button',
1237+
id: 'add',
1238+
pressed: true,
1239+
iconSvg: `<rect data-testid="custom-icon-non-pressed" />`,
1240+
pressedIconSvg: `<rect data-testid="custom-icon-pressed" />`,
1241+
text: 'Add',
1242+
},
1243+
],
1244+
});
1245+
const renderProps = await renderComponent(<AppLayout />);
1246+
const { wrapper } = renderProps;
1247+
findDrawerTriggerById('global-drawer', renderProps)!.click();
1248+
expect(wrapper.find('[data-testid="custom-icon"]')).toBeTruthy();
1249+
expect(wrapper.find('[data-testid="custom-icon-pressed"]')).toBeTruthy();
1250+
});
12231251
});
12241252
});
12251253

src/app-layout/runtime-drawer/index.tsx

Lines changed: 23 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,7 @@
22
// SPDX-License-Identifier: Apache-2.0
33
import React, { useContext, useEffect, useRef } from 'react';
44

5-
import { warnOnce } from '@cloudscape-design/component-toolkit/internal';
6-
7-
import { ButtonGroupProps } from '../../button-group/interfaces';
5+
import { ButtonGroupProps, ItemRuntime } from '../../button-group/interfaces';
86
import { fireNonCancelableEvent, NonCancelableEventHandler } from '../../internal/events';
97
import {
108
DrawerConfig as RuntimeDrawerConfig,
@@ -83,21 +81,22 @@ function RuntimeDrawerHeader({ mountHeader, unmountHeader }: RuntimeContentHeade
8381
return <div className={styles['runtime-header-wrapper']} ref={ref} />;
8482
}
8583

86-
function checkForUnsupportedProps(headerActions: ReadonlyArray<ButtonGroupProps.Item>) {
87-
const unsupportedProps = new Set([
88-
'iconSvg',
89-
'popoverFeedback',
90-
'pressedIconSvg',
91-
'popoverFeedback',
92-
'pressedPopoverFeedback',
93-
]);
94-
for (const item of headerActions) {
95-
const unsupported = Object.keys(item).filter(key => unsupportedProps.has(key));
96-
if (unsupported.length > 0) {
97-
warnOnce('AppLayout', `The headerActions properties are not supported for runtime api: ${unsupported.join(' ')}`);
98-
}
99-
}
100-
return headerActions;
84+
function mapRuntimeHeaderActionsToHeaderActions(
85+
runtimeHeaderActions: ReadonlyArray<ItemRuntime>
86+
): ReadonlyArray<ButtonGroupProps.Item> {
87+
return runtimeHeaderActions.map(runtimeHeaderAction => {
88+
return {
89+
...runtimeHeaderAction,
90+
...('iconSvg' in runtimeHeaderAction &&
91+
runtimeHeaderAction.iconSvg && {
92+
iconSvg: convertRuntimeTriggerToReactNode(runtimeHeaderAction.iconSvg),
93+
}),
94+
...('pressedIconSvg' in runtimeHeaderAction &&
95+
runtimeHeaderAction.pressedIconSvg && {
96+
iconSvg: convertRuntimeTriggerToReactNode(runtimeHeaderAction.pressedIconSvg),
97+
}),
98+
};
99+
});
101100
}
102101

103102
const convertRuntimeTriggerToReactNode = (runtimeTrigger?: string) => {
@@ -138,7 +137,9 @@ export const mapRuntimeConfigToDrawer = (
138137
onResize: event => {
139138
fireNonCancelableEvent(runtimeDrawer.onResize, { size: event.detail.size, id: runtimeDrawer.id });
140139
},
141-
headerActions: runtimeDrawer.headerActions ? checkForUnsupportedProps(runtimeDrawer.headerActions) : undefined,
140+
headerActions: runtimeDrawer.headerActions
141+
? mapRuntimeHeaderActionsToHeaderActions(runtimeDrawer.headerActions)
142+
: undefined,
142143
};
143144
};
144145

@@ -183,7 +184,9 @@ export const mapRuntimeConfigToAiDrawer = (
183184
onResize: event => {
184185
fireNonCancelableEvent(runtimeDrawer.onResize, { size: event.detail.size, id: runtimeDrawer.id });
185186
},
186-
headerActions: runtimeDrawer.headerActions ? checkForUnsupportedProps(runtimeDrawer.headerActions) : undefined,
187+
headerActions: runtimeDrawer.headerActions
188+
? mapRuntimeHeaderActionsToHeaderActions(runtimeDrawer.headerActions)
189+
: undefined,
187190
};
188191
};
189192

src/button-group/interfaces.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,24 @@ export interface InternalIconToggleButton extends ButtonGroupProps.IconToggleBut
112112
analyticsAction?: string;
113113
}
114114

115+
export interface IconButtonRuntime extends Omit<ButtonGroupProps.IconButton, 'iconSvg' | 'popoverFeedback'> {
116+
iconSvg?: string;
117+
}
118+
export interface IconToggleButtonRuntime
119+
extends Omit<
120+
ButtonGroupProps.IconToggleButton,
121+
'iconSvg' | 'pressedIconSvg' | 'popoverFeedback' | 'pressedPopoverFeedback'
122+
> {
123+
iconSvg?: string;
124+
pressedIconSvg?: string;
125+
}
126+
export type ItemOrGroupRuntime = ItemRuntime | ButtonGroupProps.Group;
127+
export type ItemRuntime =
128+
| IconButtonRuntime
129+
| IconToggleButtonRuntime
130+
| ButtonGroupProps.IconFileInput
131+
| ButtonGroupProps.MenuDropdown;
132+
115133
export type InternalItemOrGroup = InternalItem | ButtonGroupProps.Group;
116134
export type InternalItem =
117135
| InternalIconButton

src/internal/plugins/controllers/drawers.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
22
// SPDX-License-Identifier: Apache-2.0
3-
import { ButtonGroupProps } from '../../../button-group/interfaces';
3+
import { ButtonGroupProps, ItemRuntime } from '../../../button-group/interfaces';
44
import debounce from '../../debounce';
55
import { NonCancelableEventHandler } from '../../events';
66
import { reportRuntimeApiWarning } from '../helpers/metrics';
@@ -41,7 +41,7 @@ export interface DrawerConfig {
4141
unmountContent: (container: HTMLElement) => void;
4242
preserveInactiveContent?: boolean;
4343
onToggle?: NonCancelableEventHandler<DrawerStateChangeParams>;
44-
headerActions?: ReadonlyArray<ButtonGroupProps.Item>;
44+
headerActions?: ReadonlyArray<ItemRuntime>;
4545
onHeaderActionClick?: NonCancelableEventHandler<ButtonGroupProps.ItemClickDetails>;
4646
}
4747

src/internal/plugins/widget/interfaces.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
22
// SPDX-License-Identifier: Apache-2.0
3-
import { ButtonGroupProps } from '../../../button-group/interfaces';
3+
import { ButtonGroupProps, ItemRuntime } from '../../../button-group/interfaces';
44
import { NonCancelableEventHandler } from '../../events';
55

66
interface Message<Type, Payload> {
@@ -48,7 +48,7 @@ export interface DrawerPayload {
4848
onToggle?: NonCancelableEventHandler<DrawerStateChangeParams>;
4949
mountHeader?: (container: HTMLElement) => void;
5050
unmountHeader?: (container: HTMLElement) => void;
51-
headerActions?: ReadonlyArray<ButtonGroupProps.Item>;
51+
headerActions?: ReadonlyArray<ItemRuntime>;
5252
onHeaderActionClick?: NonCancelableEventHandler<ButtonGroupProps.ItemClickDetails>;
5353
onToggleFocusMode?: NonCancelableEventHandler<{ isExpanded: boolean }>;
5454
position?: 'side' | 'bottom';

0 commit comments

Comments
 (0)