Skip to content

Conversation

dkario
Copy link
Collaborator

@dkario dkario commented Sep 25, 2025

For the new avatar style macro exported from Menu, I copied a subset of ComboBox's image style macro.

Avatar controls size via its size prop, so I didn't include that in the style macro.

Closes: N/A


Combobox:

image

Picker:

Screenshot 2025-09-25 at 11 13 43 AM Screenshot 2025-09-25 at 11 13 34 AM
image

✅ Pull Request Checklist:

  • Included link to corresponding React Spectrum GitHub Issue.
  • Added/updated unit tests and storybook for this change (for new code or code which already has tests).
  • Filled out test instructions.
  • Updated documentation (if it already exists for this component).
  • Looked at the Accessibility Practices for this feature - Aria Practices

📝 Test Instructions:

  • In new "With Avatars" Combobox and Picker stories, verify avatars are positioned and sized correctly

🧢 Your Project:

Adobe

For the new `avatar` style macro, I copied a subset of ComboBox's `image` style macro.

Avatar controls size via its `size` prop, so I didn't include that in the style macro.
Comment on lines 86 to 108
```tsx render
"use client";
import {Avatar, ComboBox, ComboBoxItem, Text} from '@react-spectrum/s2';

const users = Array.from({length: 10}, (_, i) => ({
id: `user${i + 1}`,
name: `User ${i + 1}`,
email: `user${i + 1}@example.com`,
avatar: 'https://i.imgur.com/kJOwAdv.png'
}));

<ComboBox label="Share" items={users}>
{(item) => (
<ComboBoxItem id={item.id} textValue={item.name}>
{/*- begin highlight -*/}
<Avatar slot="avatar" src={item.avatar} />
{/*- end highlight -*/}
<Text slot="label">{item.name}</Text>
<Text slot="description">{item.email}</Text>
</ComboBoxItem>
)}
</ComboBox>
```
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Not sure if this doc example is good enough for the site, or if it's okay to have two tsx render blocks one after another like I did here. Same thoughts for the Picker docs I added

Copy link
Member

Choose a reason for hiding this comment

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

I think it could be moved up into the previous example, which would then render two comboboxes next to each other, one with icons, one with avatars (since they probably shouldn't be mixed)
It'll be hidden by the "expand" button, but should be obvious with the highlights once expanded

I'm sure we'll have some other opinions so happy to do this ourselves later if we can't come to an immediate consensus

Comment on lines +617 to +621
context={AvatarContext}
value={{slots: {
avatar: {size: avatarSize[size], styles: avatar}
}}}>
<DefaultProvider
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Is this the right approach to add another child DefaultProvider here?

Copy link
Member

Choose a reason for hiding this comment

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

Looks like it says

// A Context.Provider that only sets a value if not inside SelectValue.
function DefaultProvider({context, value, children}: {context: React.Con...

I haven't spent time thinking about it yet (hopefully can soon), is this what you want?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Thanks that helps, I just wanted to make sure that sandwiching this between the DefaultProviders for IconContext and TextContent is okay, since this is diff than the usual Provider that takes an array of contexts

Copy link
Member

@LFDanLu LFDanLu Sep 30, 2025

Choose a reason for hiding this comment

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

Yeah, this should be fine since those contexts won't collide since they are different contexts after all. Technically you could use a standard Provider I believe since the styles passed down are the same between the context provided at the SelectValue level and the one at the PickerItem level in this case but its probably safer to use DefaultProvider here so those can diverge if need be

@rspbot
Copy link

rspbot commented Sep 25, 2025

@rspbot
Copy link

rspbot commented Sep 25, 2025

Comment on lines +617 to +621
context={AvatarContext}
value={{slots: {
avatar: {size: avatarSize[size], styles: avatar}
}}}>
<DefaultProvider
Copy link
Member

Choose a reason for hiding this comment

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

Looks like it says

// A Context.Provider that only sets a value if not inside SelectValue.
function DefaultProvider({context, value, children}: {context: React.Con...

I haven't spent time thinking about it yet (hopefully can soon), is this what you want?

Comment on lines 86 to 108
```tsx render
"use client";
import {Avatar, ComboBox, ComboBoxItem, Text} from '@react-spectrum/s2';

const users = Array.from({length: 10}, (_, i) => ({
id: `user${i + 1}`,
name: `User ${i + 1}`,
email: `user${i + 1}@example.com`,
avatar: 'https://i.imgur.com/kJOwAdv.png'
}));

<ComboBox label="Share" items={users}>
{(item) => (
<ComboBoxItem id={item.id} textValue={item.name}>
{/*- begin highlight -*/}
<Avatar slot="avatar" src={item.avatar} />
{/*- end highlight -*/}
<Text slot="label">{item.name}</Text>
<Text slot="description">{item.email}</Text>
</ComboBoxItem>
)}
</ComboBox>
```
Copy link
Member

Choose a reason for hiding this comment

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

I think it could be moved up into the previous example, which would then render two comboboxes next to each other, one with icons, one with avatars (since they probably shouldn't be mixed)
It'll be hidden by the "expand" button, but should be obvious with the highlights once expanded

I'm sure we'll have some other opinions so happy to do this ourselves later if we can't come to an immediate consensus

@LFDanLu
Copy link
Member

LFDanLu commented Sep 29, 2025

https://www.chromatic.com/build?appId=5f0dd5ad2b5fc10022a2e320&number=1045, I wouldn't worry about the other diffs, they are from main

@rspbot
Copy link

rspbot commented Sep 29, 2025


export const AvatarContext = createContext<ContextValue<Partial<AvatarProps>, DOMRefValue<HTMLImageElement>>>(null);
export interface AvatarContextValue extends AvatarProps {
render?: (avatar: ReactNode) => ReactNode
Copy link
Member

Choose a reason for hiding this comment

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

I think rather than adding this prop we could just make Avatar have CenterBaseline by default. I can't really think of a case where you'd want to align text with it at the bottom. We may be able to avoid the extra wrapper div as well since Image already has a wrapper div. We could potentially put the ::before directly on that.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Thanks for fixing this!

@rspbot
Copy link

rspbot commented Sep 30, 2025

snowystinger
snowystinger previously approved these changes Sep 30, 2025
Copy link
Member

@snowystinger snowystinger left a comment

Choose a reason for hiding this comment

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

We should run chromatic on this, I'm in the middle of editing dependencies, so if someone else beats me to it, that's fine

@rspbot
Copy link

rspbot commented Sep 30, 2025

@LFDanLu
Copy link
Member

LFDanLu commented Sep 30, 2025

@rspbot
Copy link

rspbot commented Sep 30, 2025

@rspbot
Copy link

rspbot commented Sep 30, 2025

Comment on lines +617 to +621
context={AvatarContext}
value={{slots: {
avatar: {size: avatarSize[size], styles: avatar}
}}}>
<DefaultProvider
Copy link
Member

@LFDanLu LFDanLu Sep 30, 2025

Choose a reason for hiding this comment

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

Yeah, this should be fine since those contexts won't collide since they are different contexts after all. Technically you could use a standard Provider I believe since the styles passed down are the same between the context provided at the SelectValue level and the one at the PickerItem level in this case but its probably safer to use DefaultProvider here so those can diverge if need be

@yihuiliao yihuiliao added this pull request to the merge queue Sep 30, 2025
Merged via the queue into main with commit 8a49fc9 Sep 30, 2025
32 checks passed
@yihuiliao yihuiliao deleted the avatar-in-combobox-and-picker branch September 30, 2025 20:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants