React styled components does not recognise props - css

I'm using react styled components and react-responsive hooks for media-query. My problem is when trying to pass the variable that contains the matching window size, to the styled component CSS.
After importing the hook, I defined the window sizes and assigned to "big" and "mobile" screen. The following variables returns a boolean if the conditions are meet:
const isMobile = useMediaQuery({ query: '(max-width: 600px)' });
const isBigScreen = useMediaQuery({ query: '(min-width: 600px)' });
The next step is to create the Styled components in the render() component and in the .styles.js file. The three components from the render() are inside a paragraph tag.
My goal is to pass the props to the styling and if the width isMobile, the component inside the tag will hide the overflow text in the ellipse dots (...);
{/* House details */}
<HouseDetailsInfo>
<p>{house.city}</p>
<p>{house.street}</p>
{/* <p>Price: {house.price} £ / {house.typeOfTenancy}</p> */}
<p>
<HousePriceEl isBigScreen isMobile={isMobile}>Price: </HousePriceEl>
<HousePriceNr isBigScreen isMobile={isMobile}>{house.price}£ </HousePriceNr>
<HouseTenancy isBigScreen isMobile={isMobile}>, {house.typeOfTenancy}</HouseTenancy>
</p>
</HouseDetailsInfo>
The following css are created in the component.styles.js file:
export const HouseDetailsInfo = styled.div`
display: flex;
flex-direction: column;
justify-content: space-around;
max-width: inherit;
& > p {
margin: 0;
padding: 5px 10px 5px 5px;
width: ${props => props.isMobile ? "180px" : "inherit"};
background: ${props => props.isBigScreen ? Colors.darkGrey : Colors.orange};
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
`;
I tried also to log the width props in the main element, and is working. But no matter what I do, the styles are not applying in the styles....

Related

How do I reference another component's styled-components generated className while creating a (hover) rule in a different component?

I'm creating a menu with styled-components and React, and want the color of the icon to change on hover, but I need it to change when the icon's parent is hovered, so that hovering the text next to the icon also activates the icon's hover styles. Here is the code I'm using to get close:
import React from 'react';
import styled from 'styled-components';
import { Link } from 'react-router-dom';
import { FontAwesomeIcon } from '#fortawesome/react-fontawesome';
const Menu = styled.div`
display: flex;
flex-direction: column;
`;
const LinkContainer = styled.div`
display: flex;
flex-direction: row;
`;
const FontAwesomeIconExtended = styled.div`
flex: 1;
display: grid;
place-items: center;
height: 40px;
width: 40px;
padding: 10px 2px;
border-radius: 10px;
transition: color 0.5s ease;
color: ${(props) => (props.$isactive ? '#fff' : '#CBE3EB')};
background: ${(props) =>
props.$isactive
? 'linear-gradient(96.34deg, #004157 0%, #0090b2 100%)'
: '#fff'};
${LinkContainer}:hover & {
color: ${(props) => (props.$isactive ? '#fff' : 'green')};
} /* Problem occurring here */
`;
const LinkText = styled.div`
flex: 1 0 100px;
`;
function NavLink({ ...props }) {
return (
<Link to={props.path}>
<LinkContainer $isactive={props.$isactive}>
<FontAwesomeIconExtended
$isactive={props.$isactive}
icon={props.icon}
size='2x'
as={FontAwesomeIcon}
/>
<LinkText $isactive={props.$isactive}>{props.name}</LinkText>
</LinkContainer>
</Link>
);
}
export default function NavMenu() {
return (
<Menu>
<NavLink path='/' name='Home' icon='house' $isactive />
<NavLink path='/profile' name='Profile' icon='user' />
<NavLink path='/payments' name='Payments' icon='credit-card-front' />
<NavLink path='/contracts' name='Contracts' icon='file-contract' />
<NavLink path='/messages' name='Messages' icon='mail-bulk' />
<NavLink path='/messages' name='Documents' icon='folders' />
<NavLink path='/locations' name='Transfer' icon='truck-moving' />
</Menu>
);
}
The way you reference another styled component in a later component is very clever, but in this case when it creates the hover rule, it creates without consideration of the different type of parent container ($isactive === true, or $isactive === false), so all LinkContainers have two rules for hovering, and use the last defined rule. This can be seen by moving $isactive to the last NavLink component.
Here is a screenshot of the devtools showing what I mean about the two hover rules not taking into consideration the parents class, just the general type of the parent.
I think the solution might involve being specific about the two types of LinkContainer's classNames while creating the hover rule, but that doesn't seem well supported. Thanks for taking a look.
Increase the specificity by repeating the class name, using another '&'
${LinkContainer}:hover && {

Using Prismjs for syntax highlighted code blocks is breaking layout on mobile - <pre> element wont take dynamic width

I'm using Prismjs alongside Mdx for a code-related blog. I'm using it to show code blocks in a manner consistent with other blogs.
I'm running into an issue where the rendered code blocks (inside a <pre> element are too wide on my mobile layout. For now I am content to have things scroll on the horizontal axis. I'm 99% certain that the <pre> elements are what's breaking the layout because when I comment them out of the blog post, the layout works as expected.
Specifically, I'm using a package called prism-react-renderer (alongside Gatsby), and the code I have for the CodeBlock element (that handles the syntax highlighting) is more or less verbatim from the documentation for prism-react-renderer, but is included here for convenience:
import React from 'react'
import Highlight, { defaultProps } from 'prism-react-renderer'
import theme from 'prism-react-renderer/themes/nightOwl'
const CodeBlock = (props) => {
const className = props.children.props.className || ''
const matches = className.match(/language-(?<lang>.*)/)
return (
<Highlight {...defaultProps} code={props.children.props.children.trim()} language={
matches && matches.groups && matches.groups.lang
? matches.groups.lang
: ''
}
theme={theme}>
{({ className, style, tokens, getLineProps, getTokenProps }) => (
<pre className={className} style={{ ...style }}>
<code>
{tokens.map((line, i) => (
<div key={i} {...getLineProps({ line, key: i })}>
{line.map((token, key) => (
<span key={key} {...getTokenProps({ token, key })} />
))}
</div>
))}
</code>
</pre>
)}
</Highlight>
)
}
export default CodeBlock
This is the component used in the blog post template that handles rendering the .mdx files into HTML:
import React from 'react'
import { Link, graphql } from 'gatsby'
import { MDXRenderer } from 'gatsby-plugin-mdx'
import { MDXProvider } from '#mdx-js/react'
import Layout from '../components/layout'
import CodeBlock from '../components/code-block'
const components = {
pre: CodeBlock
}
const BlogPostTemplate = ({ data, pageContext, location }) => {
const post = data.mdx
const { previous, next } = pageContext
return (
<Layout>
*** Removed irrelevant component ***
<MDXProvider components={components}>
<div className='blog-post-wrapper'>
<article className='blog-post-content'>
<header>
<h1>
{post.frontmatter.title}
</h1>
<time dateTime={post.frontmatter.date}>
{post.frontmatter.date}
</time>
</header>
<MDXRenderer>{post.body}</MDXRenderer>
</article>
<footer className='blog-post-footer'>
*** Removed irrelevant components ***
</footer>
</div>
</MDXProvider>
</Layout>
)
}
export default BlogPostTemplate
I have tried a few different things: flex shrink, applying overflow-x: scroll and overflow-x: auto to both the <pre> element and its parents. When I apply a fixed width to the <pre> element and overflow-x: scroll I can get the behavior I want but I'd like to not have to use a fixed width on this if possible. The .css I have looks like this, including some obviously ineffectual styles:
.blog-post-wrapper {
display: flex;
flex-direction: column;
overflow-y: scroll;
overflow-x: scroll;
}
.blog-post-content {
flex-grow: 1;
margin-bottom: 2rem;
width: 100%;
display: flex;
flex-direction: column;
overflow-y: scroll;
overflow-x: scroll;
}
.blog-post-content .prism-code {
padding: 20px;
border: 3px solid red;
flex-shrink: 1;
overflow-y: scroll;
overflow-x: scroll;
}
I'll attach images of the way the <pre> element is rendering presently, in inspector:
And this is how it looks if I set a fixed width (in inspector):
It's probably too late, but I had the same issue and I was able to fix it by
Adding max-width css property to the main layout. The value should be equal to window.screen.width. I had to use the following hack to be able to get the screen size:
const [windowWidth, setWindowWidth] = useState(width)
useEffect(() => {
setWindowWidth(window.screen.width)
}, [])
Adding overflow: scroll to the pre in the CodeBlock
Not ideal, but I found this combination of CSS properties working together:
pre code {
display: inline-block;
width: 80vw;
overflow-x: auto;
}

React.js & styled-components - Styled Input with (the illusion of ) Element inside it

so what i'm trying to do is to create an styled input that seem to have a fixed text (div / children / etc...) inside it.
one image can be self explenatory so this is how it looks like right now:
it seems simple, and the basic idea i quite is:
i got some Wrapper component that has 2 children - the input itself & some element.
like so:
const Input = (props: InputProps) => {
return (
<Wrapper>
<InputStyled {...props} />
<InnerElement>cm</InnerElement>
</Wrapper>
);
};
export default Input;
So where is the problem?
The problem is when i'm trying to set width to this component.
It destroys everything.
is looks like so:
so the wrapper should get a width prop and keep the input and the text element inside of it.
here is a codesandbox i created:
https://codesandbox.io/s/reactjs-input-with-element-inside-forked-2kr6s?file=/src/App.tsx:0-1795
it'll be nice if someone understand what i'm doing wrong.
here are some files:
Input.tsx
import React, { InputHTMLAttributes, CSSProperties } from "react";
import styled from "styled-components";
export interface InputProps extends InputHTMLAttributes<HTMLInputElement> {
style?: CSSProperties;
label?: string;
value: string | number;
onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
text?: string;
isDisabled?: boolean;
hasError?: boolean;
errorLabel?: string | Function;
placeholder?: string;
width?: string;
}
const defaultProps: InputProps = {
text: "",
onChange: () => null,
value: ""
};
const Wrapper = styled.div<Partial<InputProps>>`
outline: 1px dashed red;
box-sizing: border-box;
justify-self: flex-start; // This is what prevent the child item strech inside grid column
border: 2px solid lightblue;
background: lightblue;
border-radius: 8px;
display: inline-flex;
align-items: center;
max-width: 100%;
width: ${({ width }) => width};
`;
const InputStyled = styled.input<Partial<InputProps>>`
box-sizing: border-box;
flex: 1;
outline: 1px dashed blue;
padding: 8px;
border: none;
border-radius: 8px;
max-width: 100%;
:focus {
outline: none;
}
`;
const InnerElement = styled.div`
outline: 1px dashed green;
box-sizing: border-box;
${({ children }) =>
children &&
`
padding: 0 8px;
font-size: 13px;
`};
`;
const Input = (props: InputProps) => {
return (
<Wrapper width={props.width}>
<InputStyled {...props} />
<InnerElement>{props.text}</InnerElement>
</Wrapper>
);
};
Input.defaultProps = defaultProps;
export default Input;
App.tsx
import * as React from "react";
import "./styles.css";
import Input from "./Input";
import styled from "styled-components";
const ContainerGrid = styled.div`
display: grid;
grid-template-columns: 1fr 1fr;
grid-gap: 50px;
`;
export default function App() {
const [value, setValue] = React.useState("");
const handleChange = (e: any) => {
setValue(e.target.value);
};
const renderInputWithElement = () => {
return (
<ContainerGrid>
<Input
placeholder="input with element inside"
value={value}
onChange={handleChange}
width={"60px"} // Play with this
text="inch"
/>
<Input
placeholder="input with element inside"
value={value}
onChange={handleChange}
width={"110px"} // Play with this
text="cm"
/>
<Input
placeholder="input with element inside"
value={value}
onChange={handleChange}
width={"200px"} // Play with this
text="mm"
/>
<Input
placeholder="input with element inside"
value={value}
onChange={handleChange}
width={"100%"} // Play with this
text="El"
/>
<Input
placeholder="input with element inside"
value={value}
onChange={handleChange}
width={"100%"} // Play with this
/>
</ContainerGrid>
);
};
return (
<div className="App">
<h1>StyledCode</h1>
<h3>Input with an element inside</h3>
<p>
This is kind of an illusion since input cannot have child element inside
of it really.
</p>
<hr style={{ marginBottom: "32px" }} />
{renderInputWithElement()}
</div>
);
}
I Fixed it for you:
https://codesandbox.io/s/reactjs-input-with-element-inside-forked-h2het
First problem was that your were setting the same width to Wrapper and to InputStyled (by passing ...props) Then things get messy. Obviously, both parent and children which has some other element next to it can't have same width. InputStyled's won't be rendered with width you give it, but lesser, as it's being stretched to left by InnerElement.
If you need to pass it all the props, including the width, than you can just remove that width prop by setting width to unset
<InputStyled {...props} width="unset" />
However, there was another issue with padding interfering with width calculation. See answer to another question:
According to the CSS basic box model, an element's width and height
are applied to its content box. Padding falls outside of that content
box and increases the element's overall size.
As a result, if you set an element with padding to 100% width, its
padding will make it wider than 100% of its containing element. In
your context, inputs become wider than their parent.
You can go along with solution there. However, the one simple solution is to use width lesser than your padding, so width: calc(100% - 16px). This does the trick.
So i've found the answer, and its quite simple.
since the browser initializes an input with a width, it cause problems
with the flexbox behavor - the input element can't shrink below its default width and is > forced to overflow the flex container.
So i added min-width: 0 to InputStyled.
thats it.
#justdv thanx for your answer, you right about the width being on the wrong place, i fixed it but the sandbox wasnt updated. i missed that.
thanx anyway for your time :)
here is my fixed sandbox:
https://codesandbox.io/s/reactjs-input-with-element-inside-forked-44h8i?file=/src/Input.tsx
Thanx again for reading.

Material UI Paper element not covering 100% of viewport height

I have a Material UI <Paper> component that serves as a background and exists in my main React component- it's nested inside a <ThemeProvider>, which is nested a <div>, which is then nested in the <body>. I've applied the viewport: 100vh attribute to make it take the full height of the screen. It does take up the full height, but only prior to rendering another <Paper> component on the right hand side. Then the bottom of the paper no longer extends to the bottom of the screen:
Beginning of App render method:
return (
<ThemeProvider theme={theme}>
<Paper style={{ height: '100vh', boxShadow: 'none' }}>
<Container fluid id='app'>
.......
)
I tried applying the viewport: 100vh attribute to both the <div> that encloses the App component in index.js and the <body> element in index.html. There wasn't any difference. It may be worth mentioning that I'm using react-bootstrap Containers/Rows/Cols for my grid system at the moment (haven't switched that part to Material UI yet), but they're all nested inside the Paper, so I wouldn't expect they would be causing the problem. I also tried removing any css applied to the <Container> but it didn't help.
I'm also using a muiTheme for the <ThemeProvider> (obviously):
export default function createTheme(isDarkModeEnabled) {
return createMuiTheme({
palette: {
type: isDarkModeEnabled ? 'dark' : 'light',
primary: {
main: '#6DD3CE',
dark: '#639FAB'
},
secondary: {
main: '#52CBC5'
}
},
typography: {
fontFamily: [ 'montserratlight', 'Times New Roman' ].join(','),
body2: {
fontFamily: [ 'montserratmedium', 'Times New Roman' ].join(',')
},
h3: {
fontSize: '1.75rem'
},
button: {
fontFamily: [ 'montserratmedium', 'Times New Roman' ].join(',')
}
}
})
}
Update and Solution
I did redo my layout using flexbox instead of react-bootstrap, and ultimately fixed the problem by using min-height: 100vh instead of height: 100vh for my container so it had room to expand.
It appears that your <Container> has a bit of padding that is causing the content to go beyond its height:
This could also be due to your <textarea /> not having an assigned height. This is a particular issue when you are in a medium-sized screen.
If you are already planning to do away with Bootstrap's layout system, consider flex box and styled containers:
import React from "react";
import styled from "styled-components";
export default function App() {
return (
<Container>
<StyledHeader>Hello StackOverflow</StyledHeader>
<StyledCol>
<p>This is column 1.</p>
</StyledCol>
<StyledCol>
<p>This is another column.</p>
</StyledCol>
</Container>
);
}
const Container = styled.div`
height: 700px;
width: 100%;
display: flex;
flex-flow: row wrap;
justify-content: space-between;
`;
const StyledHeader = styled.h1`
height: 30%;
width: 100%;
margin: 0;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
`;
const StyledCol = styled.div`
height: 70%;
width: 50%;
& p {
text-align: center;
}
`;
CodeSandbox Example: https://codesandbox.io/s/stack-63802170-flexbox-example-909ol
This will ultimately give you more control over the layout of the page.

How to place two divs next to each other within another div using css?

I have a labelname and its value within div tag and in another div tag, I have added a button. I want the labelname, its value and the button to be in the same line or row. below is my code typescript and react.
render = () => {
return (
<Wrapper>
<Count>
<SpanComponent>
Counter value
</LabelComponent>
<SpanComponent
color={
counter === 0
? 'red'
: counter === 1
? 'blue'
: 'green'
}
>
{' '}
{counter}
</SpanComponent>
</Count>
<ButtonWrapper>
<Button>
Button
</Button>
</ButtonWrapper>
</Wrapper>
)
}
const ButtonWrapper = styled.div`
display: flex;
justify-content: flex-end;
`;
const LabelComponent = styled.span<Props>`
margin: 0;
padding: 0;
line-height: ${props => props.lineh || 1.5};
text-align: ${props => props.align || 'initial'};
`;
With the above code, it looks like below,
Could someone help me put the labelname, value and the button in the same line...
something like below,
Counter value 0 Button
Thanks.
You just need to tell the parent div how to lay its children out. I see you are using flex already on the button. Just use it on Wrapper
Const Wrapper = styled.div`
display: flex;
justify-content: space-between;
/* ... rest of your styles here */
`
Here's an example of it live
try display: inline-block for the child elements

Resources