Using ThemeProvider props in Global Styled-Components - javascript

How can I access the ThemeProvider props in global.js when using styled-components?
For example in theme.js I have ${props => props.theme.fonts.fontSize} calling a default font size of 16px
const theme = {
fonts: {
fontSize : '16px',
}
}
export default theme
This is provided in /layouts/index.js as
import React from 'react'
import { ThemeProvider } from 'styled-components'
import '../style/global';
import theme from '../style/theme'
class Template extends React.Component {
render() {
const { children } = this.props
return (
<ThemeProvider theme={theme}>
...
{children()}
...
</ThemeProvider>
)
}
}
export default Template
From here I can access the ${props => props.theme.fonts.fontSize} within each component or child page.
But how can I pass to global.js in the same way when global is technically a level above theme.js? So that I could create a global style as
injectGlobal`
html {
font-size: (${props => props.theme.fonts.fontSize} / 16px) * 1em;
}
`

The easiest way off solving this is by creating a top level component that injects your desired styling like this:
import { Children } from 'react';
import { withTheme, injectGlobal } from 'styled-components';
const GlobalComponent = ({ theme, children }) => {
injectGlobal`
font-size: ${theme.fonts.fontSize}
}
`;
return Children.only(children);
};
export default withTheme(Global);
This will make sure all Components that have this Component as a parent will have the desired globalStyling. Hope this helped

Late but now we can actually create a Global Component and pass it as a child of ThemeProvider. It will allow you to access all the props of current theme.
Example for applying font family:
Your Global.js / Global.ts
import { createGlobalStyle } from "styled-components";
const GlobalStyle = createGlobalStyle`
html,
body {
padding: 0;
margin: 0;
font-family: ${(props) => props.theme.font.family}
}
a {
color: inherit;
text-decoration: none;
}
* {
box-sizing: border-box;
}
`;
export default GlobalStyle;
Your Main component app.tsx / app.jsx
import theme...
import { ThemeProvider } ...
imort GlobalStyle from '../path-to-global-file';
const App ...
.
.
return(
<>
<ThemeProvider theme={theme}>
<GlobalStyle />
{ /* Root component */ }
<Component/>
</ThemeProvider>
</>
);
You can use the props easily now.

Related

Any way to pass always a default props value in to a styled component?

I am using React + Material UI + Styled Component. I am trying to create a custom Input field. I want to pass always size='small' as props to my component.
In other words, if the user forgets to pass size, it always takes small. Or if the user by mistake passes any other value other than small, still it always takes 'small' only.
Here is my code:
https://codesandbox.io/s/awesome-meadow-utv9bc?file=/src/App.tsx
import * as React from "react";
import TextField, { TextFieldProps } from "#mui/material/TextField";
import { styled } from "#mui/material/styles";
const Input = styled(TextField)<TextFieldProps>(
({ disabled, multiline, theme }) => {
return {
border: `1px solid red`
};
}
);
export default Input;
I am using like this
<Input label="Outlined" />
I want to pass size always small Any idea?
I suppose a simple solution would be to create a component that overrides the size prop to always be "small" and then style that.
import TextField, { TextFieldProps } from "#mui/material/TextField";
import { styled } from "#mui/material/styles";
const SmallTextField = (props: TextFieldProps) => (
<TextField {...props} size="small" />
);
const Input = styled(SmallTextField)<TextFieldProps>(
({ disabled, multiline, theme }) => {
return {
border: `1px solid red`
};
}
);
export default Input;
Use Mui Theme Provider
import * as React from "react";
import { createTheme, ThemeProvider } from "#mui/material/styles";
import TextField from "#mui/material/TextField";
const theme = createTheme({
components: {
// Name of the component ⚛️
MuiTextField: {
defaultProps: {
size: "small"
}
}
}
});
export default function DefaultProps() {
return (
<ThemeProvider theme={theme}>
<TextField />
</ThemeProvider>
);
}
You could use a wrapper that would set size="small" and remove size from the props with Omit, so anyone who uses your Input knows he cannot set it with the help of TypeScript errors:
import TextField, { TextFieldProps } from "#mui/material/TextField";
import { styled } from "#mui/material/styles";
const InnerInput = styled(TextField)<TextFieldProps>((props) => {
return {
...props
};
});
const Input = (props: Omit<TextFieldProps, "size">) => (
<InnerInput {...props} size="small" />
);
export default Input;

React native styled components cannot override component's style

I have a weird problem with stlyed components. I have a component Header with a basic style but when a try to use this component and extend the style nothing happens. Can someone tell me what going on?
import styled from 'styled-components/native';
export const Container = styled.SafeAreaView``;
export const Content = styled.View`
height: 72px;
padding: 0 24px;
flex-direction: row;
align-items: center;
justify-content: center;
`;
Header component
import React, { PropsWithChildren, FC } from 'react';
import { Container, Content } from './styles';
const Header: FC = ({ children }: PropsWithChildren<unknown>, props) => {
return (
<Container {...props}>
<Content>{children}</Content>
</Container>
);
};
export default Header;
import styled from 'styled-components/native';
import Header from '../components/Header/index';
export const Container = styled(Header)`
background: blue;
height: 200px;
`;
You have to pass your props from into your Header component. In Container or Content. It's won't be done instead of you.
Your Header is a React component and he "doesn't know what to do" with props that it will receive from Container - const Container = styled(Header)'...'.
Props will be recognized correctly if component is working with styles, as Text, View, ...
export const Container = styled(Header)`
background: blue;
height: 200px;
`;
const Header: FC = ({ children, ...restProps }: PropsWithChildren<unknown>) => {
return (
<Container {...restProps}>
<Content>{children}</Content> // or <Content {...restProps}>...
</Container>
);
};
or you have 2 next options, without passing the props - just editing your inner Container. It's depends on your codestyle of the project
const Header: FC = ({ children }: PropsWithChildren<unknown>) => {
return (
<Container background="blue" height="200px">
<Content>{children}</Content>
</Container>
);
};
export const NewContainer = styled(Container)`
background: blue;
height: 200px;
`;
const Header: FC = ({ children }: PropsWithChildren<unknown>) => {
return (
<NewContainer>
<Content>{children}</Content>
</NewContainer>
);
};

Redux Form With Styled Component

I am trying to create a reusable component where I have redux-form <Field /> returned and in this component I am styling it with styled-component.
the challenge I have is that none of the style is reflecting
this is my simple-field-input.styles.ts
import React from 'react';
import { Field } from 'redux-form';
import styled from 'styled-components';
import { SimpleFieldProps } from './simple-field-input.type';
const ReduxFormField: React.FC<SimpleFieldProps> = ({ componentType }) => {
return <Field component={componentType} name="email" />;
};
export const Container = styled(ReduxFormField)`
outline: none;
border: none;
background-color: orangered;
color: yellow;
`;
and here is my simple-field-input.component.tsx
import React from 'react';
import * as Style from '../simple-field-input/simple-field-input.styles';
import { SimpleFieldProps } from './simple-field-input.type';
const FieldInput: React.FC<SimpleFieldProps> = ({ componentType }) => {
return <Style.Container componentType={componentType}></Style.Container>;
};
export default FieldInput;
it simple renders the input but not implementing the styles...
i will appreciate any help. thanks
When using styled on on component which is not a DOM element, what it does is add a className prop to the component. The component needs to access that className and pass it through to a DOM element.
You're actually passing it down twice here, since the className should end up on the componentType rather than the Field itself.
const ReduxFormField: React.FC<SimpleFieldProps & {className?: string}> = (
{ componentType, className }
) => {
return <Field component={componentType} props={{className}} name="email" />;
};

Change color and position of CircularProgress?

I'm trying to use CircularProgress provided by Material.
I created this component in order to change its color:
import React, { Component } from 'react';
import { withStyles } from '#material-ui/core/styles';
import { CircularProgress } from '#material-ui/core';
class ColoredCircularProgress extends Component {
render() {
const { classes } = this.props;
return <CircularProgress {...this.props} classes={{colorPrimary: classes.colorPrimary}}/>;
}
}
const styles = props => ({
colorPrimary: {
backgroundColor: '#FD8907',
}
});
export default withStyles(styles)(ColoredCircularProgress);
However on my site it looks like this:
My questions are :
I want the circle to look orange and instead the circle looks still blue and it adds a square orange box behind.
It also displays at the top left corner of my site. How can I place it right in the center?
To change the color you can simple do this:
<CircularProgress style={{'color': 'yellow'}}/>
It works for Material-UI v4.x (I didn't try with minor versions)
You can override the style by applying css on .MuiCircularProgress-colorPrimary class.
Try this, hope this will work.
Example
.MuiCircularProgress-colorPrimary {
color: green !important;
}
.MuiCircularProgress-root {
left: 43%;
position: absolute;
top: 44vh;
}
Add this to the overrides in your theme. To make the color change globally.
MuiCircularProgress:{circle:{color:"green"},}
You don't need to override css.
Here's my solution:
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { withStyles } from '#material-ui/core/styles';
import { CircularProgress } from '#material-ui/core';
const defaultSize = 50;
class ColoredCircularProgressComponent extends Component {
render() {
const { classes, size } = this.props;
return <CircularProgress {...this.props} classes={classes} size={size} />;
}
}
class ColoredCircularProgress extends Component {
render() {
const WithStylesComponent = withStyles(theme => ({
colorPrimary: {
color: this.props.foreColor
},
root: {
top: `calc(50% - ${this.props.size / 2}px)`,
left: `calc(50% - ${this.props.size / 2}px)`,
position: 'absolute'
}
}))(ColoredCircularProgressComponent);
return <WithStylesComponent {...this.props} />;
}
}
ColoredCircularProgress.propTypes = {
classes: PropTypes.object,
size: PropTypes.number,
foreColor: PropTypes.string
};
ColoredCircularProgress.defaultProps = {
size: defaultSize,
foreColor: 'green'
};
export default ColoredCircularProgress;

Styled-components delay setting property in Nextjs

I'm trying to implement styled-components in a React project with Nextjs. The problem is that, although I can implement and see the styles, there is a small delay when I see it on the browser. First it loadeds the component without style, and 22ms later the style is applied. What I'm doing wrong?
Thanks
Here is my code!
pages/index.js
import React from "react";
import Home from "../components/home/index";
const index = () => {
return (
<React.Fragment>
<Home />
</React.Fragment>
);
};
export default index;
components/home.js
import React from "react";
import styled from 'styled-components';
const Title = styled.h1`
color: red;
`;
function Home() {
return (
<div>
<Title>My First Next.js Page</Title>
</div>
);
}
export default Home;
babel.rc
{
"presets": ["next/babel"],
"plugins": [["styled-components", { "ssr": true }]]
}
pages/_document.js
import Document from 'next/document';
import { ServerStyleSheet } from 'styled-components';
export default class MyDocument extends Document {
static async getInitialProps(ctx) {
const sheet = new ServerStyleSheet();
const originalRenderPage = ctx.renderPage;
try {
ctx.renderPage = () =>
originalRenderPage({
enhanceApp: App => props => sheet.collectStyles(<App {...props} />)
});
const initialProps = await Document.getInitialProps(ctx);
return {
...initialProps,
styles: (
<>
{initialProps.styles}
{sheet.getStyleElement()}
</>
)
};
} finally {
sheet.seal();
}
}
}
This happens because your styles are being applied client side. You will need to follow this modification from the examples provided by Next.js.
You actually need to create a custom Document, collect all your styles from your <App /> component using ServerStyleSheet provided by styled-components and apply them server side, so when your app reaches the client, the styles will already be there.
As they also state on the README of this example:
For this purpose we are extending the <Document /> and injecting the server side rendered styles into the <head>, and also adding the babel-plugin-styled-components (which is required for server side rendering).
I hope this solves your issue.
Here is an example of the _document file:
import Document, { Head, Main, NextScript } from 'next/document';
import { ServerStyleSheet } from 'styled-components';
export default class MyDocument extends Document {
static getInitialProps({ renderPage }) {
const sheet = new ServerStyleSheet();
function handleCollectStyles(App) {
return props => {
return sheet.collectStyles(<App {...props} />);
};
}
const page = renderPage(App => handleCollectStyles(App));
const styleTags = sheet.getStyleElement();
return { ...page, styleTags };
}
render() {
return (
<html>
<Head>{this.props.styleTags}</Head>
<body>
<Main />
<NextScript />
</body>
</html>
);
}
}
I hope this helps!

Categories

Resources