docs/react-v9/contributing/rfcs/react-components/convergence/reusing-avatar-in-components.md
@ling1726
This RFC enumerates the possible ways to handle:
When the component is reused within other v9 components. There is a broader question of how to internally reuse components in general, hopefully resolving the problem described in this RFC will help us make easier decisions for component reuse in the future.
Fluent UI v9 has design requirements on each of its components. These requirements are compiled from product usage. This is a good thing since our components can be easily used to power products. However one of the consequences of stricter design requirements it the resuse of our own components. Sometimes the component reusage can involve different defaults. How can we handle this in React Library?
Let's use Avatar as an example. We have concrete problems here that involve the Table and Persona components.
Both of these components use Avatar and require a default size when used in those components.
<Table size="smaller">
<TableBody>
<TableRow>
<TableCell>
<TableCellLayout media={<Avatar />}>Main content</TableCellLayout>
</TabeCell>
</TableRow>
</TableBody>
</Table>
The default size for the Avatar component is 32. This means that the component that is reusing the avatar needs
to do some extra work to make sure that the avatar follows design guidance.
We could consider a AvatarContext that is a part of react-shared-contexts that can be used by components that wish
to reuse the Avatar.
This solution is prototyped in #24807
const tableAvatarSizeMap = {
small: 24,
smaller: 20,
};
export const renderTableCellLayout_unstable = state => {
const { slots, slotProps } = getSlots<TableCellLayoutSlots>(state);
return (
<slots.root {...slotProps.root}>
<AvatarContextProvider value={tableAvatarSizeGroup[state.size]}>
{slots.media && <slots.media {...slotProps.media} />}
</AvatarContextProvider>
</slots.root>
);
};
We could treat this as a userland problem, and require that apps/features follow design guidance when using an avatar with these components.
<Table size="smaller">
<TableBody>
<TableRow>
<TableCell>
<TableCellLayout media={<Avatar size={20} />}>Main content</TableCellLayout>
</TabeCell>
</TableRow>
</TableBody>
</Table>
This is a method that is already done to support a similar problem for icons in:
The component should use selectors to target the contents of the slot and force the size. In the case of icons
a simple svg selector would work
const useStyles = makeStyles({
iconSlot: {
'& svg': {
width: '10px',
height: '10px',
},
},
});
However when using this method with other components we would use the className selectors:
const useStyles = makeStyles({
avatarSlot: {
'& fui-Avatar': {
width: '10px',
height: '10px',
},
},
});
This solution is used in Toolbar where ToolbarButton is recomposed from the normal Button
const tableAvatarSizeMap = {
small: 24,
smaller: 20,
};
export function TableAvatar: React.FC<AvatarProps>(props) {
// recomposed TableAvatar can read TableContext to adapt to its size prop
const { size } = useTableContext();
const state = useAvatar({...props, size: tableAvatarSizeMap[size]});
state = useAvatarStyles(props);
return renderAvatar(state);
}
<Table size="smaller">
<TableBody>
<TableRow>
<TableCell>
<TableCellLayout media={<TableAvatar />}>Main content</TableCellLayout>
</TabeCell>
</TableRow>
</TableBody>
</Table>
This solution is what the Alert currently does (however, it does not seem necessary in that component):
export function useTableCellLayout() {
const state = {
components: {
icon: 'span',
avatar: Avatar,
}
avatar: resolveShorthand(props.avatar);
}
/** Avatar prop takes precedence over the icon*/
if (!avatar) {
icon = resolveShorthand(props.icon, {
defaultProps: {
children: defaultIcon,
},
});
state.icon = icon;
}
return state;
}
export function renderTableCellLayout() {
}
<Table size="smaller">
<TableBody>
<TableRow>
<TableCell>
<TableCellLayout icon={<Icon />}>Main content</TableCellLayout>
</TabeCell>
<TableCell>
<TableCellLayout avatar={{ presence: 'available' }}>Main content</TableCellLayout>
</TabeCell>
</TableRow>
</TableBody>
</Table>
appearance prop