Repository: mui/material-ui (TypeScript)
Date: 2019-05-03 10:37:52+00:00
Task type: Bug Fix

Issue:
Overriding "selected" style in MuiListItem has side effect on MuiTabs
- [x] This is not a v0.x issue. <!-- (v0.x is no longer maintained) -->
- [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.

## Expected Behavior 🤔
Overriding "selected" style in MuiListItem should only have an effect on MuiListItems.

## Current Behavior 😯
This seems to introduce a global ".selected" style which is also applied to other elements such as MuiTabs.

## Steps to Reproduce 🕹

Link: https://codesandbox.io/embed/0y9n729rll

## Context 🔦

## Your Environment 🌎

| Tech         | Version |
|--------------|---------|
| Material-UI  | ^4.0.0-beta.0  |



Issue discussion:
You can fix this by changing:

```
const theme = createMuiTheme({
  overrides: {
    MuiListItem: {
      selected: { textDecoration: "underline" }
    }
  }
});
```
to:
```
const theme = createMuiTheme({
  overrides: {
    MuiListItem: {
      root: {
        "&$selected": { textDecoration: "underline" }
      }
    }
  }
});
```

@oliviertassinari This brings a bit into question whether the new handling for the [pseudoClasses](https://github.com/oliviertassinari/material-ui/blob/0337af78a5b503164cfeeb2f225b83cb055b2898/packages/material-ui-styles/src/createGenerateClassName/createGenerateClassName.js#L13) is going to be OK. At a minimum, this issue (overrides targeting the pseudoClasses without being further qualified) needs to be pretty clearly documented in the migration guide.

At first I thought this was going to be a duplicate of #15533, but it looks like this will persist even after your changes in #15534.
Actually, we already handle this case. The warning was added in #13919, 5 months ago:

![Capture d’écran 2019-05-02 à 18 32 10](https://user-images.githubusercontent.com/3165635/57091090-af510800-6d08-11e9-9e5c-ceb8b45b3768.png)

Hi,

great, thanks! (This part of theming and customization is a bit hard to grasp; from reading the documentation, it seemed more straightforward.) 


> On 02 May 2019, at 18:23, Ryan Cogswell <notifications@github.com> wrote:
> 
> You can fix this by changing:
> 
> const theme = createMuiTheme({
>   overrides: {
>     MuiListItem: {
>       selected: { textDecoration: "underline" }
>     }
>   }
> });
> to:
> 
> const theme = createMuiTheme({
>   overrides: {
>     MuiListItem: {
>       root: {
>         "&$selected": { textDecoration: "underline" }
>       }
>     }
>   }
> });
> @oliviertassinari <https://github.com/oliviertassinari> This brings a bit into question whether the new handling for the pseudoClasses <https://github.com/oliviertassinari/material-ui/blob/0337af78a5b503164cfeeb2f225b83cb055b2898/packages/material-ui-styles/src/createGenerateClassName/createGenerateClassName.js#L13> is going to be OK. At a minimum, this issue (overrides targeting the pseudoClasses without being further qualified) needs to be pretty clearly documented in the migration guide.
> 
> At first I thought this was going to be a duplicate of #15533 <https://github.com/mui-org/material-ui/issues/15533>, but it looks like this will persist even after your changes in #15534 <https://github.com/mui-org/material-ui/pull/15534>.
> 
> —
> You are receiving this because you authored the thread.
> Reply to this email directly, view it on GitHub <https://github.com/mui-org/material-ui/issues/15565#issuecomment-488738339>, or mute the thread <https://github.com/notifications/unsubscribe-auth/AABUX3HKIJ7SEKS6SU5PZTDPTMIQRANCNFSM4HJ7OXLQ>.
> 



Solution patch:
diff --git a/docs/src/pages/customization/overrides/overrides.md b/docs/src/pages/customization/overrides/overrides.md
--- a/docs/src/pages/customization/overrides/overrides.md
+++ b/docs/src/pages/customization/overrides/overrides.md
@@ -83,51 +83,79 @@ const StyledButton = withStyles({
 
 {{"demo": "pages/customization/overrides/ClassesShorthand.js"}}
 
-### Internal states
+### Pseudo-classes
 
-The components internal states, like *hover*, *focus*, *disabled* and *selected*, are styled with a higher CSS specificity.
+The components special states, like *hover*, *focus*, *disabled* and *selected*, are styled with a higher CSS specificity.
 [Specificity is a weight](https://developer.mozilla.org/en-US/docs/Web/CSS/Specificity) that is applied to a given CSS declaration.
 
-In order to override the components internal states, **you need to increase specificity**.
+In order to override the components special states, **you need to increase specificity**.
 Here is an example with the *disable* state and the button component using a **pseudo-class** (`:disabled`):
 
 ```css
-.MuiButton {
+.Button {
   color: black;
 }
-/* We increase the specificity */
-.MuiButton:disabled {
+.Button:disabled { /* We increase the specificity */
   color: white;
 }
 ```
 
 ```jsx
-<Button disabled className="MuiButton">
+<Button disabled className="Button">
 ```
 
 Sometimes, you can't use a **pseudo-class** as the state doesn't exist in the platform.
 Let's take the menu item component and the *selected* state as an example.
-Aside from accessing nested elements, the `classes` property can be used to customize the internal states of Material-UI components:
+Aside from accessing nested elements, the `classes` property can be used to customize the special states of Material-UI components:
 
 ```css
-.MuiMenuItem {
+.MenuItem {
   color: black;
 }
-/* We increase the specificity */
-.MuiMenuItem.selected {
+.MenuItem.selected { /* We increase the specificity */
   color: blue;
 }
 ```
 
 ```jsx
-<MenuItem selected classes={{ root: 'MuiMenuItem', selected: 'selected' }}>
+<MenuItem selected classes={{ root: 'MenuItem', selected: 'selected' }}>
 ```
 
 #### Why do I need to increase specificity to override one component state?
 
 By design, the CSS specification makes the pseudo-classes increase the specificity.
-For consistency, Material-UI increases the specificity of its custom states.
-This has one important advantage, it's allowing you to cherry-pick the state you want to customize.
+For consistency, Material-UI increases the specificity of its custom pseudo-classes.
+This has one important advantage, it allows you to cherry-pick the state you want to customize.
+
+#### Can I use a different API that requires fewer boilerplate?
+
+Instead of providing values to the `classes` prop API, you can rely on [the global class names](/css-in-js/advanced/#with-material-ui-core) generated by Material-UI.
+It implements all these custom pseudo-classes:
+
+| classes key | Global class name |
+|:--------|:------------------|
+| checked | Mui-checked |
+| disabled | Mui-disabled |
+| error | Mui-error |
+| focused | Mui-focused |
+| focusVisible | Mui-focusVisible |
+| required | Mui-required |
+| expanded | Mui-expanded |
+| selected | Mui-selected |
+
+
+```css
+.MenuItem {
+  color: black;
+}
+.MenuItem.Mui-selected { /* We increase the specificity */
+  color: blue;
+}
+```
+
+```jsx
+<MenuItem selected className="MenuItem">
+```
 
 ### Use `$ruleName` to reference a local rule within the same style sheet
 
diff --git a/packages/material-ui/src/styles/createMuiTheme.js b/packages/material-ui/src/styles/createMuiTheme.js
--- a/packages/material-ui/src/styles/createMuiTheme.js
+++ b/packages/material-ui/src/styles/createMuiTheme.js
@@ -50,7 +50,16 @@ function createMuiTheme(options = {}) {
   };
 
   if (process.env.NODE_ENV !== 'production') {
-    const statesWarning = ['disabled', 'focused', 'selected', 'checked'];
+    const pseudoClasses = [
+      'checked',
+      'disabled',
+      'error',
+      'focused',
+      'focusVisible',
+      'required',
+      'expanded',
+      'selected',
+    ];
     const traverse = (node, parentKey, depth = 1) => {
       let key;
 
@@ -61,7 +70,7 @@ function createMuiTheme(options = {}) {
           if (key.indexOf('Mui') === 0 && child) {
             traverse(child, key, depth + 1);
           }
-        } else if (statesWarning.indexOf(key) !== -1 && Object.keys(child).length > 0) {
+        } else if (pseudoClasses.indexOf(key) !== -1 && Object.keys(child).length > 0) {
           warning(
             false,
             [
@@ -81,14 +90,16 @@ function createMuiTheme(options = {}) {
                 2,
               ),
               '',
-              'https://material-ui.com/customization/overrides#internal-states',
+              'https://next.material-ui.com/customization/overrides/#pseudo-classes',
             ].join('\n'),
           );
+          // Remove the style to prevent global conflicts.
+          node[key] = {};
         }
       }
     };
 
-    traverse(other.overrides);
+    traverse(muiTheme.overrides);
   }
 
   warning(


Test patch:
diff --git a/packages/material-ui/src/styles/createMuiTheme.test.js b/packages/material-ui/src/styles/createMuiTheme.test.js
--- a/packages/material-ui/src/styles/createMuiTheme.test.js
+++ b/packages/material-ui/src/styles/createMuiTheme.test.js
@@ -101,11 +101,15 @@ describe('createMuiTheme', () => {
     });
 
     it('should warn when trying to override an internal state the wrong way', () => {
-      createMuiTheme({ overrides: { Button: { disabled: { color: 'blue' } } } });
+      let theme;
+
+      theme = createMuiTheme({ overrides: { Button: { disabled: { color: 'blue' } } } });
+      assert.strictEqual(Object.keys(theme.overrides.Button.disabled).length, 1);
       assert.strictEqual(consoleErrorMock.args().length, 0);
-      createMuiTheme({ overrides: { MuiButton: { root: { color: 'blue' } } } });
+      theme = createMuiTheme({ overrides: { MuiButton: { root: { color: 'blue' } } } });
       assert.strictEqual(consoleErrorMock.args().length, 0);
-      createMuiTheme({ overrides: { MuiButton: { disabled: { color: 'blue' } } } });
+      theme = createMuiTheme({ overrides: { MuiButton: { disabled: { color: 'blue' } } } });
+      assert.strictEqual(Object.keys(theme.overrides.MuiButton.disabled).length, 0);
       assert.strictEqual(consoleErrorMock.args().length, 1);
       assert.match(
         consoleErrorMock.args()[0][0],