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

[Grid] Support customized breakpoints #31998

Merged
Merged
Show file tree
Hide file tree
Changes from 26 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
53de00f
Adding type Breakpoints
boutahlilsoufiane Mar 26, 2022
371c7e7
Updating propTypes
boutahlilsoufiane Mar 26, 2022
6a73874
Updating Grid api
boutahlilsoufiane Mar 26, 2022
178697b
Exporting type IfEquals
boutahlilsoufiane Mar 26, 2022
f32b093
committing api changes
boutahlilsoufiane Mar 27, 2022
33d111c
Merge branch 'master' of https://github.com/mui-org/material-ui into …
boutahlilsoufiane Mar 27, 2022
e688a8c
Merge branch 'master' of https://github.com/mui-org/material-ui into …
boutahlilsoufiane Apr 14, 2022
6b06cc0
filtering props & generating classes
boutahlilsoufiane May 4, 2022
ee27117
Adding tests for custom breakpoints
boutahlilsoufiane May 4, 2022
c87059a
Adding test for typescript
boutahlilsoufiane May 4, 2022
75759d9
Changing GridSize type to number
boutahlilsoufiane May 4, 2022
fd8d1fa
Generating propTypes
boutahlilsoufiane May 4, 2022
bb79111
Generating doc api
boutahlilsoufiane May 4, 2022
ef9377c
Replace for by Object.keys
boutahlilsoufiane May 7, 2022
04ed235
Fix typo in gridCustomBreakpoints.tsconfig.json
boutahlilsoufiane May 7, 2022
d07b138
Specify the ts error
boutahlilsoufiane May 7, 2022
22c1d19
Run yarn prettier
boutahlilsoufiane May 7, 2022
c06b70b
Change place //@ts-expect-error
boutahlilsoufiane May 7, 2022
35331e2
Fix comment @ts-expect-error
boutahlilsoufiane May 7, 2022
6fd86b3
Add more tests to test suite props: columns
boutahlilsoufiane May 15, 2022
2f36de7
Add breakpoints to ownerState
boutahlilsoufiane May 22, 2022
e29ba06
Remove unecessary classes
boutahlilsoufiane May 22, 2022
9c3cf01
Iterate through keys array
boutahlilsoufiane May 22, 2022
b064a85
Remove unecessary test-id
boutahlilsoufiane May 23, 2022
fdbcf79
Declare ownerState
boutahlilsoufiane May 23, 2022
82b0a20
Remove unecessary variables
boutahlilsoufiane May 25, 2022
aa26c47
Add 2 functions for classes and styles
boutahlilsoufiane Jun 7, 2022
8bdb573
Add tests for styles
boutahlilsoufiane Jun 7, 2022
b00714a
Update docs/src/components/pricing/FAQ.tsx
mnajdova Jun 8, 2022
1a2c30d
Update packages/mui-material/src/Grid/Grid.js
mnajdova Jun 8, 2022
710b088
Merge branch 'master' into grid-support-customized-breakpoints
mnajdova Jun 8, 2022
39dc30f
remove unrelated code in the test
mnajdova Jun 8, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 2 additions & 4 deletions docs/src/components/pricing/FAQ.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,8 @@ const faqData = [
<React.Fragment>
After you purchase a license, you'll receive a license key by email Once you have the
license key, you need to follow the{' '}
<Link href="/x/advanced-components/#license-key-installation">
instructions
</Link>{' '}
necessary to set it up.
<Link href="/x/advanced-components/#license-key-installation">instructions</Link> necessary
to set it up.
mnajdova marked this conversation as resolved.
Show resolved Hide resolved
</React.Fragment>
),
},
Expand Down
125 changes: 73 additions & 52 deletions packages/mui-material/src/Grid/Grid.d.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
import * as React from 'react';
import { ResponsiveStyleValue, SxProps, SystemProps } from '@mui/system';
import {
ResponsiveStyleValue,
SxProps,
SystemProps,
Breakpoint,
BreakpointOverrides,
} from '@mui/system';
import { IfEquals } from '@mui/types';
import { Theme } from '../styles';
import { OverridableComponent, OverrideProps } from '../OverridableComponent';
import { GridClasses } from './gridClasses';
Expand All @@ -12,6 +19,70 @@ export type GridWrap = 'nowrap' | 'wrap' | 'wrap-reverse';

export type GridSize = 'auto' | number;

interface RegularBreakpoints {
/**
* If a number, it sets the number of columns the grid item uses.
* It can't be greater than the total number of columns of the container (12 by default).
* If 'auto', the grid item's width matches its content.
* If false, the prop is ignored.
* If true, the grid item's width grows to use the space available in the grid container.
* The value is applied for the `lg` breakpoint and wider screens if not overridden.
* @default false
*/
lg?: boolean | GridSize;
/**
* If a number, it sets the number of columns the grid item uses.
* It can't be greater than the total number of columns of the container (12 by default).
* If 'auto', the grid item's width matches its content.
* If false, the prop is ignored.
* If true, the grid item's width grows to use the space available in the grid container.
* The value is applied for the `md` breakpoint and wider screens if not overridden.
* @default false
*/
md?: boolean | GridSize;
/**
* If a number, it sets the number of columns the grid item uses.
* It can't be greater than the total number of columns of the container (12 by default).
* If 'auto', the grid item's width matches its content.
* If false, the prop is ignored.
* If true, the grid item's width grows to use the space available in the grid container.
* The value is applied for the `sm` breakpoint and wider screens if not overridden.
* @default false
*/
sm?: boolean | GridSize;
/**
* If a number, it sets the number of columns the grid item uses.
* It can't be greater than the total number of columns of the container (12 by default).
* If 'auto', the grid item's width matches its content.
* If false, the prop is ignored.
* If true, the grid item's width grows to use the space available in the grid container.
* The value is applied for the `xl` breakpoint and wider screens if not overridden.
* @default false
*/
xl?: boolean | GridSize;
/**
* If a number, it sets the number of columns the grid item uses.
* It can't be greater than the total number of columns of the container (12 by default).
* If 'auto', the grid item's width matches its content.
* If false, the prop is ignored.
* If true, the grid item's width grows to use the space available in the grid container.
* The value is applied for all the screen sizes with the lowest priority.
* @default false
*/
xs?: boolean | GridSize;
}

type CustomBreakpoints = Partial<Record<Breakpoint, boolean | GridSize>>;

interface BreakpointOverridesEmpty {}

type Breakpoints = IfEquals<
BreakpointOverrides,
BreakpointOverridesEmpty,
RegularBreakpoints,
CustomBreakpoints
>;

export interface GridTypeMap<P = {}, D extends React.ElementType = 'div'> {
props: P &
SystemProps<Theme> & {
Expand Down Expand Up @@ -51,41 +122,11 @@ export interface GridTypeMap<P = {}, D extends React.ElementType = 'div'> {
* @default false
*/
item?: boolean;
/**
* If a number, it sets the number of columns the grid item uses.
* It can't be greater than the total number of columns of the container (12 by default).
* If 'auto', the grid item's width matches its content.
* If false, the prop is ignored.
* If true, the grid item's width grows to use the space available in the grid container.
* The value is applied for the `lg` breakpoint and wider screens if not overridden.
* @default false
*/
lg?: boolean | GridSize;
/**
* If a number, it sets the number of columns the grid item uses.
* It can't be greater than the total number of columns of the container (12 by default).
* If 'auto', the grid item's width matches its content.
* If false, the prop is ignored.
* If true, the grid item's width grows to use the space available in the grid container.
* The value is applied for the `md` breakpoint and wider screens if not overridden.
* @default false
*/
md?: boolean | GridSize;
/**
* Defines the vertical space between the type `item` components.
* It overrides the value of the `spacing` prop.
*/
rowSpacing?: ResponsiveStyleValue<GridSpacing>;
/**
* If a number, it sets the number of columns the grid item uses.
* It can't be greater than the total number of columns of the container (12 by default).
* If 'auto', the grid item's width matches its content.
* If false, the prop is ignored.
* If true, the grid item's width grows to use the space available in the grid container.
* The value is applied for the `sm` breakpoint and wider screens if not overridden.
* @default false
*/
sm?: boolean | GridSize;
/**
* Defines the space between the type `item` components.
* It can only be used on a type `container` component.
Expand All @@ -102,33 +143,13 @@ export interface GridTypeMap<P = {}, D extends React.ElementType = 'div'> {
* @default 'wrap'
*/
wrap?: GridWrap;
/**
* If a number, it sets the number of columns the grid item uses.
* It can't be greater than the total number of columns of the container (12 by default).
* If 'auto', the grid item's width matches its content.
* If false, the prop is ignored.
* If true, the grid item's width grows to use the space available in the grid container.
* The value is applied for the `xl` breakpoint and wider screens if not overridden.
* @default false
*/
xl?: boolean | GridSize;
/**
* If a number, it sets the number of columns the grid item uses.
* It can't be greater than the total number of columns of the container (12 by default).
* If 'auto', the grid item's width matches its content.
* If false, the prop is ignored.
* If true, the grid item's width grows to use the space available in the grid container.
* The value is applied for all the screen sizes with the lowest priority.
* @default false
*/
xs?: boolean | GridSize;
/**
* If `true`, it sets `min-width: 0` on the item.
* Refer to the limitations section of the documentation to better understand the use case.
* @default false
*/
zeroMinWidth?: boolean;
};
} & Breakpoints;
defaultComponent: D;
}

Expand Down
122 changes: 83 additions & 39 deletions packages/mui-material/src/Grid/Grid.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { unstable_composeClasses as composeClasses } from '@mui/base';
import requirePropFactory from '../utils/requirePropFactory';
import styled from '../styles/styled';
import useThemeProps from '../styles/useThemeProps';
import useTheme from '../styles/useTheme';
import GridContext from './GridContext';
import gridClasses, { getGridUtilityClass } from './gridClasses';

Expand Down Expand Up @@ -184,9 +185,9 @@ export function generateColumnGap({ theme, ownerState }) {
return styles;
}

export function resolveSpacingClasses(spacing, container, styles = {}) {
// in case of grid item or undefined/null or `spacing` <= 0
if (!container || !spacing || spacing <= 0) {
export function resolveSpacingClasses(spacing, breakpoints, styles = {}) {
// undefined/null or `spacing` <= 0
if (!spacing || spacing <= 0) {
return [];
}
// in case of string/number `spacing`
Expand All @@ -197,15 +198,21 @@ export function resolveSpacingClasses(spacing, container, styles = {}) {
return [styles[`spacing-xs-${String(spacing)}`] || `spacing-xs-${String(spacing)}`];
}
// in case of object `spacing`
const { xs, sm, md, lg, xl } = spacing;

return [
Number(xs) > 0 && (styles[`spacing-xs-${String(xs)}`] || `spacing-xs-${String(xs)}`),
Number(sm) > 0 && (styles[`spacing-sm-${String(sm)}`] || `spacing-sm-${String(sm)}`),
Number(md) > 0 && (styles[`spacing-md-${String(md)}`] || `spacing-md-${String(md)}`),
Number(lg) > 0 && (styles[`spacing-lg-${String(lg)}`] || `spacing-lg-${String(lg)}`),
Number(xl) > 0 && (styles[`spacing-xl-${String(xl)}`] || `spacing-xl-${String(xl)}`),
];
const classes = [];

breakpoints.forEach((breakpoint) => {
const value = spacing[breakpoint];

if (Number(value) > 0) {
const className =
styles[`spacing-${breakpoint}-${String(value)}`] ||
`spacing-${breakpoint}-${String(value)}`;

classes.push(className);
}
});

return classes;
}

// Default CSS values
Expand All @@ -218,22 +225,35 @@ const GridRoot = styled('div', {
name: 'MuiGrid',
slot: 'Root',
overridesResolver: (props, styles) => {
const { container, direction, item, lg, md, sm, spacing, wrap, xl, xs, zeroMinWidth } =
props.ownerState;
const { ownerState } = props;
const { container, direction, item, spacing, wrap, zeroMinWidth, breakpoints } = ownerState;

let spacingClasses = [];

// in case of grid item
if (container) {
spacingClasses = resolveSpacingClasses(spacing, breakpoints, styles);
}

const breakpointsStyles = [];

breakpoints.forEach((breakpoint) => {
const value = ownerState[breakpoint];

if (value !== false) {
breakpointsStyles.push(styles[`grid-${breakpoint}-${String(value)}`]);
}
});

return [
styles.root,
container && styles.container,
item && styles.item,
zeroMinWidth && styles.zeroMinWidth,
...resolveSpacingClasses(spacing, container, styles),
...spacingClasses,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't make much sense, why spreading spacing classes here? Based on the logic inside resolveSpacingClasses if styles do not exists for the specific key, a class name is being added.

Not saying is a fault of this PR, but it is wrong. Maybe we should have two different functions, one for classes, and one for adding the styles overrides.

direction !== 'row' && styles[`direction-xs-${String(direction)}`],
wrap !== 'wrap' && styles[`wrap-xs-${String(wrap)}`],
xs !== false && styles[`grid-xs-${String(xs)}`],
sm !== false && styles[`grid-sm-${String(sm)}`],
md !== false && styles[`grid-md-${String(md)}`],
lg !== false && styles[`grid-lg-${String(lg)}`],
xl !== false && styles[`grid-xl-${String(xl)}`],
...breakpointsStyles,
];
},
})(
Expand Down Expand Up @@ -261,23 +281,36 @@ const GridRoot = styled('div', {
);

const useUtilityClasses = (ownerState) => {
const { classes, container, direction, item, lg, md, sm, spacing, wrap, xl, xs, zeroMinWidth } =
const { classes, container, direction, item, spacing, wrap, zeroMinWidth, breakpoints } =
ownerState;

let spacingClasses = [];

// in case of grid item
if (container) {
spacingClasses = resolveSpacingClasses(spacing, breakpoints);
}

const breakpointsClasses = [];

breakpoints.forEach((breakpoint) => {
const value = ownerState[breakpoint];

if (value !== false) {
breakpointsClasses.push(`grid-${breakpoint}-${String(value)}`);
}
});

const slots = {
root: [
'root',
container && 'container',
item && 'item',
zeroMinWidth && 'zeroMinWidth',
...resolveSpacingClasses(spacing, container),
...spacingClasses,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We would use the classes util here only

direction !== 'row' && `direction-xs-${String(direction)}`,
wrap !== 'wrap' && `wrap-xs-${String(wrap)}`,
xs !== false && `grid-xs-${String(xs)}`,
sm !== false && `grid-sm-${String(sm)}`,
md !== false && `grid-md-${String(md)}`,
lg !== false && `grid-lg-${String(lg)}`,
xl !== false && `grid-xl-${String(xl)}`,
...breakpointsClasses,
],
};

Expand All @@ -286,6 +319,8 @@ const useUtilityClasses = (ownerState) => {

const Grid = React.forwardRef(function Grid(inProps, ref) {
const themeProps = useThemeProps({ props: inProps, name: 'MuiGrid' });
const { breakpoints } = useTheme();

const props = extendSxProp(themeProps);
const {
className,
Expand All @@ -295,16 +330,11 @@ const Grid = React.forwardRef(function Grid(inProps, ref) {
container = false,
direction = 'row',
item = false,
lg = false,
md = false,
rowSpacing: rowSpacingProp,
sm = false,
spacing = 0,
wrap = 'wrap',
xl = false,
xs = false,
zeroMinWidth = false,
...other
...propsRest
} = props;

const rowSpacing = rowSpacingProp || spacing;
Expand All @@ -315,21 +345,31 @@ const Grid = React.forwardRef(function Grid(inProps, ref) {
// columns set with default breakpoint unit of 12
const columns = container ? columnsProp || 12 : columnsContext;

const breakpointsValues = {};
const propsRestFiltered = { ...propsRest };

breakpoints.keys.forEach((key) => {
if (key in propsRest) {
breakpointsValues[key] = propsRest[key];
delete propsRestFiltered[key];
} else {
breakpointsValues[key] = false;
}
});

const ownerState = {
...props,
columns,
container,
direction,
item,
lg,
md,
sm,
rowSpacing,
columnSpacing,
wrap,
xl,
xs,
zeroMinWidth,
spacing,
...breakpointsValues,
breakpoints: breakpoints.keys,
};

const classes = useUtilityClasses(ownerState);
Expand All @@ -341,7 +381,7 @@ const Grid = React.forwardRef(function Grid(inProps, ref) {
className={clsx(classes.root, className)}
as={component}
ref={ref}
{...other}
{...propsRestFiltered}
/>
</GridContext.Provider>
);
Expand Down Expand Up @@ -410,6 +450,10 @@ Grid.propTypes /* remove-proptypes */ = {
* @default false
*/
item: PropTypes.bool,
/**
* @ignore
*/
key: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
/**
* If a number, it sets the number of columns the grid item uses.
* It can't be greater than the total number of columns of the container (12 by default).
Expand Down