From 0a0888ec2330467e4c3c892eab53e5db6f1dbbf7 Mon Sep 17 00:00:00 2001 From: Benny Joo Date: Sun, 25 Jul 2021 20:03:20 +0100 Subject: [PATCH 01/59] Initial implementation of responsive Masonry component --- docs/pages/api-docs/masonry-item.js | 23 +++ docs/pages/api-docs/masonry-item.json | 17 ++ docs/pages/api-docs/masonry.js | 19 ++ docs/pages/api-docs/masonry.json | 31 ++++ docs/pages/components/masonry.js | 11 ++ docs/src/pages.ts | 1 + .../pages/components/masonry/BasicMasonry.js | 28 +++ .../pages/components/masonry/BasicMasonry.tsx | 28 +++ .../pages/components/masonry/ImageMasonry.js | 103 +++++++++++ .../pages/components/masonry/ImageMasonry.tsx | 103 +++++++++++ docs/src/pages/components/masonry/masonry.md | 21 +++ docs/src/pagesApi.js | 2 + .../masonry-item/masonry-item-de.json | 10 + .../masonry-item/masonry-item-es.json | 10 + .../masonry-item/masonry-item-fr.json | 10 + .../masonry-item/masonry-item-ja.json | 10 + .../masonry-item/masonry-item-pt.json | 10 + .../masonry-item/masonry-item-ru.json | 10 + .../masonry-item/masonry-item-zh.json | 10 + .../api-docs/masonry-item/masonry-item.json | 10 + .../api-docs/masonry/masonry-de.json | 12 ++ .../api-docs/masonry/masonry-es.json | 12 ++ .../api-docs/masonry/masonry-fr.json | 12 ++ .../api-docs/masonry/masonry-ja.json | 12 ++ .../api-docs/masonry/masonry-pt.json | 12 ++ .../api-docs/masonry/masonry-ru.json | 12 ++ .../api-docs/masonry/masonry-zh.json | 12 ++ .../api-docs/masonry/masonry.json | 12 ++ docs/translations/translations.json | 1 + .../material-ui-lab/src/Masonry/Masonry.d.ts | 50 +++++ .../material-ui-lab/src/Masonry/Masonry.js | 173 ++++++++++++++++++ .../src/Masonry/Masonry.test.js | 23 +++ .../src/Masonry/MasonryContext.js | 12 ++ .../material-ui-lab/src/Masonry/index.d.ts | 5 + packages/material-ui-lab/src/Masonry/index.js | 3 + .../src/Masonry/masonryClasses.ts | 16 ++ .../src/MasonryItem/MasonryItem.d.ts | 40 ++++ .../src/MasonryItem/MasonryItem.js | 154 ++++++++++++++++ .../src/MasonryItem/MasonryItem.test.js | 23 +++ .../src/MasonryItem/index.d.ts | 5 + .../material-ui-lab/src/MasonryItem/index.js | 3 + .../src/MasonryItem/masonryItemClasses.ts | 16 ++ .../material-ui-system/src/breakpoints.js | 23 +++ packages/material-ui-system/src/index.js | 2 +- packages/material-ui/src/Grid/Grid.js | 27 +-- packages/material-ui/src/Stack/Stack.js | 22 +-- 46 files changed, 1117 insertions(+), 44 deletions(-) create mode 100644 docs/pages/api-docs/masonry-item.js create mode 100644 docs/pages/api-docs/masonry-item.json create mode 100644 docs/pages/api-docs/masonry.js create mode 100644 docs/pages/api-docs/masonry.json create mode 100644 docs/pages/components/masonry.js create mode 100644 docs/src/pages/components/masonry/BasicMasonry.js create mode 100644 docs/src/pages/components/masonry/BasicMasonry.tsx create mode 100644 docs/src/pages/components/masonry/ImageMasonry.js create mode 100644 docs/src/pages/components/masonry/ImageMasonry.tsx create mode 100644 docs/src/pages/components/masonry/masonry.md create mode 100644 docs/translations/api-docs/masonry-item/masonry-item-de.json create mode 100644 docs/translations/api-docs/masonry-item/masonry-item-es.json create mode 100644 docs/translations/api-docs/masonry-item/masonry-item-fr.json create mode 100644 docs/translations/api-docs/masonry-item/masonry-item-ja.json create mode 100644 docs/translations/api-docs/masonry-item/masonry-item-pt.json create mode 100644 docs/translations/api-docs/masonry-item/masonry-item-ru.json create mode 100644 docs/translations/api-docs/masonry-item/masonry-item-zh.json create mode 100644 docs/translations/api-docs/masonry-item/masonry-item.json create mode 100644 docs/translations/api-docs/masonry/masonry-de.json create mode 100644 docs/translations/api-docs/masonry/masonry-es.json create mode 100644 docs/translations/api-docs/masonry/masonry-fr.json create mode 100644 docs/translations/api-docs/masonry/masonry-ja.json create mode 100644 docs/translations/api-docs/masonry/masonry-pt.json create mode 100644 docs/translations/api-docs/masonry/masonry-ru.json create mode 100644 docs/translations/api-docs/masonry/masonry-zh.json create mode 100644 docs/translations/api-docs/masonry/masonry.json create mode 100644 packages/material-ui-lab/src/Masonry/Masonry.d.ts create mode 100644 packages/material-ui-lab/src/Masonry/Masonry.js create mode 100644 packages/material-ui-lab/src/Masonry/Masonry.test.js create mode 100644 packages/material-ui-lab/src/Masonry/MasonryContext.js create mode 100644 packages/material-ui-lab/src/Masonry/index.d.ts create mode 100644 packages/material-ui-lab/src/Masonry/index.js create mode 100644 packages/material-ui-lab/src/Masonry/masonryClasses.ts create mode 100644 packages/material-ui-lab/src/MasonryItem/MasonryItem.d.ts create mode 100644 packages/material-ui-lab/src/MasonryItem/MasonryItem.js create mode 100644 packages/material-ui-lab/src/MasonryItem/MasonryItem.test.js create mode 100644 packages/material-ui-lab/src/MasonryItem/index.d.ts create mode 100644 packages/material-ui-lab/src/MasonryItem/index.js create mode 100644 packages/material-ui-lab/src/MasonryItem/masonryItemClasses.ts diff --git a/docs/pages/api-docs/masonry-item.js b/docs/pages/api-docs/masonry-item.js new file mode 100644 index 00000000000000..c9dce04e3c9d82 --- /dev/null +++ b/docs/pages/api-docs/masonry-item.js @@ -0,0 +1,23 @@ +import * as React from 'react'; +import ApiPage from 'docs/src/modules/components/ApiPage'; +import mapApiPageTranslations from 'docs/src/modules/utils/mapApiPageTranslations'; +import jsonPageContent from './masonry-item.json'; + +export default function Page(props) { + const { descriptions, pageContent } = props; + return ; +} + +Page.getInitialProps = () => { + const req = require.context( + 'docs/translations/api-docs/masonry-item', + false, + /masonry-item.*.json$/, + ); + const descriptions = mapApiPageTranslations(req); + + return { + descriptions, + pageContent: jsonPageContent, + }; +}; diff --git a/docs/pages/api-docs/masonry-item.json b/docs/pages/api-docs/masonry-item.json new file mode 100644 index 00000000000000..7e2fb0fa099e95 --- /dev/null +++ b/docs/pages/api-docs/masonry-item.json @@ -0,0 +1,17 @@ +{ + "props": { + "children": { "type": { "name": "element" }, "required": true }, + "classes": { "type": { "name": "object" } }, + "component": { "type": { "name": "elementType" } }, + "sx": { "type": { "name": "object" } } + }, + "name": "MasonryItem", + "styles": { "classes": ["root"], "globalClasses": {}, "name": "MuiMasonryItem" }, + "spread": true, + "forwardsRefTo": "HTMLDivElement", + "filename": "/packages/material-ui-lab/src/MasonryItem/MasonryItem.js", + "inheritance": null, + "demos": "", + "styledComponent": true, + "cssComponent": false +} diff --git a/docs/pages/api-docs/masonry.js b/docs/pages/api-docs/masonry.js new file mode 100644 index 00000000000000..6fb94801e7b9ea --- /dev/null +++ b/docs/pages/api-docs/masonry.js @@ -0,0 +1,19 @@ +import * as React from 'react'; +import ApiPage from 'docs/src/modules/components/ApiPage'; +import mapApiPageTranslations from 'docs/src/modules/utils/mapApiPageTranslations'; +import jsonPageContent from './masonry.json'; + +export default function Page(props) { + const { descriptions, pageContent } = props; + return ; +} + +Page.getInitialProps = () => { + const req = require.context('docs/translations/api-docs/masonry', false, /masonry.*.json$/); + const descriptions = mapApiPageTranslations(req); + + return { + descriptions, + pageContent: jsonPageContent, + }; +}; diff --git a/docs/pages/api-docs/masonry.json b/docs/pages/api-docs/masonry.json new file mode 100644 index 00000000000000..2b111870fb2a3e --- /dev/null +++ b/docs/pages/api-docs/masonry.json @@ -0,0 +1,31 @@ +{ + "props": { + "children": { "type": { "name": "node" }, "required": true }, + "classes": { "type": { "name": "object" } }, + "cols": { + "type": { + "name": "union", + "description": "Array<number
| string>
| number
| object
| string" + }, + "default": "4" + }, + "component": { "type": { "name": "elementType" } }, + "spacing": { + "type": { + "name": "union", + "description": "Array<number
| string>
| number
| object
| string" + }, + "default": "1" + }, + "sx": { "type": { "name": "object" } } + }, + "name": "Masonry", + "styles": { "classes": ["root"], "globalClasses": {}, "name": "MuiMasonry" }, + "spread": true, + "forwardsRefTo": "HTMLDivElement", + "filename": "/packages/material-ui-lab/src/Masonry/Masonry.js", + "inheritance": null, + "demos": "", + "styledComponent": true, + "cssComponent": false +} diff --git a/docs/pages/components/masonry.js b/docs/pages/components/masonry.js new file mode 100644 index 00000000000000..afdce583821302 --- /dev/null +++ b/docs/pages/components/masonry.js @@ -0,0 +1,11 @@ +import * as React from 'react'; +import MarkdownDocs from 'docs/src/modules/components/MarkdownDocs'; +import { + demos, + docs, + requireDemo, +} from 'docs/src/pages/components/masonry/masonry.md?@material-ui/markdown'; + +export default function Page() { + return ; +} diff --git a/docs/src/pages.ts b/docs/src/pages.ts index 12843eac4b7995..9b80f8770be1d9 100644 --- a/docs/src/pages.ts +++ b/docs/src/pages.ts @@ -190,6 +190,7 @@ const pages: readonly MuiPage[] = [ { pathname: '/components/timeline' }, { pathname: '/components/trap-focus' }, { pathname: '/components/tree-view' }, + { pathname: '/components/masonry' }, ], }, ], diff --git a/docs/src/pages/components/masonry/BasicMasonry.js b/docs/src/pages/components/masonry/BasicMasonry.js new file mode 100644 index 00000000000000..12f0ace16a64f1 --- /dev/null +++ b/docs/src/pages/components/masonry/BasicMasonry.js @@ -0,0 +1,28 @@ +/* eslint-disable @typescript-eslint/no-use-before-define */ +import * as React from 'react'; +import Box from '@material-ui/core/Box'; +import Masonry from '@material-ui/lab/Masonry'; +import MasonryItem from '@material-ui/lab/MasonryItem'; + +export default function BasicMasonry() { + return ( + + {heights.map((height, idx) => ( + + + {idx + 1} + + + ))} + + ); +} + +const heights = [150, 30, 90, 70, 110, 150, 130, 80, 50, 90, 100, 150, 30, 50, 80]; diff --git a/docs/src/pages/components/masonry/BasicMasonry.tsx b/docs/src/pages/components/masonry/BasicMasonry.tsx new file mode 100644 index 00000000000000..5368dca95c091b --- /dev/null +++ b/docs/src/pages/components/masonry/BasicMasonry.tsx @@ -0,0 +1,28 @@ +/* eslint-disable @typescript-eslint/no-use-before-define */ +import * as React from 'react'; +import Box from '@material-ui/core/Box'; +import Masonry from '@material-ui/lab/Masonry'; +import MasonryItem from '@material-ui/lab/MasonryItem'; + +export default function BasicMasonry(): JSX.Element { + return ( + + {heights.map((height, idx) => ( + + + {idx + 1} + + + ))} + + ); +} + +const heights = [150, 30, 90, 70, 110, 150, 130, 80, 50, 90, 100, 150, 30, 50, 80]; diff --git a/docs/src/pages/components/masonry/ImageMasonry.js b/docs/src/pages/components/masonry/ImageMasonry.js new file mode 100644 index 00000000000000..097d7d8012cf27 --- /dev/null +++ b/docs/src/pages/components/masonry/ImageMasonry.js @@ -0,0 +1,103 @@ +/* eslint-disable @typescript-eslint/no-use-before-define */ +import * as React from 'react'; +import Masonry from '@material-ui/lab/Masonry'; +import MasonryItem from '@material-ui/lab/MasonryItem'; + +export default function ImageMasonry() { + return ( + + {imgData.map((item, idx) => ( + + {item.title} + + ))} + + ); +} + +const imgData = [ + { + img: 'https://images.unsplash.com/photo-1529655683826-aba9b3e77383?ixid=MnwxMjA3fDB8MHxzZWFyY2h8Mnx8bG9uZG9ufGVufDB8fDB8fA%3D%3D&ixlib=rb-1.2.1&w=1000&q=80', + title: '1', + }, + { + img: 'https://images.pexels.com/photos/1761279/pexels-photo-1761279.jpeg?auto=compress&cs=tinysrgb&dpr=1&w=500', + title: '2', + }, + { + img: 'https://www.nature.com/immersive/d41586-021-00095-y/assets/3TP4N718ac/2021-01-xx_jan-iom_tree-of-life_sh-1080x1440.jpeg', + title: '3', + }, + { + img: 'https://www.gettyimages.com/gi-resources/images/500px/983794168.jpg', + title: '4', + }, + { + img: 'https://cdn.pixabay.com/photo/2015/04/23/22/00/tree-736885__480.jpg', + title: '5', + }, + { + img: 'https://images.pexels.com/photos/674010/pexels-photo-674010.jpeg?auto=compress&cs=tinysrgb&dpr=1&w=500', + title: '6', + }, + { + img: 'https://cdn.pixabay.com/photo/2016/05/05/02/37/sunset-1373171__340.jpg', + title: '7', + }, + { + img: 'https://cdn.pixabay.com/photo/2015/04/19/08/32/marguerite-729510__340.jpg', + title: '8', + }, + { + img: 'https://images.ctfassets.net/hrltx12pl8hq/3MbF54EhWUhsXunc5Keueb/60774fbbff86e6bf6776f1e17a8016b4/04-nature_721703848.jpg?fit=fill&w=480&h=270', + title: '9', + }, + { + img: 'https://www.gettyimages.com/gi-resources/images/500px/983794168.jpg', + title: '10', + }, + { + img: 'https://images.unsplash.com/photo-1529655683826-aba9b3e77383?ixid=MnwxMjA3fDB8MHxzZWFyY2h8Mnx8bG9uZG9ufGVufDB8fDB8fA%3D%3D&ixlib=rb-1.2.1&w=1000&q=80', + title: '11', + }, + { + img: 'https://images.pexels.com/photos/1761279/pexels-photo-1761279.jpeg?auto=compress&cs=tinysrgb&dpr=1&w=500', + title: '12', + }, + { + img: 'https://www.nature.com/immersive/d41586-021-00095-y/assets/3TP4N718ac/2021-01-xx_jan-iom_tree-of-life_sh-1080x1440.jpeg', + title: '13', + }, + { + img: 'https://www.gettyimages.com/gi-resources/images/500px/983794168.jpg', + title: '14', + }, + { + img: 'https://cdn.pixabay.com/photo/2015/04/23/22/00/tree-736885__480.jpg', + title: '15', + }, + { + img: 'https://images.pexels.com/photos/674010/pexels-photo-674010.jpeg?auto=compress&cs=tinysrgb&dpr=1&w=500', + title: '16', + }, + { + img: 'https://cdn.pixabay.com/photo/2016/05/05/02/37/sunset-1373171__340.jpg', + title: '17', + }, + { + img: 'https://cdn.pixabay.com/photo/2015/04/19/08/32/marguerite-729510__340.jpg', + title: '18', + }, + { + img: 'https://images.ctfassets.net/hrltx12pl8hq/3MbF54EhWUhsXunc5Keueb/60774fbbff86e6bf6776f1e17a8016b4/04-nature_721703848.jpg?fit=fill&w=480&h=270', + title: '19', + }, + { + img: 'https://www.gettyimages.com/gi-resources/images/500px/983794168.jpg', + title: '20', + }, +]; diff --git a/docs/src/pages/components/masonry/ImageMasonry.tsx b/docs/src/pages/components/masonry/ImageMasonry.tsx new file mode 100644 index 00000000000000..9ce7bea53a4bef --- /dev/null +++ b/docs/src/pages/components/masonry/ImageMasonry.tsx @@ -0,0 +1,103 @@ +/* eslint-disable @typescript-eslint/no-use-before-define */ +import * as React from 'react'; +import Masonry from '@material-ui/lab/Masonry'; +import MasonryItem from '@material-ui/lab/MasonryItem'; + +export default function ImageMasonry(): JSX.Element { + return ( + + {imgData.map((item, idx) => ( + + {item.title} + + ))} + + ); +} + +const imgData = [ + { + img: 'https://images.unsplash.com/photo-1529655683826-aba9b3e77383?ixid=MnwxMjA3fDB8MHxzZWFyY2h8Mnx8bG9uZG9ufGVufDB8fDB8fA%3D%3D&ixlib=rb-1.2.1&w=1000&q=80', + title: '1', + }, + { + img: 'https://images.pexels.com/photos/1761279/pexels-photo-1761279.jpeg?auto=compress&cs=tinysrgb&dpr=1&w=500', + title: '2', + }, + { + img: 'https://www.nature.com/immersive/d41586-021-00095-y/assets/3TP4N718ac/2021-01-xx_jan-iom_tree-of-life_sh-1080x1440.jpeg', + title: '3', + }, + { + img: 'https://www.gettyimages.com/gi-resources/images/500px/983794168.jpg', + title: '4', + }, + { + img: 'https://cdn.pixabay.com/photo/2015/04/23/22/00/tree-736885__480.jpg', + title: '5', + }, + { + img: 'https://images.pexels.com/photos/674010/pexels-photo-674010.jpeg?auto=compress&cs=tinysrgb&dpr=1&w=500', + title: '6', + }, + { + img: 'https://cdn.pixabay.com/photo/2016/05/05/02/37/sunset-1373171__340.jpg', + title: '7', + }, + { + img: 'https://cdn.pixabay.com/photo/2015/04/19/08/32/marguerite-729510__340.jpg', + title: '8', + }, + { + img: 'https://images.ctfassets.net/hrltx12pl8hq/3MbF54EhWUhsXunc5Keueb/60774fbbff86e6bf6776f1e17a8016b4/04-nature_721703848.jpg?fit=fill&w=480&h=270', + title: '9', + }, + { + img: 'https://www.gettyimages.com/gi-resources/images/500px/983794168.jpg', + title: '10', + }, + { + img: 'https://images.unsplash.com/photo-1529655683826-aba9b3e77383?ixid=MnwxMjA3fDB8MHxzZWFyY2h8Mnx8bG9uZG9ufGVufDB8fDB8fA%3D%3D&ixlib=rb-1.2.1&w=1000&q=80', + title: '11', + }, + { + img: 'https://images.pexels.com/photos/1761279/pexels-photo-1761279.jpeg?auto=compress&cs=tinysrgb&dpr=1&w=500', + title: '12', + }, + { + img: 'https://www.nature.com/immersive/d41586-021-00095-y/assets/3TP4N718ac/2021-01-xx_jan-iom_tree-of-life_sh-1080x1440.jpeg', + title: '13', + }, + { + img: 'https://www.gettyimages.com/gi-resources/images/500px/983794168.jpg', + title: '14', + }, + { + img: 'https://cdn.pixabay.com/photo/2015/04/23/22/00/tree-736885__480.jpg', + title: '15', + }, + { + img: 'https://images.pexels.com/photos/674010/pexels-photo-674010.jpeg?auto=compress&cs=tinysrgb&dpr=1&w=500', + title: '16', + }, + { + img: 'https://cdn.pixabay.com/photo/2016/05/05/02/37/sunset-1373171__340.jpg', + title: '17', + }, + { + img: 'https://cdn.pixabay.com/photo/2015/04/19/08/32/marguerite-729510__340.jpg', + title: '18', + }, + { + img: 'https://images.ctfassets.net/hrltx12pl8hq/3MbF54EhWUhsXunc5Keueb/60774fbbff86e6bf6776f1e17a8016b4/04-nature_721703848.jpg?fit=fill&w=480&h=270', + title: '19', + }, + { + img: 'https://www.gettyimages.com/gi-resources/images/500px/983794168.jpg', + title: '20', + }, +]; diff --git a/docs/src/pages/components/masonry/masonry.md b/docs/src/pages/components/masonry/masonry.md new file mode 100644 index 00000000000000..c6d66d4765c6c2 --- /dev/null +++ b/docs/src/pages/components/masonry/masonry.md @@ -0,0 +1,21 @@ +--- +title: React Masonry component +components: Masonry, MasonryItem +githubLabel: 'component: Masonry' +--- + +# Masonry + +

Masonry lays out contents of different sizes as blocks of the same width and variable height with configurable gaps.

+ +Masonry maintains a list of content blocks with a consistent width but variable height. The contents are ordered by row. If a row is already filled with the specified number of columns, the next item starts another row, and it is added to the shortest column. + +{{"component": "modules/components/ComponentLinkHeader.js"}} + +## Masonry + +{{"demo": "pages/components/masonry/ImageMasonry.js", "bg": true}} + +> `` can receive not only `` elements but also `
` elements as shown in the following demo: + +{{"demo": "pages/components/masonry/BasicMasonry.js", "bg": true}} diff --git a/docs/src/pagesApi.js b/docs/src/pagesApi.js index 3fa8b63a0ca4db..8f4cd760d371c2 100644 --- a/docs/src/pagesApi.js +++ b/docs/src/pagesApi.js @@ -83,6 +83,8 @@ module.exports = [ { pathname: '/api-docs/list-item-text' }, { pathname: '/api-docs/list-subheader' }, { pathname: '/api-docs/loading-button' }, + { pathname: '/api-docs/masonry' }, + { pathname: '/api-docs/masonry-item' }, { pathname: '/api-docs/menu' }, { pathname: '/api-docs/menu-item' }, { pathname: '/api-docs/menu-list' }, diff --git a/docs/translations/api-docs/masonry-item/masonry-item-de.json b/docs/translations/api-docs/masonry-item/masonry-item-de.json new file mode 100644 index 00000000000000..308b7edb5b2474 --- /dev/null +++ b/docs/translations/api-docs/masonry-item/masonry-item-de.json @@ -0,0 +1,10 @@ +{ + "componentDescription": "", + "propDescriptions": { + "children": "The content of the component, normally an <img /> or a <div />.", + "classes": "Override or extend the styles applied to the component. See CSS API below for more details.", + "component": "The component used for the root node. Either a string to use a HTML element or a component.", + "sx": "Allows defining system overrides as well as additional CSS styles. See the `sx` page for more details." + }, + "classDescriptions": { "root": { "description": "Styles applied to the root element." } } +} diff --git a/docs/translations/api-docs/masonry-item/masonry-item-es.json b/docs/translations/api-docs/masonry-item/masonry-item-es.json new file mode 100644 index 00000000000000..308b7edb5b2474 --- /dev/null +++ b/docs/translations/api-docs/masonry-item/masonry-item-es.json @@ -0,0 +1,10 @@ +{ + "componentDescription": "", + "propDescriptions": { + "children": "The content of the component, normally an <img /> or a <div />.", + "classes": "Override or extend the styles applied to the component. See CSS API below for more details.", + "component": "The component used for the root node. Either a string to use a HTML element or a component.", + "sx": "Allows defining system overrides as well as additional CSS styles. See the `sx` page for more details." + }, + "classDescriptions": { "root": { "description": "Styles applied to the root element." } } +} diff --git a/docs/translations/api-docs/masonry-item/masonry-item-fr.json b/docs/translations/api-docs/masonry-item/masonry-item-fr.json new file mode 100644 index 00000000000000..308b7edb5b2474 --- /dev/null +++ b/docs/translations/api-docs/masonry-item/masonry-item-fr.json @@ -0,0 +1,10 @@ +{ + "componentDescription": "", + "propDescriptions": { + "children": "The content of the component, normally an <img /> or a <div />.", + "classes": "Override or extend the styles applied to the component. See CSS API below for more details.", + "component": "The component used for the root node. Either a string to use a HTML element or a component.", + "sx": "Allows defining system overrides as well as additional CSS styles. See the `sx` page for more details." + }, + "classDescriptions": { "root": { "description": "Styles applied to the root element." } } +} diff --git a/docs/translations/api-docs/masonry-item/masonry-item-ja.json b/docs/translations/api-docs/masonry-item/masonry-item-ja.json new file mode 100644 index 00000000000000..308b7edb5b2474 --- /dev/null +++ b/docs/translations/api-docs/masonry-item/masonry-item-ja.json @@ -0,0 +1,10 @@ +{ + "componentDescription": "", + "propDescriptions": { + "children": "The content of the component, normally an <img /> or a <div />.", + "classes": "Override or extend the styles applied to the component. See CSS API below for more details.", + "component": "The component used for the root node. Either a string to use a HTML element or a component.", + "sx": "Allows defining system overrides as well as additional CSS styles. See the `sx` page for more details." + }, + "classDescriptions": { "root": { "description": "Styles applied to the root element." } } +} diff --git a/docs/translations/api-docs/masonry-item/masonry-item-pt.json b/docs/translations/api-docs/masonry-item/masonry-item-pt.json new file mode 100644 index 00000000000000..308b7edb5b2474 --- /dev/null +++ b/docs/translations/api-docs/masonry-item/masonry-item-pt.json @@ -0,0 +1,10 @@ +{ + "componentDescription": "", + "propDescriptions": { + "children": "The content of the component, normally an <img /> or a <div />.", + "classes": "Override or extend the styles applied to the component. See CSS API below for more details.", + "component": "The component used for the root node. Either a string to use a HTML element or a component.", + "sx": "Allows defining system overrides as well as additional CSS styles. See the `sx` page for more details." + }, + "classDescriptions": { "root": { "description": "Styles applied to the root element." } } +} diff --git a/docs/translations/api-docs/masonry-item/masonry-item-ru.json b/docs/translations/api-docs/masonry-item/masonry-item-ru.json new file mode 100644 index 00000000000000..308b7edb5b2474 --- /dev/null +++ b/docs/translations/api-docs/masonry-item/masonry-item-ru.json @@ -0,0 +1,10 @@ +{ + "componentDescription": "", + "propDescriptions": { + "children": "The content of the component, normally an <img /> or a <div />.", + "classes": "Override or extend the styles applied to the component. See CSS API below for more details.", + "component": "The component used for the root node. Either a string to use a HTML element or a component.", + "sx": "Allows defining system overrides as well as additional CSS styles. See the `sx` page for more details." + }, + "classDescriptions": { "root": { "description": "Styles applied to the root element." } } +} diff --git a/docs/translations/api-docs/masonry-item/masonry-item-zh.json b/docs/translations/api-docs/masonry-item/masonry-item-zh.json new file mode 100644 index 00000000000000..308b7edb5b2474 --- /dev/null +++ b/docs/translations/api-docs/masonry-item/masonry-item-zh.json @@ -0,0 +1,10 @@ +{ + "componentDescription": "", + "propDescriptions": { + "children": "The content of the component, normally an <img /> or a <div />.", + "classes": "Override or extend the styles applied to the component. See CSS API below for more details.", + "component": "The component used for the root node. Either a string to use a HTML element or a component.", + "sx": "Allows defining system overrides as well as additional CSS styles. See the `sx` page for more details." + }, + "classDescriptions": { "root": { "description": "Styles applied to the root element." } } +} diff --git a/docs/translations/api-docs/masonry-item/masonry-item.json b/docs/translations/api-docs/masonry-item/masonry-item.json new file mode 100644 index 00000000000000..308b7edb5b2474 --- /dev/null +++ b/docs/translations/api-docs/masonry-item/masonry-item.json @@ -0,0 +1,10 @@ +{ + "componentDescription": "", + "propDescriptions": { + "children": "The content of the component, normally an <img /> or a <div />.", + "classes": "Override or extend the styles applied to the component. See CSS API below for more details.", + "component": "The component used for the root node. Either a string to use a HTML element or a component.", + "sx": "Allows defining system overrides as well as additional CSS styles. See the `sx` page for more details." + }, + "classDescriptions": { "root": { "description": "Styles applied to the root element." } } +} diff --git a/docs/translations/api-docs/masonry/masonry-de.json b/docs/translations/api-docs/masonry/masonry-de.json new file mode 100644 index 00000000000000..7b4d84fdc144ea --- /dev/null +++ b/docs/translations/api-docs/masonry/masonry-de.json @@ -0,0 +1,12 @@ +{ + "componentDescription": "", + "propDescriptions": { + "children": "The content of the component, normally <MasonryItem />s.", + "classes": "Override or extend the styles applied to the component. See CSS API below for more details.", + "cols": "Number of columns.", + "component": "The component used for the root node. Either a string to use a HTML element or a component.", + "spacing": "Defines the space between children.", + "sx": "Allows defining system overrides as well as additional CSS styles. See the `sx` page for more details." + }, + "classDescriptions": { "root": { "description": "Styles applied to the root element." } } +} diff --git a/docs/translations/api-docs/masonry/masonry-es.json b/docs/translations/api-docs/masonry/masonry-es.json new file mode 100644 index 00000000000000..7b4d84fdc144ea --- /dev/null +++ b/docs/translations/api-docs/masonry/masonry-es.json @@ -0,0 +1,12 @@ +{ + "componentDescription": "", + "propDescriptions": { + "children": "The content of the component, normally <MasonryItem />s.", + "classes": "Override or extend the styles applied to the component. See CSS API below for more details.", + "cols": "Number of columns.", + "component": "The component used for the root node. Either a string to use a HTML element or a component.", + "spacing": "Defines the space between children.", + "sx": "Allows defining system overrides as well as additional CSS styles. See the `sx` page for more details." + }, + "classDescriptions": { "root": { "description": "Styles applied to the root element." } } +} diff --git a/docs/translations/api-docs/masonry/masonry-fr.json b/docs/translations/api-docs/masonry/masonry-fr.json new file mode 100644 index 00000000000000..7b4d84fdc144ea --- /dev/null +++ b/docs/translations/api-docs/masonry/masonry-fr.json @@ -0,0 +1,12 @@ +{ + "componentDescription": "", + "propDescriptions": { + "children": "The content of the component, normally <MasonryItem />s.", + "classes": "Override or extend the styles applied to the component. See CSS API below for more details.", + "cols": "Number of columns.", + "component": "The component used for the root node. Either a string to use a HTML element or a component.", + "spacing": "Defines the space between children.", + "sx": "Allows defining system overrides as well as additional CSS styles. See the `sx` page for more details." + }, + "classDescriptions": { "root": { "description": "Styles applied to the root element." } } +} diff --git a/docs/translations/api-docs/masonry/masonry-ja.json b/docs/translations/api-docs/masonry/masonry-ja.json new file mode 100644 index 00000000000000..7b4d84fdc144ea --- /dev/null +++ b/docs/translations/api-docs/masonry/masonry-ja.json @@ -0,0 +1,12 @@ +{ + "componentDescription": "", + "propDescriptions": { + "children": "The content of the component, normally <MasonryItem />s.", + "classes": "Override or extend the styles applied to the component. See CSS API below for more details.", + "cols": "Number of columns.", + "component": "The component used for the root node. Either a string to use a HTML element or a component.", + "spacing": "Defines the space between children.", + "sx": "Allows defining system overrides as well as additional CSS styles. See the `sx` page for more details." + }, + "classDescriptions": { "root": { "description": "Styles applied to the root element." } } +} diff --git a/docs/translations/api-docs/masonry/masonry-pt.json b/docs/translations/api-docs/masonry/masonry-pt.json new file mode 100644 index 00000000000000..7b4d84fdc144ea --- /dev/null +++ b/docs/translations/api-docs/masonry/masonry-pt.json @@ -0,0 +1,12 @@ +{ + "componentDescription": "", + "propDescriptions": { + "children": "The content of the component, normally <MasonryItem />s.", + "classes": "Override or extend the styles applied to the component. See CSS API below for more details.", + "cols": "Number of columns.", + "component": "The component used for the root node. Either a string to use a HTML element or a component.", + "spacing": "Defines the space between children.", + "sx": "Allows defining system overrides as well as additional CSS styles. See the `sx` page for more details." + }, + "classDescriptions": { "root": { "description": "Styles applied to the root element." } } +} diff --git a/docs/translations/api-docs/masonry/masonry-ru.json b/docs/translations/api-docs/masonry/masonry-ru.json new file mode 100644 index 00000000000000..7b4d84fdc144ea --- /dev/null +++ b/docs/translations/api-docs/masonry/masonry-ru.json @@ -0,0 +1,12 @@ +{ + "componentDescription": "", + "propDescriptions": { + "children": "The content of the component, normally <MasonryItem />s.", + "classes": "Override or extend the styles applied to the component. See CSS API below for more details.", + "cols": "Number of columns.", + "component": "The component used for the root node. Either a string to use a HTML element or a component.", + "spacing": "Defines the space between children.", + "sx": "Allows defining system overrides as well as additional CSS styles. See the `sx` page for more details." + }, + "classDescriptions": { "root": { "description": "Styles applied to the root element." } } +} diff --git a/docs/translations/api-docs/masonry/masonry-zh.json b/docs/translations/api-docs/masonry/masonry-zh.json new file mode 100644 index 00000000000000..7b4d84fdc144ea --- /dev/null +++ b/docs/translations/api-docs/masonry/masonry-zh.json @@ -0,0 +1,12 @@ +{ + "componentDescription": "", + "propDescriptions": { + "children": "The content of the component, normally <MasonryItem />s.", + "classes": "Override or extend the styles applied to the component. See CSS API below for more details.", + "cols": "Number of columns.", + "component": "The component used for the root node. Either a string to use a HTML element or a component.", + "spacing": "Defines the space between children.", + "sx": "Allows defining system overrides as well as additional CSS styles. See the `sx` page for more details." + }, + "classDescriptions": { "root": { "description": "Styles applied to the root element." } } +} diff --git a/docs/translations/api-docs/masonry/masonry.json b/docs/translations/api-docs/masonry/masonry.json new file mode 100644 index 00000000000000..7b4d84fdc144ea --- /dev/null +++ b/docs/translations/api-docs/masonry/masonry.json @@ -0,0 +1,12 @@ +{ + "componentDescription": "", + "propDescriptions": { + "children": "The content of the component, normally <MasonryItem />s.", + "classes": "Override or extend the styles applied to the component. See CSS API below for more details.", + "cols": "Number of columns.", + "component": "The component used for the root node. Either a string to use a HTML element or a component.", + "spacing": "Defines the space between children.", + "sx": "Allows defining system overrides as well as additional CSS styles. See the `sx` page for more details." + }, + "classDescriptions": { "root": { "description": "Styles applied to the root element." } } +} diff --git a/docs/translations/translations.json b/docs/translations/translations.json index 840063aa745e2a..b085c5eb9ac5cd 100644 --- a/docs/translations/translations.json +++ b/docs/translations/translations.json @@ -263,6 +263,7 @@ "/components/timeline": "Timeline", "/components/trap-focus": "Trap Focus", "/components/tree-view": "Tree View", + "/components/masonry": "Masonry", "/api-docs": "Component API", "/system": "System", "/system/basics": "Basics", diff --git a/packages/material-ui-lab/src/Masonry/Masonry.d.ts b/packages/material-ui-lab/src/Masonry/Masonry.d.ts new file mode 100644 index 00000000000000..22c51ee32b415b --- /dev/null +++ b/packages/material-ui-lab/src/Masonry/Masonry.d.ts @@ -0,0 +1,50 @@ +import { ResponsiveStyleValue, SxProps } from '@material-ui/system'; +import { OverridableComponent, OverrideProps } from '@material-ui/core/OverridableComponent'; +import { Theme } from '@material-ui/core/styles'; +import { MasonryClasses } from './masonryClasses'; + +export interface MasonryTypeMap

{ + props: P & { + /** + * The content of the component, normally ``s. + */ + children: NonNullable; + /** + * Override or extend the styles applied to the component. + */ + classes?: Partial; + /** + * Number of columns. + * @default 4 + */ + cols?: ResponsiveStyleValue; + /** + * Defines the space between children. + * @default 1 + */ + spacing?: ResponsiveStyleValue; + /** + * Allows defining system overrides as well as additional CSS styles. + */ + sx?: SxProps; + }; + defaultComponent: D; +} +/** + * + * Demos: + * + * - [Masonry](https://material-ui.com/components/masonry/) + * + * API: + * + * - [Masonry API](https://material-ui.com/api/masonry/) + */ +declare const Masonry: OverridableComponent; + +export type MasonryProps< + D extends React.ElementType = MasonryTypeMap['defaultComponent'], + P = {}, +> = OverrideProps, D>; + +export default Masonry; diff --git a/packages/material-ui-lab/src/Masonry/Masonry.js b/packages/material-ui-lab/src/Masonry/Masonry.js new file mode 100644 index 00000000000000..50b42ab9656f61 --- /dev/null +++ b/packages/material-ui-lab/src/Masonry/Masonry.js @@ -0,0 +1,173 @@ +import * as React from 'react'; +import PropTypes from 'prop-types'; +import clsx from 'clsx'; +import { + createUnarySpacing, + getValue, + handleBreakpoints, + resolveBreakpointValues, +} from '@material-ui/system'; +import { deepmerge } from '@material-ui/utils'; +import { unstable_composeClasses as composeClasses } from '@material-ui/unstyled'; +import { styled, useThemeProps } from '@material-ui/core/styles'; +import { getMasonryUtilityClass } from './masonryClasses'; +import MasonryContext from './MasonryContext'; + +const useUtilityClasses = (styleProps) => { + const { classes } = styleProps; + + const slots = { + root: ['root'], + }; + + return composeClasses(slots, getMasonryUtilityClass, classes); +}; + +export const style = ({ styleProps, theme }) => { + let styles = { + display: 'grid', + gridAutoRows: 0, + padding: 0, + overflow: 'auto', + width: '100% !important', + rowGap: 1, + ...((typeof styleProps.spacing === 'string' || typeof styleProps.spacing === 'number') && { + columnGap: theme.spacing(styleProps.spacing), + }), + ...((typeof styleProps.cols === 'string' || typeof styleProps.cols === 'number') && { + gridTemplateColumns: `repeat(${styleProps.cols}, 1fr)`, + }), + }; + + const base = Object.keys(theme.breakpoints.values).reduce((acc, breakpoint) => { + if (styleProps.spacing[breakpoint] != null) { + acc[breakpoint] = true; + } + return acc; + }, {}); + + if (typeof styleProps.spacing === 'object' || Array.isArray(styleProps.spacing)) { + const spacingValues = resolveBreakpointValues({ values: styleProps.spacing, base }); + const transformer = createUnarySpacing(theme); + const spacingStyleFromPropValue = (propValue) => { + return { + columnGap: getValue(transformer, propValue), + }; + }; + + styles = deepmerge( + styles, + handleBreakpoints({ theme }, spacingValues, spacingStyleFromPropValue), + ); + } + + if (typeof styleProps.cols === 'object' || Array.isArray(styleProps.cols)) { + const columnValues = resolveBreakpointValues({ values: styleProps.cols, base }); + const columnStyleFromPropValue = (propValue) => { + return { + gridTemplateColumns: `repeat(${propValue}, 1fr)`, + }; + }; + + styles = deepmerge( + styles, + handleBreakpoints({ theme }, columnValues, columnStyleFromPropValue), + ); + } + + return styles; +}; + +const MasonryRoot = styled('div', { + name: 'MuiMasonry', + slot: 'Root', + overridesResolver: (props, styles) => { + return [styles.root]; + }, +})(style); + +const Masonry = React.forwardRef(function Masonry(inProps, ref) { + const props = useThemeProps({ + props: inProps, + name: 'MuiMasonry', + }); + + const { children, className, component = 'div', cols = 4, spacing = 1, ...other } = props; + const styleProps = { ...props, spacing, cols }; + const classes = useUtilityClasses(styleProps); + const [documentReady, setDocumentReady] = React.useState(false); + const handleStateChange = () => { + if (document.readyState === 'complete') { + setDocumentReady(true); + } + }; + React.useEffect(() => { + document.addEventListener('readystatechange', handleStateChange);; + return () => { + document.removeEventListener('readystatechange', handleStateChange); + }; + }); + return ( + + + {children} + + + ); +}); + +Masonry.propTypes /* remove-proptypes */ = { + // ----------------------------- Warning -------------------------------- + // | These PropTypes are generated from the TypeScript type definitions | + // | To update them edit the d.ts file and run "yarn proptypes" | + // ---------------------------------------------------------------------- + /** + * The content of the component, normally ``s. + */ + children: PropTypes /* @typescript-to-proptypes-ignore */.node.isRequired, + /** + * Override or extend the styles applied to the component. + */ + classes: PropTypes.object, + /** + * @ignore + */ + className: PropTypes.string, + /** + * Number of columns. + * @default 4 + */ + cols: PropTypes.oneOfType([ + PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.number, PropTypes.string])), + PropTypes.number, + PropTypes.object, + PropTypes.string, + ]), + /** + * The component used for the root node. + * Either a string to use a HTML element or a component. + */ + component: PropTypes.elementType, + /** + * Defines the space between children. + * @default 1 + */ + spacing: PropTypes.oneOfType([ + PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.number, PropTypes.string])), + PropTypes.number, + PropTypes.object, + PropTypes.string, + ]), + /** + * Allows defining system overrides as well as additional CSS styles. + */ + sx: PropTypes.object, +}; + +export default Masonry; diff --git a/packages/material-ui-lab/src/Masonry/Masonry.test.js b/packages/material-ui-lab/src/Masonry/Masonry.test.js new file mode 100644 index 00000000000000..a4c04a62cb9e20 --- /dev/null +++ b/packages/material-ui-lab/src/Masonry/Masonry.test.js @@ -0,0 +1,23 @@ +import * as React from 'react'; +import { createClientRender, describeConformanceV5 } from 'test/utils'; +import Masonry, { masonryClasses as classes } from '@material-ui/lab/Masonry'; + +describe('', () => { + const render = createClientRender(); + + describeConformanceV5( + +

+ , + () => ({ + classes, + inheritComponent: 'div', + render, + refInstanceof: window.HTMLDivElement, + testComponentPropWith: 'div', + testVariantProps: { variant: 'foo' }, + muiName: 'MuiMasonry', + skip: ['componentsProp'], + }), + ); +}); diff --git a/packages/material-ui-lab/src/Masonry/MasonryContext.js b/packages/material-ui-lab/src/Masonry/MasonryContext.js new file mode 100644 index 00000000000000..54f687f5a0d81f --- /dev/null +++ b/packages/material-ui-lab/src/Masonry/MasonryContext.js @@ -0,0 +1,12 @@ +import * as React from 'react'; + +/** + * @ignore - internal component. + */ +const MasonryContext = React.createContext({}); + +if (process.env.NODE_ENV !== 'production') { + MasonryContext.displayName = 'MasonryContext'; +} + +export default MasonryContext; diff --git a/packages/material-ui-lab/src/Masonry/index.d.ts b/packages/material-ui-lab/src/Masonry/index.d.ts new file mode 100644 index 00000000000000..f5e1cb096abdf2 --- /dev/null +++ b/packages/material-ui-lab/src/Masonry/index.d.ts @@ -0,0 +1,5 @@ +export * from './Masonry'; +export { default } from './Masonry'; + +export * from './masonryClasses'; +export { default as masonryClasses } from './masonryClasses'; diff --git a/packages/material-ui-lab/src/Masonry/index.js b/packages/material-ui-lab/src/Masonry/index.js new file mode 100644 index 00000000000000..840cd371f6e7dd --- /dev/null +++ b/packages/material-ui-lab/src/Masonry/index.js @@ -0,0 +1,3 @@ +export { default } from './Masonry'; +export * from './masonryClasses'; +export { default as masonryClasses } from './masonryClasses'; diff --git a/packages/material-ui-lab/src/Masonry/masonryClasses.ts b/packages/material-ui-lab/src/Masonry/masonryClasses.ts new file mode 100644 index 00000000000000..bcef28f0b67017 --- /dev/null +++ b/packages/material-ui-lab/src/Masonry/masonryClasses.ts @@ -0,0 +1,16 @@ +import { generateUtilityClass, generateUtilityClasses } from '@material-ui/unstyled'; + +export interface MasonryClasses { + /** Styles applied to the root element. */ + root: string; +} + +export type MasonryClassKey = keyof MasonryClasses; + +export function getMasonryUtilityClass(slot: string): string { + return generateUtilityClass('MuiMasonry', slot); +} + +const masonryClasses: MasonryClasses = generateUtilityClasses('MuiMasonry', ['root']); + +export default masonryClasses; diff --git a/packages/material-ui-lab/src/MasonryItem/MasonryItem.d.ts b/packages/material-ui-lab/src/MasonryItem/MasonryItem.d.ts new file mode 100644 index 00000000000000..efdab2c34a854c --- /dev/null +++ b/packages/material-ui-lab/src/MasonryItem/MasonryItem.d.ts @@ -0,0 +1,40 @@ +import { SxProps } from '@material-ui/system'; +import { OverridableComponent, OverrideProps } from '@material-ui/core/OverridableComponent'; +import { Theme } from '@material-ui/core/styles'; +import { MasonryItemClasses } from './masonryItemClasses'; + +export interface MasonryItemTypeMap

{ + props: P & { + /** + * The content of the component, normally an `` or a `

`. + */ + children: NonNullable; + /** + * Override or extend the styles applied to the component. + */ + classes?: Partial; + /** + * Allows defining system overrides as well as additional CSS styles. + */ + sx?: SxProps; + }; + defaultComponent: D; +} +/** + * + * Demos: + * + * - [Masonry](https://material-ui.com/components/masonry/) + * + * API: + * + * - [MasonryItem API](https://material-ui.com/api/masonry-item/) + */ +declare const MasonryItem: OverridableComponent; + +export type MasonryItemProps< + D extends React.ElementType = MasonryItemTypeMap['defaultComponent'], + P = {}, +> = OverrideProps, D>; + +export default MasonryItem; diff --git a/packages/material-ui-lab/src/MasonryItem/MasonryItem.js b/packages/material-ui-lab/src/MasonryItem/MasonryItem.js new file mode 100644 index 00000000000000..16a760b7c46684 --- /dev/null +++ b/packages/material-ui-lab/src/MasonryItem/MasonryItem.js @@ -0,0 +1,154 @@ +import * as React from 'react'; +import PropTypes from 'prop-types'; +import clsx from 'clsx'; +import { + createUnarySpacing, + getValue, + handleBreakpoints, + resolveBreakpointValues, +} from '@material-ui/system'; +import { deepmerge, unstable_useForkRef as useForkRef } from '@material-ui/utils'; +import { unstable_composeClasses as composeClasses } from '@material-ui/unstyled'; +import { styled, useThemeProps } from '@material-ui/core/styles'; +import { getMasonryItemUtilityClass } from './masonryItemClasses'; +import MasonryContext from '../Masonry/MasonryContext'; + +const useUtilityClasses = (styleProps) => { + const { classes } = styleProps; + + const slots = { + root: ['root'], + }; + + return composeClasses(slots, getMasonryItemUtilityClass, classes); +}; + +export const style = ({ styleProps, theme }) => { + let styles = { + width: '100%', + [`& > *`]: { + // all contents should have a width of 100% + objectFit: 'cover', + width: '100%', + }, + visibility: styleProps.contentHeight ? 'visible' : 'hidden', + ...((typeof styleProps.spacing === 'string' || typeof styleProps.spacing === 'number') && { + gridRowEnd: `span ${ + styleProps.contentHeight + Number(theme.spacing(styleProps.spacing).replace('px', '')) + }`, + paddingBottom: Number(theme.spacing(styleProps.spacing).replace('px', '')) - 1, + }), + }; + + if (typeof styleProps.spacing === 'object' || Array.isArray(styleProps.spacing)) { + const base = Object.keys(theme.breakpoints.values).reduce((acc, breakpoint) => { + if (styleProps.spacing[breakpoint] != null) { + acc[breakpoint] = true; + } + return acc; + }, {}); + const spacingValues = resolveBreakpointValues({ values: styleProps.spacing, base }); + const transformer = createUnarySpacing(theme); + const styleFromPropValue = (propValue) => { + const gap = Number(getValue(transformer, propValue).replace('px', '')); + const rowSpan = styleProps.contentHeight ? Math.ceil(styleProps.contentHeight + gap) : 0; + return { + gridRowEnd: `span ${rowSpan}`, + paddingBottom: gap - 1, + }; + }; + styles = deepmerge(styles, handleBreakpoints({ theme }, spacingValues, styleFromPropValue)); + } + return styles; +}; + +const MasonryItemRoot = styled('div', { + name: 'MuiMasonryItem', + slot: 'Root', + overridesResolver: (props, styles) => { + return [styles.root]; + }, +})(style); + +const MasonryItem = React.forwardRef(function MasonryItem(inProps, ref) { + const props = useThemeProps({ + props: inProps, + name: 'MuiMasonryItem', + }); + + const masonryItemRef = React.useRef(null); + const handleRef = useForkRef(ref, masonryItemRef); + const { + spacing = 1, + documentReady = false, + } = React.useContext(MasonryContext); + const { children, className, component = 'div', ...other } = props; + const [styleProps, setStyleProps] = React.useState({ + ...props, + spacing, + }); + + const classes = useUtilityClasses(styleProps); + + const computeHeight = () => { + const child = masonryItemRef.current.firstChild; + setStyleProps({ + ...styleProps, + contentHeight: child?.getBoundingClientRect().height, + }); + }; + + React.useEffect(() => { + if (documentReady) { + computeHeight(); + } + }, [documentReady]); // eslint-disable-line + + React.useEffect(() => { + window.addEventListener('resize', computeHeight);; + return () => { + window.removeEventListener('resize', computeHeight); + }; + }); + return ( + + {React.Children.only(children)} + + ); +}); + +MasonryItem.propTypes /* remove-proptypes */ = { + // ----------------------------- Warning -------------------------------- + // | These PropTypes are generated from the TypeScript type definitions | + // | To update them edit the d.ts file and run "yarn proptypes" | + // ---------------------------------------------------------------------- + /** + * The content of the component, normally an `` or a `
`. + */ + children: PropTypes.element.isRequired, + /** + * Override or extend the styles applied to the component. + */ + classes: PropTypes.object, + /** + * @ignore + */ + className: PropTypes.string, + /** + * The component used for the root node. + * Either a string to use a HTML element or a component. + */ + component: PropTypes.elementType, + /** + * Allows defining system overrides as well as additional CSS styles. + */ + sx: PropTypes.object, +}; + +export default MasonryItem; diff --git a/packages/material-ui-lab/src/MasonryItem/MasonryItem.test.js b/packages/material-ui-lab/src/MasonryItem/MasonryItem.test.js new file mode 100644 index 00000000000000..6a96ebad63a17b --- /dev/null +++ b/packages/material-ui-lab/src/MasonryItem/MasonryItem.test.js @@ -0,0 +1,23 @@ +import * as React from 'react'; +import { createClientRender, describeConformanceV5 } from 'test/utils'; +import MasonryItem, { masonryItemClasses as classes } from '@material-ui/lab/MasonryItem'; + +describe('', () => { + const render = createClientRender(); + + describeConformanceV5( + +
+ , + () => ({ + classes, + inheritComponent: 'div', + render, + refInstanceof: window.HTMLDivElement, + testComponentPropWith: 'div', + testVariantProps: { variant: 'foo' }, + muiName: 'MuiMasonryItem', + skip: ['componentsProp'], + }), + ); +}); diff --git a/packages/material-ui-lab/src/MasonryItem/index.d.ts b/packages/material-ui-lab/src/MasonryItem/index.d.ts new file mode 100644 index 00000000000000..5f7ad58b1ae710 --- /dev/null +++ b/packages/material-ui-lab/src/MasonryItem/index.d.ts @@ -0,0 +1,5 @@ +export * from './MasonryItem'; +export { default } from './MasonryItem'; + +export * from './masonryItemClasses'; +export { default as masonryItemClasses } from './masonryItemClasses'; diff --git a/packages/material-ui-lab/src/MasonryItem/index.js b/packages/material-ui-lab/src/MasonryItem/index.js new file mode 100644 index 00000000000000..63ed88ba665ab7 --- /dev/null +++ b/packages/material-ui-lab/src/MasonryItem/index.js @@ -0,0 +1,3 @@ +export { default } from './MasonryItem'; +export * from './masonryItemClasses'; +export { default as masonryItemClasses } from './masonryItemClasses'; diff --git a/packages/material-ui-lab/src/MasonryItem/masonryItemClasses.ts b/packages/material-ui-lab/src/MasonryItem/masonryItemClasses.ts new file mode 100644 index 00000000000000..38e9be4fa595e8 --- /dev/null +++ b/packages/material-ui-lab/src/MasonryItem/masonryItemClasses.ts @@ -0,0 +1,16 @@ +import { generateUtilityClass, generateUtilityClasses } from '@material-ui/unstyled'; + +export interface MasonryItemClasses { + /** Styles applied to the root element. */ + root: string; +} + +export type MasonryItemClassKey = keyof MasonryItemClasses; + +export function getMasonryItemUtilityClass(slot: string): string { + return generateUtilityClass('MuiMasonryItem', slot); +} + +const masonryItemClasses: MasonryItemClasses = generateUtilityClasses('MuiMasonryItem', ['root']); + +export default masonryItemClasses; diff --git a/packages/material-ui-system/src/breakpoints.js b/packages/material-ui-system/src/breakpoints.js index 084339319d2404..b070c42e9f8f9f 100644 --- a/packages/material-ui-system/src/breakpoints.js +++ b/packages/material-ui-system/src/breakpoints.js @@ -113,4 +113,27 @@ export function mergeBreakpointsInOrder(breakpointsInput, ...styles) { return removeUnusedBreakpoints(Object.keys(emptyBreakpoints), mergedOutput); } +export function resolveBreakpointValues({ values: breakpointValues, base }) { + const keys = Object.keys(base); + + if (keys.length === 0) { + return breakpointValues; + } + + let previous; + + return keys.reduce((acc, breakpoint) => { + if (typeof breakpointValues === 'object') { + acc[breakpoint] = + breakpointValues[breakpoint] != null + ? breakpointValues[breakpoint] + : breakpointValues[previous]; + } else { + acc[breakpoint] = breakpointValues; + } + previous = breakpoint; + return acc; + }, {}); +} + export default breakpoints; diff --git a/packages/material-ui-system/src/index.js b/packages/material-ui-system/src/index.js index c70da053d26e83..e0918b6471e4b2 100644 --- a/packages/material-ui-system/src/index.js +++ b/packages/material-ui-system/src/index.js @@ -2,7 +2,7 @@ export { css, keyframes, GlobalStyles, StyledEngineProvider } from '@material-ui export { default as borders } from './borders'; export * from './borders'; export { default as breakpoints } from './breakpoints'; -export { handleBreakpoints, mergeBreakpointsInOrder } from './breakpoints'; +export { handleBreakpoints, mergeBreakpointsInOrder, resolveBreakpointValues } from './breakpoints'; export { default as compose } from './compose'; export { default as display } from './display'; export { default as flexbox } from './flexbox'; diff --git a/packages/material-ui/src/Grid/Grid.js b/packages/material-ui/src/Grid/Grid.js index f864f5bce7a1de..88d2f820887069 100644 --- a/packages/material-ui/src/Grid/Grid.js +++ b/packages/material-ui/src/Grid/Grid.js @@ -12,7 +12,11 @@ import * as React from 'react'; import PropTypes from 'prop-types'; import clsx from 'clsx'; -import { unstable_extendSxProp as extendSxProp, handleBreakpoints } from '@material-ui/system'; +import { + unstable_extendSxProp as extendSxProp, + handleBreakpoints, + resolveBreakpointValues, +} from '@material-ui/system'; import { unstable_composeClasses as composeClasses } from '@material-ui/unstyled'; import requirePropFactory from '../utils/requirePropFactory'; import styled from '../styles/styled'; @@ -25,27 +29,6 @@ function getOffset(val) { return `${parse}${String(val).replace(String(parse), '') || 'px'}`; } -// Duplicated with Stack.js -function resolveBreakpointValues({ values, base }) { - const keys = Object.keys(base); - - if (keys.length === 0) { - return values; - } - - let previous; - - return keys.reduce((acc, breakpoint) => { - if (typeof values === 'object') { - acc[breakpoint] = values[breakpoint] != null ? values[breakpoint] : values[previous]; - } else { - acc[breakpoint] = values; - } - previous = breakpoint; - return acc; - }, {}); -} - function generateGrid(globalStyles, theme, breakpoint, ownerState) { const size = ownerState[breakpoint]; diff --git a/packages/material-ui/src/Stack/Stack.js b/packages/material-ui/src/Stack/Stack.js index d951873c43ba34..c0075bb13e7186 100644 --- a/packages/material-ui/src/Stack/Stack.js +++ b/packages/material-ui/src/Stack/Stack.js @@ -5,6 +5,7 @@ import { getValue, handleBreakpoints, unstable_extendSxProp as extendSxProp, + resolveBreakpointValues, } from '@material-ui/system'; import { deepmerge } from '@material-ui/utils'; import styled from '../styles/styled'; @@ -31,27 +32,6 @@ function joinChildren(children, separator) { }, []); } -// Duplicated with Grid.js -function resolveBreakpointValues({ values, base }) { - const keys = Object.keys(base); - - if (keys.length === 0) { - return values; - } - - let previous; - - return keys.reduce((acc, breakpoint) => { - if (typeof values === 'object') { - acc[breakpoint] = values[breakpoint] != null ? values[breakpoint] : values[previous]; - } else { - acc[breakpoint] = values; - } - previous = breakpoint; - return acc; - }, {}); -} - const getSideFromDirection = (direction) => { return { row: 'Left', From caa08c5a33ec394495a6aea79b5a2bed805b6c77 Mon Sep 17 00:00:00 2001 From: Benny Joo Date: Tue, 27 Jul 2021 11:06:31 +0100 Subject: [PATCH 02/59] Receive columnSpan prop for MasonryItem and use ResizeObserver --- docs/pages/api-docs/masonry-item.json | 1 + docs/pages/api-docs/masonry.json | 2 +- .../pages/components/masonry/BasicMasonry.js | 8 +- .../pages/components/masonry/BasicMasonry.tsx | 10 +- .../components/masonry/DiffColSizeMasonry.js | 44 ++++++ .../components/masonry/DiffColSizeMasonry.tsx | 44 ++++++ .../pages/components/masonry/ImageMasonry.js | 95 ++++++------- .../pages/components/masonry/ImageMasonry.tsx | 97 +++++++------- docs/src/pages/components/masonry/masonry.md | 6 +- .../api-docs/masonry-item/masonry-item.json | 1 + .../api-docs/masonry/masonry.json | 2 +- package.json | 1 + .../material-ui-lab/src/Masonry/Masonry.d.ts | 2 +- .../material-ui-lab/src/Masonry/Masonry.js | 81 +++++------ .../src/Masonry/Masonry.test.js | 126 ++++++++++++++++++ .../src/MasonryItem/MasonryItem.d.ts | 5 + .../src/MasonryItem/MasonryItem.js | 82 ++++++------ .../src/MasonryItem/MasonryItem.test.js | 110 ++++++++++++++- packages/material-ui-system/src/index.js | 6 +- packages/material-ui/src/Grid/Grid.js | 2 +- packages/material-ui/src/Stack/Stack.js | 2 +- yarn.lock | 5 + 22 files changed, 539 insertions(+), 193 deletions(-) create mode 100644 docs/src/pages/components/masonry/DiffColSizeMasonry.js create mode 100644 docs/src/pages/components/masonry/DiffColSizeMasonry.tsx diff --git a/docs/pages/api-docs/masonry-item.json b/docs/pages/api-docs/masonry-item.json index 7e2fb0fa099e95..6e0c37d63f423c 100644 --- a/docs/pages/api-docs/masonry-item.json +++ b/docs/pages/api-docs/masonry-item.json @@ -2,6 +2,7 @@ "props": { "children": { "type": { "name": "element" }, "required": true }, "classes": { "type": { "name": "object" } }, + "columnSpan": { "type": { "name": "number" }, "default": "1" }, "component": { "type": { "name": "elementType" } }, "sx": { "type": { "name": "object" } } }, diff --git a/docs/pages/api-docs/masonry.json b/docs/pages/api-docs/masonry.json index 2b111870fb2a3e..fc83112a6a8a9a 100644 --- a/docs/pages/api-docs/masonry.json +++ b/docs/pages/api-docs/masonry.json @@ -2,7 +2,7 @@ "props": { "children": { "type": { "name": "node" }, "required": true }, "classes": { "type": { "name": "object" } }, - "cols": { + "columns": { "type": { "name": "union", "description": "Array<number
| string>
| number
| object
| string" diff --git a/docs/src/pages/components/masonry/BasicMasonry.js b/docs/src/pages/components/masonry/BasicMasonry.js index 12f0ace16a64f1..54d8a1c01b88db 100644 --- a/docs/src/pages/components/masonry/BasicMasonry.js +++ b/docs/src/pages/components/masonry/BasicMasonry.js @@ -6,9 +6,9 @@ import MasonryItem from '@material-ui/lab/MasonryItem'; export default function BasicMasonry() { return ( - - {heights.map((height, idx) => ( - + + {heights.map((height, index) => ( + - {idx + 1} + {index + 1} ))} diff --git a/docs/src/pages/components/masonry/BasicMasonry.tsx b/docs/src/pages/components/masonry/BasicMasonry.tsx index 5368dca95c091b..54d8a1c01b88db 100644 --- a/docs/src/pages/components/masonry/BasicMasonry.tsx +++ b/docs/src/pages/components/masonry/BasicMasonry.tsx @@ -4,11 +4,11 @@ import Box from '@material-ui/core/Box'; import Masonry from '@material-ui/lab/Masonry'; import MasonryItem from '@material-ui/lab/MasonryItem'; -export default function BasicMasonry(): JSX.Element { +export default function BasicMasonry() { return ( - - {heights.map((height, idx) => ( - + + {heights.map((height, index) => ( + - {idx + 1} + {index + 1} ))} diff --git a/docs/src/pages/components/masonry/DiffColSizeMasonry.js b/docs/src/pages/components/masonry/DiffColSizeMasonry.js new file mode 100644 index 00000000000000..b02dd96d6738e8 --- /dev/null +++ b/docs/src/pages/components/masonry/DiffColSizeMasonry.js @@ -0,0 +1,44 @@ +/* eslint-disable @typescript-eslint/no-use-before-define */ +import * as React from 'react'; +import Box from '@material-ui/core/Box'; +import Masonry from '@material-ui/lab/Masonry'; +import MasonryItem from '@material-ui/lab/MasonryItem'; + +export default function DiffColSizeMasonry() { + return ( + + {itemData.map((item, index) => ( + + + {index + 1} + + + ))} + + ); +} + +const itemData = [ + { height: 150, span: 1 }, + { height: 30, span: 1 }, + { height: 90, span: 2 }, + { height: 110, span: 1 }, + { height: 150, span: 1 }, + { height: 150, span: 1 }, + { height: 130, span: 2 }, + { height: 80, span: 2 }, + { height: 50, span: 1 }, + { height: 90, span: 1 }, + { height: 100, span: 2 }, + { height: 150, span: 1 }, + { height: 50, span: 1 }, + { height: 50, span: 2 }, + { height: 50, span: 1 }, +]; diff --git a/docs/src/pages/components/masonry/DiffColSizeMasonry.tsx b/docs/src/pages/components/masonry/DiffColSizeMasonry.tsx new file mode 100644 index 00000000000000..b02dd96d6738e8 --- /dev/null +++ b/docs/src/pages/components/masonry/DiffColSizeMasonry.tsx @@ -0,0 +1,44 @@ +/* eslint-disable @typescript-eslint/no-use-before-define */ +import * as React from 'react'; +import Box from '@material-ui/core/Box'; +import Masonry from '@material-ui/lab/Masonry'; +import MasonryItem from '@material-ui/lab/MasonryItem'; + +export default function DiffColSizeMasonry() { + return ( + + {itemData.map((item, index) => ( + + + {index + 1} + + + ))} + + ); +} + +const itemData = [ + { height: 150, span: 1 }, + { height: 30, span: 1 }, + { height: 90, span: 2 }, + { height: 110, span: 1 }, + { height: 150, span: 1 }, + { height: 150, span: 1 }, + { height: 130, span: 2 }, + { height: 80, span: 2 }, + { height: 50, span: 1 }, + { height: 90, span: 1 }, + { height: 100, span: 2 }, + { height: 150, span: 1 }, + { height: 50, span: 1 }, + { height: 50, span: 2 }, + { height: 50, span: 1 }, +]; diff --git a/docs/src/pages/components/masonry/ImageMasonry.js b/docs/src/pages/components/masonry/ImageMasonry.js index 097d7d8012cf27..4aa77116218355 100644 --- a/docs/src/pages/components/masonry/ImageMasonry.js +++ b/docs/src/pages/components/masonry/ImageMasonry.js @@ -6,98 +6,103 @@ import MasonryItem from '@material-ui/lab/MasonryItem'; export default function ImageMasonry() { return ( - {imgData.map((item, idx) => ( - - {item.title} + {itemData.map((item) => ( + + {item.title} ))} ); } -const imgData = [ +const itemData = [ { - img: 'https://images.unsplash.com/photo-1529655683826-aba9b3e77383?ixid=MnwxMjA3fDB8MHxzZWFyY2h8Mnx8bG9uZG9ufGVufDB8fDB8fA%3D%3D&ixlib=rb-1.2.1&w=1000&q=80', - title: '1', + img: 'https://images.unsplash.com/photo-1518756131217-31eb79b20e8f', + title: 'Fern', }, { - img: 'https://images.pexels.com/photos/1761279/pexels-photo-1761279.jpeg?auto=compress&cs=tinysrgb&dpr=1&w=500', - title: '2', + img: 'https://images.unsplash.com/photo-1597645587822-e99fa5d45d25', + title: 'Mushrooms', }, { - img: 'https://www.nature.com/immersive/d41586-021-00095-y/assets/3TP4N718ac/2021-01-xx_jan-iom_tree-of-life_sh-1080x1440.jpeg', - title: '3', + img: 'https://images.unsplash.com/photo-1529655683826-aba9b3e77383?ixid=MnwxMjA3fDB8MHxzZWFyY2h8Mnx8bG9uZG9ufGVufDB8fDB8fA%3D%3D&ixlib=rb-1.2.1&w=1000&q=80', + title: 'Tower', }, { - img: 'https://www.gettyimages.com/gi-resources/images/500px/983794168.jpg', - title: '4', + img: 'https://images.unsplash.com/photo-1471357674240-e1a485acb3e1', + title: 'Sea star', }, { - img: 'https://cdn.pixabay.com/photo/2015/04/23/22/00/tree-736885__480.jpg', - title: '5', - }, - { - img: 'https://images.pexels.com/photos/674010/pexels-photo-674010.jpeg?auto=compress&cs=tinysrgb&dpr=1&w=500', - title: '6', + img: 'https://images.pexels.com/photos/1761279/pexels-photo-1761279.jpeg?auto=compress&cs=tinysrgb&dpr=1&w=500', + title: 'Bridge', }, { - img: 'https://cdn.pixabay.com/photo/2016/05/05/02/37/sunset-1373171__340.jpg', - title: '7', + img: 'https://images.unsplash.com/photo-1558642452-9d2a7deb7f62', + title: 'Honey', }, { - img: 'https://cdn.pixabay.com/photo/2015/04/19/08/32/marguerite-729510__340.jpg', - title: '8', + img: 'https://www.nature.com/immersive/d41586-021-00095-y/assets/3TP4N718ac/2021-01-xx_jan-iom_tree-of-life_sh-1080x1440.jpeg', + title: 'Lake', }, { - img: 'https://images.ctfassets.net/hrltx12pl8hq/3MbF54EhWUhsXunc5Keueb/60774fbbff86e6bf6776f1e17a8016b4/04-nature_721703848.jpg?fit=fill&w=480&h=270', - title: '9', + img: 'https://images.unsplash.com/photo-1516802273409-68526ee1bdd6', + title: 'Basketball', }, { img: 'https://www.gettyimages.com/gi-resources/images/500px/983794168.jpg', - title: '10', + title: 'Birds', }, { - img: 'https://images.unsplash.com/photo-1529655683826-aba9b3e77383?ixid=MnwxMjA3fDB8MHxzZWFyY2h8Mnx8bG9uZG9ufGVufDB8fDB8fA%3D%3D&ixlib=rb-1.2.1&w=1000&q=80', - title: '11', + img: 'https://images.unsplash.com/photo-1551963831-b3b1ca40c98e', + title: 'Breakfast', }, { - img: 'https://images.pexels.com/photos/1761279/pexels-photo-1761279.jpeg?auto=compress&cs=tinysrgb&dpr=1&w=500', - title: '12', + img: 'https://cdn.pixabay.com/photo/2015/04/23/22/00/tree-736885__480.jpg', + title: 'Sunset', }, { - img: 'https://www.nature.com/immersive/d41586-021-00095-y/assets/3TP4N718ac/2021-01-xx_jan-iom_tree-of-life_sh-1080x1440.jpeg', - title: '13', + img: 'https://images.unsplash.com/photo-1551782450-a2132b4ba21d', + title: 'Burger', }, { - img: 'https://www.gettyimages.com/gi-resources/images/500px/983794168.jpg', - title: '14', + img: 'https://images.pexels.com/photos/674010/pexels-photo-674010.jpeg?auto=compress&cs=tinysrgb&dpr=1&w=500', + title: 'Picture', }, { - img: 'https://cdn.pixabay.com/photo/2015/04/23/22/00/tree-736885__480.jpg', - title: '15', + img: 'https://images.unsplash.com/photo-1522770179533-24471fcdba45', + title: 'Camera', }, { - img: 'https://images.pexels.com/photos/674010/pexels-photo-674010.jpeg?auto=compress&cs=tinysrgb&dpr=1&w=500', - title: '16', + img: 'https://cdn.pixabay.com/photo/2016/05/05/02/37/sunset-1373171__340.jpg', + title: 'Landscape', }, { - img: 'https://cdn.pixabay.com/photo/2016/05/05/02/37/sunset-1373171__340.jpg', - title: '17', + img: 'https://images.unsplash.com/photo-1444418776041-9c7e33cc5a9c', + title: 'Coffee', }, { img: 'https://cdn.pixabay.com/photo/2015/04/19/08/32/marguerite-729510__340.jpg', - title: '18', + title: 'Flower', }, { - img: 'https://images.ctfassets.net/hrltx12pl8hq/3MbF54EhWUhsXunc5Keueb/60774fbbff86e6bf6776f1e17a8016b4/04-nature_721703848.jpg?fit=fill&w=480&h=270', - title: '19', + img: 'https://images.unsplash.com/photo-1533827432537-70133748f5c8', + title: 'Hats', }, { - img: 'https://www.gettyimages.com/gi-resources/images/500px/983794168.jpg', - title: '20', + img: 'https://images.unsplash.com/photo-1567306301408-9b74779a11af', + title: 'Tomato basil', + }, + { + img: 'https://images.unsplash.com/photo-1589118949245-7d38baf380d6', + title: 'Bike', }, ]; diff --git a/docs/src/pages/components/masonry/ImageMasonry.tsx b/docs/src/pages/components/masonry/ImageMasonry.tsx index 9ce7bea53a4bef..4aa77116218355 100644 --- a/docs/src/pages/components/masonry/ImageMasonry.tsx +++ b/docs/src/pages/components/masonry/ImageMasonry.tsx @@ -3,101 +3,106 @@ import * as React from 'react'; import Masonry from '@material-ui/lab/Masonry'; import MasonryItem from '@material-ui/lab/MasonryItem'; -export default function ImageMasonry(): JSX.Element { +export default function ImageMasonry() { return ( - {imgData.map((item, idx) => ( - - {item.title} + {itemData.map((item) => ( + + {item.title} ))} ); } -const imgData = [ +const itemData = [ { - img: 'https://images.unsplash.com/photo-1529655683826-aba9b3e77383?ixid=MnwxMjA3fDB8MHxzZWFyY2h8Mnx8bG9uZG9ufGVufDB8fDB8fA%3D%3D&ixlib=rb-1.2.1&w=1000&q=80', - title: '1', + img: 'https://images.unsplash.com/photo-1518756131217-31eb79b20e8f', + title: 'Fern', }, { - img: 'https://images.pexels.com/photos/1761279/pexels-photo-1761279.jpeg?auto=compress&cs=tinysrgb&dpr=1&w=500', - title: '2', + img: 'https://images.unsplash.com/photo-1597645587822-e99fa5d45d25', + title: 'Mushrooms', }, { - img: 'https://www.nature.com/immersive/d41586-021-00095-y/assets/3TP4N718ac/2021-01-xx_jan-iom_tree-of-life_sh-1080x1440.jpeg', - title: '3', + img: 'https://images.unsplash.com/photo-1529655683826-aba9b3e77383?ixid=MnwxMjA3fDB8MHxzZWFyY2h8Mnx8bG9uZG9ufGVufDB8fDB8fA%3D%3D&ixlib=rb-1.2.1&w=1000&q=80', + title: 'Tower', }, { - img: 'https://www.gettyimages.com/gi-resources/images/500px/983794168.jpg', - title: '4', + img: 'https://images.unsplash.com/photo-1471357674240-e1a485acb3e1', + title: 'Sea star', }, { - img: 'https://cdn.pixabay.com/photo/2015/04/23/22/00/tree-736885__480.jpg', - title: '5', - }, - { - img: 'https://images.pexels.com/photos/674010/pexels-photo-674010.jpeg?auto=compress&cs=tinysrgb&dpr=1&w=500', - title: '6', + img: 'https://images.pexels.com/photos/1761279/pexels-photo-1761279.jpeg?auto=compress&cs=tinysrgb&dpr=1&w=500', + title: 'Bridge', }, { - img: 'https://cdn.pixabay.com/photo/2016/05/05/02/37/sunset-1373171__340.jpg', - title: '7', + img: 'https://images.unsplash.com/photo-1558642452-9d2a7deb7f62', + title: 'Honey', }, { - img: 'https://cdn.pixabay.com/photo/2015/04/19/08/32/marguerite-729510__340.jpg', - title: '8', + img: 'https://www.nature.com/immersive/d41586-021-00095-y/assets/3TP4N718ac/2021-01-xx_jan-iom_tree-of-life_sh-1080x1440.jpeg', + title: 'Lake', }, { - img: 'https://images.ctfassets.net/hrltx12pl8hq/3MbF54EhWUhsXunc5Keueb/60774fbbff86e6bf6776f1e17a8016b4/04-nature_721703848.jpg?fit=fill&w=480&h=270', - title: '9', + img: 'https://images.unsplash.com/photo-1516802273409-68526ee1bdd6', + title: 'Basketball', }, { img: 'https://www.gettyimages.com/gi-resources/images/500px/983794168.jpg', - title: '10', + title: 'Birds', }, { - img: 'https://images.unsplash.com/photo-1529655683826-aba9b3e77383?ixid=MnwxMjA3fDB8MHxzZWFyY2h8Mnx8bG9uZG9ufGVufDB8fDB8fA%3D%3D&ixlib=rb-1.2.1&w=1000&q=80', - title: '11', + img: 'https://images.unsplash.com/photo-1551963831-b3b1ca40c98e', + title: 'Breakfast', }, { - img: 'https://images.pexels.com/photos/1761279/pexels-photo-1761279.jpeg?auto=compress&cs=tinysrgb&dpr=1&w=500', - title: '12', + img: 'https://cdn.pixabay.com/photo/2015/04/23/22/00/tree-736885__480.jpg', + title: 'Sunset', }, { - img: 'https://www.nature.com/immersive/d41586-021-00095-y/assets/3TP4N718ac/2021-01-xx_jan-iom_tree-of-life_sh-1080x1440.jpeg', - title: '13', + img: 'https://images.unsplash.com/photo-1551782450-a2132b4ba21d', + title: 'Burger', }, { - img: 'https://www.gettyimages.com/gi-resources/images/500px/983794168.jpg', - title: '14', + img: 'https://images.pexels.com/photos/674010/pexels-photo-674010.jpeg?auto=compress&cs=tinysrgb&dpr=1&w=500', + title: 'Picture', }, { - img: 'https://cdn.pixabay.com/photo/2015/04/23/22/00/tree-736885__480.jpg', - title: '15', + img: 'https://images.unsplash.com/photo-1522770179533-24471fcdba45', + title: 'Camera', }, { - img: 'https://images.pexels.com/photos/674010/pexels-photo-674010.jpeg?auto=compress&cs=tinysrgb&dpr=1&w=500', - title: '16', + img: 'https://cdn.pixabay.com/photo/2016/05/05/02/37/sunset-1373171__340.jpg', + title: 'Landscape', }, { - img: 'https://cdn.pixabay.com/photo/2016/05/05/02/37/sunset-1373171__340.jpg', - title: '17', + img: 'https://images.unsplash.com/photo-1444418776041-9c7e33cc5a9c', + title: 'Coffee', }, { img: 'https://cdn.pixabay.com/photo/2015/04/19/08/32/marguerite-729510__340.jpg', - title: '18', + title: 'Flower', }, { - img: 'https://images.ctfassets.net/hrltx12pl8hq/3MbF54EhWUhsXunc5Keueb/60774fbbff86e6bf6776f1e17a8016b4/04-nature_721703848.jpg?fit=fill&w=480&h=270', - title: '19', + img: 'https://images.unsplash.com/photo-1533827432537-70133748f5c8', + title: 'Hats', }, { - img: 'https://www.gettyimages.com/gi-resources/images/500px/983794168.jpg', - title: '20', + img: 'https://images.unsplash.com/photo-1567306301408-9b74779a11af', + title: 'Tomato basil', + }, + { + img: 'https://images.unsplash.com/photo-1589118949245-7d38baf380d6', + title: 'Bike', }, ]; diff --git a/docs/src/pages/components/masonry/masonry.md b/docs/src/pages/components/masonry/masonry.md index c6d66d4765c6c2..055ffcdcf7904d 100644 --- a/docs/src/pages/components/masonry/masonry.md +++ b/docs/src/pages/components/masonry/masonry.md @@ -10,7 +10,7 @@ githubLabel: 'component: Masonry' Masonry maintains a list of content blocks with a consistent width but variable height. The contents are ordered by row. If a row is already filled with the specified number of columns, the next item starts another row, and it is added to the shortest column. -{{"component": "modules/components/ComponentLinkHeader.js"}} +{{"component": "modules/components/ComponentLinkHeader.js", "design": true}} ## Masonry @@ -19,3 +19,7 @@ Masonry maintains a list of content blocks with a consistent width but variable > `` can receive not only `` elements but also `
` elements as shown in the following demo: {{"demo": "pages/components/masonry/BasicMasonry.js", "bg": true}} + +> You can configure the number of columns taken up by each `` as shown in the following demo: + +{{"demo": "pages/components/masonry/DiffColSizeMasonry.js", "bg": true}} diff --git a/docs/translations/api-docs/masonry-item/masonry-item.json b/docs/translations/api-docs/masonry-item/masonry-item.json index 308b7edb5b2474..d0d9953b91fa7c 100644 --- a/docs/translations/api-docs/masonry-item/masonry-item.json +++ b/docs/translations/api-docs/masonry-item/masonry-item.json @@ -3,6 +3,7 @@ "propDescriptions": { "children": "The content of the component, normally an <img /> or a <div />.", "classes": "Override or extend the styles applied to the component. See CSS API below for more details.", + "columnSpan": "The number of columns taken up by the component", "component": "The component used for the root node. Either a string to use a HTML element or a component.", "sx": "Allows defining system overrides as well as additional CSS styles. See the `sx` page for more details." }, diff --git a/docs/translations/api-docs/masonry/masonry.json b/docs/translations/api-docs/masonry/masonry.json index 7b4d84fdc144ea..529c7b45599a51 100644 --- a/docs/translations/api-docs/masonry/masonry.json +++ b/docs/translations/api-docs/masonry/masonry.json @@ -3,7 +3,7 @@ "propDescriptions": { "children": "The content of the component, normally <MasonryItem />s.", "classes": "Override or extend the styles applied to the component. See CSS API below for more details.", - "cols": "Number of columns.", + "columns": "Number of columns.", "component": "The component used for the root node. Either a string to use a HTML element or a component.", "spacing": "Defines the space between children.", "sx": "Allows defining system overrides as well as additional CSS styles. See the `sx` page for more details." diff --git a/package.json b/package.json index 5f9513b9aa9839..d0da3691eadfcc 100644 --- a/package.json +++ b/package.json @@ -157,6 +157,7 @@ "react-router-dom": "^5.2.0", "react-test-renderer": "^17.0.2", "remark": "^13.0.0", + "resize-observer": "^1.0.2", "rimraf": "^3.0.0", "rollup": "^2.56.2", "rollup-plugin-babel": "^4.3.3", diff --git a/packages/material-ui-lab/src/Masonry/Masonry.d.ts b/packages/material-ui-lab/src/Masonry/Masonry.d.ts index 22c51ee32b415b..9b02d573f55a65 100644 --- a/packages/material-ui-lab/src/Masonry/Masonry.d.ts +++ b/packages/material-ui-lab/src/Masonry/Masonry.d.ts @@ -17,7 +17,7 @@ export interface MasonryTypeMap

{ * Number of columns. * @default 4 */ - cols?: ResponsiveStyleValue; + columns?: ResponsiveStyleValue; /** * Defines the space between children. * @default 1 diff --git a/packages/material-ui-lab/src/Masonry/Masonry.js b/packages/material-ui-lab/src/Masonry/Masonry.js index 50b42ab9656f61..f68b9fc0c4e879 100644 --- a/packages/material-ui-lab/src/Masonry/Masonry.js +++ b/packages/material-ui-lab/src/Masonry/Masonry.js @@ -5,7 +5,7 @@ import { createUnarySpacing, getValue, handleBreakpoints, - resolveBreakpointValues, + unstable_resolveBreakpointValues as resolveBreakpointValues, } from '@material-ui/system'; import { deepmerge } from '@material-ui/utils'; import { unstable_composeClasses as composeClasses } from '@material-ui/unstyled'; @@ -29,14 +29,8 @@ export const style = ({ styleProps, theme }) => { gridAutoRows: 0, padding: 0, overflow: 'auto', - width: '100% !important', + width: '100%', rowGap: 1, - ...((typeof styleProps.spacing === 'string' || typeof styleProps.spacing === 'number') && { - columnGap: theme.spacing(styleProps.spacing), - }), - ...((typeof styleProps.cols === 'string' || typeof styleProps.cols === 'number') && { - gridTemplateColumns: `repeat(${styleProps.cols}, 1fr)`, - }), }; const base = Object.keys(theme.breakpoints.values).reduce((acc, breakpoint) => { @@ -46,34 +40,27 @@ export const style = ({ styleProps, theme }) => { return acc; }, {}); - if (typeof styleProps.spacing === 'object' || Array.isArray(styleProps.spacing)) { - const spacingValues = resolveBreakpointValues({ values: styleProps.spacing, base }); - const transformer = createUnarySpacing(theme); - const spacingStyleFromPropValue = (propValue) => { - return { - columnGap: getValue(transformer, propValue), - }; + const spacingValues = resolveBreakpointValues({ values: styleProps.spacing, base }); + const transformer = createUnarySpacing(theme); + const spacingStyleFromPropValue = (propValue) => { + return { + columnGap: getValue(transformer, propValue), }; + }; + + styles = deepmerge( + styles, + handleBreakpoints({ theme }, spacingValues, spacingStyleFromPropValue), + ); - styles = deepmerge( - styles, - handleBreakpoints({ theme }, spacingValues, spacingStyleFromPropValue), - ); - } - - if (typeof styleProps.cols === 'object' || Array.isArray(styleProps.cols)) { - const columnValues = resolveBreakpointValues({ values: styleProps.cols, base }); - const columnStyleFromPropValue = (propValue) => { - return { - gridTemplateColumns: `repeat(${propValue}, 1fr)`, - }; + const columnValues = resolveBreakpointValues({ values: styleProps.columns, base }); + const columnStyleFromPropValue = (propValue) => { + return { + gridTemplateColumns: `repeat(${propValue}, 1fr)`, }; + }; - styles = deepmerge( - styles, - handleBreakpoints({ theme }, columnValues, columnStyleFromPropValue), - ); - } + styles = deepmerge(styles, handleBreakpoints({ theme }, columnValues, columnStyleFromPropValue)); return styles; }; @@ -92,8 +79,8 @@ const Masonry = React.forwardRef(function Masonry(inProps, ref) { name: 'MuiMasonry', }); - const { children, className, component = 'div', cols = 4, spacing = 1, ...other } = props; - const styleProps = { ...props, spacing, cols }; + const { children, className, component = 'div', columns = 4, spacing = 1, ...other } = props; + const styleProps = { ...props, spacing, columns }; const classes = useUtilityClasses(styleProps); const [documentReady, setDocumentReady] = React.useState(false); const handleStateChange = () => { @@ -102,23 +89,25 @@ const Masonry = React.forwardRef(function Masonry(inProps, ref) { } }; React.useEffect(() => { - document.addEventListener('readystatechange', handleStateChange);; + document.addEventListener('readystatechange', handleStateChange); return () => { document.removeEventListener('readystatechange', handleStateChange); }; }); + const masonry = React.useMemo(() => ({ spacing, documentReady }), [spacing, documentReady]); + return ( - - + + {children} - - + + ); }); @@ -143,7 +132,7 @@ Masonry.propTypes /* remove-proptypes */ = { * Number of columns. * @default 4 */ - cols: PropTypes.oneOfType([ + columns: PropTypes.oneOfType([ PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.number, PropTypes.string])), PropTypes.number, PropTypes.object, diff --git a/packages/material-ui-lab/src/Masonry/Masonry.test.js b/packages/material-ui-lab/src/Masonry/Masonry.test.js index a4c04a62cb9e20..6b4e18f8082feb 100644 --- a/packages/material-ui-lab/src/Masonry/Masonry.test.js +++ b/packages/material-ui-lab/src/Masonry/Masonry.test.js @@ -1,6 +1,10 @@ +import { expect } from 'chai'; import * as React from 'react'; import { createClientRender, describeConformanceV5 } from 'test/utils'; import Masonry, { masonryClasses as classes } from '@material-ui/lab/Masonry'; +import { createTheme } from '@material-ui/core/styles'; +import defaultTheme from '@material-ui/core/styles/defaultTheme'; +import { style } from './Masonry'; describe('', () => { const render = createClientRender(); @@ -20,4 +24,126 @@ describe('', () => { skip: ['componentsProp'], }), ); + + const itemsData = [ + { + img: '/fake1.png', + title: 'fake1', + }, + { + img: '/fake2.png', + title: 'fake2', + }, + ]; + const theme = createTheme({ spacing: 8 }); + const children = itemsData.map((item, idx) => ( +

+ {item.title} +
+ )); + + describe('style attribute:', () => { + it('should render with correct default styles', () => { + expect( + style({ + styleProps: { + columns: 4, + spacing: 1, + }, + theme, + }), + ).to.deep.equal({ + display: 'grid', + gridAutoRows: 0, + padding: 0, + overflow: 'auto', + width: '100%', + rowGap: 1, + columnGap: theme.spacing(1), + gridTemplateColumns: 'repeat(4, 1fr)', + }); + }); + + it('should render with column gap responsive to breakpoints', () => { + expect( + style({ + styleProps: { + columns: 4, + spacing: { xs: 1, sm: 2, md: 3 }, + }, + theme, + }), + ).to.deep.equal({ + '@media (min-width:0px)': { + columnGap: theme.spacing(1), + gridTemplateColumns: 'repeat(4, 1fr)', + }, + [`@media (min-width:${defaultTheme.breakpoints.values.sm}px)`]: { + columnGap: theme.spacing(2), + gridTemplateColumns: 'repeat(4, 1fr)', + }, + [`@media (min-width:${defaultTheme.breakpoints.values.md}px)`]: { + columnGap: theme.spacing(3), + gridTemplateColumns: 'repeat(4, 1fr)', + }, + display: 'grid', + gridAutoRows: 0, + padding: 0, + overflow: 'auto', + width: '100%', + rowGap: 1, + }); + }); + + it('should render with grid-template-columns responsive to breakpoints', () => { + expect( + style({ + styleProps: { + columns: { xs: 3, sm: 5, md: 7 }, + spacing: 1, + }, + theme, + }), + ).to.deep.equal({ + '@media (min-width:0px)': { + gridTemplateColumns: 'repeat(3, 1fr)', + }, + [`@media (min-width:${defaultTheme.breakpoints.values.sm}px)`]: { + gridTemplateColumns: 'repeat(5, 1fr)', + }, + [`@media (min-width:${defaultTheme.breakpoints.values.md}px)`]: { + gridTemplateColumns: 'repeat(7, 1fr)', + }, + display: 'grid', + gridAutoRows: 0, + padding: 0, + overflow: 'auto', + width: '100%', + columnGap: theme.spacing(1), + rowGap: 1, + }); + }); + }); + + describe('props:', () => { + describe('prop: component', () => { + it('should render a div by default', () => { + const { container } = render({children}); + expect(container.firstChild).to.have.property('nodeName', 'DIV'); + }); + + it('should render a different component', () => { + const { container } = render({children}); + expect(container.firstChild).to.have.property('nodeName', 'SPAN'); + }); + }); + + describe('prop: className', () => { + it('should append the className to the root element', () => { + const { container } = render({children}); + expect(container.firstChild).to.have.class(classes.root); + expect(container.firstChild).to.have.class('foo'); + }); + }); + }); }); diff --git a/packages/material-ui-lab/src/MasonryItem/MasonryItem.d.ts b/packages/material-ui-lab/src/MasonryItem/MasonryItem.d.ts index efdab2c34a854c..dd08b9bf0da1af 100644 --- a/packages/material-ui-lab/src/MasonryItem/MasonryItem.d.ts +++ b/packages/material-ui-lab/src/MasonryItem/MasonryItem.d.ts @@ -13,6 +13,11 @@ export interface MasonryItemTypeMap

* Override or extend the styles applied to the component. */ classes?: Partial; + /** + * The number of columns taken up by the component + * @default 1 + */ + columnSpan?: number; /** * Allows defining system overrides as well as additional CSS styles. */ diff --git a/packages/material-ui-lab/src/MasonryItem/MasonryItem.js b/packages/material-ui-lab/src/MasonryItem/MasonryItem.js index 16a760b7c46684..a9d7807a23ea3f 100644 --- a/packages/material-ui-lab/src/MasonryItem/MasonryItem.js +++ b/packages/material-ui-lab/src/MasonryItem/MasonryItem.js @@ -5,11 +5,12 @@ import { createUnarySpacing, getValue, handleBreakpoints, - resolveBreakpointValues, + unstable_resolveBreakpointValues as resolveBreakpointValues, } from '@material-ui/system'; import { deepmerge, unstable_useForkRef as useForkRef } from '@material-ui/utils'; import { unstable_composeClasses as composeClasses } from '@material-ui/unstyled'; import { styled, useThemeProps } from '@material-ui/core/styles'; +import { ResizeObserver } from 'resize-observer'; import { getMasonryItemUtilityClass } from './masonryItemClasses'; import MasonryContext from '../Masonry/MasonryContext'; @@ -28,37 +29,30 @@ export const style = ({ styleProps, theme }) => { width: '100%', [`& > *`]: { // all contents should have a width of 100% - objectFit: 'cover', width: '100%', }, visibility: styleProps.contentHeight ? 'visible' : 'hidden', - ...((typeof styleProps.spacing === 'string' || typeof styleProps.spacing === 'number') && { - gridRowEnd: `span ${ - styleProps.contentHeight + Number(theme.spacing(styleProps.spacing).replace('px', '')) - }`, - paddingBottom: Number(theme.spacing(styleProps.spacing).replace('px', '')) - 1, - }), + gridColumnEnd: `span ${styleProps.columnSpan}`, }; - if (typeof styleProps.spacing === 'object' || Array.isArray(styleProps.spacing)) { - const base = Object.keys(theme.breakpoints.values).reduce((acc, breakpoint) => { - if (styleProps.spacing[breakpoint] != null) { - acc[breakpoint] = true; - } - return acc; - }, {}); - const spacingValues = resolveBreakpointValues({ values: styleProps.spacing, base }); - const transformer = createUnarySpacing(theme); - const styleFromPropValue = (propValue) => { - const gap = Number(getValue(transformer, propValue).replace('px', '')); - const rowSpan = styleProps.contentHeight ? Math.ceil(styleProps.contentHeight + gap) : 0; - return { - gridRowEnd: `span ${rowSpan}`, - paddingBottom: gap - 1, - }; + const base = Object.keys(theme.breakpoints.values).reduce((acc, breakpoint) => { + if (styleProps.spacing[breakpoint] != null) { + acc[breakpoint] = true; + } + return acc; + }, {}); + const spacingValues = resolveBreakpointValues({ values: styleProps.spacing, base }); + const transformer = createUnarySpacing(theme); + const styleFromPropValue = (propValue) => { + const gap = Number(getValue(transformer, propValue).replace('px', '')); + const rowSpan = styleProps.contentHeight ? Math.ceil(styleProps.contentHeight + gap) : 0; + return { + gridRowEnd: `span ${rowSpan}`, + paddingBottom: gap - 1, }; - styles = deepmerge(styles, handleBreakpoints({ theme }, spacingValues, styleFromPropValue)); - } + }; + styles = deepmerge(styles, handleBreakpoints({ theme }, spacingValues, styleFromPropValue)); + return styles; }; @@ -77,15 +71,13 @@ const MasonryItem = React.forwardRef(function MasonryItem(inProps, ref) { }); const masonryItemRef = React.useRef(null); - const handleRef = useForkRef(ref, masonryItemRef); - const { - spacing = 1, - documentReady = false, - } = React.useContext(MasonryContext); - const { children, className, component = 'div', ...other } = props; + + const { spacing = 1, documentReady = false } = React.useContext(MasonryContext); + const { children, className, component = 'div', columnSpan = 1, ...other } = props; const [styleProps, setStyleProps] = React.useState({ ...props, spacing, + columnSpan, }); const classes = useUtilityClasses(styleProps); @@ -97,19 +89,26 @@ const MasonryItem = React.forwardRef(function MasonryItem(inProps, ref) { contentHeight: child?.getBoundingClientRect().height, }); }; + const resizeObserver = React.useRef(new ResizeObserver(computeHeight)); + const resizedItemRef = React.useCallback( + (item) => { + if (item !== null) { + resizeObserver.current.observe(item); + } else if (resizeObserver.current) { + resizeObserver.current.disconnect(); + } + }, + [resizeObserver], + ); React.useEffect(() => { if (documentReady) { computeHeight(); + resizeObserver.current.observe(masonryItemRef.current); } }, [documentReady]); // eslint-disable-line - - React.useEffect(() => { - window.addEventListener('resize', computeHeight);; - return () => { - window.removeEventListener('resize', computeHeight); - }; - }); + const handleOwnRef = useForkRef(masonryItemRef, resizedItemRef); + const handleRef = useForkRef(ref, handleOwnRef); return ( ', () => { const render = createClientRender(); @@ -20,4 +24,108 @@ describe('', () => { skip: ['componentsProp'], }), ); + + const children =

; + const theme = createTheme({ + spacing: 8, + }); + + it('should render children by default', () => { + let item = null; + act(() => { + const { getByTestId } = render({children}); + item = getByTestId('test-children'); + }); + expect(item).not.to.equal(null); + }); + + describe('style attribute:', () => { + it('should render with padding bottom and grid-row-end responsive to breakpoints', () => { + expect( + style({ + styleProps: { + contentHeight: 100, + columnSpan: 1, + spacing: { xs: 1, sm: 2, md: 3 }, + }, + theme, + }), + ).to.deep.equal({ + '@media (min-width:0px)': { + gridRowEnd: `span ${Math.ceil(100 + Number(theme.spacing(1).replace('px', '')))}`, + paddingBottom: Number(theme.spacing(1).replace('px', '')) - 1, + }, + [`@media (min-width:${defaultTheme.breakpoints.values.sm}px)`]: { + gridRowEnd: `span ${Math.ceil(100 + Number(theme.spacing(2).replace('px', '')))}`, + paddingBottom: Number(theme.spacing(2).replace('px', '')) - 1, + }, + [`@media (min-width:${defaultTheme.breakpoints.values.md}px)`]: { + gridRowEnd: `span ${Math.ceil(100 + Number(theme.spacing(3).replace('px', '')))}`, + paddingBottom: Number(theme.spacing(3).replace('px', '')) - 1, + }, + width: '100%', + [`& > *`]: { + width: '100%', + }, + visibility: 'visible', + gridColumnEnd: 'span 1', + }); + }); + + it('should render with given column span', () => { + expect( + style({ + styleProps: { + contentHeight: 100, + columnSpan: 2, + spacing: 1, + }, + theme, + }), + ).to.deep.equal({ + gridRowEnd: `span ${Math.ceil(100 + Number(theme.spacing(1).replace('px', '')))}`, + paddingBottom: Number(theme.spacing(1).replace('px', '')) - 1, + width: '100%', + [`& > *`]: { + width: '100%', + }, + visibility: 'visible', + gridColumnEnd: 'span 2', + }); + }); + }); + + describe('props:', () => { + describe('prop: component', () => { + it('should render a div by default', () => { + let item = null; + act(() => { + const { container } = render({children}); + item = container; + }); + expect(item.firstChild).to.have.property('nodeName', 'DIV'); + }); + + it('should render a different component', () => { + let item = null; + act(() => { + const { container } = render({children}); + item = container; + }); + expect(item.firstChild).to.have.property('nodeName', 'SPAN'); + }); + }); + + describe('prop: className', () => { + it('should append the className to the root element', () => { + let item = null; + act(() => { + const { container } = render({children}); + item = container; + }); + expect(item.firstChild).to.have.class(classes.root); + expect(item.firstChild).to.have.class('foo'); + }); + }); + }); }); diff --git a/packages/material-ui-system/src/index.js b/packages/material-ui-system/src/index.js index e0918b6471e4b2..8ee5f188fe3ce7 100644 --- a/packages/material-ui-system/src/index.js +++ b/packages/material-ui-system/src/index.js @@ -2,7 +2,11 @@ export { css, keyframes, GlobalStyles, StyledEngineProvider } from '@material-ui export { default as borders } from './borders'; export * from './borders'; export { default as breakpoints } from './breakpoints'; -export { handleBreakpoints, mergeBreakpointsInOrder, resolveBreakpointValues } from './breakpoints'; +export { + handleBreakpoints, + mergeBreakpointsInOrder, + resolveBreakpointValues as unstable_resolveBreakpointValues, +} from './breakpoints'; export { default as compose } from './compose'; export { default as display } from './display'; export { default as flexbox } from './flexbox'; diff --git a/packages/material-ui/src/Grid/Grid.js b/packages/material-ui/src/Grid/Grid.js index 88d2f820887069..c8ca466ae313a6 100644 --- a/packages/material-ui/src/Grid/Grid.js +++ b/packages/material-ui/src/Grid/Grid.js @@ -15,7 +15,7 @@ import clsx from 'clsx'; import { unstable_extendSxProp as extendSxProp, handleBreakpoints, - resolveBreakpointValues, + unstable_resolveBreakpointValues as resolveBreakpointValues, } from '@material-ui/system'; import { unstable_composeClasses as composeClasses } from '@material-ui/unstyled'; import requirePropFactory from '../utils/requirePropFactory'; diff --git a/packages/material-ui/src/Stack/Stack.js b/packages/material-ui/src/Stack/Stack.js index c0075bb13e7186..d4f1d6a9624e9f 100644 --- a/packages/material-ui/src/Stack/Stack.js +++ b/packages/material-ui/src/Stack/Stack.js @@ -5,7 +5,7 @@ import { getValue, handleBreakpoints, unstable_extendSxProp as extendSxProp, - resolveBreakpointValues, + unstable_resolveBreakpointValues as resolveBreakpointValues, } from '@material-ui/system'; import { deepmerge } from '@material-ui/utils'; import styled from '../styles/styled'; diff --git a/yarn.lock b/yarn.lock index 0fcc2d488d1aea..fcafde8b4b2d95 100644 --- a/yarn.lock +++ b/yarn.lock @@ -14261,6 +14261,11 @@ resize-observer-polyfill@^1.5.1: resolved "https://registry.yarnpkg.com/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz#0e9020dd3d21024458d4ebd27e23e40269810464" integrity sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg== +resize-observer@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/resize-observer/-/resize-observer-1.0.2.tgz#9a87697b275343d12d8b371940e02e9a93e1f452" + integrity sha512-X0lHFNsxItpBRIRsdwOTkl/VguTaLGx7Gz9xoTGix9ObBN3jRYq9J/rSIuYDrey8AdU3IkfgIMpCeVSEW1QS0Q== + resolve-cwd@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d" From b066279538c1bbd84b59c44f402953a5946a9c75 Mon Sep 17 00:00:00 2001 From: Benny Joo Date: Tue, 27 Jul 2021 23:38:25 +0100 Subject: [PATCH 03/59] Provide SSR via height prop and optimise images in demo --- docs/pages/api-docs/masonry-item.json | 1 + .../pages/components/masonry/ImageMasonry.js | 46 +++++++------------ .../pages/components/masonry/ImageMasonry.tsx | 46 +++++++------------ .../pages/components/masonry/SSRMasonry.js | 27 +++++++++++ .../pages/components/masonry/SSRMasonry.tsx | 27 +++++++++++ docs/src/pages/components/masonry/masonry.md | 4 ++ .../api-docs/masonry-item/masonry-item.json | 1 + .../src/MasonryItem/MasonryItem.d.ts | 4 ++ .../src/MasonryItem/MasonryItem.js | 38 ++++++++------- 9 files changed, 120 insertions(+), 74 deletions(-) create mode 100644 docs/src/pages/components/masonry/SSRMasonry.js create mode 100644 docs/src/pages/components/masonry/SSRMasonry.tsx diff --git a/docs/pages/api-docs/masonry-item.json b/docs/pages/api-docs/masonry-item.json index 6e0c37d63f423c..a06eac80460153 100644 --- a/docs/pages/api-docs/masonry-item.json +++ b/docs/pages/api-docs/masonry-item.json @@ -4,6 +4,7 @@ "classes": { "type": { "name": "object" } }, "columnSpan": { "type": { "name": "number" }, "default": "1" }, "component": { "type": { "name": "elementType" } }, + "height": { "type": { "name": "number" } }, "sx": { "type": { "name": "object" } } }, "name": "MasonryItem", diff --git a/docs/src/pages/components/masonry/ImageMasonry.js b/docs/src/pages/components/masonry/ImageMasonry.js index 4aa77116218355..17fbd2d69619c5 100644 --- a/docs/src/pages/components/masonry/ImageMasonry.js +++ b/docs/src/pages/components/masonry/ImageMasonry.js @@ -6,15 +6,15 @@ import MasonryItem from '@material-ui/lab/MasonryItem'; export default function ImageMasonry() { return ( {itemData.map((item) => ( {item.title} @@ -29,69 +29,53 @@ const itemData = [ img: 'https://images.unsplash.com/photo-1518756131217-31eb79b20e8f', title: 'Fern', }, + { + img: 'https://images.unsplash.com/photo-1627308595229-7830a5c91f9f', + title: 'Snacks', + }, { img: 'https://images.unsplash.com/photo-1597645587822-e99fa5d45d25', title: 'Mushrooms', }, { - img: 'https://images.unsplash.com/photo-1529655683826-aba9b3e77383?ixid=MnwxMjA3fDB8MHxzZWFyY2h8Mnx8bG9uZG9ufGVufDB8fDB8fA%3D%3D&ixlib=rb-1.2.1&w=1000&q=80', + img: 'https://images.unsplash.com/photo-1529655683826-aba9b3e77383', title: 'Tower', }, { img: 'https://images.unsplash.com/photo-1471357674240-e1a485acb3e1', title: 'Sea star', }, - { - img: 'https://images.pexels.com/photos/1761279/pexels-photo-1761279.jpeg?auto=compress&cs=tinysrgb&dpr=1&w=500', - title: 'Bridge', - }, { img: 'https://images.unsplash.com/photo-1558642452-9d2a7deb7f62', title: 'Honey', }, - { - img: 'https://www.nature.com/immersive/d41586-021-00095-y/assets/3TP4N718ac/2021-01-xx_jan-iom_tree-of-life_sh-1080x1440.jpeg', - title: 'Lake', - }, { img: 'https://images.unsplash.com/photo-1516802273409-68526ee1bdd6', title: 'Basketball', }, - { - img: 'https://www.gettyimages.com/gi-resources/images/500px/983794168.jpg', - title: 'Birds', - }, { img: 'https://images.unsplash.com/photo-1551963831-b3b1ca40c98e', title: 'Breakfast', }, { - img: 'https://cdn.pixabay.com/photo/2015/04/23/22/00/tree-736885__480.jpg', - title: 'Sunset', + img: 'https://images.unsplash.com/photo-1627328715728-7bcc1b5db87d', + title: 'Tree', }, { img: 'https://images.unsplash.com/photo-1551782450-a2132b4ba21d', title: 'Burger', }, - { - img: 'https://images.pexels.com/photos/674010/pexels-photo-674010.jpeg?auto=compress&cs=tinysrgb&dpr=1&w=500', - title: 'Picture', - }, { img: 'https://images.unsplash.com/photo-1522770179533-24471fcdba45', title: 'Camera', }, - { - img: 'https://cdn.pixabay.com/photo/2016/05/05/02/37/sunset-1373171__340.jpg', - title: 'Landscape', - }, { img: 'https://images.unsplash.com/photo-1444418776041-9c7e33cc5a9c', title: 'Coffee', }, { - img: 'https://cdn.pixabay.com/photo/2015/04/19/08/32/marguerite-729510__340.jpg', - title: 'Flower', + img: 'https://images.unsplash.com/photo-1627000086207-76eabf23aa2e', + title: 'Camping Car', }, { img: 'https://images.unsplash.com/photo-1533827432537-70133748f5c8', @@ -101,6 +85,10 @@ const itemData = [ img: 'https://images.unsplash.com/photo-1567306301408-9b74779a11af', title: 'Tomato basil', }, + { + img: 'https://images.unsplash.com/photo-1627328561499-a3584d4ee4f7', + title: 'Mountain', + }, { img: 'https://images.unsplash.com/photo-1589118949245-7d38baf380d6', title: 'Bike', diff --git a/docs/src/pages/components/masonry/ImageMasonry.tsx b/docs/src/pages/components/masonry/ImageMasonry.tsx index 4aa77116218355..17fbd2d69619c5 100644 --- a/docs/src/pages/components/masonry/ImageMasonry.tsx +++ b/docs/src/pages/components/masonry/ImageMasonry.tsx @@ -6,15 +6,15 @@ import MasonryItem from '@material-ui/lab/MasonryItem'; export default function ImageMasonry() { return ( {itemData.map((item) => ( {item.title} @@ -29,69 +29,53 @@ const itemData = [ img: 'https://images.unsplash.com/photo-1518756131217-31eb79b20e8f', title: 'Fern', }, + { + img: 'https://images.unsplash.com/photo-1627308595229-7830a5c91f9f', + title: 'Snacks', + }, { img: 'https://images.unsplash.com/photo-1597645587822-e99fa5d45d25', title: 'Mushrooms', }, { - img: 'https://images.unsplash.com/photo-1529655683826-aba9b3e77383?ixid=MnwxMjA3fDB8MHxzZWFyY2h8Mnx8bG9uZG9ufGVufDB8fDB8fA%3D%3D&ixlib=rb-1.2.1&w=1000&q=80', + img: 'https://images.unsplash.com/photo-1529655683826-aba9b3e77383', title: 'Tower', }, { img: 'https://images.unsplash.com/photo-1471357674240-e1a485acb3e1', title: 'Sea star', }, - { - img: 'https://images.pexels.com/photos/1761279/pexels-photo-1761279.jpeg?auto=compress&cs=tinysrgb&dpr=1&w=500', - title: 'Bridge', - }, { img: 'https://images.unsplash.com/photo-1558642452-9d2a7deb7f62', title: 'Honey', }, - { - img: 'https://www.nature.com/immersive/d41586-021-00095-y/assets/3TP4N718ac/2021-01-xx_jan-iom_tree-of-life_sh-1080x1440.jpeg', - title: 'Lake', - }, { img: 'https://images.unsplash.com/photo-1516802273409-68526ee1bdd6', title: 'Basketball', }, - { - img: 'https://www.gettyimages.com/gi-resources/images/500px/983794168.jpg', - title: 'Birds', - }, { img: 'https://images.unsplash.com/photo-1551963831-b3b1ca40c98e', title: 'Breakfast', }, { - img: 'https://cdn.pixabay.com/photo/2015/04/23/22/00/tree-736885__480.jpg', - title: 'Sunset', + img: 'https://images.unsplash.com/photo-1627328715728-7bcc1b5db87d', + title: 'Tree', }, { img: 'https://images.unsplash.com/photo-1551782450-a2132b4ba21d', title: 'Burger', }, - { - img: 'https://images.pexels.com/photos/674010/pexels-photo-674010.jpeg?auto=compress&cs=tinysrgb&dpr=1&w=500', - title: 'Picture', - }, { img: 'https://images.unsplash.com/photo-1522770179533-24471fcdba45', title: 'Camera', }, - { - img: 'https://cdn.pixabay.com/photo/2016/05/05/02/37/sunset-1373171__340.jpg', - title: 'Landscape', - }, { img: 'https://images.unsplash.com/photo-1444418776041-9c7e33cc5a9c', title: 'Coffee', }, { - img: 'https://cdn.pixabay.com/photo/2015/04/19/08/32/marguerite-729510__340.jpg', - title: 'Flower', + img: 'https://images.unsplash.com/photo-1627000086207-76eabf23aa2e', + title: 'Camping Car', }, { img: 'https://images.unsplash.com/photo-1533827432537-70133748f5c8', @@ -101,6 +85,10 @@ const itemData = [ img: 'https://images.unsplash.com/photo-1567306301408-9b74779a11af', title: 'Tomato basil', }, + { + img: 'https://images.unsplash.com/photo-1627328561499-a3584d4ee4f7', + title: 'Mountain', + }, { img: 'https://images.unsplash.com/photo-1589118949245-7d38baf380d6', title: 'Bike', diff --git a/docs/src/pages/components/masonry/SSRMasonry.js b/docs/src/pages/components/masonry/SSRMasonry.js new file mode 100644 index 00000000000000..e5ee1c050a9e02 --- /dev/null +++ b/docs/src/pages/components/masonry/SSRMasonry.js @@ -0,0 +1,27 @@ +/* eslint-disable @typescript-eslint/no-use-before-define */ +import * as React from 'react'; +import Box from '@material-ui/core/Box'; +import Masonry from '@material-ui/lab/Masonry'; +import MasonryItem from '@material-ui/lab/MasonryItem'; + +export default function SSRMasonry() { + return ( + + {heights.map((height, index) => ( + + + {index + 1} + + + ))} + + ); +} +const heights = [150, 30, 90, 70, 110, 150, 130, 80, 50, 90, 100, 150, 30, 50, 80]; diff --git a/docs/src/pages/components/masonry/SSRMasonry.tsx b/docs/src/pages/components/masonry/SSRMasonry.tsx new file mode 100644 index 00000000000000..e5ee1c050a9e02 --- /dev/null +++ b/docs/src/pages/components/masonry/SSRMasonry.tsx @@ -0,0 +1,27 @@ +/* eslint-disable @typescript-eslint/no-use-before-define */ +import * as React from 'react'; +import Box from '@material-ui/core/Box'; +import Masonry from '@material-ui/lab/Masonry'; +import MasonryItem from '@material-ui/lab/MasonryItem'; + +export default function SSRMasonry() { + return ( + + {heights.map((height, index) => ( + + + {index + 1} + + + ))} + + ); +} +const heights = [150, 30, 90, 70, 110, 150, 130, 80, 50, 90, 100, 150, 30, 50, 80]; diff --git a/docs/src/pages/components/masonry/masonry.md b/docs/src/pages/components/masonry/masonry.md index 055ffcdcf7904d..72f04fb750e416 100644 --- a/docs/src/pages/components/masonry/masonry.md +++ b/docs/src/pages/components/masonry/masonry.md @@ -23,3 +23,7 @@ Masonry maintains a list of content blocks with a consistent width but variable > You can configure the number of columns taken up by each `` as shown in the following demo: {{"demo": "pages/components/masonry/DiffColSizeMasonry.js", "bg": true}} + +> In order to use server-side rendering, you can pass the height of the content of `` as shown in the following demo: + +{{"demo": "pages/components/masonry/SSRMasonry.js", "bg": true}} diff --git a/docs/translations/api-docs/masonry-item/masonry-item.json b/docs/translations/api-docs/masonry-item/masonry-item.json index d0d9953b91fa7c..2affccb38312ea 100644 --- a/docs/translations/api-docs/masonry-item/masonry-item.json +++ b/docs/translations/api-docs/masonry-item/masonry-item.json @@ -5,6 +5,7 @@ "classes": "Override or extend the styles applied to the component. See CSS API below for more details.", "columnSpan": "The number of columns taken up by the component", "component": "The component used for the root node. Either a string to use a HTML element or a component.", + "height": "The height of the component in px.", "sx": "Allows defining system overrides as well as additional CSS styles. See the `sx` page for more details." }, "classDescriptions": { "root": { "description": "Styles applied to the root element." } } diff --git a/packages/material-ui-lab/src/MasonryItem/MasonryItem.d.ts b/packages/material-ui-lab/src/MasonryItem/MasonryItem.d.ts index dd08b9bf0da1af..2e0e53d6de0da2 100644 --- a/packages/material-ui-lab/src/MasonryItem/MasonryItem.d.ts +++ b/packages/material-ui-lab/src/MasonryItem/MasonryItem.d.ts @@ -13,6 +13,10 @@ export interface MasonryItemTypeMap

* Override or extend the styles applied to the component. */ classes?: Partial; + /** + * The height of the component in px. + */ + height?: number; /** * The number of columns taken up by the component * @default 1 diff --git a/packages/material-ui-lab/src/MasonryItem/MasonryItem.js b/packages/material-ui-lab/src/MasonryItem/MasonryItem.js index a9d7807a23ea3f..819226cd68c96f 100644 --- a/packages/material-ui-lab/src/MasonryItem/MasonryItem.js +++ b/packages/material-ui-lab/src/MasonryItem/MasonryItem.js @@ -31,7 +31,7 @@ export const style = ({ styleProps, theme }) => { // all contents should have a width of 100% width: '100%', }, - visibility: styleProps.contentHeight ? 'visible' : 'hidden', + visibility: styleProps.height ? 'visible' : 'hidden', gridColumnEnd: `span ${styleProps.columnSpan}`, }; @@ -45,7 +45,7 @@ export const style = ({ styleProps, theme }) => { const transformer = createUnarySpacing(theme); const styleFromPropValue = (propValue) => { const gap = Number(getValue(transformer, propValue).replace('px', '')); - const rowSpan = styleProps.contentHeight ? Math.ceil(styleProps.contentHeight + gap) : 0; + const rowSpan = styleProps.height ? Math.ceil(styleProps.height + gap) : 0; return { gridRowEnd: `span ${rowSpan}`, paddingBottom: gap - 1, @@ -86,29 +86,31 @@ const MasonryItem = React.forwardRef(function MasonryItem(inProps, ref) { const child = masonryItemRef.current.firstChild; setStyleProps({ ...styleProps, - contentHeight: child?.getBoundingClientRect().height, + height: child?.getBoundingClientRect().height, }); }; const resizeObserver = React.useRef(new ResizeObserver(computeHeight)); - const resizedItemRef = React.useCallback( - (item) => { - if (item !== null) { - resizeObserver.current.observe(item); - } else if (resizeObserver.current) { - resizeObserver.current.disconnect(); - } - }, - [resizeObserver], - ); React.useEffect(() => { if (documentReady) { computeHeight(); - resizeObserver.current.observe(masonryItemRef.current); } }, [documentReady]); // eslint-disable-line - const handleOwnRef = useForkRef(masonryItemRef, resizedItemRef); - const handleRef = useForkRef(ref, handleOwnRef); + + // eslint-disable-next-line + React.useEffect(() => { + if (resizeObserver?.current && masonryItemRef?.current) { + const observer = resizeObserver.current; + const item = masonryItemRef.current; + observer.observe(item); + return () => { + observer.unobserve(item); + }; + } + }, [masonryItemRef]); + + const handleRef = useForkRef(ref, masonryItemRef); + return ( Date: Wed, 28 Jul 2021 14:17:42 +0100 Subject: [PATCH 04/59] Correct translation doc and use resize-observer-polyfill package --- .../masonry-item/masonry-item-de.json | 2 + .../masonry-item/masonry-item-es.json | 2 + .../masonry-item/masonry-item-fr.json | 2 + .../masonry-item/masonry-item-ja.json | 2 + .../masonry-item/masonry-item-pt.json | 2 + .../masonry-item/masonry-item-ru.json | 2 + .../masonry-item/masonry-item-zh.json | 2 + .../api-docs/masonry/masonry-de.json | 2 +- .../api-docs/masonry/masonry-es.json | 2 +- .../api-docs/masonry/masonry-fr.json | 2 +- .../api-docs/masonry/masonry-ja.json | 2 +- .../api-docs/masonry/masonry-pt.json | 2 +- .../api-docs/masonry/masonry-ru.json | 2 +- .../api-docs/masonry/masonry-zh.json | 2 +- package.json | 2 +- .../src/MasonryItem/MasonryItem.js | 7 ++- .../src/MasonryItem/MasonryItem.test.js | 60 ++++++++++--------- yarn.lock | 5 -- 18 files changed, 57 insertions(+), 45 deletions(-) diff --git a/docs/translations/api-docs/masonry-item/masonry-item-de.json b/docs/translations/api-docs/masonry-item/masonry-item-de.json index 308b7edb5b2474..2affccb38312ea 100644 --- a/docs/translations/api-docs/masonry-item/masonry-item-de.json +++ b/docs/translations/api-docs/masonry-item/masonry-item-de.json @@ -3,7 +3,9 @@ "propDescriptions": { "children": "The content of the component, normally an <img /> or a <div />.", "classes": "Override or extend the styles applied to the component. See CSS API below for more details.", + "columnSpan": "The number of columns taken up by the component", "component": "The component used for the root node. Either a string to use a HTML element or a component.", + "height": "The height of the component in px.", "sx": "Allows defining system overrides as well as additional CSS styles. See the `sx` page for more details." }, "classDescriptions": { "root": { "description": "Styles applied to the root element." } } diff --git a/docs/translations/api-docs/masonry-item/masonry-item-es.json b/docs/translations/api-docs/masonry-item/masonry-item-es.json index 308b7edb5b2474..2affccb38312ea 100644 --- a/docs/translations/api-docs/masonry-item/masonry-item-es.json +++ b/docs/translations/api-docs/masonry-item/masonry-item-es.json @@ -3,7 +3,9 @@ "propDescriptions": { "children": "The content of the component, normally an <img /> or a <div />.", "classes": "Override or extend the styles applied to the component. See CSS API below for more details.", + "columnSpan": "The number of columns taken up by the component", "component": "The component used for the root node. Either a string to use a HTML element or a component.", + "height": "The height of the component in px.", "sx": "Allows defining system overrides as well as additional CSS styles. See the `sx` page for more details." }, "classDescriptions": { "root": { "description": "Styles applied to the root element." } } diff --git a/docs/translations/api-docs/masonry-item/masonry-item-fr.json b/docs/translations/api-docs/masonry-item/masonry-item-fr.json index 308b7edb5b2474..2affccb38312ea 100644 --- a/docs/translations/api-docs/masonry-item/masonry-item-fr.json +++ b/docs/translations/api-docs/masonry-item/masonry-item-fr.json @@ -3,7 +3,9 @@ "propDescriptions": { "children": "The content of the component, normally an <img /> or a <div />.", "classes": "Override or extend the styles applied to the component. See CSS API below for more details.", + "columnSpan": "The number of columns taken up by the component", "component": "The component used for the root node. Either a string to use a HTML element or a component.", + "height": "The height of the component in px.", "sx": "Allows defining system overrides as well as additional CSS styles. See the `sx` page for more details." }, "classDescriptions": { "root": { "description": "Styles applied to the root element." } } diff --git a/docs/translations/api-docs/masonry-item/masonry-item-ja.json b/docs/translations/api-docs/masonry-item/masonry-item-ja.json index 308b7edb5b2474..2affccb38312ea 100644 --- a/docs/translations/api-docs/masonry-item/masonry-item-ja.json +++ b/docs/translations/api-docs/masonry-item/masonry-item-ja.json @@ -3,7 +3,9 @@ "propDescriptions": { "children": "The content of the component, normally an <img /> or a <div />.", "classes": "Override or extend the styles applied to the component. See CSS API below for more details.", + "columnSpan": "The number of columns taken up by the component", "component": "The component used for the root node. Either a string to use a HTML element or a component.", + "height": "The height of the component in px.", "sx": "Allows defining system overrides as well as additional CSS styles. See the `sx` page for more details." }, "classDescriptions": { "root": { "description": "Styles applied to the root element." } } diff --git a/docs/translations/api-docs/masonry-item/masonry-item-pt.json b/docs/translations/api-docs/masonry-item/masonry-item-pt.json index 308b7edb5b2474..2affccb38312ea 100644 --- a/docs/translations/api-docs/masonry-item/masonry-item-pt.json +++ b/docs/translations/api-docs/masonry-item/masonry-item-pt.json @@ -3,7 +3,9 @@ "propDescriptions": { "children": "The content of the component, normally an <img /> or a <div />.", "classes": "Override or extend the styles applied to the component. See CSS API below for more details.", + "columnSpan": "The number of columns taken up by the component", "component": "The component used for the root node. Either a string to use a HTML element or a component.", + "height": "The height of the component in px.", "sx": "Allows defining system overrides as well as additional CSS styles. See the `sx` page for more details." }, "classDescriptions": { "root": { "description": "Styles applied to the root element." } } diff --git a/docs/translations/api-docs/masonry-item/masonry-item-ru.json b/docs/translations/api-docs/masonry-item/masonry-item-ru.json index 308b7edb5b2474..2affccb38312ea 100644 --- a/docs/translations/api-docs/masonry-item/masonry-item-ru.json +++ b/docs/translations/api-docs/masonry-item/masonry-item-ru.json @@ -3,7 +3,9 @@ "propDescriptions": { "children": "The content of the component, normally an <img /> or a <div />.", "classes": "Override or extend the styles applied to the component. See CSS API below for more details.", + "columnSpan": "The number of columns taken up by the component", "component": "The component used for the root node. Either a string to use a HTML element or a component.", + "height": "The height of the component in px.", "sx": "Allows defining system overrides as well as additional CSS styles. See the `sx` page for more details." }, "classDescriptions": { "root": { "description": "Styles applied to the root element." } } diff --git a/docs/translations/api-docs/masonry-item/masonry-item-zh.json b/docs/translations/api-docs/masonry-item/masonry-item-zh.json index 308b7edb5b2474..2affccb38312ea 100644 --- a/docs/translations/api-docs/masonry-item/masonry-item-zh.json +++ b/docs/translations/api-docs/masonry-item/masonry-item-zh.json @@ -3,7 +3,9 @@ "propDescriptions": { "children": "The content of the component, normally an <img /> or a <div />.", "classes": "Override or extend the styles applied to the component. See CSS API below for more details.", + "columnSpan": "The number of columns taken up by the component", "component": "The component used for the root node. Either a string to use a HTML element or a component.", + "height": "The height of the component in px.", "sx": "Allows defining system overrides as well as additional CSS styles. See the `sx` page for more details." }, "classDescriptions": { "root": { "description": "Styles applied to the root element." } } diff --git a/docs/translations/api-docs/masonry/masonry-de.json b/docs/translations/api-docs/masonry/masonry-de.json index 7b4d84fdc144ea..529c7b45599a51 100644 --- a/docs/translations/api-docs/masonry/masonry-de.json +++ b/docs/translations/api-docs/masonry/masonry-de.json @@ -3,7 +3,7 @@ "propDescriptions": { "children": "The content of the component, normally <MasonryItem />s.", "classes": "Override or extend the styles applied to the component. See CSS API below for more details.", - "cols": "Number of columns.", + "columns": "Number of columns.", "component": "The component used for the root node. Either a string to use a HTML element or a component.", "spacing": "Defines the space between children.", "sx": "Allows defining system overrides as well as additional CSS styles. See the `sx` page for more details." diff --git a/docs/translations/api-docs/masonry/masonry-es.json b/docs/translations/api-docs/masonry/masonry-es.json index 7b4d84fdc144ea..529c7b45599a51 100644 --- a/docs/translations/api-docs/masonry/masonry-es.json +++ b/docs/translations/api-docs/masonry/masonry-es.json @@ -3,7 +3,7 @@ "propDescriptions": { "children": "The content of the component, normally <MasonryItem />s.", "classes": "Override or extend the styles applied to the component. See CSS API below for more details.", - "cols": "Number of columns.", + "columns": "Number of columns.", "component": "The component used for the root node. Either a string to use a HTML element or a component.", "spacing": "Defines the space between children.", "sx": "Allows defining system overrides as well as additional CSS styles. See the `sx` page for more details." diff --git a/docs/translations/api-docs/masonry/masonry-fr.json b/docs/translations/api-docs/masonry/masonry-fr.json index 7b4d84fdc144ea..529c7b45599a51 100644 --- a/docs/translations/api-docs/masonry/masonry-fr.json +++ b/docs/translations/api-docs/masonry/masonry-fr.json @@ -3,7 +3,7 @@ "propDescriptions": { "children": "The content of the component, normally <MasonryItem />s.", "classes": "Override or extend the styles applied to the component. See CSS API below for more details.", - "cols": "Number of columns.", + "columns": "Number of columns.", "component": "The component used for the root node. Either a string to use a HTML element or a component.", "spacing": "Defines the space between children.", "sx": "Allows defining system overrides as well as additional CSS styles. See the `sx` page for more details." diff --git a/docs/translations/api-docs/masonry/masonry-ja.json b/docs/translations/api-docs/masonry/masonry-ja.json index 7b4d84fdc144ea..529c7b45599a51 100644 --- a/docs/translations/api-docs/masonry/masonry-ja.json +++ b/docs/translations/api-docs/masonry/masonry-ja.json @@ -3,7 +3,7 @@ "propDescriptions": { "children": "The content of the component, normally <MasonryItem />s.", "classes": "Override or extend the styles applied to the component. See CSS API below for more details.", - "cols": "Number of columns.", + "columns": "Number of columns.", "component": "The component used for the root node. Either a string to use a HTML element or a component.", "spacing": "Defines the space between children.", "sx": "Allows defining system overrides as well as additional CSS styles. See the `sx` page for more details." diff --git a/docs/translations/api-docs/masonry/masonry-pt.json b/docs/translations/api-docs/masonry/masonry-pt.json index 7b4d84fdc144ea..529c7b45599a51 100644 --- a/docs/translations/api-docs/masonry/masonry-pt.json +++ b/docs/translations/api-docs/masonry/masonry-pt.json @@ -3,7 +3,7 @@ "propDescriptions": { "children": "The content of the component, normally <MasonryItem />s.", "classes": "Override or extend the styles applied to the component. See CSS API below for more details.", - "cols": "Number of columns.", + "columns": "Number of columns.", "component": "The component used for the root node. Either a string to use a HTML element or a component.", "spacing": "Defines the space between children.", "sx": "Allows defining system overrides as well as additional CSS styles. See the `sx` page for more details." diff --git a/docs/translations/api-docs/masonry/masonry-ru.json b/docs/translations/api-docs/masonry/masonry-ru.json index 7b4d84fdc144ea..529c7b45599a51 100644 --- a/docs/translations/api-docs/masonry/masonry-ru.json +++ b/docs/translations/api-docs/masonry/masonry-ru.json @@ -3,7 +3,7 @@ "propDescriptions": { "children": "The content of the component, normally <MasonryItem />s.", "classes": "Override or extend the styles applied to the component. See CSS API below for more details.", - "cols": "Number of columns.", + "columns": "Number of columns.", "component": "The component used for the root node. Either a string to use a HTML element or a component.", "spacing": "Defines the space between children.", "sx": "Allows defining system overrides as well as additional CSS styles. See the `sx` page for more details." diff --git a/docs/translations/api-docs/masonry/masonry-zh.json b/docs/translations/api-docs/masonry/masonry-zh.json index 7b4d84fdc144ea..529c7b45599a51 100644 --- a/docs/translations/api-docs/masonry/masonry-zh.json +++ b/docs/translations/api-docs/masonry/masonry-zh.json @@ -3,7 +3,7 @@ "propDescriptions": { "children": "The content of the component, normally <MasonryItem />s.", "classes": "Override or extend the styles applied to the component. See CSS API below for more details.", - "cols": "Number of columns.", + "columns": "Number of columns.", "component": "The component used for the root node. Either a string to use a HTML element or a component.", "spacing": "Defines the space between children.", "sx": "Allows defining system overrides as well as additional CSS styles. See the `sx` page for more details." diff --git a/package.json b/package.json index d0da3691eadfcc..f57d2ae35c0674 100644 --- a/package.json +++ b/package.json @@ -157,7 +157,7 @@ "react-router-dom": "^5.2.0", "react-test-renderer": "^17.0.2", "remark": "^13.0.0", - "resize-observer": "^1.0.2", + "resize-observer-polyfill": "^1.5.1", "rimraf": "^3.0.0", "rollup": "^2.56.2", "rollup-plugin-babel": "^4.3.3", diff --git a/packages/material-ui-lab/src/MasonryItem/MasonryItem.js b/packages/material-ui-lab/src/MasonryItem/MasonryItem.js index 819226cd68c96f..8f46321a20124e 100644 --- a/packages/material-ui-lab/src/MasonryItem/MasonryItem.js +++ b/packages/material-ui-lab/src/MasonryItem/MasonryItem.js @@ -10,7 +10,7 @@ import { import { deepmerge, unstable_useForkRef as useForkRef } from '@material-ui/utils'; import { unstable_composeClasses as composeClasses } from '@material-ui/unstyled'; import { styled, useThemeProps } from '@material-ui/core/styles'; -import { ResizeObserver } from 'resize-observer'; +import ResizeObserver from 'resize-observer-polyfill'; import { getMasonryItemUtilityClass } from './masonryItemClasses'; import MasonryContext from '../Masonry/MasonryContext'; @@ -45,7 +45,7 @@ export const style = ({ styleProps, theme }) => { const transformer = createUnarySpacing(theme); const styleFromPropValue = (propValue) => { const gap = Number(getValue(transformer, propValue).replace('px', '')); - const rowSpan = styleProps.height ? Math.ceil(styleProps.height + gap) : 0; + const rowSpan = Math.ceil(styleProps.height + gap); return { gridRowEnd: `span ${rowSpan}`, paddingBottom: gap - 1, @@ -73,11 +73,12 @@ const MasonryItem = React.forwardRef(function MasonryItem(inProps, ref) { const masonryItemRef = React.useRef(null); const { spacing = 1, documentReady = false } = React.useContext(MasonryContext); - const { children, className, component = 'div', columnSpan = 1, ...other } = props; + const { children, className, component = 'div', columnSpan = 1, height = 0, ...other } = props; const [styleProps, setStyleProps] = React.useState({ ...props, spacing, columnSpan, + height, }); const classes = useUtilityClasses(styleProps); diff --git a/packages/material-ui-lab/src/MasonryItem/MasonryItem.test.js b/packages/material-ui-lab/src/MasonryItem/MasonryItem.test.js index 5b4b33ae1e3581..0e4017a2d3e3fe 100644 --- a/packages/material-ui-lab/src/MasonryItem/MasonryItem.test.js +++ b/packages/material-ui-lab/src/MasonryItem/MasonryItem.test.js @@ -1,5 +1,5 @@ import * as React from 'react'; -import { createClientRender, describeConformanceV5, act } from 'test/utils'; +import { createClientRender, describeConformanceV5 } from 'test/utils'; import MasonryItem, { masonryItemClasses as classes } from '@material-ui/lab/MasonryItem'; import { expect } from 'chai'; import { createTheme } from '@material-ui/core/styles'; @@ -21,7 +21,13 @@ describe('', () => { testComponentPropWith: 'div', testVariantProps: { variant: 'foo' }, muiName: 'MuiMasonryItem', - skip: ['componentsProp'], + skip: [ + 'componentsProp', + // reactTestRenderer fails because React state updates should be wrapped into act(...) + // I followed the guideline here: https://reactjs.org/link/wrap-tests-with-act + // but tests still couldn't pass + 'reactTestRenderer', + ], }), ); @@ -31,12 +37,8 @@ describe('', () => { }); it('should render children by default', () => { - let item = null; - act(() => { - const { getByTestId } = render({children}); - item = getByTestId('test-children'); - }); - expect(item).not.to.equal(null); + const { getByTestId } = render({children}); + expect(getByTestId('test-children')).not.to.equal(null); }); describe('style attribute:', () => { @@ -44,7 +46,7 @@ describe('', () => { expect( style({ styleProps: { - contentHeight: 100, + height: 100, columnSpan: 1, spacing: { xs: 1, sm: 2, md: 3 }, }, @@ -76,7 +78,7 @@ describe('', () => { expect( style({ styleProps: { - contentHeight: 100, + height: 100, columnSpan: 2, spacing: 1, }, @@ -93,38 +95,38 @@ describe('', () => { gridColumnEnd: 'span 2', }); }); + + it('should compute grid-row-end based on given height', () => { + const { getByTestId } = render( + + {children} + , + ); + const computedStyle = getComputedStyle(getByTestId('test-root')); + expect(computedStyle['grid-row-end']).to.equal( + `span ${Math.ceil(150 + Number(theme.spacing(1).replace('px', '')))}`, + ); + }); }); describe('props:', () => { describe('prop: component', () => { it('should render a div by default', () => { - let item = null; - act(() => { - const { container } = render({children}); - item = container; - }); - expect(item.firstChild).to.have.property('nodeName', 'DIV'); + const { container } = render({children}); + expect(container.firstChild).to.have.property('nodeName', 'DIV'); }); it('should render a different component', () => { - let item = null; - act(() => { - const { container } = render({children}); - item = container; - }); - expect(item.firstChild).to.have.property('nodeName', 'SPAN'); + const { container } = render({children}); + expect(container.firstChild).to.have.property('nodeName', 'SPAN'); }); }); describe('prop: className', () => { it('should append the className to the root element', () => { - let item = null; - act(() => { - const { container } = render({children}); - item = container; - }); - expect(item.firstChild).to.have.class(classes.root); - expect(item.firstChild).to.have.class('foo'); + const { container } = render({children}); + expect(container.firstChild).to.have.class(classes.root); + expect(container.firstChild).to.have.class('foo'); }); }); }); diff --git a/yarn.lock b/yarn.lock index fcafde8b4b2d95..0fcc2d488d1aea 100644 --- a/yarn.lock +++ b/yarn.lock @@ -14261,11 +14261,6 @@ resize-observer-polyfill@^1.5.1: resolved "https://registry.yarnpkg.com/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz#0e9020dd3d21024458d4ebd27e23e40269810464" integrity sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg== -resize-observer@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/resize-observer/-/resize-observer-1.0.2.tgz#9a87697b275343d12d8b371940e02e9a93e1f452" - integrity sha512-X0lHFNsxItpBRIRsdwOTkl/VguTaLGx7Gz9xoTGix9ObBN3jRYq9J/rSIuYDrey8AdU3IkfgIMpCeVSEW1QS0Q== - resolve-cwd@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d" From 23c2318efc784f678925d9d2dbb1d43c8d8d88ba Mon Sep 17 00:00:00 2001 From: Benny Joo Date: Wed, 28 Jul 2021 16:24:49 +0100 Subject: [PATCH 05/59] Handle negative heights and set default value of height to undefined --- docs/pages/api-docs/masonry-item.json | 2 +- .../src/MasonryItem/MasonryItem.d.ts | 1 + .../src/MasonryItem/MasonryItem.js | 39 +++++++++++++------ 3 files changed, 30 insertions(+), 12 deletions(-) diff --git a/docs/pages/api-docs/masonry-item.json b/docs/pages/api-docs/masonry-item.json index a06eac80460153..ea4d3a2d8a506d 100644 --- a/docs/pages/api-docs/masonry-item.json +++ b/docs/pages/api-docs/masonry-item.json @@ -4,7 +4,7 @@ "classes": { "type": { "name": "object" } }, "columnSpan": { "type": { "name": "number" }, "default": "1" }, "component": { "type": { "name": "elementType" } }, - "height": { "type": { "name": "number" } }, + "height": { "type": { "name": "number" }, "default": "undefined" }, "sx": { "type": { "name": "object" } } }, "name": "MasonryItem", diff --git a/packages/material-ui-lab/src/MasonryItem/MasonryItem.d.ts b/packages/material-ui-lab/src/MasonryItem/MasonryItem.d.ts index 2e0e53d6de0da2..a23f8a9a740963 100644 --- a/packages/material-ui-lab/src/MasonryItem/MasonryItem.d.ts +++ b/packages/material-ui-lab/src/MasonryItem/MasonryItem.d.ts @@ -15,6 +15,7 @@ export interface MasonryItemTypeMap

classes?: Partial; /** * The height of the component in px. + * @default undefined */ height?: number; /** diff --git a/packages/material-ui-lab/src/MasonryItem/MasonryItem.js b/packages/material-ui-lab/src/MasonryItem/MasonryItem.js index 8f46321a20124e..e739f827832d05 100644 --- a/packages/material-ui-lab/src/MasonryItem/MasonryItem.js +++ b/packages/material-ui-lab/src/MasonryItem/MasonryItem.js @@ -44,8 +44,8 @@ export const style = ({ styleProps, theme }) => { const spacingValues = resolveBreakpointValues({ values: styleProps.spacing, base }); const transformer = createUnarySpacing(theme); const styleFromPropValue = (propValue) => { - const gap = Number(getValue(transformer, propValue).replace('px', '')); - const rowSpan = Math.ceil(styleProps.height + gap); + const gap = styleProps.height ? Number(getValue(transformer, propValue).replace('px', '')) : 0; + const rowSpan = styleProps.height ? Math.ceil(styleProps.height + gap) : 0; return { gridRowEnd: `span ${rowSpan}`, paddingBottom: gap - 1, @@ -73,24 +73,40 @@ const MasonryItem = React.forwardRef(function MasonryItem(inProps, ref) { const masonryItemRef = React.useRef(null); const { spacing = 1, documentReady = false } = React.useContext(MasonryContext); - const { children, className, component = 'div', columnSpan = 1, height = 0, ...other } = props; + const { + children, + className, + component = 'div', + columnSpan = 1, + height = undefined, + ...other + } = props; const [styleProps, setStyleProps] = React.useState({ ...props, spacing, columnSpan, - height, + height: height < 0 ? 0 : height, // MasonryItems to which negative or zero height is passed will be hidden }); const classes = useUtilityClasses(styleProps); const computeHeight = () => { - const child = masonryItemRef.current.firstChild; - setStyleProps({ - ...styleProps, - height: child?.getBoundingClientRect().height, - }); + if (masonryItemRef?.current) { + const child = masonryItemRef.current.firstChild; + setStyleProps({ + ...styleProps, + height: + styleProps.height === undefined + ? child?.getBoundingClientRect().height + : styleProps.height, + }); + } }; - const resizeObserver = React.useRef(new ResizeObserver(computeHeight)); + + // If height is passed by user, ResizeObserver is not used. + const resizeObserver = React.useRef( + height === undefined ? new ResizeObserver(computeHeight) : null, + ); React.useEffect(() => { if (documentReady) { @@ -98,7 +114,6 @@ const MasonryItem = React.forwardRef(function MasonryItem(inProps, ref) { } }, [documentReady]); // eslint-disable-line - // eslint-disable-next-line React.useEffect(() => { if (resizeObserver?.current && masonryItemRef?.current) { const observer = resizeObserver.current; @@ -108,6 +123,7 @@ const MasonryItem = React.forwardRef(function MasonryItem(inProps, ref) { observer.unobserve(item); }; } + return true; }, [masonryItemRef]); const handleRef = useForkRef(ref, masonryItemRef); @@ -154,6 +170,7 @@ MasonryItem.propTypes /* remove-proptypes */ = { component: PropTypes.elementType, /** * The height of the component in px. + * @default undefined */ height: PropTypes.number, /** From 0c1b29390104cd4c93e49a9b680e48fdfd2c3e0f Mon Sep 17 00:00:00 2001 From: Benny Joo Date: Wed, 28 Jul 2021 23:29:49 +0100 Subject: [PATCH 06/59] Properly use ResizeObserver and remove other event listeners --- .../pages/components/masonry/ImageMasonry.js | 6 ++-- .../pages/components/masonry/ImageMasonry.tsx | 6 ++-- .../material-ui-lab/src/Masonry/Masonry.js | 15 ++------ packages/material-ui-lab/src/Masonry/index.js | 1 + .../src/MasonryItem/MasonryItem.js | 36 +++++++------------ .../material-ui-lab/src/MasonryItem/index.js | 1 + 6 files changed, 22 insertions(+), 43 deletions(-) diff --git a/docs/src/pages/components/masonry/ImageMasonry.js b/docs/src/pages/components/masonry/ImageMasonry.js index 17fbd2d69619c5..720c0fd44a3b88 100644 --- a/docs/src/pages/components/masonry/ImageMasonry.js +++ b/docs/src/pages/components/masonry/ImageMasonry.js @@ -6,15 +6,15 @@ import MasonryItem from '@material-ui/lab/MasonryItem'; export default function ImageMasonry() { return ( {itemData.map((item) => ( {item.title} diff --git a/docs/src/pages/components/masonry/ImageMasonry.tsx b/docs/src/pages/components/masonry/ImageMasonry.tsx index 17fbd2d69619c5..720c0fd44a3b88 100644 --- a/docs/src/pages/components/masonry/ImageMasonry.tsx +++ b/docs/src/pages/components/masonry/ImageMasonry.tsx @@ -6,15 +6,15 @@ import MasonryItem from '@material-ui/lab/MasonryItem'; export default function ImageMasonry() { return ( {itemData.map((item) => ( {item.title} diff --git a/packages/material-ui-lab/src/Masonry/Masonry.js b/packages/material-ui-lab/src/Masonry/Masonry.js index f68b9fc0c4e879..f457a99eb0a6e5 100644 --- a/packages/material-ui-lab/src/Masonry/Masonry.js +++ b/packages/material-ui-lab/src/Masonry/Masonry.js @@ -82,19 +82,8 @@ const Masonry = React.forwardRef(function Masonry(inProps, ref) { const { children, className, component = 'div', columns = 4, spacing = 1, ...other } = props; const styleProps = { ...props, spacing, columns }; const classes = useUtilityClasses(styleProps); - const [documentReady, setDocumentReady] = React.useState(false); - const handleStateChange = () => { - if (document.readyState === 'complete') { - setDocumentReady(true); - } - }; - React.useEffect(() => { - document.addEventListener('readystatechange', handleStateChange); - return () => { - document.removeEventListener('readystatechange', handleStateChange); - }; - }); - const masonry = React.useMemo(() => ({ spacing, documentReady }), [spacing, documentReady]); + + const masonry = React.useMemo(() => ({ spacing }), [spacing]); return ( diff --git a/packages/material-ui-lab/src/Masonry/index.js b/packages/material-ui-lab/src/Masonry/index.js index 840cd371f6e7dd..055003e228425e 100644 --- a/packages/material-ui-lab/src/Masonry/index.js +++ b/packages/material-ui-lab/src/Masonry/index.js @@ -1,3 +1,4 @@ export { default } from './Masonry'; + export * from './masonryClasses'; export { default as masonryClasses } from './masonryClasses'; diff --git a/packages/material-ui-lab/src/MasonryItem/MasonryItem.js b/packages/material-ui-lab/src/MasonryItem/MasonryItem.js index e739f827832d05..7dc9e2b52239ca 100644 --- a/packages/material-ui-lab/src/MasonryItem/MasonryItem.js +++ b/packages/material-ui-lab/src/MasonryItem/MasonryItem.js @@ -27,7 +27,7 @@ const useUtilityClasses = (styleProps) => { export const style = ({ styleProps, theme }) => { let styles = { width: '100%', - [`& > *`]: { + '& > *': { // all contents should have a width of 100% width: '100%', }, @@ -72,7 +72,7 @@ const MasonryItem = React.forwardRef(function MasonryItem(inProps, ref) { const masonryItemRef = React.useRef(null); - const { spacing = 1, documentReady = false } = React.useContext(MasonryContext); + const { spacing = 1 } = React.useContext(MasonryContext); const { children, className, @@ -90,41 +90,29 @@ const MasonryItem = React.forwardRef(function MasonryItem(inProps, ref) { const classes = useUtilityClasses(styleProps); - const computeHeight = () => { - if (masonryItemRef?.current) { - const child = masonryItemRef.current.firstChild; - setStyleProps({ - ...styleProps, - height: - styleProps.height === undefined - ? child?.getBoundingClientRect().height - : styleProps.height, - }); - } - }; - // If height is passed by user, ResizeObserver is not used. const resizeObserver = React.useRef( - height === undefined ? new ResizeObserver(computeHeight) : null, + height === undefined + ? new ResizeObserver(([item]) => { + setStyleProps({ + ...styleProps, + height: item.contentRect.height, + }); + }) + : null, ); - React.useEffect(() => { - if (documentReady) { - computeHeight(); - } - }, [documentReady]); // eslint-disable-line - React.useEffect(() => { if (resizeObserver?.current && masonryItemRef?.current) { const observer = resizeObserver.current; - const item = masonryItemRef.current; + const item = masonryItemRef.current.firstChild; observer.observe(item); return () => { observer.unobserve(item); }; } return true; - }, [masonryItemRef]); + }, []); const handleRef = useForkRef(ref, masonryItemRef); diff --git a/packages/material-ui-lab/src/MasonryItem/index.js b/packages/material-ui-lab/src/MasonryItem/index.js index 63ed88ba665ab7..126c43ca7ee4c2 100644 --- a/packages/material-ui-lab/src/MasonryItem/index.js +++ b/packages/material-ui-lab/src/MasonryItem/index.js @@ -1,3 +1,4 @@ export { default } from './MasonryItem'; + export * from './masonryItemClasses'; export { default as masonryItemClasses } from './masonryItemClasses'; From 8184f92209c1b4e613ba13cff94cd6997500cf30 Mon Sep 17 00:00:00 2001 From: Benny Joo Date: Thu, 29 Jul 2021 13:15:20 +0100 Subject: [PATCH 07/59] Refactor codes and improve docs to explain props more clearly --- .../components/masonry/DiffColSizeMasonry.js | 20 +++++----- .../components/masonry/DiffColSizeMasonry.tsx | 20 +++++----- docs/src/pages/components/masonry/masonry.md | 2 +- .../masonry-item/masonry-item-de.json | 2 +- .../masonry-item/masonry-item-es.json | 2 +- .../masonry-item/masonry-item-fr.json | 2 +- .../masonry-item/masonry-item-ja.json | 2 +- .../masonry-item/masonry-item-pt.json | 2 +- .../masonry-item/masonry-item-ru.json | 2 +- .../masonry-item/masonry-item-zh.json | 2 +- .../api-docs/masonry-item/masonry-item.json | 2 +- .../api-docs/masonry/masonry-de.json | 4 +- .../api-docs/masonry/masonry-es.json | 4 +- .../api-docs/masonry/masonry-fr.json | 4 +- .../api-docs/masonry/masonry-ja.json | 4 +- .../api-docs/masonry/masonry-pt.json | 4 +- .../api-docs/masonry/masonry-ru.json | 4 +- .../api-docs/masonry/masonry-zh.json | 4 +- .../api-docs/masonry/masonry.json | 4 +- package.json | 1 - packages/material-ui-lab/package.json | 1 + .../material-ui-lab/src/Masonry/Masonry.d.ts | 4 +- .../material-ui-lab/src/Masonry/Masonry.js | 16 ++++---- .../src/Masonry/Masonry.test.js | 39 +------------------ .../src/MasonryItem/MasonryItem.d.ts | 2 +- .../src/MasonryItem/MasonryItem.js | 15 ++----- .../src/MasonryItem/MasonryItem.test.js | 24 +----------- 27 files changed, 62 insertions(+), 130 deletions(-) diff --git a/docs/src/pages/components/masonry/DiffColSizeMasonry.js b/docs/src/pages/components/masonry/DiffColSizeMasonry.js index b02dd96d6738e8..425d45f4d8b2e4 100644 --- a/docs/src/pages/components/masonry/DiffColSizeMasonry.js +++ b/docs/src/pages/components/masonry/DiffColSizeMasonry.js @@ -26,19 +26,19 @@ export default function DiffColSizeMasonry() { } const itemData = [ - { height: 150, span: 1 }, - { height: 30, span: 1 }, + { height: 150 }, + { height: 30 }, { height: 90, span: 2 }, - { height: 110, span: 1 }, - { height: 150, span: 1 }, - { height: 150, span: 1 }, + { height: 110 }, + { height: 150 }, + { height: 150 }, { height: 130, span: 2 }, { height: 80, span: 2 }, - { height: 50, span: 1 }, - { height: 90, span: 1 }, + { height: 50 }, + { height: 90 }, { height: 100, span: 2 }, - { height: 150, span: 1 }, - { height: 50, span: 1 }, + { height: 150 }, + { height: 50 }, { height: 50, span: 2 }, - { height: 50, span: 1 }, + { height: 50 }, ]; diff --git a/docs/src/pages/components/masonry/DiffColSizeMasonry.tsx b/docs/src/pages/components/masonry/DiffColSizeMasonry.tsx index b02dd96d6738e8..425d45f4d8b2e4 100644 --- a/docs/src/pages/components/masonry/DiffColSizeMasonry.tsx +++ b/docs/src/pages/components/masonry/DiffColSizeMasonry.tsx @@ -26,19 +26,19 @@ export default function DiffColSizeMasonry() { } const itemData = [ - { height: 150, span: 1 }, - { height: 30, span: 1 }, + { height: 150 }, + { height: 30 }, { height: 90, span: 2 }, - { height: 110, span: 1 }, - { height: 150, span: 1 }, - { height: 150, span: 1 }, + { height: 110 }, + { height: 150 }, + { height: 150 }, { height: 130, span: 2 }, { height: 80, span: 2 }, - { height: 50, span: 1 }, - { height: 90, span: 1 }, + { height: 50 }, + { height: 90 }, { height: 100, span: 2 }, - { height: 150, span: 1 }, - { height: 50, span: 1 }, + { height: 150 }, + { height: 50 }, { height: 50, span: 2 }, - { height: 50, span: 1 }, + { height: 50 }, ]; diff --git a/docs/src/pages/components/masonry/masonry.md b/docs/src/pages/components/masonry/masonry.md index 72f04fb750e416..20b8747929a967 100644 --- a/docs/src/pages/components/masonry/masonry.md +++ b/docs/src/pages/components/masonry/masonry.md @@ -24,6 +24,6 @@ Masonry maintains a list of content blocks with a consistent width but variable {{"demo": "pages/components/masonry/DiffColSizeMasonry.js", "bg": true}} -> In order to use server-side rendering, you can pass the height of the content of `` as shown in the following demo: +> In order to use server-side rendering, you should pass the height of the content of `` as shown in the following demo: {{"demo": "pages/components/masonry/SSRMasonry.js", "bg": true}} diff --git a/docs/translations/api-docs/masonry-item/masonry-item-de.json b/docs/translations/api-docs/masonry-item/masonry-item-de.json index 2affccb38312ea..0f70ca91820611 100644 --- a/docs/translations/api-docs/masonry-item/masonry-item-de.json +++ b/docs/translations/api-docs/masonry-item/masonry-item-de.json @@ -1,7 +1,7 @@ { "componentDescription": "", "propDescriptions": { - "children": "The content of the component, normally an <img /> or a <div />.", + "children": "The content of the component, normally an <img /> or a <div />. It should be only one element.", "classes": "Override or extend the styles applied to the component. See CSS API below for more details.", "columnSpan": "The number of columns taken up by the component", "component": "The component used for the root node. Either a string to use a HTML element or a component.", diff --git a/docs/translations/api-docs/masonry-item/masonry-item-es.json b/docs/translations/api-docs/masonry-item/masonry-item-es.json index 2affccb38312ea..0f70ca91820611 100644 --- a/docs/translations/api-docs/masonry-item/masonry-item-es.json +++ b/docs/translations/api-docs/masonry-item/masonry-item-es.json @@ -1,7 +1,7 @@ { "componentDescription": "", "propDescriptions": { - "children": "The content of the component, normally an <img /> or a <div />.", + "children": "The content of the component, normally an <img /> or a <div />. It should be only one element.", "classes": "Override or extend the styles applied to the component. See CSS API below for more details.", "columnSpan": "The number of columns taken up by the component", "component": "The component used for the root node. Either a string to use a HTML element or a component.", diff --git a/docs/translations/api-docs/masonry-item/masonry-item-fr.json b/docs/translations/api-docs/masonry-item/masonry-item-fr.json index 2affccb38312ea..0f70ca91820611 100644 --- a/docs/translations/api-docs/masonry-item/masonry-item-fr.json +++ b/docs/translations/api-docs/masonry-item/masonry-item-fr.json @@ -1,7 +1,7 @@ { "componentDescription": "", "propDescriptions": { - "children": "The content of the component, normally an <img /> or a <div />.", + "children": "The content of the component, normally an <img /> or a <div />. It should be only one element.", "classes": "Override or extend the styles applied to the component. See CSS API below for more details.", "columnSpan": "The number of columns taken up by the component", "component": "The component used for the root node. Either a string to use a HTML element or a component.", diff --git a/docs/translations/api-docs/masonry-item/masonry-item-ja.json b/docs/translations/api-docs/masonry-item/masonry-item-ja.json index 2affccb38312ea..0f70ca91820611 100644 --- a/docs/translations/api-docs/masonry-item/masonry-item-ja.json +++ b/docs/translations/api-docs/masonry-item/masonry-item-ja.json @@ -1,7 +1,7 @@ { "componentDescription": "", "propDescriptions": { - "children": "The content of the component, normally an <img /> or a <div />.", + "children": "The content of the component, normally an <img /> or a <div />. It should be only one element.", "classes": "Override or extend the styles applied to the component. See CSS API below for more details.", "columnSpan": "The number of columns taken up by the component", "component": "The component used for the root node. Either a string to use a HTML element or a component.", diff --git a/docs/translations/api-docs/masonry-item/masonry-item-pt.json b/docs/translations/api-docs/masonry-item/masonry-item-pt.json index 2affccb38312ea..0f70ca91820611 100644 --- a/docs/translations/api-docs/masonry-item/masonry-item-pt.json +++ b/docs/translations/api-docs/masonry-item/masonry-item-pt.json @@ -1,7 +1,7 @@ { "componentDescription": "", "propDescriptions": { - "children": "The content of the component, normally an <img /> or a <div />.", + "children": "The content of the component, normally an <img /> or a <div />. It should be only one element.", "classes": "Override or extend the styles applied to the component. See CSS API below for more details.", "columnSpan": "The number of columns taken up by the component", "component": "The component used for the root node. Either a string to use a HTML element or a component.", diff --git a/docs/translations/api-docs/masonry-item/masonry-item-ru.json b/docs/translations/api-docs/masonry-item/masonry-item-ru.json index 2affccb38312ea..0f70ca91820611 100644 --- a/docs/translations/api-docs/masonry-item/masonry-item-ru.json +++ b/docs/translations/api-docs/masonry-item/masonry-item-ru.json @@ -1,7 +1,7 @@ { "componentDescription": "", "propDescriptions": { - "children": "The content of the component, normally an <img /> or a <div />.", + "children": "The content of the component, normally an <img /> or a <div />. It should be only one element.", "classes": "Override or extend the styles applied to the component. See CSS API below for more details.", "columnSpan": "The number of columns taken up by the component", "component": "The component used for the root node. Either a string to use a HTML element or a component.", diff --git a/docs/translations/api-docs/masonry-item/masonry-item-zh.json b/docs/translations/api-docs/masonry-item/masonry-item-zh.json index 2affccb38312ea..0f70ca91820611 100644 --- a/docs/translations/api-docs/masonry-item/masonry-item-zh.json +++ b/docs/translations/api-docs/masonry-item/masonry-item-zh.json @@ -1,7 +1,7 @@ { "componentDescription": "", "propDescriptions": { - "children": "The content of the component, normally an <img /> or a <div />.", + "children": "The content of the component, normally an <img /> or a <div />. It should be only one element.", "classes": "Override or extend the styles applied to the component. See CSS API below for more details.", "columnSpan": "The number of columns taken up by the component", "component": "The component used for the root node. Either a string to use a HTML element or a component.", diff --git a/docs/translations/api-docs/masonry-item/masonry-item.json b/docs/translations/api-docs/masonry-item/masonry-item.json index 2affccb38312ea..0f70ca91820611 100644 --- a/docs/translations/api-docs/masonry-item/masonry-item.json +++ b/docs/translations/api-docs/masonry-item/masonry-item.json @@ -1,7 +1,7 @@ { "componentDescription": "", "propDescriptions": { - "children": "The content of the component, normally an <img /> or a <div />.", + "children": "The content of the component, normally an <img /> or a <div />. It should be only one element.", "classes": "Override or extend the styles applied to the component. See CSS API below for more details.", "columnSpan": "The number of columns taken up by the component", "component": "The component used for the root node. Either a string to use a HTML element or a component.", diff --git a/docs/translations/api-docs/masonry/masonry-de.json b/docs/translations/api-docs/masonry/masonry-de.json index 529c7b45599a51..548f75a86fdaa2 100644 --- a/docs/translations/api-docs/masonry/masonry-de.json +++ b/docs/translations/api-docs/masonry/masonry-de.json @@ -1,11 +1,11 @@ { "componentDescription": "", "propDescriptions": { - "children": "The content of the component, normally <MasonryItem />s.", + "children": "The content of the component. It's recommended to be <MasonryItem />s.", "classes": "Override or extend the styles applied to the component. See CSS API below for more details.", "columns": "Number of columns.", "component": "The component used for the root node. Either a string to use a HTML element or a component.", - "spacing": "Defines the space between children.", + "spacing": "Defines the space between children. It is a factor of the theme's spacing.", "sx": "Allows defining system overrides as well as additional CSS styles. See the `sx` page for more details." }, "classDescriptions": { "root": { "description": "Styles applied to the root element." } } diff --git a/docs/translations/api-docs/masonry/masonry-es.json b/docs/translations/api-docs/masonry/masonry-es.json index 529c7b45599a51..548f75a86fdaa2 100644 --- a/docs/translations/api-docs/masonry/masonry-es.json +++ b/docs/translations/api-docs/masonry/masonry-es.json @@ -1,11 +1,11 @@ { "componentDescription": "", "propDescriptions": { - "children": "The content of the component, normally <MasonryItem />s.", + "children": "The content of the component. It's recommended to be <MasonryItem />s.", "classes": "Override or extend the styles applied to the component. See CSS API below for more details.", "columns": "Number of columns.", "component": "The component used for the root node. Either a string to use a HTML element or a component.", - "spacing": "Defines the space between children.", + "spacing": "Defines the space between children. It is a factor of the theme's spacing.", "sx": "Allows defining system overrides as well as additional CSS styles. See the `sx` page for more details." }, "classDescriptions": { "root": { "description": "Styles applied to the root element." } } diff --git a/docs/translations/api-docs/masonry/masonry-fr.json b/docs/translations/api-docs/masonry/masonry-fr.json index 529c7b45599a51..548f75a86fdaa2 100644 --- a/docs/translations/api-docs/masonry/masonry-fr.json +++ b/docs/translations/api-docs/masonry/masonry-fr.json @@ -1,11 +1,11 @@ { "componentDescription": "", "propDescriptions": { - "children": "The content of the component, normally <MasonryItem />s.", + "children": "The content of the component. It's recommended to be <MasonryItem />s.", "classes": "Override or extend the styles applied to the component. See CSS API below for more details.", "columns": "Number of columns.", "component": "The component used for the root node. Either a string to use a HTML element or a component.", - "spacing": "Defines the space between children.", + "spacing": "Defines the space between children. It is a factor of the theme's spacing.", "sx": "Allows defining system overrides as well as additional CSS styles. See the `sx` page for more details." }, "classDescriptions": { "root": { "description": "Styles applied to the root element." } } diff --git a/docs/translations/api-docs/masonry/masonry-ja.json b/docs/translations/api-docs/masonry/masonry-ja.json index 529c7b45599a51..548f75a86fdaa2 100644 --- a/docs/translations/api-docs/masonry/masonry-ja.json +++ b/docs/translations/api-docs/masonry/masonry-ja.json @@ -1,11 +1,11 @@ { "componentDescription": "", "propDescriptions": { - "children": "The content of the component, normally <MasonryItem />s.", + "children": "The content of the component. It's recommended to be <MasonryItem />s.", "classes": "Override or extend the styles applied to the component. See CSS API below for more details.", "columns": "Number of columns.", "component": "The component used for the root node. Either a string to use a HTML element or a component.", - "spacing": "Defines the space between children.", + "spacing": "Defines the space between children. It is a factor of the theme's spacing.", "sx": "Allows defining system overrides as well as additional CSS styles. See the `sx` page for more details." }, "classDescriptions": { "root": { "description": "Styles applied to the root element." } } diff --git a/docs/translations/api-docs/masonry/masonry-pt.json b/docs/translations/api-docs/masonry/masonry-pt.json index 529c7b45599a51..548f75a86fdaa2 100644 --- a/docs/translations/api-docs/masonry/masonry-pt.json +++ b/docs/translations/api-docs/masonry/masonry-pt.json @@ -1,11 +1,11 @@ { "componentDescription": "", "propDescriptions": { - "children": "The content of the component, normally <MasonryItem />s.", + "children": "The content of the component. It's recommended to be <MasonryItem />s.", "classes": "Override or extend the styles applied to the component. See CSS API below for more details.", "columns": "Number of columns.", "component": "The component used for the root node. Either a string to use a HTML element or a component.", - "spacing": "Defines the space between children.", + "spacing": "Defines the space between children. It is a factor of the theme's spacing.", "sx": "Allows defining system overrides as well as additional CSS styles. See the `sx` page for more details." }, "classDescriptions": { "root": { "description": "Styles applied to the root element." } } diff --git a/docs/translations/api-docs/masonry/masonry-ru.json b/docs/translations/api-docs/masonry/masonry-ru.json index 529c7b45599a51..548f75a86fdaa2 100644 --- a/docs/translations/api-docs/masonry/masonry-ru.json +++ b/docs/translations/api-docs/masonry/masonry-ru.json @@ -1,11 +1,11 @@ { "componentDescription": "", "propDescriptions": { - "children": "The content of the component, normally <MasonryItem />s.", + "children": "The content of the component. It's recommended to be <MasonryItem />s.", "classes": "Override or extend the styles applied to the component. See CSS API below for more details.", "columns": "Number of columns.", "component": "The component used for the root node. Either a string to use a HTML element or a component.", - "spacing": "Defines the space between children.", + "spacing": "Defines the space between children. It is a factor of the theme's spacing.", "sx": "Allows defining system overrides as well as additional CSS styles. See the `sx` page for more details." }, "classDescriptions": { "root": { "description": "Styles applied to the root element." } } diff --git a/docs/translations/api-docs/masonry/masonry-zh.json b/docs/translations/api-docs/masonry/masonry-zh.json index 529c7b45599a51..548f75a86fdaa2 100644 --- a/docs/translations/api-docs/masonry/masonry-zh.json +++ b/docs/translations/api-docs/masonry/masonry-zh.json @@ -1,11 +1,11 @@ { "componentDescription": "", "propDescriptions": { - "children": "The content of the component, normally <MasonryItem />s.", + "children": "The content of the component. It's recommended to be <MasonryItem />s.", "classes": "Override or extend the styles applied to the component. See CSS API below for more details.", "columns": "Number of columns.", "component": "The component used for the root node. Either a string to use a HTML element or a component.", - "spacing": "Defines the space between children.", + "spacing": "Defines the space between children. It is a factor of the theme's spacing.", "sx": "Allows defining system overrides as well as additional CSS styles. See the `sx` page for more details." }, "classDescriptions": { "root": { "description": "Styles applied to the root element." } } diff --git a/docs/translations/api-docs/masonry/masonry.json b/docs/translations/api-docs/masonry/masonry.json index 529c7b45599a51..548f75a86fdaa2 100644 --- a/docs/translations/api-docs/masonry/masonry.json +++ b/docs/translations/api-docs/masonry/masonry.json @@ -1,11 +1,11 @@ { "componentDescription": "", "propDescriptions": { - "children": "The content of the component, normally <MasonryItem />s.", + "children": "The content of the component. It's recommended to be <MasonryItem />s.", "classes": "Override or extend the styles applied to the component. See CSS API below for more details.", "columns": "Number of columns.", "component": "The component used for the root node. Either a string to use a HTML element or a component.", - "spacing": "Defines the space between children.", + "spacing": "Defines the space between children. It is a factor of the theme's spacing.", "sx": "Allows defining system overrides as well as additional CSS styles. See the `sx` page for more details." }, "classDescriptions": { "root": { "description": "Styles applied to the root element." } } diff --git a/package.json b/package.json index f57d2ae35c0674..5f9513b9aa9839 100644 --- a/package.json +++ b/package.json @@ -157,7 +157,6 @@ "react-router-dom": "^5.2.0", "react-test-renderer": "^17.0.2", "remark": "^13.0.0", - "resize-observer-polyfill": "^1.5.1", "rimraf": "^3.0.0", "rollup": "^2.56.2", "rollup-plugin-babel": "^4.3.3", diff --git a/packages/material-ui-lab/package.json b/packages/material-ui-lab/package.json index 8ce8c6c20cde98..d627377d369ed7 100644 --- a/packages/material-ui-lab/package.json +++ b/packages/material-ui-lab/package.json @@ -75,6 +75,7 @@ "prop-types": "^15.7.2", "react-is": "^17.0.2", "react-transition-group": "^4.4.1", + "resize-observer-polyfill": "^1.5.1", "rifm": "^0.12.0" }, "devDependencies": { diff --git a/packages/material-ui-lab/src/Masonry/Masonry.d.ts b/packages/material-ui-lab/src/Masonry/Masonry.d.ts index 9b02d573f55a65..90a5f588fa6956 100644 --- a/packages/material-ui-lab/src/Masonry/Masonry.d.ts +++ b/packages/material-ui-lab/src/Masonry/Masonry.d.ts @@ -6,7 +6,7 @@ import { MasonryClasses } from './masonryClasses'; export interface MasonryTypeMap

{ props: P & { /** - * The content of the component, normally ``s. + * The content of the component. It's recommended to be ``s. */ children: NonNullable; /** @@ -19,7 +19,7 @@ export interface MasonryTypeMap

{ */ columns?: ResponsiveStyleValue; /** - * Defines the space between children. + * Defines the space between children. It is a factor of the theme's spacing. * @default 1 */ spacing?: ResponsiveStyleValue; diff --git a/packages/material-ui-lab/src/Masonry/Masonry.js b/packages/material-ui-lab/src/Masonry/Masonry.js index f457a99eb0a6e5..60efec7a2e3f61 100644 --- a/packages/material-ui-lab/src/Masonry/Masonry.js +++ b/packages/material-ui-lab/src/Masonry/Masonry.js @@ -48,10 +48,10 @@ export const style = ({ styleProps, theme }) => { }; }; - styles = deepmerge( - styles, - handleBreakpoints({ theme }, spacingValues, spacingStyleFromPropValue), - ); + styles = { + ...styles, + ...handleBreakpoints({ theme }, spacingValues, spacingStyleFromPropValue), + }; const columnValues = resolveBreakpointValues({ values: styleProps.columns, base }); const columnStyleFromPropValue = (propValue) => { @@ -83,10 +83,8 @@ const Masonry = React.forwardRef(function Masonry(inProps, ref) { const styleProps = { ...props, spacing, columns }; const classes = useUtilityClasses(styleProps); - const masonry = React.useMemo(() => ({ spacing }), [spacing]); - return ( - + `s. + * The content of the component. It's recommended to be ``s. */ children: PropTypes /* @typescript-to-proptypes-ignore */.node.isRequired, /** @@ -133,7 +131,7 @@ Masonry.propTypes /* remove-proptypes */ = { */ component: PropTypes.elementType, /** - * Defines the space between children. + * Defines the space between children. It is a factor of the theme's spacing. * @default 1 */ spacing: PropTypes.oneOfType([ diff --git a/packages/material-ui-lab/src/Masonry/Masonry.test.js b/packages/material-ui-lab/src/Masonry/Masonry.test.js index 6b4e18f8082feb..13f228f630581b 100644 --- a/packages/material-ui-lab/src/Masonry/Masonry.test.js +++ b/packages/material-ui-lab/src/Masonry/Masonry.test.js @@ -18,29 +18,14 @@ describe('', () => { inheritComponent: 'div', render, refInstanceof: window.HTMLDivElement, - testComponentPropWith: 'div', + testComponentPropWith: 'span', testVariantProps: { variant: 'foo' }, muiName: 'MuiMasonry', skip: ['componentsProp'], }), ); - const itemsData = [ - { - img: '/fake1.png', - title: 'fake1', - }, - { - img: '/fake2.png', - title: 'fake2', - }, - ]; const theme = createTheme({ spacing: 8 }); - const children = itemsData.map((item, idx) => ( -

- {item.title} -
- )); describe('style attribute:', () => { it('should render with correct default styles', () => { @@ -124,26 +109,4 @@ describe('', () => { }); }); }); - - describe('props:', () => { - describe('prop: component', () => { - it('should render a div by default', () => { - const { container } = render({children}); - expect(container.firstChild).to.have.property('nodeName', 'DIV'); - }); - - it('should render a different component', () => { - const { container } = render({children}); - expect(container.firstChild).to.have.property('nodeName', 'SPAN'); - }); - }); - - describe('prop: className', () => { - it('should append the className to the root element', () => { - const { container } = render({children}); - expect(container.firstChild).to.have.class(classes.root); - expect(container.firstChild).to.have.class('foo'); - }); - }); - }); }); diff --git a/packages/material-ui-lab/src/MasonryItem/MasonryItem.d.ts b/packages/material-ui-lab/src/MasonryItem/MasonryItem.d.ts index a23f8a9a740963..aff6a3db7ac905 100644 --- a/packages/material-ui-lab/src/MasonryItem/MasonryItem.d.ts +++ b/packages/material-ui-lab/src/MasonryItem/MasonryItem.d.ts @@ -6,7 +6,7 @@ import { MasonryItemClasses } from './masonryItemClasses'; export interface MasonryItemTypeMap

{ props: P & { /** - * The content of the component, normally an `` or a `

`. + * The content of the component, normally an `` or a `
`. It should be only one element. */ children: NonNullable; /** diff --git a/packages/material-ui-lab/src/MasonryItem/MasonryItem.js b/packages/material-ui-lab/src/MasonryItem/MasonryItem.js index 7dc9e2b52239ca..c51d2204ca71ea 100644 --- a/packages/material-ui-lab/src/MasonryItem/MasonryItem.js +++ b/packages/material-ui-lab/src/MasonryItem/MasonryItem.js @@ -7,7 +7,7 @@ import { handleBreakpoints, unstable_resolveBreakpointValues as resolveBreakpointValues, } from '@material-ui/system'; -import { deepmerge, unstable_useForkRef as useForkRef } from '@material-ui/utils'; +import { unstable_useForkRef as useForkRef } from '@material-ui/utils'; import { unstable_composeClasses as composeClasses } from '@material-ui/unstyled'; import { styled, useThemeProps } from '@material-ui/core/styles'; import ResizeObserver from 'resize-observer-polyfill'; @@ -51,7 +51,7 @@ export const style = ({ styleProps, theme }) => { paddingBottom: gap - 1, }; }; - styles = deepmerge(styles, handleBreakpoints({ theme }, spacingValues, styleFromPropValue)); + styles = { ...styles, ...handleBreakpoints({ theme }, spacingValues, styleFromPropValue) }; return styles; }; @@ -73,14 +73,7 @@ const MasonryItem = React.forwardRef(function MasonryItem(inProps, ref) { const masonryItemRef = React.useRef(null); const { spacing = 1 } = React.useContext(MasonryContext); - const { - children, - className, - component = 'div', - columnSpan = 1, - height = undefined, - ...other - } = props; + const { children, className, component = 'div', columnSpan = 1, height, ...other } = props; const [styleProps, setStyleProps] = React.useState({ ...props, spacing, @@ -135,7 +128,7 @@ MasonryItem.propTypes /* remove-proptypes */ = { // | To update them edit the d.ts file and run "yarn proptypes" | // ---------------------------------------------------------------------- /** - * The content of the component, normally an `` or a `
`. + * The content of the component, normally an `` or a `
`. It should be only one element. */ children: PropTypes.element.isRequired, /** diff --git a/packages/material-ui-lab/src/MasonryItem/MasonryItem.test.js b/packages/material-ui-lab/src/MasonryItem/MasonryItem.test.js index 0e4017a2d3e3fe..2d5c3bdc369b15 100644 --- a/packages/material-ui-lab/src/MasonryItem/MasonryItem.test.js +++ b/packages/material-ui-lab/src/MasonryItem/MasonryItem.test.js @@ -18,7 +18,7 @@ describe('', () => { inheritComponent: 'div', render, refInstanceof: window.HTMLDivElement, - testComponentPropWith: 'div', + testComponentPropWith: 'span', testVariantProps: { variant: 'foo' }, muiName: 'MuiMasonryItem', skip: [ @@ -108,26 +108,4 @@ describe('', () => { ); }); }); - - describe('props:', () => { - describe('prop: component', () => { - it('should render a div by default', () => { - const { container } = render({children}); - expect(container.firstChild).to.have.property('nodeName', 'DIV'); - }); - - it('should render a different component', () => { - const { container } = render({children}); - expect(container.firstChild).to.have.property('nodeName', 'SPAN'); - }); - }); - - describe('prop: className', () => { - it('should append the className to the root element', () => { - const { container } = render({children}); - expect(container.firstChild).to.have.class(classes.root); - expect(container.firstChild).to.have.class('foo'); - }); - }); - }); }); From 5fdfb537b37f2c71b9721a0e730fc4c5ce534692 Mon Sep 17 00:00:00 2001 From: Benny Joo Date: Thu, 29 Jul 2021 20:19:10 +0100 Subject: [PATCH 08/59] Use native ResizeObserver for component and pass tests --- packages/material-ui-lab/package.json | 1 - .../src/Masonry/Masonry.test.js | 3 +- .../src/MasonryItem/MasonryItem.js | 31 ++++++++----------- .../src/MasonryItem/MasonryItem.test.js | 10 +++--- 4 files changed, 20 insertions(+), 25 deletions(-) diff --git a/packages/material-ui-lab/package.json b/packages/material-ui-lab/package.json index d627377d369ed7..8ce8c6c20cde98 100644 --- a/packages/material-ui-lab/package.json +++ b/packages/material-ui-lab/package.json @@ -75,7 +75,6 @@ "prop-types": "^15.7.2", "react-is": "^17.0.2", "react-transition-group": "^4.4.1", - "resize-observer-polyfill": "^1.5.1", "rifm": "^0.12.0" }, "devDependencies": { diff --git a/packages/material-ui-lab/src/Masonry/Masonry.test.js b/packages/material-ui-lab/src/Masonry/Masonry.test.js index 13f228f630581b..5e3b307fa4be5b 100644 --- a/packages/material-ui-lab/src/Masonry/Masonry.test.js +++ b/packages/material-ui-lab/src/Masonry/Masonry.test.js @@ -19,9 +19,8 @@ describe('', () => { render, refInstanceof: window.HTMLDivElement, testComponentPropWith: 'span', - testVariantProps: { variant: 'foo' }, muiName: 'MuiMasonry', - skip: ['componentsProp'], + skip: ['componentsProp', 'themeVariants'], }), ); diff --git a/packages/material-ui-lab/src/MasonryItem/MasonryItem.js b/packages/material-ui-lab/src/MasonryItem/MasonryItem.js index c51d2204ca71ea..3a9e39d6b093af 100644 --- a/packages/material-ui-lab/src/MasonryItem/MasonryItem.js +++ b/packages/material-ui-lab/src/MasonryItem/MasonryItem.js @@ -10,7 +10,6 @@ import { import { unstable_useForkRef as useForkRef } from '@material-ui/utils'; import { unstable_composeClasses as composeClasses } from '@material-ui/unstyled'; import { styled, useThemeProps } from '@material-ui/core/styles'; -import ResizeObserver from 'resize-observer-polyfill'; import { getMasonryItemUtilityClass } from './masonryItemClasses'; import MasonryContext from '../Masonry/MasonryContext'; @@ -83,29 +82,25 @@ const MasonryItem = React.forwardRef(function MasonryItem(inProps, ref) { const classes = useUtilityClasses(styleProps); - // If height is passed by user, ResizeObserver is not used. - const resizeObserver = React.useRef( - height === undefined - ? new ResizeObserver(([item]) => { - setStyleProps({ - ...styleProps, - height: item.contentRect.height, - }); - }) - : null, - ); - React.useEffect(() => { - if (resizeObserver?.current && masonryItemRef?.current) { - const observer = resizeObserver.current; + const resizeObserver = + height === undefined + ? new ResizeObserver(([item]) => { + setStyleProps({ + ...styleProps, + height: item.contentRect.height, + }); + }) + : null; // If height is passed by user, ResizeObserver is not used. + if (resizeObserver && masonryItemRef?.current) { const item = masonryItemRef.current.firstChild; - observer.observe(item); + resizeObserver.observe(item); return () => { - observer.unobserve(item); + resizeObserver.unobserve(item); }; } return true; - }, []); + }, []); // eslint-disable-line const handleRef = useForkRef(ref, masonryItemRef); diff --git a/packages/material-ui-lab/src/MasonryItem/MasonryItem.test.js b/packages/material-ui-lab/src/MasonryItem/MasonryItem.test.js index 2d5c3bdc369b15..5e5109d521785c 100644 --- a/packages/material-ui-lab/src/MasonryItem/MasonryItem.test.js +++ b/packages/material-ui-lab/src/MasonryItem/MasonryItem.test.js @@ -4,8 +4,12 @@ import MasonryItem, { masonryItemClasses as classes } from '@material-ui/lab/Mas import { expect } from 'chai'; import { createTheme } from '@material-ui/core/styles'; import defaultTheme from '@material-ui/core/styles/defaultTheme'; +import ResizeObserver from 'resize-observer-polyfill'; import { style } from './MasonryItem'; +// Mount ResizeObserver; otherwise, this error comes up: `[ReferenceError: ResizeObserver is not defined]` +global.ResizeObserver = ResizeObserver; + describe('', () => { const render = createClientRender(); @@ -19,13 +23,11 @@ describe('', () => { render, refInstanceof: window.HTMLDivElement, testComponentPropWith: 'span', - testVariantProps: { variant: 'foo' }, muiName: 'MuiMasonryItem', skip: [ 'componentsProp', - // reactTestRenderer fails because React state updates should be wrapped into act(...) - // I followed the guideline here: https://reactjs.org/link/wrap-tests-with-act - // but tests still couldn't pass + 'themeVariants', + // reactTestRenderer fails due to this error: `TypeError: parameter 1 is not of type "Element"` 'reactTestRenderer', ], }), From 0c04aa1f7eeb3f8f8dacdb928850b4cbdc3e9c09 Mon Sep 17 00:00:00 2001 From: Benny Joo Date: Mon, 2 Aug 2021 10:23:28 +0100 Subject: [PATCH 09/59] Clean up resize observer in test and general refactoring --- docs/pages/api-docs/masonry-item.json | 2 +- docs/src/pages/components/masonry/masonry.md | 6 +-- .../material-ui-lab/src/Masonry/Masonry.js | 8 ++-- .../src/MasonryItem/MasonryItem.d.ts | 1 - .../src/MasonryItem/MasonryItem.js | 42 +++++++++---------- .../src/MasonryItem/MasonryItem.test.js | 12 ++++-- 6 files changed, 37 insertions(+), 34 deletions(-) diff --git a/docs/pages/api-docs/masonry-item.json b/docs/pages/api-docs/masonry-item.json index ea4d3a2d8a506d..a06eac80460153 100644 --- a/docs/pages/api-docs/masonry-item.json +++ b/docs/pages/api-docs/masonry-item.json @@ -4,7 +4,7 @@ "classes": { "type": { "name": "object" } }, "columnSpan": { "type": { "name": "number" }, "default": "1" }, "component": { "type": { "name": "elementType" } }, - "height": { "type": { "name": "number" }, "default": "undefined" }, + "height": { "type": { "name": "number" } }, "sx": { "type": { "name": "object" } } }, "name": "MasonryItem", diff --git a/docs/src/pages/components/masonry/masonry.md b/docs/src/pages/components/masonry/masonry.md index 20b8747929a967..2e09b602015ad2 100644 --- a/docs/src/pages/components/masonry/masonry.md +++ b/docs/src/pages/components/masonry/masonry.md @@ -16,14 +16,14 @@ Masonry maintains a list of content blocks with a consistent width but variable {{"demo": "pages/components/masonry/ImageMasonry.js", "bg": true}} -> `` can receive not only `` elements but also `
` elements as shown in the following demo: +`` can receive not only `` elements but any element, such as `
`: {{"demo": "pages/components/masonry/BasicMasonry.js", "bg": true}} -> You can configure the number of columns taken up by each `` as shown in the following demo: +You can configure the number of columns taken up by each ``: {{"demo": "pages/components/masonry/DiffColSizeMasonry.js", "bg": true}} -> In order to use server-side rendering, you should pass the height of the content of `` as shown in the following demo: +In order to use server-side rendering, you should pass the height of the content of ``: {{"demo": "pages/components/masonry/SSRMasonry.js", "bg": true}} diff --git a/packages/material-ui-lab/src/Masonry/Masonry.js b/packages/material-ui-lab/src/Masonry/Masonry.js index 60efec7a2e3f61..42afd896e54308 100644 --- a/packages/material-ui-lab/src/Masonry/Masonry.js +++ b/packages/material-ui-lab/src/Masonry/Masonry.js @@ -33,12 +33,12 @@ export const style = ({ styleProps, theme }) => { rowGap: 1, }; - const base = Object.keys(theme.breakpoints.values).reduce((acc, breakpoint) => { + const base = {}; + Object.keys(theme.breakpoints.values).forEach((breakpoint) => { if (styleProps.spacing[breakpoint] != null) { - acc[breakpoint] = true; + base[breakpoint] = true; } - return acc; - }, {}); + }); const spacingValues = resolveBreakpointValues({ values: styleProps.spacing, base }); const transformer = createUnarySpacing(theme); diff --git a/packages/material-ui-lab/src/MasonryItem/MasonryItem.d.ts b/packages/material-ui-lab/src/MasonryItem/MasonryItem.d.ts index aff6a3db7ac905..d410df61ee1cad 100644 --- a/packages/material-ui-lab/src/MasonryItem/MasonryItem.d.ts +++ b/packages/material-ui-lab/src/MasonryItem/MasonryItem.d.ts @@ -15,7 +15,6 @@ export interface MasonryItemTypeMap

classes?: Partial; /** * The height of the component in px. - * @default undefined */ height?: number; /** diff --git a/packages/material-ui-lab/src/MasonryItem/MasonryItem.js b/packages/material-ui-lab/src/MasonryItem/MasonryItem.js index 3a9e39d6b093af..64035568f154b6 100644 --- a/packages/material-ui-lab/src/MasonryItem/MasonryItem.js +++ b/packages/material-ui-lab/src/MasonryItem/MasonryItem.js @@ -34,12 +34,12 @@ export const style = ({ styleProps, theme }) => { gridColumnEnd: `span ${styleProps.columnSpan}`, }; - const base = Object.keys(theme.breakpoints.values).reduce((acc, breakpoint) => { + const base = {}; + Object.keys(theme.breakpoints.values).forEach((breakpoint) => { if (styleProps.spacing[breakpoint] != null) { - acc[breakpoint] = true; + base[breakpoint] = true; } - return acc; - }, {}); + }); const spacingValues = resolveBreakpointValues({ values: styleProps.spacing, base }); const transformer = createUnarySpacing(theme); const styleFromPropValue = (propValue) => { @@ -83,24 +83,23 @@ const MasonryItem = React.forwardRef(function MasonryItem(inProps, ref) { const classes = useUtilityClasses(styleProps); React.useEffect(() => { - const resizeObserver = - height === undefined - ? new ResizeObserver(([item]) => { - setStyleProps({ - ...styleProps, - height: item.contentRect.height, - }); - }) - : null; // If height is passed by user, ResizeObserver is not used. - if (resizeObserver && masonryItemRef?.current) { - const item = masonryItemRef.current.firstChild; - resizeObserver.observe(item); - return () => { - resizeObserver.unobserve(item); - }; + // If height is passed by user, ResizeObserver is not used. + if (height !== undefined) { + return true; } - return true; - }, []); // eslint-disable-line + const resizeObserver = new ResizeObserver(([item]) => { + setStyleProps({ + ...styleProps, + height: item.contentRect.height, + }); + }); + + const item = masonryItemRef.current?.firstChild; + resizeObserver.observe(item); + return () => { + resizeObserver.unobserve(item); + }; + }, [height]); // eslint-disable-line const handleRef = useForkRef(ref, masonryItemRef); @@ -146,7 +145,6 @@ MasonryItem.propTypes /* remove-proptypes */ = { component: PropTypes.elementType, /** * The height of the component in px. - * @default undefined */ height: PropTypes.number, /** diff --git a/packages/material-ui-lab/src/MasonryItem/MasonryItem.test.js b/packages/material-ui-lab/src/MasonryItem/MasonryItem.test.js index 5e5109d521785c..27e28e486dfc9a 100644 --- a/packages/material-ui-lab/src/MasonryItem/MasonryItem.test.js +++ b/packages/material-ui-lab/src/MasonryItem/MasonryItem.test.js @@ -7,10 +7,16 @@ import defaultTheme from '@material-ui/core/styles/defaultTheme'; import ResizeObserver from 'resize-observer-polyfill'; import { style } from './MasonryItem'; -// Mount ResizeObserver; otherwise, this error comes up: `[ReferenceError: ResizeObserver is not defined]` -global.ResizeObserver = ResizeObserver; - describe('', () => { + // Mount ResizeObserver; otherwise, this error comes up: `[ReferenceError: ResizeObserver is not defined]` + before(() => { + global.ResizeObserver = ResizeObserver; + }); + + after(() => { + delete global.ResizeObserver; + }); + const render = createClientRender(); describeConformanceV5( From 411bf2d4c92620b56336f3c88a02340f85432a8e Mon Sep 17 00:00:00 2001 From: Benny Joo Date: Mon, 2 Aug 2021 12:29:34 +0100 Subject: [PATCH 10/59] Improve demos and add more demos --- .../pages/components/masonry/FixedColumns.js | 28 +++++++++++++++ .../pages/components/masonry/FixedColumns.tsx | 28 +++++++++++++++ .../pages/components/masonry/FixedSpacing.js | 28 +++++++++++++++ .../pages/components/masonry/FixedSpacing.tsx | 28 +++++++++++++++ .../pages/components/masonry/ImageMasonry.js | 6 +--- .../pages/components/masonry/ImageMasonry.tsx | 6 +--- .../components/masonry/ResponsiveColumns.js | 28 +++++++++++++++ .../components/masonry/ResponsiveColumns.tsx | 28 +++++++++++++++ .../components/masonry/ResponsiveSpacing.js | 28 +++++++++++++++ .../components/masonry/ResponsiveSpacing.tsx | 28 +++++++++++++++ docs/src/pages/components/masonry/masonry.md | 36 ++++++++++++++++--- 11 files changed, 257 insertions(+), 15 deletions(-) create mode 100644 docs/src/pages/components/masonry/FixedColumns.js create mode 100644 docs/src/pages/components/masonry/FixedColumns.tsx create mode 100644 docs/src/pages/components/masonry/FixedSpacing.js create mode 100644 docs/src/pages/components/masonry/FixedSpacing.tsx create mode 100644 docs/src/pages/components/masonry/ResponsiveColumns.js create mode 100644 docs/src/pages/components/masonry/ResponsiveColumns.tsx create mode 100644 docs/src/pages/components/masonry/ResponsiveSpacing.js create mode 100644 docs/src/pages/components/masonry/ResponsiveSpacing.tsx diff --git a/docs/src/pages/components/masonry/FixedColumns.js b/docs/src/pages/components/masonry/FixedColumns.js new file mode 100644 index 00000000000000..f03a6b1e1b6d68 --- /dev/null +++ b/docs/src/pages/components/masonry/FixedColumns.js @@ -0,0 +1,28 @@ +/* eslint-disable @typescript-eslint/no-use-before-define */ +import * as React from 'react'; +import Box from '@material-ui/core/Box'; +import Masonry from '@material-ui/lab/Masonry'; +import MasonryItem from '@material-ui/lab/MasonryItem'; + +export default function FixedColumns() { + return ( + + {heights.map((height, index) => ( + + + {index + 1} + + + ))} + + ); +} + +const heights = [150, 30, 90, 70, 110, 150, 130, 80, 50, 90, 100, 150, 30, 50, 80]; diff --git a/docs/src/pages/components/masonry/FixedColumns.tsx b/docs/src/pages/components/masonry/FixedColumns.tsx new file mode 100644 index 00000000000000..f03a6b1e1b6d68 --- /dev/null +++ b/docs/src/pages/components/masonry/FixedColumns.tsx @@ -0,0 +1,28 @@ +/* eslint-disable @typescript-eslint/no-use-before-define */ +import * as React from 'react'; +import Box from '@material-ui/core/Box'; +import Masonry from '@material-ui/lab/Masonry'; +import MasonryItem from '@material-ui/lab/MasonryItem'; + +export default function FixedColumns() { + return ( + + {heights.map((height, index) => ( + + + {index + 1} + + + ))} + + ); +} + +const heights = [150, 30, 90, 70, 110, 150, 130, 80, 50, 90, 100, 150, 30, 50, 80]; diff --git a/docs/src/pages/components/masonry/FixedSpacing.js b/docs/src/pages/components/masonry/FixedSpacing.js new file mode 100644 index 00000000000000..16ee7a6c5bdf6d --- /dev/null +++ b/docs/src/pages/components/masonry/FixedSpacing.js @@ -0,0 +1,28 @@ +/* eslint-disable @typescript-eslint/no-use-before-define */ +import * as React from 'react'; +import Box from '@material-ui/core/Box'; +import Masonry from '@material-ui/lab/Masonry'; +import MasonryItem from '@material-ui/lab/MasonryItem'; + +export default function FixedSpacing() { + return ( + + {heights.map((height, index) => ( + + + {index + 1} + + + ))} + + ); +} + +const heights = [150, 30, 90, 70, 110, 150, 130, 80, 50, 90, 100, 150, 30, 50, 80]; diff --git a/docs/src/pages/components/masonry/FixedSpacing.tsx b/docs/src/pages/components/masonry/FixedSpacing.tsx new file mode 100644 index 00000000000000..16ee7a6c5bdf6d --- /dev/null +++ b/docs/src/pages/components/masonry/FixedSpacing.tsx @@ -0,0 +1,28 @@ +/* eslint-disable @typescript-eslint/no-use-before-define */ +import * as React from 'react'; +import Box from '@material-ui/core/Box'; +import Masonry from '@material-ui/lab/Masonry'; +import MasonryItem from '@material-ui/lab/MasonryItem'; + +export default function FixedSpacing() { + return ( + + {heights.map((height, index) => ( + + + {index + 1} + + + ))} + + ); +} + +const heights = [150, 30, 90, 70, 110, 150, 130, 80, 50, 90, 100, 150, 30, 50, 80]; diff --git a/docs/src/pages/components/masonry/ImageMasonry.js b/docs/src/pages/components/masonry/ImageMasonry.js index 720c0fd44a3b88..7ad856c182791e 100644 --- a/docs/src/pages/components/masonry/ImageMasonry.js +++ b/docs/src/pages/components/masonry/ImageMasonry.js @@ -5,11 +5,7 @@ import MasonryItem from '@material-ui/lab/MasonryItem'; export default function ImageMasonry() { return ( - + {itemData.map((item) => ( + {itemData.map((item) => ( + {heights.map((height, index) => ( + + + {index + 1} + + + ))} + + ); +} + +const heights = [150, 30, 90, 70, 110, 150, 130, 80, 50, 90, 100, 150, 30, 50, 80]; diff --git a/docs/src/pages/components/masonry/ResponsiveColumns.tsx b/docs/src/pages/components/masonry/ResponsiveColumns.tsx new file mode 100644 index 00000000000000..b472fd618c9468 --- /dev/null +++ b/docs/src/pages/components/masonry/ResponsiveColumns.tsx @@ -0,0 +1,28 @@ +/* eslint-disable @typescript-eslint/no-use-before-define */ +import * as React from 'react'; +import Box from '@material-ui/core/Box'; +import Masonry from '@material-ui/lab/Masonry'; +import MasonryItem from '@material-ui/lab/MasonryItem'; + +export default function ResponsiveColumns() { + return ( + + {heights.map((height, index) => ( + + + {index + 1} + + + ))} + + ); +} + +const heights = [150, 30, 90, 70, 110, 150, 130, 80, 50, 90, 100, 150, 30, 50, 80]; diff --git a/docs/src/pages/components/masonry/ResponsiveSpacing.js b/docs/src/pages/components/masonry/ResponsiveSpacing.js new file mode 100644 index 00000000000000..a5259af225d88e --- /dev/null +++ b/docs/src/pages/components/masonry/ResponsiveSpacing.js @@ -0,0 +1,28 @@ +/* eslint-disable @typescript-eslint/no-use-before-define */ +import * as React from 'react'; +import Box from '@material-ui/core/Box'; +import Masonry from '@material-ui/lab/Masonry'; +import MasonryItem from '@material-ui/lab/MasonryItem'; + +export default function ResponsiveSpacing() { + return ( + + {heights.map((height, index) => ( + + + {index + 1} + + + ))} + + ); +} + +const heights = [150, 30, 90, 70, 110, 150, 130, 80, 50, 90, 100, 150, 30, 50, 80]; diff --git a/docs/src/pages/components/masonry/ResponsiveSpacing.tsx b/docs/src/pages/components/masonry/ResponsiveSpacing.tsx new file mode 100644 index 00000000000000..a5259af225d88e --- /dev/null +++ b/docs/src/pages/components/masonry/ResponsiveSpacing.tsx @@ -0,0 +1,28 @@ +/* eslint-disable @typescript-eslint/no-use-before-define */ +import * as React from 'react'; +import Box from '@material-ui/core/Box'; +import Masonry from '@material-ui/lab/Masonry'; +import MasonryItem from '@material-ui/lab/MasonryItem'; + +export default function ResponsiveSpacing() { + return ( + + {heights.map((height, index) => ( + + + {index + 1} + + + ))} + + ); +} + +const heights = [150, 30, 90, 70, 110, 150, 130, 80, 50, 90, 100, 150, 30, 50, 80]; diff --git a/docs/src/pages/components/masonry/masonry.md b/docs/src/pages/components/masonry/masonry.md index 2e09b602015ad2..e8353635baf5a4 100644 --- a/docs/src/pages/components/masonry/masonry.md +++ b/docs/src/pages/components/masonry/masonry.md @@ -12,18 +12,44 @@ Masonry maintains a list of content blocks with a consistent width but variable {{"component": "modules/components/ComponentLinkHeader.js", "design": true}} -## Masonry +## Basic Masonry + +`` is a container for one or more ``s. `` can receive any element including `

` and ``. Also, it is important to note that each `` accepts only one element. + +{{"demo": "pages/components/masonry/BasicMasonry.js", "bg": true}} + +## Image Masonry {{"demo": "pages/components/masonry/ImageMasonry.js", "bg": true}} -`` can receive not only `` elements but any element, such as `
`: +## Columns -{{"demo": "pages/components/masonry/BasicMasonry.js", "bg": true}} +By passing `columns` to ``, you can configure the number of columns of your masonry. + +{{"demo": "pages/components/masonry/FixedColumns.js", "bg": true}} + +`columns` accepts responsive values: + +{{"demo": "pages/components/masonry/ResponsiveColumns.js", "bg": true}} -You can configure the number of columns taken up by each ``: +## Spacing + +By passing `spacing` to ``, you can configure the spacing between ``s. It is important to note that `spacing` is a factor of the theme's spacing. + +{{"demo": "pages/components/masonry/FixedSpacing.js", "bg": true}} + +`spacing` accepts responsive values: + +{{"demo": "pages/components/masonry/ResponsiveSpacing.js", "bg": true}} + +## Column spanning + +By passing `columnSpan` to ``, you can configure the number of columns taken up by each ``. {{"demo": "pages/components/masonry/DiffColSizeMasonry.js", "bg": true}} -In order to use server-side rendering, you should pass the height of the content of ``: +## Server-side rendering + +By passing `height` to ``. you can use server-side rendering. You should either set `height: 100%` or a fixed height equivalent to `height` to the content of ``. Otherwise, there will be unwanted gap between `` and the content that you pass to it. {{"demo": "pages/components/masonry/SSRMasonry.js", "bg": true}} From 82169f3ca428f44dc0ccee89cf4846f960ab3acb Mon Sep 17 00:00:00 2001 From: Benny Joo Date: Mon, 2 Aug 2021 20:09:00 +0100 Subject: [PATCH 11/59] Explain difference from Masonry Image List --- docs/src/pages/components/masonry/masonry.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/src/pages/components/masonry/masonry.md b/docs/src/pages/components/masonry/masonry.md index e8353635baf5a4..2f19ee1b10d778 100644 --- a/docs/src/pages/components/masonry/masonry.md +++ b/docs/src/pages/components/masonry/masonry.md @@ -20,6 +20,8 @@ Masonry maintains a list of content blocks with a consistent width but variable ## Image Masonry +`` orders its children by row. If you would like to order images by column, you can use ``, whose more details can be found in [Masonry Image List](https://next.material-ui.com/components/image-list/#masonry-image-list). + {{"demo": "pages/components/masonry/ImageMasonry.js", "bg": true}} ## Columns From a1ce91ecdcc55a725c78660b6f3e69d3b5d760be Mon Sep 17 00:00:00 2001 From: Benny Joo Date: Mon, 2 Aug 2021 20:36:32 +0100 Subject: [PATCH 12/59] Set width to demos and make image masonry ignored from regression test --- docs/src/pages/components/masonry/BasicMasonry.js | 2 +- docs/src/pages/components/masonry/BasicMasonry.tsx | 2 +- docs/src/pages/components/masonry/DiffColSizeMasonry.js | 2 +- docs/src/pages/components/masonry/DiffColSizeMasonry.tsx | 2 +- docs/src/pages/components/masonry/FixedColumns.js | 2 +- docs/src/pages/components/masonry/FixedColumns.tsx | 2 +- docs/src/pages/components/masonry/FixedSpacing.js | 2 +- docs/src/pages/components/masonry/FixedSpacing.tsx | 2 +- docs/src/pages/components/masonry/ImageMasonry.js | 2 +- docs/src/pages/components/masonry/ImageMasonry.tsx | 2 +- docs/src/pages/components/masonry/ResponsiveColumns.js | 2 +- docs/src/pages/components/masonry/ResponsiveColumns.tsx | 2 +- docs/src/pages/components/masonry/ResponsiveSpacing.js | 6 +++++- docs/src/pages/components/masonry/ResponsiveSpacing.tsx | 6 +++++- docs/src/pages/components/masonry/SSRMasonry.js | 2 +- docs/src/pages/components/masonry/SSRMasonry.tsx | 2 +- packages/material-ui-lab/src/MasonryItem/MasonryItem.js | 2 +- test/regressions/index.js | 1 + 18 files changed, 26 insertions(+), 17 deletions(-) diff --git a/docs/src/pages/components/masonry/BasicMasonry.js b/docs/src/pages/components/masonry/BasicMasonry.js index 54d8a1c01b88db..aba9af235441ef 100644 --- a/docs/src/pages/components/masonry/BasicMasonry.js +++ b/docs/src/pages/components/masonry/BasicMasonry.js @@ -6,7 +6,7 @@ import MasonryItem from '@material-ui/lab/MasonryItem'; export default function BasicMasonry() { return ( - + {heights.map((height, index) => ( + {heights.map((height, index) => ( + {itemData.map((item, index) => ( + {itemData.map((item, index) => ( + {heights.map((height, index) => ( + {heights.map((height, index) => ( + {heights.map((height, index) => ( + {heights.map((height, index) => ( + {itemData.map((item) => ( + {itemData.map((item) => ( + {heights.map((height, index) => ( + {heights.map((height, index) => ( + {heights.map((height, index) => ( + {heights.map((height, index) => ( + {heights.map((height, index) => ( + {heights.map((height, index) => ( { resizeObserver.unobserve(item); }; - }, [height]); // eslint-disable-line + }, []); // eslint-disable-line const handleRef = useForkRef(ref, masonryItemRef); diff --git a/test/regressions/index.js b/test/regressions/index.js index 5febba1071a708..6575785d8044e6 100644 --- a/test/regressions/index.js +++ b/test/regressions/index.js @@ -68,6 +68,7 @@ const blacklist = [ 'docs-components-hidden', // Need to dynamically resize to test 'docs-components-icons/FontAwesomeIconSize.png', // Relies on cascading network requests 'docs-components-image-list', // Image don't load + 'docs-components-masonry/ImageMasonry.png', // Image don't load 'docs-components-material-icons/synonyms.png', // No component 'docs-components-menus', // Need interaction 'docs-components-modal/KeepMountedModal.png', // Needs interaction From 95e8cbb24c049809a2e24605da3d55785eef8821 Mon Sep 17 00:00:00 2001 From: Benny Joo Date: Mon, 2 Aug 2021 21:45:35 +0100 Subject: [PATCH 13/59] Describe limitation of Masonry with different column spanning --- docs/src/pages/components/masonry/ImageMasonry.js | 6 +++--- docs/src/pages/components/masonry/ImageMasonry.tsx | 6 +++--- docs/src/pages/components/masonry/masonry.md | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/src/pages/components/masonry/ImageMasonry.js b/docs/src/pages/components/masonry/ImageMasonry.js index ca750239ab90ba..29e4259075592a 100644 --- a/docs/src/pages/components/masonry/ImageMasonry.js +++ b/docs/src/pages/components/masonry/ImageMasonry.js @@ -5,12 +5,12 @@ import MasonryItem from '@material-ui/lab/MasonryItem'; export default function ImageMasonry() { return ( - + {itemData.map((item) => ( {item.title} diff --git a/docs/src/pages/components/masonry/ImageMasonry.tsx b/docs/src/pages/components/masonry/ImageMasonry.tsx index ca750239ab90ba..29e4259075592a 100644 --- a/docs/src/pages/components/masonry/ImageMasonry.tsx +++ b/docs/src/pages/components/masonry/ImageMasonry.tsx @@ -5,12 +5,12 @@ import MasonryItem from '@material-ui/lab/MasonryItem'; export default function ImageMasonry() { return ( - + {itemData.map((item) => ( {item.title} diff --git a/docs/src/pages/components/masonry/masonry.md b/docs/src/pages/components/masonry/masonry.md index 2f19ee1b10d778..f11fff69f16ba4 100644 --- a/docs/src/pages/components/masonry/masonry.md +++ b/docs/src/pages/components/masonry/masonry.md @@ -46,7 +46,7 @@ By passing `spacing` to ``, you can configure the spacing between ``, you can configure the number of columns taken up by each ``. +By passing `columnSpan` to ``, you can configure the number of columns taken up by each ``. However, you have to choose the value of `columnSpan` for each item carefully so that your masonry does not break. {{"demo": "pages/components/masonry/DiffColSizeMasonry.js", "bg": true}} From 50f6b556b3f2bd0dde7f2f33a07245c4441d017e Mon Sep 17 00:00:00 2001 From: Benny Joo Date: Tue, 3 Aug 2021 08:27:55 +0100 Subject: [PATCH 14/59] Add demo for broken column spanning masonry and fix border cut-off --- .../masonry/DiffColSizeMasonryBroken.js | 44 +++++++++++++++++++ .../masonry/DiffColSizeMasonryBroken.tsx | 44 +++++++++++++++++++ docs/src/pages/components/masonry/masonry.md | 6 ++- .../material-ui-lab/src/Masonry/Masonry.js | 1 + .../src/Masonry/Masonry.test.js | 3 ++ .../src/MasonryItem/MasonryItem.js | 2 + .../src/MasonryItem/MasonryItem.test.js | 4 ++ 7 files changed, 103 insertions(+), 1 deletion(-) create mode 100644 docs/src/pages/components/masonry/DiffColSizeMasonryBroken.js create mode 100644 docs/src/pages/components/masonry/DiffColSizeMasonryBroken.tsx diff --git a/docs/src/pages/components/masonry/DiffColSizeMasonryBroken.js b/docs/src/pages/components/masonry/DiffColSizeMasonryBroken.js new file mode 100644 index 00000000000000..103733da1061d0 --- /dev/null +++ b/docs/src/pages/components/masonry/DiffColSizeMasonryBroken.js @@ -0,0 +1,44 @@ +/* eslint-disable @typescript-eslint/no-use-before-define */ +import * as React from 'react'; +import Box from '@material-ui/core/Box'; +import Masonry from '@material-ui/lab/Masonry'; +import MasonryItem from '@material-ui/lab/MasonryItem'; + +export default function DiffColSizeMasonry() { + return ( + + {itemData.map((item, index) => ( + + + {index + 1} + + + ))} + + ); +} + +const itemData = [ + { height: 150 }, + { height: 30 }, + { height: 90, span: 2 }, + { height: 70 }, + { height: 150 }, + { height: 120 }, + { height: 100, span: 2 }, + { height: 80, span: 2 }, + { height: 35 }, + { height: 70 }, + { height: 100, span: 2 }, + { height: 157 }, + { height: 50 }, + { height: 50, span: 2 }, + { height: 50 }, +]; diff --git a/docs/src/pages/components/masonry/DiffColSizeMasonryBroken.tsx b/docs/src/pages/components/masonry/DiffColSizeMasonryBroken.tsx new file mode 100644 index 00000000000000..103733da1061d0 --- /dev/null +++ b/docs/src/pages/components/masonry/DiffColSizeMasonryBroken.tsx @@ -0,0 +1,44 @@ +/* eslint-disable @typescript-eslint/no-use-before-define */ +import * as React from 'react'; +import Box from '@material-ui/core/Box'; +import Masonry from '@material-ui/lab/Masonry'; +import MasonryItem from '@material-ui/lab/MasonryItem'; + +export default function DiffColSizeMasonry() { + return ( + + {itemData.map((item, index) => ( + + + {index + 1} + + + ))} + + ); +} + +const itemData = [ + { height: 150 }, + { height: 30 }, + { height: 90, span: 2 }, + { height: 70 }, + { height: 150 }, + { height: 120 }, + { height: 100, span: 2 }, + { height: 80, span: 2 }, + { height: 35 }, + { height: 70 }, + { height: 100, span: 2 }, + { height: 157 }, + { height: 50 }, + { height: 50, span: 2 }, + { height: 50 }, +]; diff --git a/docs/src/pages/components/masonry/masonry.md b/docs/src/pages/components/masonry/masonry.md index f11fff69f16ba4..7a2c0f3dd92146 100644 --- a/docs/src/pages/components/masonry/masonry.md +++ b/docs/src/pages/components/masonry/masonry.md @@ -46,10 +46,14 @@ By passing `spacing` to ``, you can configure the spacing between ``, you can configure the number of columns taken up by each ``. However, you have to choose the value of `columnSpan` for each item carefully so that your masonry does not break. +By passing `columnSpan` to ``, you can configure the number of columns taken up by each ``. {{"demo": "pages/components/masonry/DiffColSizeMasonry.js", "bg": true}} +However, you have to choose the value of `columnSpan` for each item carefully or fine-tune heights of items so that your masonry does not break. + +{{"demo": "pages/components/masonry/DiffColSizeMasonryBroken.js", "bg": true}} + ## Server-side rendering By passing `height` to ``. you can use server-side rendering. You should either set `height: 100%` or a fixed height equivalent to `height` to the content of ``. Otherwise, there will be unwanted gap between `` and the content that you pass to it. diff --git a/packages/material-ui-lab/src/Masonry/Masonry.js b/packages/material-ui-lab/src/Masonry/Masonry.js index 42afd896e54308..d833641ab57aa0 100644 --- a/packages/material-ui-lab/src/Masonry/Masonry.js +++ b/packages/material-ui-lab/src/Masonry/Masonry.js @@ -31,6 +31,7 @@ export const style = ({ styleProps, theme }) => { overflow: 'auto', width: '100%', rowGap: 1, + boxSizing: 'border-box', }; const base = {}; diff --git a/packages/material-ui-lab/src/Masonry/Masonry.test.js b/packages/material-ui-lab/src/Masonry/Masonry.test.js index 5e3b307fa4be5b..550a2335565c41 100644 --- a/packages/material-ui-lab/src/Masonry/Masonry.test.js +++ b/packages/material-ui-lab/src/Masonry/Masonry.test.js @@ -45,6 +45,7 @@ describe('', () => { rowGap: 1, columnGap: theme.spacing(1), gridTemplateColumns: 'repeat(4, 1fr)', + boxSizing: 'border-box', }); }); @@ -76,6 +77,7 @@ describe('', () => { overflow: 'auto', width: '100%', rowGap: 1, + boxSizing: 'border-box', }); }); @@ -105,6 +107,7 @@ describe('', () => { width: '100%', columnGap: theme.spacing(1), rowGap: 1, + boxSizing: 'border-box', }); }); }); diff --git a/packages/material-ui-lab/src/MasonryItem/MasonryItem.js b/packages/material-ui-lab/src/MasonryItem/MasonryItem.js index 7b75b5444b45f8..ebb254b344b9a8 100644 --- a/packages/material-ui-lab/src/MasonryItem/MasonryItem.js +++ b/packages/material-ui-lab/src/MasonryItem/MasonryItem.js @@ -29,9 +29,11 @@ export const style = ({ styleProps, theme }) => { '& > *': { // all contents should have a width of 100% width: '100%', + boxSizing: 'inherit', }, visibility: styleProps.height ? 'visible' : 'hidden', gridColumnEnd: `span ${styleProps.columnSpan}`, + boxSizing: 'inherit', }; const base = {}; diff --git a/packages/material-ui-lab/src/MasonryItem/MasonryItem.test.js b/packages/material-ui-lab/src/MasonryItem/MasonryItem.test.js index 27e28e486dfc9a..164548ba7d4e45 100644 --- a/packages/material-ui-lab/src/MasonryItem/MasonryItem.test.js +++ b/packages/material-ui-lab/src/MasonryItem/MasonryItem.test.js @@ -76,9 +76,11 @@ describe('', () => { width: '100%', [`& > *`]: { width: '100%', + boxSizing: 'inherit', }, visibility: 'visible', gridColumnEnd: 'span 1', + boxSizing: 'inherit', }); }); @@ -98,9 +100,11 @@ describe('', () => { width: '100%', [`& > *`]: { width: '100%', + boxSizing: 'inherit', }, visibility: 'visible', gridColumnEnd: 'span 2', + boxSizing: 'inherit', }); }); From b2fcabd1e77c243c1c3c71d2695d8fa8d18ef18f Mon Sep 17 00:00:00 2001 From: Benny Joo Date: Tue, 3 Aug 2021 09:24:04 +0100 Subject: [PATCH 15/59] Edit demo styles and add regression tests --- .../pages/components/masonry/ImageMasonry.js | 2 +- .../pages/components/masonry/ImageMasonry.tsx | 2 +- .../components/masonry/ResponsiveColumns.js | 2 +- .../components/masonry/ResponsiveColumns.tsx | 2 +- .../components/masonry/ResponsiveSpacing.js | 6 +-- .../components/masonry/ResponsiveSpacing.tsx | 6 +-- .../fixtures/Masonry/BasicMasonry.js | 21 +++++++++ .../fixtures/Masonry/DiffColMasonry.js | 44 +++++++++++++++++++ .../fixtures/Masonry/ResponsiveMasonry.js | 25 +++++++++++ .../fixtures/Masonry/SSRMasonry.js | 20 +++++++++ 10 files changed, 116 insertions(+), 14 deletions(-) create mode 100644 test/regressions/fixtures/Masonry/BasicMasonry.js create mode 100644 test/regressions/fixtures/Masonry/DiffColMasonry.js create mode 100644 test/regressions/fixtures/Masonry/ResponsiveMasonry.js create mode 100644 test/regressions/fixtures/Masonry/SSRMasonry.js diff --git a/docs/src/pages/components/masonry/ImageMasonry.js b/docs/src/pages/components/masonry/ImageMasonry.js index 29e4259075592a..6081d036985424 100644 --- a/docs/src/pages/components/masonry/ImageMasonry.js +++ b/docs/src/pages/components/masonry/ImageMasonry.js @@ -5,7 +5,7 @@ import MasonryItem from '@material-ui/lab/MasonryItem'; export default function ImageMasonry() { return ( - + {itemData.map((item) => ( + {itemData.map((item) => ( + {heights.map((height, index) => ( + {heights.map((height, index) => ( + {heights.map((height, index) => ( + {heights.map((height, index) => ( + {heights.map((height, index) => ( + + + {index + 1} + + + ))} + + ); +} + +const heights = [150, 30, 90, 70, 110, 150, 130, 80, 50, 90, 100, 150, 30, 50, 80]; diff --git a/test/regressions/fixtures/Masonry/DiffColMasonry.js b/test/regressions/fixtures/Masonry/DiffColMasonry.js new file mode 100644 index 00000000000000..f4d216f0f416d6 --- /dev/null +++ b/test/regressions/fixtures/Masonry/DiffColMasonry.js @@ -0,0 +1,44 @@ +/* eslint-disable @typescript-eslint/no-use-before-define */ +import * as React from 'react'; +import Box from '@material-ui/core/Box'; +import Masonry from '@material-ui/lab/Masonry'; +import MasonryItem from '@material-ui/lab/MasonryItem'; + +export default function DiffColSizeMasonry() { + return ( + + {itemData.map((item, index) => ( + + + {index + 1} + + + ))} + + ); +} + +const itemData = [ + { height: 150 }, + { height: 30 }, + { height: 90, span: 2 }, + { height: 110 }, + { height: 150 }, + { height: 150 }, + { height: 130, span: 2 }, + { height: 80, span: 2 }, + { height: 50 }, + { height: 90 }, + { height: 100, span: 2 }, + { height: 150 }, + { height: 50 }, + { height: 50, span: 2 }, + { height: 50 }, +]; diff --git a/test/regressions/fixtures/Masonry/ResponsiveMasonry.js b/test/regressions/fixtures/Masonry/ResponsiveMasonry.js new file mode 100644 index 00000000000000..05d8e8b00338d1 --- /dev/null +++ b/test/regressions/fixtures/Masonry/ResponsiveMasonry.js @@ -0,0 +1,25 @@ +/* eslint-disable @typescript-eslint/no-use-before-define */ +import * as React from 'react'; +import Box from '@material-ui/core/Box'; +import Masonry from '@material-ui/lab/Masonry'; +import MasonryItem from '@material-ui/lab/MasonryItem'; + +export default function ResponsiveColumns() { + return ( + + {heights.map((height, index) => ( + + + {index + 1} + + + ))} + + ); +} + +const heights = [150, 30, 90, 70, 110, 150, 130, 80, 50, 90, 100, 150, 30, 50, 80]; diff --git a/test/regressions/fixtures/Masonry/SSRMasonry.js b/test/regressions/fixtures/Masonry/SSRMasonry.js new file mode 100644 index 00000000000000..4c155733962f6c --- /dev/null +++ b/test/regressions/fixtures/Masonry/SSRMasonry.js @@ -0,0 +1,20 @@ +/* eslint-disable @typescript-eslint/no-use-before-define */ +import * as React from 'react'; +import Box from '@material-ui/core/Box'; +import Masonry from '@material-ui/lab/Masonry'; +import MasonryItem from '@material-ui/lab/MasonryItem'; + +export default function SSRMasonry() { + return ( + + {heights.map((height, index) => ( + + + {index + 1} + + + ))} + + ); +} +const heights = [150, 30, 90, 70, 110, 150, 130, 80, 50, 90, 100, 150, 30, 50, 80]; From 5c851d33e589cf7d07f52a1ade2d65ff0248de47 Mon Sep 17 00:00:00 2001 From: Benny Joo Date: Wed, 4 Aug 2021 14:08:20 +0100 Subject: [PATCH 16/59] Fix lazy-loaded images to load perperly --- docs/src/pages/components/masonry/ImageMasonry.js | 4 ++-- docs/src/pages/components/masonry/ImageMasonry.tsx | 4 ++-- packages/material-ui-lab/src/MasonryItem/MasonryItem.js | 4 +++- test/regressions/fixtures/Masonry/SSRMasonry.js | 2 +- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/docs/src/pages/components/masonry/ImageMasonry.js b/docs/src/pages/components/masonry/ImageMasonry.js index 6081d036985424..801923ae479217 100644 --- a/docs/src/pages/components/masonry/ImageMasonry.js +++ b/docs/src/pages/components/masonry/ImageMasonry.js @@ -9,8 +9,8 @@ export default function ImageMasonry() { {itemData.map((item) => ( {item.title} diff --git a/docs/src/pages/components/masonry/ImageMasonry.tsx b/docs/src/pages/components/masonry/ImageMasonry.tsx index 6081d036985424..801923ae479217 100644 --- a/docs/src/pages/components/masonry/ImageMasonry.tsx +++ b/docs/src/pages/components/masonry/ImageMasonry.tsx @@ -9,8 +9,8 @@ export default function ImageMasonry() { {itemData.map((item) => ( {item.title} diff --git a/packages/material-ui-lab/src/MasonryItem/MasonryItem.js b/packages/material-ui-lab/src/MasonryItem/MasonryItem.js index ebb254b344b9a8..fc8c3205b13446 100644 --- a/packages/material-ui-lab/src/MasonryItem/MasonryItem.js +++ b/packages/material-ui-lab/src/MasonryItem/MasonryItem.js @@ -46,7 +46,9 @@ export const style = ({ styleProps, theme }) => { const transformer = createUnarySpacing(theme); const styleFromPropValue = (propValue) => { const gap = styleProps.height ? Number(getValue(transformer, propValue).replace('px', '')) : 0; - const rowSpan = styleProps.height ? Math.ceil(styleProps.height + gap) : 0; + // for lazy-loaded images to load properly, they should take up space greater than 1px + // taking into account a row gap of 1px, rowSpan should at least be 3 + const rowSpan = styleProps.height ? Math.ceil(styleProps.height + gap) : 3; return { gridRowEnd: `span ${rowSpan}`, paddingBottom: gap - 1, diff --git a/test/regressions/fixtures/Masonry/SSRMasonry.js b/test/regressions/fixtures/Masonry/SSRMasonry.js index 4c155733962f6c..f80db6c32f883f 100644 --- a/test/regressions/fixtures/Masonry/SSRMasonry.js +++ b/test/regressions/fixtures/Masonry/SSRMasonry.js @@ -9,7 +9,7 @@ export default function SSRMasonry() { {heights.map((height, index) => ( - + {index + 1} From 67e66f64dd92641670c6bba116bb8456a3b3fd78 Mon Sep 17 00:00:00 2001 From: Benny Joo Date: Wed, 4 Aug 2021 15:59:50 +0100 Subject: [PATCH 17/59] Make masonry responsive to columnSpan, spacing and height --- .../src/MasonryItem/MasonryItem.js | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/packages/material-ui-lab/src/MasonryItem/MasonryItem.js b/packages/material-ui-lab/src/MasonryItem/MasonryItem.js index fc8c3205b13446..5f99f8d5595c45 100644 --- a/packages/material-ui-lab/src/MasonryItem/MasonryItem.js +++ b/packages/material-ui-lab/src/MasonryItem/MasonryItem.js @@ -89,21 +89,35 @@ const MasonryItem = React.forwardRef(function MasonryItem(inProps, ref) { React.useEffect(() => { // If height is passed by user, ResizeObserver is not used. if (height !== undefined) { + setStyleProps({ + ...styleProps, + spacing, + columnSpan, + height: height < 0 ? 0 : height, + }); + return true; } const resizeObserver = new ResizeObserver(([item]) => { setStyleProps({ ...styleProps, + spacing, + columnSpan, height: item.contentRect.height, }); }); - + const handleError = () => { + const src = masonryItemRef.current.firstChild.src; + masonryItemRef.current.firstChild.src = src; + }; const item = masonryItemRef.current?.firstChild; + item.addEventListener('error', handleError); resizeObserver.observe(item); return () => { resizeObserver.unobserve(item); + item.removeEventListener('error', handleError); }; - }, []); // eslint-disable-line + }, [height, spacing, columnSpan]); // eslint-disable-line const handleRef = useForkRef(ref, masonryItemRef); From 4047d8853f96cfdeec9437327c18e2ee9ca714f8 Mon Sep 17 00:00:00 2001 From: Olivier Tassinari Date: Thu, 5 Aug 2021 19:34:48 +0200 Subject: [PATCH 18/59] header convention --- docs/src/pages/components/masonry/masonry.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/src/pages/components/masonry/masonry.md b/docs/src/pages/components/masonry/masonry.md index 7a2c0f3dd92146..b4c98e60458c82 100644 --- a/docs/src/pages/components/masonry/masonry.md +++ b/docs/src/pages/components/masonry/masonry.md @@ -12,13 +12,13 @@ Masonry maintains a list of content blocks with a consistent width but variable {{"component": "modules/components/ComponentLinkHeader.js", "design": true}} -## Basic Masonry +## Basic masonry `` is a container for one or more ``s. `` can receive any element including `
` and ``. Also, it is important to note that each `` accepts only one element. {{"demo": "pages/components/masonry/BasicMasonry.js", "bg": true}} -## Image Masonry +## Image masonry `` orders its children by row. If you would like to order images by column, you can use ``, whose more details can be found in [Masonry Image List](https://next.material-ui.com/components/image-list/#masonry-image-list). From 22295e6c86e77eae0c403b600f14c318ba977b7c Mon Sep 17 00:00:00 2001 From: Olivier Tassinari Date: Thu, 5 Aug 2021 19:40:41 +0200 Subject: [PATCH 19/59] isolate the dimensions element for simplicity --- .../pages/components/masonry/BasicMasonry.js | 34 ++++++++++--------- .../pages/components/masonry/BasicMasonry.tsx | 34 ++++++++++--------- .../components/masonry/DiffColSizeMasonry.js | 34 ++++++++++--------- .../components/masonry/DiffColSizeMasonry.tsx | 34 ++++++++++--------- .../masonry/DiffColSizeMasonryBroken.js | 34 ++++++++++--------- .../masonry/DiffColSizeMasonryBroken.tsx | 34 ++++++++++--------- .../pages/components/masonry/FixedColumns.js | 34 ++++++++++--------- .../pages/components/masonry/FixedColumns.tsx | 34 ++++++++++--------- .../pages/components/masonry/FixedSpacing.js | 34 ++++++++++--------- .../pages/components/masonry/FixedSpacing.tsx | 34 ++++++++++--------- .../pages/components/masonry/ImageMasonry.js | 25 ++++++++------ .../pages/components/masonry/ImageMasonry.tsx | 25 ++++++++------ .../components/masonry/ResponsiveColumns.js | 34 ++++++++++--------- .../components/masonry/ResponsiveColumns.tsx | 34 ++++++++++--------- .../components/masonry/ResponsiveSpacing.js | 34 ++++++++++--------- .../components/masonry/ResponsiveSpacing.tsx | 34 ++++++++++--------- .../pages/components/masonry/SSRMasonry.js | 34 ++++++++++--------- .../pages/components/masonry/SSRMasonry.tsx | 34 ++++++++++--------- 18 files changed, 316 insertions(+), 278 deletions(-) diff --git a/docs/src/pages/components/masonry/BasicMasonry.js b/docs/src/pages/components/masonry/BasicMasonry.js index aba9af235441ef..ec44d2b9cd67db 100644 --- a/docs/src/pages/components/masonry/BasicMasonry.js +++ b/docs/src/pages/components/masonry/BasicMasonry.js @@ -6,22 +6,24 @@ import MasonryItem from '@material-ui/lab/MasonryItem'; export default function BasicMasonry() { return ( - - {heights.map((height, index) => ( - - - {index + 1} - - - ))} - + + + {heights.map((height, index) => ( + + + {index + 1} + + + ))} + + ); } diff --git a/docs/src/pages/components/masonry/BasicMasonry.tsx b/docs/src/pages/components/masonry/BasicMasonry.tsx index aba9af235441ef..ec44d2b9cd67db 100644 --- a/docs/src/pages/components/masonry/BasicMasonry.tsx +++ b/docs/src/pages/components/masonry/BasicMasonry.tsx @@ -6,22 +6,24 @@ import MasonryItem from '@material-ui/lab/MasonryItem'; export default function BasicMasonry() { return ( - - {heights.map((height, index) => ( - - - {index + 1} - - - ))} - + + + {heights.map((height, index) => ( + + + {index + 1} + + + ))} + + ); } diff --git a/docs/src/pages/components/masonry/DiffColSizeMasonry.js b/docs/src/pages/components/masonry/DiffColSizeMasonry.js index f4d216f0f416d6..a9b4e762b11b4d 100644 --- a/docs/src/pages/components/masonry/DiffColSizeMasonry.js +++ b/docs/src/pages/components/masonry/DiffColSizeMasonry.js @@ -6,22 +6,24 @@ import MasonryItem from '@material-ui/lab/MasonryItem'; export default function DiffColSizeMasonry() { return ( - - {itemData.map((item, index) => ( - - - {index + 1} - - - ))} - + + + {itemData.map((item, index) => ( + + + {index + 1} + + + ))} + + ); } diff --git a/docs/src/pages/components/masonry/DiffColSizeMasonry.tsx b/docs/src/pages/components/masonry/DiffColSizeMasonry.tsx index f4d216f0f416d6..a9b4e762b11b4d 100644 --- a/docs/src/pages/components/masonry/DiffColSizeMasonry.tsx +++ b/docs/src/pages/components/masonry/DiffColSizeMasonry.tsx @@ -6,22 +6,24 @@ import MasonryItem from '@material-ui/lab/MasonryItem'; export default function DiffColSizeMasonry() { return ( - - {itemData.map((item, index) => ( - - - {index + 1} - - - ))} - + + + {itemData.map((item, index) => ( + + + {index + 1} + + + ))} + + ); } diff --git a/docs/src/pages/components/masonry/DiffColSizeMasonryBroken.js b/docs/src/pages/components/masonry/DiffColSizeMasonryBroken.js index 103733da1061d0..960757a344055d 100644 --- a/docs/src/pages/components/masonry/DiffColSizeMasonryBroken.js +++ b/docs/src/pages/components/masonry/DiffColSizeMasonryBroken.js @@ -6,22 +6,24 @@ import MasonryItem from '@material-ui/lab/MasonryItem'; export default function DiffColSizeMasonry() { return ( - - {itemData.map((item, index) => ( - - - {index + 1} - - - ))} - + + + {itemData.map((item, index) => ( + + + {index + 1} + + + ))} + + ); } diff --git a/docs/src/pages/components/masonry/DiffColSizeMasonryBroken.tsx b/docs/src/pages/components/masonry/DiffColSizeMasonryBroken.tsx index 103733da1061d0..960757a344055d 100644 --- a/docs/src/pages/components/masonry/DiffColSizeMasonryBroken.tsx +++ b/docs/src/pages/components/masonry/DiffColSizeMasonryBroken.tsx @@ -6,22 +6,24 @@ import MasonryItem from '@material-ui/lab/MasonryItem'; export default function DiffColSizeMasonry() { return ( - - {itemData.map((item, index) => ( - - - {index + 1} - - - ))} - + + + {itemData.map((item, index) => ( + + + {index + 1} + + + ))} + + ); } diff --git a/docs/src/pages/components/masonry/FixedColumns.js b/docs/src/pages/components/masonry/FixedColumns.js index 584333c97b2a28..4efb166a7fc2fc 100644 --- a/docs/src/pages/components/masonry/FixedColumns.js +++ b/docs/src/pages/components/masonry/FixedColumns.js @@ -6,22 +6,24 @@ import MasonryItem from '@material-ui/lab/MasonryItem'; export default function FixedColumns() { return ( - - {heights.map((height, index) => ( - - - {index + 1} - - - ))} - + + + {heights.map((height, index) => ( + + + {index + 1} + + + ))} + + ); } diff --git a/docs/src/pages/components/masonry/FixedColumns.tsx b/docs/src/pages/components/masonry/FixedColumns.tsx index 584333c97b2a28..4efb166a7fc2fc 100644 --- a/docs/src/pages/components/masonry/FixedColumns.tsx +++ b/docs/src/pages/components/masonry/FixedColumns.tsx @@ -6,22 +6,24 @@ import MasonryItem from '@material-ui/lab/MasonryItem'; export default function FixedColumns() { return ( - - {heights.map((height, index) => ( - - - {index + 1} - - - ))} - + + + {heights.map((height, index) => ( + + + {index + 1} + + + ))} + + ); } diff --git a/docs/src/pages/components/masonry/FixedSpacing.js b/docs/src/pages/components/masonry/FixedSpacing.js index df2c9171c749a9..ae32d7944f2923 100644 --- a/docs/src/pages/components/masonry/FixedSpacing.js +++ b/docs/src/pages/components/masonry/FixedSpacing.js @@ -6,22 +6,24 @@ import MasonryItem from '@material-ui/lab/MasonryItem'; export default function FixedSpacing() { return ( - - {heights.map((height, index) => ( - - - {index + 1} - - - ))} - + + + {heights.map((height, index) => ( + + + {index + 1} + + + ))} + + ); } diff --git a/docs/src/pages/components/masonry/FixedSpacing.tsx b/docs/src/pages/components/masonry/FixedSpacing.tsx index df2c9171c749a9..ae32d7944f2923 100644 --- a/docs/src/pages/components/masonry/FixedSpacing.tsx +++ b/docs/src/pages/components/masonry/FixedSpacing.tsx @@ -6,22 +6,24 @@ import MasonryItem from '@material-ui/lab/MasonryItem'; export default function FixedSpacing() { return ( - - {heights.map((height, index) => ( - - - {index + 1} - - - ))} - + + + {heights.map((height, index) => ( + + + {index + 1} + + + ))} + + ); } diff --git a/docs/src/pages/components/masonry/ImageMasonry.js b/docs/src/pages/components/masonry/ImageMasonry.js index 801923ae479217..8f0c205e45cbd6 100644 --- a/docs/src/pages/components/masonry/ImageMasonry.js +++ b/docs/src/pages/components/masonry/ImageMasonry.js @@ -1,22 +1,25 @@ /* eslint-disable @typescript-eslint/no-use-before-define */ import * as React from 'react'; +import Box from '@material-ui/core/Box'; import Masonry from '@material-ui/lab/Masonry'; import MasonryItem from '@material-ui/lab/MasonryItem'; export default function ImageMasonry() { return ( - - {itemData.map((item) => ( - - + + {itemData.map((item) => ( + + {item.title} - - ))} - + alt={item.title} + loading="lazy" + /> + + ))} + + ); } diff --git a/docs/src/pages/components/masonry/ImageMasonry.tsx b/docs/src/pages/components/masonry/ImageMasonry.tsx index 801923ae479217..8f0c205e45cbd6 100644 --- a/docs/src/pages/components/masonry/ImageMasonry.tsx +++ b/docs/src/pages/components/masonry/ImageMasonry.tsx @@ -1,22 +1,25 @@ /* eslint-disable @typescript-eslint/no-use-before-define */ import * as React from 'react'; +import Box from '@material-ui/core/Box'; import Masonry from '@material-ui/lab/Masonry'; import MasonryItem from '@material-ui/lab/MasonryItem'; export default function ImageMasonry() { return ( - - {itemData.map((item) => ( - - + + {itemData.map((item) => ( + + {item.title} - - ))} - + alt={item.title} + loading="lazy" + /> + + ))} + + ); } diff --git a/docs/src/pages/components/masonry/ResponsiveColumns.js b/docs/src/pages/components/masonry/ResponsiveColumns.js index 74785dcba7a7aa..9bfee19eef5097 100644 --- a/docs/src/pages/components/masonry/ResponsiveColumns.js +++ b/docs/src/pages/components/masonry/ResponsiveColumns.js @@ -6,22 +6,24 @@ import MasonryItem from '@material-ui/lab/MasonryItem'; export default function ResponsiveColumns() { return ( - - {heights.map((height, index) => ( - - - {index + 1} - - - ))} - + + + {heights.map((height, index) => ( + + + {index + 1} + + + ))} + + ); } diff --git a/docs/src/pages/components/masonry/ResponsiveColumns.tsx b/docs/src/pages/components/masonry/ResponsiveColumns.tsx index 74785dcba7a7aa..9bfee19eef5097 100644 --- a/docs/src/pages/components/masonry/ResponsiveColumns.tsx +++ b/docs/src/pages/components/masonry/ResponsiveColumns.tsx @@ -6,22 +6,24 @@ import MasonryItem from '@material-ui/lab/MasonryItem'; export default function ResponsiveColumns() { return ( - - {heights.map((height, index) => ( - - - {index + 1} - - - ))} - + + + {heights.map((height, index) => ( + + + {index + 1} + + + ))} + + ); } diff --git a/docs/src/pages/components/masonry/ResponsiveSpacing.js b/docs/src/pages/components/masonry/ResponsiveSpacing.js index 53086ae8735ecf..07ea8c28bff7d1 100644 --- a/docs/src/pages/components/masonry/ResponsiveSpacing.js +++ b/docs/src/pages/components/masonry/ResponsiveSpacing.js @@ -6,22 +6,24 @@ import MasonryItem from '@material-ui/lab/MasonryItem'; export default function ResponsiveSpacing() { return ( - - {heights.map((height, index) => ( - - - {index + 1} - - - ))} - + + + {heights.map((height, index) => ( + + + {index + 1} + + + ))} + + ); } diff --git a/docs/src/pages/components/masonry/ResponsiveSpacing.tsx b/docs/src/pages/components/masonry/ResponsiveSpacing.tsx index 53086ae8735ecf..07ea8c28bff7d1 100644 --- a/docs/src/pages/components/masonry/ResponsiveSpacing.tsx +++ b/docs/src/pages/components/masonry/ResponsiveSpacing.tsx @@ -6,22 +6,24 @@ import MasonryItem from '@material-ui/lab/MasonryItem'; export default function ResponsiveSpacing() { return ( - - {heights.map((height, index) => ( - - - {index + 1} - - - ))} - + + + {heights.map((height, index) => ( + + + {index + 1} + + + ))} + + ); } diff --git a/docs/src/pages/components/masonry/SSRMasonry.js b/docs/src/pages/components/masonry/SSRMasonry.js index 450d2d817355ca..81794e48b88d5c 100644 --- a/docs/src/pages/components/masonry/SSRMasonry.js +++ b/docs/src/pages/components/masonry/SSRMasonry.js @@ -6,22 +6,24 @@ import MasonryItem from '@material-ui/lab/MasonryItem'; export default function SSRMasonry() { return ( - - {heights.map((height, index) => ( - - - {index + 1} - - - ))} - + + + {heights.map((height, index) => ( + + + {index + 1} + + + ))} + + ); } const heights = [150, 30, 90, 70, 110, 150, 130, 80, 50, 90, 100, 150, 30, 50, 80]; diff --git a/docs/src/pages/components/masonry/SSRMasonry.tsx b/docs/src/pages/components/masonry/SSRMasonry.tsx index 450d2d817355ca..81794e48b88d5c 100644 --- a/docs/src/pages/components/masonry/SSRMasonry.tsx +++ b/docs/src/pages/components/masonry/SSRMasonry.tsx @@ -6,22 +6,24 @@ import MasonryItem from '@material-ui/lab/MasonryItem'; export default function SSRMasonry() { return ( - - {heights.map((height, index) => ( - - - {index + 1} - - - ))} - + + + {heights.map((height, index) => ( + + + {index + 1} + + + ))} + + ); } const heights = [150, 30, 90, 70, 110, 150, 130, 80, 50, 90, 100, 150, 30, 50, 80]; From d476925197c200cbec7c97ca5f96b4b5f29ff109 Mon Sep 17 00:00:00 2001 From: Olivier Tassinari Date: Thu, 5 Aug 2021 19:43:21 +0200 Subject: [PATCH 20/59] prefer one sentence per line to ease review --- docs/src/pages/components/masonry/masonry.md | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/docs/src/pages/components/masonry/masonry.md b/docs/src/pages/components/masonry/masonry.md index b4c98e60458c82..6c20b5a4eeebf0 100644 --- a/docs/src/pages/components/masonry/masonry.md +++ b/docs/src/pages/components/masonry/masonry.md @@ -8,7 +8,9 @@ githubLabel: 'component: Masonry'

Masonry lays out contents of different sizes as blocks of the same width and variable height with configurable gaps.

-Masonry maintains a list of content blocks with a consistent width but variable height. The contents are ordered by row. If a row is already filled with the specified number of columns, the next item starts another row, and it is added to the shortest column. +Masonry maintains a list of content blocks with a consistent width but variable height. +The contents are ordered by row. +If a row is already filled with the specified number of columns, the next item starts another row, and it is added to the shortest column. {{"component": "modules/components/ComponentLinkHeader.js", "design": true}} @@ -20,7 +22,8 @@ Masonry maintains a list of content blocks with a consistent width but variable ## Image masonry -`` orders its children by row. If you would like to order images by column, you can use ``, whose more details can be found in [Masonry Image List](https://next.material-ui.com/components/image-list/#masonry-image-list). +`` orders its children by row. +If you would like to order images by column, you can use ``, whose more details can be found in [Masonry Image List](https://next.material-ui.com/components/image-list/#masonry-image-list). {{"demo": "pages/components/masonry/ImageMasonry.js", "bg": true}} @@ -36,7 +39,8 @@ By passing `columns` to ``, you can configure the number of columns o ## Spacing -By passing `spacing` to ``, you can configure the spacing between ``s. It is important to note that `spacing` is a factor of the theme's spacing. +By passing `spacing` to ``, you can configure the spacing between ``s. +It is important to note that `spacing` is a factor of the theme's spacing. {{"demo": "pages/components/masonry/FixedSpacing.js", "bg": true}} @@ -56,6 +60,8 @@ However, you have to choose the value of `columnSpan` for each item carefully or ## Server-side rendering -By passing `height` to ``. you can use server-side rendering. You should either set `height: 100%` or a fixed height equivalent to `height` to the content of ``. Otherwise, there will be unwanted gap between `` and the content that you pass to it. +By passing `height` to ``, you can use server-side rendering. +You should either set `height: 100%` or a fixed height equivalent to `height` to the content of ``. +Otherwise, there will be unwanted gap between `` and the content that you pass to it. {{"demo": "pages/components/masonry/SSRMasonry.js", "bg": true}} From e1ec597f22cc26be8e3d1c702bcb16ab74e9d423 Mon Sep 17 00:00:00 2001 From: Olivier Tassinari Date: Thu, 5 Aug 2021 19:43:47 +0200 Subject: [PATCH 21/59] use relative links --- docs/src/pages/components/masonry/masonry.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/pages/components/masonry/masonry.md b/docs/src/pages/components/masonry/masonry.md index 6c20b5a4eeebf0..da43df72213bcb 100644 --- a/docs/src/pages/components/masonry/masonry.md +++ b/docs/src/pages/components/masonry/masonry.md @@ -23,7 +23,7 @@ If a row is already filled with the specified number of columns, the next item s ## Image masonry `` orders its children by row. -If you would like to order images by column, you can use ``, whose more details can be found in [Masonry Image List](https://next.material-ui.com/components/image-list/#masonry-image-list). +If you would like to order images by column, you can use ``, whose more details can be found in [Masonry Image List](/components/image-list/#masonry-image-list). {{"demo": "pages/components/masonry/ImageMasonry.js", "bg": true}} From 519d3d1ab8eaef628e5ed8e800f2bc97a4302bbf Mon Sep 17 00:00:00 2001 From: Olivier Tassinari Date: Thu, 5 Aug 2021 19:47:41 +0200 Subject: [PATCH 22/59] use demos as simple as possible --- docs/src/pages/components/masonry/FixedColumns.js | 2 +- docs/src/pages/components/masonry/FixedColumns.tsx | 2 +- docs/src/pages/components/masonry/FixedSpacing.js | 2 +- docs/src/pages/components/masonry/FixedSpacing.tsx | 2 +- docs/src/pages/components/masonry/ResponsiveColumns.js | 2 +- docs/src/pages/components/masonry/ResponsiveColumns.tsx | 2 +- docs/src/pages/components/masonry/ResponsiveSpacing.js | 2 +- docs/src/pages/components/masonry/ResponsiveSpacing.tsx | 2 +- docs/src/pages/components/masonry/SSRMasonry.js | 3 ++- docs/src/pages/components/masonry/SSRMasonry.tsx | 3 ++- 10 files changed, 12 insertions(+), 10 deletions(-) diff --git a/docs/src/pages/components/masonry/FixedColumns.js b/docs/src/pages/components/masonry/FixedColumns.js index 4efb166a7fc2fc..487e69ae559c5c 100644 --- a/docs/src/pages/components/masonry/FixedColumns.js +++ b/docs/src/pages/components/masonry/FixedColumns.js @@ -27,4 +27,4 @@ export default function FixedColumns() { ); } -const heights = [150, 30, 90, 70, 110, 150, 130, 80, 50, 90, 100, 150, 30, 50, 80]; +const heights = [150, 30, 90, 70, 90, 100, 150, 30, 50, 80]; diff --git a/docs/src/pages/components/masonry/FixedColumns.tsx b/docs/src/pages/components/masonry/FixedColumns.tsx index 4efb166a7fc2fc..487e69ae559c5c 100644 --- a/docs/src/pages/components/masonry/FixedColumns.tsx +++ b/docs/src/pages/components/masonry/FixedColumns.tsx @@ -27,4 +27,4 @@ export default function FixedColumns() { ); } -const heights = [150, 30, 90, 70, 110, 150, 130, 80, 50, 90, 100, 150, 30, 50, 80]; +const heights = [150, 30, 90, 70, 90, 100, 150, 30, 50, 80]; diff --git a/docs/src/pages/components/masonry/FixedSpacing.js b/docs/src/pages/components/masonry/FixedSpacing.js index ae32d7944f2923..fbc4d33cc402f8 100644 --- a/docs/src/pages/components/masonry/FixedSpacing.js +++ b/docs/src/pages/components/masonry/FixedSpacing.js @@ -27,4 +27,4 @@ export default function FixedSpacing() { ); } -const heights = [150, 30, 90, 70, 110, 150, 130, 80, 50, 90, 100, 150, 30, 50, 80]; +const heights = [150, 30, 90, 70, 90, 100, 150, 30, 50, 80]; diff --git a/docs/src/pages/components/masonry/FixedSpacing.tsx b/docs/src/pages/components/masonry/FixedSpacing.tsx index ae32d7944f2923..fbc4d33cc402f8 100644 --- a/docs/src/pages/components/masonry/FixedSpacing.tsx +++ b/docs/src/pages/components/masonry/FixedSpacing.tsx @@ -27,4 +27,4 @@ export default function FixedSpacing() { ); } -const heights = [150, 30, 90, 70, 110, 150, 130, 80, 50, 90, 100, 150, 30, 50, 80]; +const heights = [150, 30, 90, 70, 90, 100, 150, 30, 50, 80]; diff --git a/docs/src/pages/components/masonry/ResponsiveColumns.js b/docs/src/pages/components/masonry/ResponsiveColumns.js index 9bfee19eef5097..160df13e965da9 100644 --- a/docs/src/pages/components/masonry/ResponsiveColumns.js +++ b/docs/src/pages/components/masonry/ResponsiveColumns.js @@ -27,4 +27,4 @@ export default function ResponsiveColumns() { ); } -const heights = [150, 30, 90, 70, 110, 150, 130, 80, 50, 90, 100, 150, 30, 50, 80]; +const heights = [150, 30, 90, 70, 90, 100, 150, 30, 50, 80]; diff --git a/docs/src/pages/components/masonry/ResponsiveColumns.tsx b/docs/src/pages/components/masonry/ResponsiveColumns.tsx index 9bfee19eef5097..160df13e965da9 100644 --- a/docs/src/pages/components/masonry/ResponsiveColumns.tsx +++ b/docs/src/pages/components/masonry/ResponsiveColumns.tsx @@ -27,4 +27,4 @@ export default function ResponsiveColumns() { ); } -const heights = [150, 30, 90, 70, 110, 150, 130, 80, 50, 90, 100, 150, 30, 50, 80]; +const heights = [150, 30, 90, 70, 90, 100, 150, 30, 50, 80]; diff --git a/docs/src/pages/components/masonry/ResponsiveSpacing.js b/docs/src/pages/components/masonry/ResponsiveSpacing.js index 07ea8c28bff7d1..71b84584c7e0b1 100644 --- a/docs/src/pages/components/masonry/ResponsiveSpacing.js +++ b/docs/src/pages/components/masonry/ResponsiveSpacing.js @@ -27,4 +27,4 @@ export default function ResponsiveSpacing() { ); } -const heights = [150, 30, 90, 70, 110, 150, 130, 80, 50, 90, 100, 150, 30, 50, 80]; +const heights = [150, 30, 90, 70, 90, 100, 150, 30, 50, 80]; diff --git a/docs/src/pages/components/masonry/ResponsiveSpacing.tsx b/docs/src/pages/components/masonry/ResponsiveSpacing.tsx index 07ea8c28bff7d1..71b84584c7e0b1 100644 --- a/docs/src/pages/components/masonry/ResponsiveSpacing.tsx +++ b/docs/src/pages/components/masonry/ResponsiveSpacing.tsx @@ -27,4 +27,4 @@ export default function ResponsiveSpacing() { ); } -const heights = [150, 30, 90, 70, 110, 150, 130, 80, 50, 90, 100, 150, 30, 50, 80]; +const heights = [150, 30, 90, 70, 90, 100, 150, 30, 50, 80]; diff --git a/docs/src/pages/components/masonry/SSRMasonry.js b/docs/src/pages/components/masonry/SSRMasonry.js index 81794e48b88d5c..3888d9abd7cab7 100644 --- a/docs/src/pages/components/masonry/SSRMasonry.js +++ b/docs/src/pages/components/masonry/SSRMasonry.js @@ -26,4 +26,5 @@ export default function SSRMasonry() { ); } -const heights = [150, 30, 90, 70, 110, 150, 130, 80, 50, 90, 100, 150, 30, 50, 80]; + +const heights = [150, 30, 90, 70, 90, 100, 150, 30, 50, 80]; diff --git a/docs/src/pages/components/masonry/SSRMasonry.tsx b/docs/src/pages/components/masonry/SSRMasonry.tsx index 81794e48b88d5c..3888d9abd7cab7 100644 --- a/docs/src/pages/components/masonry/SSRMasonry.tsx +++ b/docs/src/pages/components/masonry/SSRMasonry.tsx @@ -26,4 +26,5 @@ export default function SSRMasonry() { ); } -const heights = [150, 30, 90, 70, 110, 150, 130, 80, 50, 90, 100, 150, 30, 50, 80]; + +const heights = [150, 30, 90, 70, 90, 100, 150, 30, 50, 80]; From 6126e39969488d274bb55bccda76c6b4439fca72 Mon Sep 17 00:00:00 2001 From: Olivier Tassinari Date: Thu, 5 Aug 2021 19:52:56 +0200 Subject: [PATCH 23/59] fix SSR layout shift --- docs/src/pages/components/masonry/BasicMasonry.js | 2 +- docs/src/pages/components/masonry/BasicMasonry.tsx | 2 +- docs/src/pages/components/masonry/DiffColSizeMasonry.js | 2 +- docs/src/pages/components/masonry/DiffColSizeMasonry.tsx | 2 +- docs/src/pages/components/masonry/DiffColSizeMasonryBroken.js | 2 +- docs/src/pages/components/masonry/DiffColSizeMasonryBroken.tsx | 2 +- docs/src/pages/components/masonry/FixedColumns.js | 2 +- docs/src/pages/components/masonry/FixedColumns.tsx | 2 +- docs/src/pages/components/masonry/FixedSpacing.js | 2 +- docs/src/pages/components/masonry/FixedSpacing.tsx | 2 +- docs/src/pages/components/masonry/ImageMasonry.js | 2 +- docs/src/pages/components/masonry/ImageMasonry.tsx | 2 +- docs/src/pages/components/masonry/ResponsiveColumns.js | 2 +- docs/src/pages/components/masonry/ResponsiveColumns.tsx | 2 +- docs/src/pages/components/masonry/ResponsiveSpacing.js | 2 +- docs/src/pages/components/masonry/ResponsiveSpacing.tsx | 2 +- docs/src/pages/components/masonry/SSRMasonry.js | 2 +- docs/src/pages/components/masonry/SSRMasonry.tsx | 2 +- 18 files changed, 18 insertions(+), 18 deletions(-) diff --git a/docs/src/pages/components/masonry/BasicMasonry.js b/docs/src/pages/components/masonry/BasicMasonry.js index ec44d2b9cd67db..525bc4a19dacb7 100644 --- a/docs/src/pages/components/masonry/BasicMasonry.js +++ b/docs/src/pages/components/masonry/BasicMasonry.js @@ -6,7 +6,7 @@ import MasonryItem from '@material-ui/lab/MasonryItem'; export default function BasicMasonry() { return ( - + {heights.map((height, index) => ( diff --git a/docs/src/pages/components/masonry/BasicMasonry.tsx b/docs/src/pages/components/masonry/BasicMasonry.tsx index ec44d2b9cd67db..525bc4a19dacb7 100644 --- a/docs/src/pages/components/masonry/BasicMasonry.tsx +++ b/docs/src/pages/components/masonry/BasicMasonry.tsx @@ -6,7 +6,7 @@ import MasonryItem from '@material-ui/lab/MasonryItem'; export default function BasicMasonry() { return ( - + {heights.map((height, index) => ( diff --git a/docs/src/pages/components/masonry/DiffColSizeMasonry.js b/docs/src/pages/components/masonry/DiffColSizeMasonry.js index a9b4e762b11b4d..de587bab4be47d 100644 --- a/docs/src/pages/components/masonry/DiffColSizeMasonry.js +++ b/docs/src/pages/components/masonry/DiffColSizeMasonry.js @@ -6,7 +6,7 @@ import MasonryItem from '@material-ui/lab/MasonryItem'; export default function DiffColSizeMasonry() { return ( - + {itemData.map((item, index) => ( diff --git a/docs/src/pages/components/masonry/DiffColSizeMasonry.tsx b/docs/src/pages/components/masonry/DiffColSizeMasonry.tsx index a9b4e762b11b4d..de587bab4be47d 100644 --- a/docs/src/pages/components/masonry/DiffColSizeMasonry.tsx +++ b/docs/src/pages/components/masonry/DiffColSizeMasonry.tsx @@ -6,7 +6,7 @@ import MasonryItem from '@material-ui/lab/MasonryItem'; export default function DiffColSizeMasonry() { return ( - + {itemData.map((item, index) => ( diff --git a/docs/src/pages/components/masonry/DiffColSizeMasonryBroken.js b/docs/src/pages/components/masonry/DiffColSizeMasonryBroken.js index 960757a344055d..430e30bb3cbbd5 100644 --- a/docs/src/pages/components/masonry/DiffColSizeMasonryBroken.js +++ b/docs/src/pages/components/masonry/DiffColSizeMasonryBroken.js @@ -6,7 +6,7 @@ import MasonryItem from '@material-ui/lab/MasonryItem'; export default function DiffColSizeMasonry() { return ( - + {itemData.map((item, index) => ( diff --git a/docs/src/pages/components/masonry/DiffColSizeMasonryBroken.tsx b/docs/src/pages/components/masonry/DiffColSizeMasonryBroken.tsx index 960757a344055d..430e30bb3cbbd5 100644 --- a/docs/src/pages/components/masonry/DiffColSizeMasonryBroken.tsx +++ b/docs/src/pages/components/masonry/DiffColSizeMasonryBroken.tsx @@ -6,7 +6,7 @@ import MasonryItem from '@material-ui/lab/MasonryItem'; export default function DiffColSizeMasonry() { return ( - + {itemData.map((item, index) => ( diff --git a/docs/src/pages/components/masonry/FixedColumns.js b/docs/src/pages/components/masonry/FixedColumns.js index 487e69ae559c5c..bca583bc53f288 100644 --- a/docs/src/pages/components/masonry/FixedColumns.js +++ b/docs/src/pages/components/masonry/FixedColumns.js @@ -6,7 +6,7 @@ import MasonryItem from '@material-ui/lab/MasonryItem'; export default function FixedColumns() { return ( - + {heights.map((height, index) => ( diff --git a/docs/src/pages/components/masonry/FixedColumns.tsx b/docs/src/pages/components/masonry/FixedColumns.tsx index 487e69ae559c5c..bca583bc53f288 100644 --- a/docs/src/pages/components/masonry/FixedColumns.tsx +++ b/docs/src/pages/components/masonry/FixedColumns.tsx @@ -6,7 +6,7 @@ import MasonryItem from '@material-ui/lab/MasonryItem'; export default function FixedColumns() { return ( - + {heights.map((height, index) => ( diff --git a/docs/src/pages/components/masonry/FixedSpacing.js b/docs/src/pages/components/masonry/FixedSpacing.js index fbc4d33cc402f8..3976075842e3c6 100644 --- a/docs/src/pages/components/masonry/FixedSpacing.js +++ b/docs/src/pages/components/masonry/FixedSpacing.js @@ -6,7 +6,7 @@ import MasonryItem from '@material-ui/lab/MasonryItem'; export default function FixedSpacing() { return ( - + {heights.map((height, index) => ( diff --git a/docs/src/pages/components/masonry/FixedSpacing.tsx b/docs/src/pages/components/masonry/FixedSpacing.tsx index fbc4d33cc402f8..3976075842e3c6 100644 --- a/docs/src/pages/components/masonry/FixedSpacing.tsx +++ b/docs/src/pages/components/masonry/FixedSpacing.tsx @@ -6,7 +6,7 @@ import MasonryItem from '@material-ui/lab/MasonryItem'; export default function FixedSpacing() { return ( - + {heights.map((height, index) => ( diff --git a/docs/src/pages/components/masonry/ImageMasonry.js b/docs/src/pages/components/masonry/ImageMasonry.js index 8f0c205e45cbd6..ee105174a3f5d6 100644 --- a/docs/src/pages/components/masonry/ImageMasonry.js +++ b/docs/src/pages/components/masonry/ImageMasonry.js @@ -6,7 +6,7 @@ import MasonryItem from '@material-ui/lab/MasonryItem'; export default function ImageMasonry() { return ( - + {itemData.map((item) => ( diff --git a/docs/src/pages/components/masonry/ImageMasonry.tsx b/docs/src/pages/components/masonry/ImageMasonry.tsx index 8f0c205e45cbd6..ee105174a3f5d6 100644 --- a/docs/src/pages/components/masonry/ImageMasonry.tsx +++ b/docs/src/pages/components/masonry/ImageMasonry.tsx @@ -6,7 +6,7 @@ import MasonryItem from '@material-ui/lab/MasonryItem'; export default function ImageMasonry() { return ( - + {itemData.map((item) => ( diff --git a/docs/src/pages/components/masonry/ResponsiveColumns.js b/docs/src/pages/components/masonry/ResponsiveColumns.js index 160df13e965da9..e92bbd2998e15f 100644 --- a/docs/src/pages/components/masonry/ResponsiveColumns.js +++ b/docs/src/pages/components/masonry/ResponsiveColumns.js @@ -6,7 +6,7 @@ import MasonryItem from '@material-ui/lab/MasonryItem'; export default function ResponsiveColumns() { return ( - + {heights.map((height, index) => ( diff --git a/docs/src/pages/components/masonry/ResponsiveColumns.tsx b/docs/src/pages/components/masonry/ResponsiveColumns.tsx index 160df13e965da9..e92bbd2998e15f 100644 --- a/docs/src/pages/components/masonry/ResponsiveColumns.tsx +++ b/docs/src/pages/components/masonry/ResponsiveColumns.tsx @@ -6,7 +6,7 @@ import MasonryItem from '@material-ui/lab/MasonryItem'; export default function ResponsiveColumns() { return ( - + {heights.map((height, index) => ( diff --git a/docs/src/pages/components/masonry/ResponsiveSpacing.js b/docs/src/pages/components/masonry/ResponsiveSpacing.js index 71b84584c7e0b1..9d5e0a156b2691 100644 --- a/docs/src/pages/components/masonry/ResponsiveSpacing.js +++ b/docs/src/pages/components/masonry/ResponsiveSpacing.js @@ -6,7 +6,7 @@ import MasonryItem from '@material-ui/lab/MasonryItem'; export default function ResponsiveSpacing() { return ( - + {heights.map((height, index) => ( diff --git a/docs/src/pages/components/masonry/ResponsiveSpacing.tsx b/docs/src/pages/components/masonry/ResponsiveSpacing.tsx index 71b84584c7e0b1..9d5e0a156b2691 100644 --- a/docs/src/pages/components/masonry/ResponsiveSpacing.tsx +++ b/docs/src/pages/components/masonry/ResponsiveSpacing.tsx @@ -6,7 +6,7 @@ import MasonryItem from '@material-ui/lab/MasonryItem'; export default function ResponsiveSpacing() { return ( - + {heights.map((height, index) => ( diff --git a/docs/src/pages/components/masonry/SSRMasonry.js b/docs/src/pages/components/masonry/SSRMasonry.js index 3888d9abd7cab7..beb7fd5da75d21 100644 --- a/docs/src/pages/components/masonry/SSRMasonry.js +++ b/docs/src/pages/components/masonry/SSRMasonry.js @@ -6,7 +6,7 @@ import MasonryItem from '@material-ui/lab/MasonryItem'; export default function SSRMasonry() { return ( - + {heights.map((height, index) => ( diff --git a/docs/src/pages/components/masonry/SSRMasonry.tsx b/docs/src/pages/components/masonry/SSRMasonry.tsx index 3888d9abd7cab7..beb7fd5da75d21 100644 --- a/docs/src/pages/components/masonry/SSRMasonry.tsx +++ b/docs/src/pages/components/masonry/SSRMasonry.tsx @@ -6,7 +6,7 @@ import MasonryItem from '@material-ui/lab/MasonryItem'; export default function SSRMasonry() { return ( - + {heights.map((height, index) => ( From 7b3475ef00e8c310c4a89e21bdd8cda7b915d5b2 Mon Sep 17 00:00:00 2001 From: Olivier Tassinari Date: Thu, 5 Aug 2021 20:00:55 +0200 Subject: [PATCH 24/59] demo should match filename --- docs/src/pages/components/masonry/DiffColSizeMasonryBroken.js | 2 +- docs/src/pages/components/masonry/DiffColSizeMasonryBroken.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/src/pages/components/masonry/DiffColSizeMasonryBroken.js b/docs/src/pages/components/masonry/DiffColSizeMasonryBroken.js index 430e30bb3cbbd5..628cdfb39e4f60 100644 --- a/docs/src/pages/components/masonry/DiffColSizeMasonryBroken.js +++ b/docs/src/pages/components/masonry/DiffColSizeMasonryBroken.js @@ -4,7 +4,7 @@ import Box from '@material-ui/core/Box'; import Masonry from '@material-ui/lab/Masonry'; import MasonryItem from '@material-ui/lab/MasonryItem'; -export default function DiffColSizeMasonry() { +export default function DiffColSizeMasonryBroken() { return ( diff --git a/docs/src/pages/components/masonry/DiffColSizeMasonryBroken.tsx b/docs/src/pages/components/masonry/DiffColSizeMasonryBroken.tsx index 430e30bb3cbbd5..628cdfb39e4f60 100644 --- a/docs/src/pages/components/masonry/DiffColSizeMasonryBroken.tsx +++ b/docs/src/pages/components/masonry/DiffColSizeMasonryBroken.tsx @@ -4,7 +4,7 @@ import Box from '@material-ui/core/Box'; import Masonry from '@material-ui/lab/Masonry'; import MasonryItem from '@material-ui/lab/MasonryItem'; -export default function DiffColSizeMasonry() { +export default function DiffColSizeMasonryBroken() { return ( From b96b2e976073f910b69d7af53a0dd4352b71ae1b Mon Sep 17 00:00:00 2001 From: Benny Joo Date: Fri, 6 Aug 2021 09:59:36 +0100 Subject: [PATCH 25/59] Refactor MasonryItem --- docs/src/pages/components/masonry/masonry.md | 2 +- .../api-docs/masonry-item/masonry-item.json | 2 +- .../material-ui-lab/src/Masonry/Masonry.js | 4 +- .../src/MasonryItem/MasonryItem.d.ts | 2 +- .../src/MasonryItem/MasonryItem.js | 57 +++++++++++-------- 5 files changed, 40 insertions(+), 27 deletions(-) diff --git a/docs/src/pages/components/masonry/masonry.md b/docs/src/pages/components/masonry/masonry.md index da43df72213bcb..2b1b44b64702b5 100644 --- a/docs/src/pages/components/masonry/masonry.md +++ b/docs/src/pages/components/masonry/masonry.md @@ -23,7 +23,7 @@ If a row is already filled with the specified number of columns, the next item s ## Image masonry `` orders its children by row. -If you would like to order images by column, you can use ``, whose more details can be found in [Masonry Image List](/components/image-list/#masonry-image-list). +If you would like to order images by column, you can use ``. More details on this component can be found in [Masonry Image List](/components/image-list/#masonry-image-list). {{"demo": "pages/components/masonry/ImageMasonry.js", "bg": true}} diff --git a/docs/translations/api-docs/masonry-item/masonry-item.json b/docs/translations/api-docs/masonry-item/masonry-item.json index 0f70ca91820611..5f337a77397546 100644 --- a/docs/translations/api-docs/masonry-item/masonry-item.json +++ b/docs/translations/api-docs/masonry-item/masonry-item.json @@ -5,7 +5,7 @@ "classes": "Override or extend the styles applied to the component. See CSS API below for more details.", "columnSpan": "The number of columns taken up by the component", "component": "The component used for the root node. Either a string to use a HTML element or a component.", - "height": "The height of the component in px.", + "height": "The height of the component in px. This is provided for server-side rendering.", "sx": "Allows defining system overrides as well as additional CSS styles. See the `sx` page for more details." }, "classDescriptions": { "root": { "description": "Styles applied to the root element." } } diff --git a/packages/material-ui-lab/src/Masonry/Masonry.js b/packages/material-ui-lab/src/Masonry/Masonry.js index d833641ab57aa0..bfbda868ef768d 100644 --- a/packages/material-ui-lab/src/Masonry/Masonry.js +++ b/packages/material-ui-lab/src/Masonry/Masonry.js @@ -84,8 +84,10 @@ const Masonry = React.forwardRef(function Masonry(inProps, ref) { const styleProps = { ...props, spacing, columns }; const classes = useUtilityClasses(styleProps); + const contextValue = React.useMemo(() => ({ spacing }), [spacing]); + return ( - + */ classes?: Partial; /** - * The height of the component in px. + * The height of the component in px. This is provided for server-side rendering. */ height?: number; /** diff --git a/packages/material-ui-lab/src/MasonryItem/MasonryItem.js b/packages/material-ui-lab/src/MasonryItem/MasonryItem.js index 5f99f8d5595c45..28c0cdd754f100 100644 --- a/packages/material-ui-lab/src/MasonryItem/MasonryItem.js +++ b/packages/material-ui-lab/src/MasonryItem/MasonryItem.js @@ -85,42 +85,53 @@ const MasonryItem = React.forwardRef(function MasonryItem(inProps, ref) { }); const classes = useUtilityClasses(styleProps); - + const resizeObserver = React.useRef(null); React.useEffect(() => { - // If height is passed by user, ResizeObserver is not used. + // do not create a resize observer in case of SSR masonry if (height !== undefined) { - setStyleProps({ - ...styleProps, - spacing, - columnSpan, - height: height < 0 ? 0 : height, - }); - return true; } - const resizeObserver = new ResizeObserver(([item]) => { - setStyleProps({ - ...styleProps, - spacing, - columnSpan, - height: item.contentRect.height, - }); - }); const handleError = () => { const src = masonryItemRef.current.firstChild.src; masonryItemRef.current.firstChild.src = src; }; - const item = masonryItemRef.current?.firstChild; + resizeObserver.current = new ResizeObserver(([item]) => { + if (styleProps.height !== item.contentRect.height) { + setStyleProps({ + ...styleProps, + height: item.contentRect.height, + }); + } + }); + const item = masonryItemRef.current.firstChild; + const observer = resizeObserver.current; item.addEventListener('error', handleError); - resizeObserver.observe(item); + observer.observe(item); return () => { - resizeObserver.unobserve(item); + observer.unobserve(item); item.removeEventListener('error', handleError); }; - }, [height, spacing, columnSpan]); // eslint-disable-line + }, [height, styleProps]); - const handleRef = useForkRef(ref, masonryItemRef); + React.useEffect(() => { + // handle responsiveness to `height` prop in case of SSR masonry + if (height !== undefined && height !== styleProps.height) { + setStyleProps({ + ...styleProps, + height: height < 0 ? 0 : height, + }); + } + // re-style if `spacing` updates + if (spacing !== styleProps.spacing) { + setStyleProps({ ...styleProps, spacing }); + } + // re-style if `columnSpan` updates + if (columnSpan !== styleProps.columnSpan) { + setStyleProps({ ...styleProps, columnSpan }); + } + }, [height, spacing, columnSpan, styleProps]); + const handleRef = useForkRef(ref, masonryItemRef); return ( Date: Fri, 6 Aug 2021 20:49:18 +0100 Subject: [PATCH 26/59] SSR works without setting height to content of MasonryItem --- docs/src/pages/components/masonry/SSRMasonry.js | 1 - docs/src/pages/components/masonry/SSRMasonry.tsx | 1 - docs/src/pages/components/masonry/masonry.md | 4 +--- .../src/MasonryItem/MasonryItem.js | 15 +++++++++------ 4 files changed, 10 insertions(+), 11 deletions(-) diff --git a/docs/src/pages/components/masonry/SSRMasonry.js b/docs/src/pages/components/masonry/SSRMasonry.js index beb7fd5da75d21..08219297dd8fb6 100644 --- a/docs/src/pages/components/masonry/SSRMasonry.js +++ b/docs/src/pages/components/masonry/SSRMasonry.js @@ -13,7 +13,6 @@ export default function SSRMasonry() { `, you can use server-side rendering. -You should either set `height: 100%` or a fixed height equivalent to `height` to the content of ``. -Otherwise, there will be unwanted gap between `` and the content that you pass to it. +By passing `height` to ``, you can use server-side rendering. By default, `height: 100%` will be set to the content of ``. If you change this, there can be unwanted gap between `` and the content that you pass to it. {{"demo": "pages/components/masonry/SSRMasonry.js", "bg": true}} diff --git a/packages/material-ui-lab/src/MasonryItem/MasonryItem.js b/packages/material-ui-lab/src/MasonryItem/MasonryItem.js index 28c0cdd754f100..2716c0d5c98507 100644 --- a/packages/material-ui-lab/src/MasonryItem/MasonryItem.js +++ b/packages/material-ui-lab/src/MasonryItem/MasonryItem.js @@ -30,6 +30,7 @@ export const style = ({ styleProps, theme }) => { // all contents should have a width of 100% width: '100%', boxSizing: 'inherit', + ...(styleProps.isSSR && { height: '100%' }), }, visibility: styleProps.height ? 'visible' : 'hidden', gridColumnEnd: `span ${styleProps.columnSpan}`, @@ -46,8 +47,8 @@ export const style = ({ styleProps, theme }) => { const transformer = createUnarySpacing(theme); const styleFromPropValue = (propValue) => { const gap = styleProps.height ? Number(getValue(transformer, propValue).replace('px', '')) : 0; - // for lazy-loaded images to load properly, they should take up space greater than 1px - // taking into account a row gap of 1px, rowSpan should at least be 3 + // For lazy-loaded images to load properly, masonry item should take up space greater than 1px. + // Taking into account a row gap of 1px, rowSpan should at least be 3. const rowSpan = styleProps.height ? Math.ceil(styleProps.height + gap) : 3; return { gridRowEnd: `span ${rowSpan}`, @@ -77,8 +78,10 @@ const MasonryItem = React.forwardRef(function MasonryItem(inProps, ref) { const { spacing = 1 } = React.useContext(MasonryContext); const { children, className, component = 'div', columnSpan = 1, height, ...other } = props; + const isSSR = height !== undefined; const [styleProps, setStyleProps] = React.useState({ ...props, + isSSR, spacing, columnSpan, height: height < 0 ? 0 : height, // MasonryItems to which negative or zero height is passed will be hidden @@ -88,7 +91,7 @@ const MasonryItem = React.forwardRef(function MasonryItem(inProps, ref) { const resizeObserver = React.useRef(null); React.useEffect(() => { // do not create a resize observer in case of SSR masonry - if (height !== undefined) { + if (isSSR) { return true; } const handleError = () => { @@ -111,11 +114,11 @@ const MasonryItem = React.forwardRef(function MasonryItem(inProps, ref) { observer.unobserve(item); item.removeEventListener('error', handleError); }; - }, [height, styleProps]); + }, [isSSR, styleProps]); React.useEffect(() => { // handle responsiveness to `height` prop in case of SSR masonry - if (height !== undefined && height !== styleProps.height) { + if (isSSR && height !== styleProps.height) { setStyleProps({ ...styleProps, height: height < 0 ? 0 : height, @@ -129,7 +132,7 @@ const MasonryItem = React.forwardRef(function MasonryItem(inProps, ref) { if (columnSpan !== styleProps.columnSpan) { setStyleProps({ ...styleProps, columnSpan }); } - }, [height, spacing, columnSpan, styleProps]); + }, [isSSR, height, spacing, columnSpan, styleProps]); const handleRef = useForkRef(ref, masonryItemRef); return ( From 66d4d8d89ed7d4e9c2d0e4ae932cea91f0aa6737 Mon Sep 17 00:00:00 2001 From: Benny Joo Date: Fri, 6 Aug 2021 20:57:48 +0100 Subject: [PATCH 27/59] Reload image if load fails --- packages/material-ui-lab/src/MasonryItem/MasonryItem.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/material-ui-lab/src/MasonryItem/MasonryItem.js b/packages/material-ui-lab/src/MasonryItem/MasonryItem.js index 2716c0d5c98507..159474289e1a8a 100644 --- a/packages/material-ui-lab/src/MasonryItem/MasonryItem.js +++ b/packages/material-ui-lab/src/MasonryItem/MasonryItem.js @@ -96,7 +96,7 @@ const MasonryItem = React.forwardRef(function MasonryItem(inProps, ref) { } const handleError = () => { const src = masonryItemRef.current.firstChild.src; - masonryItemRef.current.firstChild.src = src; + masonryItemRef.current.firstChild.src = `${src}?${new Date().getTime()}`; }; resizeObserver.current = new ResizeObserver(([item]) => { if (styleProps.height !== item.contentRect.height) { From e1af1ca2d51a388353571b2f5be277844304a5bd Mon Sep 17 00:00:00 2001 From: Benny Joo Date: Fri, 6 Aug 2021 22:33:15 +0100 Subject: [PATCH 28/59] Fix overlapping images --- .../src/MasonryItem/MasonryItem.js | 52 +++++++++++++++---- 1 file changed, 43 insertions(+), 9 deletions(-) diff --git a/packages/material-ui-lab/src/MasonryItem/MasonryItem.js b/packages/material-ui-lab/src/MasonryItem/MasonryItem.js index 159474289e1a8a..e1df31a8f702f9 100644 --- a/packages/material-ui-lab/src/MasonryItem/MasonryItem.js +++ b/packages/material-ui-lab/src/MasonryItem/MasonryItem.js @@ -31,6 +31,7 @@ export const style = ({ styleProps, theme }) => { width: '100%', boxSizing: 'inherit', ...(styleProps.isSSR && { height: '100%' }), + ...(styleProps.isImage && !styleProps.isSSR && { height: styleProps.height }), }, visibility: styleProps.height ? 'visible' : 'hidden', gridColumnEnd: `span ${styleProps.columnSpan}`, @@ -47,9 +48,7 @@ export const style = ({ styleProps, theme }) => { const transformer = createUnarySpacing(theme); const styleFromPropValue = (propValue) => { const gap = styleProps.height ? Number(getValue(transformer, propValue).replace('px', '')) : 0; - // For lazy-loaded images to load properly, masonry item should take up space greater than 1px. - // Taking into account a row gap of 1px, rowSpan should at least be 3. - const rowSpan = styleProps.height ? Math.ceil(styleProps.height + gap) : 3; + const rowSpan = styleProps.height ? Math.ceil(styleProps.height + gap) : 0; return { gridRowEnd: `span ${rowSpan}`, paddingBottom: gap - 1, @@ -77,11 +76,24 @@ const MasonryItem = React.forwardRef(function MasonryItem(inProps, ref) { const masonryItemRef = React.useRef(null); const { spacing = 1 } = React.useContext(MasonryContext); - const { children, className, component = 'div', columnSpan = 1, height, ...other } = props; - const isSSR = height !== undefined; + const isImage = (element) => { + return element.type === 'img'; + }; + const { + children, + className, + component = 'div', + columnSpan = 1, + height = isImage(children) ? 1 : undefined, + // images should have defined dimensions for browser to correctly lay out and paint. + // browser can't handle a large number of images without pre-defined dimensions + ...other + } = props; + const isSSR = !((isImage(children) && height === 1) || height === undefined); const [styleProps, setStyleProps] = React.useState({ ...props, isSSR, + isImage: isImage(children), spacing, columnSpan, height: height < 0 ? 0 : height, // MasonryItems to which negative or zero height is passed will be hidden @@ -89,12 +101,34 @@ const MasonryItem = React.forwardRef(function MasonryItem(inProps, ref) { const classes = useUtilityClasses(styleProps); const resizeObserver = React.useRef(null); + + React.useEffect(() => { + if (isImage(children) && styleProps.height === 1) { + const img = new Image(); + img.src = + children.props.srcSet || + children.props.src || + children.props['data-src'] || + children.props['data-srcSet']; + img.onload = () => { + setStyleProps({ + ...styleProps, + height: img.height, + }); + }; + } + }, [children, styleProps]); + React.useEffect(() => { // do not create a resize observer in case of SSR masonry if (isSSR) { return true; } - const handleError = () => { + // do not create a resize observer until height is set by onload() callback above + if (isImage(children) && styleProps.height === 1) { + return true; + } + const handleImageLoadError = () => { const src = masonryItemRef.current.firstChild.src; masonryItemRef.current.firstChild.src = `${src}?${new Date().getTime()}`; }; @@ -108,13 +142,13 @@ const MasonryItem = React.forwardRef(function MasonryItem(inProps, ref) { }); const item = masonryItemRef.current.firstChild; const observer = resizeObserver.current; - item.addEventListener('error', handleError); + item.addEventListener('error', handleImageLoadError); observer.observe(item); return () => { observer.unobserve(item); - item.removeEventListener('error', handleError); + item.removeEventListener('error', handleImageLoadError); }; - }, [isSSR, styleProps]); + }, [children, isSSR, styleProps]); React.useEffect(() => { // handle responsiveness to `height` prop in case of SSR masonry From 713074f85f4ba33b86d059f36940883b9ce0a4bb Mon Sep 17 00:00:00 2001 From: Olivier Tassinari Date: Sat, 7 Aug 2021 21:28:16 +0200 Subject: [PATCH 29/59] src is a required img attribute --- docs/src/pages/components/masonry/ImageMasonry.js | 4 ++-- docs/src/pages/components/masonry/ImageMasonry.tsx | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/src/pages/components/masonry/ImageMasonry.js b/docs/src/pages/components/masonry/ImageMasonry.js index ee105174a3f5d6..ebb5eda20a923e 100644 --- a/docs/src/pages/components/masonry/ImageMasonry.js +++ b/docs/src/pages/components/masonry/ImageMasonry.js @@ -11,8 +11,8 @@ export default function ImageMasonry() { {itemData.map((item) => ( {item.title} diff --git a/docs/src/pages/components/masonry/ImageMasonry.tsx b/docs/src/pages/components/masonry/ImageMasonry.tsx index ee105174a3f5d6..ebb5eda20a923e 100644 --- a/docs/src/pages/components/masonry/ImageMasonry.tsx +++ b/docs/src/pages/components/masonry/ImageMasonry.tsx @@ -11,8 +11,8 @@ export default function ImageMasonry() { {itemData.map((item) => ( {item.title} From ede87503a71564e9b4f313b9d504905186414a44 Mon Sep 17 00:00:00 2001 From: Benny Joo Date: Sat, 7 Aug 2021 23:10:45 +0100 Subject: [PATCH 30/59] Revert the incorrect fix for image overlapping on some browsers --- .../src/MasonryItem/MasonryItem.js | 46 +++---------------- 1 file changed, 7 insertions(+), 39 deletions(-) diff --git a/packages/material-ui-lab/src/MasonryItem/MasonryItem.js b/packages/material-ui-lab/src/MasonryItem/MasonryItem.js index e1df31a8f702f9..772a54de169fa8 100644 --- a/packages/material-ui-lab/src/MasonryItem/MasonryItem.js +++ b/packages/material-ui-lab/src/MasonryItem/MasonryItem.js @@ -31,7 +31,6 @@ export const style = ({ styleProps, theme }) => { width: '100%', boxSizing: 'inherit', ...(styleProps.isSSR && { height: '100%' }), - ...(styleProps.isImage && !styleProps.isSSR && { height: styleProps.height }), }, visibility: styleProps.height ? 'visible' : 'hidden', gridColumnEnd: `span ${styleProps.columnSpan}`, @@ -48,7 +47,9 @@ export const style = ({ styleProps, theme }) => { const transformer = createUnarySpacing(theme); const styleFromPropValue = (propValue) => { const gap = styleProps.height ? Number(getValue(transformer, propValue).replace('px', '')) : 0; - const rowSpan = styleProps.height ? Math.ceil(styleProps.height + gap) : 0; + // For lazy-loaded images to load properly, masonry item should take up space greater than 1px. + // Taking into account a row gap of 1px, rowSpan should at least be 3. + const rowSpan = styleProps.height ? Math.ceil(styleProps.height + gap) : 3; return { gridRowEnd: `span ${rowSpan}`, paddingBottom: gap - 1, @@ -76,24 +77,12 @@ const MasonryItem = React.forwardRef(function MasonryItem(inProps, ref) { const masonryItemRef = React.useRef(null); const { spacing = 1 } = React.useContext(MasonryContext); - const isImage = (element) => { - return element.type === 'img'; - }; - const { - children, - className, - component = 'div', - columnSpan = 1, - height = isImage(children) ? 1 : undefined, - // images should have defined dimensions for browser to correctly lay out and paint. - // browser can't handle a large number of images without pre-defined dimensions - ...other - } = props; - const isSSR = !((isImage(children) && height === 1) || height === undefined); + const { children, className, component = 'div', columnSpan = 1, height, ...other } = props; + const isSSR = height !== undefined; + const [styleProps, setStyleProps] = React.useState({ ...props, isSSR, - isImage: isImage(children), spacing, columnSpan, height: height < 0 ? 0 : height, // MasonryItems to which negative or zero height is passed will be hidden @@ -102,32 +91,11 @@ const MasonryItem = React.forwardRef(function MasonryItem(inProps, ref) { const classes = useUtilityClasses(styleProps); const resizeObserver = React.useRef(null); - React.useEffect(() => { - if (isImage(children) && styleProps.height === 1) { - const img = new Image(); - img.src = - children.props.srcSet || - children.props.src || - children.props['data-src'] || - children.props['data-srcSet']; - img.onload = () => { - setStyleProps({ - ...styleProps, - height: img.height, - }); - }; - } - }, [children, styleProps]); - React.useEffect(() => { // do not create a resize observer in case of SSR masonry if (isSSR) { return true; } - // do not create a resize observer until height is set by onload() callback above - if (isImage(children) && styleProps.height === 1) { - return true; - } const handleImageLoadError = () => { const src = masonryItemRef.current.firstChild.src; masonryItemRef.current.firstChild.src = `${src}?${new Date().getTime()}`; @@ -148,7 +116,7 @@ const MasonryItem = React.forwardRef(function MasonryItem(inProps, ref) { observer.unobserve(item); item.removeEventListener('error', handleImageLoadError); }; - }, [children, isSSR, styleProps]); + }, [isSSR, styleProps]); React.useEffect(() => { // handle responsiveness to `height` prop in case of SSR masonry From 33f5724a7c2e1fd60684cc6621581305f466b45c Mon Sep 17 00:00:00 2001 From: Benny Joo Date: Mon, 9 Aug 2021 08:40:23 +0100 Subject: [PATCH 31/59] Refactor MasonryItem --- .../material-ui-lab/src/MasonryItem/MasonryItem.js | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/packages/material-ui-lab/src/MasonryItem/MasonryItem.js b/packages/material-ui-lab/src/MasonryItem/MasonryItem.js index 772a54de169fa8..7cb9e39c14462b 100644 --- a/packages/material-ui-lab/src/MasonryItem/MasonryItem.js +++ b/packages/material-ui-lab/src/MasonryItem/MasonryItem.js @@ -89,18 +89,17 @@ const MasonryItem = React.forwardRef(function MasonryItem(inProps, ref) { }); const classes = useUtilityClasses(styleProps); - const resizeObserver = React.useRef(null); React.useEffect(() => { // do not create a resize observer in case of SSR masonry if (isSSR) { - return true; + return () => {}; } const handleImageLoadError = () => { const src = masonryItemRef.current.firstChild.src; masonryItemRef.current.firstChild.src = `${src}?${new Date().getTime()}`; }; - resizeObserver.current = new ResizeObserver(([item]) => { + const resizeObserver = new ResizeObserver(([item]) => { if (styleProps.height !== item.contentRect.height) { setStyleProps({ ...styleProps, @@ -109,11 +108,10 @@ const MasonryItem = React.forwardRef(function MasonryItem(inProps, ref) { } }); const item = masonryItemRef.current.firstChild; - const observer = resizeObserver.current; item.addEventListener('error', handleImageLoadError); - observer.observe(item); + resizeObserver.observe(item); return () => { - observer.unobserve(item); + resizeObserver.unobserve(item); item.removeEventListener('error', handleImageLoadError); }; }, [isSSR, styleProps]); From 3f36763be4306c73e039e046e55ab75ff17c8223 Mon Sep 17 00:00:00 2001 From: Benny Joo Date: Mon, 9 Aug 2021 10:51:24 +0100 Subject: [PATCH 32/59] No need to handle prop updates in useEffect --- .../src/MasonryItem/MasonryItem.js | 34 +++++++++---------- 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/packages/material-ui-lab/src/MasonryItem/MasonryItem.js b/packages/material-ui-lab/src/MasonryItem/MasonryItem.js index 7cb9e39c14462b..37926511f3731b 100644 --- a/packages/material-ui-lab/src/MasonryItem/MasonryItem.js +++ b/packages/material-ui-lab/src/MasonryItem/MasonryItem.js @@ -88,6 +88,22 @@ const MasonryItem = React.forwardRef(function MasonryItem(inProps, ref) { height: height < 0 ? 0 : height, // MasonryItems to which negative or zero height is passed will be hidden }); + // handle responsiveness to `height` prop in case of SSR masonry + if (isSSR && height !== styleProps.height) { + setStyleProps({ + ...styleProps, + height: height < 0 ? 0 : height, + }); + } + // re-style if `spacing` updates + if (spacing !== styleProps.spacing) { + setStyleProps({ ...styleProps, spacing }); + } + // re-style if `columnSpan` updates + if (columnSpan !== styleProps.columnSpan) { + setStyleProps({ ...styleProps, columnSpan }); + } + const classes = useUtilityClasses(styleProps); React.useEffect(() => { @@ -116,24 +132,6 @@ const MasonryItem = React.forwardRef(function MasonryItem(inProps, ref) { }; }, [isSSR, styleProps]); - React.useEffect(() => { - // handle responsiveness to `height` prop in case of SSR masonry - if (isSSR && height !== styleProps.height) { - setStyleProps({ - ...styleProps, - height: height < 0 ? 0 : height, - }); - } - // re-style if `spacing` updates - if (spacing !== styleProps.spacing) { - setStyleProps({ ...styleProps, spacing }); - } - // re-style if `columnSpan` updates - if (columnSpan !== styleProps.columnSpan) { - setStyleProps({ ...styleProps, columnSpan }); - } - }, [isSSR, height, spacing, columnSpan, styleProps]); - const handleRef = useForkRef(ref, masonryItemRef); return ( Date: Mon, 9 Aug 2021 15:03:39 +0100 Subject: [PATCH 33/59] Remove image load error handler --- packages/material-ui-lab/src/MasonryItem/MasonryItem.js | 6 ------ 1 file changed, 6 deletions(-) diff --git a/packages/material-ui-lab/src/MasonryItem/MasonryItem.js b/packages/material-ui-lab/src/MasonryItem/MasonryItem.js index 37926511f3731b..5dfcb2b7d00a92 100644 --- a/packages/material-ui-lab/src/MasonryItem/MasonryItem.js +++ b/packages/material-ui-lab/src/MasonryItem/MasonryItem.js @@ -111,10 +111,6 @@ const MasonryItem = React.forwardRef(function MasonryItem(inProps, ref) { if (isSSR) { return () => {}; } - const handleImageLoadError = () => { - const src = masonryItemRef.current.firstChild.src; - masonryItemRef.current.firstChild.src = `${src}?${new Date().getTime()}`; - }; const resizeObserver = new ResizeObserver(([item]) => { if (styleProps.height !== item.contentRect.height) { setStyleProps({ @@ -124,11 +120,9 @@ const MasonryItem = React.forwardRef(function MasonryItem(inProps, ref) { } }); const item = masonryItemRef.current.firstChild; - item.addEventListener('error', handleImageLoadError); resizeObserver.observe(item); return () => { resizeObserver.unobserve(item); - item.removeEventListener('error', handleImageLoadError); }; }, [isSSR, styleProps]); From b2e2747908ec7b25b19a87537157842cf8c3ab2c Mon Sep 17 00:00:00 2001 From: eps1lon Date: Thu, 12 Aug 2021 08:50:46 +0200 Subject: [PATCH 34/59] Don't support change of height --- docs/pages/api-docs/masonry-item.json | 2 +- .../api-docs/masonry-item/masonry-item.json | 2 +- .../src/MasonryItem/MasonryItem.d.ts | 4 +- .../src/MasonryItem/MasonryItem.js | 39 ++++++------------- .../src/MasonryItem/MasonryItem.test.js | 2 +- 5 files changed, 16 insertions(+), 33 deletions(-) diff --git a/docs/pages/api-docs/masonry-item.json b/docs/pages/api-docs/masonry-item.json index a06eac80460153..0789482ca0eb35 100644 --- a/docs/pages/api-docs/masonry-item.json +++ b/docs/pages/api-docs/masonry-item.json @@ -4,7 +4,7 @@ "classes": { "type": { "name": "object" } }, "columnSpan": { "type": { "name": "number" }, "default": "1" }, "component": { "type": { "name": "elementType" } }, - "height": { "type": { "name": "number" } }, + "defaultHeight": { "type": { "name": "number" } }, "sx": { "type": { "name": "object" } } }, "name": "MasonryItem", diff --git a/docs/translations/api-docs/masonry-item/masonry-item.json b/docs/translations/api-docs/masonry-item/masonry-item.json index 5f337a77397546..535abe02463228 100644 --- a/docs/translations/api-docs/masonry-item/masonry-item.json +++ b/docs/translations/api-docs/masonry-item/masonry-item.json @@ -5,7 +5,7 @@ "classes": "Override or extend the styles applied to the component. See CSS API below for more details.", "columnSpan": "The number of columns taken up by the component", "component": "The component used for the root node. Either a string to use a HTML element or a component.", - "height": "The height of the component in px. This is provided for server-side rendering.", + "defaultHeight": "The initial height of the component in px. This is provided for server-side rendering.", "sx": "Allows defining system overrides as well as additional CSS styles. See the `sx` page for more details." }, "classDescriptions": { "root": { "description": "Styles applied to the root element." } } diff --git a/packages/material-ui-lab/src/MasonryItem/MasonryItem.d.ts b/packages/material-ui-lab/src/MasonryItem/MasonryItem.d.ts index 597e86fd8f0770..b7a773daf4e0ca 100644 --- a/packages/material-ui-lab/src/MasonryItem/MasonryItem.d.ts +++ b/packages/material-ui-lab/src/MasonryItem/MasonryItem.d.ts @@ -14,9 +14,9 @@ export interface MasonryItemTypeMap

*/ classes?: Partial; /** - * The height of the component in px. This is provided for server-side rendering. + * The initial height of the component in px. This is provided for server-side rendering. */ - height?: number; + defaultHeight?: number; /** * The number of columns taken up by the component * @default 1 diff --git a/packages/material-ui-lab/src/MasonryItem/MasonryItem.js b/packages/material-ui-lab/src/MasonryItem/MasonryItem.js index 5dfcb2b7d00a92..1ffd802b690942 100644 --- a/packages/material-ui-lab/src/MasonryItem/MasonryItem.js +++ b/packages/material-ui-lab/src/MasonryItem/MasonryItem.js @@ -77,32 +77,18 @@ const MasonryItem = React.forwardRef(function MasonryItem(inProps, ref) { const masonryItemRef = React.useRef(null); const { spacing = 1 } = React.useContext(MasonryContext); - const { children, className, component = 'div', columnSpan = 1, height, ...other } = props; - const isSSR = height !== undefined; + const { children, className, component = 'div', columnSpan = 1, defaultHeight, ...other } = props; + const isSSR = defaultHeight !== undefined; - const [styleProps, setStyleProps] = React.useState({ + const [height, setHeight] = React.useState(defaultHeight); + + const styleProps = { ...props, isSSR, spacing, columnSpan, height: height < 0 ? 0 : height, // MasonryItems to which negative or zero height is passed will be hidden - }); - - // handle responsiveness to `height` prop in case of SSR masonry - if (isSSR && height !== styleProps.height) { - setStyleProps({ - ...styleProps, - height: height < 0 ? 0 : height, - }); - } - // re-style if `spacing` updates - if (spacing !== styleProps.spacing) { - setStyleProps({ ...styleProps, spacing }); - } - // re-style if `columnSpan` updates - if (columnSpan !== styleProps.columnSpan) { - setStyleProps({ ...styleProps, columnSpan }); - } + }; const classes = useUtilityClasses(styleProps); @@ -112,11 +98,8 @@ const MasonryItem = React.forwardRef(function MasonryItem(inProps, ref) { return () => {}; } const resizeObserver = new ResizeObserver(([item]) => { - if (styleProps.height !== item.contentRect.height) { - setStyleProps({ - ...styleProps, - height: item.contentRect.height, - }); + if (height !== item.contentRect.height) { + setHeight(item.contentRect.height); } }); const item = masonryItemRef.current.firstChild; @@ -124,7 +107,7 @@ const MasonryItem = React.forwardRef(function MasonryItem(inProps, ref) { return () => { resizeObserver.unobserve(item); }; - }, [isSSR, styleProps]); + }, [isSSR, height]); const handleRef = useForkRef(ref, masonryItemRef); return ( @@ -168,9 +151,9 @@ MasonryItem.propTypes /* remove-proptypes */ = { */ component: PropTypes.elementType, /** - * The height of the component in px. This is provided for server-side rendering. + * The initial height of the component in px. This is provided for server-side rendering. */ - height: PropTypes.number, + defaultHeight: PropTypes.number, /** * Allows defining system overrides as well as additional CSS styles. */ diff --git a/packages/material-ui-lab/src/MasonryItem/MasonryItem.test.js b/packages/material-ui-lab/src/MasonryItem/MasonryItem.test.js index 164548ba7d4e45..4a97fd6b27cef9 100644 --- a/packages/material-ui-lab/src/MasonryItem/MasonryItem.test.js +++ b/packages/material-ui-lab/src/MasonryItem/MasonryItem.test.js @@ -110,7 +110,7 @@ describe('', () => { it('should compute grid-row-end based on given height', () => { const { getByTestId } = render( - + {children} , ); From 3617f062cb0277a0c55685361f03e7fbba0caf7e Mon Sep 17 00:00:00 2001 From: Benny Joo Date: Thu, 12 Aug 2021 08:03:50 +0100 Subject: [PATCH 35/59] Increase row height to 2px --- packages/material-ui-lab/src/Masonry/Masonry.js | 2 +- packages/material-ui-lab/src/MasonryItem/MasonryItem.js | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/material-ui-lab/src/Masonry/Masonry.js b/packages/material-ui-lab/src/Masonry/Masonry.js index bfbda868ef768d..6cd614f3b37fda 100644 --- a/packages/material-ui-lab/src/Masonry/Masonry.js +++ b/packages/material-ui-lab/src/Masonry/Masonry.js @@ -30,7 +30,7 @@ export const style = ({ styleProps, theme }) => { padding: 0, overflow: 'auto', width: '100%', - rowGap: 1, + rowGap: 2, boxSizing: 'border-box', }; diff --git a/packages/material-ui-lab/src/MasonryItem/MasonryItem.js b/packages/material-ui-lab/src/MasonryItem/MasonryItem.js index 1ffd802b690942..8a5fc1bf062925 100644 --- a/packages/material-ui-lab/src/MasonryItem/MasonryItem.js +++ b/packages/material-ui-lab/src/MasonryItem/MasonryItem.js @@ -48,11 +48,11 @@ export const style = ({ styleProps, theme }) => { const styleFromPropValue = (propValue) => { const gap = styleProps.height ? Number(getValue(transformer, propValue).replace('px', '')) : 0; // For lazy-loaded images to load properly, masonry item should take up space greater than 1px. - // Taking into account a row gap of 1px, rowSpan should at least be 3. - const rowSpan = styleProps.height ? Math.ceil(styleProps.height + gap) : 3; + // Taking into account a row gap of 2px, rowSpan should at least be 2. + const rowSpan = styleProps.height ? Math.ceil((styleProps.height + gap) / 2) : 2; return { gridRowEnd: `span ${rowSpan}`, - paddingBottom: gap - 1, + paddingBottom: gap - 2, }; }; styles = { ...styles, ...handleBreakpoints({ theme }, spacingValues, styleFromPropValue) }; From 375624b8ff29ddfc27be5416e27270bb89346192 Mon Sep 17 00:00:00 2001 From: Benny Joo Date: Thu, 12 Aug 2021 09:08:21 +0100 Subject: [PATCH 36/59] Change to defaultHeight in SSR demo --- docs/src/pages/components/masonry/SSRMasonry.js | 2 +- docs/src/pages/components/masonry/SSRMasonry.tsx | 2 +- packages/material-ui-lab/src/MasonryItem/MasonryItem.js | 4 +--- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/docs/src/pages/components/masonry/SSRMasonry.js b/docs/src/pages/components/masonry/SSRMasonry.js index 08219297dd8fb6..4cc1ba731be40f 100644 --- a/docs/src/pages/components/masonry/SSRMasonry.js +++ b/docs/src/pages/components/masonry/SSRMasonry.js @@ -9,7 +9,7 @@ export default function SSRMasonry() { {heights.map((height, index) => ( - + {heights.map((height, index) => ( - + {}; } const resizeObserver = new ResizeObserver(([item]) => { - if (height !== item.contentRect.height) { - setHeight(item.contentRect.height); - } + setHeight(item.contentRect.height); }); const item = masonryItemRef.current.firstChild; resizeObserver.observe(item); From be9cd99684e7f2d9185bd06bdfa65e276f37a638 Mon Sep 17 00:00:00 2001 From: Benny Joo Date: Thu, 12 Aug 2021 13:09:42 +0100 Subject: [PATCH 37/59] Remove height from dependency array --- packages/material-ui-lab/src/MasonryItem/MasonryItem.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/material-ui-lab/src/MasonryItem/MasonryItem.js b/packages/material-ui-lab/src/MasonryItem/MasonryItem.js index ca5dfa67a1fe49..3b097cc6b25ba5 100644 --- a/packages/material-ui-lab/src/MasonryItem/MasonryItem.js +++ b/packages/material-ui-lab/src/MasonryItem/MasonryItem.js @@ -105,7 +105,7 @@ const MasonryItem = React.forwardRef(function MasonryItem(inProps, ref) { return () => { resizeObserver.unobserve(item); }; - }, [isSSR, height]); + }, [isSSR]); const handleRef = useForkRef(ref, masonryItemRef); return ( From 3a8191f2e1cba9b528973e47df327a73265760ff Mon Sep 17 00:00:00 2001 From: Benny Joo Date: Thu, 12 Aug 2021 13:37:18 +0100 Subject: [PATCH 38/59] Fix tests as we changed row height to 2px --- .../src/Masonry/Masonry.test.js | 6 +++--- .../src/MasonryItem/MasonryItem.test.js | 18 +++++++++--------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/packages/material-ui-lab/src/Masonry/Masonry.test.js b/packages/material-ui-lab/src/Masonry/Masonry.test.js index 550a2335565c41..20dead08f532f2 100644 --- a/packages/material-ui-lab/src/Masonry/Masonry.test.js +++ b/packages/material-ui-lab/src/Masonry/Masonry.test.js @@ -42,7 +42,7 @@ describe('', () => { padding: 0, overflow: 'auto', width: '100%', - rowGap: 1, + rowGap: 2, columnGap: theme.spacing(1), gridTemplateColumns: 'repeat(4, 1fr)', boxSizing: 'border-box', @@ -76,7 +76,7 @@ describe('', () => { padding: 0, overflow: 'auto', width: '100%', - rowGap: 1, + rowGap: 2, boxSizing: 'border-box', }); }); @@ -106,7 +106,7 @@ describe('', () => { overflow: 'auto', width: '100%', columnGap: theme.spacing(1), - rowGap: 1, + rowGap: 2, boxSizing: 'border-box', }); }); diff --git a/packages/material-ui-lab/src/MasonryItem/MasonryItem.test.js b/packages/material-ui-lab/src/MasonryItem/MasonryItem.test.js index 4a97fd6b27cef9..afd3f4627cdeb6 100644 --- a/packages/material-ui-lab/src/MasonryItem/MasonryItem.test.js +++ b/packages/material-ui-lab/src/MasonryItem/MasonryItem.test.js @@ -62,16 +62,16 @@ describe('', () => { }), ).to.deep.equal({ '@media (min-width:0px)': { - gridRowEnd: `span ${Math.ceil(100 + Number(theme.spacing(1).replace('px', '')))}`, - paddingBottom: Number(theme.spacing(1).replace('px', '')) - 1, + gridRowEnd: `span ${Math.ceil((100 + Number(theme.spacing(1).replace('px', ''))) / 2)}`, + paddingBottom: Number(theme.spacing(1).replace('px', '')) - 2, }, [`@media (min-width:${defaultTheme.breakpoints.values.sm}px)`]: { - gridRowEnd: `span ${Math.ceil(100 + Number(theme.spacing(2).replace('px', '')))}`, - paddingBottom: Number(theme.spacing(2).replace('px', '')) - 1, + gridRowEnd: `span ${Math.ceil((100 + Number(theme.spacing(2).replace('px', ''))) / 2)}`, + paddingBottom: Number(theme.spacing(2).replace('px', '')) - 2, }, [`@media (min-width:${defaultTheme.breakpoints.values.md}px)`]: { - gridRowEnd: `span ${Math.ceil(100 + Number(theme.spacing(3).replace('px', '')))}`, - paddingBottom: Number(theme.spacing(3).replace('px', '')) - 1, + gridRowEnd: `span ${Math.ceil((100 + Number(theme.spacing(3).replace('px', ''))) / 2)}`, + paddingBottom: Number(theme.spacing(3).replace('px', '')) - 2, }, width: '100%', [`& > *`]: { @@ -95,8 +95,8 @@ describe('', () => { theme, }), ).to.deep.equal({ - gridRowEnd: `span ${Math.ceil(100 + Number(theme.spacing(1).replace('px', '')))}`, - paddingBottom: Number(theme.spacing(1).replace('px', '')) - 1, + gridRowEnd: `span ${Math.ceil((100 + Number(theme.spacing(1).replace('px', ''))) / 2)}`, + paddingBottom: Number(theme.spacing(1).replace('px', '')) - 2, width: '100%', [`& > *`]: { width: '100%', @@ -116,7 +116,7 @@ describe('', () => { ); const computedStyle = getComputedStyle(getByTestId('test-root')); expect(computedStyle['grid-row-end']).to.equal( - `span ${Math.ceil(150 + Number(theme.spacing(1).replace('px', '')))}`, + `span ${Math.ceil((150 + Number(theme.spacing(1).replace('px', ''))) / 2)}`, ); }); }); From 157cd42daa84a02db92020debdc0ca21f990d598 Mon Sep 17 00:00:00 2001 From: Benny Joo Date: Thu, 19 Aug 2021 08:31:57 +0100 Subject: [PATCH 39/59] Remove polyfill package and use dummy resize observer instad --- .../src/MasonryItem/MasonryItem.js | 24 ++++++++++++++----- .../src/MasonryItem/MasonryItem.test.js | 10 -------- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/packages/material-ui-lab/src/MasonryItem/MasonryItem.js b/packages/material-ui-lab/src/MasonryItem/MasonryItem.js index 3b097cc6b25ba5..c09033e5cb5a32 100644 --- a/packages/material-ui-lab/src/MasonryItem/MasonryItem.js +++ b/packages/material-ui-lab/src/MasonryItem/MasonryItem.js @@ -13,6 +13,14 @@ import { styled, useThemeProps } from '@material-ui/core/styles'; import { getMasonryItemUtilityClass } from './masonryItemClasses'; import MasonryContext from '../Masonry/MasonryContext'; +const MockResizeObserver = () => { + return { + observe: () => {}, + unobserve: () => {}, + disconnect: () => {}, + }; +}; + const useUtilityClasses = (styleProps) => { const { classes } = styleProps; @@ -91,19 +99,23 @@ const MasonryItem = React.forwardRef(function MasonryItem(inProps, ref) { }; const classes = useUtilityClasses(styleProps); - + const resizeObserver = React.useRef(null); React.useEffect(() => { // do not create a resize observer in case of SSR masonry if (isSSR) { return () => {}; } - const resizeObserver = new ResizeObserver(([item]) => { - setHeight(item.contentRect.height); - }); + try { + resizeObserver.current = new ResizeObserver(([item]) => { + setHeight(item.contentRect.height); + }); + } catch (err) { + resizeObserver.current = MockResizeObserver(); // Prevent crash for old browsers (e..g., 11IE) + } const item = masonryItemRef.current.firstChild; - resizeObserver.observe(item); + resizeObserver.current.observe(item); return () => { - resizeObserver.unobserve(item); + resizeObserver.current.unobserve(item); }; }, [isSSR]); diff --git a/packages/material-ui-lab/src/MasonryItem/MasonryItem.test.js b/packages/material-ui-lab/src/MasonryItem/MasonryItem.test.js index afd3f4627cdeb6..0ec3baae3dfb55 100644 --- a/packages/material-ui-lab/src/MasonryItem/MasonryItem.test.js +++ b/packages/material-ui-lab/src/MasonryItem/MasonryItem.test.js @@ -4,19 +4,9 @@ import MasonryItem, { masonryItemClasses as classes } from '@material-ui/lab/Mas import { expect } from 'chai'; import { createTheme } from '@material-ui/core/styles'; import defaultTheme from '@material-ui/core/styles/defaultTheme'; -import ResizeObserver from 'resize-observer-polyfill'; import { style } from './MasonryItem'; describe('', () => { - // Mount ResizeObserver; otherwise, this error comes up: `[ReferenceError: ResizeObserver is not defined]` - before(() => { - global.ResizeObserver = ResizeObserver; - }); - - after(() => { - delete global.ResizeObserver; - }); - const render = createClientRender(); describeConformanceV5( From 9cd1cb474bbf9e225048fc68311ef26fdc7692a2 Mon Sep 17 00:00:00 2001 From: Benny Joo Date: Thu, 19 Aug 2021 08:44:07 +0100 Subject: [PATCH 40/59] Change height to defaultHeight in regression test --- test/regressions/fixtures/Masonry/SSRMasonry.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/regressions/fixtures/Masonry/SSRMasonry.js b/test/regressions/fixtures/Masonry/SSRMasonry.js index f80db6c32f883f..2a56136da40f38 100644 --- a/test/regressions/fixtures/Masonry/SSRMasonry.js +++ b/test/regressions/fixtures/Masonry/SSRMasonry.js @@ -8,7 +8,7 @@ export default function SSRMasonry() { return ( {heights.map((height, index) => ( - + {index + 1} From 872a35076ad901b27f279dc41f1b222936ee5bc4 Mon Sep 17 00:00:00 2001 From: Marija Najdova Date: Thu, 19 Aug 2021 11:08:16 +0200 Subject: [PATCH 41/59] Update packages/material-ui-lab/src/Masonry/Masonry.test.js --- packages/material-ui-lab/src/Masonry/Masonry.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/material-ui-lab/src/Masonry/Masonry.test.js b/packages/material-ui-lab/src/Masonry/Masonry.test.js index 20dead08f532f2..0e781f5a41b814 100644 --- a/packages/material-ui-lab/src/Masonry/Masonry.test.js +++ b/packages/material-ui-lab/src/Masonry/Masonry.test.js @@ -1,6 +1,6 @@ import { expect } from 'chai'; import * as React from 'react'; -import { createClientRender, describeConformanceV5 } from 'test/utils'; +import { createClientRender, describeConformance } from 'test/utils'; import Masonry, { masonryClasses as classes } from '@material-ui/lab/Masonry'; import { createTheme } from '@material-ui/core/styles'; import defaultTheme from '@material-ui/core/styles/defaultTheme'; From 7bb24f5de2d1b0702be00fcce1d9ce1ffc9c9db2 Mon Sep 17 00:00:00 2001 From: Marija Najdova Date: Thu, 19 Aug 2021 11:08:33 +0200 Subject: [PATCH 42/59] Update packages/material-ui-lab/src/Masonry/Masonry.test.js --- packages/material-ui-lab/src/Masonry/Masonry.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/material-ui-lab/src/Masonry/Masonry.test.js b/packages/material-ui-lab/src/Masonry/Masonry.test.js index 0e781f5a41b814..70bbe5ec8a0cb4 100644 --- a/packages/material-ui-lab/src/Masonry/Masonry.test.js +++ b/packages/material-ui-lab/src/Masonry/Masonry.test.js @@ -9,7 +9,7 @@ import { style } from './Masonry'; describe('', () => { const render = createClientRender(); - describeConformanceV5( + describeConformance(

, From 2f6741434cc4f833186baed05c73dff815aea30e Mon Sep 17 00:00:00 2001 From: Marija Najdova Date: Thu, 19 Aug 2021 11:12:54 +0200 Subject: [PATCH 43/59] Update docs/src/pages/components/masonry/masonry.md --- docs/src/pages/components/masonry/masonry.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/pages/components/masonry/masonry.md b/docs/src/pages/components/masonry/masonry.md index 84641e2b1857f8..17887a66640f01 100644 --- a/docs/src/pages/components/masonry/masonry.md +++ b/docs/src/pages/components/masonry/masonry.md @@ -60,6 +60,6 @@ However, you have to choose the value of `columnSpan` for each item carefully or ## Server-side rendering -By passing `height` to ``, you can use server-side rendering. By default, `height: 100%` will be set to the content of ``. If you change this, there can be unwanted gap between `` and the content that you pass to it. +By passing `defaultHeight` to ``, you can use server-side rendering. By default, `height: 100%` will be set to the content of ``. If you change this, there can be unwanted gap between `` and the content that you pass to it. {{"demo": "pages/components/masonry/SSRMasonry.js", "bg": true}} From 39bddd9f1890bee5099131010e36db10ac10d4a5 Mon Sep 17 00:00:00 2001 From: Marija Najdova Date: Thu, 19 Aug 2021 11:21:10 +0200 Subject: [PATCH 44/59] Update packages/material-ui-lab/src/MasonryItem/MasonryItem.test.js --- packages/material-ui-lab/src/MasonryItem/MasonryItem.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/material-ui-lab/src/MasonryItem/MasonryItem.test.js b/packages/material-ui-lab/src/MasonryItem/MasonryItem.test.js index 0ec3baae3dfb55..bff1921931e434 100644 --- a/packages/material-ui-lab/src/MasonryItem/MasonryItem.test.js +++ b/packages/material-ui-lab/src/MasonryItem/MasonryItem.test.js @@ -1,5 +1,5 @@ import * as React from 'react'; -import { createClientRender, describeConformanceV5 } from 'test/utils'; +import { createClientRender, describeConformance } from 'test/utils'; import MasonryItem, { masonryItemClasses as classes } from '@material-ui/lab/MasonryItem'; import { expect } from 'chai'; import { createTheme } from '@material-ui/core/styles'; From 15a3236ee5a1a95025caf43861582f65a043eef3 Mon Sep 17 00:00:00 2001 From: Marija Najdova Date: Thu, 19 Aug 2021 11:21:33 +0200 Subject: [PATCH 45/59] Update packages/material-ui-lab/src/MasonryItem/MasonryItem.test.js --- packages/material-ui-lab/src/MasonryItem/MasonryItem.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/material-ui-lab/src/MasonryItem/MasonryItem.test.js b/packages/material-ui-lab/src/MasonryItem/MasonryItem.test.js index bff1921931e434..b682aef56e4093 100644 --- a/packages/material-ui-lab/src/MasonryItem/MasonryItem.test.js +++ b/packages/material-ui-lab/src/MasonryItem/MasonryItem.test.js @@ -9,7 +9,7 @@ import { style } from './MasonryItem'; describe('', () => { const render = createClientRender(); - describeConformanceV5( + describeConformance(
, From bc4de1df815d19e0e2578166489ca35115225547 Mon Sep 17 00:00:00 2001 From: Benny Joo Date: Thu, 19 Aug 2021 10:19:47 +0100 Subject: [PATCH 46/59] Update translations and change styleProps to ownerState --- .../masonry-item/masonry-item-de.json | 2 +- .../masonry-item/masonry-item-es.json | 2 +- .../masonry-item/masonry-item-fr.json | 2 +- .../masonry-item/masonry-item-ja.json | 2 +- .../masonry-item/masonry-item-pt.json | 2 +- .../masonry-item/masonry-item-ru.json | 2 +- .../masonry-item/masonry-item-zh.json | 2 +- .../material-ui-lab/src/Masonry/Masonry.js | 18 ++++++------- .../src/Masonry/Masonry.test.js | 6 ++--- .../src/MasonryItem/MasonryItem.js | 26 +++++++++---------- .../src/MasonryItem/MasonryItem.test.js | 4 +-- 11 files changed, 34 insertions(+), 34 deletions(-) diff --git a/docs/translations/api-docs/masonry-item/masonry-item-de.json b/docs/translations/api-docs/masonry-item/masonry-item-de.json index 0f70ca91820611..535abe02463228 100644 --- a/docs/translations/api-docs/masonry-item/masonry-item-de.json +++ b/docs/translations/api-docs/masonry-item/masonry-item-de.json @@ -5,7 +5,7 @@ "classes": "Override or extend the styles applied to the component. See CSS API below for more details.", "columnSpan": "The number of columns taken up by the component", "component": "The component used for the root node. Either a string to use a HTML element or a component.", - "height": "The height of the component in px.", + "defaultHeight": "The initial height of the component in px. This is provided for server-side rendering.", "sx": "Allows defining system overrides as well as additional CSS styles. See the `sx` page for more details." }, "classDescriptions": { "root": { "description": "Styles applied to the root element." } } diff --git a/docs/translations/api-docs/masonry-item/masonry-item-es.json b/docs/translations/api-docs/masonry-item/masonry-item-es.json index 0f70ca91820611..535abe02463228 100644 --- a/docs/translations/api-docs/masonry-item/masonry-item-es.json +++ b/docs/translations/api-docs/masonry-item/masonry-item-es.json @@ -5,7 +5,7 @@ "classes": "Override or extend the styles applied to the component. See CSS API below for more details.", "columnSpan": "The number of columns taken up by the component", "component": "The component used for the root node. Either a string to use a HTML element or a component.", - "height": "The height of the component in px.", + "defaultHeight": "The initial height of the component in px. This is provided for server-side rendering.", "sx": "Allows defining system overrides as well as additional CSS styles. See the `sx` page for more details." }, "classDescriptions": { "root": { "description": "Styles applied to the root element." } } diff --git a/docs/translations/api-docs/masonry-item/masonry-item-fr.json b/docs/translations/api-docs/masonry-item/masonry-item-fr.json index 0f70ca91820611..535abe02463228 100644 --- a/docs/translations/api-docs/masonry-item/masonry-item-fr.json +++ b/docs/translations/api-docs/masonry-item/masonry-item-fr.json @@ -5,7 +5,7 @@ "classes": "Override or extend the styles applied to the component. See CSS API below for more details.", "columnSpan": "The number of columns taken up by the component", "component": "The component used for the root node. Either a string to use a HTML element or a component.", - "height": "The height of the component in px.", + "defaultHeight": "The initial height of the component in px. This is provided for server-side rendering.", "sx": "Allows defining system overrides as well as additional CSS styles. See the `sx` page for more details." }, "classDescriptions": { "root": { "description": "Styles applied to the root element." } } diff --git a/docs/translations/api-docs/masonry-item/masonry-item-ja.json b/docs/translations/api-docs/masonry-item/masonry-item-ja.json index 0f70ca91820611..535abe02463228 100644 --- a/docs/translations/api-docs/masonry-item/masonry-item-ja.json +++ b/docs/translations/api-docs/masonry-item/masonry-item-ja.json @@ -5,7 +5,7 @@ "classes": "Override or extend the styles applied to the component. See CSS API below for more details.", "columnSpan": "The number of columns taken up by the component", "component": "The component used for the root node. Either a string to use a HTML element or a component.", - "height": "The height of the component in px.", + "defaultHeight": "The initial height of the component in px. This is provided for server-side rendering.", "sx": "Allows defining system overrides as well as additional CSS styles. See the `sx` page for more details." }, "classDescriptions": { "root": { "description": "Styles applied to the root element." } } diff --git a/docs/translations/api-docs/masonry-item/masonry-item-pt.json b/docs/translations/api-docs/masonry-item/masonry-item-pt.json index 0f70ca91820611..535abe02463228 100644 --- a/docs/translations/api-docs/masonry-item/masonry-item-pt.json +++ b/docs/translations/api-docs/masonry-item/masonry-item-pt.json @@ -5,7 +5,7 @@ "classes": "Override or extend the styles applied to the component. See CSS API below for more details.", "columnSpan": "The number of columns taken up by the component", "component": "The component used for the root node. Either a string to use a HTML element or a component.", - "height": "The height of the component in px.", + "defaultHeight": "The initial height of the component in px. This is provided for server-side rendering.", "sx": "Allows defining system overrides as well as additional CSS styles. See the `sx` page for more details." }, "classDescriptions": { "root": { "description": "Styles applied to the root element." } } diff --git a/docs/translations/api-docs/masonry-item/masonry-item-ru.json b/docs/translations/api-docs/masonry-item/masonry-item-ru.json index 0f70ca91820611..535abe02463228 100644 --- a/docs/translations/api-docs/masonry-item/masonry-item-ru.json +++ b/docs/translations/api-docs/masonry-item/masonry-item-ru.json @@ -5,7 +5,7 @@ "classes": "Override or extend the styles applied to the component. See CSS API below for more details.", "columnSpan": "The number of columns taken up by the component", "component": "The component used for the root node. Either a string to use a HTML element or a component.", - "height": "The height of the component in px.", + "defaultHeight": "The initial height of the component in px. This is provided for server-side rendering.", "sx": "Allows defining system overrides as well as additional CSS styles. See the `sx` page for more details." }, "classDescriptions": { "root": { "description": "Styles applied to the root element." } } diff --git a/docs/translations/api-docs/masonry-item/masonry-item-zh.json b/docs/translations/api-docs/masonry-item/masonry-item-zh.json index 0f70ca91820611..535abe02463228 100644 --- a/docs/translations/api-docs/masonry-item/masonry-item-zh.json +++ b/docs/translations/api-docs/masonry-item/masonry-item-zh.json @@ -5,7 +5,7 @@ "classes": "Override or extend the styles applied to the component. See CSS API below for more details.", "columnSpan": "The number of columns taken up by the component", "component": "The component used for the root node. Either a string to use a HTML element or a component.", - "height": "The height of the component in px.", + "defaultHeight": "The initial height of the component in px. This is provided for server-side rendering.", "sx": "Allows defining system overrides as well as additional CSS styles. See the `sx` page for more details." }, "classDescriptions": { "root": { "description": "Styles applied to the root element." } } diff --git a/packages/material-ui-lab/src/Masonry/Masonry.js b/packages/material-ui-lab/src/Masonry/Masonry.js index 6cd614f3b37fda..d5525c9b0b5e22 100644 --- a/packages/material-ui-lab/src/Masonry/Masonry.js +++ b/packages/material-ui-lab/src/Masonry/Masonry.js @@ -13,8 +13,8 @@ import { styled, useThemeProps } from '@material-ui/core/styles'; import { getMasonryUtilityClass } from './masonryClasses'; import MasonryContext from './MasonryContext'; -const useUtilityClasses = (styleProps) => { - const { classes } = styleProps; +const useUtilityClasses = (ownerState) => { + const { classes } = ownerState; const slots = { root: ['root'], @@ -23,7 +23,7 @@ const useUtilityClasses = (styleProps) => { return composeClasses(slots, getMasonryUtilityClass, classes); }; -export const style = ({ styleProps, theme }) => { +export const style = ({ ownerState, theme }) => { let styles = { display: 'grid', gridAutoRows: 0, @@ -36,12 +36,12 @@ export const style = ({ styleProps, theme }) => { const base = {}; Object.keys(theme.breakpoints.values).forEach((breakpoint) => { - if (styleProps.spacing[breakpoint] != null) { + if (ownerState.spacing[breakpoint] != null) { base[breakpoint] = true; } }); - const spacingValues = resolveBreakpointValues({ values: styleProps.spacing, base }); + const spacingValues = resolveBreakpointValues({ values: ownerState.spacing, base }); const transformer = createUnarySpacing(theme); const spacingStyleFromPropValue = (propValue) => { return { @@ -54,7 +54,7 @@ export const style = ({ styleProps, theme }) => { ...handleBreakpoints({ theme }, spacingValues, spacingStyleFromPropValue), }; - const columnValues = resolveBreakpointValues({ values: styleProps.columns, base }); + const columnValues = resolveBreakpointValues({ values: ownerState.columns, base }); const columnStyleFromPropValue = (propValue) => { return { gridTemplateColumns: `repeat(${propValue}, 1fr)`, @@ -81,8 +81,8 @@ const Masonry = React.forwardRef(function Masonry(inProps, ref) { }); const { children, className, component = 'div', columns = 4, spacing = 1, ...other } = props; - const styleProps = { ...props, spacing, columns }; - const classes = useUtilityClasses(styleProps); + const ownerState = { ...props, spacing, columns }; + const classes = useUtilityClasses(ownerState); const contextValue = React.useMemo(() => ({ spacing }), [spacing]); @@ -92,7 +92,7 @@ const Masonry = React.forwardRef(function Masonry(inProps, ref) { as={component} className={clsx(classes.root, className)} ref={ref} - styleProps={styleProps} + ownerState={ownerState} {...other} > {children} diff --git a/packages/material-ui-lab/src/Masonry/Masonry.test.js b/packages/material-ui-lab/src/Masonry/Masonry.test.js index 70bbe5ec8a0cb4..48d1ab18104dab 100644 --- a/packages/material-ui-lab/src/Masonry/Masonry.test.js +++ b/packages/material-ui-lab/src/Masonry/Masonry.test.js @@ -30,7 +30,7 @@ describe('', () => { it('should render with correct default styles', () => { expect( style({ - styleProps: { + ownerState: { columns: 4, spacing: 1, }, @@ -52,7 +52,7 @@ describe('', () => { it('should render with column gap responsive to breakpoints', () => { expect( style({ - styleProps: { + ownerState: { columns: 4, spacing: { xs: 1, sm: 2, md: 3 }, }, @@ -84,7 +84,7 @@ describe('', () => { it('should render with grid-template-columns responsive to breakpoints', () => { expect( style({ - styleProps: { + ownerState: { columns: { xs: 3, sm: 5, md: 7 }, spacing: 1, }, diff --git a/packages/material-ui-lab/src/MasonryItem/MasonryItem.js b/packages/material-ui-lab/src/MasonryItem/MasonryItem.js index c09033e5cb5a32..56c3f31beb049e 100644 --- a/packages/material-ui-lab/src/MasonryItem/MasonryItem.js +++ b/packages/material-ui-lab/src/MasonryItem/MasonryItem.js @@ -21,8 +21,8 @@ const MockResizeObserver = () => { }; }; -const useUtilityClasses = (styleProps) => { - const { classes } = styleProps; +const useUtilityClasses = (ownerState) => { + const { classes } = ownerState; const slots = { root: ['root'], @@ -31,33 +31,33 @@ const useUtilityClasses = (styleProps) => { return composeClasses(slots, getMasonryItemUtilityClass, classes); }; -export const style = ({ styleProps, theme }) => { +export const style = ({ ownerState, theme }) => { let styles = { width: '100%', '& > *': { // all contents should have a width of 100% width: '100%', boxSizing: 'inherit', - ...(styleProps.isSSR && { height: '100%' }), + ...(ownerState.isSSR && { height: '100%' }), }, - visibility: styleProps.height ? 'visible' : 'hidden', - gridColumnEnd: `span ${styleProps.columnSpan}`, + visibility: ownerState.height ? 'visible' : 'hidden', + gridColumnEnd: `span ${ownerState.columnSpan}`, boxSizing: 'inherit', }; const base = {}; Object.keys(theme.breakpoints.values).forEach((breakpoint) => { - if (styleProps.spacing[breakpoint] != null) { + if (ownerState.spacing[breakpoint] != null) { base[breakpoint] = true; } }); - const spacingValues = resolveBreakpointValues({ values: styleProps.spacing, base }); + const spacingValues = resolveBreakpointValues({ values: ownerState.spacing, base }); const transformer = createUnarySpacing(theme); const styleFromPropValue = (propValue) => { - const gap = styleProps.height ? Number(getValue(transformer, propValue).replace('px', '')) : 0; + const gap = ownerState.height ? Number(getValue(transformer, propValue).replace('px', '')) : 0; // For lazy-loaded images to load properly, masonry item should take up space greater than 1px. // Taking into account a row gap of 2px, rowSpan should at least be 2. - const rowSpan = styleProps.height ? Math.ceil((styleProps.height + gap) / 2) : 2; + const rowSpan = ownerState.height ? Math.ceil((ownerState.height + gap) / 2) : 2; return { gridRowEnd: `span ${rowSpan}`, paddingBottom: gap - 2, @@ -90,7 +90,7 @@ const MasonryItem = React.forwardRef(function MasonryItem(inProps, ref) { const [height, setHeight] = React.useState(defaultHeight); - const styleProps = { + const ownerState = { ...props, isSSR, spacing, @@ -98,7 +98,7 @@ const MasonryItem = React.forwardRef(function MasonryItem(inProps, ref) { height: height < 0 ? 0 : height, // MasonryItems to which negative or zero height is passed will be hidden }; - const classes = useUtilityClasses(styleProps); + const classes = useUtilityClasses(ownerState); const resizeObserver = React.useRef(null); React.useEffect(() => { // do not create a resize observer in case of SSR masonry @@ -125,7 +125,7 @@ const MasonryItem = React.forwardRef(function MasonryItem(inProps, ref) { as={component} className={clsx(classes.root, className)} ref={handleRef} - styleProps={styleProps} + ownerState={ownerState} {...other} > {React.Children.only(children)} diff --git a/packages/material-ui-lab/src/MasonryItem/MasonryItem.test.js b/packages/material-ui-lab/src/MasonryItem/MasonryItem.test.js index b682aef56e4093..0915133bcf01d0 100644 --- a/packages/material-ui-lab/src/MasonryItem/MasonryItem.test.js +++ b/packages/material-ui-lab/src/MasonryItem/MasonryItem.test.js @@ -43,7 +43,7 @@ describe('', () => { it('should render with padding bottom and grid-row-end responsive to breakpoints', () => { expect( style({ - styleProps: { + ownerState: { height: 100, columnSpan: 1, spacing: { xs: 1, sm: 2, md: 3 }, @@ -77,7 +77,7 @@ describe('', () => { it('should render with given column span', () => { expect( style({ - styleProps: { + ownerState: { height: 100, columnSpan: 2, spacing: 1, From e8c0ae4bf55ece7c605ca39eccdf6d6548337032 Mon Sep 17 00:00:00 2001 From: Benny Joo Date: Thu, 19 Aug 2021 10:21:30 +0100 Subject: [PATCH 47/59] Add a comment explaining mock resize observer --- packages/material-ui-lab/src/MasonryItem/MasonryItem.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/material-ui-lab/src/MasonryItem/MasonryItem.js b/packages/material-ui-lab/src/MasonryItem/MasonryItem.js index 56c3f31beb049e..1f97720f84bbd0 100644 --- a/packages/material-ui-lab/src/MasonryItem/MasonryItem.js +++ b/packages/material-ui-lab/src/MasonryItem/MasonryItem.js @@ -13,6 +13,7 @@ import { styled, useThemeProps } from '@material-ui/core/styles'; import { getMasonryItemUtilityClass } from './masonryItemClasses'; import MasonryContext from '../Masonry/MasonryContext'; +// dummy resize observer used to prevent crash for old browsers that do not support ResizeObserver API(e.g., 11IE) const MockResizeObserver = () => { return { observe: () => {}, @@ -110,7 +111,7 @@ const MasonryItem = React.forwardRef(function MasonryItem(inProps, ref) { setHeight(item.contentRect.height); }); } catch (err) { - resizeObserver.current = MockResizeObserver(); // Prevent crash for old browsers (e..g., 11IE) + resizeObserver.current = MockResizeObserver(); } const item = masonryItemRef.current.firstChild; resizeObserver.current.observe(item); From 60e1aa99d65f58892ddf5c75dcf577317a5a26b4 Mon Sep 17 00:00:00 2001 From: Benny Joo Date: Thu, 19 Aug 2021 10:27:23 +0100 Subject: [PATCH 48/59] Delete regression tests as they are same as demos --- .../fixtures/Masonry/BasicMasonry.js | 21 --------- .../fixtures/Masonry/DiffColMasonry.js | 44 ------------------- .../fixtures/Masonry/ResponsiveMasonry.js | 25 ----------- .../fixtures/Masonry/SSRMasonry.js | 20 --------- 4 files changed, 110 deletions(-) delete mode 100644 test/regressions/fixtures/Masonry/BasicMasonry.js delete mode 100644 test/regressions/fixtures/Masonry/DiffColMasonry.js delete mode 100644 test/regressions/fixtures/Masonry/ResponsiveMasonry.js delete mode 100644 test/regressions/fixtures/Masonry/SSRMasonry.js diff --git a/test/regressions/fixtures/Masonry/BasicMasonry.js b/test/regressions/fixtures/Masonry/BasicMasonry.js deleted file mode 100644 index f8674fc57673b2..00000000000000 --- a/test/regressions/fixtures/Masonry/BasicMasonry.js +++ /dev/null @@ -1,21 +0,0 @@ -/* eslint-disable @typescript-eslint/no-use-before-define */ -import * as React from 'react'; -import Box from '@material-ui/core/Box'; -import Masonry from '@material-ui/lab/Masonry'; -import MasonryItem from '@material-ui/lab/MasonryItem'; - -export default function BasicMasonry() { - return ( - - {heights.map((height, index) => ( - - - {index + 1} - - - ))} - - ); -} - -const heights = [150, 30, 90, 70, 110, 150, 130, 80, 50, 90, 100, 150, 30, 50, 80]; diff --git a/test/regressions/fixtures/Masonry/DiffColMasonry.js b/test/regressions/fixtures/Masonry/DiffColMasonry.js deleted file mode 100644 index f4d216f0f416d6..00000000000000 --- a/test/regressions/fixtures/Masonry/DiffColMasonry.js +++ /dev/null @@ -1,44 +0,0 @@ -/* eslint-disable @typescript-eslint/no-use-before-define */ -import * as React from 'react'; -import Box from '@material-ui/core/Box'; -import Masonry from '@material-ui/lab/Masonry'; -import MasonryItem from '@material-ui/lab/MasonryItem'; - -export default function DiffColSizeMasonry() { - return ( - - {itemData.map((item, index) => ( - - - {index + 1} - - - ))} - - ); -} - -const itemData = [ - { height: 150 }, - { height: 30 }, - { height: 90, span: 2 }, - { height: 110 }, - { height: 150 }, - { height: 150 }, - { height: 130, span: 2 }, - { height: 80, span: 2 }, - { height: 50 }, - { height: 90 }, - { height: 100, span: 2 }, - { height: 150 }, - { height: 50 }, - { height: 50, span: 2 }, - { height: 50 }, -]; diff --git a/test/regressions/fixtures/Masonry/ResponsiveMasonry.js b/test/regressions/fixtures/Masonry/ResponsiveMasonry.js deleted file mode 100644 index 05d8e8b00338d1..00000000000000 --- a/test/regressions/fixtures/Masonry/ResponsiveMasonry.js +++ /dev/null @@ -1,25 +0,0 @@ -/* eslint-disable @typescript-eslint/no-use-before-define */ -import * as React from 'react'; -import Box from '@material-ui/core/Box'; -import Masonry from '@material-ui/lab/Masonry'; -import MasonryItem from '@material-ui/lab/MasonryItem'; - -export default function ResponsiveColumns() { - return ( - - {heights.map((height, index) => ( - - - {index + 1} - - - ))} - - ); -} - -const heights = [150, 30, 90, 70, 110, 150, 130, 80, 50, 90, 100, 150, 30, 50, 80]; diff --git a/test/regressions/fixtures/Masonry/SSRMasonry.js b/test/regressions/fixtures/Masonry/SSRMasonry.js deleted file mode 100644 index 2a56136da40f38..00000000000000 --- a/test/regressions/fixtures/Masonry/SSRMasonry.js +++ /dev/null @@ -1,20 +0,0 @@ -/* eslint-disable @typescript-eslint/no-use-before-define */ -import * as React from 'react'; -import Box from '@material-ui/core/Box'; -import Masonry from '@material-ui/lab/Masonry'; -import MasonryItem from '@material-ui/lab/MasonryItem'; - -export default function SSRMasonry() { - return ( - - {heights.map((height, index) => ( - - - {index + 1} - - - ))} - - ); -} -const heights = [150, 30, 90, 70, 110, 150, 130, 80, 50, 90, 100, 150, 30, 50, 80]; From ae41204d6443b4e925f12fb5dbe9e8a48d363337 Mon Sep 17 00:00:00 2001 From: Benny Joo Date: Thu, 19 Aug 2021 12:03:32 +0100 Subject: [PATCH 49/59] Change docs prop name to demoComponents --- docs/pages/components/masonry.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/pages/components/masonry.js b/docs/pages/components/masonry.js index afdce583821302..2a4f8621fd449b 100644 --- a/docs/pages/components/masonry.js +++ b/docs/pages/components/masonry.js @@ -3,9 +3,9 @@ import MarkdownDocs from 'docs/src/modules/components/MarkdownDocs'; import { demos, docs, - requireDemo, + demoComponents, } from 'docs/src/pages/components/masonry/masonry.md?@material-ui/markdown'; export default function Page() { - return ; + return ; } From c61f7ec3e121099d2ed84deb4489c0aaf9102aa2 Mon Sep 17 00:00:00 2001 From: Benny Joo Date: Thu, 19 Aug 2021 13:32:24 +0100 Subject: [PATCH 50/59] Order components alphabetically in nav bar --- docs/src/pages.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/pages.ts b/docs/src/pages.ts index 9b80f8770be1d9..37f70258c1adbe 100644 --- a/docs/src/pages.ts +++ b/docs/src/pages.ts @@ -187,10 +187,10 @@ const pages: readonly MuiPage[] = [ { pathname: '/components/time-picker' }, ], }, + { pathname: '/components/masonry' }, { pathname: '/components/timeline' }, { pathname: '/components/trap-focus' }, { pathname: '/components/tree-view' }, - { pathname: '/components/masonry' }, ], }, ], From 33703f578f4ad36365babc00209b2cb85e733579 Mon Sep 17 00:00:00 2001 From: Benny Joo Date: Thu, 19 Aug 2021 14:06:27 +0100 Subject: [PATCH 51/59] Put Masonry above timeline in translations doc --- docs/translations/translations.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/translations/translations.json b/docs/translations/translations.json index b085c5eb9ac5cd..7b5ba99ae91880 100644 --- a/docs/translations/translations.json +++ b/docs/translations/translations.json @@ -260,10 +260,10 @@ "/components/date-range-picker": "Date Range Picker ⚡️", "/components/date-time-picker": "Date Time Picker", "/components/time-picker": "Time Picker", + "/components/masonry": "Masonry", "/components/timeline": "Timeline", "/components/trap-focus": "Trap Focus", "/components/tree-view": "Tree View", - "/components/masonry": "Masonry", "/api-docs": "Component API", "/system": "System", "/system/basics": "Basics", From b1fc9f3b8932a2fa4402a68b05bf73d6224eac13 Mon Sep 17 00:00:00 2001 From: Benny Joo Date: Mon, 23 Aug 2021 13:52:29 +0100 Subject: [PATCH 52/59] Bring constant variable to the top for some demos --- docs/src/pages/components/masonry/BasicMasonry.js | 5 ++--- docs/src/pages/components/masonry/BasicMasonry.tsx | 5 ++--- docs/src/pages/components/masonry/FixedColumns.js | 5 ++--- docs/src/pages/components/masonry/FixedColumns.tsx | 5 ++--- docs/src/pages/components/masonry/FixedSpacing.js | 5 ++--- docs/src/pages/components/masonry/FixedSpacing.tsx | 5 ++--- docs/src/pages/components/masonry/ResponsiveColumns.js | 5 ++--- docs/src/pages/components/masonry/ResponsiveColumns.tsx | 5 ++--- docs/src/pages/components/masonry/ResponsiveSpacing.js | 5 ++--- docs/src/pages/components/masonry/ResponsiveSpacing.tsx | 5 ++--- docs/src/pages/components/masonry/SSRMasonry.js | 5 ++--- docs/src/pages/components/masonry/SSRMasonry.tsx | 5 ++--- 12 files changed, 24 insertions(+), 36 deletions(-) diff --git a/docs/src/pages/components/masonry/BasicMasonry.js b/docs/src/pages/components/masonry/BasicMasonry.js index 525bc4a19dacb7..803ca3b1380160 100644 --- a/docs/src/pages/components/masonry/BasicMasonry.js +++ b/docs/src/pages/components/masonry/BasicMasonry.js @@ -1,9 +1,10 @@ -/* eslint-disable @typescript-eslint/no-use-before-define */ import * as React from 'react'; import Box from '@material-ui/core/Box'; import Masonry from '@material-ui/lab/Masonry'; import MasonryItem from '@material-ui/lab/MasonryItem'; +const heights = [150, 30, 90, 70, 110, 150, 130, 80, 50, 90, 100, 150, 30, 50, 80]; + export default function BasicMasonry() { return ( @@ -26,5 +27,3 @@ export default function BasicMasonry() { ); } - -const heights = [150, 30, 90, 70, 110, 150, 130, 80, 50, 90, 100, 150, 30, 50, 80]; diff --git a/docs/src/pages/components/masonry/BasicMasonry.tsx b/docs/src/pages/components/masonry/BasicMasonry.tsx index 525bc4a19dacb7..803ca3b1380160 100644 --- a/docs/src/pages/components/masonry/BasicMasonry.tsx +++ b/docs/src/pages/components/masonry/BasicMasonry.tsx @@ -1,9 +1,10 @@ -/* eslint-disable @typescript-eslint/no-use-before-define */ import * as React from 'react'; import Box from '@material-ui/core/Box'; import Masonry from '@material-ui/lab/Masonry'; import MasonryItem from '@material-ui/lab/MasonryItem'; +const heights = [150, 30, 90, 70, 110, 150, 130, 80, 50, 90, 100, 150, 30, 50, 80]; + export default function BasicMasonry() { return ( @@ -26,5 +27,3 @@ export default function BasicMasonry() { ); } - -const heights = [150, 30, 90, 70, 110, 150, 130, 80, 50, 90, 100, 150, 30, 50, 80]; diff --git a/docs/src/pages/components/masonry/FixedColumns.js b/docs/src/pages/components/masonry/FixedColumns.js index bca583bc53f288..4ccc5c1243ecd0 100644 --- a/docs/src/pages/components/masonry/FixedColumns.js +++ b/docs/src/pages/components/masonry/FixedColumns.js @@ -1,9 +1,10 @@ -/* eslint-disable @typescript-eslint/no-use-before-define */ import * as React from 'react'; import Box from '@material-ui/core/Box'; import Masonry from '@material-ui/lab/Masonry'; import MasonryItem from '@material-ui/lab/MasonryItem'; +const heights = [150, 30, 90, 70, 90, 100, 150, 30, 50, 80]; + export default function FixedColumns() { return ( @@ -26,5 +27,3 @@ export default function FixedColumns() { ); } - -const heights = [150, 30, 90, 70, 90, 100, 150, 30, 50, 80]; diff --git a/docs/src/pages/components/masonry/FixedColumns.tsx b/docs/src/pages/components/masonry/FixedColumns.tsx index bca583bc53f288..4ccc5c1243ecd0 100644 --- a/docs/src/pages/components/masonry/FixedColumns.tsx +++ b/docs/src/pages/components/masonry/FixedColumns.tsx @@ -1,9 +1,10 @@ -/* eslint-disable @typescript-eslint/no-use-before-define */ import * as React from 'react'; import Box from '@material-ui/core/Box'; import Masonry from '@material-ui/lab/Masonry'; import MasonryItem from '@material-ui/lab/MasonryItem'; +const heights = [150, 30, 90, 70, 90, 100, 150, 30, 50, 80]; + export default function FixedColumns() { return ( @@ -26,5 +27,3 @@ export default function FixedColumns() { ); } - -const heights = [150, 30, 90, 70, 90, 100, 150, 30, 50, 80]; diff --git a/docs/src/pages/components/masonry/FixedSpacing.js b/docs/src/pages/components/masonry/FixedSpacing.js index 3976075842e3c6..58489b4c539738 100644 --- a/docs/src/pages/components/masonry/FixedSpacing.js +++ b/docs/src/pages/components/masonry/FixedSpacing.js @@ -1,9 +1,10 @@ -/* eslint-disable @typescript-eslint/no-use-before-define */ import * as React from 'react'; import Box from '@material-ui/core/Box'; import Masonry from '@material-ui/lab/Masonry'; import MasonryItem from '@material-ui/lab/MasonryItem'; +const heights = [150, 30, 90, 70, 90, 100, 150, 30, 50, 80]; + export default function FixedSpacing() { return ( @@ -26,5 +27,3 @@ export default function FixedSpacing() { ); } - -const heights = [150, 30, 90, 70, 90, 100, 150, 30, 50, 80]; diff --git a/docs/src/pages/components/masonry/FixedSpacing.tsx b/docs/src/pages/components/masonry/FixedSpacing.tsx index 3976075842e3c6..58489b4c539738 100644 --- a/docs/src/pages/components/masonry/FixedSpacing.tsx +++ b/docs/src/pages/components/masonry/FixedSpacing.tsx @@ -1,9 +1,10 @@ -/* eslint-disable @typescript-eslint/no-use-before-define */ import * as React from 'react'; import Box from '@material-ui/core/Box'; import Masonry from '@material-ui/lab/Masonry'; import MasonryItem from '@material-ui/lab/MasonryItem'; +const heights = [150, 30, 90, 70, 90, 100, 150, 30, 50, 80]; + export default function FixedSpacing() { return ( @@ -26,5 +27,3 @@ export default function FixedSpacing() { ); } - -const heights = [150, 30, 90, 70, 90, 100, 150, 30, 50, 80]; diff --git a/docs/src/pages/components/masonry/ResponsiveColumns.js b/docs/src/pages/components/masonry/ResponsiveColumns.js index e92bbd2998e15f..b920086df750be 100644 --- a/docs/src/pages/components/masonry/ResponsiveColumns.js +++ b/docs/src/pages/components/masonry/ResponsiveColumns.js @@ -1,9 +1,10 @@ -/* eslint-disable @typescript-eslint/no-use-before-define */ import * as React from 'react'; import Box from '@material-ui/core/Box'; import Masonry from '@material-ui/lab/Masonry'; import MasonryItem from '@material-ui/lab/MasonryItem'; +const heights = [150, 30, 90, 70, 90, 100, 150, 30, 50, 80]; + export default function ResponsiveColumns() { return ( @@ -26,5 +27,3 @@ export default function ResponsiveColumns() { ); } - -const heights = [150, 30, 90, 70, 90, 100, 150, 30, 50, 80]; diff --git a/docs/src/pages/components/masonry/ResponsiveColumns.tsx b/docs/src/pages/components/masonry/ResponsiveColumns.tsx index e92bbd2998e15f..b920086df750be 100644 --- a/docs/src/pages/components/masonry/ResponsiveColumns.tsx +++ b/docs/src/pages/components/masonry/ResponsiveColumns.tsx @@ -1,9 +1,10 @@ -/* eslint-disable @typescript-eslint/no-use-before-define */ import * as React from 'react'; import Box from '@material-ui/core/Box'; import Masonry from '@material-ui/lab/Masonry'; import MasonryItem from '@material-ui/lab/MasonryItem'; +const heights = [150, 30, 90, 70, 90, 100, 150, 30, 50, 80]; + export default function ResponsiveColumns() { return ( @@ -26,5 +27,3 @@ export default function ResponsiveColumns() { ); } - -const heights = [150, 30, 90, 70, 90, 100, 150, 30, 50, 80]; diff --git a/docs/src/pages/components/masonry/ResponsiveSpacing.js b/docs/src/pages/components/masonry/ResponsiveSpacing.js index 9d5e0a156b2691..3e65472251b86d 100644 --- a/docs/src/pages/components/masonry/ResponsiveSpacing.js +++ b/docs/src/pages/components/masonry/ResponsiveSpacing.js @@ -1,9 +1,10 @@ -/* eslint-disable @typescript-eslint/no-use-before-define */ import * as React from 'react'; import Box from '@material-ui/core/Box'; import Masonry from '@material-ui/lab/Masonry'; import MasonryItem from '@material-ui/lab/MasonryItem'; +const heights = [150, 30, 90, 70, 90, 100, 150, 30, 50, 80]; + export default function ResponsiveSpacing() { return ( @@ -26,5 +27,3 @@ export default function ResponsiveSpacing() { ); } - -const heights = [150, 30, 90, 70, 90, 100, 150, 30, 50, 80]; diff --git a/docs/src/pages/components/masonry/ResponsiveSpacing.tsx b/docs/src/pages/components/masonry/ResponsiveSpacing.tsx index 9d5e0a156b2691..3e65472251b86d 100644 --- a/docs/src/pages/components/masonry/ResponsiveSpacing.tsx +++ b/docs/src/pages/components/masonry/ResponsiveSpacing.tsx @@ -1,9 +1,10 @@ -/* eslint-disable @typescript-eslint/no-use-before-define */ import * as React from 'react'; import Box from '@material-ui/core/Box'; import Masonry from '@material-ui/lab/Masonry'; import MasonryItem from '@material-ui/lab/MasonryItem'; +const heights = [150, 30, 90, 70, 90, 100, 150, 30, 50, 80]; + export default function ResponsiveSpacing() { return ( @@ -26,5 +27,3 @@ export default function ResponsiveSpacing() { ); } - -const heights = [150, 30, 90, 70, 90, 100, 150, 30, 50, 80]; diff --git a/docs/src/pages/components/masonry/SSRMasonry.js b/docs/src/pages/components/masonry/SSRMasonry.js index 4cc1ba731be40f..32682ed8484f7d 100644 --- a/docs/src/pages/components/masonry/SSRMasonry.js +++ b/docs/src/pages/components/masonry/SSRMasonry.js @@ -1,9 +1,10 @@ -/* eslint-disable @typescript-eslint/no-use-before-define */ import * as React from 'react'; import Box from '@material-ui/core/Box'; import Masonry from '@material-ui/lab/Masonry'; import MasonryItem from '@material-ui/lab/MasonryItem'; +const heights = [150, 30, 90, 70, 90, 100, 150, 30, 50, 80]; + export default function SSRMasonry() { return ( @@ -25,5 +26,3 @@ export default function SSRMasonry() { ); } - -const heights = [150, 30, 90, 70, 90, 100, 150, 30, 50, 80]; diff --git a/docs/src/pages/components/masonry/SSRMasonry.tsx b/docs/src/pages/components/masonry/SSRMasonry.tsx index 4cc1ba731be40f..32682ed8484f7d 100644 --- a/docs/src/pages/components/masonry/SSRMasonry.tsx +++ b/docs/src/pages/components/masonry/SSRMasonry.tsx @@ -1,9 +1,10 @@ -/* eslint-disable @typescript-eslint/no-use-before-define */ import * as React from 'react'; import Box from '@material-ui/core/Box'; import Masonry from '@material-ui/lab/Masonry'; import MasonryItem from '@material-ui/lab/MasonryItem'; +const heights = [150, 30, 90, 70, 90, 100, 150, 30, 50, 80]; + export default function SSRMasonry() { return ( @@ -25,5 +26,3 @@ export default function SSRMasonry() { ); } - -const heights = [150, 30, 90, 70, 90, 100, 150, 30, 50, 80]; From a4f10c8db0c9317aa1154b09cbc90b828a05ef98 Mon Sep 17 00:00:00 2001 From: Benny Joo Date: Tue, 24 Aug 2021 08:55:40 +0100 Subject: [PATCH 53/59] Prevent use of negative value for padding --- packages/material-ui-lab/src/MasonryItem/MasonryItem.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/material-ui-lab/src/MasonryItem/MasonryItem.js b/packages/material-ui-lab/src/MasonryItem/MasonryItem.js index 1f97720f84bbd0..cd0065049258a0 100644 --- a/packages/material-ui-lab/src/MasonryItem/MasonryItem.js +++ b/packages/material-ui-lab/src/MasonryItem/MasonryItem.js @@ -61,7 +61,7 @@ export const style = ({ ownerState, theme }) => { const rowSpan = ownerState.height ? Math.ceil((ownerState.height + gap) / 2) : 2; return { gridRowEnd: `span ${rowSpan}`, - paddingBottom: gap - 2, + paddingBottom: gap === 0 ? 0 : gap - 2, }; }; styles = { ...styles, ...handleBreakpoints({ theme }, spacingValues, styleFromPropValue) }; From b273cda41528aedb7120eb1b5ff81307182d099a Mon Sep 17 00:00:00 2001 From: Benny Joo Date: Tue, 24 Aug 2021 10:42:27 +0100 Subject: [PATCH 54/59] Display warning when masonry goess beyond 2000px --- docs/src/pages/components/masonry/masonry.md | 4 +++ .../material-ui-lab/src/Masonry/Masonry.js | 29 +++++++++++++++++-- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/docs/src/pages/components/masonry/masonry.md b/docs/src/pages/components/masonry/masonry.md index 17887a66640f01..b0f2050d896697 100644 --- a/docs/src/pages/components/masonry/masonry.md +++ b/docs/src/pages/components/masonry/masonry.md @@ -14,6 +14,10 @@ If a row is already filled with the specified number of columns, the next item s {{"component": "modules/components/ComponentLinkHeader.js", "design": true}} +> Warning: This component has been developed with the use of CSS Grid Level 2. Unfortunately, Chrome only allows to render at most 1000 rows for each grid. +> Hence, with the current design, a masonry component has the maximum height of 2,000px, and the items beyond this height will fail to be rendered. +> An [issue](<[www.google.com](https://github.com/mui-org/material-ui/issues/27934)>) has been created on Github to gather workarounds for this limitation. It is worth noting that this limitation does not exist on Firefox or Safari. + ## Basic masonry `` is a container for one or more ``s. `` can receive any element including `
` and ``. Also, it is important to note that each `` accepts only one element. diff --git a/packages/material-ui-lab/src/Masonry/Masonry.js b/packages/material-ui-lab/src/Masonry/Masonry.js index d5525c9b0b5e22..656aa2ffb3b63d 100644 --- a/packages/material-ui-lab/src/Masonry/Masonry.js +++ b/packages/material-ui-lab/src/Masonry/Masonry.js @@ -7,7 +7,7 @@ import { handleBreakpoints, unstable_resolveBreakpointValues as resolveBreakpointValues, } from '@material-ui/system'; -import { deepmerge } from '@material-ui/utils'; +import { deepmerge, unstable_useForkRef as useForkRef } from '@material-ui/utils'; import { unstable_composeClasses as composeClasses } from '@material-ui/unstyled'; import { styled, useThemeProps } from '@material-ui/core/styles'; import { getMasonryUtilityClass } from './masonryClasses'; @@ -80,18 +80,43 @@ const Masonry = React.forwardRef(function Masonry(inProps, ref) { name: 'MuiMasonry', }); + const masonryRef = React.useRef(); const { children, className, component = 'div', columns = 4, spacing = 1, ...other } = props; const ownerState = { ...props, spacing, columns }; const classes = useUtilityClasses(ownerState); const contextValue = React.useMemo(() => ({ spacing }), [spacing]); + let didWarn = false; + React.useEffect(() => { + // scroller always appears when masonry's height goes beyond 2,000px on Chrome + const handleScroll = () => { + if (masonryRef.current.clientHeight === 1998 && !didWarn) { + console.warn( + [ + 'Material-UI: The Masonry can have the maximum height of 2,000px on Chrome browser.', + 'Items that go beyond this height fail to be rendered on Chrome browser.', + 'You can find more in this open issue: https://github.com/mui-org/material-ui/issues/27934', + ].join('\n'), + ); + + // eslint-disable-next-line react-hooks/exhaustive-deps + didWarn = true; + } + }; + const container = masonryRef.current; + container.addEventListener('scroll', handleScroll); + return () => { + container.removeEventListener('scroll', handleScroll); + }; + }, []); + const handleRef = useForkRef(ref, masonryRef); return ( From 301638ccdfd67fe90d02e24a8294c1f7cc274c0e Mon Sep 17 00:00:00 2001 From: Benny Joo Date: Tue, 24 Aug 2021 12:05:36 +0100 Subject: [PATCH 55/59] Use inline style when not dealing with responsive spacing values --- .../src/MasonryItem/MasonryItem.js | 53 ++++++++++++------- .../src/MasonryItem/MasonryItem.test.js | 2 - 2 files changed, 34 insertions(+), 21 deletions(-) diff --git a/packages/material-ui-lab/src/MasonryItem/MasonryItem.js b/packages/material-ui-lab/src/MasonryItem/MasonryItem.js index cd0065049258a0..1ede57d524620e 100644 --- a/packages/material-ui-lab/src/MasonryItem/MasonryItem.js +++ b/packages/material-ui-lab/src/MasonryItem/MasonryItem.js @@ -10,6 +10,7 @@ import { import { unstable_useForkRef as useForkRef } from '@material-ui/utils'; import { unstable_composeClasses as composeClasses } from '@material-ui/unstyled'; import { styled, useThemeProps } from '@material-ui/core/styles'; +import useTheme from 'packages/material-ui-system/src/useTheme'; import { getMasonryItemUtilityClass } from './masonryItemClasses'; import MasonryContext from '../Masonry/MasonryContext'; @@ -46,26 +47,29 @@ export const style = ({ ownerState, theme }) => { boxSizing: 'inherit', }; - const base = {}; - Object.keys(theme.breakpoints.values).forEach((breakpoint) => { - if (ownerState.spacing[breakpoint] != null) { - base[breakpoint] = true; - } - }); - const spacingValues = resolveBreakpointValues({ values: ownerState.spacing, base }); - const transformer = createUnarySpacing(theme); - const styleFromPropValue = (propValue) => { - const gap = ownerState.height ? Number(getValue(transformer, propValue).replace('px', '')) : 0; - // For lazy-loaded images to load properly, masonry item should take up space greater than 1px. - // Taking into account a row gap of 2px, rowSpan should at least be 2. - const rowSpan = ownerState.height ? Math.ceil((ownerState.height + gap) / 2) : 2; - return { - gridRowEnd: `span ${rowSpan}`, - paddingBottom: gap === 0 ? 0 : gap - 2, + if (Array.isArray(ownerState.spacing) || typeof ownerState.spacing === 'object') { + const base = {}; + Object.keys(theme.breakpoints.values).forEach((breakpoint) => { + if (ownerState.spacing[breakpoint] != null) { + base[breakpoint] = true; + } + }); + const spacingValues = resolveBreakpointValues({ values: ownerState.spacing, base }); + const transformer = createUnarySpacing(theme); + const styleFromPropValue = (propValue) => { + const gap = ownerState.height + ? Number(getValue(transformer, propValue).replace('px', '')) + : 0; + // For lazy-loaded images to load properly, masonry item should take up space greater than 1px. + // Taking into account a row gap of 2px, rowSpan should at least be 2. + const rowSpan = ownerState.height ? Math.ceil((ownerState.height + gap) / 2) : 2; + return { + gridRowEnd: `span ${rowSpan}`, + paddingBottom: gap === 0 ? 0 : gap - 2, + }; }; - }; - styles = { ...styles, ...handleBreakpoints({ theme }, spacingValues, styleFromPropValue) }; - + styles = { ...styles, ...handleBreakpoints({ theme }, spacingValues, styleFromPropValue) }; + } return styles; }; @@ -121,12 +125,23 @@ const MasonryItem = React.forwardRef(function MasonryItem(inProps, ref) { }, [isSSR]); const handleRef = useForkRef(ref, masonryItemRef); + + const theme = useTheme(); + const styleProp = {}; + if (!Array.isArray(spacing) && typeof spacing !== 'object') { + const gap = height ? Number(theme.spacing(spacing).replace('px', '')) : 0; + const rowSpan = height ? Math.ceil((height + gap) / 2) : 2; + styleProp.gridRowEnd = `span ${rowSpan}`; + styleProp.paddingBottom = gap === 0 ? 0 : gap - 2; + } + return ( {React.Children.only(children)} diff --git a/packages/material-ui-lab/src/MasonryItem/MasonryItem.test.js b/packages/material-ui-lab/src/MasonryItem/MasonryItem.test.js index 0915133bcf01d0..c0173c121f63d6 100644 --- a/packages/material-ui-lab/src/MasonryItem/MasonryItem.test.js +++ b/packages/material-ui-lab/src/MasonryItem/MasonryItem.test.js @@ -85,8 +85,6 @@ describe('', () => { theme, }), ).to.deep.equal({ - gridRowEnd: `span ${Math.ceil((100 + Number(theme.spacing(1).replace('px', ''))) / 2)}`, - paddingBottom: Number(theme.spacing(1).replace('px', '')) - 2, width: '100%', [`& > *`]: { width: '100%', From c163bb20c3fec84498f65d310c09a066a326d339 Mon Sep 17 00:00:00 2001 From: Benny Joo Date: Tue, 24 Aug 2021 14:54:52 +0100 Subject: [PATCH 56/59] Improve docs --- docs/src/pages/components/masonry/masonry.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/src/pages/components/masonry/masonry.md b/docs/src/pages/components/masonry/masonry.md index b0f2050d896697..011186b12470cc 100644 --- a/docs/src/pages/components/masonry/masonry.md +++ b/docs/src/pages/components/masonry/masonry.md @@ -14,9 +14,9 @@ If a row is already filled with the specified number of columns, the next item s {{"component": "modules/components/ComponentLinkHeader.js", "design": true}} -> Warning: This component has been developed with the use of CSS Grid Level 2. Unfortunately, Chrome only allows to render at most 1000 rows for each grid. -> Hence, with the current design, a masonry component has the maximum height of 2,000px, and the items beyond this height will fail to be rendered. -> An [issue](<[www.google.com](https://github.com/mui-org/material-ui/issues/27934)>) has been created on Github to gather workarounds for this limitation. It is worth noting that this limitation does not exist on Firefox or Safari. +> Warning: This component has been developed with the use of CSS Grid Level 2. Unfortunately, Chrome only allows to render at most 1,000 rows for each grid. +> Hence, with the current design, a masonry component has a maximum height of 2,000px, and the items beyond this height will fail to be rendered. +> An [issue](https://github.com/mui-org/material-ui/issues/27934) has been created on GitHub to gather workarounds for this limitation. It is worth noting that this limitation does not exist on Firefox or Safari. ## Basic masonry From b3daef08b5be8eae31b14282d50621f31cf3e06a Mon Sep 17 00:00:00 2001 From: Benny Joo Date: Tue, 24 Aug 2021 18:37:48 +0100 Subject: [PATCH 57/59] Pass all tests --- packages/material-ui-lab/src/MasonryItem/MasonryItem.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/material-ui-lab/src/MasonryItem/MasonryItem.js b/packages/material-ui-lab/src/MasonryItem/MasonryItem.js index 1ede57d524620e..d69ac0eacce3f9 100644 --- a/packages/material-ui-lab/src/MasonryItem/MasonryItem.js +++ b/packages/material-ui-lab/src/MasonryItem/MasonryItem.js @@ -9,8 +9,7 @@ import { } from '@material-ui/system'; import { unstable_useForkRef as useForkRef } from '@material-ui/utils'; import { unstable_composeClasses as composeClasses } from '@material-ui/unstyled'; -import { styled, useThemeProps } from '@material-ui/core/styles'; -import useTheme from 'packages/material-ui-system/src/useTheme'; +import { styled, useThemeProps, useTheme } from '@material-ui/core/styles'; import { getMasonryItemUtilityClass } from './masonryItemClasses'; import MasonryContext from '../Masonry/MasonryContext'; From 2339dd0c32867396177acece6b745cdaf6f9e71e Mon Sep 17 00:00:00 2001 From: Benny Joo Date: Wed, 25 Aug 2021 08:37:55 +0100 Subject: [PATCH 58/59] Improve docs' descriptions of props --- docs/src/pages/components/masonry/masonry.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/docs/src/pages/components/masonry/masonry.md b/docs/src/pages/components/masonry/masonry.md index 011186b12470cc..e224503f0949be 100644 --- a/docs/src/pages/components/masonry/masonry.md +++ b/docs/src/pages/components/masonry/masonry.md @@ -20,20 +20,20 @@ If a row is already filled with the specified number of columns, the next item s ## Basic masonry -`` is a container for one or more ``s. `` can receive any element including `
` and ``. Also, it is important to note that each `` accepts only one element. +A simple example of a ``. `` is a container for one or more ``s. `` can receive any element including `
` and ``. Also, it is important to note that each `` accepts only one element. {{"demo": "pages/components/masonry/BasicMasonry.js", "bg": true}} ## Image masonry -`` orders its children by row. +This example demonstrates the use of `` for images. `` orders its children by row. If you would like to order images by column, you can use ``. More details on this component can be found in [Masonry Image List](/components/image-list/#masonry-image-list). {{"demo": "pages/components/masonry/ImageMasonry.js", "bg": true}} ## Columns -By passing `columns` to ``, you can configure the number of columns of your masonry. +This example demonstrates the use of the `columns` to configure the number of columns of a ``. {{"demo": "pages/components/masonry/FixedColumns.js", "bg": true}} @@ -43,7 +43,7 @@ By passing `columns` to ``, you can configure the number of columns o ## Spacing -By passing `spacing` to ``, you can configure the spacing between ``s. +This example demonstrates the use of the `spacing` to configure the spacing between ``s. It is important to note that `spacing` is a factor of the theme's spacing. {{"demo": "pages/components/masonry/FixedSpacing.js", "bg": true}} @@ -54,7 +54,7 @@ It is important to note that `spacing` is a factor of the theme's spacing. ## Column spanning -By passing `columnSpan` to ``, you can configure the number of columns taken up by each ``. +This example demonstrates the use of the `columnSpan` to configure the number of columns taken up by each ``. {{"demo": "pages/components/masonry/DiffColSizeMasonry.js", "bg": true}} @@ -64,6 +64,7 @@ However, you have to choose the value of `columnSpan` for each item carefully or ## Server-side rendering -By passing `defaultHeight` to ``, you can use server-side rendering. By default, `height: 100%` will be set to the content of ``. If you change this, there can be unwanted gap between `` and the content that you pass to it. +This example demonstrates the use of the `defaultHeight` to configure a fixed height of each ``. This is used for server-side rendering. +By default, `height: 100%` will be set to the content of ``. If you change this, there can be unwanted gap between `` and the content that you pass to it. {{"demo": "pages/components/masonry/SSRMasonry.js", "bg": true}} From 3bfc972a1ea57cd3fb334be865649a1a9c637af4 Mon Sep 17 00:00:00 2001 From: Benny Joo Date: Wed, 25 Aug 2021 10:27:00 +0100 Subject: [PATCH 59/59] Pass prettier test --- docs/src/pages/components/masonry/masonry.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/pages/components/masonry/masonry.md b/docs/src/pages/components/masonry/masonry.md index e224503f0949be..5be1e7de3e42cd 100644 --- a/docs/src/pages/components/masonry/masonry.md +++ b/docs/src/pages/components/masonry/masonry.md @@ -64,7 +64,7 @@ However, you have to choose the value of `columnSpan` for each item carefully or ## Server-side rendering -This example demonstrates the use of the `defaultHeight` to configure a fixed height of each ``. This is used for server-side rendering. +This example demonstrates the use of the `defaultHeight` to configure a fixed height of each ``. This is used for server-side rendering. By default, `height: 100%` will be set to the content of ``. If you change this, there can be unwanted gap between `` and the content that you pass to it. {{"demo": "pages/components/masonry/SSRMasonry.js", "bg": true}}