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
14 changes: 13 additions & 1 deletion renderers/react/src/v0_9/A2uiSurface.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,18 @@ DeferredChild.displayName = 'DeferredChild';
export const A2uiSurface: React.FC<{surface: SurfaceModel<ReactComponentImplementation>}> = ({
surface,
}) => {
const themeStyle = useMemo(() => {
const style: Record<string, string> = {};
if (surface.theme?.primaryColor) {
style['--a2ui-primary-color'] = surface.theme.primaryColor;
}
return style;
}, [surface.theme]);
Comment on lines +130 to +136
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

high

The new theme integration logic introduced in A2uiSurface.tsx and its consumption in components like CheckBox, Slider, and TextField do not appear to have corresponding tests. The repository's style guide (line 17) explicitly states that 'If there are code changes, code should have tests.' Please add unit tests to verify that the primaryColor is correctly applied and that components react as expected to theme changes.

References
  1. If there are code changes, code should have tests.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

There's no existing test infrastructure for React v0.9 inline style assertions — the current tests focus on component rendering and catalog resolution, not CSS property values. Adding style-level testing would require a significant setup (e.g. computed style checks in jsdom). Happy to add tests in a follow-up if the team establishes the pattern.


// The root component always has ID 'root' and base path '/'
return <DeferredChild surface={surface} id="root" basePath="/" />;
return (
<div style={themeStyle as React.CSSProperties}>
<DeferredChild surface={surface} id="root" basePath="/" />
</div>
);
};
4 changes: 2 additions & 2 deletions renderers/react/src/v0_9/catalog/basic/components/Button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
import React from 'react';
import {createReactComponent} from '../../../adapter';
import {ButtonApi} from '@a2ui/web_core/v0_9/basic_catalog';
import {LEAF_MARGIN} from '../utils';
import {LEAF_MARGIN, PRIMARY_COLOR} from '../utils';

export const Button = createReactComponent(ButtonApi, ({props, buildChild}) => {
const style: React.CSSProperties = {
Expand All @@ -27,7 +27,7 @@ export const Button = createReactComponent(ButtonApi, ({props, buildChild}) => {
border: props.variant === 'borderless' ? 'none' : '1px solid #ccc',
backgroundColor:
props.variant === 'primary'
? 'var(--a2ui-primary-color, #007bff)'
? PRIMARY_COLOR
: props.variant === 'borderless'
? 'transparent'
: '#fff',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
import React from 'react';
import {createReactComponent} from '../../../adapter';
import {CheckBoxApi} from '@a2ui/web_core/v0_9/basic_catalog';
import {LEAF_MARGIN} from '../utils';
import {LEAF_MARGIN, PRIMARY_COLOR} from '../utils';

export const CheckBox = createReactComponent(CheckBoxApi, ({props}) => {
const onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
Expand All @@ -36,7 +36,7 @@ export const CheckBox = createReactComponent(CheckBoxApi, ({props}) => {
type="checkbox"
checked={!!props.value}
onChange={onChange}
style={{cursor: 'pointer', outline: hasError ? '1px solid red' : 'none'}}
style={{cursor: 'pointer', outline: hasError ? '1px solid red' : 'none', accentColor: PRIMARY_COLOR}}
/>
{props.label && (
<label
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
import React, {useState} from 'react';
import {createReactComponent} from '../../../adapter';
import {ChoicePickerApi} from '@a2ui/web_core/v0_9/basic_catalog';
import {LEAF_MARGIN, STANDARD_BORDER, STANDARD_RADIUS} from '../utils';
import {LEAF_MARGIN, PRIMARY_COLOR, STANDARD_BORDER, STANDARD_RADIUS} from '../utils';

// The type of an option is deeply nested into the ChoicePickerApi schema, and
// it seems z.infer is not inferring it correctly (?). We use `any` for now.
Expand Down Expand Up @@ -87,9 +87,9 @@ export const ChoicePicker = createReactComponent(ChoicePickerApi, ({props, conte
padding: '4px 12px',
borderRadius: '16px',
border: isSelected
? '1px solid var(--a2ui-primary-color, #007bff)'
? `1px solid ${PRIMARY_COLOR}`
: STANDARD_BORDER,
backgroundColor: isSelected ? 'var(--a2ui-primary-color, #007bff)' : '#fff',
backgroundColor: isSelected ? PRIMARY_COLOR : '#fff',
color: isSelected ? '#fff' : 'inherit',
cursor: 'pointer',
fontSize: '12px',
Expand Down
4 changes: 2 additions & 2 deletions renderers/react/src/v0_9/catalog/basic/components/Slider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
import React from 'react';
import {createReactComponent} from '../../../adapter';
import {SliderApi} from '@a2ui/web_core/v0_9/basic_catalog';
import {LEAF_MARGIN} from '../utils';
import {LEAF_MARGIN, PRIMARY_COLOR} from '../utils';

export const Slider = createReactComponent(SliderApi, ({props}) => {
const onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
Expand Down Expand Up @@ -51,7 +51,7 @@ export const Slider = createReactComponent(SliderApi, ({props}) => {
max={props.max}
value={props.value ?? 0}
onChange={onChange}
style={{width: '100%', cursor: 'pointer'}}
style={{width: '100%', cursor: 'pointer', accentColor: PRIMARY_COLOR}}
/>
</div>
);
Expand Down
6 changes: 3 additions & 3 deletions renderers/react/src/v0_9/catalog/basic/components/Tabs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
import {useState} from 'react';
import {createReactComponent} from '../../../adapter';
import {TabsApi} from '@a2ui/web_core/v0_9/basic_catalog';
import {LEAF_MARGIN} from '../utils';
import {LEAF_MARGIN, PRIMARY_COLOR} from '../utils';

// The type of a tab is deeply nested into the TabsApi schema, and
// it seems z.infer is not inferring it correctly (?). We use `any` for now.
Expand All @@ -42,10 +42,10 @@ export const Tabs = createReactComponent(TabsApi, ({props, buildChild}) => {
border: 'none',
background: 'none',
borderBottom:
selectedIndex === i ? '2px solid var(--a2ui-primary-color, #007bff)' : 'none',
selectedIndex === i ? `2px solid ${PRIMARY_COLOR}` : 'none',
fontWeight: selectedIndex === i ? 'bold' : 'normal',
cursor: 'pointer',
color: selectedIndex === i ? 'var(--a2ui-primary-color, #007bff)' : 'inherit',
color: selectedIndex === i ? PRIMARY_COLOR : 'inherit',
}}
>
{tab.title}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
import React from 'react';
import {createReactComponent} from '../../../adapter';
import {TextFieldApi} from '@a2ui/web_core/v0_9/basic_catalog';
import {LEAF_MARGIN, STANDARD_BORDER, STANDARD_RADIUS} from '../utils';
import {LEAF_MARGIN, PRIMARY_COLOR, STANDARD_BORDER, STANDARD_RADIUS} from '../utils';

export const TextField = createReactComponent(TextFieldApi, ({props}) => {
const onChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
Expand All @@ -34,6 +34,7 @@ export const TextField = createReactComponent(TextFieldApi, ({props}) => {
border: STANDARD_BORDER,
borderRadius: STANDARD_RADIUS,
boxSizing: 'border-box',
outlineColor: PRIMARY_COLOR,
};

// Note: To have a unique id without passing context we can use a random or provided id,
Expand Down
6 changes: 6 additions & 0 deletions renderers/react/src/v0_9/catalog/basic/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@ export const STANDARD_BORDER = '1px solid #ccc';
/** Standard border radius. */
export const STANDARD_RADIUS = '8px';

/** Default primary color, used as fallback when no theme is provided. */
export const DEFAULT_PRIMARY_COLOR = '#007bff';

/** CSS value referencing the theme primary color with fallback. */
export const PRIMARY_COLOR = `var(--a2ui-primary-color, ${DEFAULT_PRIMARY_COLOR})`;

export const mapJustify = (j?: string) => {
switch (j) {
case 'center':
Expand Down