Skip to content

Commit 7cdab43

Browse files
authored
Rename Icon path property to svgPath to fix type collision (#1347)
The Icon component's name property accepted either a string (for standard icons) or an object with a path property (for custom SVG paths) However, the Angular renderer uses a utility type to resolve properties that strips away shapes matching DataBindingType (which is defined as { path: string }). Because of this structural match, the { path: string } branch was being incorrectly excluded from the resolved type, leaving only the string enum and forcing developers to use as any casts when dealing with custom SVG paths. This PR renames the property inside the object from path to svgPath. This breaks the structural match with DataBindingType and preserves the type in the union, allowing for proper type inference without forced casts.
1 parent f59dacc commit 7cdab43

10 files changed

Lines changed: 68 additions & 19 deletions

File tree

renderers/angular/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
## Unreleased
22

3+
- **BREAKING CHANGE**: (v0_9) Rename Icon `path` property to `svgPath` to fix type collision and avoid forced casts.
34
- (v0_9) Re-style the v0_9 catalog components using the default theme from
45
`web_core`. [#1166](https://github.com/google/A2UI/pull/1166)
56
- (v0_9) Improve type safety of `props()` in Catalog components. Custom catalog

renderers/angular/src/v0_9/catalog/basic/icon.component.ts

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,9 @@ const ICON_NAME_OVERRIDES: Record<string, string> = {
4242
standalone: true,
4343
imports: [],
4444
template: `
45-
@if (isPath()) {
45+
@if (isSvgPath()) {
4646
<svg class="a2ui-icon svg" viewBox="0 0 24 24" [style.fill]="color() || 'currentColor'">
47-
<path [attr.d]="path()"></path>
47+
<path [attr.d]="svgPath()"></path>
4848
</svg>
4949
} @else {
5050
<i class="material-icons a2ui-icon" [style.color]="color()">
@@ -88,14 +88,17 @@ export class IconComponent extends BasicCatalogComponent<typeof IconApi> {
8888
readonly color = computed(() => (this.props() as AnyDuringSchemaAlignment)['color']?.value());
8989
readonly iconNameRaw = computed(() => this.props()['name']?.value());
9090

91-
readonly isPath = computed(() => {
91+
readonly isSvgPath = computed(() => {
9292
const name = this.iconNameRaw();
93-
return typeof name === 'object' && name !== null && 'path' in name;
93+
return typeof name === 'object' && name !== null && 'svgPath' in name;
9494
});
9595

96-
readonly path = computed(() => {
96+
readonly svgPath = computed(() => {
9797
const name = this.iconNameRaw();
98-
return (name as any)?.path || '';
98+
if (typeof name === 'object' && name !== null && 'svgPath' in name) {
99+
return name.svgPath;
100+
}
101+
return '';
99102
});
100103

101104
readonly iconName = computed(() => {

renderers/angular/src/v0_9/catalog/basic/simple-components.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -284,7 +284,7 @@ describe('Simple Components', () => {
284284
setComponentProps(fixture, {
285285
...defaultProps,
286286
name: createBoundProperty({
287-
path: 'M10 10...',
287+
svgPath: 'M10 10...',
288288
}) as unknown as ComponentToProps<IconComponent>['name'],
289289
});
290290
fixture.detectChanges();

renderers/lit/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
## Unreleased
22

3+
- **BREAKING CHANGE**: (v0_9) Rename Icon `path` property to `svgPath` and update component to correctly render SVG elements.
34
- (v0_9) Wire up agent-provided primary color to basic catalog components.
45
- (v0_9) Re-style the v0_9 catalog components using the default theme from
56
`web_core`. [#1079](https://github.com/google/A2UI/pull/1079)

renderers/lit/src/v0_9/catalogs/basic/components/Icon.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,11 @@ export class A2uiIconElement extends BasicCatalogA2uiLitElement<typeof IconApi>
6464
color: var(--a2ui-icon-color, inherit);
6565
font-variation-settings: var(--a2ui-icon-font-variation-settings, 'FILL' 1);
6666
}
67+
.svg {
68+
fill: currentColor;
69+
width: var(--_icon-size);
70+
height: var(--_icon-size);
71+
}
6772
`;
6873

6974
protected createController() {
@@ -74,8 +79,15 @@ export class A2uiIconElement extends BasicCatalogA2uiLitElement<typeof IconApi>
7479
const props = this.controller.props;
7580
if (!props) return nothing;
7681

77-
const iconName =
78-
typeof props.name === 'string' ? toMaterialSymbol(props.name) : (props.name as any)?.path;
82+
const name = props.name;
83+
const isPath = typeof name === 'object' && name !== null && 'svgPath' in name;
84+
85+
if (isPath) {
86+
const path = (name as {svgPath: string}).svgPath;
87+
return html`<svg class="svg" viewBox="0 0 24 24"><path d=${path}></path></svg>`;
88+
}
89+
90+
const iconName = typeof name === 'string' ? toMaterialSymbol(name) : '';
7991
return html`<span class="material-symbol">${iconName}</span>`;
8092
}
8193
}

renderers/react/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
## Unreleased
22

3+
- **BREAKING CHANGE**: (v0_9) Rename Icon `path` property to `svgPath` and update component to correctly render SVG elements.
34
- Added license.
45
- (v0_8) Exclude SVG elements and descendants from CSS reset to restore SVG rendering. [#1252](https://github.com/google/A2UI/pull/1252)
56
- **BREAKING CHANGE**: Renamed `createReactComponent` to `createComponentImplementation`.

renderers/react/src/v0_9/catalog/basic/components/Icon.tsx

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -36,29 +36,51 @@ function toMaterialSymbol(str: string): string {
3636

3737
export const Icon = createComponentImplementation(IconApi, ({props}) => {
3838
useBasicCatalogStyles();
39-
const iconName =
40-
typeof props.name === 'string'
41-
? toMaterialSymbol(props.name)
42-
: (props.name as {path?: string})?.path;
4339

44-
const style: React.CSSProperties = {
40+
const isPath = typeof props.name === 'object' && props.name !== null && 'svgPath' in props.name;
41+
42+
const baseStyle: React.CSSProperties = {
4543
...getBaseLeafStyle(),
4644
display: 'inline-flex',
4745
alignItems: 'center',
4846
justifyContent: 'center',
49-
fontFamily: 'var(--a2ui-icon-font-family, "Material Symbols Outlined", sans-serif)',
5047
fontSize: 'var(--a2ui-icon-size, var(--a2ui-font-size-xl, 24px))',
5148
color: 'var(--a2ui-icon-color, inherit)',
49+
lineHeight: 1,
50+
};
51+
52+
if (isPath) {
53+
const path = (props.name as {svgPath: string}).svgPath;
54+
return (
55+
<svg
56+
className="a2ui-icon svg"
57+
viewBox="0 0 24 24"
58+
style={{
59+
...baseStyle,
60+
fill: 'currentColor',
61+
width: 'var(--a2ui-icon-size, 24px)',
62+
height: 'var(--a2ui-icon-size, 24px)',
63+
}}
64+
>
65+
<path d={path}></path>
66+
</svg>
67+
);
68+
}
69+
70+
const iconName = typeof props.name === 'string' ? toMaterialSymbol(props.name) : '';
71+
72+
const fontStyle: React.CSSProperties = {
73+
...baseStyle,
74+
fontFamily: 'var(--a2ui-icon-font-family, "Material Symbols Outlined", sans-serif)',
5275
fontVariationSettings: 'var(--a2ui-icon-font-variation-settings, "FILL" 1)',
5376
fontWeight: 'normal',
5477
fontStyle: 'normal',
55-
lineHeight: 1,
5678
letterSpacing: 'normal',
5779
textTransform: 'none',
5880
};
5981

6082
return (
61-
<span className="material-symbols-outlined" style={style}>
83+
<span className="material-symbols-outlined" style={fontStyle}>
6284
{iconName}
6385
</span>
6486
);

renderers/web_core/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
## Unreleased
22

3+
- **BREAKING CHANGE**: Rename Icon `path` property to `svgPath` to fix type collision with `DataBindingType`.
34
- (v0_9) Add `computeColorVariant` helper function for basic catalog components to generate CSS formulas for color variants (light, dark, hover), allowing reuse across renderers.
45

56
## 0.9.1

renderers/web_core/src/v0_9/basic_catalog/components/basic_components.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,11 @@ export const IconApi = {
150150
name: z
151151
.union([
152152
z.enum(ICON_NAMES),
153+
z
154+
.object({
155+
svgPath: z.string().describe('Custom SVG path data'),
156+
})
157+
.strict(),
153158
z
154159
.object({
155160
path: z.string(),

specification/v0_9/json/basic_catalog.json

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -162,12 +162,15 @@
162162
{
163163
"type": "object",
164164
"properties": {
165-
"path": {
165+
"svgPath": {
166166
"type": "string"
167167
}
168168
},
169-
"required": ["path"],
169+
"required": ["svgPath"],
170170
"additionalProperties": false
171+
},
172+
{
173+
"$ref": "common_types.json#/$defs/DataBinding"
171174
}
172175
]
173176
}

0 commit comments

Comments
 (0)