I have a React component that asks user to upload profile photo. In the same view, the user can see a greyed out 'next' button. When the photo is uploaded and displayed, the user sees a horizontal scrolling bar to adjust photo size that comes between the photo and next button. My goal is to keep the next button in view at all times but also keep all elements of the component in the same order. Right now, a user has to scroll down to see the 'next' once the image is loaded. I've been experimenting with flex and position and even a ternary in the JSX to add paddingBottom when !isLoading. Can someone give a good direction so that the user doesn't have to scroll down to see the next' button?
Here is my component:
import React, { useContext, useState, useEffect } from 'react';
import styled from 'styled-components';
import Typography from 'components/Typography';
// import { useMediaQuery } from 'beautiful-react-hooks';
import { medium } from 'constants/mediaQueries';
import AppButton from 'components/AppButton';
import useFetcher from 'hooks/useFetcher';
import { getMeApi } from 'utils/apiRoutes';
import Loader from 'components/Loader';
import routePaths from 'containers/Router/routePaths';
import { useHistory, useLocation } from 'react-router-dom';
import { UserContext } from 'containers/context/UserContext';
import checkmarkImage from 'assets/images/checkmark.svg';
import { useDispatch } from 'react-redux';
import BackArrowGrid from 'components/Auth/BackArrowGrid';
import UploadImageInput from 'components/UploadImageInput';
import { setPlainLogo, setInitialLogo } from '../../store/header/actions';
// const MOBILE = 250;
// const DESKTOP = 356;
const Container = styled.div`
width: 100%;
position: relative;
align-items: center;
justify-content: flex-end;
display: flex;
flex-direction: column;
border: pink solid 2px;
`;
const ButtonContainer = styled.div`
margin-top: 32px;
display: flex;
align-items: center;
flex-direction: row;
div {
margin-bottom: 16px;
}
img {
margin-left: 8px;
}
`;
const Title = styled(Typography)`
font-size: 40px;
text-transform: uppercase;
margin-bottom: 64px;
#media (min-width: ${medium}) {
margin-bottom: 64px;
}
`;
const ButtonContent = styled.span`
display: flex;
align-items: center;
`;
const Form = styled.div`
width: 100%;
display: flex;
flex-direction: column;
align-self: flex-end;
border: yellow solid 2px;
.empty {
display: none;
}
#media (min-width: ${medium}) {
flex-direction: column;
align-items: center;
min-height: auto;
.empty {
display: block;
width: 118px;
}
}
`;
const UploadPicture = () => {
const { fetcher, error, isLoading } = useFetcher();
const { setUserFromLogin } = useContext(UserContext);
const history = useHistory();
const location = useLocation();
// const isDesktop = useMediaQuery(`(min-width: ${medium})`);
const dispatch = useDispatch();
const [image, setImage] = useState({});
const uploadImage = async () => {
const result = await fetcher({
url: getMeApi,
method: 'POST',
body: {
photo: image.croppedImage.source,
},
});
if (result.errors) {
return;
}
setUserFromLogin(result.user);
if (location.profile) {
history.push(routePaths.Profile);
} else {
history.push(routePaths.Welcome);
}
};
useEffect(() => {
dispatch(setPlainLogo());
return () => dispatch(setInitialLogo());
}, []);
const onPictureChange = newImage => {
setImage(prevImage => ({
...prevImage,
...newImage,
}));
};
return (
<>
<BackArrowGrid>
<Container>
<Title h1>Profile photo</Title>
<Form>
<div className="empty"> </div>
<UploadImageInput
onChange={onPictureChange}
image={image}
placeholder={
"Upload a profile photo. This is how you'll appear to other Color TV users."
}
height={356}
width={356}
horizontalPadding={60}
aspect={1}
round
/>
<ButtonContainer>
{error && (
<Typography error>
Error while uploading the image, please try again.
</Typography>
)}
<AppButton
primary={!!image.croppedImage}
disabled={isLoading || !image.croppedImage}
onClick={uploadImage}
noBorder
>
<ButtonContent>
{isLoading ? (
<Loader className="center" size={30} />
) : (
'Next'
)}
{!isLoading && !!image.croppedImage && (
<img src={checkmarkImage} alt="checkmark_icon" />
)}
</ButtonContent>
</AppButton>
</ButtonContainer>
</Form>
</Container>
</BackArrowGrid>
</>
);
};
export default UploadPicture;
Since this component is part of a larger code base, I'm not sure how to run a snippet of it but here are 2 photos - before and after. Once the photo is uploaded, the next button drops out of view because the slider component is added in. I want to next button to stay in view no matter how the browser size.
Related
I noticed that my styles on a component look right in storybook but not in the running application. The component code is at the bottom of this post.
All the components are from MUI, and looking at the "Computed" tab of the Chrome inspector, I can see that the Avatar components are getting their styles differently in the two environments -- the priorities of the classes are swapped:
In the app:
In the storybook
Wrapping the styles in && fixes the problem, and I've used this solution before for MUI style issues, but I've never come across the case where it behaves differently in the app vs storybook.
Here's my main decorator in preview.tsx
export const decorators: ComponentStoryObj<React.FC>['decorators'] = [
story => {
return (
<>
<header>
<link
href='https://fonts.googleapis.com/css?family=Roboto:100,200,300,400,500,600,700'
rel='stylesheet'
/>
<link
href='https://fonts.googleapis.com/css?family=Roboto+Condensed:400,500,600,700'
rel='stylesheet'
/>
</header>
<AppWrapperWithUser>{story()}</AppWrapperWithUser>
</>
);
},
];
Here's the AppWrapper used above, which also wraps the application itself (so that the styles match)
import { ThemeProvider as MuiThemeProvider } from '#mui/material/styles';
import { ThemeProvider as StyledThemeProvider } from 'styled-components';
import { globalStyles, theme } from './theme';
type Props = {
store?: ReturnType<typeof createStore>;
initialState?: RootState;
};
export const AppWrapper: React.FC<Props> = ({ children, store, initialState }) => {
const appStore = store || createStore(initialState);
return (
<MuiThemeProvider theme={theme}>
<StyledThemeProvider theme={theme}>
{globalStyles}
<Provider store={appStore}>{children}</Provider>
</StyledThemeProvider>
</MuiThemeProvider>
);
};
And the component in question:
const S = {
Wrapper: styled(Paper)`
padding: 32px;
display: flex;
justify-content: space-between;
align-items: center;
position: relative;Ø
overflow: hidden;
`,
Pane: styled.div<{ centered?: boolean }>`
display: flex;
flex-direction: column;
gap: 4px;
align-items: ${p => p.centered && 'center'};
`,
Subtitle: styled(Typography).attrs({ variant: 'body2' })`
font-size: 14px;
text-transform: uppercase;
`,
Title: styled(Typography)`
&& {
font-size: 28px;
font-weight: 700;
}
`,
Avatar: styled(Avatar)`
height: 84px;
width: 84px;
box-sizing: content-box;
border: 2px solid rgba(0, 0, 0, 0.1);
`,
TeamLogoWrapper: styled.div`
display: flex;
align-items: center;
height: 100%;
width: 222px;
`,
TeamLogo: styled(Avatar)`
height: 222px;
width: 222px;
box-sizing: content-box;
opacity: 0.3;
border: 2px solid rgba(0, 0, 0, 0.1);
position: absolute;
`,
};
const PlayerDetailHeader = ({ player }: { player: ProcessedPropUIPlayer }) => {
const { ... } = player;
const infoStr = [teamName, `#${uniformNumber}`, position].join(' | ');
return (
<S.Wrapper data-testid={`PlayerDetailHeader-${playerId}`}>
<S.Avatar alt={playerName} src={playerImg} />
<S.Pane>
<S.Title>{playerName}</S.Title>
<S.Subtitle>{infoStr}</S.Subtitle>
</S.Pane>
<S.Pane centered>
<S.Title>{openProps}</S.Title>
<S.Subtitle>Open Props</S.Subtitle>
</S.Pane>
<S.Pane centered>
<S.Title>{dollarFormatter(openAction)}</S.Title>
<S.Subtitle>Open Action</S.Subtitle>
</S.Pane>
<S.TeamLogoWrapper>
<S.TeamLogo alt={teamName} src={teamImg} />
</S.TeamLogoWrapper>
</S.Wrapper>
);
};
I can scroll on the x axis only by moving the laptop touchpad right to left or by pressing in the scroll button and then moving right to left.Not with normal scroll.
the css is the following:
.row {
color: white;
margin-left: 20px;
}
.row__posters {
display: flex;
overflow-y: hidden;
overflow-x: scroll;
padding: 20px;
}
.row__posters::-webkit-scrollbar {
display: none;
}
.row__poster {
object-fit: contain;
width : 100%;
max-height: 100px;
margin-right: 10px;
transition: transform 450ms;
}
.row__poster:hover {
transform: scale(1.08);
}
.row__posterLarge {
max-height: 250px;
}
.row__posterLarge:hover {
transform: scale(1.09);
}
the Javascipt file is:
import React,{ useState , useEffect} from 'react'
import axios from './axios';
import './Row.css';
const base_url = "https://image.tmdb.org/t/p/original/";
function Row({ title ,fetchUrl,isLargeRow }) {
const [movies, setMovies] = useState([]);
// A snippet of code which runs based on a specific condition
useEffect(() => {
// if we leave the brackets blank [] ,run once when the row loads
and dont run again
async function fetchData() {
const request = await axios.get(fetchUrl);
setMovies(request.data.results);
return request;
}
fetchData();
}, [fetchUrl]);
return (
<div className="row">
<h2>{title}</h2>
<div className="row__posters">
{/* several row_posters */}
{movies.map(movie => (
<img
key={movie.id}
className={`row__poster ${isLargeRow && "row__posterLarge"}`}
src={`${base_url}${
isLargeRow ? movie.poster_path : movie.backdrop_path
}`}
alt={movie.name}
/>
))}
</div>
</div>
)
}
export default Row
I tried alot of solutions but I must be doing something wrong because nothing worked .it could be that I used the proposed code in the wrong department.
Thank you for the help in advance!!
I have following react code.
the code
What I would like is to just when I click on "about" menu background color should change from red to blue otherwise stay in red.
in another scenario:
(my problem is how to access to links "route" or "path" in react because i want to add condition like ==> if (path =="./about) do somthing...)
is there any way to do this? i would appreciate for your help.
If you are using react-router-dom
Then you can use the useLocation hook to get the current location
First import it
import { useLocation } from 'react-router-dom';
Then get the location
const location = useLocation();
You can use a useEffect hook to change the background color whenever the location changes
useEffect(()=>
{
if(location.pathname == '/about'){
// Change background color
}
}
},[location])
This should work for you
Try to include this code in your App.js
Following code works:
const Nav = styled.nav`
height: 100px;
display: flex;
flex-wrap: wrap;
justify-content: space-between;
align-items: center;
padding: 0px 10px;
width: 100%;
z-index: 100;
/* background-color: red; */
`;
const Menu = styled.div`
display: flex;
flex-wrap: wrap;
gap: 20px;
align-items: center;
justify-content: center;
`;
const Navbar = ({ toggle }) => {
const [navBg, setNavBg] = React.useState("red");
const location = useLocation();
React.useEffect(() => {
if (window.location.pathname === "/about") {
setNavBg("yellow");
} else {
setNavBg('red')
}
}, [location]);
return (
<Nav style={{ backgroundColor: navBg }}>
<h1 to="/">logo</h1>
<Menu>
<Link to="/">home</Link>
<Link to="/about">about</Link>
<Link to="/services">services</Link>
</Menu>
</Nav>
I'm new to react and css.
My goal here is to center my 3 switches and the name of them in the center of my page. Then I would like the 3 switches button to adjust their position automatically no matter the length of the text on its left.
Here is what I have for the moment.
This is the switch button + text. I made a style to align the text with the middle of the switch icon. I tried the verticalAlign: "middle" but it the text went at the bottom position.
import React, { useState } from "react";
import Switch from "react-switch";
const SwitchMode = ({text}) => {
const [checked, setChecked] = useState(false)
const handleChange = StateCheck => {
setChecked(StateCheck)
}
return(
<div className="switch-position">
<label>
<span style={{display: "inline-flex", verticalAlign: '50%', marginRight: '4%'}}>{text}</span>
<Switch
onChange={handleChange}
checked={checked}
className="react-switch"
/>
</label>
</div>
)
}
export default SwitchMode
And this is the css I've made for the moment
body {
margin: 0;
}
.image-position {
display: flex;
justify-content: center;
width: 100%;
margin-top: 5%;
height: auto;
}
.service-position {
display: flex;
flex-direction: column;
margin-top: 5%;
}
.switch-position {
display: inline;
justify-content: center;
width: 100%;
margin-top: 1%;
}
in this file I create my switch button, apply the css and give them a name.
import React from 'react';
import './index.css';
import ReactRoundedImage from "react-rounded-image";
import Img from './images/Diamond.png'
import SwitchMode from './Switch';
const Profile = () => {
return(
<div>
<div className='image-position'>
<ReactRoundedImage image={Img} roundedSize="0" imageWidth="150" imageHeight="150" />
</div>
<div className='service-position'>
<SwitchMode text="Discord"/>
<SwitchMode text="Google"/>
<SwitchMode text="FEJZIOFZEJ"/>
</div>
</div>
)
}
export default Profile
this is the actual result
You can do something like this:
body {
margin: 0;
}
.image-position {
display: flex;
justify-content: center;
width: 100%;
margin-top: 5%;
height: auto;
}
.service-position {
display: flex;
justify-content: center;
flex-direction: row;
margin-top: 5%;
}
.switch-position {
text-align: center;
width: 100px;
}
and change your SwitchMode component to this:
const SwitchMode = ({ text }) => {
const [checked, setChecked] = useState(false);
const handleChange = (StateCheck) => {
setChecked(StateCheck);
};
return (
<div className="switch-position">
<label>
<span>{text}</span>
<Switch
onChange={handleChange}
checked={checked}
className="react-switch"
/>
</label>
</div>
);
};
The main things I did was set justify-content: center and flex-direction: row on .service-position. To make sure the .switch-position items displayed in a row and were centered.
For centering the text next to the switch button I used text-align: center and to make sure the text is displayed above the switch button I set a fixed with on .switch-position.
Sandbox Example
I've been working on a section with expandable/collapsible sections. When I click on a section to expand or collapse it, a blue focus area shows up but it is placed on a weird angle. I don't know what is causing it and would like a solution to either get rid of it or place it back at the normal horizontal angle. Does anybody have any suggestions as to how to fix this?
I am using a Macbook and Chrome browser.
The entire grey block that this component appears in is placed at an angle as you can see from the top of the image attached below but in the reverse direction from the highlighted focus area.
My css:
#import '../../theme/variables.css';
.rotatedSection {
padding-bottom: 2rem;
}
.container {
max-width: 64rem;
margin: 0 auto;
display: flex;
padding: 2rem 0;
#media screen and (max-width: 68rem) {
margin: 0 3rem;
}
}
.accordianContainer {
flex: 1;
margin-right: 2rem;
min-width: 500px;
#media screen and (max-width: $tablet-lg-max-width) {
margin-right: 0;
}
#media screen and (max-width: 900px) {
min-width: 0;
}
}
.imageContainer {
flex: 1;
margin-left: 2rem;
max-height: 300px;
display: flex;
justify-content: center;
img {
flex: 1;
}
#media screen and (max-width: $tablet-lg-max-width) {
margin-left: 0;
}
}
.heading {
composes: h2 from 'theme/text';
margin-left: auto;
margin-right: auto;
}
My react code:
import React, {Component, PropTypes} from 'react';
import RotatedSection from 'components/RotatedSection';
import AccordionItem from './AccordionItem';
import css from './styles.css';
class AccordionSectionWithImage extends Component {
constructor (props) {
super(props);
this.state = {
activeIndex: null,
};
this.onOpen = this.onOpen.bind(this);
this.onClose = this.onClose.bind(this);
this.setActive = this.setActive.bind(this);
this.handleClickOutside = this.handleClickOutside.bind(this);
}
onOpen = (index) => {
this.setActive(index);
};
onClose = (callback = () => null) => {
this.setActive(null);
callback();
};
setActive = (activeIndex) => this.setState({activeIndex});
handleClickOutside = () => this.props.collapseOnBlur && this.onClose();
render () {
const {
entry: {
items,
heading,
image,
},
showIndex,
classNames,
meta = {},
} = this.props;
const {routeParams, toggleHamburger} = meta;
const {activeIndex} = this.state;
return (
<RotatedSection color='whisper' className={css.rotatedSection}>
<div className={css.container}>
<div className={css.accordianContainer}>
<h2 className={css.heading}>{heading}</h2>
{items && items.map((item, index) => (
<AccordionItem
key={index}
showIndex={showIndex}
entry={item}
meta={{
position: index,
isOpen: (index === activeIndex),
onOpen: () => this.onOpen(index),
onClose: () => this.onClose(),
onChildClick: () => this.onClose(toggleHamburger),
routeParams,
}}
classNames={classNames}
/>
))}
</div>
<div className={css.imageContainer}>
<img src={image && image.fields && image.fields.file.url} alt='Educational assessment' />
</div>
</div>
</RotatedSection>
);
}
}
AccordionSectionWithImage.propTypes = {
meta: PropTypes.object,
entry: PropTypes.object,
collapseOnBlur: PropTypes.bool,
showIndex: PropTypes.bool,
classNames: PropTypes.object,
};
export default AccordionSectionWithImage;
React component for individual section:
function AccordionItem (props) {
const {
meta: {
isOpen,
onOpen,
onClose,
},
entry: {
heading,
text,
},
} = props;
const handleClick = () => (isOpen ? onClose() : onOpen());
return (
<div className={css.itemContainer}>
<div className={css.innerContainer}>
<h3 className={css.heading} onClick={handleClick}>
<span className={css.titleText}>{heading}</span>
<i className={`zmdi zmdi-plus ${css.titleToggle}`} />
</h3>
{isOpen && (
<div className={css.contents}>
{text}
</div>
)}
</div>
</div>
);
}
For anybody else experiencing a similar problem:
Problem only appeared on mobile phones and the device mode of chrome inspector. It was due to the tap-highlight property.
Setting -webkit-tap-highlight-color to rgba(0,0,0,0) hid the problem but it's a non standard css property so the solution may not work for all devices/browsers/users.