Styled components are not rendering in React Iframe? - javascript

List item
I've created a React Iframe (using 'react-frame-component'), and some styled components. When I try to render the styled components inside the React Iframe, the styled components inherits the Parent's (App.css) style, and loses its unique style. Is it possible to maintain the styled component?
Here is my code:
Content.js
import React from 'react';
import ReactDOM from 'react-dom';
import Frame, { FrameContextConsumer } from 'react-frame-component';
import App from "./App";
class Main extends React.Component {
render() {
return (
<Frame head={[<link type="text/css" rel="stylesheet" href={chrome.runtime.getURL("/static/css/content.css")} ></link>]}>
<FrameContextConsumer>
{
({document, window}) => {
return <App document={document} window={window} isExt={true}/>
}
}
</FrameContextConsumer>
</Frame>
)
}
}
App.js
import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
import Card from './components/Card/index';
class App extends Component {
render() {
return (
<>
<div className="App">
{this.props.isExt ?
<img src={chrome.runtime.getURL("static/media/logo.svg")} className="App-logo" alt="logo" />
:
<img src={logo} className="App-logo" alt="logo" />
}
<Card />
</div>
</>
);
}
}
export default App;
Style:
import React from "react";
import styled, { css } from "styled-components";
export const CardWrapper = styled.div`
overflow: hidden;
padding: 0 0 32px;
margin: 48px auto 0;
width: 300px;
font-family: Quicksand, arial, sans-serif;
box-shadow: 0 0 20px rgba(0, 0, 0, 0.05), 0 0px 40px rgba(0, 0, 0, 0.08);
border-radius: 5px;
`;
export const CardHeader = styled.header`
padding-top: 32px;
padding-bottom: 32px;
`;
export const CardHeading = styled.h1`
font-size: 24px;
font-weight: bold;
text-align: center;
`;
export const CardBody = styled.div`
padding-right: 32px;
padding-left: 32px;
`;
export const CardFieldset = styled.fieldset`
position: relative;
padding: 0;
margin: 0;
border: 0;
& + & {
margin-top: 24px;
}
&:nth-last-of-type(2) {
margin-top: 32px;
}
&:last-of-type {
text-align: center;
}
`;
export const CardInput = styled.input`
padding: 7px 0;
width: 100%;
font-family: inherit;
font-size: 14px;
border-top: 0;
border-right: 0;
border-bottom: 1px solid #ddd;
border-left: 0;
transition: border-bottom-color 0.25s ease-in;
&:focus {
border-bottom-color: #e5195f;
outline: 0;
}
`;
export const CardIcon = styled.span`
color: #666;
cursor: pointer;
opacity: .25;
transition: opacity .25s ease-in;
&:hover {
opacity: .95;
}
${props =>
props.big &&
css`
font-size: 26px;
`}
${props =>
props.eye &&
css`
position: absolute;
top: 8px;
right: 0;
`}
${props =>
props.small &&
css`
font-size: 14px;
`}
`;
export const CardOptionsNote = styled.small`
padding-top: 8px;
display: block;
width: 100%;
font-size: 12px;
text-align: center;
text-transform: uppercase;
`;
export const CardOptions = styled.ul`
padding: 0;
margin: 16px 0 8px;
display: flex;
flex-direction: row;
flex-wrap: wrap;
align-items: center;
justify-content: center;
width: 100%;
list-style-type: none;
`;
export const CardOptionsItem = styled.li`
&:nth-of-type(n + 2) {
margin-left: 16px;
}
`;
export const CardButton = styled.button`
display: block;
width: 100%;
padding: 12px 0;
font-family: inherit;
font-size: 14px;
font-weight: 700;
color: #fff;
background-color: #e5195f;
border: 0;
border-radius: 35px;
box-shadow: 0 10px 10px rgba(0, 0, 0, 0.08);
cursor: pointer;
transition: all 0.25s cubic-bezier(0.02, 0.01, 0.47, 1);
&:hover {
box-shadow: 0 15px 15px rgba(0, 0, 0, 0.16);
transform: translate(0, -5px);
}
`;
export const CardLink = styled.a`
display: inline-block;
font-size: 12px;
text-decoration: none;
color: #aaa;
border-bottom: 1px solid #ddd;
cursor: pointer;
transition: color 0.25s ease-in;
&:hover {
color: #777;
}
`;
Component:
import React from "react";
import {
CardWrapper,
CardHeader,
CardHeading,
CardBody,
CardIcon,
CardFieldset,
CardInput,
CardOptionsItem,
CardOptions,
CardOptionsNote,
CardButton,
CardLink
} from './Card';
const Card = () => {
return (
<>
<CardWrapper>
<CardHeader>
<CardHeading>Sign in</CardHeading>
</CardHeader>
<CardBody>
<CardFieldset>
<CardInput placeholder="Username" type="text" required />
</CardFieldset>
<CardFieldset>
<CardInput placeholder="E-mail" type="text" required />
</CardFieldset>
<CardFieldset>
<CardInput placeholder="Password" type="password" required />
<CardIcon className="fa fa-eye" eye small />
</CardFieldset>
<CardFieldset>
<CardOptionsNote>Or sign up with</CardOptionsNote>
<CardOptions>
<CardOptionsItem>
<CardIcon className="fab fa-google" big />
</CardOptionsItem>
<CardOptionsItem>
<CardIcon className="fab fa-twitter" big />
</CardOptionsItem>
<CardOptionsItem>
<CardIcon className="fab fa-facebook" big />
</CardOptionsItem>
</CardOptions>
</CardFieldset>
<CardFieldset>
<CardButton type="button">Sign Up</CardButton>
</CardFieldset>
<CardFieldset>
<CardLink>I already have an account</CardLink>
</CardFieldset>
</CardBody>
</CardWrapper>
</>
);
};
export default Card;
Attempt at injecting stylesheet for CardWrapper:
/*global chrome*/
/* src/content.js */
import React from 'react';
import { useContext, FrameContext } from 'react';
import ReactDOM from 'react-dom';
import Frame, { FrameContextConsumer } from 'react-frame-component';
import { StyleSheetManager } from 'styled-components';
import App from "./App";
export const StyledFrame = (props) => {
const {
CardWrapper,
CardHeader,
CardHeading,
CardBody,
CardFieldset,
CardInput,
CardIcon,
CardOptionsNote,
CardOptions,
CardOptionsItem,
CardButton,
CardLink,
} = props;
class Main extends React.Component {
render() {
return (
<Frame head={[<link type="text/css" rel="stylesheet" href={chrome.runtime.getURL("/static/css/content.css")} ></link>]}>
<InjectFrameStyles>
{props.CardWrapper}
</InjectFrameStyles>
</Frame>
)
}
}
};
const InjectFrameStyles = (props) => {
const { document } = useContext(FrameContext);
return <StyleSheetManager target={document.head}>{props.CardWrapper}</StyleSheetManager>;
}
const app = document.createElement('div');
app.id = "my-extension-root";
document.body.appendChild(app);
ReactDOM.render(<Main />, app);
app.style.display = "none";
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
if( request.message === "clicked_browser_action") {
toggle();
}
}
);
function toggle(){
if(app.style.display === "none"){
app.style.display = "block";
}else{
app.style.display = "none";
}
}

I had the same problem. I ended up creating a helper component called StyledFrame.
What happens is that your styled-components css is being rendered in the parent frame. You need to intercept those styles, and render them inside your iframe instead. Thats what StyleSheetManager is for.
You can use StyledFrame just like you would the Frame component.
import React, { useContext } from 'react';
import Frame, { FrameContext } from 'react-frame-component';
import { StyleSheetManager } from 'styled-components';
export const StyledFrame = (props) => {
const { style, children, ...otherProps } = props;
return (
<Frame
initialContent={
'<!DOCTYPE html><html><head></head><body><div class="frame-root"></div><div id="modal-root"></div></body></html>'
}
style={{ display: 'block', overflow: 'scroll', border: 0, ...style }}
{...otherProps}
>
<InjectFrameStyles>{props.children}</InjectFrameStyles>
</Frame>
);
};
const InjectFrameStyles = (props) => {
const { document } = useContext(FrameContext);
return <StyleSheetManager target={document.head}>{props.children}</StyleSheetManager>;
};

Related

How to display data from clicked container to other con

Can someone help me to display specific song while clicking on that in Your Playlist container on the left??
I am trying to list data on the left in Your Playlist container When I click on one of the music it should show it in Your Playlist container. It has to save it to browser history as well and it has to remove it from Search because it is already gonna be in Your Playlist container. I will deploy it later to Firebase but now I need help.
It should be added to the left while clicking on one of the listed songs after a search.
Please support me on that.
I am adding my codes here as well for my project:
I have App.js
import "./App.css";
import MySongs from "./MySongs.js";
import Search from "./Search.jsx";
function App() {
return (
<div className="App">
<div className="body">
<MySongs />
<Search />
</div>
</div>
);
}
export default App;
.App {
background-color: #303030;
width: 100%;
height: 100vh;
}
.body {
display: flex;
}
I have Search.jsx
import React, { useState, useEffect, useRef } from "react";
import "./Search.css";
import styled from 'styled-components';
import { IoSearch,IoClose } from "react-icons/io5";
import {motion, AnimatePresence} from "framer-motion";
import {useClickOutside} from "react-click-outside-hook";
import MoonLoader from 'react-spinners/MoonLoader';
import { useDebounce } from "./hooks/debounceHook";
import axios from "axios";
import { TvShow } from "./tvShow";
const SearchBarContainer = styled(motion.div)`
margin-left: 10px;
margin-top: 20px;
display: flex;
flex-direction: column;
width: 96%;
height: 2.5em;
background-color: #424242;
border-radius: 3px;
`;
const SearchInputContainer = styled.div`
width: 98%;
min-height: 2.5em;
display: flex;
align-items: center;
position: relative;
padding: 2px 15px;
`;
const SearchInput = styled.input`
width: 100%;
height: 100%;
outline: none;
border: none;
font-size: 15px;
color: white;
font-weight: 300;
border-radius: 6px;
background-color: transparent;
&:focus {
outline: none;
&::placeholder {
opacity: 0;
}
}
&::placeholder {
color: #white;
transition: all 250ms ease-in-out;
}
`;
const SearchIcon = styled.span`
color: #bebebe;
font-size: 14px;
margin-right: 10px;
margin-top: 6px;
vertical-align: middle;
`;
const CloseIcon = styled(motion.span)`
color: #bebebe;
font-size: 15px;
vertical-align: middle;
transition: all 200ms ease-in-out;
cursor: pointer;
&:hover {
color: #dfdfdf;
}
`;
const LineSeperator = styled.span`
display: flex;
min-width: 100%;
min-height: 2px;
background-color: #d8d8d878;
`;
const SearchContent = styled.div`
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
padding: 1em;
overflow-y: auto;
`;
const LoadingWrapper = styled.div`
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
`;
const WarningMessage = styled.span`
color: #a1a1a1;
font-size: 14px;
display: flex;
align-self: center;
justify-self: center;
`;
const containerVariants = {
expanded: {
height: "26em",
},
collapsed: {
height: "2.5em",
},
};
const containerTransition = { type: "spring", damping: 22, stiffness: 150 };
export function SearchBar(props) {
const [isExpanded, setExpanded] = useState(false);
const [parentRef, isClickedOutside] = useClickOutside();
const inputRef = useRef();
const [searchQuery, setSearchQuery] = useState("");
const [isLoading, setLoading] = useState(false);
const [tvShows, setTvShows] = useState([]);
const [noTvShows, setNoTvShows] = useState(false);
const isEmpty = !tvShows || tvShows.length === 0;
const changeHandler = (e) => {
e.preventDefault();
if (e.target.value.trim() === "") setNoTvShows(false);
setSearchQuery(e.target.value);
};
const expandContainer = () => {
setExpanded(true);
};
const collapseContainer = () => {
setExpanded(false);
setSearchQuery("");
setLoading(false);
setNoTvShows(false);
setTvShows([]);
if (inputRef.current) inputRef.current.value = "";
};
useEffect(() => {
if (isClickedOutside) collapseContainer();
}, [isClickedOutside]);
const searchTvShow = async () => {
if (!searchQuery || searchQuery.trim() === "") return;
setLoading(true);
setNoTvShows(false);
const options = {
method: 'GET',
url: 'https://deezerdevs-deezer.p.rapidapi.com/search',
params: {q: searchQuery},
headers: {
'x-rapidapi-host': 'deezerdevs-deezer.p.rapidapi.com',
'x-rapidapi-key': '6a99d5e101msh1e9f2b2f948746fp1ae1f3jsn6b458fe8b4e4'
}
};
axios.request(options).then(function (response) {
if (response) {
if (response.data && response.data.length === 0) setNoTvShows(true);
setTvShows(response.data.data);
}
}).catch(function (error) {
console.error(error);
});
setLoading(false);
};
useDebounce(searchQuery, 500, searchTvShow);
// console.log(tvShows);
return (
<div className="my__search">
<SearchBarContainer
animate={isExpanded ? "expanded" : "collapsed"}
variants={containerVariants}
transition={containerTransition}
ref={parentRef}
>
<SearchInputContainer>
<SearchIcon>
<IoSearch />
</SearchIcon>
<SearchInput
placeholder="Search for Series/Shows"
onFocus={expandContainer}
ref={inputRef}
value={searchQuery}
onChange={changeHandler}
/>
<AnimatePresence>
{isExpanded && (
<CloseIcon
key="close-icon"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
onClick={collapseContainer}
transition={{ duration: 0.2 }}
>
<IoClose />
</CloseIcon>
)}
</AnimatePresence>
</SearchInputContainer>
{isExpanded && <LineSeperator />}
{isExpanded && (
<SearchContent>
{isLoading && (
<LoadingWrapper>
<MoonLoader loading color="#000" size={20} />
</LoadingWrapper>
)}
{!isLoading && isEmpty && !noTvShows && (
<LoadingWrapper>
<WarningMessage>Start typing to Search</WarningMessage>
</LoadingWrapper>
)}
{!isLoading && noTvShows && (
<LoadingWrapper>
<WarningMessage>No Tv Shows or Series found!</WarningMessage>
</LoadingWrapper>
)}
{!isLoading && !isEmpty && (
<>
{tvShows.map((show) => (
<TvShow
key={show.id}
thumbnailSrc={show.album.cover_medium}
name={show.title_short}
artist={show.artist.name}
/>
))}
</>
)}
</SearchContent>
)}
</SearchBarContainer>
</div>
);
}
export default SearchBar;
.my__search {
margin-top: 20px;
flex: 0.6;
height: 450px;
border-radius: 5px;
border: 1px solid black;
margin-left: 80px;
background-color: #424242;
}
I have tvShow.jsx
import React, { useState } from "react";
import styled from "styled-components";
import {ImDownload} from "react-icons/im";
const TvShowContainer = styled.div`
width: 96%%;
min-height: 3em;
display: flex;
border-bottom: 2px solid #555555;
align-items: center;
`;
const Thumbnail = styled.div`
width: auto;
height: 80%;
display: flex;
flex: 0.4;
img {
border-radius: 20px;
width: auto;
height: 100%;
}
`;
const Name = styled.h3`
font-size: 12px;
color: white;
flex: 2;
display: flex;
flex-direction: column;
`;
const Artist = styled.span`
margin-top: 10px;
font-size: 8px;
color: white;
display: flex;
align-items: center;
`;
const Rating = styled.span`
color: #a1a1a1;
font-size: 16px;
display: flex;
flex: 0.2;
`;
export function TvShow(props) {
const { thumbnailSrc, name, artist,clickedMusic } = props;
const [wantedMusic, setWantedMusic] = useState("");
// const [clickedShow, setClickedShow] = useState("");
// function clickedContainer(e){
// const element = e.currentTarget();
// setClickedShow(element);
// console.log("I am clickedShow " +clickedShow);
// }
return (
<TvShowContainer onclick="location.href='#';" >
<Thumbnail>
<img src={thumbnailSrc} />
</Thumbnail>
<Name>{name}
<Artist>
{artist}
</Artist>
</Name>
</TvShowContainer>
);
}
I have mySongs.js
import React from "react";
import "./MySongs.css";
function MySongs() {
return (
<div className="my__songs">
<p>Your Playlist</p>
</div>
);
}
export default MySongs;
.my__songs {
margin-left: 10px;
margin-top: 20px;
flex: 0.3;
height: 300px;
height: 450px;
border: 1px solid black;
border-radius: 5px;
background-color: #424242;
}
.my__songs > p {
color: white;
opacity: 90%;
margin-left: 10px;
font-size: 13px;
}
Only partly answering the question: move an item from one list to another (and back) on mouse click.
The basic situation can be solved if you use the parent component to hold the state that the children components display. Then you only need to implement a function that toggles a "flag" (like selected), and the components can be rendered based on that flag.
const {useState} = React
const tracklist = [
{
id: 1,
title: 'Track 1',
selected: false,
},
{
id: 2,
title: 'Track 2',
selected: false,
},
{
id: 3,
title: 'Track 3',
selected: false,
},
{
id: 4,
title: 'Track 4',
selected: false,
},
{
id: 5,
title: 'Track 5',
selected: false,
},
]
const ListItem = ({ title, onToggleSelect }) => <div className="list-item" onClick={onToggleSelect}>{title}</div>
const App = ({ tracklist }) => {
const [tracks, setTracks] = useState(tracklist)
const toggleSelect = (id) => {
setTracks((prevState) => prevState.map(item => item.id === id ? {...item, selected: !item.selected} : item))
}
const listItem = (track) => <ListItem key={track.id} {...track} onToggleSelect={() => toggleSelect(track.id)}/>
return (
<div className="container" >
<div className="tracklist">
{
tracks
.filter(({ selected }) => selected)
.map(listItem)
}
</div>
<div className="tracklist">
{
tracks
.filter(({ selected }) => !selected)
.map(listItem)
}
</div>
</div>
)
}
ReactDOM.render(
<App tracklist={tracklist} />,
document.getElementById('root')
);
html, body {
margin: 0;
padding: 5px 10px;
}
.container {
display: grid;
grid-template-columns: 1fr 3fr;
gap: 20px;
}
.list-item {
cursor: pointer;
}
.list-item:hover {
background: lightgray;
}
.tracklist {
border: 1px solid gray;
}
<script src="https://unpkg.com/react#17/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom#17/umd/react-dom.development.js" crossorigin></script>
<div id="root"></div>

Chunk loading error in production React for MacOS and IOS users

I've got a problem for few weeks and I'm not even sure of his reason.
After building with the create-react-app template and deploying the app in production for an unknown reason a CSS chunk isn't called on all MacOS, IOS navigators (there aren't any errors on the console) but on Windows and Android the chunk is called by the navigators.
However, with the versions deployed in test and dev, it works fine for every user.
I've tried many things to debug this:
Remove the LazyLoading of this component.
Include the chunk in the head of index.html.
Suppress the CSS file and paste his content into another CSS file to reduce the number of chunks.
Change the instance type where it's deployed to have the same environment as dev and test.
Here's some screenshots of the difference:
The chunk called on Windows and Android
The render on Windows and Android
The render on MacOS and IOS
Here the code of the component:
import React, { useState } from 'react';
import { Button, Modal, Form } from 'react-bootstrap';
import '../../../../styles/reusable/popIn/AddFreeModal/addFreeModal.scss';
import validator from 'validator';
import { sendInviteEmail } from '../../../../reducers/mail';
import { useDispatch } from 'react-redux';
import { useToasts } from 'react-toast-notifications';
export default function AddFreeModal(props) {
const { handleClose, show } = props;
const { addToast } = useToasts();
const [email, setEmail] = useState('');
const [first_name, setFirst] = useState('');
const [last_name, setLast] = useState('');
const [emailErr, setEmailErr] = useState('');
const [first_nameErr, setFirstErr] = useState('');
const [last_nameErr, setLastErr] = useState('');
const dispatch = useDispatch();
const verifMail = (email) => validator.isEmail(email);
const verifFirst = (first_name) => first_name.length > 2;
const verifLast = (last_name) => last_name.length > 2;
const emailNotExist = () => addToast(`Echec de l'invitation, adresse mail erronée: ${email}`, {
appearance: 'error',
autoDismiss: true,
});
const emailAlreadySend = () => addToast('Invitation déja envoyée cette semaine réessayez plus tard !', {
appearance: 'error',
autoDismiss: true,
});
const mailSend = () => handleClose('true');
const handleForm = async () => {
setEmailErr('');
setFirstErr('');
setLastErr('');
if (verifMail(email) && verifFirst(first_name) && verifLast(last_name)) {
const res = await dispatch(sendInviteEmail(email, first_name, last_name));
if (res === 'False') emailNotExist();
else if (res === 'Already Send') emailAlreadySend();
else mailSend()
setEmail('')
setFirst('')
setLast('')
} else {
if (!verifMail(email)) setEmailErr('Veuillez entrer un mail valide !');
if (!verifFirst(first_name)) setFirstErr('Veuillez entrer un prenom !');
if (!verifLast(last_name)) setLastErr('Veuillez entrer un nom !');
}
};
return (<>
<Modal size="lg" className='addFreeModal' show={show} onHide={() => {handleClose(); setFirst(''); setEmail(''), setLast(''), setEmailErr(''); }}>
<Modal.Header closeButton>
<Modal.Title>Inviter un Consultant</Modal.Title>
</Modal.Header>
<Modal.Body className='formBody'>
<Form>
<Form.Control className='form-input' onChange={(e) => setEmail(e.target.value)} type="email"
placeholder="Email" />
{emailErr && <p className='txt-danger'>{emailErr}</p>}
<Form.Control className='form-input' onChange={(e) => setFirst(e.target.value)} type="email"
placeholder="Prénom" />
{first_nameErr && <p className='txt-danger'>{first_nameErr}</p>}
<Form.Control className='form-input' onChange={(e) => setLast(e.target.value)} type="email"
placeholder="Nom" />
{last_nameErr && <p className='txt-danger'>{last_nameErr}</p>}
</Form>
</Modal.Body>
<Modal.Footer>
<Button className='cancel-btn' onClick={() => {handleClose(); setFirst(''); setEmail(''), setLast('')}}> Annuler </Button>
<Button className='send-btn' onClick={handleForm}> Envoyer </Button>
</Modal.Footer>
</Modal>
</>);
}
And his style:
$graycolor: #ecececec;
$cancelColor: #57585a;
$greenColor: #3de4cf;
$blueColor: #426DEA;
$redcolor: red;
$whitecolor: #fff;
$blackColor: #000;
.addFreeModal {
.modal-content {
border-radius: 30px;
}
.modal-lg {
max-width: 40% !important;
}
.modal-header {
justify-content: center;
padding: 1rem 1rem 0rem 1rem;
button.close {
opacity: 1;
height: 5%;
width: 5%;
border-radius: 50px;
font-family: monospace;
background-color: $blueColor !important;
&:hover {
opacity: 1;
color: white;
background-color: $blueColor !important;
}
}
.close {
color: #fff;
text-shadow: none;
font-size: 1.8rem;
padding: 1px 0px 28px 0px;
font-weight: 400;
}
.modal-title {
font-weight: 600;
font-size: 2.5em;
margin: 20px auto 0px auto;
color: $greenColor;
}
}
.form-control{
height: calc(2em + .5rem + 2px);
}
.form-input {
width: 70%;
margin: 10px 0px 0px 15%;
border: solid #d1d2d4 1px;
border-radius: 12px;
}
.txt-danger {
font-size: 15px;
text-align: center;
}
.modal-footer {
padding: 0rem 1rem 1.5rem 1rem;
.send-btn {
width: 34%;
height: calc(2em + .5rem + 2px);
border-radius: 20px;
font-weight: 600;
font-size: 1.2em;
background-color: $blueColor;
margin-bottom: 5%;
&:hover {
cursor: pointer;
}
}
.cancel-btn {
width: 34%;
height: calc(2em + .5rem + 2px);
font-weight: 600;
background-color: $graycolor !important;
border: solid $graycolor 1px;
border-radius: 20px;
font-size: 1.2em;
color: $cancelColor;
margin-bottom: 5%;
&:hover {
cursor: pointer;
}
}
}
}

Styled Component only pass some value to custom component

Hi I'm trying to configure the colour of a button base on it's button type which will be put in a modal. In this case, either "Success" or "Danger":
import React from "react";
import styled from "styled-components";
const ButtonStyled = styled.button`
background-color: ${(props) =>
props.btnType === "Success" ? "green" : "red"};
border: none;
color: white;
outline: none;
cursor: pointer;
font: Regular 400;
padding: 0.5rem 0;
margin: 0.5rem 1rem;
font-weight: bold;
margin-left: 0;
padding-left: 0;
`;
const Button = (props) => {
console.log(props.btnType);
return (
<ButtonStyled btnType={props.btnType} onClick={props.clicked}>
{props.children}
</ButtonStyled>
);
};
export default Button;
Here's where I called the button:
import React from "react";
import Button from "../../UI/Button/Button";
const OrderSummary = (props) => {
return (
<div>
<Button btnType="Danger" clicked={props.cancelPurchase}>
Cancel
</Button>
<Button btnType="Success" clicked={props.continuePurchase}>
Continue
</Button>
</div>
);
};
export default OrderSummary;
Here is where the Order Summary being called:
import React, { useState } from "react";
import Modal from "../../components/UI/Modal/Modal";
import OrderSummary from "../../components/Burger/OrderSummary/OrderSummary";
const INGREDIENT_PRICES = {
salad: 0.5,
cheese: 0.4,
meat: 1.3,
bacon: 0.7,
};
const BurgerBuilder = () => {
const [ingredients, setIngredients] = useState({
salad: 0,
bacon: 0,
cheese: 0,
meat: 0,
});
const [totalPrice, setTotalPrice] = useState(4);
const [purchasing, setPurchasing] = useState(false);
const purchaseCancelHandler = () => {
setPurchasing(false);
};
const purchaseContinueHandler = () => {
alert("continue");
};
return (
<div>
<Modal show={purchasing} modalClosed={purchaseCancelHandler}>
<OrderSummary
continuePurchase={purchaseContinueHandler}
cancelPurchase={purchaseCancelHandler}
ingredients={ingredients}
price={totalPrice.toFixed(2)}
/>
</Modal>
</div>
);
};
export default BurgerBuilder;
Down here is the modal where the Button being applied
import React from "react";
import styled from "styled-components";
import Backdrop from "../Backdrop/Backdrop";
const ModalWrapper = styled.div`
box-shadow: 0 5px 16px rgba(0, 0, 0, 0.2);
background: #fff;
justify-content: center;
align-items: center;
color: #000;
display: grid;
grid-template-columns: 1fr 1fr;
position: fixed;
z-index: 500;
border-radius: 10px;
transition: all 0.3s ease-out;
padding: 16px;
left: 15%;
top: 30%;
#media (min-width: 600px) {
width: 500px;
left: calc(50% - 250px);
}
`;
const ModalContent = styled.div`
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
line-height: 1.8;
color: #141414;
p {
margin-bottom: 1rem;
}
button {
padding: 10px 24px;
background: #141414;
// color: #fff;
border: none;
}
`;
const Modal = (props) => {
return (
<div>
<Backdrop show={props.show} clicked={props.modalClosed} />
<ModalWrapper
style={{
transform: props.show ? "translateY(0)" : "translateY(-100vh)",
opacity: props.show ? "1" : "0",
}}
>
<ModalContent>{props.children}</ModalContent>
</ModalWrapper>
</div>
// <ModalStyled>{props.children}</ModalStyled>
);
};
export default Modal;
As I did some testing, the values in the styled component Modal Content which state the button in that content will be that. Hence it affected the value of the styled component Button Styled. However, if I removed the value of button in Modal Content, the value of the color in ButtonStyled won't be accepted. Does anyone happen to know why?
The button style defined in the ModalContent is overriding the button style you've defined in your Button component.
button {
padding: 10px 24px;
background: #141414; // <-- overrides button background color
border: none;
}
With CSS the most specific selector will be the one that sets the style and I suspect the ModalContent styled component's computed style results with a more specific selector for button elements than the one from ButtonStyled.
What you can do is bump the selector specificity of the ButtonStyled background color.
Pseudoelements, pseudoselectors, and nesting, the last section.
Finally, the ampersand can be used to increase the specificity of
rules on the component; this can be useful if you are dealing with a
mixed styled-components and vanilla CSS environment where there might
be conflicting styles
const ButtonStyled = styled.button`
&& {
background-color: ${(props) =>
props.btnType === "Success" ? "green" : "red"};
}
border: none;
color: white;
outline: none;
cursor: pointer;
font: Regular 400;
padding: 0.5rem 0;
margin: 0.5rem 1rem;
font-weight: bold;
margin-left: 0;
padding-left: 0;
`;

ReactJS: Changing button hover property depending on the text inside the button

I am working on a React application and I am using Redux to store the state. I have the following code:
request.component.jsx:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import Loading from '../loading/loading.component';
import { changeRequestStatus } from '../../redux/requests/requests.actions';
import { RESOLVED, AWAITING_WAIT_STAFF } from '../../redux/requests/requests.status-types'
import './request.styles.scss';
class Request extends Component {
state = { isLoading: false }
render() {
const { _id, table_no, timestamp, description, status } = this.props.request;
const { user, changeRequestStatus } = this.props;
return (
<>
{this.state.isLoading ? <Loading /> : null}
<div className="request-box">
<div className="request-details">
<div>
<h1 style={{ color: status === AWAITING_WAIT_STAFF ? "#28bfa6" : "#f5a953" }}>Table {table_no}, {new Date(timestamp).toLocaleString()}</h1>
<h2>{description}</h2>
</div>
<div className="status-button">
<button
className="request-button"
onClick={async () => {
this.setState({ isLoading: true })
await changeRequestStatus(_id, status === AWAITING_WAIT_STAFF ? user.username : RESOLVED)
this.setState({ isLoading: false })
}} style={{ background: status === AWAITING_WAIT_STAFF ? "linear-gradient(to right, rgba(141,227,227,1) 0%, rgba(114,240,218,1) 100%)" : "linear-gradient(to right, rgba(255,213,94,1) 0%, rgba(246,170,123,1) 100%)" }}>
{status}
</button>
</div>
</div>
</div>
</>
)
}
}
const mapStateToProps = (state) => {
return {
requests: state.requests.requests,
user: state.user.currentUser
}
}
export default connect(mapStateToProps, { changeRequestStatus })(Request);
request.styles.scss:
.request-box {
border: 1px solid #c3c9c8;
height: 200px;
max-width: 100%;
border-radius: 5px;
position: relative;
background-color: white;
font-family: Helvetica;
box-shadow: 0 10px 6px -6px #ededed;
margin: 10px;
}
.request-details {
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: space-between;
padding: 0 30px;
height: 100%;
h1 {
font-size: 30px;
color: #28bfa6;
text-align: left;
}
h2 {
font-size: 22px;
text-align: left;
}
}
.status-button {
padding-bottom: 25px;
width: 100%;
#media (min-width: 1000px) {
width: auto;
padding-right: 20px;
padding-left: 100px;
}
}
.request-button {
height: 50px;
font-size: 19px;
font-weight: 600;
border: none;
border-radius: 5px;
padding: 10px 25px;
background-size: 150% auto;
background: linear-gradient(to right, rgba(141,227,227,1) 0%, rgba(114,240,218,1) 100%);
cursor: pointer;
&:hover {
background: #2de1c2;
}
}
In my Request component, I am changing the background property of my request-button div depending on the value of the status variable.
However, I would like to change the request-buttton:hover property depending on the value of status variable in my Request component.
I am not sure what the correct syntax would be to achieve this. Any insights are appreciated.
Create different CSS classes for each button color/status. Then use a ternary operator to apply the CSS class to the button when status changes. Check the status via Redux and then apply a CSS className like this.

Centering active element inside custom React carousel

I have a simple custom React carousel:
https://codesandbox.io/s/infallible-wood-740s2?fontsize=14
It goes through numbers from 1 to 100. I'm trying to have an active number centered with large font size, and its neighbors with smaller font size:
To progress through this carousel, you have to click on the carousel.
The issue is that progressing through the carousel numbers starts to misalign. I suppose that it is because of the wrong calculation for left property.
I've tried to tweak the formula, use React Transition Group, and find some existing package which could solve this issue, but I haven't succeeded. I would appreciate any help.
Component code:
import React, { Component } from "react";
import ReactDOM from "react-dom";
import {
Wrapper,
NumbersWrapper,
NumbersScroller,
NumberText
} from "./Numbers.style";
const hundred = new Array(100)
.fill(0)
.map((k, v) => ({ key: v, label: v + 1 }));
class Numbers extends Component {
constructor(props) {
super(props);
this.state = {
activeNumber: 0
};
}
setActiveNumber(number) {
this.setState({
activeNumber: number
});
}
render() {
const { activeNumber } = this.state;
const intAciveNumber = Number(activeNumber);
return (
<Wrapper>
<NumbersWrapper
onClick={() => this.setActiveNumber(intAciveNumber + 1)}
>
<NumbersScroller
style={{
left: `${130 - intAciveNumber * 55}px`
}}
>
{hundred.map(({ key, label }) => {
const isNeighbor =
key + 1 === activeNumber || key - 1 === activeNumber;
const isActive = key === activeNumber;
return (
<NumberText
key={key}
isNeighbor={isNeighbor}
isActive={isActive}
>
{label}
</NumberText>
);
})}
</NumbersScroller>
</NumbersWrapper>
</Wrapper>
);
}
}
export default Numbers;
const rootElement = document.getElementById("root");
ReactDOM.render(<Numbers />, rootElement);
Numbers.style.js:
import styled from "styled-components";
export const Wrapper = styled.div`
height: 549px;
width: 612px;
border-radius: 28px;
background-color: #ffffff;
margin-top: 116px;
padding: 26px 0 0 0;
display: flex;
flex-direction: column;
align-items: center;
position: absolute;
left: 50%;
top: 0%;
transform: translate(-50%, -10%);
overflow: hidden;
`;
export const NumbersWrapper = styled.div`
white-space: nowrap;
width: 359.5px;
overflow: hidden;
`;
export const NumbersScroller = styled.div`
transition: all 150ms ease-in;
position: relative;
left: 130px;
`;
const numberTextStyle = props => {
if (props.isNeighbor) {
return `
height: 88px;
width: 53px;
opacity: 0.45;
color: #6C879C;
font-size: 80px;
font-weight: 300;
letter-spacing: -1.6px;
line-height: 88px;
text-align: center;
`;
}
if (props.isActive) {
return `
height: 156px;
color: #6C879C;
font-size: 150px;
font-weight: 300;
letter-spacing: -3px;
line-height: 156px;
text-align: center;
`;
}
return `
opacity: 0.2;
color: #6C879C;
font-size: 40px;
font-weight: 300;
letter-spacing: -0.8px;
line-height: 48px;
text-align: center;
`;
};
export const NumberText = styled.span`
font-family: Avenir;
margin: 0 15px;
user-select: none;
&:first-child {
margin-left: 0;
}
&:last-child {
margin-right: 0;
}
${props => numberTextStyle(props)}
`;

Categories

Resources