diff --git a/docs/data/joy/components/chip/DeleteButtonChip.js b/docs/data/joy/components/chip/DeleteButtonChip.js index 4d8230a43fd020..c3b093bf6d6bb9 100644 --- a/docs/data/joy/components/chip/DeleteButtonChip.js +++ b/docs/data/joy/components/chip/DeleteButtonChip.js @@ -1,7 +1,7 @@ +import * as React from 'react'; import Box from '@mui/joy/Box'; import Chip from '@mui/joy/Chip'; import ChipDelete from '@mui/joy/ChipDelete'; -import * as React from 'react'; export default function DeleteButtonChip() { return ( @@ -10,14 +10,23 @@ export default function DeleteButtonChip() { size="sm" variant="outlined" color="danger" - endDecorator={} + endDecorator={ alert('Delete')} />} > Remove - }> + alert('Delete')} />} + > Delete - }> + alert('Delete')} />} + > Delete diff --git a/docs/data/joy/components/chip/DeleteButtonChip.tsx b/docs/data/joy/components/chip/DeleteButtonChip.tsx index 4d8230a43fd020..c3b093bf6d6bb9 100644 --- a/docs/data/joy/components/chip/DeleteButtonChip.tsx +++ b/docs/data/joy/components/chip/DeleteButtonChip.tsx @@ -1,7 +1,7 @@ +import * as React from 'react'; import Box from '@mui/joy/Box'; import Chip from '@mui/joy/Chip'; import ChipDelete from '@mui/joy/ChipDelete'; -import * as React from 'react'; export default function DeleteButtonChip() { return ( @@ -10,14 +10,23 @@ export default function DeleteButtonChip() { size="sm" variant="outlined" color="danger" - endDecorator={} + endDecorator={ alert('Delete')} />} > Remove - }> + alert('Delete')} />} + > Delete - }> + alert('Delete')} />} + > Delete diff --git a/docs/data/joy/components/chip/DeleteButtonChip.tsx.preview b/docs/data/joy/components/chip/DeleteButtonChip.tsx.preview deleted file mode 100644 index f422aedbec814a..00000000000000 --- a/docs/data/joy/components/chip/DeleteButtonChip.tsx.preview +++ /dev/null @@ -1,14 +0,0 @@ -} -> - Remove - -}> - Delete - -}> - Delete - \ No newline at end of file diff --git a/docs/data/joy/components/chip/chip.md b/docs/data/joy/components/chip/chip.md index 9595edf652ea6d..3088d6f17d69ed 100644 --- a/docs/data/joy/components/chip/chip.md +++ b/docs/data/joy/components/chip/chip.md @@ -49,7 +49,11 @@ Use the `startDecorator` and/or `endDecorator` props to add supporting icons to ### Delete button To add a delete action inside a chip, use the complementary `ChipDelete` component. -Note that its design will automatically adapt to the parent `Chip`. + +The `onDelete` callback is fired on `ChipDelete` either when: + +- `Backspace`, `Enter` or `Delete` is pressed. +- The `ChipDelete` is clicked. ```jsx import ChipDelete from '@mui/joy/ChipDelete'; diff --git a/packages/mui-joy/src/ChipDelete/ChipDelete.test.js b/packages/mui-joy/src/ChipDelete/ChipDelete.test.js index f4a99280886acb..55fd5647ce561c 100644 --- a/packages/mui-joy/src/ChipDelete/ChipDelete.test.js +++ b/packages/mui-joy/src/ChipDelete/ChipDelete.test.js @@ -1,6 +1,7 @@ import * as React from 'react'; import { expect } from 'chai'; -import { createRenderer, describeConformance } from 'test/utils'; +import { spy } from 'sinon'; +import { createRenderer, describeConformance, act, fireEvent } from 'test/utils'; import { ThemeProvider } from '@mui/joy/styles'; import Chip from '@mui/joy/Chip'; import ChipDelete, { chipDeleteClasses as classes } from '@mui/joy/ChipDelete'; @@ -67,4 +68,32 @@ describe('', () => { expect(getByRole('button')).to.have.class(classes.colorNeutral); }); }); + describe('Chip onDelete', () => { + it('should call onDelete function when backspace, enter or delete is pressed', () => { + const handleDelete = spy(); + const { getByRole } = render( {}} />); + const chipDelete = getByRole('button'); + act(() => { + chipDelete.focus(); + }); + fireEvent.keyDown(chipDelete, { key: 'Backspace' }); + fireEvent.keyDown(chipDelete, { key: 'Enter' }); + fireEvent.keyDown(chipDelete, { key: 'Delete' }); + fireEvent.click(chipDelete); + expect(handleDelete.callCount).to.equal(4); + }); + + it('should not call onDelete function when ChipDelete is disabled', () => { + const handleDelete = spy(); + const { getByRole } = render( + {}} />, + ); + const chipDelete = getByRole('button'); + act(() => { + chipDelete.focus(); + }); + fireEvent.click(chipDelete); + expect(handleDelete.callCount).to.equal(0); + }); + }); }); diff --git a/packages/mui-joy/src/ChipDelete/ChipDelete.tsx b/packages/mui-joy/src/ChipDelete/ChipDelete.tsx index ae77b64ae334c7..3b91c645582bd6 100644 --- a/packages/mui-joy/src/ChipDelete/ChipDelete.tsx +++ b/packages/mui-joy/src/ChipDelete/ChipDelete.tsx @@ -75,6 +75,9 @@ const ChipDelete = React.forwardRef(function ChipDelete(inProps, ref) { variant: variantProp, color: colorProp, disabled: disabledProp, + onKeyDown, + onDelete, + onClick, ...other } = props; const chipContext = React.useContext(ChipContext); @@ -101,6 +104,27 @@ const ChipDelete = React.forwardRef(function ChipDelete(inProps, ref) { const classes = useUtilityClasses(ownerState); + const handleClickDelete = (event: React.MouseEvent) => { + if (!disabled && onDelete) { + onDelete(event); + } + if (onClick) { + onClick(event); + } + }; + + const handleKeyDelete = (event: React.KeyboardEvent) => { + if (['Backspace', 'Enter', 'Delete'].includes(event.key)) { + event.preventDefault(); + if (!disabled && onDelete) { + onDelete(event); + } + } + if (onKeyDown) { + onKeyDown(event); + } + }; + const rootProps = useSlotProps({ elementType: ChipDeleteRoot, getSlotProps: getRootProps, @@ -109,11 +133,14 @@ const ChipDelete = React.forwardRef(function ChipDelete(inProps, ref) { ownerState, additionalProps: { as: component, + onKeyDown: handleKeyDelete, + onClick: handleClickDelete, }, className: classes.root, }); - return {children ?? }; + const { onDelete: excludeOnDelete, ...restOfRootProps } = rootProps; + return {children ?? }; }) as OverridableComponent; ChipDelete.propTypes /* remove-proptypes */ = { @@ -143,6 +170,20 @@ ChipDelete.propTypes /* remove-proptypes */ = { * If `undefined`, the value inherits from the parent chip via a React context. */ disabled: PropTypes.bool, + /** + * @ignore + */ + onClick: PropTypes.func, + /** + * Callback fired when the component is not disabled and either: + * - `Backspace`, `Enter` or `Delete` is pressed. + * - The component is clicked. + */ + onDelete: PropTypes.func, + /** + * @ignore + */ + onKeyDown: PropTypes.func, /** * The system prop that allows defining system overrides as well as additional CSS styles. */ diff --git a/packages/mui-joy/src/ChipDelete/ChipDeleteProps.ts b/packages/mui-joy/src/ChipDelete/ChipDeleteProps.ts index efdb0a9d84b34f..f94220f58e1001 100644 --- a/packages/mui-joy/src/ChipDelete/ChipDeleteProps.ts +++ b/packages/mui-joy/src/ChipDelete/ChipDeleteProps.ts @@ -23,6 +23,14 @@ export interface ChipDeleteTypeMap