I'm setting up a component library with React, Storybook, Typescript and Material UI. One of the main targets of the library is to re-export components that are imported from MUI (with a bit of config on top of them). I stumbled upon an issue where one of the components is not being rendered as intended when used in another React app. The component I am talking about is the Stepper component. Below is what I have now:
Stepper.tsx
import Stack from '#mui/material/Stack';
import MUIStepper, { StepperProps } from '#mui/material/Stepper';
const Stepper = (props: StepperProps) => {
return (
<Stack sx={{ width: '100%' }} spacing={4}>
<MUIStepper {...props}></MUIStepper>
</Stack>
);
};
export default Stepper
This is going to be built as a library using rollup.
I am not going to paste the entire rollup config here, but these are the plugins the config is using:
import babel from '#rollup/plugin-babel';
import resolve from '#rollup/plugin-node-resolve';
import commonjs from '#rollup/plugin-commonjs';
import postcss from 'rollup-plugin-postcss';
import filesize from 'rollup-plugin-filesize';
import autoprefixer from 'autoprefixer';
import typescript from "rollup-plugin-typescript2";
import dts from "rollup-plugin-dts";
After the process of building and publishing the library to an npm registry, I am trying to use it in some other React application, but some of the styles/internal components of the Stepper are totally missing (ex: active step css, step connectors etc.). The usage of the Stepper component is the same as in the official docs and it works perfectly with the original Stepper component.
Can you think of any configuration I am missing? It looks like something is lost along the way while building the library, but not sure what. Either that or the children components do not receive props properly.
I can provide further insight into this if necessary, but I didn't want to clutter the post anymore that it already is.
The Stepper component expects two or more Step components as children. To fix this, you need to pass props.children to the Stepper component.
import Stack from '#mui/material/Stack';
import MUIStepper, { StepperProps } from '#mui/material/Stepper';
const Stepper = (props: StepperProps) => {
return (
<Stack sx={{ width: '100%' }} spacing={4}>
<MUIStepper {...props}>{props.children}</MUIStepper>
</Stack>
);
};
export default Stepper;
Answering my own question here:
It looks like this behavior si encountered whenever we deal with components that use the Context API internally. Somehow, the context gets messed up if we use the root component from our library and other descendant components from MUI. Simply importing and re-exporting descendant components and using them from owr library as well fixes this issue, although I would want to avoid that. Until then, I created an issue on the github page to see if it is a bug on their side or just an intended behavior which affects my case here.
All in all, it is not really a rollup config issue. Transpilation and bundling work as intended.
Issue link: https://github.com/mui/material-ui/issues/33320
Related
I am using single spa to create a my react microfrontends, in one of the MFE(microfrontend) I need to dynamically import a ".js" from another MFE and render the component , for that I am using systemjs import
Below is a sample code on how the child component exposes a function to export a js file in the main files
export function getcode() {
return import("./views/rk.js");
}
and in the parent component I use react.lazy to import the js file from the function and render as component
const Test = React.lazy(() =>
import("#Test/react-singlespa").then((mod) => mod.getcode())
);
and render the above const as below using
<React.Suspense fallback="Loading ...">
<Test/>
</React.Suspense>
I don't have issue while running the code in development mode but in the production mode the styles are messed up for first load but subsequently the styles are fine.
The question seems to be common with material ui issues import of webpack, but none of the solutions online seem to help. I tried every possible solution given in the below links
https://github.com/mui-org/material-ui/issues/15610
https://material-ui.com/getting-started/faq/#i-have-several-instances-of-styles-on-the-page
Assuming this is something that has to be done with Single-spa framework ...any help in this regard is greatly appreciated
Could you please help with the following?
We use Ionic with React in a team having 2 distinct projects.
We need to style these Ionic components a little differently.
I need to share these a-little-differently-styled Ionic components with another team for code re-use.
I would like to showcase these styled Ionic components using Storybook to the UX team and say "this is how a button looks like"
To begin with I just added the styling in a css file and imported that file in the root React page.
But it would be nice to just encapsulate component + styling in a re-usable isolated component that other people can re-use without having to import custom css files.
I am thinking of :
import { IonButton } from "#ionic/react";
import styled from "styled-components";
const StyledIonButton = styled(IonButton)`
// some custom css styling here
`;
const MyIonButton = () => <StyledIonButton {...props} />;
export default MyIonButton;
Now, I can track/export above component using e.g Bit.dev and have the other team consume it via npm-install.
However, I dislike :
a) changing the name from "IonButton" to "MyIonButton"
b) having to wrap each and every Ionic component as shown above
I would like myself and the other team to just use Ionic components in the standard way e.g
import { IonButton } from "#ionic/react";
...
<IonButton>Close</IonButton>
BUT having these base building-blocks/components styled in a certain way.
Could you please describe to me the right approach please? Many thanks for your time
I'm trying to use Atomize (https://atomizecode.com/) with Gatsby JS (https://www.gatsbyjs.org/) and while it is successfully installed, the atomize components are not being rendered to the page.
I am trying to import a button to start "Hey", and while it renders something it is not the standard button component. Also, even though the button component is imported, I get the warning that reads it is not being used.
I am wrapping atomize around gatsby app by the following way:
import React from "react"
import { StyleReset } from "atomize"
export const wrapRootElement = ({ element }) => (
<>
<StyleReset />
{element}
</>
)
Did you install styletron toolkit?
According to their docs it's a dependency and looking at their package.json it's defined under peerDependencies which means it won't get installed along with atomize.
Styletron's docs sugggets using gatsby-plugin-styletron for Gatsby.
Hope that helps.
If I install emotion/css then the API is nice and clear:
package.json:
"dependencies": {
"#emotion/css": "^11.1.3",
React component:
import React from "react";
import { css } from "#emotion/css";
const someStyle = css`
display: none;
`
function MyComponent() {
return (
<div className={someStyle} />
);
}
However according to the docs you should install #emotion/react:
https://emotion.sh/docs/introduction
package.json:
"dependencies": {
"#emotion/react": "^11.4.1",
Now the API is a lot more messy:
import React from "react";
/** #jsx jsx */
import { jsx, css } from "#emotion/react";
const someStyle = css`
display: none;
`;
function MyComponent() {
return (
<div css={someStyle} />
);
}
Not only do you need a comment /** #jsx jsx */, but you also have to import jsx even though it's not used. In my IDE this means I get a lint warning by my React import.
What is the benefit of the recommended way? Im tempted to ignore the documentation and do it the old way.
The jsx pragma/import is required because the css prop is provided by a wrapper around React.createElement - without that transform being applied, the css prop would not work.
That said, including it in every file is tedious - that's why there is a babel plugin that does it for you.
{
"presets": ["#emotion/babel-preset-css-prop"]
}
With regards to the benefits of using this method over the stock emotion package, the docs list some on the page you linked:
CSS prop support
Similar to the style prop but adds support for nested selectors, media queries, and auto-prefixing.
Allows developers to skip the styled API abstraction and style components and elements directly.
The css prop also accepts a function that is called with your theme as an argument allowing developers easy access to common and customizable values.
Reduces boilerplate when composing components and styled with emotion.
Server side rendering with zero configuration.
Theming works out of the box.
ESLint plugins available to ensure proper patterns and configuration are set.
Both are valid and each approach has its benefits:
emotion/css:
use vanilla react className prop
no custom jsx parser
no babel config or file pragmas
no extra typescript config
generate class names outside of react components
emotion/react:
zero config for #emotion/cache
easy server side rendering
easy access to theme through css function
More resources
React vs Vanilla Essay
Outline of pros of vanilla over react
Github issue
Great respectful github discussion between essay author and emotion dev
I'm using lerna to create a monorepo where I'd have a structure like this:
root
packages
application - Our root application
components - Just some react components, that are to be used by the application
Here is a working Github with a simple example for this.
The issue I've run into, is I'm using Material-UI and its theming functionality, where at the application root we'll have a ThemeProvider:
import { ThemeProvider } from '#material-ui/styles';
//...
function App() {
return (
<ThemeProvider theme={theme}>
<MyMaterialComponent/>
</ThemeProvider>
);
}
And later in the library component, we consume the theme, in our case using the makeStyles hook.
import React from 'react';
import { makeStyles } from '#material-ui/styles';
import Card from "#material-ui/core/Card";
const useStyles = makeStyles(theme => {
console.log(theme); //When this component doesn't have access to the theme, this is `{}`
return {
root: {
//color: theme.palette.primary.main //will error
}
}
});
export function MyMaterialComponent({ }) {
const classes = useStyles();
return (<Card>
<span className={classes.root}>This is some component</span>
</Card>
);
}
Now this seems all pretty straight forward. When we run this code all within the same package, it works fine. That styles function has access to the theme.
But when I'm running from another package (our application package), the the component library no longer has access to the theme (the theme is just an empty object).
The only way I currently know how to solve this, is the same way I've solved a similar hooks issue, which is to setup a webpack alias configuration in my application, to direct the component library to share the same same node module. (See this Github thread and the suggested solution).
ie. using react-app-rewired and customize-cra I have a config-overrides.js that looks like this:
const {
override,
addWebpackAlias,
} = require("customize-cra");
const path = require('path');
module.exports = override(
addWebpackAlias({
react: path.resolve('./node_modules/react'),
//comment out the line below to reproduce the issue
"#material-ui/styles": path.resolve("./node_modules/#material-ui/styles")
})
)
or you could be managing your webpack manually to do a similar thing.
So this works fine, but this isn't a particularly satisfying solution.
Especially for a library like Material-UI, you want users to be able to use your component library without having them to mess with their webpack configuration.
So I figure I must be doing something wrong here - can you tell me what?
You can achieve that by placing your #material-ui/core dependency from devDependencies to peerDependencies in your library's project. In my case, that solved my problem.
More information on peerDependencies :
https://classic.yarnpkg.com/en/docs/dependency-types/#toc-peerdependencies