Repository: mui/material-ui (TypeScript)
Date: 2020-04-01 06:47:56+00:00
Task type: Bug Fix

Issue:
[Chip] Backspace and delete keys don't work when nesting an input
I'm using a `TextField` component as label prop for a `Chip`. That worked without problems for a long time. Since the type of label is specified with `node`, I assumed that I could insert any child component there. Since I recently updated to the latest version, there is the following problem: I can still add text (cut and paste works too), but I can no longer delete text with the backspace or delete keys.

- [x] The issue is present in the latest release.
- [x] I have searched the [issues](https://github.com/mui-org/material-ui/issues) of this repository and believe that this is not a duplicate.

## Current Behavior 😯

Text **cannot** be deleted using backspace / delete.

## Expected Behavior 🤔

Text **can** be deleted using backspace / delete.

## Steps to Reproduce 🕹

[https://codesandbox.io/s/sleepy-dust-yjiee](https://codesandbox.io/s/sleepy-dust-yjiee)

Steps:

1. Click on the TextField with the current value "foo"
2. Try to delete the word by pressing the backspace key
3. Nothing happens...

## Context 🔦

I built a editable Chip component. First, the user sees a _normal_ Chip. But when he clicks on it, he can edit the content on-the-fly. As I said at the beginning, it worked very well for a long time.

## Your Environment 🌎

| Tech        | Version |
| ----------- | ------- |
| Material-UI |  v4.9.7  |
| React       |  v16.13.0  |
| Browser     |  Google Chrome Version 70.0.3538.77 (Official Build) (64-bit)  |



Issue discussion:
I think it has to do with some of the recent changes to the `Chip` component (see [this commit](https://github.com/mui-org/material-ui/commit/c5f4a86918cf21577f2b18139b362ec9ae01ff6a#diff-e0528e6b6a6fe93ca94abbac71825ac3)).

`handleKeyDown` will do `event.preventDefault()` for backspace and delete keyboard events.

I wonder if my use case is no longer supported or if this is a bug that should be fixed?
@bunste Thanks for the report, I think that we should have the handler of `keyDown` and `keyUp` to use the same logic. What do you think of this patch to align them?

```diff
diff --git a/packages/material-ui/src/Chip/Chip.js b/packages/material-ui/src/Chip/Chip.js
index f80d27599..09abc369d 100644
--- a/packages/material-ui/src/Chip/Chip.js
+++ b/packages/material-ui/src/Chip/Chip.js
@@ -260,6 +260,10 @@ export const styles = (theme) => {
   };
 };

+function isDeleteKeyboardEvent(keyboardEvent) {
+  return keyboardEvent.key === 'Backspace' || keyboardEvent.key === 'Delete';
+}
+
 /**
  * Chips represent complex entities in small blocks, such as a contact.
  */
@@ -295,19 +299,21 @@ const Chip = React.forwardRef(function Chip(props, ref) {
     }
   };

-  const isDeleteKeyboardEvent = (keyboardEvent) =>
-    keyboardEvent.key === 'Backspace' || keyboardEvent.key === 'Delete';
-
   const handleKeyDown = (event) => {
+    if (onKeyDown) {
+      onKeyDown(event);
+    }
+
+    // Ignore events from children of `Chip`.
+    if (event.currentTarget !== event.target) {
+      return;
+    }
+
     if (isDeleteKeyboardEvent(event)) {
       // will be handled in keyUp, otherwise some browsers
       // might init navigation
       event.preventDefault();
     }
-
-    if (onKeyDown) {
-      onKeyDown(event);
-    }
   };

   const handleKeyUp = (event) => {
```

Do you want to work on a pull request? :)
I can work on the this PR? cc @oliviertassinari  @bunste 

Solution patch:
diff --git a/packages/material-ui/src/ButtonBase/ButtonBase.js b/packages/material-ui/src/ButtonBase/ButtonBase.js
--- a/packages/material-ui/src/ButtonBase/ButtonBase.js
+++ b/packages/material-ui/src/ButtonBase/ButtonBase.js
@@ -224,6 +224,7 @@ const ButtonBase = React.forwardRef(function ButtonBase(props, ref) {
       }
     }
   });
+
   const handleKeyUp = useEventCallback((event) => {
     // calling preventDefault in keyUp on a <button> will not dispatch a click event if Space is pressed
     // https://codesandbox.io/s/button-keyup-preventdefault-dn7f0
diff --git a/packages/material-ui/src/Chip/Chip.js b/packages/material-ui/src/Chip/Chip.js
--- a/packages/material-ui/src/Chip/Chip.js
+++ b/packages/material-ui/src/Chip/Chip.js
@@ -260,6 +260,10 @@ export const styles = (theme) => {
   };
 };
 
+function isDeleteKeyboardEvent(keyboardEvent) {
+  return keyboardEvent.key === 'Backspace' || keyboardEvent.key === 'Delete';
+}
+
 /**
  * Chips represent complex entities in small blocks, such as a contact.
  */
@@ -295,11 +299,9 @@ const Chip = React.forwardRef(function Chip(props, ref) {
     }
   };
 
-  const isDeleteKeyboardEvent = (keyboardEvent) =>
-    keyboardEvent.key === 'Backspace' || keyboardEvent.key === 'Delete';
-
   const handleKeyDown = (event) => {
-    if (isDeleteKeyboardEvent(event)) {
+    // Ignore events from children of `Chip`.
+    if (event.currentTarget === event.target && isDeleteKeyboardEvent(event)) {
       // will be handled in keyUp, otherwise some browsers
       // might init navigation
       event.preventDefault();
@@ -311,19 +313,17 @@ const Chip = React.forwardRef(function Chip(props, ref) {
   };
 
   const handleKeyUp = (event) => {
-    if (onKeyUp) {
-      onKeyUp(event);
-    }
-
     // Ignore events from children of `Chip`.
-    if (event.currentTarget !== event.target) {
-      return;
+    if (event.currentTarget === event.target) {
+      if (onDelete && isDeleteKeyboardEvent(event)) {
+        onDelete(event);
+      } else if (event.key === 'Escape' && chipRef.current) {
+        chipRef.current.blur();
+      }
     }
 
-    if (onDelete && isDeleteKeyboardEvent(event)) {
-      onDelete(event);
-    } else if (event.key === 'Escape' && chipRef.current) {
-      chipRef.current.blur();
+    if (onKeyUp) {
+      onKeyUp(event);
     }
   };
 


Test patch:
diff --git a/packages/material-ui/src/Chip/Chip.test.js b/packages/material-ui/src/Chip/Chip.test.js
--- a/packages/material-ui/src/Chip/Chip.test.js
+++ b/packages/material-ui/src/Chip/Chip.test.js
@@ -362,9 +362,7 @@ describe('<Chip />', () => {
       ['Backspace', 'Delete'].forEach((key) => {
         it(`should call onDelete '${key}' is released`, () => {
           const handleDelete = spy();
-          const handleKeyDown = spy((event) => {
-            return event.defaultPrevented;
-          });
+          const handleKeyDown = spy((event) => event.defaultPrevented);
           const { getAllByRole } = render(
             <Chip onClick={() => {}} onKeyDown={handleKeyDown} onDelete={handleDelete} />,
           );
@@ -382,6 +380,17 @@ describe('<Chip />', () => {
           expect(handleDelete.callCount).to.equal(1);
         });
       });
+
+      it('should not prevent default on input', () => {
+        const handleKeyDown = spy((event) => event.defaultPrevented);
+        const { container } = render(<Chip label={<input />} onKeyDown={handleKeyDown} />);
+        const input = container.querySelector('input');
+        input.focus();
+        fireEvent.keyDown(document.activeElement, { key: 'Backspace' });
+
+        // defaultPrevented?
+        expect(handleKeyDown.returnValues[0]).to.equal(false);
+      });
     });
 
     describe('with children that generate events', () => {