Repository: mui/material-ui (TypeScript)
Date: 2021-06-27 11:47:03+00:00
Task type: Bug Fix

Issue:
[Box] doesn't apply maxWidth prop
Box doesn't apply maxWidth prop
```
    <Box maxWidth="xs">
      Hello World
    </Box>
```
CodeSandBox:
https://codesandbox.io/s/hardcore-platform-tyfvw?file=/index.js:131-183

Mui version: v4.9.8 ✓


Issue discussion:
@PetrShchukin What lead you to believe this API is supported?
In this page https://material-ui.com/components/box/#box, the latest sentence is "Any other properties supplied will be used by the style functions or spread to the root element." I'd expect maxWidth to be support as width is supported.
@PetrShchukin Ok thanks. I can't think of any way we could have solved the root cause, so I'm simply going to answer the direct question.

xs is a breakpoint value (and is `0px`), it's not accepted as a final value. This would work

```jsx
import React from "react";
import ReactDOM from "react-dom";
import Box from "@material-ui/core/Box";

function App() {
  return (
    <Box maxWidth={100} bgcolor="red">
      Hello World
    </Box>
  );
}

ReactDOM.render(<App />, document.querySelector("#app"));
```

https://codesandbox.io/s/quirky-moon-ss53u

In v5, you can also do:

```jsx
import React from "react";
import ReactDOM from "react-dom";
import Box from "@material-ui/core/Box";

function App() {
  return (
    <Box maxWidth={(theme) => theme.breakpoints.values.sm} bgcolor="red">
      Hello World
    </Box>
  );
}

ReactDOM.render(<App />, document.querySelector("#app"));
```

https://codesandbox.io/s/agitated-agnesi-f5rfs
@oliviertassinari how to use your v5 example with Typescript? I tried here https://codesandbox.io/s/brave-pine-mhmf2?file=/index.tsx but I'm receiving the following TS Error:

```No overload matches this call.
  Overload 1 of 2, '(props: { component: ElementType<any>; } & { borderTop?: ResponsiveStyleValue<string | number | (string & {}) | MaxWidth<Key>[]>; borderRight?: ResponsiveStyleValue<...>; ... 62 more ...; textAlign?: ResponsiveStyleValue<...>; } & ... 4 more ... & Pick<...>): Element', gave the following error.
    Type '(theme: Theme) => number' is not assignable to type 'ResponsiveStyleValue<string | number | (string & {}) | MaxWidth<Key>[]>'.
      Type '(theme: Theme) => number' is not assignable to type '{ [key: string]: string | number | (string & {}) | MaxWidth<Key>[]; }'.
        Index signature is missing in type '(theme: Theme) => number'.
  Overload 2 of 2, '(props: DefaultComponentProps<BoxTypeMap<{}, "div">>): Element', gave the following error.
    Type '(theme: Theme) => number' is not assignable to type 'ResponsiveStyleValue<string | number | (string & {}) | MaxWidth<Key>[]>'.
      Type '(theme: Theme) => number' is not assignable to type '{ [key: string]: string | number | (string & {}) | MaxWidth<Key>[]; }'.ts(2769)
index.tsx(8, 19): Did you mean to call this expression?
```

Is the type definition correct?

It would be nice use the breakpoints tokens as strings, just as Chakra-UI has implemented, e.g.:

```jsx
import React from "react";
import ReactDOM from "react-dom";
import Box from "@material-ui/core/Box";

function App() {
  return (
    <Box maxWidth="sm" bgcolor="red">
      Hello World
    </Box>
  );
}

ReactDOM.render(<App />, document.querySelector("#app"));
````

@matheusmb Thanks for raising the TypeScript issue. I can reproduce it too. @mnajdova should we fix it?

```diff
diff --git a/packages/material-ui-system/src/index.d.ts b/packages/material-ui-system/src/index.d.ts
index 6473f986fc..6d37237c87 100644
--- a/packages/material-ui-system/src/index.d.ts
+++ b/packages/material-ui-system/src/index.d.ts
@@ -4,7 +4,7 @@ import { CSSProperties } from './CSSProperties';
 import {
   OverwriteCSSProperties,
   AliasesCSSProperties,
-  StandardCSSProperties,
+  AllSystemCSSProperties,
 } from './styleFunctionSx';
 // disable automatic export
 export {};
@@ -291,36 +291,34 @@ export interface CSSOthersObjectForCSSObject {
   [propertiesName: string]: CSSInterpolation;
 }

-export type CustomSystemProps = OverwriteCSSProperties & AliasesCSSProperties;
+export interface CustomSystemProps extends AliasesCSSProperties, OverwriteCSSProperties {}

-export type SimpleSystemKeys = keyof Omit<
-  PropsFor<
-    ComposedStyleFunction<
-      [
-        typeof borders,
-        typeof display,
-        typeof flexbox,
-        typeof grid,
-        typeof palette,
-        typeof positions,
-        typeof shadows,
-        typeof sizing,
-        typeof spacing,
-        typeof typography,
-      ]
-    >
-  >,
-  keyof CustomSystemProps
+export type SimpleSystemKeys = keyof PropsFor<
+  ComposedStyleFunction<
+    [
+      typeof borders,
+      typeof display,
+      typeof flexbox,
+      typeof grid,
+      typeof palette,
+      typeof positions,
+      typeof shadows,
+      typeof sizing,
+      typeof spacing,
+      typeof typography,
+    ]
+  >
 >;

-// The SimpleSystemKeys are subset of the StandardCSSProperties, so this should be ok
-// This is needed as these are used as keys inside StandardCSSProperties
-type StandardSystemKeys = Extract<SimpleSystemKeys, keyof StandardCSSProperties>;
+// The SimpleSystemKeys are subset of the AllSystemCSSProperties, so this should be ok
+// This is needed as these are used as keys inside AllSystemCSSProperties
+type StandardSystemKeys = Extract<SimpleSystemKeys, keyof AllSystemCSSProperties>;

-export type SystemProps = {
-  [K in StandardSystemKeys]?: ResponsiveStyleValue<StandardCSSProperties[K]>;
-} &
-  CustomSystemProps;
+export type SystemProps<Theme extends object = {}> = {
+  [K in StandardSystemKeys]?:
+    | ResponsiveStyleValue<AllSystemCSSProperties[K]>
+    | ((theme: Theme) => ResponsiveStyleValue<AllSystemCSSProperties[K]>);
+};

 export {
   default as unstable_styleFunctionSx,
diff --git a/packages/material-ui-system/src/styleFunctionSx/styleFunctionSx.d.ts b/packages/material-ui-system/src/styleFunctionSx/styleFunctionSx.
d.ts
index 2a30a79bcc..5800fbedc3 100644
--- a/packages/material-ui-system/src/styleFunctionSx/styleFunctionSx.d.ts
+++ b/packages/material-ui-system/src/styleFunctionSx/styleFunctionSx.d.ts
@@ -26,13 +26,13 @@ export interface CSSSelectorObject<Theme extends object = {}> {

 /**
  * Map of all available CSS properties (including aliases) and their raw value.
- * Only used internally to map CCS properties to input types (responsive value,
+ * Only used internally to map CSS properties to input types (responsive value,
  * theme function or nested) in `SystemCssProperties`.
  */
 export interface AllSystemCSSProperties
   extends Omit<StandardCSSProperties, keyof OverwriteCSSProperties>,
-    AliasesCSSProperties,
-    OverwriteCSSProperties {}
+    OverwriteCSSProperties,
+    AliasesCSSProperties {}

 export type SystemCssProperties<Theme extends object = {}> = {
   [K in keyof AllSystemCSSProperties]:
diff --git a/packages/material-ui/src/Box/Box.d.ts b/packages/material-ui/src/Box/Box.d.ts
index d9c41651a2..f4a5104ede 100644
--- a/packages/material-ui/src/Box/Box.d.ts
+++ b/packages/material-ui/src/Box/Box.d.ts
@@ -5,7 +5,7 @@ import { Theme } from '../styles/createTheme';

 export interface BoxTypeMap<P = {}, D extends React.ElementType = 'div'> {
   props: P &
-    SystemProps & {
+    SystemProps<Theme> & {
       children?: React.ReactNode;
       component?: React.ElementType;
       ref?: React.Ref<unknown>;
diff --git a/packages/material-ui/src/Grid/Grid.d.ts b/packages/material-ui/src/Grid/Grid.d.ts
index f7e35fbc2f..4a5b602225 100644
--- a/packages/material-ui/src/Grid/Grid.d.ts
+++ b/packages/material-ui/src/Grid/Grid.d.ts
@@ -14,7 +14,7 @@ export type GridSize = 'auto' | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12

 export interface GridTypeMap<P = {}, D extends React.ElementType = 'div'> {
   props: P &
-    SystemProps & {
+    SystemProps<Theme> & {
       /**
        * The content of the component.
        */
diff --git a/packages/material-ui/src/Stack/Stack.d.ts b/packages/material-ui/src/Stack/Stack.d.ts
index 1703f2a4b5..e65d46be71 100644
--- a/packages/material-ui/src/Stack/Stack.d.ts
+++ b/packages/material-ui/src/Stack/Stack.d.ts
@@ -5,7 +5,7 @@ import { Theme } from '../styles/createTheme';

 export interface StackTypeMap<P = {}, D extends React.ElementType = 'div'> {
   props: P &
-    SystemProps & {
+    SystemProps<Theme> & {
       ref?: React.Ref<unknown>;
       /**
        * The content of the component.
diff --git a/packages/material-ui/src/Typography/Typography.d.ts b/packages/material-ui/src/Typography/Typography.d.ts
index eecbc311fb..721e634f5b 100644
--- a/packages/material-ui/src/Typography/Typography.d.ts
+++ b/packages/material-ui/src/Typography/Typography.d.ts
@@ -10,7 +10,7 @@ export interface TypographyPropsVariantOverrides {}

 export interface TypographyTypeMap<P = {}, D extends React.ElementType = 'span'> {
   props: P &
-    SystemProps & {
+    SystemProps<Theme> & {
       /**
        * Set the text-align on the component.
        * @default 'inherit'
```

--- 

Regarding native support of the breakpoint values. It could make sense. We have these props on the Dialog and Container components. We would need to do:

```diff
diff --git a/packages/material-ui-system/src/sizing.js b/packages/material-ui-system/src/sizing.js
index f957b440be..f9227c74fe 100644
--- a/packages/material-ui-system/src/sizing.js
+++ b/packages/material-ui-system/src/sizing.js
@@ -1,5 +1,6 @@
 import style from './style';
 import compose from './compose';
+import { handleBreakpoints } from './breakpoints';

 function transform(value) {
   return value <= 1 ? `${value * 100}%` : value;
@@ -10,10 +11,20 @@ export const width = style({
   transform,
 });

-export const maxWidth = style({
-  prop: 'maxWidth',
-  transform,
-});
+export const maxWidth = (props) => {
+  if (props.maxWidth) {
+    const styleFromPropValue = (propValue) => {
+      const breakpoint = props.theme.breakpoints.values[propValue];
+      return {
+        maxWidth: breakpoint || transform(propValue),
+      };
+    }
+    return handleBreakpoints(props, props.maxWidth, styleFromPropValue);
+  }
+  return null;
+};
+
+maxWidth.filterProps = ['maxWidth'];

 export const minWidth = style({
   prop: 'minWidth',
```

to have 

```jsx
import React from 'react';
import Box from '@material-ui/core/Box';

export default function App() {
  return (
    <Box maxWidth="sm" bgcolor="red" m={[1, 2]}>
      Hello World
    </Box>
  );
}
```

it's for instance supported in Tailwind: https://tailwindcss.com/docs/max-width.
I like this idea. Using the breakpoints as values makes sense 👍 
@oliviertassinari Can I work on this?
Hey @oliviertassinari I opened a PR which only fixes the TypeScript issue.
I have not implemented the tailwind kind of functionality on the `maxWidth` prop. Should I implement it in a different PR?
> Should I implement it in a different PR?

@ansh-saini Doing it in a second PR sounds great. 
Alright, thanks.

Solution patch:
diff --git a/docs/src/pages/system/sizing/sizing.md b/docs/src/pages/system/sizing/sizing.md
--- a/docs/src/pages/system/sizing/sizing.md
+++ b/docs/src/pages/system/sizing/sizing.md
@@ -4,13 +4,21 @@
 
 ## Supported values
 
-The sizing style functions support different property input types:
+The sizing properties: `width`, `height`, `minHeight`, `maxHeight`, `minWidth`, and `maxWidth` are using the following custom transform function for the value:
+
+```js
+function transform(value) {
+  return value <= 1 ? `${value * 100}%` : value;
+}
+```
+
+If the value is between [0, 1], it's converted to percent.
+Otherwise, it is directly set on the CSS property.
 
 {{"demo": "pages/system/sizing/Values.js", "defaultCodeOpen": false}}
 
 ```jsx
-// Numbers in [0,1] are multiplied by 100 and converted to % values.
-<Box sx={{ width: 1/4 }}>
+<Box sx={{ width: 1/4 }}> // Equivalent to width: '25%'
 <Box sx={{ width: 300 }}> // Numbers are converted to pixel values.
 <Box sx={{ width: '75%' }}> // String values are used as raw CSS.
 <Box sx={{ width: 1 }}> // 100%
@@ -28,6 +36,15 @@ The sizing style functions support different property input types:
 <Box sx={{ width: 'auto' }}>…
 ```
 
+### Max-width
+
+The max-width property allows setting a constraint on your breakpoints.
+In this example, the value resolves to [`theme.breakpoints.values.md`](/customization/default-theme/?expand-path=$.breakpoints.values).
+
+```jsx
+<Box sx={{ maxWidth: 'md' }}>…
+```
+
 ## Height
 
 {{"demo": "pages/system/sizing/Height.js", "defaultCodeOpen": false}}
@@ -45,12 +62,12 @@ The sizing style functions support different property input types:
 import { sizing } from '@material-ui/system';
 ```
 
-| Import name | Prop        | CSS property | Theme key |
-| :---------- | :---------- | :----------- | :-------- |
-| `width`     | `width`     | `width`      | none      |
-| `maxWidth`  | `maxWidth`  | `max-width`  | none      |
-| `minWidth`  | `minWidth`  | `min-width`  | none      |
-| `height`    | `height`    | `height`     | none      |
-| `maxHeight` | `maxHeight` | `max-height` | none      |
-| `minHeight` | `minHeight` | `min-height` | none      |
-| `boxSizing` | `boxSizing` | `box-sizing` | none      |
+| Import name | Prop        | CSS property | Theme key                                                                                    |
+| :---------- | :---------- | :----------- | :------------------------------------------------------------------------------------------- |
+| `width`     | `width`     | `width`      | none                                                                                         |
+| `maxWidth`  | `maxWidth`  | `max-width`  | [`theme.breakpoints.values`](/customization/default-theme/?expand-path=$.breakpoints.values) |
+| `minWidth`  | `minWidth`  | `min-width`  | none                                                                                         |
+| `height`    | `height`    | `height`     | none                                                                                         |
+| `maxHeight` | `maxHeight` | `max-height` | none                                                                                         |
+| `minHeight` | `minHeight` | `min-height` | none                                                                                         |
+| `boxSizing` | `boxSizing` | `box-sizing` | none                                                                                         |
diff --git a/docs/src/pages/system/the-sx-prop/the-sx-prop.md b/docs/src/pages/system/the-sx-prop/the-sx-prop.md
--- a/docs/src/pages/system/the-sx-prop/the-sx-prop.md
+++ b/docs/src/pages/system/the-sx-prop/the-sx-prop.md
@@ -38,7 +38,7 @@ The `borderRadius` properties multiples the value it receives by the `theme.shap
 // equivalent to borderRadius: theme => 2 * theme.shape.borderRadius
 ```
 
-_Head to the [borders page](/system/borders/) for more examples._
+_Head to the [borders page](/system/borders/) for more details._
 
 ### Display
 
@@ -48,7 +48,7 @@ The `displayPrint` property allows you to specify CSS `display` value, that will
 <Box sx={{ displayPrint: 'none' }} /> // equivalent to '@media print': { display: 'none' }
 ```
 
-_Head to the [display page](/system/display/) for more examples._
+_Head to the [display page](/system/display/) for more details._
 
 ### Grid
 
@@ -59,7 +59,7 @@ The grid CSS properties `gap`, `rowGap` and `columnGap` multiply the values they
 // equivalent to gap: theme => theme.spacing(2)
 ```
 
-_Head to the [grid page](/system/grid/) for more examples._
+_Head to the [grid page](/system/grid/) for more details._
 
 ### Palette
 
@@ -77,7 +77,7 @@ The `backgroundColor` property is also available trough its alias `bgcolor`.
 // equivalent to backgroundColor: theme => theme.palette.primary.main
 ```
 
-_Head to the [palette page](/system/palette/) for more examples._
+_Head to the [palette page](/system/palette/) for more details._
 
 ### Positions
 
@@ -88,7 +88,7 @@ The `zIndex` property maps its value to the `theme.zIndex` value.
 // equivalent to backgroundColor: theme => theme.zIndex.tooltip
 ```
 
-_Head to the [positions page](/system/positions/) for more examples._
+_Head to the [positions page](/system/positions/) for more details._
 
 ### Shadows
 
@@ -99,7 +99,7 @@ The `boxShadow` property maps its value to the `theme.shadows` value.
 // equivalent to boxShadow: theme => theme.shadows[1]
 ```
 
-_Head to the [shadows page](/system/shadows/) for more examples._
+_Head to the [shadows page](/system/shadows/) for more details._
 
 ### Sizing
 
@@ -111,14 +111,15 @@ function transform(value) {
 }
 ```
 
-Basically, if the value is between [0, 1] it is converted to percent, otherwise it is directly set on the CSS property.
+If the value is between [0, 1], it's converted to percent.
+Otherwise, it is directly set on the CSS property.
 
 ```jsx
-<Box sx={{ width: 0.5 }} /> // equivalent to width: '50%'
+<Box sx={{ width: 1/2 }} /> // equivalent to width: '50%'
 <Box sx={{ width: 20 }} /> // equivalent to width: '20px'
 ```
 
-_Head to the [sizing page](/system/sizing/) for more examples._
+_Head to the [sizing page](/system/sizing/) for more details._
 
 ### Spacing
 
@@ -148,7 +149,7 @@ The following aliases are availabel for the spacing properties:
 | `px` | `padding-left`, `padding-right` |
 | `py` | `padding-top`, `padding-bottom` |
 
-_Head to the [spacing page](/system/spacing/) for more examples._
+_Head to the [spacing page](/system/spacing/) for more details._
 
 ### Typography
 
@@ -173,7 +174,7 @@ There is additional `typography` prop available, which sets all values defined i
 // equivalent to { ...theme.typography.body1 }
 ```
 
-_Head to the [typography page](/system/typography/) for more examples._
+_Head to the [typography page](/system/typography/) for more details._
 
 ## Responsive values
 
diff --git a/packages/material-ui-system/src/sizing.js b/packages/material-ui-system/src/sizing.js
--- a/packages/material-ui-system/src/sizing.js
+++ b/packages/material-ui-system/src/sizing.js
@@ -1,5 +1,6 @@
 import style from './style';
 import compose from './compose';
+import { handleBreakpoints } from './breakpoints';
 
 function transform(value) {
   return value <= 1 ? `${value * 100}%` : value;
@@ -10,10 +11,19 @@ export const width = style({
   transform,
 });
 
-export const maxWidth = style({
-  prop: 'maxWidth',
-  transform,
-});
+export const maxWidth = (props) => {
+  if (props.maxWidth) {
+    const styleFromPropValue = (propValue) => {
+      const breakpoint = props.theme.breakpoints.values[propValue];
+      return {
+        maxWidth: breakpoint || transform(propValue),
+      };
+    };
+    return handleBreakpoints(props, props.maxWidth, styleFromPropValue);
+  }
+  return null;
+};
+maxWidth.filterProps = ['maxWidth'];
 
 export const minWidth = style({
   prop: 'minWidth',


Test patch:
diff --git a/packages/material-ui-system/src/styleFunctionSx/styleFunctionSx.test.js b/packages/material-ui-system/src/styleFunctionSx/styleFunctionSx.test.js
--- a/packages/material-ui-system/src/styleFunctionSx/styleFunctionSx.test.js
+++ b/packages/material-ui-system/src/styleFunctionSx/styleFunctionSx.test.js
@@ -63,6 +63,7 @@ describe('styleFunctionSx', () => {
           fontFamily: 'default',
           fontWeight: 'light',
           fontSize: 'fontSize',
+          maxWidth: 'sm',
           displayPrint: 'block',
           border: [1, 2, 3, 4, 5],
         },
@@ -76,6 +77,7 @@ describe('styleFunctionSx', () => {
         fontFamily: '"Roboto", "Helvetica", "Arial", sans-serif',
         fontWeight: 300,
         fontSize: 14,
+        maxWidth: 600,
         '@media print': {
           display: 'block',
         },