How to extend OverridableComponent interface in Material-UI - reactjs

I'm trying to use Container component with styled-components using ContainerProps but then I can't pass component prop which belongs to OverridableComponent interface.
Code below gives me error which tells me that I can't pass component property. When I change <Container/> to <MuiContainer/> it works.
MuiContainer has type OverridableComponent<ContainerTypeMap<{}, 'div'>> but I can't import OverridableComponent from #material-ui/core
How can I make passing component property possible?
import { Container as MuiContainer, ContainerProps } from '#material-ui/core';
import React from 'react';
import styled from 'styled-components';
const Container = styled(MuiContainer)<ContainerProps>``;
export const Test = () => {
return (
<>
<Container maxWidth="lg" component="main">
content
</Container>
</>
);
};

Usage of component prop
const Container = styled(MuiContainer)<ContainerProps<'main', { component: 'main' }>>``;

You can tell the Typescript compiler to retain the original Container prop types by asserting it:
import MuiContainer from "#mui/material/Container";
import { styled } from "#mui/material/styles";
const Container = styled(MuiContainer)`
background-color: pink;
padding: 16px;
` as typeof MuiContainer;
export default function StyledComponents() {
return (
<Container maxWidth="lg" component="main">
content
</Container>
);
}
Live Demo
Reference
https://mui.com/guides/composition/#with-typescript

Related

How to use Ant design v5 theme with style component

Antd 5.0 has been introduced their new theme system.
But I wonder how to access to those design tokens of theme when I declare a component by style-component.
normally I declare my component like this.
const MyComponent = styled.button`
color:${props=> props.theme.color.primary};
`;
theme here is getting from ThemeProvider of styled component that is defined in App.jsx
<ThemeProvider theme={baseTheme}>
<App/>
</ThemeProvider>
So, theme only can access to design tokens that were defined in theme file.
How can I access other tokens of Antd theme?
One way I'm thinking is creating a theme that overrides every single design token of Antd. But I think that's a bad idea
From the documentation consume-design-token, you can get and consume the token by using the theme.useToken() hook.
Create a ThemeProvider get the antd theme token and combine it with base theme, then pass the combined theme to the ThemeProvider of styled-components.
Then you can get the combined theme via passed props of styled-components.
theme-provider.tsx:
import { ThemeProvider } from "styled-components";
import { theme } from "antd";
import React from "react";
export default ({ children }: React.PropsWithChildren) => {
const { token } = theme.useToken();
return (
<ThemeProvider theme={{ antd: token, base: { color: "mediumseagreen" } }}>
{children}
</ThemeProvider>
);
};
App.tsx:
import styled from "styled-components";
import { ConfigProvider } from "antd";
import ThemeProvider from "./theme-provider";
const Button = styled.button`
color: ${(props) => {
console.log("props.theme: ", props.theme);
return props.theme.antd.colorPrimary;
}};
`;
export default function App() {
return (
<ConfigProvider
theme={{
token: {
colorPrimary: "red"
}
}}
>
<ThemeProvider>
<Button>Hello World</Button>
</ThemeProvider>
</ConfigProvider>
);
}
The log:
props.theme: {antd: Object, base: Object}
codesandbox

override component mui v5 react

I'm using mui v5 and I'm trying to override the Container component to be have a padding of 4rem.
here is a simple code example:
import * as React from "react";
import { Container } from "#mui/material";
import Navbar from "./components/Navbar";
import { styled } from "#mui/system";
import CssBaseline from "#mui/material/CssBaseline";
import { ThemeProvider, StyledEngineProvider } from "#mui/material/styles";
import theme from "./theme";
const MyContainer = styled(Container, {})`
padding-left: 4rem;
background-color: aqua;
`;
export default function App() {
return (
<ThemeProvider theme={theme}>
<StyledEngineProvider injectFirst>
<CssBaseline />
<MyContainer maxWidth="xl">
<div>test</div>
</MyContainer>
</StyledEngineProvider>
</ThemeProvider>,
);
}
But this yields no results.
here is a sandbox:
https://codesandbox.io/s/wizardly-leakey-xumyd
You can use make use of shouldForwardProp of mui v5.
const MyContainer = styled(Container, {
shouldForwardProp: (prop) => prop
})(({ padding }) => ({
padding: padding,
backgroundColor: "aqua"
}));
You can check forked codesandbox here
open this

Material UI nested theme providers breaks withStyles HOC

I have a React application created with Create React App and I use the #material-ui/core npm package for theming.
To customize components I use the withStyles higher-order component provided by MaterialUI.
According to documentation it supports nested ThemeProviders https://material-ui.com/customization/theming/#nesting-the-theme.
But inside the child ThemeProvider withStyles won't apply classes.
Here is a basic application demonstrating the issue -> https://codesandbox.io/s/vibrant-tree-eh83d
ExampleComponent.tsx
import React, { FunctionComponent } from "react";
import {
WithStyles,
withStyles,
createStyles,
StepButton,
Step,
Stepper,
Box
} from "#material-ui/core";
const styles = createStyles({
button: {
"& .MuiStepIcon-root.MuiStepIcon-active": {
fill: "red"
}
}
});
interface Props extends WithStyles<typeof styles> {
title: string;
}
const ExampleComponent: FunctionComponent<Props> = ({ title, classes }) => {
console.log(title, classes);
return (
<Box display="flex" alignItems="center">
<span>{title}</span>
<Stepper activeStep={0}>
<Step>
<StepButton className={classes.button}>Test</StepButton>;
</Step>
</Stepper>
</Box>
);
};
export default withStyles(styles)(ExampleComponent);
App.tsx
import * as React from "react";
import { ThemeProvider, createMuiTheme } from "#material-ui/core";
import ExampleComponent from "./ExampleComponent";
const theme = createMuiTheme();
function App() {
return (
<ThemeProvider theme={theme}>
<ExampleComponent title="Root" />
<ThemeProvider theme={theme}>
<ExampleComponent title="Nested" />
</ThemeProvider>
</ThemeProvider>
);
}
export default App;
Inside the ExampleComponent I console.log the generated classes object.
I want to use nested ThemeProviders and override classes inside components regardless of the ThemeProvider.
Am I missing something or is this not possible?
When you are using nested themes, you cannot reliably use Material-UI's global class names (e.g. .MuiStepIcon-root.MuiStepIcon-active). Within a nested theme, the "Mui..." class names have to be different to avoid conflicting with the CSS classes for the top-level theme since the nested theme will cause some of the CSS for the "Mui..." classes to be different.
You can use the following syntax in order to successfully match the suffixed versions of the Mui class names that occur within nested themes:
const styles = createStyles({
button: {
'& [class*="MuiStepIcon-root"][class*="MuiStepIcon-active"]': {
fill: "red"
}
}
});
Related answer:
How reliable are MUI Global Class names in JSS?

How i can add style of a existent component in styled components?

I'm trying to use styled components to personalize a header component from semantic-ui-react.
I try:
header.jsx:
import React from 'react';
import { Header } from 'semantic-ui-react';
import TipografiaHeader from './cabecalho.css'
const HeaderPages = () => (
<div>
<TipografiaHeader as='h2'
textAlign='center'
>
Workout Log
</TipografiaHeader>
</div>
)
export default HeaderPages
cabecalho.jss.js:
import styled from "styled-components";
import { Header } from 'semantic-ui-react';
const TipografiaHeader = styled.Header`
background: red;
`;
export { TipografiaHeader };
But in the console i'm receiving:
Uncaught TypeError: _styledComponents2.default.Header is not a
function
The syntax for styled.element can only be used for HTML elements. For eg:
styled.h1``
For styling a custom component, the syntax to be used is:
styled(Header)``
Note that this custom component requires the className prop to be passed into the DOM element underneath for this to work.
Docs

How to pass Styled-Component theme variables to Components?

Within my React+StyledComponent app, I have a theme file like so:
theme.js:
const colors = {
blacks: [
'#14161B',
'#2E2E34',
'#3E3E43',
],
};
const theme = {
colors,
};
export default theme;
Currently, I can easily use these colors to style my components like so:
const MyStyledContainer = styled.div`
background-color: ${(props) => props.theme.colors.blacks[1]};
`;
The problem is, how do I pass blacks[1] to a Component as the prop of the color to use like so:
<Text color="black[1]">Hello</Text>
Where Text.js is:
const StyledSpan = styled.span`
color: ${(props) => props.theme.colors[props.color]};
`;
const Text = ({
color,
}) => {
return (
<StyledSpan
color={color}
>
{text}
</StyledSpan>
);
};
Text.propTypes = {
color: PropTypes.string,
};
export default Text;
Currently the above is silently failing and rending the following in the DOM:
<span class="sc-brqgn" color="blacks[1]">Hello</span>
Any ideas on how I can get this to work? Thank you
EDIT: Updated to use styled-components withTheme HOC
New answer
You could wrap the component rendering <Text> in the higher order component (HOC) withTheme provided by styled-components. This enables you to use the theme given to the <ThemeProvider> directly in the React component.
Example (based on the styled-components docs):
import React from 'react'
import { withTheme } from 'styled-components'
import Text from './Text.js'
class MyComponent extends React.Component {
render() {
<Text color={this.props.theme.colors.blacks[1]} />;
}
}
export default withTheme(MyComponent)
Then you could do
const MyStyledContainer = styled.div`
background-color: ${(props) => props.color};
`;
Old answer
You could import the theme where you render and pass <Text color={theme.blacks[1]} />.
import theme from './theme.js'
...
<Text color={theme.colors.blacks[1]} />
Then you could do
const MyStyledContainer = styled.div`
background-color: ${(props) => props.color};
`;
You can use defaultProps
import PropTypes from 'prop-types'
MyStyledContainer.defaultProps = { theme }
App.js
App gets theme and passes color to Text
import React, { Component } from 'react'
import styled from 'styled-components'
const Text = styled.div`
color: ${props => props.color || 'inherit'}
`
class App extends Component {
render() {
const { theme } = this.props
return (
<Text color={theme.colors.black[1]} />
)
}
}
export default App
Root.js
Root component passes theme to entire application.
import React, { Component } from 'react'
import { ThemeProvider } from 'styled-components'
import theme from './theme'
import App from './App'
class Root extends Component {
render() {
return (
<ThemeProvider theme={theme}>
<App />
</ThemeProvider>
)
}
}
export default Root
If you're using functional components in React and v4.x and higher styled-components, you need to leverage useContext and styled-components' ThemeContext. Together, these allow you to use your theme settings inside of components that aren't styled-components.
import { useContext } from 'react'
import { ThemeContext } from 'styled-components'
export default function MyComponent() {
// place ThemeContext into a context that is scoped to just this component
const themeProps = useContext(ThemeContext)
return(
<>
{/* Example here is a wrapper component that needs sizing params */}
{/* We access the context and all of our theme props are attached to it */}
<Wrapper maxWidth={ themeProps.maxWidth }>
</Wrapper>
</>
)
}
Further reading in the styled-components docs: https://styled-components.com/docs/advanced#via-usecontext-react-hook

Resources