Styled components on hover change img src attribute - reactjs

I have a styled component as below. It is a google social login button imgGoogleLogin is a path loaded by webpack.
I want to change the src attribute to another src when it is on hover.
Is there any way to achieve this? Thanks a lot
const GoogleLoginButton = styled.img.attrs({
src: imgGoogleLogin,
})`
width: 190px;
height: 45px;
&:hover {
cursor: pointer;
}
`;

I wanted to achieve something similar but I found that styled components cannot explicitly do this. I had to do it this way, ie create two components one hidden and when the parent is hovered I unhide it and hide the other one. Seems hacky but better than using e.setAttribute I think.
const GoogleLoginButton = styled.img.attrs(
props => ({'src': props.img})
)`
display: inline;
width: 190px;
height: 45px;
&:hover {
cursor: pointer;
}
`;
const GoogleLoginButtonHover = styled.img.attrs(
props => ({'src': props.img})
)`
display: none;
width: 190px;
height: 45px;
&:hover {
cursor: pointer;
}
`;
const GoogleLoginButtonParent = styled.div`
&:hover ${GoogleLoginButtonHover} {
display: inline;
}
&:hover ${GoogleLoginButton} {
display: none;
}
`;
In your render you use it like this:
<GoogleLoginButtonParent>
<GoogleLoginButton
img = {props.img}
/>
<GoogleLoginButtonHover
img = {props.imgHover}
/>
</GoogleLoginButtonParent>

You can achieve this with pure CSS:
By replacing your img tag with a div and setting its CSS as follow:
div {
background: url('to_first_image');
}
div:hover {
background: url('to_second_image');
}
If you rather keep your img tag and use some JS:
onHover = (e) => {
e.setAttribute('src', 'source_to_first_img');
}
onUnhover = (e) => {
e.setAttribute('src', 'source_to_second_img');
}
Credit to this answer : https://stackoverflow.com/a/18032363/10449875

Related

How to test Next.js background image? using Sass module. jest can't find rendered background string

while writing tests for my application I stumbled upon a problem where Next changes the background image URL to a different, unique, URL.
Investigating this problem led me into a small rabbit hall however the main conclusion that I found was that getComputedStyle method didn't recognize my CSS styles at all.
I assume that the reason for that is that I'm using CSS modules.
what do you think may be the cause, and how can I properly test the header's background image?
Header component (contains the style responsible displaying the background image):
export const Header : NextFunctionComponent = ({toggleMode, theme}) => {
return (
<header data-theme={theme} className={styles.header} data-testid="header">
<div className={styles.header__content}>
<h1 className={styles.header__title}>todo</h1>
<button className={styles.header__button}>
<Image className={styles.header__image} src={ theme === "light" ? Moon : Sun} onClick={toggleMode} alt="theme-toggle-icon" />
</button>
</div>
</header>
);
};
header style file (CSS/SASS module, file ends with .module.scss) :
$light-background : url("../../public/static/bg-desktop-light.jpg");
$dark-background : url("../../public/static/bg-desktop-dark.jpg");
#mixin header_content(){
margin: auto;
height: 100%;
width: 100%;
display: flex;
justify-content: center;
align-items: center;
}
#mixin header_title(){
font-size: 3.2rem;
letter-spacing: 20px;
color: white;
// Will need to change to a relative value
margin-right: 200px;
}
#mixin header_button(){
background-color: transparent;
height: 15%;
width: 3%;
border: none;
};
#mixin header_image(){
background-color: transparent;
height: 100%;
width: 100%;
}
#mixin mode_toggle($image){
display: flex;
flex-direction: column;
background-repeat: no-repeat;
background-size: cover;
background-position: center;
height: 35vh;
background-image: $image;
transition: all 0.3s ease-in-out;
& .header__content {
#include header_content();
.header__title {
#include header_title();
};
.header__button {
#include header_button();
&:hover {
cursor: pointer;
}
}
.header__image {
#include header_image();
}
}
}
.header[data-theme="light"]{
#include mode_toggle($light-background);
};
.header[data-theme="dark"] {
#include mode_toggle($dark-background);
};
test file:
describe("render the header and it's child components", () => {
let header;
const headerBackroundLight = "../public/static/bg-desktop-light.jpg";
// bg-desktop-light.jpg";
beforeEach(() => {
render(<Header />);
header = screen.getByTestId("header");
})
// header section
it("renders a header", () => {
expect(header).toBeInTheDocument();
});
it("renders a header", () => {
const headingText = screen.getByText(/todo/i);
expect(headingText).toBeInTheDocument();
});
it("the header's background is the one that the user expects", () => {
let computed = getComputedStyle(header);
expect(header).toHaveStyle("background-size: cover");
});
it("the header contains the darkmode toggle button ", () => {
expect(header);
});
// body section (list items of the todo list)
it('', () => {
expect()
})

How to make Styled components generic to receive parameters?

I'm trying to use Styled Components with React FC. I want to display few details inside a div container, which all has the same stylings. Instead of writing hard coded such as Name: Height: Weight: etc. in the h-tags, can I do it in a more generic way to pass the text as parameter?
This is the way I have implemented, but I am thinking there should be some better way to do it. Like giving a parameter to the Styled component.
const CardContentContainer= styled.div`
margin: 4px;
padding: 4px;
text-align: left;
font-size: 14px;
`;
const CardContentDetails = styled.h3`
font-weight: normal;
color: #fff;
margin: 8px;
white-space: nowrap;
`;
this is the code inside the return:
return (
<CardContentContainer>
<CardContentDetails>
Name: {ItemDetails?.name}
</CardContentDetails>
<CardContentDetails>
Height:{ItemDetails?.height}
</CardContentDetails>
<CardContentDetails>
Weight:{ItemDetails?.weight}
</CardContentDetails>
</CardContentContainer>
);
You cant pass such props to Styled components because the main point of SC is to apply a custom style to that component. To make it receive some custom props, you have to wrap it inside another component like so...
const StyledTextField = styled(TextField)`
margin-bottom: 1rem;
width: 20em;
`;
const Comp: React.FC<{ title: string }> = ({ title }) => (
<StyledTextField>{title}</StyledTextField>
);
return (
<Comp title="some title" />

How to Desktop View, Tablet View ,Mobile view change inside a browser ReactJS

I am new to Reactjs . How to add 3 views inside a browser,by automatically change view.
Reference Page Link
You can wrap all your content with a div and control the width of that div.
// App.js
import { useState } from 'react';
import './App.css';
function App() {
const [state, setState] = useState('desktop');
return (
<div className="App">
<button onClick={() => setState('desktop')}>Desktop</button>
<button onClick={() => setState('tablet')}>Tablet</button>
<button onClick={() => setState('phone')}>Phone</button>
<div className={`container ${state}`}>Your content here</div>
</div>
);
}
export default App;
/*App.css*/
.App {
height: 100%;
background-image: -webkit-linear-gradient(0deg, #766dff 0%, #88f3ff 100%);
}
/* General styling */
button {
padding: 0.5rem;
margin: 1rem;
border-radius: 10px;
cursor: pointer;
border: none;
}
.container {
/* General styling */
background-color: whitesmoke;
padding: 1rem;
border-radius: 5px;
margin-top: 1rem;
transition: all 1s;
/* Center the container */
margin-left: auto;
margin-right: auto;
}
.desktop {
width: 90%;
}
.tablet {
width: 768px;
}
.phone {
width: 480px;
}
/* index.css */
/* To make full width and height */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
html,
body,
#root {
height: 100%;
}
The above answer is correct. You can simply give a width to the main container to 100% and place your other container with other elements inside of it, also specify width using #mediaqueries(" https://www.w3schools.com/css/css_rwd_mediaqueries.asp "). This way, you can control the elements inside the main container. I can say for sure that you need to try it by yourself and see how everything works, use devtools(there is toogle device toolbar).

How can I get variables from my Theme with styled components and use within astroturf css

I've come across a "problem" which is making me feel like I've chosen the wrong stack for what I'd like. I'm pretty new with React & Typescript but I wondered if anyone knew of a way too do the following.
theme.ts
import { createGlobalStyle } from "styled-components"
import { ThemeType } from "../types/theme"
export const lightTheme = {
background: "#F2F3EF",
text: "#252524",
}
export const darkTheme = {
background: "#1C282B",
text: "#F2F3EF",
}
export const GlobalStyles = createGlobalStyle<{ theme: ThemeType }>`
body {
margin: 0;
margin-left: 300px;
background: ${({ theme }) => theme.background};
color: ${({ theme }) => theme.text};
font-family: Tahoma, Helvetica, Arial, Roboto, sans-serif;
}
`
styles.ts
import { css } from "astroturf"
import { useTheme } from "styled-components"
const theme = useTheme()
const styles = css`
.navContainer {
width: 300px;
height: 100%;
position: absolute;
top: 0;
left: 0;
}
.themeToggle {
position: absolute;
bottom: 0;
text-align: left;
padding: 15px;
}
.logo {
padding: 20px;
}
.menuItems {
a {
text-decoration: none;
display: block;
padding: 5px;
}
a.light:hover {
background-color: ${({ theme }) => theme.background};
}
a.dark:hover {
background-color: ${({ theme }) => theme.background};
}
}
`
export default styles
I get the following error message, which DOES make sense
Could not resolve interpolation to a value, css returned class name,
or styled component. All interpolated styled components
must be in the same file and values must be statically determinable at
compile time
I'm running webpack with postcss but also, trying to use themes from style-components.
The end goal is to use a toggle switch to change theme from light/dark but I just need some guidance on what approach I should be taking. Clearly implementing css-in-js with astroturf for postcss is going to be problematic with the way I've used themes but I wanted to ask you guys before I tear it out.
There could be an easy solution which I'm missing. I have tried just importing the variables light and dark and then trying to use those within my css string but, this also doesnt work.
Thanks in advance

using styled-component in react.js

I am first time using styled-component with react.js. My code is in Abc.js and in styles.js I have styled-component code.
export const StyledPopopDiv = styled.div`
width: 200px;
height: 100px;
background: #cecece;
position: fixed;
z-index: 200;
left: ${state => state.pos.x}px;
top: {state.pos.y}px;
`;
StyledPopopDiv.displayName = 'StyledPopopDiv';
I am trying to pass state.pos.x in styled-component, but this is giving error Cannot read property 'x' of undefined.
I don't know what to do. Please help.
You should first of all pass the state as a prop to the styled component example :
// Abc.js
<StyledPopopDiv pos={posState} />
So you can use this prop on your styled component definition as below:
//styled.js
export const StyledPopopDiv = styled.div`
width: 200px;
height: 100px;
background: #cecece;
position: fixed;
z-index: 200;
left: ${ props => props.pos.x}px; // outputs '10px' in this example
top: ${ props => props.pos.y}px; // outputs '20px' in this example
`;
A better way/tip is to use destruction/spread operator example :
export const StyledPopopDiv = styled.div`
width: 200px;
height: 100px;
background: #cecece;
position: fixed;
z-index: 200;
left: ${ ({ pos }) => pos.x}px;
top: ${ ({ pos }) => pos.y}px;
`;
it should be props instead of state, assuming you're using StyledPopupDiv in Abc.js render.
interface StyledPopupDivProps{
x:string
}
export const StyledPopopDiv =
styled<LinkProps, "div">("div")`
margin: ${props=>props.x}
`;
and pass
<StyledPopupDiv x={intendedValue}/>
where its being used.

Resources