Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Joy] Add Chip component #31983

Merged
merged 37 commits into from
Apr 20, 2022
Merged
Show file tree
Hide file tree
Changes from 32 commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
518fedc
Fix small typos in Avatar docs and its demo
hbjORbj Mar 25, 2022
5e2d7ce
Fix typo in Avatar API
hbjORbj Mar 25, 2022
7624709
[Joy] Add Chip component
hbjORbj Mar 25, 2022
8a04d98
[Joy] Add tests for Chip component
hbjORbj Mar 25, 2022
5a24424
Add Chip to components type file
hbjORbj Mar 25, 2022
c27c211
Fix type error
hbjORbj Mar 25, 2022
5b59c4a
Fix disabled prop
hbjORbj Mar 25, 2022
0670132
Fix test
hbjORbj Mar 25, 2022
7dfdbd0
Export Chip
hbjORbj Mar 25, 2022
18d5508
[Joy] Remove label prop
hbjORbj Mar 31, 2022
4739837
Improve demo
hbjORbj Mar 31, 2022
605cd29
Clean up SvgIcon demo
hbjORbj Apr 4, 2022
a9813e5
Replace ButtonBase (material component) with HTML button
hbjORbj Apr 14, 2022
91f49f6
Change default variant / color
hbjORbj Apr 14, 2022
d512b6d
Add ChipDelete component
hbjORbj Apr 14, 2022
d471126
Improve code and style
hbjORbj Apr 14, 2022
61d6603
Tweak style
hbjORbj Apr 14, 2022
7f835f8
Decrease opacity on hover for clickable Chip and ChipDelete
hbjORbj Apr 14, 2022
4214ec5
Add more examples
hbjORbj Apr 14, 2022
7fac8ec
move close icon to internal and add variant,color to ChipDelete
siriwatknp Apr 18, 2022
a2b0756
add ChipColorContext
siriwatknp Apr 18, 2022
85c7824
make avatar radius overridable
siriwatknp Apr 18, 2022
2564736
fix Chip variables and update examples
siriwatknp Apr 18, 2022
f456d39
rename context to include other props
siriwatknp Apr 18, 2022
232f743
remove text variant exclusion
siriwatknp Apr 18, 2022
ceaf33e
fix ChipDelete to have variant, color
siriwatknp Apr 18, 2022
974e818
add componentsProps and remove clickable
siriwatknp Apr 18, 2022
6cdd7a8
add outlineOffset zero
siriwatknp Apr 18, 2022
83301ab
add examples
siriwatknp Apr 18, 2022
078bdbe
minor fixes and add more examples
siriwatknp Apr 18, 2022
b9a87ee
minor fixes and add more examples
siriwatknp Apr 19, 2022
8d7f636
fix classes and add more tests
siriwatknp Apr 19, 2022
063daee
run proptypes
siriwatknp Apr 19, 2022
c6ec4e8
fix typo
siriwatknp Apr 19, 2022
1553d9e
remove unused variable
siriwatknp Apr 19, 2022
31b8cec
fix avatar img radius
siriwatknp Apr 19, 2022
e151fb6
add chip to components experiment playground
siriwatknp Apr 19, 2022
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
2 changes: 1 addition & 1 deletion docs/pages/experiments/joy/avatar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ const props = {
variant: ['contained', 'outlined', 'light'],
} as const;

export default function JoySvgIcon() {
export default function JoyAvatar() {
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
return (
<CssVarsProvider>
Expand Down
285 changes: 285 additions & 0 deletions docs/pages/experiments/joy/chip.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,285 @@
/* eslint-disable no-alert */
import * as React from 'react';
import { CssVarsProvider, useColorScheme } from '@mui/joy/styles';
import Avatar from '@mui/joy/Avatar';
import Box from '@mui/joy/Box';
import Button from '@mui/joy/Button';
import Chip from '@mui/joy/Chip';
import ChipDelete from '@mui/joy/ChipDelete';
import Typography from '@mui/joy/Typography';
import ArrowDropDown from '@mui/icons-material/ArrowDropDown';
import CheckIcon from '@mui/icons-material/Check';
import Moon from '@mui/icons-material/DarkMode';
import Sun from '@mui/icons-material/LightMode';
import ThumbUp from '@mui/icons-material/ThumbUp';
import DeleteForever from '@mui/icons-material/DeleteForever';
import LocationOn from '@mui/icons-material/LocationOn';
import DirectionsBike from '@mui/icons-material/DirectionsBike';
import AlarmOn from '@mui/icons-material/AlarmOn';

const ColorSchemePicker = () => {
const { mode, setMode } = useColorScheme();
const [mounted, setMounted] = React.useState(false);
React.useEffect(() => {
setMounted(true);
}, []);
if (!mounted) {
return null;
}

return (
<Button
variant="outlined"
onClick={() => {
if (mode === 'light') {
setMode('dark');
} else {
setMode('light');
}
}}
sx={{ minWidth: 40, p: '0.25rem' }}
>
{mode === 'light' ? <Moon /> : <Sun />}
</Button>
);
};

const props = {
size: ['sm', 'md', 'lg'],
color: ['primary', 'danger', 'info', 'success', 'warning', 'neutral'],
variant: ['contained', 'outlined', 'light'],
disabled: [true, false],
} as const;

export default function JoyChip() {
return (
<CssVarsProvider>
<Box sx={{ py: 5, maxWidth: { md: 1152, xl: 1536 }, mx: 'auto' }}>
<Box sx={{ px: 3 }}>
<ColorSchemePicker />
</Box>
{/* Examples */}
<Box sx={{ display: 'flex', alignItems: 'center', flexWrap: 'wrap', gap: 5, mt: 5 }}>
<Chip endDecorator={<ChipDelete />}>Benny</Chip>
<Chip disabled onClick={() => {}} endDecorator={<ChipDelete />}>
Benny
</Chip>
<Chip variant="light" startDecorator={<ChipDelete />}>
Benny
</Chip>
<Chip variant="light" onClick={() => alert('hey')}>
Benny
</Chip>
<Chip variant="light" startDecorator={<ChipDelete />} onClick={() => alert('hey')}>
Benny
</Chip>
<Chip
variant="light"
endDecorator={<ChipDelete />}
componentsProps={{
action: {
component: 'a',
href: '#unknown',
},
}}
>
Benny
</Chip>
<Chip
variant="contained"
color="success"
size="sm"
startDecorator={
<Avatar
size="sm"
src={`/static/images/avatar/1.jpg`}
sx={{ m: 'calc(-1 * var(--Chip-paddingBlock))', mr: 0, '--Avatar-size': '28px' }}
/>
}
endDecorator={<CheckIcon />}
>
Benny
</Chip>
<Chip
variant="outlined"
color="neutral"
size="lg"
startDecorator={<Avatar size="sm" src={`/static/images/avatar/1.jpg`} />}
endDecorator={<CheckIcon fontSize="md" sx={{ mr: 0.25 }} />}
onClick={() => alert('hey')}
sx={{ '--Chip-radius': '8px' }}
>
Benny
</Chip>
<Chip
variant="outlined"
color="neutral"
size="lg"
startDecorator={<Avatar src={`/static/images/avatar/1.jpg`} size="sm" />}
endDecorator={<ChipDelete variant="text" />}
sx={{ '--Chip-radius': '8px' }}
>
Benny
</Chip>
<Chip
variant="outlined"
color="danger"
size="sm"
endDecorator={
<ChipDelete color="danger" variant="text">
<DeleteForever />
</ChipDelete>
}
sx={{ '--Chip-radius': '12px' }}
>
Clear
</Chip>
<Chip
variant="outlined"
color="danger"
size="sm"
onClick={() => {}}
endDecorator={<DeleteForever />}
sx={{ '--Chip-radius': '12px', minHeight: '28px' }}
>
Clear
</Chip>
</Box>
<Box
sx={{
display: 'grid',
gridTemplateColumns: 'repeat(auto-fill, minmax(300px, 1fr))',
gap: 5,
my: 3,
}}
>
<Box sx={(theme) => ({ p: 0.5, borderRadius: 'sm', ...theme.variants.outlined.neutral })}>
{['Apple', 'Mango', 'Pineapple', 'Strawberry', 'Mixberry'].map((item) => (
<Chip
key={item}
variant="outlined"
endDecorator={<ChipDelete sx={{ boxShadow: 'xs' }} />}
sx={{ mb: 0.5, mr: 0.5 }}
>
{item}
</Chip>
))}
</Box>
<Box>
<Typography level="h2" fontSize="lg" id="refine-title" mb={1}>
Refine results
</Typography>
<Box
role="group"
aria-labelledby="refine-title"
sx={{ display: 'flex', flexWrap: 'wrap', gap: 1 }}
>
{[
'Top Rated',
'Men',
'Black',
'Red',
'Green',
'Blue',
'Turquoise',
'Shoes',
'Watches',
].map((item) => (
<Chip
key={item}
color="neutral"
variant="outlined"
endDecorator={<ChipDelete variant="text" />}
sx={{ '--Chip-radius': '10px' }}
>
{item}
</Chip>
))}
</Box>
</Box>
<Box sx={{ display: 'flex', gap: 1, alignItems: 'center' }}>
{(['light', 'outlined'] as const).map((variant) => (
<React.Fragment key={variant}>
<Chip
color="neutral"
variant={variant}
size="sm"
endDecorator={<ChipDelete />}
sx={{ '--Chip-delete-size': '16px' }}
>
<LocationOn sx={{ mr: 0.5 }} /> Portland
</Chip>
<Chip
color="neutral"
variant={variant}
size="sm"
endDecorator={<ChipDelete />}
sx={{ '--Chip-delete-size': '16px' }}
>
<DirectionsBike sx={{ mr: 0.5 }} /> Biking
</Chip>
</React.Fragment>
))}
</Box>
<Box sx={{ display: 'flex', gap: 1, alignItems: 'center' }}>
<Chip variant="light" startDecorator={<Sun />}>
Turn on lights
</Chip>
<Chip variant="light" startDecorator={<AlarmOn />}>
Set alarm
</Chip>
</Box>
</Box>
<Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 5 }}>
{/* Without decorators */}
{Object.entries(props).map(([propName, propValue]) => (
<Box
key={propName}
sx={{ display: 'flex', flexDirection: 'column', gap: 5, p: 2, alignItems: 'center' }}
>
<Typography sx={{ textDecoration: 'underline' }}>{propName}</Typography>
{propValue.map((value) => (
<Box
key={`${value}-without-decorator`}
sx={{ display: 'flex', flexDirection: 'column', alignItems: 'center' }}
>
<Chip {...{ [propName]: value }}>{`${propName}: ${value}`}</Chip>
{value !== undefined && (
<Typography level="body3" sx={{ textAlign: 'center', mt: '4px' }}>
{`${value}`}
</Typography>
)}
</Box>
))}
</Box>
))}
{/* With decorators */}
{Object.entries(props).map(([propName, propValue]) => (
<Box
key={propName}
sx={{ display: 'flex', flexDirection: 'column', gap: 5, p: 2, alignItems: 'center' }}
>
<Typography sx={{ textDecoration: 'underline' }}>{propName}</Typography>
{propValue.map((value) => (
<Box
key={`${value}-with-decorator`}
sx={{ display: 'flex', flexDirection: 'column', alignItems: 'center' }}
>
<Chip endDecorator={<ArrowDropDown />} {...{ [propName]: value }}>
<ThumbUp sx={{ mr: 'var(--Chip-gap)' }} />
{`${propName}: ${value}`}
</Chip>
{value !== undefined && (
<Typography level="body3" sx={{ textAlign: 'center', mt: '4px' }}>
{`${value}`}
</Typography>
)}
</Box>
))}
</Box>
))}
</Box>
</Box>
</CssVarsProvider>
);
}
38 changes: 2 additions & 36 deletions docs/pages/experiments/joy/svg-icon.tsx
Original file line number Diff line number Diff line change
@@ -1,45 +1,11 @@
import Moon from '@mui/icons-material/DarkMode';
import Sun from '@mui/icons-material/LightMode';
import Box from '@mui/joy/Box';
import Button from '@mui/joy/Button';
import { CssVarsProvider, useColorScheme } from '@mui/joy/styles';
import SvgIcon from '@mui/joy/SvgIcon';
import Typography from '@mui/joy/Typography';
import * as React from 'react';
// @ts-ignore
import { jsx as _jsx } from 'react/jsx-runtime';

function createSvgIcon(path: any, displayName: any, initialProps?: any) {
const Component = (props: any, ref: any) =>
(
<SvgIcon
data-testid={`${displayName}Icon`}
ref={ref}
viewBox="0 0 24 24"
fontSize="extraLarge"
{...initialProps}
{...props}
sx={{ ...initialProps?.sx, ...props.sx }}
>
{path}
</SvgIcon>
) as unknown as typeof SvgIcon;

// @ts-ignore
return React.memo(React.forwardRef(Component));
}

export const Moon = createSvgIcon(
_jsx('path', {
d: 'M12 3c-4.97 0-9 4.03-9 9s4.03 9 9 9 9-4.03 9-9c0-.46-.04-.92-.1-1.36-.98 1.37-2.58 2.26-4.4 2.26-2.98 0-5.4-2.42-5.4-5.4 0-1.81.89-3.42 2.26-4.4-.44-.06-.9-.1-1.36-.1z',
}),
'DarkMode',
);

export const Sun = createSvgIcon(
_jsx('path', {
d: 'M12 7c-2.76 0-5 2.24-5 5s2.24 5 5 5 5-2.24 5-5-2.24-5-5-5zM2 13h2c.55 0 1-.45 1-1s-.45-1-1-1H2c-.55 0-1 .45-1 1s.45 1 1 1zm18 0h2c.55 0 1-.45 1-1s-.45-1-1-1h-2c-.55 0-1 .45-1 1s.45 1 1 1zM11 2v2c0 .55.45 1 1 1s1-.45 1-1V2c0-.55-.45-1-1-1s-1 .45-1 1zm0 18v2c0 .55.45 1 1 1s1-.45 1-1v-2c0-.55-.45-1-1-1s-1 .45-1 1zM5.99 4.58c-.39-.39-1.03-.39-1.41 0-.39.39-.39 1.03 0 1.41l1.06 1.06c.39.39 1.03.39 1.41 0s.39-1.03 0-1.41L5.99 4.58zm12.37 12.37c-.39-.39-1.03-.39-1.41 0-.39.39-.39 1.03 0 1.41l1.06 1.06c.39.39 1.03.39 1.41 0 .39-.39.39-1.03 0-1.41l-1.06-1.06zm1.06-10.96c.39-.39.39-1.03 0-1.41-.39-.39-1.03-.39-1.41 0l-1.06 1.06c-.39.39-.39 1.03 0 1.41s1.03.39 1.41 0l1.06-1.06zM7.05 18.36c.39-.39.39-1.03 0-1.41-.39-.39-1.03-.39-1.41 0l-1.06 1.06c-.39.39-.39 1.03 0 1.41s1.03.39 1.41 0l1.06-1.06z',
}),
'LightMode',
);

const ColorSchemePicker = () => {
const { mode, setMode } = useColorScheme();
Expand Down
6 changes: 3 additions & 3 deletions packages/mui-joy/src/Avatar/Avatar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ const AvatarRoot = styled('div', {
width: 'var(--Avatar-size)',
height: 'var(--Avatar-size)',
lineHeight: 1,
borderRadius: '50%',
borderRadius: 'var(--Avatar-radius, 50%)',
userSelect: 'none',
},
theme.variants[ownerState.variant!]?.[ownerState.color!],
Expand All @@ -80,7 +80,7 @@ const AvatarImg = styled('img', {
color: 'transparent',
// Hide the image broken icon, only works on Chrome.
textIndent: 10000,
borderRadius: '50%',
borderRadius: 'var(--Avatar-radius, 50%)',
});

const AvatarFallback = styled(Person, {
Expand Down Expand Up @@ -248,7 +248,7 @@ Avatar.propTypes /* remove-proptypes */ = {
imgProps: PropTypes.object,
/**
* The size of the component.
* It accepts theme values between 'xs' and 'xl'.
* It accepts theme values between 'sm' and 'lg'.
* @default 'md'
*/
size: PropTypes /* @typescript-to-proptypes-ignore */.oneOfType([
Expand Down
2 changes: 1 addition & 1 deletion packages/mui-joy/src/Avatar/AvatarProps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export interface AvatarTypeMap<P = {}, D extends React.ElementType = 'div'> {
};
/**
* The size of the component.
* It accepts theme values between 'xs' and 'xl'.
* It accepts theme values between 'sm' and 'lg'.
* @default 'md'
*/
size?: OverridableStringUnion<'sm' | 'md' | 'lg', AvatarPropsSizeOverrides>;
Expand Down
2 changes: 1 addition & 1 deletion packages/mui-joy/src/Avatar/avatarClasses.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export interface AvatarClasses {
colorSuccess: string;
/** Styles applied to the root element if `color="warning"`. */
colorWarning: string;
/** Styles applied to the fallback icon */
/** Styles applied to the fallback icon. */
fallback: string;
/** Styles applied to the root element if `size="sm"`. */
sizeSm: string;
Expand Down
2 changes: 1 addition & 1 deletion packages/mui-joy/src/AvatarGroup/AvatarGroup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ AvatarGroup.propTypes /* remove-proptypes */ = {
component: PropTypes.elementType,
/**
* The size of the component.
* It accepts theme values between 'xs' and 'xl'.
* It accepts theme values between 'sm' and 'lg'.
* @default 'md'
*/
size: PropTypes /* @typescript-to-proptypes-ignore */.oneOfType([
Expand Down
Loading