From 98d42baee240ada10ebc8470fb242552c3dc2ad4 Mon Sep 17 00:00:00 2001 From: Danilo Leal <67129314+danilo-leal@users.noreply.github.com> Date: Wed, 6 Mar 2024 14:02:35 -0300 Subject: [PATCH 01/17] [website] Update the Careers page role (#41384) --- docs/pages/blog/base-ui-2024-plans.md | 8 +++----- docs/pages/careers.tsx | 6 ------ 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/docs/pages/blog/base-ui-2024-plans.md b/docs/pages/blog/base-ui-2024-plans.md index cd4c4d88556739..ede47e3ba5611f 100644 --- a/docs/pages/blog/base-ui-2024-plans.md +++ b/docs/pages/blog/base-ui-2024-plans.md @@ -10,7 +10,7 @@ card: true The [story of Base UI](/blog/introducing-base-ui/) began several years ago—long before headless React component libraries skyrocketed in popularity—when we started to imagine a world in which Material UI could exist without Material Design. We're super excited to share that this dream is becoming a reality! -This year will see a lot of investment in Base UI as we expand the team ([we're hiring!](/careers/staff-ui-engineer-base-ui/)) and focus hard on a stable release (tentatively planned for late 2024), which will come full of new components, features, and improvements. +This year will see a lot of investment in Base UI as we expand the team and focus hard on a stable release (tentatively planned for late 2024), which will come full of new components, features, and improvements. Let's walk through some of the things we're cooking up. @@ -97,11 +97,9 @@ To acknowledge that Base UI has the potential to outgrow Material UI, we plan The new repository is live-[check it out on GitHub](https://github.com/mui/base-ui)! ::: -## Join us on the ride +## Help us on the ride -If you're passionate about extending the web platform with powerful, accessible, unstyled components, [we're hiring UI Engineers](/careers/staff-ui-engineer-base-ui/) to work on the Base UI team and help us accelerate its growth. - -Lastly, we'd love to hear your feedback. +We'd love to hear your feedback. The best place to share your ideas and requests is in [the GitHub repository](https://github.com/mui/base-ui/issues). Check out the existing issues and add your thoughts, and feel free to open your own issue if you don't see your concerns addressed elsewhere. diff --git a/docs/pages/careers.tsx b/docs/pages/careers.tsx index 74b7a5d7652524..dc3aeb54857037 100644 --- a/docs/pages/careers.tsx +++ b/docs/pages/careers.tsx @@ -158,12 +158,6 @@ const openRolesData = [ // 'You will strengthen the MUI X product, build ambitious and complex new features, work on strategic problems, and help grow adoption.', // url: '/careers/react-engineer-x/', // }, - { - title: 'Staff UI Engineer — Base UI', - description: - 'Research, build, document, and ship high-quality, unstyled UI components with a focus on a11y.', - url: '/careers/staff-ui-engineer-base-ui/', - }, ], }, { From 0faf15dabc34e83939ac8016b6b24454c8db11a2 Mon Sep 17 00:00:00 2001 From: Jan Potoms <2109932+Janpot@users.noreply.github.com> Date: Thu, 7 Mar 2024 10:22:29 +0100 Subject: [PATCH 02/17] [code-infra] Reduce concurrency of typescript:ci further (#41392) --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 3a44f43f2da443..a8bf59a37a6a95 100644 --- a/package.json +++ b/package.json @@ -76,7 +76,7 @@ "test:unit": "cross-env NODE_ENV=test mocha 'packages/**/*.test.{js,ts,tsx}' 'docs/**/*.test.{js,ts,tsx}'", "test:argos": "node ./scripts/pushArgos.mjs", "typescript": "lerna run --no-bail --parallel typescript", - "typescript:ci": "lerna run --concurrency 4 --no-bail --no-sort typescript", + "typescript:ci": "lerna run --concurrency 3 --no-bail --no-sort typescript", "validate-declarations": "tsx scripts/validateTypescriptDeclarations.mts", "generate-codeowners": "node scripts/generateCodeowners.mjs", "watch:zero": "nx run-many -t watch --projects=\"@pigmentcss/*\" --parallel" From a8ae789a3ea7a8af6567e832eaefa8ebac03840e Mon Sep 17 00:00:00 2001 From: Peter Wang Date: Thu, 7 Mar 2024 01:39:50 -0800 Subject: [PATCH 03/17] [docs] Fix v4 docs appearing in notifications (#41390) --- docs/notifications.json | 8 ++++---- docs/src/modules/components/Notifications.js | 5 +---- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/docs/notifications.json b/docs/notifications.json index 4b3431c0de8f13..2a2e958a32fb42 100644 --- a/docs/notifications.json +++ b/docs/notifications.json @@ -1,22 +1,22 @@ [ { "id": 68, - "title": "Check out Base UI today 💥", + "title": "Check out Base UI today 💥", "text": "Love Material UI, but don't need Material Design? Try Base UI, the new \"unstyled\" alternative. Read more in this announcement." }, { "id": 78, - "title": "MUI X v6.18.x and the latest improvements before the next major", + "title": "MUI X v6.18.x and the latest improvements before the next major", "text": "New stable components, polished features, better performance and more. Check out the details in our recent blog post." }, { "id": 79, - "title": "A new Developer Survey is open", + "title": "A new Developer Survey is open", "text": "Take a few minutes to share your feedback and expectations in the Developer Survey." }, { "id": 80, - "title": "MUI X v7.0.0-beta.0", + "title": "MUI X v7.0.0-beta.0", "text": "Featuring new components and multiple enhancements for both developers and end-users. Discover all the specifics in the announcement blog post." } ] diff --git a/docs/src/modules/components/Notifications.js b/docs/src/modules/components/Notifications.js index 6fc4cb9857a4eb..63c3e0f82e009f 100644 --- a/docs/src/modules/components/Notifications.js +++ b/docs/src/modules/components/Notifications.js @@ -231,10 +231,7 @@ export default function Notifications() { - + {message.title} Date: Thu, 7 Mar 2024 15:44:58 +0530 Subject: [PATCH 04/17] [material-ui][Autocomplete] Fix the options list being added to the DOM in `freeSolo` mode even when there are no options, causing style problems (#41300) Co-authored-by: ZeeshanTamboli --- .../src/Autocomplete/Autocomplete.js | 135 +++++++++--------- .../src/Autocomplete/Autocomplete.test.js | 16 +++ 2 files changed, 87 insertions(+), 64 deletions(-) diff --git a/packages/mui-material/src/Autocomplete/Autocomplete.js b/packages/mui-material/src/Autocomplete/Autocomplete.js index 18e0e9d58180c1..3672e8f334d4d6 100644 --- a/packages/mui-material/src/Autocomplete/Autocomplete.js +++ b/packages/mui-material/src/Autocomplete/Autocomplete.js @@ -582,6 +582,76 @@ const Autocomplete = React.forwardRef(function Autocomplete(inProps, ref) { const popperSlotProps = slotProps.popper ?? componentsProps.popper; const popupIndicatorSlotProps = slotProps.popupIndicator ?? componentsProps.popupIndicator; + const renderAutocompletePopperChildren = (children) => ( + + + {children} + + + ); + + let autocompletePopper = null; + if (!loading && groupedOptions.length > 0) { + autocompletePopper = renderAutocompletePopperChildren( + + {groupedOptions.map((option, index) => { + if (groupBy) { + return renderGroup({ + key: option.key, + group: option.group, + children: option.options.map((option2, index2) => + renderListOption(option2, option.index + index2), + ), + }); + } + return renderListOption(option, index); + })} + , + ); + } else if (loading && groupedOptions.length === 0) { + autocompletePopper = renderAutocompletePopperChildren( + + {loadingText} + , + ); + } else if (groupedOptions.length === 0 && !freeSolo && !loading) { + autocompletePopper = renderAutocompletePopperChildren( + { + // Prevent input blur when interacting with the "no options" content + event.preventDefault(); + }} + > + {noOptionsText} + , + ); + } + return ( - {anchorEl ? ( - - - {loading && groupedOptions.length === 0 ? ( - - {loadingText} - - ) : null} - {groupedOptions.length === 0 && !freeSolo && !loading ? ( - { - // Prevent input blur when interacting with the "no options" content - event.preventDefault(); - }} - > - {noOptionsText} - - ) : null} - {groupedOptions.length > 0 ? ( - - {groupedOptions.map((option, index) => { - if (groupBy) { - return renderGroup({ - key: option.key, - group: option.group, - children: option.options.map((option2, index2) => - renderListOption(option2, option.index + index2), - ), - }); - } - return renderListOption(option, index); - })} - - ) : null} - - - ) : null} + {anchorEl ? autocompletePopper : null} ); }); diff --git a/packages/mui-material/src/Autocomplete/Autocomplete.test.js b/packages/mui-material/src/Autocomplete/Autocomplete.test.js index 5ae89516371bfd..c20e977925918e 100644 --- a/packages/mui-material/src/Autocomplete/Autocomplete.test.js +++ b/packages/mui-material/src/Autocomplete/Autocomplete.test.js @@ -2336,6 +2336,22 @@ describe('', () => { expect(container.querySelector(`.${classes.endAdornment}`)).to.equal(null); }); + + it('should not render popper when there are no options', () => { + render( + } + slotProps={{ + popper: { 'data-testid': 'popperRoot' }, + }} + />, + ); + const popper = screen.queryByTestId('popperRoot'); + expect(popper).to.equal(null); + }); }); describe('prop: onChange', () => { From 16e8ef70c0cc9553cc869ad8504440591e7c95e7 Mon Sep 17 00:00:00 2001 From: Jan Potoms <2109932+Janpot@users.noreply.github.com> Date: Thu, 7 Mar 2024 15:54:55 +0100 Subject: [PATCH 05/17] [code-infra] Add alias for icon types (#41248) Signed-off-by: Jan Potoms <2109932+Janpot@users.noreply.github.com> --- .codesandbox/ci.json | 2 ++ .../snackbar/TransitionComponentSnackbar.tsx | 2 +- .../UnstyledSnackbarIntroduction/css/index.tsx | 2 +- .../system/index.tsx | 2 +- .../tailwind/index.tsx | 2 +- .../integrations/icon-libraries/IconFontSizes.js | 13 ++++++++----- .../icon-libraries/IconFontSizes.tsx | 13 ++++++++----- .../icon-libraries/IconFontSizes.tsx.preview | 11 ++++++++--- .../experiments/base/components-gallery.tsx | 4 ++-- .../productBaseUI/BaseUIThemesDemo.tsx | 2 +- docs/src/components/showcase/TaskCard.tsx | 2 +- docs/src/modules/brandingTheme.ts | 16 ++++++++++++++++ packages/mui-icons-material/src/icon.d.ts | 1 + packages/mui-icons-material/tsconfig.json | 7 +------ packages/mui-material/src/Alert/Alert.spec.tsx | 2 +- tsconfig.json | 3 +-- 16 files changed, 54 insertions(+), 30 deletions(-) create mode 100644 packages/mui-icons-material/src/icon.d.ts diff --git a/.codesandbox/ci.json b/.codesandbox/ci.json index cff1e590be9904..6b395d8ebe8487 100644 --- a/.codesandbox/ci.json +++ b/.codesandbox/ci.json @@ -9,6 +9,7 @@ "packages/mui-base", "packages/mui-codemod", "packages/mui-core-downloads-tracker", + "packages/mui-docs", "packages/mui-icons-material", "packages/mui-joy", "packages/mui-lab", @@ -28,6 +29,7 @@ "@mui/base": "packages/mui-base/build", "@mui/codemod": "packages/mui-codemod/build", "@mui/core-downloads-tracker": "packages/mui-core-downloads-tracker/build", + "@mui/docs": "packages/mui-docs/build", "@mui/icons-material": "packages/mui-icons-material/build", "@mui/internal-babel-macros": "packages/mui-babel-macros", "@mui/internal-markdown": "packages/markdown", diff --git a/docs/data/base/components/snackbar/TransitionComponentSnackbar.tsx b/docs/data/base/components/snackbar/TransitionComponentSnackbar.tsx index c04116d1174cfc..ccd8821e10f1d1 100644 --- a/docs/data/base/components/snackbar/TransitionComponentSnackbar.tsx +++ b/docs/data/base/components/snackbar/TransitionComponentSnackbar.tsx @@ -10,7 +10,7 @@ export default function TransitionComponentSnackbar() { const [exited, setExited] = React.useState(true); const nodeRef = React.useRef(null); - const handleClose = (_: any, reason: SnackbarCloseReason) => { + const handleClose = (_: any, reason?: SnackbarCloseReason) => { if (reason === 'clickaway') { return; } diff --git a/docs/data/base/components/snackbar/UnstyledSnackbarIntroduction/css/index.tsx b/docs/data/base/components/snackbar/UnstyledSnackbarIntroduction/css/index.tsx index 36e003cd345db8..93f36426d306cc 100644 --- a/docs/data/base/components/snackbar/UnstyledSnackbarIntroduction/css/index.tsx +++ b/docs/data/base/components/snackbar/UnstyledSnackbarIntroduction/css/index.tsx @@ -11,7 +11,7 @@ export default function UnstyledSnackbarIntroduction() { const [exited, setExited] = React.useState(true); const nodeRef = React.useRef(null); - const handleClose = (_: any, reason: SnackbarCloseReason) => { + const handleClose = (_: any, reason?: SnackbarCloseReason) => { if (reason === 'clickaway') { return; } diff --git a/docs/data/base/components/snackbar/UnstyledSnackbarIntroduction/system/index.tsx b/docs/data/base/components/snackbar/UnstyledSnackbarIntroduction/system/index.tsx index d73eb6c45eea53..b4fed03c2a230f 100644 --- a/docs/data/base/components/snackbar/UnstyledSnackbarIntroduction/system/index.tsx +++ b/docs/data/base/components/snackbar/UnstyledSnackbarIntroduction/system/index.tsx @@ -11,7 +11,7 @@ export default function UnstyledSnackbarIntroduction() { const [exited, setExited] = React.useState(true); const nodeRef = React.useRef(null); - const handleClose = (_: any, reason: SnackbarCloseReason) => { + const handleClose = (_: any, reason?: SnackbarCloseReason) => { if (reason === 'clickaway') { return; } diff --git a/docs/data/base/components/snackbar/UnstyledSnackbarIntroduction/tailwind/index.tsx b/docs/data/base/components/snackbar/UnstyledSnackbarIntroduction/tailwind/index.tsx index e34bf3d5c5c45f..f36868dd2de782 100644 --- a/docs/data/base/components/snackbar/UnstyledSnackbarIntroduction/tailwind/index.tsx +++ b/docs/data/base/components/snackbar/UnstyledSnackbarIntroduction/tailwind/index.tsx @@ -18,7 +18,7 @@ export default function UnstyledSnackbarIntroduction() { const [exited, setExited] = React.useState(true); const nodeRef = React.useRef(null); - const handleClose = (_: any, reason: SnackbarCloseReason) => { + const handleClose = (_: any, reason?: SnackbarCloseReason) => { if (reason === 'clickaway') { return; } diff --git a/docs/data/joy/integrations/icon-libraries/IconFontSizes.js b/docs/data/joy/integrations/icon-libraries/IconFontSizes.js index b8bcc044becadb..72a86184653993 100644 --- a/docs/data/joy/integrations/icon-libraries/IconFontSizes.js +++ b/docs/data/joy/integrations/icon-libraries/IconFontSizes.js @@ -1,19 +1,22 @@ import * as React from 'react'; -import { useTheme } from '@mui/joy/styles'; import Stack from '@mui/joy/Stack'; import Person from '@mui/icons-material/Person'; export default function IconFontSizes() { - const theme = useTheme(); return ( - {Object.keys(theme.fontSize).map((size) => ( - - ))} + + + + + + + + ); } diff --git a/docs/data/joy/integrations/icon-libraries/IconFontSizes.tsx b/docs/data/joy/integrations/icon-libraries/IconFontSizes.tsx index b8bcc044becadb..72a86184653993 100644 --- a/docs/data/joy/integrations/icon-libraries/IconFontSizes.tsx +++ b/docs/data/joy/integrations/icon-libraries/IconFontSizes.tsx @@ -1,19 +1,22 @@ import * as React from 'react'; -import { useTheme } from '@mui/joy/styles'; import Stack from '@mui/joy/Stack'; import Person from '@mui/icons-material/Person'; export default function IconFontSizes() { - const theme = useTheme(); return ( - {Object.keys(theme.fontSize).map((size) => ( - - ))} + + + + + + + + ); } diff --git a/docs/data/joy/integrations/icon-libraries/IconFontSizes.tsx.preview b/docs/data/joy/integrations/icon-libraries/IconFontSizes.tsx.preview index a94d346b70bd0c..bccdfe423278bf 100644 --- a/docs/data/joy/integrations/icon-libraries/IconFontSizes.tsx.preview +++ b/docs/data/joy/integrations/icon-libraries/IconFontSizes.tsx.preview @@ -1,3 +1,8 @@ -{Object.keys(theme.fontSize).map((size) => ( - -))} \ No newline at end of file + + + + + + + + \ No newline at end of file diff --git a/docs/pages/experiments/base/components-gallery.tsx b/docs/pages/experiments/base/components-gallery.tsx index 084ce223ad1511..94c04408330fb0 100644 --- a/docs/pages/experiments/base/components-gallery.tsx +++ b/docs/pages/experiments/base/components-gallery.tsx @@ -143,7 +143,7 @@ export default function ComponentsGallery() { const [exited, setExited] = React.useState(true); const nodeRef = React.useRef(null); - const handleClose = (_: any, reason: SnackbarCloseReason) => { + const handleClose = (_: any, reason?: SnackbarCloseReason) => { if (reason === 'clickaway') { return; } @@ -208,7 +208,7 @@ export default function ComponentsGallery() { setCopySnackbarOpen(true); } - const handleCopyClose = (_: any, reason: SnackbarCloseReason) => { + const handleCopyClose = (_: any, reason?: SnackbarCloseReason) => { if (reason === 'clickaway') { return; } diff --git a/docs/src/components/productBaseUI/BaseUIThemesDemo.tsx b/docs/src/components/productBaseUI/BaseUIThemesDemo.tsx index b8d24d54b97e9d..040742e06aa145 100644 --- a/docs/src/components/productBaseUI/BaseUIThemesDemo.tsx +++ b/docs/src/components/productBaseUI/BaseUIThemesDemo.tsx @@ -785,7 +785,7 @@ export default function BaseUIThemesDemo() { // Snackbar const [openSnackbar, setOpenSnackbar] = React.useState(false); - const handleCloseSnackbar = (_: any, reason: SnackbarCloseReason) => { + const handleCloseSnackbar = (_: any, reason?: SnackbarCloseReason) => { if (reason === 'clickaway') { return; } diff --git a/docs/src/components/showcase/TaskCard.tsx b/docs/src/components/showcase/TaskCard.tsx index 304e9e65788b54..d437f7a6f6b217 100644 --- a/docs/src/components/showcase/TaskCard.tsx +++ b/docs/src/components/showcase/TaskCard.tsx @@ -33,7 +33,7 @@ export default function TaskCard() { - + Customize every button and chip instance primary color diff --git a/docs/src/modules/brandingTheme.ts b/docs/src/modules/brandingTheme.ts index ae02ed72e32070..3fba71c44d36fa 100644 --- a/docs/src/modules/brandingTheme.ts +++ b/docs/src/modules/brandingTheme.ts @@ -65,6 +65,22 @@ declare module '@mui/material/Chip' { } } +declare module '@mui/material/SvgIcon' { + interface SvgIconPropsColorOverrides { + danger: true; + } + interface SvgIconPropsSizeOverrides { + xs: true; + sm: true; + md: true; + lg: true; + xl: true; + xl2: true; + xl3: true; + xl4: true; + } +} + // TODO: enable this once types conflict is fixed // declare module '@mui/material/Button' { // interface ButtonPropsVariantOverrides { diff --git a/packages/mui-icons-material/src/icon.d.ts b/packages/mui-icons-material/src/icon.d.ts new file mode 100644 index 00000000000000..72c99eef1a4c82 --- /dev/null +++ b/packages/mui-icons-material/src/icon.d.ts @@ -0,0 +1 @@ +export { default } from '@mui/material/SvgIcon'; diff --git a/packages/mui-icons-material/tsconfig.json b/packages/mui-icons-material/tsconfig.json index f762d7e08aeb2f..73a65ef2ef6e56 100644 --- a/packages/mui-icons-material/tsconfig.json +++ b/packages/mui-icons-material/tsconfig.json @@ -1,10 +1,5 @@ { "extends": "../../tsconfig.json", - "compilerOptions": { - "paths": { - "@mui/material": ["../mui-material/src"], - "@mui/material/*": ["../mui-material/src/*"] - } - }, + "compilerOptions": {}, "include": ["src/**/*"] } diff --git a/packages/mui-material/src/Alert/Alert.spec.tsx b/packages/mui-material/src/Alert/Alert.spec.tsx index dbfeb1222427a0..349694b19f9ba4 100644 --- a/packages/mui-material/src/Alert/Alert.spec.tsx +++ b/packages/mui-material/src/Alert/Alert.spec.tsx @@ -1,4 +1,4 @@ -import { CloseRounded } from '@mui/icons-material'; +import CloseRounded from '@mui/icons-material/CloseRounded'; import { createTheme } from '@mui/material'; createTheme({ diff --git a/tsconfig.json b/tsconfig.json index 2805977e8c472f..94f8dff28f53c3 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -43,8 +43,7 @@ "@mui/material-nextjs/*": ["./packages/mui-material-nextjs/src/*"], "@mui/joy": ["./packages/mui-joy/src"], "@mui/joy/*": ["./packages/mui-joy/src/*"], - "@mui/icons-material": ["./packages/mui-icons-material/lib"], - "@mui/icons-material/*": ["./packages/mui-icons-material/lib/*"], + "@mui/icons-material/*": ["./packages/mui-icons-material/src/icon.d.ts"], "@pigment-css/nextjs-plugin": ["./packages/pigment-nextjs-plugin/src"], "@pigment-css/nextjs-plugin/*": ["./packages/pigment-nextjs-plugin/src/*"], "@pigment-css/react": ["./packages/pigment-react/src"], From 727cc390c8b0d2967ed0d9d03ed7836ebb97c864 Mon Sep 17 00:00:00 2001 From: sai chand <60743144+sai6855@users.noreply.github.com> Date: Thu, 7 Mar 2024 23:54:21 +0530 Subject: [PATCH 06/17] [mui-codemod] Fix merging of slotProps and componentProps (#41323) --- .../alert-props/alert-props.test.js | 2 +- .../alert-props/test-cases/actual.js | 6 +++ .../alert-props/test-cases/expected.js | 14 +++++- .../alert-props/test-cases/theme.actual.js | 11 +++++ .../alert-props/test-cases/theme.expected.js | 45 +++++++++++++++---- .../slider-props/slider-props.test.js | 2 +- .../slider-props/test-cases/actual.js | 6 +++ .../slider-props/test-cases/expected.js | 14 +++++- .../slider-props/test-cases/theme.actual.js | 11 +++++ .../slider-props/test-cases/theme.expected.js | 45 +++++++++++++++---- .../utils/replaceComponentsWithSlots.js | 23 +++++++++- 11 files changed, 156 insertions(+), 23 deletions(-) diff --git a/packages/mui-codemod/src/deprecations/alert-props/alert-props.test.js b/packages/mui-codemod/src/deprecations/alert-props/alert-props.test.js index d9741ce1a1e13c..761af47c09f51d 100644 --- a/packages/mui-codemod/src/deprecations/alert-props/alert-props.test.js +++ b/packages/mui-codemod/src/deprecations/alert-props/alert-props.test.js @@ -31,7 +31,7 @@ describe('@mui/codemod', () => { const actual = transform( { source: read('./test-cases/theme.actual.js') }, { jscodeshift }, - { printOptions: { trailingComma: true } }, + { printOptions: { trailingComma: false } }, ); const expected = read('./test-cases/theme.expected.js'); diff --git a/packages/mui-codemod/src/deprecations/alert-props/test-cases/actual.js b/packages/mui-codemod/src/deprecations/alert-props/test-cases/actual.js index 380a640bce04fc..fd952d3fa91fcf 100644 --- a/packages/mui-codemod/src/deprecations/alert-props/test-cases/actual.js +++ b/packages/mui-codemod/src/deprecations/alert-props/test-cases/actual.js @@ -16,3 +16,9 @@ import Alert from '@mui/material/Alert'; slotProps={{ closeIcon: slotsIconProps, closeButton: slotsButtonProps }} componentsProps={{ closeButton: componentsButtonProps }} />; +; diff --git a/packages/mui-codemod/src/deprecations/alert-props/test-cases/expected.js b/packages/mui-codemod/src/deprecations/alert-props/test-cases/expected.js index 1c1a02c7b7e601..0b05305960385b 100644 --- a/packages/mui-codemod/src/deprecations/alert-props/test-cases/expected.js +++ b/packages/mui-codemod/src/deprecations/alert-props/test-cases/expected.js @@ -17,4 +17,16 @@ import Alert from '@mui/material/Alert'; }} />; ; + slotProps={{ closeIcon: slotsIconProps, closeButton: { + ...componentsButtonProps, + ...slotsButtonProps + } }} />; +; diff --git a/packages/mui-codemod/src/deprecations/alert-props/test-cases/theme.actual.js b/packages/mui-codemod/src/deprecations/alert-props/test-cases/theme.actual.js index 53747aee31ea17..10591ba1dfb8b1 100644 --- a/packages/mui-codemod/src/deprecations/alert-props/test-cases/theme.actual.js +++ b/packages/mui-codemod/src/deprecations/alert-props/test-cases/theme.actual.js @@ -28,3 +28,14 @@ fn({ }, }, }); + +fn({ + MuiAlert: { + defaultProps: { + components: { CloseButton: ComponentsButton }, + slots: { closeIcon: SlotsIcon, closeButton: SlotsButton }, + componentsProps: { closeButton: componentsButtonProps, closeIcon: componentsIconProps }, + slotProps: { closeIcon: slotsIconProps, closeButton: slotsButtonProps }, + }, + }, +}); diff --git a/packages/mui-codemod/src/deprecations/alert-props/test-cases/theme.expected.js b/packages/mui-codemod/src/deprecations/alert-props/test-cases/theme.expected.js index dfab24532bd162..c3f2cb05133264 100644 --- a/packages/mui-codemod/src/deprecations/alert-props/test-cases/theme.expected.js +++ b/packages/mui-codemod/src/deprecations/alert-props/test-cases/theme.expected.js @@ -2,12 +2,12 @@ fn({ MuiAlert: { defaultProps: { slots: { - closeButton: ComponentsButton, + closeButton: ComponentsButton }, slotProps: { - closeButton: componentsButtonProps, - }, + closeButton: componentsButtonProps + } }, }, }); @@ -17,13 +17,13 @@ fn({ defaultProps: { slots: { closeButton: ComponentsButton, - closeIcon: SlotsIcon, + closeIcon: SlotsIcon }, slotProps: { closeButton: componentsButtonProps, - closeIcon: slotsIconProps, - }, + closeIcon: slotsIconProps + } }, }, }); @@ -33,13 +33,40 @@ fn({ defaultProps: { slots: { closeButton: SlotsButton, - closeIcon: SlotsIcon, + closeIcon: SlotsIcon }, slotProps: { - closeButton: slotsButtonProps, - closeIcon: slotsIconProps, + closeButton: { + ...componentsButtonProps, + ...slotsButtonProps + }, + + closeIcon: slotsIconProps + } + }, + }, +}); + +fn({ + MuiAlert: { + defaultProps: { + slots: { + closeButton: SlotsButton, + closeIcon: SlotsIcon }, + + slotProps: { + closeButton: { + ...componentsButtonProps, + ...slotsButtonProps + }, + + closeIcon: { + ...componentsIconProps, + ...slotsIconProps + } + } }, }, }); diff --git a/packages/mui-codemod/src/deprecations/slider-props/slider-props.test.js b/packages/mui-codemod/src/deprecations/slider-props/slider-props.test.js index b6d5682eb2a76a..033acf231969bd 100644 --- a/packages/mui-codemod/src/deprecations/slider-props/slider-props.test.js +++ b/packages/mui-codemod/src/deprecations/slider-props/slider-props.test.js @@ -31,7 +31,7 @@ describe('@mui/codemod', () => { const actual = transform( { source: read('./test-cases/theme.actual.js') }, { jscodeshift }, - { printOptions: { trailingComma: true } }, + { printOptions: { trailingComma: false } }, ); const expected = read('./test-cases/theme.expected.js'); diff --git a/packages/mui-codemod/src/deprecations/slider-props/test-cases/actual.js b/packages/mui-codemod/src/deprecations/slider-props/test-cases/actual.js index c73ad39187900b..1ac48c7504c528 100644 --- a/packages/mui-codemod/src/deprecations/slider-props/test-cases/actual.js +++ b/packages/mui-codemod/src/deprecations/slider-props/test-cases/actual.js @@ -16,3 +16,9 @@ import Slider from '@mui/material/Slider'; slotProps={{ rail: slotsRailProps, track: slotsTrackProps }} componentsProps={{ track: componentsTrackProps }} />; +; diff --git a/packages/mui-codemod/src/deprecations/slider-props/test-cases/expected.js b/packages/mui-codemod/src/deprecations/slider-props/test-cases/expected.js index c1809d70cfa6eb..20037c1639358c 100644 --- a/packages/mui-codemod/src/deprecations/slider-props/test-cases/expected.js +++ b/packages/mui-codemod/src/deprecations/slider-props/test-cases/expected.js @@ -17,4 +17,16 @@ import Slider from '@mui/material/Slider'; }} />; ; + slotProps={{ rail: slotsRailProps, track: { + ...componentsTrackProps, + ...slotsTrackProps + } }} />; +; diff --git a/packages/mui-codemod/src/deprecations/slider-props/test-cases/theme.actual.js b/packages/mui-codemod/src/deprecations/slider-props/test-cases/theme.actual.js index 5b68897f2ddc97..90df34bdd843f5 100644 --- a/packages/mui-codemod/src/deprecations/slider-props/test-cases/theme.actual.js +++ b/packages/mui-codemod/src/deprecations/slider-props/test-cases/theme.actual.js @@ -28,3 +28,14 @@ fn({ }, }, }); + +fn({ + MuiSlider: { + defaultProps: { + components: { Track: ComponentsTrack }, + slots: { rail: SlotsRail, track: SlotsTrack }, + componentsProps: { track: componentsTrackProps, rail: componentsRailProps }, + slotProps: { rail: slotsRailProps, track: slotsTrackProps }, + }, + }, +}); diff --git a/packages/mui-codemod/src/deprecations/slider-props/test-cases/theme.expected.js b/packages/mui-codemod/src/deprecations/slider-props/test-cases/theme.expected.js index 74c5dab30c7553..0ef10d93f7a880 100644 --- a/packages/mui-codemod/src/deprecations/slider-props/test-cases/theme.expected.js +++ b/packages/mui-codemod/src/deprecations/slider-props/test-cases/theme.expected.js @@ -2,12 +2,12 @@ fn({ MuiSlider: { defaultProps: { slots: { - track: ComponentsTrack, + track: ComponentsTrack }, slotProps: { - track: componentsTrackProps, - }, + track: componentsTrackProps + } }, }, }); @@ -17,13 +17,13 @@ fn({ defaultProps: { slots: { track: ComponentsTrack, - rail: SlotsRail, + rail: SlotsRail }, slotProps: { track: componentsTrackProps, - rail: slotsRailProps, - }, + rail: slotsRailProps + } }, }, }); @@ -33,13 +33,40 @@ fn({ defaultProps: { slots: { track: SlotsTrack, - rail: SlotsRail, + rail: SlotsRail }, slotProps: { - track: slotsTrackProps, - rail: slotsRailProps, + track: { + ...componentsTrackProps, + ...slotsTrackProps + }, + + rail: slotsRailProps + } + }, + }, +}); + +fn({ + MuiSlider: { + defaultProps: { + slots: { + track: SlotsTrack, + rail: SlotsRail }, + + slotProps: { + track: { + ...componentsTrackProps, + ...slotsTrackProps + }, + + rail: { + ...componentsRailProps, + ...slotsRailProps + } + } }, }, }); diff --git a/packages/mui-codemod/src/deprecations/utils/replaceComponentsWithSlots.js b/packages/mui-codemod/src/deprecations/utils/replaceComponentsWithSlots.js index fb1ee1d87edad4..eaac90c45e6b4c 100644 --- a/packages/mui-codemod/src/deprecations/utils/replaceComponentsWithSlots.js +++ b/packages/mui-codemod/src/deprecations/utils/replaceComponentsWithSlots.js @@ -69,6 +69,19 @@ function replaceJsxComponentsPropsProp(j, element) { key: prop.key.name, expression: prop.value, }); + } else { + attr.value.expression.properties = attr.value.expression.properties.filter( + (p) => p?.key?.name !== prop.key.name, + ); + + assignObject(j, { + target: attr, + key: prop.key.name, + expression: j.objectExpression([ + j.spreadElement(prop.value), + j.spreadElement(slotProps[prop.key.name]), + ]), + }); } }); } @@ -131,7 +144,15 @@ function replaceDefaultPropsComponentsPropsProp(j, defaultPropsPathCollection) { const slots = existingSlots ? existingSlots.value.properties.reduce((acc, prop) => { - return { ...acc, [prop.key.name]: prop.value }; + return { + ...acc, + [prop.key.name]: components[prop.key.name] + ? j.objectExpression([ + j.spreadElement(components[prop.key.name]), + j.spreadElement(prop.value), + ]) + : prop.value, + }; }, {}) : {}; From f1eacff00628085a8c27eca52a5853a71e6fcf86 Mon Sep 17 00:00:00 2001 From: Olivier Tassinari Date: Fri, 8 Mar 2024 01:40:50 +0100 Subject: [PATCH 07/17] [docs-infra] Simplify Algolia crawler config (#41312) --- docs/src/modules/components/ApiPage/table/ClassesTable.tsx | 4 ++-- docs/src/modules/components/ApiPage/table/PropertiesTable.tsx | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/src/modules/components/ApiPage/table/ClassesTable.tsx b/docs/src/modules/components/ApiPage/table/ClassesTable.tsx index d49d35f8cc7236..b2c98cf1674cf9 100644 --- a/docs/src/modules/components/ApiPage/table/ClassesTable.tsx +++ b/docs/src/modules/components/ApiPage/table/ClassesTable.tsx @@ -81,13 +81,13 @@ export default function ClassesTable(props: ClassesTableProps) { return ( - + .{className} {displayClassKeys && ( {!isGlobal && {key}} )} - + - + {propName} {isRequired ? '*' : ''} {isOptional ? '?' : ''} @@ -200,7 +200,7 @@ export default function PropertiesTable(props: PropertiesTableProps) { )} )} - + {description && } {seeMoreDescription && (

Date: Fri, 8 Mar 2024 10:40:45 +0700 Subject: [PATCH 08/17] [docs][pigment] Add example and guide section (#41249) --- packages/pigment-react/README.md | 149 +++++++++++++++++++++++++++++-- 1 file changed, 143 insertions(+), 6 deletions(-) diff --git a/packages/pigment-react/README.md b/packages/pigment-react/README.md index 74c5468d771e95..afca1fa7e9e461 100644 --- a/packages/pigment-react/README.md +++ b/packages/pigment-react/README.md @@ -1,6 +1,6 @@ # Pigment CSS -A zero-runtime CSS-in-JS library that extracts the colocated styles to their own CSS files at build-time. +Pigment CSS is a zero-runtime CSS-in-JS library that extracts the colocated styles to their own CSS files at build time. - [Getting started](#getting-started) - [Why this project exists?](#why-choose-pigment-css) @@ -19,6 +19,8 @@ A zero-runtime CSS-in-JS library that extracts the colocated styles to their own - [Color schemes](#color-schemes) - [Switching color schemes](#switching-color-schemes) - [TypeScript](#typescript) +- [How-to guides](#how-to-guides) + - [Coming from Emotion or styled-components](#coming-from-emotion-or-styled-components) ## Getting started @@ -142,7 +144,7 @@ Use the `variants` key to define styles for a combination of the component's pro Each variant is an object with `props` and `style` keys. The styles are applied when the component's props match the `props` object. -**Example 1**: A button component with `small` and `large` sizes: +**Example 1** — A button component with `small` and `large` sizes: ```jsx const Button = styled('button')({ @@ -166,7 +168,7 @@ const Button = styled('button')({ ; // padding: 0.5rem ``` -**Example 2**: A button component with variants and colors: +**Example 2** — A button component with variants and colors: ```jsx const Button = styled('button')({ @@ -187,7 +189,7 @@ const Button = styled('button')({ ; ``` -**Example 3**: Apply styles based on a condition: +**Example 3** — Apply styles based on a condition: The value of the `props` can be a function that returns a boolean. If the function returns `true`, the styles are applied. @@ -205,6 +207,37 @@ const Button = styled('button')({ }); ``` +Note that the `props` function will not work if it is inside another closure, for example, inside an `array.map`: + +```jsx +const Button = styled('button')({ + border: 'none', + padding: '0.75rem', + // ...other base styles + variants: ['red', 'blue', 'green'].map((item) => ({ + props: (props) => { + // ❌ Cannot access `item` in this closure + return props.color === item && !props.disabled; + }, + style: { backgroundColor: 'tomato' }, + })), +}); +``` + +Instead, use plain objects to define the variants: + +```jsx +const Button = styled('button')({ + border: 'none', + padding: '0.75rem', + // ...other base styles + variants: ['red', 'blue', 'green'].map((item) => ({ + props: { color: item, disabled: false }, + style: { backgroundColor: 'tomato' }, + })), +}); +``` + #### Styling based on runtime values > 💡 This approach is recommended when the value of a prop is **unknown** ahead of time or possibly unlimited values, e.g. styling based on the user's input. @@ -217,7 +250,7 @@ const Heading = styled('h1')({ }); ``` -Zero-runtime will replace the callback with a CSS variable and inject the value through inline style. This makes it possible to create a static CSS file while still allowing dynamic styles. +Pigment CSS will replace the callback with a CSS variable and inject the value through inline style. This makes it possible to create a static CSS file while still allowing dynamic styles. ```css .Heading_class_akjsdfb { @@ -322,7 +355,7 @@ const Heading = styled('h1')(({ theme }) => ({ #### CSS variables support -Zero-runtime can generate CSS variables from the theme values when you wrap your theme with `extendTheme` utility. For example, in a `next.config.js` file: +Pigment CSS can generate CSS variables from the theme values when you wrap your theme with `extendTheme` utility. For example, in a `next.config.js` file: ```js const { withPigment, extendTheme } = require('@pigment-css/nextjs-plugin'); @@ -461,3 +494,107 @@ declare module '@pigment-css/react/theme' { } } ``` + +## How-to guides + +### Coming from Emotion or styled-components + +Emotion and styled-components are runtime CSS-in-JS libraries. What you write in your styles is what you get in the final bundle, which means the styles can be as dynamic as you want with bundle size and performance overhead trade-offs. + +On the other hand, Pigment CSS extracts CSS at build time and replaces the JS code with hashed class names and some CSS variables. This means that it has to know all of the styles to be extracted ahead of time, so there are rules and limitations that you need to be aware of when using JavaScript callbacks or variables in Pigment CSS's APIs. + +Here are some common patterns and how to achieve them with Pigment CSS: + +1. **Fixed set of styles** + +In Emotion or styled-components, you can use props to create styles conditionally: + +```js +const Flex = styled('div')((props) => ({ + display: 'flex', + ...(props.vertical // ❌ Pigment CSS will throw an error + ? { + flexDirection: 'column', + paddingBlock: '1rem', + } + : { + paddingInline: '1rem', + }), +})); +``` + +But in Pigment CSS, you need to define all of the styles ahead of time using the `variants` key: + +```js +const Flex = styled('div')((props) => ({ + display: 'flex', + variants: [ + { + props: { vertical: true }, + style: { + flexDirection: 'column', + paddingBlock: '1rem', + }, + }, + { + props: { vertical: false }, + style: { + paddingInline: '1rem', + }, + }, + ], +})); +``` + +> 💡 Keep in mind that the `variants` key is for fixed values of props, for example, a component's colors, sizes, and states. + +2. **Programatically generated styles** + +For Emotion and styled-components, the styles will be different on each render and instance because the styles are generated at runtime: + +```js +function randomBetween(min: number, max: number) { + return Math.floor(Math.random() * (max - min + 1) + min); +} +function generateBubbleVars() { + return ` + --x: ${randomBetween(10, 90)}%; + --y: ${randomBetween(15, 85)}%; + `; +} + +function App() { + return ( +

+ {[...Array(10)].map((_, index) => ( +
+ ))} +
+ ) +} +``` + +However, in Pigment CSS with the same code as above, all instances will have the same styles and won't change between renders because the styles are extracted at build time. + +To achieve the same result, you need to move the dynamic logic to props and pass the value to CSS variables instead: + +```js +function randomBetween(min: number, max: number) { + return Math.floor(Math.random() * (max - min + 1) + min); +} + +const Bubble = styled('div')({ + '--x': props => props.x, + '--y': props => props.y, +}); + +function App() { + return ( +
+ {[...Array(10)].map((_, index) => ( + + ))} +
+ ) +} +``` From 0e765a618a7676c507852cec024464b1fc1cd80f Mon Sep 17 00:00:00 2001 From: Brijesh Bittu Date: Fri, 8 Mar 2024 09:39:00 +0530 Subject: [PATCH 09/17] [pigment] Make all Pigment CSS packages public (#41404) --- CHANGELOG.md | 4 ++++ packages/pigment-nextjs-plugin/package.json | 1 - packages/pigment-react/package.json | 1 - packages/pigment-unplugin/package.json | 1 - packages/pigment-vite-plugin/package.json | 1 - 5 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8e274d1eed3c52..c392f200b594fe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,10 @@ _Mar 5, 2024_ A big thanks to the 21 contributors who made this release possible. This release was mostly about 🐛 bug fixes and 📚 documentation improvements. +### `@pigment-css/react@0.0.1`, `@pigment-css/nextjs-plugin@0.0.1`, `@pigment-css/vite-plugin@0.0.1`, & `@pigment-css/unplugin@0.0.1` + +- This is the first public release of our new zero-runtime CSS-in-JS library, Pigment CSS. + ### `@mui/material@5.15.12` - ​Support props callback type in theme variants (#40946) @ZeeshanTamboli diff --git a/packages/pigment-nextjs-plugin/package.json b/packages/pigment-nextjs-plugin/package.json index f08bc5574be851..bf6f89b61f5204 100644 --- a/packages/pigment-nextjs-plugin/package.json +++ b/packages/pigment-nextjs-plugin/package.json @@ -1,7 +1,6 @@ { "name": "@pigment-css/nextjs-plugin", "version": "0.0.1", - "private": true, "main": "build/index.js", "module": "build/index.mjs", "types": "build/index.d.ts", diff --git a/packages/pigment-react/package.json b/packages/pigment-react/package.json index 0faf9bfb2f31ca..21780ca073c0f6 100644 --- a/packages/pigment-react/package.json +++ b/packages/pigment-react/package.json @@ -1,7 +1,6 @@ { "name": "@pigment-css/react", "version": "0.0.1", - "private": true, "main": "build/index.js", "module": "build/index.mjs", "types": "build/index.d.ts", diff --git a/packages/pigment-unplugin/package.json b/packages/pigment-unplugin/package.json index 59b389b7c9d163..b42ca85edcb7e1 100644 --- a/packages/pigment-unplugin/package.json +++ b/packages/pigment-unplugin/package.json @@ -1,7 +1,6 @@ { "name": "@pigment-css/unplugin", "version": "0.0.1", - "private": true, "main": "build/index.js", "module": "build/index.mjs", "types": "build/index.d.ts", diff --git a/packages/pigment-vite-plugin/package.json b/packages/pigment-vite-plugin/package.json index c86edd75123147..d91e7618636110 100644 --- a/packages/pigment-vite-plugin/package.json +++ b/packages/pigment-vite-plugin/package.json @@ -1,7 +1,6 @@ { "name": "@pigment-css/vite-plugin", "version": "0.0.1", - "private": true, "main": "build/index.js", "module": "build/index.mjs", "types": "build/index.d.ts", From 13c9f43f2f2d63fca5d12a4e01fa32eb9fc582fc Mon Sep 17 00:00:00 2001 From: Siriwat K Date: Fri, 8 Mar 2024 13:35:25 +0700 Subject: [PATCH 10/17] [examples] Add pigment-css-nextjs-ts starter project (#41105) --- examples/pigment-css-nextjs-ts/.gitignore | 36 ++++ examples/pigment-css-nextjs-ts/README.md | 38 ++++ examples/pigment-css-nextjs-ts/next-env.d.ts | 5 + examples/pigment-css-nextjs-ts/next.config.js | 28 +++ examples/pigment-css-nextjs-ts/package.json | 27 +++ .../pigment-css-nextjs-ts/public/.gitkeep | 0 .../pigment-css-nextjs-ts/src/app/favicon.ico | Bin 0 -> 619 bytes .../pigment-css-nextjs-ts/src/app/globals.css | 10 + .../pigment-css-nextjs-ts/src/app/layout.tsx | 31 +++ .../pigment-css-nextjs-ts/src/app/page.tsx | 204 ++++++++++++++++++ .../pigment-css-nextjs-ts/src/augment.d.ts | 19 ++ examples/pigment-css-nextjs-ts/tsconfig.json | 29 +++ packages/pigment-react/README.md | 110 ++++++++++ 13 files changed, 537 insertions(+) create mode 100644 examples/pigment-css-nextjs-ts/.gitignore create mode 100644 examples/pigment-css-nextjs-ts/README.md create mode 100644 examples/pigment-css-nextjs-ts/next-env.d.ts create mode 100644 examples/pigment-css-nextjs-ts/next.config.js create mode 100644 examples/pigment-css-nextjs-ts/package.json create mode 100644 examples/pigment-css-nextjs-ts/public/.gitkeep create mode 100644 examples/pigment-css-nextjs-ts/src/app/favicon.ico create mode 100644 examples/pigment-css-nextjs-ts/src/app/globals.css create mode 100644 examples/pigment-css-nextjs-ts/src/app/layout.tsx create mode 100644 examples/pigment-css-nextjs-ts/src/app/page.tsx create mode 100644 examples/pigment-css-nextjs-ts/src/augment.d.ts create mode 100644 examples/pigment-css-nextjs-ts/tsconfig.json diff --git a/examples/pigment-css-nextjs-ts/.gitignore b/examples/pigment-css-nextjs-ts/.gitignore new file mode 100644 index 00000000000000..28c8a5adb7c034 --- /dev/null +++ b/examples/pigment-css-nextjs-ts/.gitignore @@ -0,0 +1,36 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* +.pnpm-debug.log* + +# local env files +.env*.local + +# vercel +.vercel + +# typescript +*.tsbuildinfo +# next-env.d.ts diff --git a/examples/pigment-css-nextjs-ts/README.md b/examples/pigment-css-nextjs-ts/README.md new file mode 100644 index 00000000000000..3449ebdbd149ec --- /dev/null +++ b/examples/pigment-css-nextjs-ts/README.md @@ -0,0 +1,38 @@ +# Pigment CSS - Next.js App Router with TypeScript example project + +This is a [Next.js](https://nextjs.org/) project bootstrapped using [`create-next-app`](https://github.com/vercel/next.js/tree/HEAD/packages/create-next-app), with TypeScript and MUI's zero-runtime CSS-in-JS installed. + +## How to use + +Download the example [or clone the repo](https://github.com/mui/material-ui): + + + +```bash +curl https://codeload.github.com/mui/material-ui/tar.gz/master | tar -xz --strip=2 material-ui-master/examples/pigment-css-nextjs-ts +cd pigment-css-nextjs-ts +``` + +Install it and run: + +```bash +npm install +npm run dev +``` + +Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. + +or: + + + +[![Edit on StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/mui/material-ui/tree/master/examples/pigment-css-nextjs-ts) + +[![Edit on CodeSandbox](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/p/sandbox/github/mui/material-ui/tree/master/examples/pigment-css-nextjs-ts) + +## Learn more + +To learn more about this example: + +- [Pigment CSS documentation](https://github.com/mui/material-ui/blob/master/packages/pigment-react/README.md) - learn more about Pigment CSS features and APIs. +- [Next.js documentation](https://nextjs.org/docs) - learn about Next.js features and APIs. diff --git a/examples/pigment-css-nextjs-ts/next-env.d.ts b/examples/pigment-css-nextjs-ts/next-env.d.ts new file mode 100644 index 00000000000000..4f11a03dc6cc37 --- /dev/null +++ b/examples/pigment-css-nextjs-ts/next-env.d.ts @@ -0,0 +1,5 @@ +/// +/// + +// NOTE: This file should not be edited +// see https://nextjs.org/docs/basic-features/typescript for more information. diff --git a/examples/pigment-css-nextjs-ts/next.config.js b/examples/pigment-css-nextjs-ts/next.config.js new file mode 100644 index 00000000000000..2196cab8c75dda --- /dev/null +++ b/examples/pigment-css-nextjs-ts/next.config.js @@ -0,0 +1,28 @@ +const { withPigment, extendTheme } = require('@pigment-css/nextjs-plugin'); + +// To learn more about theming, visit https://github.com/mui/material-ui/blob/master/packages/zero-runtime/README.md#theming +const theme = extendTheme({ + colorSchemes: { + light: { + palette: { + background: '0 0% 100%', + foreground: '240 10% 3.9%', + primary: '240 5.9% 10%', + border: '240 5.9% 90%', + }, + }, + dark: { + palette: { + background: '240 10% 3.9%', + foreground: '0 0% 80%', + primary: '0 0% 98%', + border: '240 3.7% 15.9%', + }, + }, + }, +}); + +/** @type {import('next').NextConfig} */ +const nextConfig = {}; + +module.exports = withPigment(nextConfig, { theme }); diff --git a/examples/pigment-css-nextjs-ts/package.json b/examples/pigment-css-nextjs-ts/package.json new file mode 100644 index 00000000000000..b2579629a61906 --- /dev/null +++ b/examples/pigment-css-nextjs-ts/package.json @@ -0,0 +1,27 @@ +{ + "name": "pigment-css-nextjs-ts", + "version": "5.0.0", + "private": true, + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start", + "lint": "next lint", + "post-update": "echo \"codesandbox preview only, need an update\" && pnpm update --latest" + }, + "dependencies": { + "@pigment-css/react": "latest", + "react": "latest", + "react-dom": "latest", + "next": "latest" + }, + "devDependencies": { + "@pigment-css/nextjs-plugin": "latest", + "@types/node": "latest", + "@types/react": "latest", + "@types/react-dom": "latest", + "eslint": "latest", + "eslint-config-next": "latest", + "typescript": "latest" + } +} diff --git a/examples/pigment-css-nextjs-ts/public/.gitkeep b/examples/pigment-css-nextjs-ts/public/.gitkeep new file mode 100644 index 00000000000000..e69de29bb2d1d6 diff --git a/examples/pigment-css-nextjs-ts/src/app/favicon.ico b/examples/pigment-css-nextjs-ts/src/app/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..e19f48f591206d5992bf3d6a72d16938d1c2299a GIT binary patch literal 619 zcmV-x0+juUP)q!Bof{+fM--X)!)*dUV4k5l zB!myd;)wP*W68gU=IgMsCYy`R_o0mdDr5*NKKN)QBDW-pg)A_J3j;jarXJu%PGkgy z1!8s5*GZ7;{=~JVK2S(d;pT_X#K%@Yi|h2o4@=6*=WN8J$|pxeg#UuVgcT(|O9zfa z7AzaM6(!m#;DnB_5nx}dm?dNd&o_vijFlowe*Z;BGzB4a;7JU`l<;%LBFdKhJw;pO zMXKbUW&#C~HIHj_fuyWt6X|pn;*aeKxNzo-P>y-RmdCt0R+T9i+XKkHDC{d1Ew z-l)4Ei*_4sUANSPcdOOXrq7p7>p`F`i0k~~hVsl+5i1|Nw)-|CI}`__{MjfJ2l%43 zwM}1jLs$0Jky*93%%1e&gemN + ({ + color: 'hsl(var(--palette-foreground))', + backgroundColor: 'hsl(var(--palette-background))', + fontFamily: + "system-ui, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'", + ...theme.applyStyles('dark', { + colorScheme: 'dark', + }), + }))} + > + {props.children} + + + ); +} diff --git a/examples/pigment-css-nextjs-ts/src/app/page.tsx b/examples/pigment-css-nextjs-ts/src/app/page.tsx new file mode 100644 index 00000000000000..08a5fc7e6cb3fe --- /dev/null +++ b/examples/pigment-css-nextjs-ts/src/app/page.tsx @@ -0,0 +1,204 @@ +import * as React from 'react'; +import { Kalnia, Josefin_Sans } from 'next/font/google'; +import { styled, css, keyframes } from '@pigment-css/react'; + +const kalnia = Kalnia({ subsets: ['latin'] }); +const josefin = Josefin_Sans({ subsets: ['latin'] }); + +const scale = keyframes({ + to: { scale: 'var(--s2)' }, +}); + +const Link = styled('a', { shouldForwardProp: (prop) => prop !== 'outlined' })<{ + outlined?: boolean; +}>(({ theme }) => ({ + fontSize: '1rem', + background: 'rgba(0 0 0 / 0.04)', + padding: '0.5rem 1rem', + letterSpacing: '1px', + borderRadius: '4px', + textAlign: 'center', + ...theme.applyStyles('dark', { + background: 'rgba(255 255 255 / 0.1)', + }), + variants: [ + { + props: { outlined: true }, + style: { + background: 'transparent', + color: `hsl(${theme.vars.palette.primary})`, + border: `1px solid hsl(${theme.vars.palette.border})`, + }, + }, + ], +})); + +const Bubble = styled('span')({ + height: 'var(--size, 100%)', + aspectRatio: '1', + background: 'radial-gradient(hsl(var(--h) 100% 70%) 25%, transparent 50%)', + position: 'absolute', + display: 'inline-block', + left: 'var(--x, 0)', + top: 'var(--y, 0)', + scale: '0', + translate: '-50% -50%', + mixBlendMode: 'multiply', + filter: 'blur(2px)', + animation: `${scale} var(--s, 2s) var(--d, 0s) infinite alternate`, +}); + +function randomBetween(min: number, max: number) { + return Math.floor(Math.random() * (max - min + 1) + min); +} +function generateBubbleVars() { + return ` + --x: ${randomBetween(10, 90)}%; + --y: ${randomBetween(15, 85)}%; + --h: ${randomBetween(0, 360)}; + --s2: ${randomBetween(2, 6)}; + --d: -${randomBetween(250, 400) / 1000}s; + --s: ${randomBetween(3, 6)}s; + `; +} + +export default function Home() { + return ( +
+

({ + fontSize: 'clamp(3rem, 1.9503rem + 4.4789vw, 6.25rem)', + fontWeight: 500, + textAlign: 'center', + position: 'relative', + display: 'flex', + alignItems: 'center', + color: '#888', + marginBottom: '1rem', + ...theme.applyStyles('dark', { color: '#fff' }), + }))}`} + > + Pigment CSS + ({ + position: 'absolute', + inset: '0', + background: 'white', + mixBlendMode: 'color-burn', + overflow: 'hidden', + pointerEvents: 'none', + ...theme.applyStyles('dark', { + mixBlendMode: 'darken', + filter: 'brightness(2)', + }), + }))} + > + + + + + + + + + + + +

+
+ CSS-in-JS library with static extraction +
+ +
*': { flex: 'auto' }, + })} + > + + Documentation + + + Roadmap + +
+
+ ); +} diff --git a/examples/pigment-css-nextjs-ts/src/augment.d.ts b/examples/pigment-css-nextjs-ts/src/augment.d.ts new file mode 100644 index 00000000000000..d10b46e01d4da0 --- /dev/null +++ b/examples/pigment-css-nextjs-ts/src/augment.d.ts @@ -0,0 +1,19 @@ +import type {} from '@pigment-css/react/theme'; +import type { ExtendTheme } from '@pigment-css/react'; + +declare module '@pigment-css/react/theme' { + export interface ThemeArgs { + theme: ExtendTheme<{ + colorScheme: 'light' | 'dark'; + tokens: { + palette: { + background: string; + foreground: string; + primary: string; + primaryForeground: string; + border: string; + }; + }; + }>; + } +} diff --git a/examples/pigment-css-nextjs-ts/tsconfig.json b/examples/pigment-css-nextjs-ts/tsconfig.json new file mode 100644 index 00000000000000..6a9c1a2e7a02a6 --- /dev/null +++ b/examples/pigment-css-nextjs-ts/tsconfig.json @@ -0,0 +1,29 @@ +{ + "compilerOptions": { + "target": "es5", + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "forceConsistentCasingInFileNames": true, + "noEmit": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "node", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "preserve", + "incremental": true, + "plugins": [ + { + "name": "next" + } + ], + "baseUrl": ".", + "paths": { + "@/*": ["./src/*"] + } + }, + "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], + "exclude": ["node_modules"] +} diff --git a/packages/pigment-react/README.md b/packages/pigment-react/README.md index afca1fa7e9e461..358a92fc19ba1f 100644 --- a/packages/pigment-react/README.md +++ b/packages/pigment-react/README.md @@ -37,6 +37,17 @@ Pigment CSS is built on top of [WyW-in-JS](https://wyw-in-js.dev/), enabling us ### Next.js +#### Starter template + +Use the following commands to create a new Next.js project with Pigment CSS set up: + +```bash +curl https://codeload.github.com/mui/material-ui/tar.gz/master | tar -xz --strip=2 material-ui-master/examples/pigment-css-nextjs-ts +cd pigment-css-nextjs-ts +``` + +#### Manual installation + ```bash npm install @pigment-css/react npm install --save-dev @pigment-css/nextjs-plugin @@ -52,6 +63,26 @@ module.exports = withPigment({ }); ``` +Finally, import the stylesheet in the root `layout.tsx` file: + +```diff + import type { Metadata } from 'next'; ++import '@mui/zero-runtime/styles.css'; + + export const metadata: Metadata = { + title: 'Create Next App', + description: 'Generated by create next app', + }; + + export default function RootLayout(props: { children: React.ReactNode }) { + return ( + + {props.children} + + ); + } +``` + ### Vite ```bash @@ -72,6 +103,24 @@ export default defineConfig({ }); ``` +Finally, import the stylesheet in the root `main.tsx` file: + +```diff + import * as React from 'react'; + import { createRoot } from 'react-dom/client'; ++import '@mui/zero-runtime/styles.css'; + import App from './App'; + + const rootElement = document.getElementById('root'); + const root = createRoot(rootElement); + + root.render( + + + , + ); +``` + ## Basic usage > You must configure Pigment CSS with [Next.js](#nextjs) or [Vite](#vite) first. @@ -306,6 +355,52 @@ const Heading = styled('h1')<{ isError?: boolean }>({ }); ``` +### Creating animation keyframes + +Use the `keyframes` API to create reusable [animation keyframes](https://developer.mozilla.org/en-US/docs/Web/CSS/@keyframes): + +```js +import { keyframes } from '@mui/zero-runtime'; + +const fadeIn = keyframes` + from { + opacity: 0; + } + to { + opacity: 1; + } +`; + +function Example1() { + return
I am invisible
; +} +``` + +The call to the `keyframes` function will be replaced with a unique string that represents the CSS animation name. It can be used with `css` or `styled` too. + +```js +import { css, styled, keyframes } from '@mui/zero-runtime'; + +const fadeIn = keyframes(...); + +const Example2 = styled('div')({ + animation: `${fadeIn} 0.5s`, +}); + +function App() { + return ( + <> + +
+ + ) +} +``` + ### Theming Theming is an **optional** feature that lets you reuse the same values, such as colors, spacing, and typography, across your application. It is a plain object of any structure that you can define in your config file. @@ -454,6 +549,21 @@ function App() { } ``` +#### Styling based on color scheme + +The `extendTheme` utility attach a function called `applyStyles` to the theme object. It receives a color scheme as the first argument followed by a style object. It will return a proper CSS selector based on the theme configuration. + +```jsx +const Heading = styled('h1')(({ theme }) => ({ + color: theme.colors.primary, + fontSize: theme.spacing.unit * 4, + fontFamily: theme.typography.fontFamily, + ...theme.applyStyles('dark', { + color: theme.colors.primaryLight, + }), +})); +``` + #### CSS variables prefix You can add a prefix to the generated CSS variables by providing a `cssVarPrefix` option to the `extendTheme` utility: From 62b6ad3eb67e4cb7e9df6fbef46cfba757de062e Mon Sep 17 00:00:00 2001 From: Siriwat K Date: Fri, 8 Mar 2024 13:41:36 +0700 Subject: [PATCH 11/17] [examples] Add pigment-css-vite-ts starter example (#41196) --- examples/pigment-css-vite-ts/.gitignore | 24 +++ examples/pigment-css-vite-ts/README.md | 34 +++ examples/pigment-css-vite-ts/index.html | 20 ++ examples/pigment-css-vite-ts/package.json | 24 +++ examples/pigment-css-vite-ts/public/vite.svg | 1 + examples/pigment-css-vite-ts/src/App.tsx | 202 ++++++++++++++++++ examples/pigment-css-vite-ts/src/augment.d.ts | 19 ++ examples/pigment-css-vite-ts/src/globals.css | 16 ++ examples/pigment-css-vite-ts/src/main.tsx | 11 + .../pigment-css-vite-ts/src/vite-env.d.ts | 1 + examples/pigment-css-vite-ts/tsconfig.json | 21 ++ .../pigment-css-vite-ts/tsconfig.node.json | 9 + examples/pigment-css-vite-ts/vite.config.ts | 35 +++ packages/pigment-react/README.md | 11 + 14 files changed, 428 insertions(+) create mode 100644 examples/pigment-css-vite-ts/.gitignore create mode 100644 examples/pigment-css-vite-ts/README.md create mode 100644 examples/pigment-css-vite-ts/index.html create mode 100644 examples/pigment-css-vite-ts/package.json create mode 100644 examples/pigment-css-vite-ts/public/vite.svg create mode 100644 examples/pigment-css-vite-ts/src/App.tsx create mode 100644 examples/pigment-css-vite-ts/src/augment.d.ts create mode 100644 examples/pigment-css-vite-ts/src/globals.css create mode 100644 examples/pigment-css-vite-ts/src/main.tsx create mode 100644 examples/pigment-css-vite-ts/src/vite-env.d.ts create mode 100644 examples/pigment-css-vite-ts/tsconfig.json create mode 100644 examples/pigment-css-vite-ts/tsconfig.node.json create mode 100644 examples/pigment-css-vite-ts/vite.config.ts diff --git a/examples/pigment-css-vite-ts/.gitignore b/examples/pigment-css-vite-ts/.gitignore new file mode 100644 index 00000000000000..a547bf36d8d11a --- /dev/null +++ b/examples/pigment-css-vite-ts/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/examples/pigment-css-vite-ts/README.md b/examples/pigment-css-vite-ts/README.md new file mode 100644 index 00000000000000..ee0b8e25c330a7 --- /dev/null +++ b/examples/pigment-css-vite-ts/README.md @@ -0,0 +1,34 @@ +# Pigment CSS - Vite with TypeScript example project + +## How to use + +Download the example [or clone the repo](https://github.com/mui/material-ui): + + + +```bash +curl https://codeload.github.com/mui/material-ui/tar.gz/master | tar -xz --strip=2 material-ui-master/examples/pigment-css-vite-ts +cd pigment-css-vite-ts +``` + +Install it and run: + +```bash +npm install +npm run dev +``` + +or: + + + +[![Edit on StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/mui/material-ui/tree/master/examples/pigment-css-vite-ts) + +[![Edit on CodeSandbox](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/p/sandbox/github/mui/material-ui/tree/master/examples/pigment-css-vite-ts) + +## Learn more + +To learn more about this example: + +- [Pigment CSS documentation](https://github.com/mui/material-ui/blob/master/packages/pigment-react/README.md) - learn more about Pigment CSS features and APIs. +- [Vite documentation](https://vitejs.dev/guide/) - learn about Vite features and APIs. diff --git a/examples/pigment-css-vite-ts/index.html b/examples/pigment-css-vite-ts/index.html new file mode 100644 index 00000000000000..dac8779ebcdecb --- /dev/null +++ b/examples/pigment-css-vite-ts/index.html @@ -0,0 +1,20 @@ + + + + + + + + + + + Vite + Zero + TS + + +
+ + + diff --git a/examples/pigment-css-vite-ts/package.json b/examples/pigment-css-vite-ts/package.json new file mode 100644 index 00000000000000..8b61ac52d46cb2 --- /dev/null +++ b/examples/pigment-css-vite-ts/package.json @@ -0,0 +1,24 @@ +{ + "name": "pigment-css-vite-ts", + "private": true, + "version": "5.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc && vite build", + "preview": "vite preview" + }, + "dependencies": { + "@pigment-css/react": "latest", + "react": "latest", + "react-dom": "latest" + }, + "devDependencies": { + "@pigment-css/vite-plugin": "latest", + "@types/react": "latest", + "@types/react-dom": "latest", + "@vitejs/plugin-react": "latest", + "typescript": "latest", + "vite": "latest" + } +} diff --git a/examples/pigment-css-vite-ts/public/vite.svg b/examples/pigment-css-vite-ts/public/vite.svg new file mode 100644 index 00000000000000..e7b8dfb1b2a60b --- /dev/null +++ b/examples/pigment-css-vite-ts/public/vite.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/examples/pigment-css-vite-ts/src/App.tsx b/examples/pigment-css-vite-ts/src/App.tsx new file mode 100644 index 00000000000000..b3946c6bb8e9cd --- /dev/null +++ b/examples/pigment-css-vite-ts/src/App.tsx @@ -0,0 +1,202 @@ +import * as React from 'react'; +import { styled, css, keyframes } from '@pigment-css/react'; + +const scale = keyframes({ + to: { scale: 'var(--s2)' }, +}); + +const Link = styled('a', { shouldForwardProp: (prop) => prop !== 'outlined' })<{ + outlined?: boolean; +}>(({ theme }) => ({ + fontSize: '1rem', + background: 'rgba(0 0 0 / 0.04)', + padding: '0.5rem 1rem', + letterSpacing: '1px', + borderRadius: '4px', + textAlign: 'center', + ...theme.applyStyles('dark', { + background: 'rgba(255 255 255 / 0.1)', + }), + variants: [ + { + props: { outlined: true }, + style: { + background: 'transparent', + color: `hsl(${theme.vars.palette.primary})`, + border: `1px solid hsl(${theme.vars.palette.border})`, + }, + }, + ], +})); + +const Bubble = styled('span')({ + height: 'var(--size, 100%)', + aspectRatio: '1', + background: 'radial-gradient(hsl(var(--h) 100% 70%) 25%, transparent 50%)', + position: 'absolute', + display: 'inline-block', + left: 'var(--x, 0)', + top: 'var(--y, 0)', + scale: '0', + translate: '-50% -50%', + mixBlendMode: 'multiply', + filter: 'blur(2px)', + animation: `${scale} var(--s, 2s) var(--d, 0s) infinite alternate`, +}); + +function randomBetween(min: number, max: number) { + return Math.floor(Math.random() * (max - min + 1) + min); +} +function generateBubbleVars() { + return ` + --x: ${randomBetween(10, 90)}%; + --y: ${randomBetween(15, 85)}%; + --h: ${randomBetween(0, 360)}; + --s2: ${randomBetween(2, 6)}; + --d: -${randomBetween(250, 400) / 1000}s; + --s: ${randomBetween(3, 6)}s; + `; +} + +export default function Home() { + return ( +
+

({ + fontFamily: 'Kalnia, sans-serif', + fontSize: 'clamp(3rem, 1.9503rem + 4.4789vw, 6.25rem)', + fontWeight: 500, + textAlign: 'center', + position: 'relative', + display: 'flex', + alignItems: 'center', + color: '#888', + marginBottom: '1rem', + ...theme.applyStyles('dark', { color: '#fff' }), + }))}`} + > + Pigment CSS + ({ + position: 'absolute', + inset: '0', + background: 'white', + mixBlendMode: 'color-burn', + overflow: 'hidden', + pointerEvents: 'none', + ...theme.applyStyles('dark', { + mixBlendMode: 'darken', + filter: 'brightness(2)', + }), + }))} + > + + + + + + + + + + + +

+
+ CSS-JS library with static extraction +
+ +
*': { flex: 'auto' }, + })} + > + + Documentation + + + Roadmap + +
+
+ ); +} diff --git a/examples/pigment-css-vite-ts/src/augment.d.ts b/examples/pigment-css-vite-ts/src/augment.d.ts new file mode 100644 index 00000000000000..d10b46e01d4da0 --- /dev/null +++ b/examples/pigment-css-vite-ts/src/augment.d.ts @@ -0,0 +1,19 @@ +import type {} from '@pigment-css/react/theme'; +import type { ExtendTheme } from '@pigment-css/react'; + +declare module '@pigment-css/react/theme' { + export interface ThemeArgs { + theme: ExtendTheme<{ + colorScheme: 'light' | 'dark'; + tokens: { + palette: { + background: string; + foreground: string; + primary: string; + primaryForeground: string; + border: string; + }; + }; + }>; + } +} diff --git a/examples/pigment-css-vite-ts/src/globals.css b/examples/pigment-css-vite-ts/src/globals.css new file mode 100644 index 00000000000000..a1e5313f646dca --- /dev/null +++ b/examples/pigment-css-vite-ts/src/globals.css @@ -0,0 +1,16 @@ +* { + box-sizing: border-box; + padding: 0; + margin: 0; +} + +a { + color: inherit; + text-decoration: none; +} + +@media (prefers-color-scheme: dark) { + html { + color-scheme: dark; + } +} diff --git a/examples/pigment-css-vite-ts/src/main.tsx b/examples/pigment-css-vite-ts/src/main.tsx new file mode 100644 index 00000000000000..71b2cfa8aa8868 --- /dev/null +++ b/examples/pigment-css-vite-ts/src/main.tsx @@ -0,0 +1,11 @@ +import * as React from 'react'; +import * as ReactDOM from 'react-dom/client'; +import '@pigment-css/react/styles.css'; +import './globals.css'; +import App from './App'; + +ReactDOM.createRoot(document.getElementById('root')!).render( + + + , +); diff --git a/examples/pigment-css-vite-ts/src/vite-env.d.ts b/examples/pigment-css-vite-ts/src/vite-env.d.ts new file mode 100644 index 00000000000000..11f02fe2a0061d --- /dev/null +++ b/examples/pigment-css-vite-ts/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/examples/pigment-css-vite-ts/tsconfig.json b/examples/pigment-css-vite-ts/tsconfig.json new file mode 100644 index 00000000000000..3d0a51a86e2024 --- /dev/null +++ b/examples/pigment-css-vite-ts/tsconfig.json @@ -0,0 +1,21 @@ +{ + "compilerOptions": { + "target": "ESNext", + "useDefineForClassFields": true, + "lib": ["DOM", "DOM.Iterable", "ESNext"], + "allowJs": false, + "skipLibCheck": true, + "esModuleInterop": false, + "allowSyntheticDefaultImports": true, + "strict": true, + "forceConsistentCasingInFileNames": true, + "module": "ESNext", + "moduleResolution": "Node", + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx" + }, + "include": ["src"], + "references": [{ "path": "./tsconfig.node.json" }] +} diff --git a/examples/pigment-css-vite-ts/tsconfig.node.json b/examples/pigment-css-vite-ts/tsconfig.node.json new file mode 100644 index 00000000000000..9d31e2aed93c87 --- /dev/null +++ b/examples/pigment-css-vite-ts/tsconfig.node.json @@ -0,0 +1,9 @@ +{ + "compilerOptions": { + "composite": true, + "module": "ESNext", + "moduleResolution": "Node", + "allowSyntheticDefaultImports": true + }, + "include": ["vite.config.ts"] +} diff --git a/examples/pigment-css-vite-ts/vite.config.ts b/examples/pigment-css-vite-ts/vite.config.ts new file mode 100644 index 00000000000000..1a6ee2cd2f8942 --- /dev/null +++ b/examples/pigment-css-vite-ts/vite.config.ts @@ -0,0 +1,35 @@ +import { defineConfig } from 'vite'; +import react from '@vitejs/plugin-react'; +import { pigment, extendTheme } from '@pigment-css/vite-plugin'; + +// To learn more about theming, visit https://github.com/mui/material-ui/blob/master/packages/zero-runtime/README.md#theming +const theme = extendTheme({ + colorSchemes: { + light: { + palette: { + background: '0 0% 100%', + foreground: '240 10% 3.9%', + primary: '240 5.9% 10%', + border: '240 5.9% 90%', + }, + }, + dark: { + palette: { + background: '240 10% 3.9%', + foreground: '0 0% 80%', + primary: '0 0% 98%', + border: '240 3.7% 15.9%', + }, + }, + }, +}); + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [ + pigment({ + theme, + }), + react(), + ], +}); diff --git a/packages/pigment-react/README.md b/packages/pigment-react/README.md index 358a92fc19ba1f..ce49ec5b4ffd86 100644 --- a/packages/pigment-react/README.md +++ b/packages/pigment-react/README.md @@ -85,6 +85,17 @@ Finally, import the stylesheet in the root `layout.tsx` file: ### Vite +#### Starter template + +Use the following commands to create a new Vite project with Pigment CSS set up: + +```bash +curl https://codeload.github.com/mui/material-ui/tar.gz/master | tar -xz --strip=2 material-ui-master/examples/pigment-css-vite-ts +cd pigment-css-vite-ts +``` + +#### Manual installation + ```bash npm install @pigment-css/react npm install --save-dev @pigment-css/vite-plugin From 4e38622b9cbfd1d101b2eb479596f32d8ccee359 Mon Sep 17 00:00:00 2001 From: Brijesh Bittu Date: Fri, 8 Mar 2024 13:00:46 +0530 Subject: [PATCH 12/17] [pigment] Improve testing of fixtures (#41389) Co-authored-by: siriwatknp --- .eslintignore | 2 +- packages/pigment-react/package.json | 3 + packages/pigment-react/tests/README.md | 36 +++++++- packages/pigment-react/tests/css/css.test.ts | 40 ++++++++ .../tests/css/fixtures/css.input.js | 6 ++ .../tests/css/fixtures/css.output.css | 4 + .../tests/css/fixtures/css.output.js | 1 + .../tests/fixtures/styled.output.css | 6 -- .../tests/fixtures/styled.output.js | 17 ---- .../keyframes/fixtures/keyframes.input.js | 10 ++ .../keyframes/fixtures/keyframes.output.css | 8 ++ .../keyframes/fixtures/keyframes.output.js | 1 + .../tests/keyframes/keyframes.test.ts | 13 +++ packages/pigment-react/tests/pigment.test.ts | 81 ----------------- .../{ => styled}/fixtures/styled.input.js | 9 +- .../tests/styled/fixtures/styled.output.css | 28 ++++++ .../tests/styled/fixtures/styled.output.js | 16 ++++ .../pigment-react/tests/styled/styled.test.ts | 40 ++++++++ packages/pigment-react/tests/testUtils.ts | 91 +++++++++++++++++++ packages/pigment-react/tsconfig.json | 3 +- pnpm-lock.yaml | 6 ++ 21 files changed, 306 insertions(+), 115 deletions(-) create mode 100644 packages/pigment-react/tests/css/css.test.ts create mode 100644 packages/pigment-react/tests/css/fixtures/css.input.js create mode 100644 packages/pigment-react/tests/css/fixtures/css.output.css create mode 100644 packages/pigment-react/tests/css/fixtures/css.output.js delete mode 100644 packages/pigment-react/tests/fixtures/styled.output.css delete mode 100644 packages/pigment-react/tests/fixtures/styled.output.js create mode 100644 packages/pigment-react/tests/keyframes/fixtures/keyframes.input.js create mode 100644 packages/pigment-react/tests/keyframes/fixtures/keyframes.output.css create mode 100644 packages/pigment-react/tests/keyframes/fixtures/keyframes.output.js create mode 100644 packages/pigment-react/tests/keyframes/keyframes.test.ts delete mode 100644 packages/pigment-react/tests/pigment.test.ts rename packages/pigment-react/tests/{ => styled}/fixtures/styled.input.js (75%) create mode 100644 packages/pigment-react/tests/styled/fixtures/styled.output.css create mode 100644 packages/pigment-react/tests/styled/fixtures/styled.output.js create mode 100644 packages/pigment-react/tests/styled/styled.test.ts create mode 100644 packages/pigment-react/tests/testUtils.ts diff --git a/.eslintignore b/.eslintignore index 40b0fcc4a9fce1..5043b4bb97f494 100644 --- a/.eslintignore +++ b/.eslintignore @@ -23,7 +23,7 @@ /packages/pigment-react/processors/ /packages/pigment-react/exports/ /packages/pigment-react/theme/ -/packages/pigment-react/tests/fixtures/ +/packages/pigment-react/tests/**/fixtures /packages/pigment-nextjs-plugin/loader.js # Ignore fixtures /packages-internal/scripts/typescript-to-proptypes/test/*/* diff --git a/packages/pigment-react/package.json b/packages/pigment-react/package.json index 21780ca073c0f6..f82fe99d7c1855 100644 --- a/packages/pigment-react/package.json +++ b/packages/pigment-react/package.json @@ -26,6 +26,7 @@ "copy-license": "node ../../scripts/pigment-license.mjs", "build": "tsup", "test": "cd ../../ && cross-env NODE_ENV=test mocha 'packages/pigment-react/**/*.test.{js,ts,tsx}'", + "test:update": "cd ../../ && cross-env NODE_ENV=test UPDATE_FIXTURES=true mocha 'packages/pigment-react/**/*.test.{js,ts,tsx}'", "test:ci": "cd ../../ && cross-env NODE_ENV=test BABEL_ENV=coverage nyc --reporter=lcov --report-dir=./coverage/pigment-react mocha 'packages/pigment-react/**/*.test.{js,ts,tsx}'", "typecheck": "tsc --noEmit -p ." }, @@ -53,12 +54,14 @@ "@types/babel__core": "^7.20.5", "@types/babel__helper-module-imports": "^7.18.3", "@types/babel__helper-plugin-utils": "^7.10.3", + "@types/chai": "^4.3.12", "@types/cssesc": "^3.0.2", "@types/lodash": "^4.14.202", "@types/node": "^18.19.21", "@types/react": "^18.2.55", "@types/stylis": "^4.2.5", "chai": "^4.4.1", + "prettier": "^3.2.5", "react": "^18.2.0" }, "peerDependencies": { diff --git a/packages/pigment-react/tests/README.md b/packages/pigment-react/tests/README.md index 3508b94a7ac824..5da4275ebf7d59 100644 --- a/packages/pigment-react/tests/README.md +++ b/packages/pigment-react/tests/README.md @@ -1,3 +1,35 @@ -# Adding new fixtures +# Pigment CSS testing -Create a new file name with `[name].input.js` and add `styled`, `css` or other zero-runtime calls into the file. Also add equivalent `[name].output.js` and `[name].output.css` and run the test. After the new test fails, get the results from the received output and add it to the equivalent js and css files. This is equivalent to snapshot testing and will make sure any change in internal css generation logic does not fail any other existing tests. +## Folder structure + +- `tests` is the root folder for all tests + - `fixtures` contains all the fixtures for the tests + - `*.input.js` are the input files created manually + - `*.output.*` are the expected output files created by running the tests + - `*.test.js` are the test files that run the fixtures + +## Running tests + +At the root project terminal: + +```bash +pnpm nx run @pigment-css/react:test +``` + +To update the output fixtures: + +```bash +pnpm nx run @pigment-css/react:test:update +``` + +## Adding new tests + +Each folder inside `tests` is a Pigment CSS feature. To add a new test, create a new folder with the feature name and add a new test file with the `.test.js` extension. Inside the test file, import the fixtures and run the tests. + +## Adding new fixtures + +Create a new file name with `[name].input.js` and add `styled`, `css` or other Pigment CSS calls into the file. + +The first time you run the tests, the output files will be created automatically. Then check the output files to make sure they are correct. + +For any implementation changes after that, if you want to update the output files, run the tests with the `test:update` script. diff --git a/packages/pigment-react/tests/css/css.test.ts b/packages/pigment-react/tests/css/css.test.ts new file mode 100644 index 00000000000000..d926f6de9a4162 --- /dev/null +++ b/packages/pigment-react/tests/css/css.test.ts @@ -0,0 +1,40 @@ +import path from 'node:path'; +import { runTransformation, expect } from '../testUtils'; + +const theme = { + palette: { + primary: { + main: 'red', + }, + }, + size: { + font: { + h1: '3rem', + }, + }, + components: { + MuiSlider: { + styleOverrides: { + rail: { + fontSize: '3rem', + }, + }, + }, + }, +}; + +describe('Pigment CSS - css', () => { + it('basics', async () => { + const { output, fixture } = await runTransformation( + path.join(__dirname, 'fixtures/css.input.js'), + { + themeArgs: { + theme, + }, + }, + ); + + expect(output.js).to.equal(fixture.js); + expect(output.css).to.equal(fixture.css); + }); +}); diff --git a/packages/pigment-react/tests/css/fixtures/css.input.js b/packages/pigment-react/tests/css/fixtures/css.input.js new file mode 100644 index 00000000000000..24519d991b09ac --- /dev/null +++ b/packages/pigment-react/tests/css/fixtures/css.input.js @@ -0,0 +1,6 @@ +import { css } from '@pigment-css/react'; + +const cls1 = css` + color: ${({ theme }) => theme.palette.primary.main}; + font-size: ${({ theme }) => theme.size.font.h1}; +`; diff --git a/packages/pigment-react/tests/css/fixtures/css.output.css b/packages/pigment-react/tests/css/fixtures/css.output.css new file mode 100644 index 00000000000000..e34d404861da6d --- /dev/null +++ b/packages/pigment-react/tests/css/fixtures/css.output.css @@ -0,0 +1,4 @@ +.c1wr0t7p { + color: red; + font-size: 3rem; +} diff --git a/packages/pigment-react/tests/css/fixtures/css.output.js b/packages/pigment-react/tests/css/fixtures/css.output.js new file mode 100644 index 00000000000000..afbe5dcc772db6 --- /dev/null +++ b/packages/pigment-react/tests/css/fixtures/css.output.js @@ -0,0 +1 @@ +const cls1 = 'c1wr0t7p'; diff --git a/packages/pigment-react/tests/fixtures/styled.output.css b/packages/pigment-react/tests/fixtures/styled.output.css deleted file mode 100644 index 64f8bb93f42fc4..00000000000000 --- a/packages/pigment-react/tests/fixtures/styled.output.css +++ /dev/null @@ -1,6 +0,0 @@ -@keyframes r1ub6j9g{from{transform:rotate(360deg);}to{transform:rotate(0deg);}} -.c1y26wbb{color:red;animation:r1ub6j9g 2s ease-out 0s infinite;} -.ct00dwm{color:red;font-size:3rem;} -.soujkwr{display:block;position:absolute;border-radius:inherit;background-color:currentColor;opacity:0.38;font-size:3rem;} -.soujkwr-1{font-size:3rem;} -.s14dtw5g{display:block;opacity:0.38;font-size:3rem;} diff --git a/packages/pigment-react/tests/fixtures/styled.output.js b/packages/pigment-react/tests/fixtures/styled.output.js deleted file mode 100644 index bfc47724a88307..00000000000000 --- a/packages/pigment-react/tests/fixtures/styled.output.js +++ /dev/null @@ -1,17 +0,0 @@ -import { styled as _styled3 } from "@pigment-css/react"; -import { styled as _styled2 } from "@pigment-css/react"; -import { styled as _styled } from "@pigment-css/react"; -import _theme from "@pigment-css/react/theme"; -const Component = /*#__PURE__*/_styled("div")({ - classes: ["c1y26wbb"] -}); -const cls1 = "ct00dwm"; -const SliderRail = /*#__PURE__*/_styled2("span", { - name: 'MuiSlider', - slot: 'Rail' -})({ - classes: ["soujkwr", "soujkwr-1"] -}); -const SliderRail2 = /*#__PURE__*/_styled3("span")({ - classes: ["s14dtw5g"] -}); diff --git a/packages/pigment-react/tests/keyframes/fixtures/keyframes.input.js b/packages/pigment-react/tests/keyframes/fixtures/keyframes.input.js new file mode 100644 index 00000000000000..8ff26f9d247b2b --- /dev/null +++ b/packages/pigment-react/tests/keyframes/fixtures/keyframes.input.js @@ -0,0 +1,10 @@ +import { keyframes } from '@pigment-css/react'; + +const rotateKeyframe = keyframes({ + from: { + transform: 'rotate(360deg)', + }, + to: { + transform: 'rotate(0deg)', + }, +}); diff --git a/packages/pigment-react/tests/keyframes/fixtures/keyframes.output.css b/packages/pigment-react/tests/keyframes/fixtures/keyframes.output.css new file mode 100644 index 00000000000000..43ac2b3341e9fb --- /dev/null +++ b/packages/pigment-react/tests/keyframes/fixtures/keyframes.output.css @@ -0,0 +1,8 @@ +@keyframes r14c1bqo { + from { + transform: rotate(360deg); + } + to { + transform: rotate(0deg); + } +} diff --git a/packages/pigment-react/tests/keyframes/fixtures/keyframes.output.js b/packages/pigment-react/tests/keyframes/fixtures/keyframes.output.js new file mode 100644 index 00000000000000..2df1df78892cc8 --- /dev/null +++ b/packages/pigment-react/tests/keyframes/fixtures/keyframes.output.js @@ -0,0 +1 @@ +const rotateKeyframe = 'r14c1bqo'; diff --git a/packages/pigment-react/tests/keyframes/keyframes.test.ts b/packages/pigment-react/tests/keyframes/keyframes.test.ts new file mode 100644 index 00000000000000..337dedbaf76fdb --- /dev/null +++ b/packages/pigment-react/tests/keyframes/keyframes.test.ts @@ -0,0 +1,13 @@ +import path from 'node:path'; +import { runTransformation, expect } from '../testUtils'; + +describe('Pigment CSS - keyframes', () => { + it('basics', async () => { + const { output, fixture } = await runTransformation( + path.join(__dirname, 'fixtures/keyframes.input.js'), + ); + + expect(output.js).to.equal(fixture.js); + expect(output.css).to.equal(fixture.css); + }); +}); diff --git a/packages/pigment-react/tests/pigment.test.ts b/packages/pigment-react/tests/pigment.test.ts deleted file mode 100644 index 34addd06ead91f..00000000000000 --- a/packages/pigment-react/tests/pigment.test.ts +++ /dev/null @@ -1,81 +0,0 @@ -import * as fs from 'node:fs'; -import * as path from 'node:path'; -import { expect } from 'chai'; -import { asyncResolveFallback } from '@wyw-in-js/shared'; -import { TransformCacheCollection, transform, createFileReporter } from '@wyw-in-js/transform'; -import { preprocessor } from '@pigment-css/react/utils'; - -const files = fs.readdirSync(path.join(__dirname, 'fixtures')); - -const theme = { - palette: { - primary: { - main: 'red', - }, - }, - size: { - font: { - h1: '3rem', - }, - }, - components: { - MuiSlider: { - styleOverrides: { - rail: { - fontSize: '3rem', - }, - }, - }, - }, -}; - -describe('zero-runtime', () => { - files.forEach((file) => { - it(`test input file ${file}`, async () => { - if (file.includes('.output.')) { - return; - } - const cache = new TransformCacheCollection(); - const { emitter: eventEmitter } = createFileReporter(false); - const inputFilePath = path.join(__dirname, 'fixtures', file); - const outputFilePath = path.join(__dirname, 'fixtures', file.replace('.input.', '.output.')); - const outputCssFilePath = path.join( - __dirname, - 'fixtures', - file.replace('.input.js', '.output.css'), - ); - const inputContent = fs.readFileSync(inputFilePath, 'utf8'); - const outputContent = fs.readFileSync(outputFilePath, 'utf8'); - const outputCssContent = fs.readFileSync(outputCssFilePath, 'utf8'); - - const pluginOptions = { - themeArgs: { - theme, - }, - babelOptions: { - configFile: false, - babelrc: false, - }, - tagResolver(_source: string, tag: string) { - return require.resolve(`../exports/${tag}`); - }, - }; - const result = await transform( - { - options: { - filename: inputFilePath, - preprocessor, - pluginOptions, - }, - cache, - eventEmitter, - }, - inputContent, - asyncResolveFallback, - ); - - expect(result.cssText).to.equal(outputCssContent); - expect(result.code.trim()).to.equal(outputContent.trim()); - }); - }); -}); diff --git a/packages/pigment-react/tests/fixtures/styled.input.js b/packages/pigment-react/tests/styled/fixtures/styled.input.js similarity index 75% rename from packages/pigment-react/tests/fixtures/styled.input.js rename to packages/pigment-react/tests/styled/fixtures/styled.input.js index 0d08d32f0886b6..8e2c133a04dacd 100644 --- a/packages/pigment-react/tests/fixtures/styled.input.js +++ b/packages/pigment-react/tests/styled/fixtures/styled.input.js @@ -1,4 +1,4 @@ -import { styled, keyframes, css } from '@pigment-css/react'; +import { styled, keyframes } from '@pigment-css/react'; const rotateKeyframe = keyframes({ from: { @@ -14,16 +14,11 @@ const Component = styled.div(({ theme }) => ({ animation: `${rotateKeyframe} 2s ease-out 0s infinite`, })); -const cls1 = css` - color: ${({ theme }) => theme.palette.primary.main}; - font-size: ${({ theme }) => theme.size.font.h1}; -`; - const SliderRail = styled('span', { name: 'MuiSlider', slot: 'Rail', })` - display: block; + display: none; position: absolute; border-radius: inherit; background-color: currentColor; diff --git a/packages/pigment-react/tests/styled/fixtures/styled.output.css b/packages/pigment-react/tests/styled/fixtures/styled.output.css new file mode 100644 index 00000000000000..79471464008abb --- /dev/null +++ b/packages/pigment-react/tests/styled/fixtures/styled.output.css @@ -0,0 +1,28 @@ +@keyframes r1419f2q { + from { + transform: rotate(360deg); + } + to { + transform: rotate(0deg); + } +} +.c1vtarpi { + color: red; + animation: r1419f2q 2s ease-out 0s infinite; +} +.s1sjy0ja { + display: none; + position: absolute; + border-radius: inherit; + background-color: currentColor; + opacity: 0.38; + font-size: 3rem; +} +.s1sjy0ja-1 { + font-size: 3rem; +} +.s6hrafw { + display: block; + opacity: 0.38; + font-size: 3rem; +} diff --git a/packages/pigment-react/tests/styled/fixtures/styled.output.js b/packages/pigment-react/tests/styled/fixtures/styled.output.js new file mode 100644 index 00000000000000..d5519e6a7a196b --- /dev/null +++ b/packages/pigment-react/tests/styled/fixtures/styled.output.js @@ -0,0 +1,16 @@ +import { styled as _styled3 } from '@pigment-css/react'; +import { styled as _styled2 } from '@pigment-css/react'; +import { styled as _styled } from '@pigment-css/react'; +import _theme from '@pigment-css/react/theme'; +const Component = /*#__PURE__*/ _styled('div')({ + classes: ['c1vtarpi'], +}); +const SliderRail = /*#__PURE__*/ _styled2('span', { + name: 'MuiSlider', + slot: 'Rail', +})({ + classes: ['s1sjy0ja', 's1sjy0ja-1'], +}); +const SliderRail2 = /*#__PURE__*/ _styled3('span')({ + classes: ['s6hrafw'], +}); diff --git a/packages/pigment-react/tests/styled/styled.test.ts b/packages/pigment-react/tests/styled/styled.test.ts new file mode 100644 index 00000000000000..4827244fa29ca9 --- /dev/null +++ b/packages/pigment-react/tests/styled/styled.test.ts @@ -0,0 +1,40 @@ +import path from 'node:path'; +import { runTransformation, expect } from '../testUtils'; + +const theme = { + palette: { + primary: { + main: 'red', + }, + }, + size: { + font: { + h1: '3rem', + }, + }, + components: { + MuiSlider: { + styleOverrides: { + rail: { + fontSize: '3rem', + }, + }, + }, + }, +}; + +describe('Pigment CSS - styled', () => { + it('basics', async () => { + const { output, fixture } = await runTransformation( + path.join(__dirname, 'fixtures/styled.input.js'), + { + themeArgs: { + theme, + }, + }, + ); + + expect(output.js).to.equal(fixture.js); + expect(output.css).to.equal(fixture.css); + }); +}); diff --git a/packages/pigment-react/tests/testUtils.ts b/packages/pigment-react/tests/testUtils.ts new file mode 100644 index 00000000000000..88ff6ab98a3c38 --- /dev/null +++ b/packages/pigment-react/tests/testUtils.ts @@ -0,0 +1,91 @@ +import * as fs from 'node:fs'; +import * as path from 'node:path'; +import { expect as chaiExpect } from 'chai'; +import { asyncResolveFallback } from '@wyw-in-js/shared'; +import { TransformCacheCollection, transform, createFileReporter } from '@wyw-in-js/transform'; +import { preprocessor } from '@pigment-css/react/utils'; +import * as prettier from 'prettier'; + +const shouldUpdateOutput = process.env.UPDATE_FIXTURES === 'true'; + +export async function runTransformation( + absolutePath: string, + options?: { themeArgs?: { theme?: any } }, +) { + const cache = new TransformCacheCollection(); + const { emitter: eventEmitter } = createFileReporter(false); + const inputFilePath = absolutePath; + const outputFilePath = absolutePath.replace('.input.', '.output.'); + const outputCssFilePath = absolutePath.replace('.input.js', '.output.css'); + + const inputContent = fs.readFileSync(inputFilePath, 'utf8'); + let outputContent = fs.existsSync(outputFilePath) ? fs.readFileSync(outputFilePath, 'utf8') : ''; + let outputCssContent = fs.existsSync(outputCssFilePath) + ? fs.readFileSync(outputCssFilePath, 'utf8') + : ''; + + const pluginOptions = { + themeArgs: { + theme: options?.themeArgs?.theme, + }, + babelOptions: { + configFile: false, + babelrc: false, + }, + tagResolver(_source: string, tag: string) { + return require.resolve(`../exports/${tag}`); + }, + }; + const result = await transform( + { + options: { + filename: inputFilePath, + preprocessor, + pluginOptions, + }, + cache, + eventEmitter, + }, + inputContent, + asyncResolveFallback, + ); + + const prettierConfig = await prettier.resolveConfig( + path.join(process.cwd(), 'prettier.config.js'), + ); + const formattedJs = await prettier.format(result.code, { + ...prettierConfig, + parser: 'babel', + }); + const formattedCss = await prettier.format(result.cssText ?? '', { + ...prettierConfig, + parser: 'css', + }); + + if (!outputContent || shouldUpdateOutput) { + fs.writeFileSync(outputFilePath, formattedJs, 'utf-8'); + outputContent = formattedJs; + } + + if (!outputCssContent || shouldUpdateOutput) { + fs.writeFileSync(outputCssFilePath, formattedCss, 'utf-8'); + outputCssContent = formattedCss; + } + + return { + output: { + js: formattedJs, + css: formattedCss, + }, + fixture: { + js: outputContent, + css: outputCssContent, + }, + }; +} + +export function expect(val: any): ReturnType { + const CUSTOM_ERROR = + 'The file contents have changed. Run "test:update" command to update the file if this is expected.'; + return chaiExpect(val, CUSTOM_ERROR); +} diff --git a/packages/pigment-react/tsconfig.json b/packages/pigment-react/tsconfig.json index 1e4e9fefe33535..55ad292e876743 100644 --- a/packages/pigment-react/tsconfig.json +++ b/packages/pigment-react/tsconfig.json @@ -12,7 +12,8 @@ "@mui/system/*": ["./packages/mui-system/src/*"], "@mui/utils": ["./packages/mui-utils/src"], "@mui/utils/*": ["./packages/mui-utils/src/*"] - } + }, + "types": ["node", "mocha"] }, "include": ["src/**/*.ts"], "exclude": ["./tsup.config.ts"] diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index aa2fd7b0363311..a12a5fec79085a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -2161,6 +2161,9 @@ importers: '@types/babel__helper-plugin-utils': specifier: ^7.10.3 version: 7.10.3 + '@types/chai': + specifier: ^4.3.12 + version: 4.3.12 '@types/cssesc': specifier: ^3.0.2 version: 3.0.2 @@ -2179,6 +2182,9 @@ importers: chai: specifier: ^4.4.1 version: 4.4.1 + prettier: + specifier: ^3.2.5 + version: 3.2.5 react: specifier: ^18.2.0 version: 18.2.0 From dab76c7968bb94289519cac28aab88332f8c984b Mon Sep 17 00:00:00 2001 From: Jan Potoms <2109932+Janpot@users.noreply.github.com> Date: Fri, 8 Mar 2024 08:34:07 +0100 Subject: [PATCH 13/17] [code-infra] Add short note about e2e-website workflow schedule (#41355) --- .circleci/config.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index 74a0ab30b61d76..c10fe610fb550f 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -385,6 +385,7 @@ jobs: name: Test umd release command: pnpm test:umd test_e2e_website: + # NOTE: This workflow runs after successful docs deploy. See /test/e2e-website/README.md#ci <<: *defaults docker: - image: mcr.microsoft.com/playwright:v1.42.1-focal From 1e80616d3dde536227af56d49f91b28f830bd33c Mon Sep 17 00:00:00 2001 From: Marija Najdova Date: Fri, 8 Mar 2024 11:21:44 +0100 Subject: [PATCH 14/17] [RtlProvider] Add component & hook (#41241) --- packages/mui-material/src/Drawer/Drawer.js | 8 +++-- .../src/LinearProgress/LinearProgress.js | 8 ++--- packages/mui-material/src/Menu/Menu.js | 9 +++--- .../mui-material/src/MenuList/MenuList.js | 4 +-- .../src/PaginationItem/PaginationItem.js | 31 +++++++++--------- packages/mui-material/src/Rating/Rating.js | 6 ++-- packages/mui-material/src/Slider/Slider.js | 5 ++- .../src/TabScrollButton/TabScrollButton.js | 5 ++- .../TablePagination/TablePaginationActions.js | 32 ++++++++----------- packages/mui-material/src/Tabs/Tabs.js | 5 +-- packages/mui-material/src/Tooltip/Tooltip.js | 3 +- .../mui-system/src/RtlProvider/index.d.ts | 11 +++++++ packages/mui-system/src/RtlProvider/index.js | 20 ++++++++++++ .../src/ThemeProvider/ThemeProvider.js | 4 ++- .../src/ThemeProvider/ThemeProvider.test.js | 22 +++++++++++++ packages/mui-system/src/index.js | 2 ++ .../zero-runtime/src/RtlProvider/index.ts | 1 + 17 files changed, 116 insertions(+), 60 deletions(-) create mode 100644 packages/mui-system/src/RtlProvider/index.d.ts create mode 100644 packages/mui-system/src/RtlProvider/index.js create mode 100644 packages/zero-runtime/src/RtlProvider/index.ts diff --git a/packages/mui-material/src/Drawer/Drawer.js b/packages/mui-material/src/Drawer/Drawer.js index eef70ec528fcd4..1849bd69a9d238 100644 --- a/packages/mui-material/src/Drawer/Drawer.js +++ b/packages/mui-material/src/Drawer/Drawer.js @@ -4,6 +4,7 @@ import PropTypes from 'prop-types'; import clsx from 'clsx'; import integerPropType from '@mui/utils/integerPropType'; import composeClasses from '@mui/utils/composeClasses'; +import { useRtl } from '@mui/system/RtlProvider'; import Modal from '../Modal'; import Slide from '../Slide'; import Paper from '../Paper'; @@ -137,8 +138,8 @@ export function isHorizontal(anchor) { return ['left', 'right'].indexOf(anchor) !== -1; } -export function getAnchor(theme, anchor) { - return theme.direction === 'rtl' && isHorizontal(anchor) ? oppositeDirection[anchor] : anchor; +export function getAnchor({ direction }, anchor) { + return direction === 'rtl' && isHorizontal(anchor) ? oppositeDirection[anchor] : anchor; } /** @@ -148,6 +149,7 @@ export function getAnchor(theme, anchor) { const Drawer = React.forwardRef(function Drawer(inProps, ref) { const props = useThemeProps({ props: inProps, name: 'MuiDrawer' }); const theme = useTheme(); + const isRtl = useRtl(); const defaultTransitionDuration = { enter: theme.transitions.duration.enteringScreen, exit: theme.transitions.duration.leavingScreen, @@ -180,7 +182,7 @@ const Drawer = React.forwardRef(function Drawer(inProps, ref) { mounted.current = true; }, []); - const anchorInvariant = getAnchor(theme, anchorProp); + const anchorInvariant = getAnchor({ direction: isRtl ? 'rtl' : 'ltr' }, anchorProp); const anchor = anchorProp; const ownerState = { diff --git a/packages/mui-material/src/LinearProgress/LinearProgress.js b/packages/mui-material/src/LinearProgress/LinearProgress.js index 4ca989cf10592e..bb9d8b85cafb3d 100644 --- a/packages/mui-material/src/LinearProgress/LinearProgress.js +++ b/packages/mui-material/src/LinearProgress/LinearProgress.js @@ -5,8 +5,8 @@ import clsx from 'clsx'; import composeClasses from '@mui/utils/composeClasses'; import { keyframes, css } from '@mui/system'; import { darken, lighten } from '@mui/system/colorManipulator'; +import { useRtl } from '@mui/system/RtlProvider'; import capitalize from '../utils/capitalize'; -import useTheme from '../styles/useTheme'; import styled from '../styles/styled'; import useThemeProps from '../styles/useThemeProps'; import { getLinearProgressUtilityClass } from './linearProgressClasses'; @@ -283,7 +283,7 @@ const LinearProgress = React.forwardRef(function LinearProgress(inProps, ref) { }; const classes = useUtilityClasses(ownerState); - const theme = useTheme(); + const isRtl = useRtl(); const rootProps = {}; const inlineStyles = { bar1: {}, bar2: {} }; @@ -294,7 +294,7 @@ const LinearProgress = React.forwardRef(function LinearProgress(inProps, ref) { rootProps['aria-valuemin'] = 0; rootProps['aria-valuemax'] = 100; let transform = value - 100; - if (theme.direction === 'rtl') { + if (isRtl) { transform = -transform; } inlineStyles.bar1.transform = `translateX(${transform}%)`; @@ -308,7 +308,7 @@ const LinearProgress = React.forwardRef(function LinearProgress(inProps, ref) { if (variant === 'buffer') { if (valueBuffer !== undefined) { let transform = (valueBuffer || 0) - 100; - if (theme.direction === 'rtl') { + if (isRtl) { transform = -transform; } inlineStyles.bar2.transform = `translateX(${transform}%)`; diff --git a/packages/mui-material/src/Menu/Menu.js b/packages/mui-material/src/Menu/Menu.js index 0a292d37b0d2cf..8fb919b0fb70da 100644 --- a/packages/mui-material/src/Menu/Menu.js +++ b/packages/mui-material/src/Menu/Menu.js @@ -6,10 +6,10 @@ import clsx from 'clsx'; import composeClasses from '@mui/utils/composeClasses'; import { useSlotProps } from '@mui/base/utils'; import HTMLElementType from '@mui/utils/HTMLElementType'; +import { useRtl } from '@mui/system/RtlProvider'; import MenuList from '../MenuList'; import Popover, { PopoverPaper } from '../Popover'; import styled, { rootShouldForwardProp } from '../styles/styled'; -import useTheme from '../styles/useTheme'; import useThemeProps from '../styles/useThemeProps'; import { getMenuUtilityClass } from './menuClasses'; @@ -85,8 +85,7 @@ const Menu = React.forwardRef(function Menu(inProps, ref) { ...other } = props; - const theme = useTheme(); - const isRtl = theme.direction === 'rtl'; + const isRtl = useRtl(); const ownerState = { ...props, @@ -108,7 +107,9 @@ const Menu = React.forwardRef(function Menu(inProps, ref) { const handleEntering = (element, isAppearing) => { if (menuListActionsRef.current) { - menuListActionsRef.current.adjustStyleForScrollbar(element, theme); + menuListActionsRef.current.adjustStyleForScrollbar(element, { + direction: isRtl ? 'rtl' : 'ltr', + }); } if (onEntering) { diff --git a/packages/mui-material/src/MenuList/MenuList.js b/packages/mui-material/src/MenuList/MenuList.js index c25b1ac6c4bdb6..29b80a6a3ec953 100644 --- a/packages/mui-material/src/MenuList/MenuList.js +++ b/packages/mui-material/src/MenuList/MenuList.js @@ -125,13 +125,13 @@ const MenuList = React.forwardRef(function MenuList(props, ref) { React.useImperativeHandle( actions, () => ({ - adjustStyleForScrollbar: (containerElement, theme) => { + adjustStyleForScrollbar: (containerElement, { direction }) => { // Let's ignore that piece of logic if users are already overriding the width // of the menu. const noExplicitWidth = !listRef.current.style.width; if (containerElement.clientHeight < listRef.current.clientHeight && noExplicitWidth) { const scrollbarSize = `${getScrollbarSize(ownerDocument(containerElement))}px`; - listRef.current.style[theme.direction === 'rtl' ? 'paddingLeft' : 'paddingRight'] = + listRef.current.style[direction === 'rtl' ? 'paddingLeft' : 'paddingRight'] = scrollbarSize; listRef.current.style.width = `calc(100% + ${scrollbarSize})`; } diff --git a/packages/mui-material/src/PaginationItem/PaginationItem.js b/packages/mui-material/src/PaginationItem/PaginationItem.js index 715286a41a4458..3f51141d0587a9 100644 --- a/packages/mui-material/src/PaginationItem/PaginationItem.js +++ b/packages/mui-material/src/PaginationItem/PaginationItem.js @@ -4,9 +4,9 @@ import PropTypes from 'prop-types'; import clsx from 'clsx'; import composeClasses from '@mui/utils/composeClasses'; import { alpha } from '@mui/system/colorManipulator'; +import { useRtl } from '@mui/system/RtlProvider'; import useThemeProps from '../styles/useThemeProps'; import paginationItemClasses, { getPaginationItemUtilityClass } from './paginationItemClasses'; -import useTheme from '../styles/useTheme'; import ButtonBase from '../ButtonBase'; import capitalize from '../utils/capitalize'; import FirstPageIcon from '../internal/svg-icons/FirstPage'; @@ -288,23 +288,22 @@ const PaginationItem = React.forwardRef(function PaginationItem(inProps, ref) { variant, }; - const theme = useTheme(); + const isRtl = useRtl(); const classes = useUtilityClasses(ownerState); - const normalizedIcons = - theme.direction === 'rtl' - ? { - previous: slots.next || components.next || NavigateNextIcon, - next: slots.previous || components.previous || NavigateBeforeIcon, - last: slots.first || components.first || FirstPageIcon, - first: slots.last || components.last || LastPageIcon, - } - : { - previous: slots.previous || components.previous || NavigateBeforeIcon, - next: slots.next || components.next || NavigateNextIcon, - first: slots.first || components.first || FirstPageIcon, - last: slots.last || components.last || LastPageIcon, - }; + const normalizedIcons = isRtl + ? { + previous: slots.next || components.next || NavigateNextIcon, + next: slots.previous || components.previous || NavigateBeforeIcon, + last: slots.first || components.first || FirstPageIcon, + first: slots.last || components.last || LastPageIcon, + } + : { + previous: slots.previous || components.previous || NavigateBeforeIcon, + next: slots.next || components.next || NavigateNextIcon, + first: slots.first || components.first || FirstPageIcon, + last: slots.last || components.last || LastPageIcon, + }; const Icon = normalizedIcons[type]; diff --git a/packages/mui-material/src/Rating/Rating.js b/packages/mui-material/src/Rating/Rating.js index dcf47bd122362d..882e57bcf04e24 100644 --- a/packages/mui-material/src/Rating/Rating.js +++ b/packages/mui-material/src/Rating/Rating.js @@ -6,7 +6,7 @@ import clamp from '@mui/utils/clamp'; import visuallyHidden from '@mui/utils/visuallyHidden'; import chainPropTypes from '@mui/utils/chainPropTypes'; import composeClasses from '@mui/utils/composeClasses'; -import useTheme from '../styles/useTheme'; +import { useRtl } from '@mui/system/RtlProvider'; import { capitalize, useForkRef, @@ -329,7 +329,7 @@ const Rating = React.forwardRef(function Rating(inProps, ref) { }); const valueRounded = roundValueToPrecision(valueDerived, precision); - const theme = useTheme(); + const isRtl = useRtl(); const [{ hover, focus }, setState] = React.useState({ hover: -1, focus: -1, @@ -364,7 +364,7 @@ const Rating = React.forwardRef(function Rating(inProps, ref) { let percent; - if (theme.direction === 'rtl') { + if (isRtl) { percent = (right - event.clientX) / containerWidth; } else { percent = (event.clientX - left) / containerWidth; diff --git a/packages/mui-material/src/Slider/Slider.js b/packages/mui-material/src/Slider/Slider.js index 42b1c83a4757cf..096e13f1c25555 100644 --- a/packages/mui-material/src/Slider/Slider.js +++ b/packages/mui-material/src/Slider/Slider.js @@ -7,9 +7,9 @@ import { isHostComponent, useSlotProps } from '@mui/base/utils'; import composeClasses from '@mui/utils/composeClasses'; import { useSlider, valueToPercent } from '@mui/base/useSlider'; import { alpha, lighten, darken } from '@mui/system/colorManipulator'; +import { useRtl } from '@mui/system/RtlProvider'; import useThemeProps from '../styles/useThemeProps'; import styled, { slotShouldForwardProp } from '../styles/styled'; -import useTheme from '../styles/useTheme'; import shouldSpreadAdditionalProps from '../utils/shouldSpreadAdditionalProps'; import capitalize from '../utils/capitalize'; import BaseSliderValueLabel from './SliderValueLabel'; @@ -404,8 +404,7 @@ const Forward = ({ children }) => children; const Slider = React.forwardRef(function Slider(inputProps, ref) { const props = useThemeProps({ props: inputProps, name: 'MuiSlider' }); - const theme = useTheme(); - const isRtl = theme.direction === 'rtl'; + const isRtl = useRtl(); const { 'aria-label': ariaLabel, diff --git a/packages/mui-material/src/TabScrollButton/TabScrollButton.js b/packages/mui-material/src/TabScrollButton/TabScrollButton.js index c2fdad8acb5f9e..9dcdd8fe8efbc3 100644 --- a/packages/mui-material/src/TabScrollButton/TabScrollButton.js +++ b/packages/mui-material/src/TabScrollButton/TabScrollButton.js @@ -5,10 +5,10 @@ import PropTypes from 'prop-types'; import clsx from 'clsx'; import { useSlotProps } from '@mui/base/utils'; import composeClasses from '@mui/utils/composeClasses'; +import { useRtl } from '@mui/system/RtlProvider'; import KeyboardArrowLeft from '../internal/svg-icons/KeyboardArrowLeft'; import KeyboardArrowRight from '../internal/svg-icons/KeyboardArrowRight'; import ButtonBase from '../ButtonBase'; -import useTheme from '../styles/useTheme'; import useThemeProps from '../styles/useThemeProps'; import styled from '../styles/styled'; import tabScrollButtonClasses, { getTabScrollButtonUtilityClass } from './tabScrollButtonClasses'; @@ -59,8 +59,7 @@ const TabScrollButton = React.forwardRef(function TabScrollButton(inProps, ref) ...other } = props; - const theme = useTheme(); - const isRtl = theme.direction === 'rtl'; + const isRtl = useRtl(); const ownerState = { isRtl, ...props }; diff --git a/packages/mui-material/src/TablePagination/TablePaginationActions.js b/packages/mui-material/src/TablePagination/TablePaginationActions.js index 7a792e706e5d36..99f8ffae9f2b4d 100644 --- a/packages/mui-material/src/TablePagination/TablePaginationActions.js +++ b/packages/mui-material/src/TablePagination/TablePaginationActions.js @@ -1,9 +1,9 @@ 'use client'; import * as React from 'react'; import PropTypes from 'prop-types'; +import { useRtl } from '@mui/system/RtlProvider'; import KeyboardArrowLeft from '../internal/svg-icons/KeyboardArrowLeft'; import KeyboardArrowRight from '../internal/svg-icons/KeyboardArrowRight'; -import useTheme from '../styles/useTheme'; import IconButton from '../IconButton'; import LastPageIconDefault from '../internal/svg-icons/LastPage'; import FirstPageIconDefault from '../internal/svg-icons/FirstPage'; @@ -28,7 +28,7 @@ const TablePaginationActions = React.forwardRef(function TablePaginationActions( ...other } = props; - const theme = useTheme(); + const isRtl = useRtl(); const handleFirstPageButtonClick = (event) => { onPageChange(event, 0); @@ -55,19 +55,15 @@ const TablePaginationActions = React.forwardRef(function TablePaginationActions( const NextButtonIcon = slots.nextButtonIcon ?? KeyboardArrowRight; const PreviousButtonIcon = slots.previousButtonIcon ?? KeyboardArrowLeft; - const FirstButtonSlot = theme.direction === 'rtl' ? LastButton : FirstButton; - const PreviousButtonSlot = theme.direction === 'rtl' ? NextButton : PreviousButton; - const NextButtonSlot = theme.direction === 'rtl' ? PreviousButton : NextButton; - const LastButtonSlot = theme.direction === 'rtl' ? FirstButton : LastButton; + const FirstButtonSlot = isRtl ? LastButton : FirstButton; + const PreviousButtonSlot = isRtl ? NextButton : PreviousButton; + const NextButtonSlot = isRtl ? PreviousButton : NextButton; + const LastButtonSlot = isRtl ? FirstButton : LastButton; - const firstButtonSlotProps = - theme.direction === 'rtl' ? slotProps.lastButton : slotProps.firstButton; - const previousButtonSlotProps = - theme.direction === 'rtl' ? slotProps.nextButton : slotProps.previousButton; - const nextButtonSlotProps = - theme.direction === 'rtl' ? slotProps.previousButton : slotProps.nextButton; - const lastButtonSlotProps = - theme.direction === 'rtl' ? slotProps.firstButton : slotProps.lastButton; + const firstButtonSlotProps = isRtl ? slotProps.lastButton : slotProps.firstButton; + const previousButtonSlotProps = isRtl ? slotProps.nextButton : slotProps.previousButton; + const nextButtonSlotProps = isRtl ? slotProps.previousButton : slotProps.nextButton; + const lastButtonSlotProps = isRtl ? slotProps.firstButton : slotProps.lastButton; return (
@@ -79,7 +75,7 @@ const TablePaginationActions = React.forwardRef(function TablePaginationActions( title={getItemAriaLabel('first', page)} {...firstButtonSlotProps} > - {theme.direction === 'rtl' ? ( + {isRtl ? ( ) : ( @@ -94,7 +90,7 @@ const TablePaginationActions = React.forwardRef(function TablePaginationActions( title={getItemAriaLabel('previous', page)} {...(previousButtonSlotProps ?? backIconButtonProps)} > - {theme.direction === 'rtl' ? ( + {isRtl ? ( ) : ( @@ -108,7 +104,7 @@ const TablePaginationActions = React.forwardRef(function TablePaginationActions( title={getItemAriaLabel('next', page)} {...(nextButtonSlotProps ?? nextIconButtonProps)} > - {theme.direction === 'rtl' ? ( + {isRtl ? ( ) : ( @@ -122,7 +118,7 @@ const TablePaginationActions = React.forwardRef(function TablePaginationActions( title={getItemAriaLabel('last', page)} {...lastButtonSlotProps} > - {theme.direction === 'rtl' ? ( + {isRtl ? ( ) : ( diff --git a/packages/mui-material/src/Tabs/Tabs.js b/packages/mui-material/src/Tabs/Tabs.js index 31a26e7f16b83a..dfbcd083803bd0 100644 --- a/packages/mui-material/src/Tabs/Tabs.js +++ b/packages/mui-material/src/Tabs/Tabs.js @@ -6,6 +6,7 @@ import clsx from 'clsx'; import refType from '@mui/utils/refType'; import { useSlotProps } from '@mui/base/utils'; import composeClasses from '@mui/utils/composeClasses'; +import { useRtl } from '@mui/system/RtlProvider'; import styled from '../styles/styled'; import useThemeProps from '../styles/useThemeProps'; import useTheme from '../styles/useTheme'; @@ -231,7 +232,7 @@ let warnedOnceTabPresent = false; const Tabs = React.forwardRef(function Tabs(inProps, ref) { const props = useThemeProps({ props: inProps, name: 'MuiTabs' }); const theme = useTheme(); - const isRtl = theme.direction === 'rtl'; + const isRtl = useRtl(); const { 'aria-label': ariaLabel, 'aria-labelledby': ariaLabelledBy, @@ -333,7 +334,7 @@ const Tabs = React.forwardRef(function Tabs(inProps, ref) { clientWidth: tabsNode.clientWidth, scrollLeft: tabsNode.scrollLeft, scrollTop: tabsNode.scrollTop, - scrollLeftNormalized: getNormalizedScrollLeft(tabsNode, theme.direction), + scrollLeftNormalized: getNormalizedScrollLeft(tabsNode, isRtl ? 'rtl' : 'ltr'), scrollWidth: tabsNode.scrollWidth, top: rect.top, bottom: rect.bottom, diff --git a/packages/mui-material/src/Tooltip/Tooltip.js b/packages/mui-material/src/Tooltip/Tooltip.js index 88d6d7e563d55b..fa17187fdbb82d 100644 --- a/packages/mui-material/src/Tooltip/Tooltip.js +++ b/packages/mui-material/src/Tooltip/Tooltip.js @@ -7,6 +7,7 @@ import elementAcceptingRef from '@mui/utils/elementAcceptingRef'; import { appendOwnerState } from '@mui/base/utils'; import composeClasses from '@mui/utils/composeClasses'; import { alpha } from '@mui/system/colorManipulator'; +import { useRtl } from '@mui/system/RtlProvider'; import styled from '../styles/styled'; import useTheme from '../styles/useTheme'; import useThemeProps from '../styles/useThemeProps'; @@ -270,7 +271,7 @@ const Tooltip = React.forwardRef(function Tooltip(inProps, ref) { const children = React.isValidElement(childrenProp) ? childrenProp : {childrenProp}; const theme = useTheme(); - const isRtl = theme.direction === 'rtl'; + const isRtl = useRtl(); const [childNode, setChildNode] = React.useState(); const [arrowRef, setArrowRef] = React.useState(null); diff --git a/packages/mui-system/src/RtlProvider/index.d.ts b/packages/mui-system/src/RtlProvider/index.d.ts new file mode 100644 index 00000000000000..2572ebe7c8b259 --- /dev/null +++ b/packages/mui-system/src/RtlProvider/index.d.ts @@ -0,0 +1,11 @@ +import * as React from 'react'; + +interface RtlProviderProps { + children?: React.ReactNode; + value?: boolean; +} + +declare const RtlProvider: React.FC; +export const useRtl: () => boolean; + +export default RtlProvider; diff --git a/packages/mui-system/src/RtlProvider/index.js b/packages/mui-system/src/RtlProvider/index.js new file mode 100644 index 00000000000000..17c01ee6d6b564 --- /dev/null +++ b/packages/mui-system/src/RtlProvider/index.js @@ -0,0 +1,20 @@ +import * as React from 'react'; +import PropTypes from 'prop-types'; + +const RtlContext = React.createContext(); + +function RtlProvider({ value, ...props }) { + return ; +} + +RtlProvider.propTypes = { + children: PropTypes.node, + value: PropTypes.bool, +}; + +export const useRtl = () => { + const value = React.useContext(RtlContext); + return value ?? false; +}; + +export default RtlProvider; diff --git a/packages/mui-system/src/ThemeProvider/ThemeProvider.js b/packages/mui-system/src/ThemeProvider/ThemeProvider.js index 48d4e8a3d97dda..760b4c4645ee50 100644 --- a/packages/mui-system/src/ThemeProvider/ThemeProvider.js +++ b/packages/mui-system/src/ThemeProvider/ThemeProvider.js @@ -8,6 +8,7 @@ import { import exactProp from '@mui/utils/exactProp'; import { ThemeContext as StyledEngineThemeContext } from '@mui/styled-engine'; import useThemeWithoutDefault from '../useThemeWithoutDefault'; +import RtlProvider from '../RtlProvider'; const EMPTY_THEME = {}; @@ -61,11 +62,12 @@ function ThemeProvider(props) { const engineTheme = useThemeScoping(themeId, upperTheme, localTheme); const privateTheme = useThemeScoping(themeId, upperPrivateTheme, localTheme, true); + const rtlValue = engineTheme.direction === 'rtl'; return ( - {children} + {children} ); diff --git a/packages/mui-system/src/ThemeProvider/ThemeProvider.test.js b/packages/mui-system/src/ThemeProvider/ThemeProvider.test.js index febe6050490f54..f6eccf9e8dd104 100644 --- a/packages/mui-system/src/ThemeProvider/ThemeProvider.test.js +++ b/packages/mui-system/src/ThemeProvider/ThemeProvider.test.js @@ -4,6 +4,7 @@ import { createRenderer } from '@mui-internal/test-utils'; import { useTheme as usePrivateTheme } from '@mui/private-theming'; import { ThemeContext } from '@mui/styled-engine'; import { ThemeProvider } from '@mui/system'; +import { useRtl } from '@mui/system/RtlProvider'; const useEngineTheme = () => React.useContext(ThemeContext); @@ -277,4 +278,25 @@ describe('ThemeProvider', () => { ' outerTheme} />', ]); }); + + it('sets the correct value for the RtlProvider based on the theme.direction', () => { + let rtlValue = null; + function Test() { + rtlValue = useRtl(); + return null; + } + render( + + + , + ); + expect(rtlValue).to.equal(true); + + render( + + + , + ); + expect(rtlValue).to.equal(false); + }); }); diff --git a/packages/mui-system/src/index.js b/packages/mui-system/src/index.js index d44e83fc65a04d..b7215ef8dee1b4 100644 --- a/packages/mui-system/src/index.js +++ b/packages/mui-system/src/index.js @@ -64,6 +64,8 @@ export { default as unstable_cssVarsParser } from './cssVars/cssVarsParser'; export { default as unstable_prepareCssVars } from './cssVars/prepareCssVars'; export { default as unstable_createCssVarsTheme } from './cssVars/createCssVarsTheme'; export { default as responsivePropType } from './responsivePropType'; +export { default as RtlProvider } from './RtlProvider'; +export * from './RtlProvider'; /** ----------------- */ /** Layout components */ diff --git a/packages/zero-runtime/src/RtlProvider/index.ts b/packages/zero-runtime/src/RtlProvider/index.ts new file mode 100644 index 00000000000000..811b0c2c514f04 --- /dev/null +++ b/packages/zero-runtime/src/RtlProvider/index.ts @@ -0,0 +1 @@ +export { default as RtlProvider, useRtl } from '@mui/system/RtlProvider'; From 892b3f4d66a012d9fee50dcb778ce74a4d3bc6d3 Mon Sep 17 00:00:00 2001 From: Danilo Leal <67129314+danilo-leal@users.noreply.github.com> Date: Fri, 8 Mar 2024 07:56:41 -0300 Subject: [PATCH 15/17] Vite example: simplify styles, writing tweaks, README link fix --- examples/pigment-css-vite-ts/index.html | 9 +-------- examples/pigment-css-vite-ts/src/App.tsx | 24 ++++++++++-------------- 2 files changed, 11 insertions(+), 22 deletions(-) diff --git a/examples/pigment-css-vite-ts/index.html b/examples/pigment-css-vite-ts/index.html index dac8779ebcdecb..33cbc8ad392ca2 100644 --- a/examples/pigment-css-vite-ts/index.html +++ b/examples/pigment-css-vite-ts/index.html @@ -4,14 +4,7 @@ - - - - - Vite + Zero + TS + Pigment CSS + Vite + TypeScript
diff --git a/examples/pigment-css-vite-ts/src/App.tsx b/examples/pigment-css-vite-ts/src/App.tsx index b3946c6bb8e9cd..cbc7ed15dc3740 100644 --- a/examples/pigment-css-vite-ts/src/App.tsx +++ b/examples/pigment-css-vite-ts/src/App.tsx @@ -10,9 +10,9 @@ const Link = styled('a', { shouldForwardProp: (prop) => prop !== 'outlined' })<{ }>(({ theme }) => ({ fontSize: '1rem', background: 'rgba(0 0 0 / 0.04)', - padding: '0.5rem 1rem', + padding: '0.8rem 1rem', letterSpacing: '1px', - borderRadius: '4px', + borderRadius: '8px', textAlign: 'center', ...theme.applyStyles('dark', { background: 'rgba(255 255 255 / 0.1)', @@ -47,6 +47,7 @@ const Bubble = styled('span')({ function randomBetween(min: number, max: number) { return Math.floor(Math.random() * (max - min + 1) + min); } + function generateBubbleVars() { return ` --x: ${randomBetween(10, 90)}%; @@ -77,8 +78,8 @@ export default function Home() {

({ - fontFamily: 'Kalnia, sans-serif', - fontSize: 'clamp(3rem, 1.9503rem + 4.4789vw, 6.25rem)', + fontFamily: 'system-ui, sans-serif', + fontSize: '4rem', fontWeight: 500, textAlign: 'center', position: 'relative', @@ -158,31 +159,26 @@ export default function Home() {

- CSS-JS library with static extraction + CSS-in-JS library with static extraction
-
*': { flex: 'auto' }, + marginTop: '2rem', })} > From 0c18856b399c778faefbb3867a83c2e19a0a3078 Mon Sep 17 00:00:00 2001 From: Danilo Leal <67129314+danilo-leal@users.noreply.github.com> Date: Fri, 8 Mar 2024 08:03:50 -0300 Subject: [PATCH 16/17] Next example: simplify styles to match Vite's --- examples/pigment-css-nextjs-ts/README.md | 2 +- examples/pigment-css-nextjs-ts/src/app/page.tsx | 16 +++++----------- 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/examples/pigment-css-nextjs-ts/README.md b/examples/pigment-css-nextjs-ts/README.md index 3449ebdbd149ec..f4ef7dd52a8ece 100644 --- a/examples/pigment-css-nextjs-ts/README.md +++ b/examples/pigment-css-nextjs-ts/README.md @@ -1,6 +1,6 @@ # Pigment CSS - Next.js App Router with TypeScript example project -This is a [Next.js](https://nextjs.org/) project bootstrapped using [`create-next-app`](https://github.com/vercel/next.js/tree/HEAD/packages/create-next-app), with TypeScript and MUI's zero-runtime CSS-in-JS installed. +This is a [Next.js](https://nextjs.org/) project bootstrapped using [`create-next-app`](https://github.com/vercel/next.js/tree/HEAD/packages/create-next-app), with TypeScript and Pigment CSS, a zero-runtime CSS-in-JS library, installed. ## How to use diff --git a/examples/pigment-css-nextjs-ts/src/app/page.tsx b/examples/pigment-css-nextjs-ts/src/app/page.tsx index 08a5fc7e6cb3fe..109b5cbdad5f10 100644 --- a/examples/pigment-css-nextjs-ts/src/app/page.tsx +++ b/examples/pigment-css-nextjs-ts/src/app/page.tsx @@ -1,10 +1,6 @@ import * as React from 'react'; -import { Kalnia, Josefin_Sans } from 'next/font/google'; import { styled, css, keyframes } from '@pigment-css/react'; -const kalnia = Kalnia({ subsets: ['latin'] }); -const josefin = Josefin_Sans({ subsets: ['latin'] }); - const scale = keyframes({ to: { scale: 'var(--s2)' }, }); @@ -14,9 +10,9 @@ const Link = styled('a', { shouldForwardProp: (prop) => prop !== 'outlined' })<{ }>(({ theme }) => ({ fontSize: '1rem', background: 'rgba(0 0 0 / 0.04)', - padding: '0.5rem 1rem', + padding: '0.8rem 1rem', letterSpacing: '1px', - borderRadius: '4px', + borderRadius: '8px', textAlign: 'center', ...theme.applyStyles('dark', { background: 'rgba(255 255 255 / 0.1)', @@ -76,9 +72,9 @@ export default function Home() { >

({ - fontSize: 'clamp(3rem, 1.9503rem + 4.4789vw, 6.25rem)', + fontFamily: 'system-ui, sans-serif', + fontSize: '4rem', fontWeight: 500, textAlign: 'center', position: 'relative', @@ -158,11 +154,9 @@ export default function Home() {

Date: Fri, 8 Mar 2024 08:09:34 -0300 Subject: [PATCH 17/17] main README adjustments --- packages/pigment-react/README.md | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/packages/pigment-react/README.md b/packages/pigment-react/README.md index ce49ec5b4ffd86..baf514ec870234 100644 --- a/packages/pigment-react/README.md +++ b/packages/pigment-react/README.md @@ -4,8 +4,8 @@ Pigment CSS is a zero-runtime CSS-in-JS library that extracts the colocated styl - [Getting started](#getting-started) - [Why this project exists?](#why-choose-pigment-css) - - [Next.js](#nextjs) - - [Vite](#vite) + - [Next.js](#nextjs-starter-template) + - [Vite](#vite-starter-template) - [Basic usage](#basic-usage) - [Creating styles](#creating-styles) - [Creating components](#creating-components) @@ -35,9 +35,7 @@ Pigment CSS addresses the needs of the modern React developer by providing a zer Compared to its predecessors, Pigment CSS offers improved DX and runtime performance (though at the cost of increased build time) while also being compatible with React Server Components. Pigment CSS is built on top of [WyW-in-JS](https://wyw-in-js.dev/), enabling us to provide the smoothest possible experience for Material UI users when migrating from Emotion in v5 to Pigment in v6. -### Next.js - -#### Starter template +### Next.js - Starter template Use the following commands to create a new Next.js project with Pigment CSS set up: @@ -83,9 +81,7 @@ Finally, import the stylesheet in the root `layout.tsx` file: } ``` -### Vite - -#### Starter template +### Vite - Starter template Use the following commands to create a new Vite project with Pigment CSS set up: @@ -134,7 +130,7 @@ Finally, import the stylesheet in the root `main.tsx` file: ## Basic usage -> You must configure Pigment CSS with [Next.js](#nextjs) or [Vite](#vite) first. +**You must configure Pigment CSS with [Next.js](#nextjs) or [Vite](#vite) first.** ### Creating styles @@ -371,7 +367,7 @@ const Heading = styled('h1')<{ isError?: boolean }>({ Use the `keyframes` API to create reusable [animation keyframes](https://developer.mozilla.org/en-US/docs/Web/CSS/@keyframes): ```js -import { keyframes } from '@mui/zero-runtime'; +import { keyframes } from '@pigment-css/react'; const fadeIn = keyframes` from { @@ -390,7 +386,7 @@ function Example1() { The call to the `keyframes` function will be replaced with a unique string that represents the CSS animation name. It can be used with `css` or `styled` too. ```js -import { css, styled, keyframes } from '@mui/zero-runtime'; +import { css, styled, keyframes } from '@pigment-css/react'; const fadeIn = keyframes(...);