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

[system] Improve breakpoints resolver function #29300

Merged
merged 12 commits into from
Oct 28, 2021
44 changes: 4 additions & 40 deletions packages/mui-lab/src/Masonry/Masonry.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,40 +22,6 @@ const useUtilityClasses = (ownerState) => {
return composeClasses(slots, getMasonryUtilityClass, classes);
};

// compute base for responsive values; e.g.,
// [1,2,3] => {xs: true, sm: true, md: true}
// {xs: 1, sm: 2, md: 3} => {xs: true, sm: true, md: true}
const computeBreakpointsBase = (breakpoints, prop) => {
const base = {};
if (Array.isArray(prop)) {
Object.keys(breakpoints.values).forEach((breakpoint, i, arr) => {
if (i < arr.length) {
base[breakpoint] = true;
}
});
} else {
Object.keys(breakpoints.values).forEach((breakpoint) => {
if (prop[breakpoint] != null) {
base[breakpoint] = true;
}
});
}
return base;
};

// if prop is an array, convert to object; e.g.,
// (base: {xs: true, sm: true, md: true}, prop: [1,2,3]) => {xs: 1, sm: 2, md: 3}
const validatePropValues = (base, prop) => {
const values = {};
if (Array.isArray(prop)) {
Object.keys(base).forEach((breakpoint, i) => {
values[breakpoint] = prop[i];
});
return values;
}
return prop;
};

export const getStyle = ({ ownerState, theme }) => {
let styles = {
width: '100%',
Expand Down Expand Up @@ -94,10 +60,9 @@ export const getStyle = ({ ownerState, theme }) => {
};
}

const spacingBreakpointsBase = computeBreakpointsBase(theme.breakpoints, ownerState.spacing);
const spacingValues = resolveBreakpointValues({
values: validatePropValues(spacingBreakpointsBase, ownerState.spacing),
base: spacingBreakpointsBase,
values: ownerState.spacing,
breakpoints: theme.breakpoints.values,
});

const transformer = createUnarySpacing(theme);
Expand All @@ -120,10 +85,9 @@ export const getStyle = ({ ownerState, theme }) => {
handleBreakpoints({ theme }, spacingValues, spacingStyleFromPropValue),
);

const columnBreakpointsBase = computeBreakpointsBase(theme.breakpoints, ownerState.columns);
const columnValues = resolveBreakpointValues({
values: validatePropValues(columnBreakpointsBase, ownerState.columns),
base: columnBreakpointsBase,
values: ownerState.columns,
breakpoints: theme.breakpoints.values,
});

const columnStyleFromPropValue = (propValue) => {
Expand Down
95 changes: 95 additions & 0 deletions packages/mui-lab/src/Masonry/Masonry.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -213,4 +213,99 @@ describe('<Masonry />', () => {
});
});
});

describe('prop: columns', () => {
it('should generate correct responsive styles regardless of breakpoints order', () => {
const columns = { sm: 5, md: 7, xs: 3 };
const spacing = 1;
expect(
getStyle({
ownerState: {
columns,
spacing,
maxColumnHeight,
},
theme,
}),
).to.deep.equal({
width: '100%',
display: 'flex',
flexFlow: 'column wrap',
alignContent: 'space-between',
boxSizing: 'border-box',
'& > *': {
boxSizing: 'border-box',
margin: parseToNumber(theme.spacing(spacing)) / 2,
},
margin: -(parseToNumber(theme.spacing(spacing)) / 2),
height: maxColumnHeight + parseToNumber(theme.spacing(spacing)),
[`@media (min-width:${theme.breakpoints.values.xs}px)`]: {
'& > *': {
width: `calc(${(100 / columns.xs).toFixed(2)}% - ${theme.spacing(spacing)})`,
},
},
[`@media (min-width:${theme.breakpoints.values.sm}px)`]: {
'& > *': {
width: `calc(${(100 / columns.sm).toFixed(2)}% - ${theme.spacing(spacing)})`,
},
},
[`@media (min-width:${theme.breakpoints.values.md}px)`]: {
'& > *': {
width: `calc(${(100 / columns.md).toFixed(2)}% - ${theme.spacing(spacing)})`,
},
},
});
});
});

describe('prop: spacing', () => {
it('should generate correct responsive styles regardless of breakpoints order', () => {
const columns = 4;
const spacing = { sm: 2, md: 3, xs: 1 };
expect(
getStyle({
ownerState: {
columns,
spacing,
maxColumnHeight,
},
theme,
}),
).to.deep.equal({
width: '100%',
display: 'flex',
flexFlow: 'column wrap',
alignContent: 'space-between',
boxSizing: 'border-box',
'& > *': {
boxSizing: 'border-box',
width: `calc(${(100 / columns).toFixed(2)}% - 0px)`,
},
[`@media (min-width:${theme.breakpoints.values.xs}px)`]: {
'& > *': {
margin: parseToNumber(theme.spacing(spacing.xs)) / 2,
width: `calc(${(100 / columns).toFixed(2)}% - ${theme.spacing(spacing.xs)})`,
},
margin: -(parseToNumber(theme.spacing(spacing.xs)) / 2),
height: maxColumnHeight + parseToNumber(theme.spacing(spacing.xs)),
},
[`@media (min-width:${theme.breakpoints.values.sm}px)`]: {
'& > *': {
margin: parseToNumber(theme.spacing(spacing.sm)) / 2,
width: `calc(${(100 / columns).toFixed(2)}% - ${theme.spacing(spacing.sm)})`,
},
margin: -(parseToNumber(theme.spacing(spacing.sm)) / 2),
height: maxColumnHeight + parseToNumber(theme.spacing(spacing.sm)),
},
[`@media (min-width:${theme.breakpoints.values.md}px)`]: {
'& > *': {
margin: parseToNumber(theme.spacing(spacing.md)) / 2,
width: `calc(${(100 / columns).toFixed(2)}% - ${theme.spacing(spacing.md)})`,
},
margin: -(parseToNumber(theme.spacing(spacing.md)) / 2),
height: maxColumnHeight + parseToNumber(theme.spacing(spacing.md)),
},
});
});
});
});
32 changes: 25 additions & 7 deletions packages/mui-material/src/Grid/Grid.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,15 @@ function generateGrid(globalStyles, theme, breakpoint, ownerState) {
} else {
const columnsBreakpointValues = resolveBreakpointValues({
values: ownerState.columns,
base: theme.breakpoints.values,
breakpoints: theme.breakpoints.values,
});

const columnValue =
typeof columnsBreakpointValues === 'object'
? columnsBreakpointValues[breakpoint]
: columnsBreakpointValues;
// Keep 7 significant numbers.
const width = `${Math.round((size / columnsBreakpointValues[breakpoint]) * 10e7) / 10e5}%`;
const width = `${Math.round((size / columnValue) * 10e7) / 10e5}%`;
let more = {};

if (ownerState.container && ownerState.item && ownerState.columnSpacing !== 0) {
Expand Down Expand Up @@ -92,8 +96,13 @@ function generateGrid(globalStyles, theme, breakpoint, ownerState) {
}
}

function generateDirection({ theme, ownerState }) {
return handleBreakpoints({ theme }, ownerState.direction, (propValue) => {
export function generateDirection({ theme, ownerState }) {
const directionValues = resolveBreakpointValues({
values: ownerState.direction,
breakpoints: theme.breakpoints.values,
});

return handleBreakpoints({ theme }, directionValues, (propValue) => {
const output = {
flexDirection: propValue,
};
Expand All @@ -113,7 +122,12 @@ export function generateRowGap({ theme, ownerState }) {
let styles = {};

if (container && rowSpacing !== 0) {
styles = handleBreakpoints({ theme }, rowSpacing, (propValue) => {
const rowSpacingValues = resolveBreakpointValues({
values: rowSpacing,
breakpoints: theme.breakpoints.values,
});

styles = handleBreakpoints({ theme }, rowSpacingValues, (propValue) => {
const themeSpacing = theme.spacing(propValue);

if (themeSpacing !== '0px') {
Expand All @@ -137,9 +151,13 @@ export function generateColumnGap({ theme, ownerState }) {
let styles = {};

if (container && columnSpacing !== 0) {
styles = handleBreakpoints({ theme }, columnSpacing, (propValue) => {
const themeSpacing = theme.spacing(propValue);
const columnSpacingValues = resolveBreakpointValues({
values: columnSpacing,
breakpoints: theme.breakpoints.values,
});

styles = handleBreakpoints({ theme }, columnSpacingValues, (propValue) => {
const themeSpacing = theme.spacing(propValue);
if (themeSpacing !== '0px') {
return {
width: `calc(100% + ${getOffset(themeSpacing)})`,
Expand Down
Loading