Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions renderers/react/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

38 changes: 37 additions & 1 deletion renderers/react/src/v0_9/catalog/basic/components/Icon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,44 @@
import {IconApi} from '@a2ui/web_core/v0_9/basic_catalog';
import {getBaseLeafStyle} from '../utils';

// Programmatically load the material symbols font if the application hasn't already.
if (typeof document !== 'undefined') {
const isLoaded = Array.from(document.querySelectorAll('link')).some(

Check failure on line 24 in renderers/react/src/v0_9/catalog/basic/components/Icon.tsx

View workflow job for this annotation

GitHub Actions / lint

Replace `⏎····(link)·=>` with `(link)·=>⏎···`
(link) => link.href.includes('Material+Symbols+Outlined')
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will in some edge cases end up burning you. Material Symbols offers dynamic icon loading from their CDN, meaning that users can provide Material Symbols URLs that look like:

<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200&icon_names=close" />

Note the icon_names=close, which will cause that to only load the close icon and nothing else, saving significantly on weight.

This code will match the above href, even though it doesn't pull in the full library, which I guess we probably need for the sake of the AI choosing any icon? More on that in my other comment.

);
if (!isLoaded) {
const link = document.createElement('link');
link.rel = 'stylesheet';
link.href = 'https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@24,400,0,1';

Check failure on line 30 in renderers/react/src/v0_9/catalog/basic/components/Icon.tsx

View workflow job for this annotation

GitHub Actions / lint

Insert `⏎·····`
document.head.appendChild(link);
}
}
Comment on lines +22 to +33
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

While automatically loading the font makes the component self-contained, injecting a <link> tag as a module-level side effect is generally discouraged in React. It can have unintended consequences in different environments (like SSR), can be hard to test, and takes control away from the application developer who might want to manage font loading differently (e.g., self-hosting).

A more robust approach for a component library is to remove this logic and instead document that the consuming application is responsible for including the 'Material Symbols Outlined' font. This gives developers more control and makes the component's behavior more predictable.


const ICON_MAPPING: Record<string, string> = {
play: 'play_arrow',
rewind: 'fast_rewind',
favoriteOff: 'favorite',
starOff: 'star',
};

const FILLED_ICONS = ['star', 'starHalf', 'favorite'];

export const Icon = createReactComponent(IconApi, ({props}) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The changes in this file introduce new logic for icon name mapping and font loading, but there are no corresponding tests to verify this new functionality for the v0.9 Icon component. According to the repository's style guide, code changes should be accompanied by tests.

Please add unit tests to cover the new logic, including:

  • Correct mapping of icon names from ICON_MAPPING.
  • Correct conversion of camelCase names to snake_case.
  • Correct application of the fontVariationSettings for filled icons.
References
  1. Code changes should have tests. (link)

const iconName =
const rawIconName =
typeof props.name === 'string' ? props.name : (props.name as {path?: string})?.path;

Check failure on line 47 in renderers/react/src/v0_9/catalog/basic/components/Icon.tsx

View workflow job for this annotation

GitHub Actions / lint

Delete `··`
// Use mapping if available, otherwise convert camelCase to snake_case
let iconName = '';
if (rawIconName) {
if (ICON_MAPPING[rawIconName]) {
iconName = ICON_MAPPING[rawIconName];
} else {
iconName = rawIconName.replace(/([A-Z])/gm, "_$1").toLowerCase();

Check failure on line 54 in renderers/react/src/v0_9/catalog/basic/components/Icon.tsx

View workflow job for this annotation

GitHub Actions / lint

Replace `"_$1"` with `'_$1'`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The regular expression used to convert camelCase to snake_case can produce a leading underscore for PascalCase icon names (e.g., MyIcon becomes _my_icon). While the current icon set might not use PascalCase, this could be a source of bugs for custom icons.

A small adjustment can make this more robust by removing the potential leading underscore.

Suggested change
iconName = rawIconName.replace(/([A-Z])/gm, "_$1").toLowerCase();
iconName = rawIconName.replace(/([A-Z])/gm, "_$1").toLowerCase().replace(/^_/, '');

}
}

const isFilled = FILLED_ICONS.includes(rawIconName || '');

const style: React.CSSProperties = {
...getBaseLeafStyle(),
fontSize: '24px',
Expand All @@ -30,6 +65,7 @@
display: 'inline-flex',
alignItems: 'center',
justifyContent: 'center',
fontVariationSettings: isFilled ? '"FILL" 1' : undefined,
};

return (
Expand Down
Loading