building a component that takes a value (attribute.readonly), and only displays the first 40 characters, unless the <Chevron onClick is triggered which means I then want to show fullDescription as opposed to the shortHeading - ideally this would render similar to how it does currently only doubling the height of the Accordion for example, to allow the rest of the content to fit. I know I'm still a bit off but some pointers for my next steps/ ideas for improving what I have already would be really appreciated!
// #flow
import styled from "styled-components";
import chevron from "../Assets/chevron-icon.svg";
type Props = { attribute: AttributeType, className?: string };
const Accordion = styled.div`
background-color: #e5e9eb;
height: 56px;
width: 612px;
border-radius: 2px;
border: 1px solid #27282a;
margin-bottom: 48px;
display: flex;
align-items: center;
span {
padding-left: 24px;
}
`;
const Chevron = styled.img`
height: 40px;
width: 40px;
float: right;
margin-right: 12px;
`;
const ExpandableString = ({ attribute, className }: Props) => {
const fullDescription = attribute.readonlyvalue;
const shortHeading = fullDescription.substring(0, 40) + '...';
const isExpanded = false;
function toggleContent() {
isExpanded = true;
}
return (
<Accordion className={className}>
<span>{shortHeading}</span>
<Chevron onClick={toggleContent} src={chevron} alt="Expand or collapse content" />
</Accordion>
);
};
export default ExpandableString;
Put your data in the state and toggle it
const ExpandableString = ({ attribute, className }: Props) => {
const [isExpanded, setIsExpanded] = React.useState(false);
function toggleContent() {
setIsExpanded(prev => !prev);
}
return (
<Accordion className={className}>
<span>{shortHeading}</span>
<Chevron onClick={toggleContent} src={chevron} alt="Expand or collapse content" />
</Accordion>
);
};
Then, according to the isExpanded state, you could conditionally render your React component.
Related
I am new to coding with React.js and monday.com. I am trying to create a weather app that works with monday.com. I want the app to use the location column on the monday.com board to display the weather data for a specific city. My api seems to work just fine and gives the weather results when I enter a city into my app. The below error occurred once I started to try and get the app to work with the monday.com board.
I have an import/export error. I have checked all my opening and closing brackets but they all seem correct. Please advise how I can fix this problem. Thanks!
Error:
Error in ./src/reportWebVitals.js
Syntax error: C:/Users/E7470/weather-app/src/reportWebVitals.js: 'import' and 'export' may only appear at the top level (3:4)
1 |
const reportWebVitals = onPerfEntry => {
2 | if (onPerfEntry && onPerfEntry instanceof Function) {
> 3 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
| ^
4 | getCLS(onPerfEntry);
5 | getFID(onPerfEntry);
6 | getFCP(onPerfEntry);
# ./src/index.js 19:23-51
App.js:
import React, { useState } from 'react';
import './App.css';
import mondaySdk from 'monday-sdk-js';
import 'monday-ui-react-core/dist/main.css';
//Explore more Monday React Components here: https://style.monday.com/
import AttentionBox from 'monday-ui-react-core/dist/AttentionBox.js';
const monday = mondaySdk();
function App() {
const apiKey = 'API KEY'; //I inserted my api key here
const [weatherData, setWeatherData] = useState([{}]);
const [city, setCity] = useState('');
const [context, setContext] = useState();
const [weatherArr, setWeatherArr] = useState();
useEffect(() => {
const getContext = async () => {
try {
monday.listen('context', (res) => {
console.log(res.data);
getBoards(res.data);
setContext(res.data);
});
monday.listen("settings", (res) => {
console.log(res.data);
setMeasurementUnit(res.data.dropdown)
})
//board id (Add board Id Here eg.328567743)
} catch (err) {
throw new Error(err);
}
};
getContext();
}, []);
const getBoards = async (context) => {
try {
var arr = [];
var boards = [];
for( const boardId of context.boardIds){
var res = await monday.api(`query { boards(limit:1, ids:[${boardId}]) {
name
items {
name
column_values {
id
value
text
}
}
}
}`);
boards.push(res.data.boards);
}
for (const board of boards ){
console.log(boards)
console.log(board)
for (const item of board[0].items){
var location = item.column_values[0].text;
var latLng = item.column_values[0].value
var itemObj = {
location,
latLng
}
const getWeather = (event) => {
if (event.key == 'Enter') {
fetch(
`https://api.openweathermap.org/data/2.5/weather?q=${city}&units=imperil&APPID=${apiKey}` //api given in video tutorial
)
.then((response) => response.json())
.then((data) => {
setWeatherData(data);
setCity('');
});
}
};
setWeatherArr(arr);
} catch (err) {
throw new Error(err);
}
};
return (
<div className="container">
<input
className="input"
placeholder="Enter City..."
onChange={(e) => setCity(e.target.value)}
value={city}
onKeyPress={getWeather}
/>
{typeof weatherData.main === 'undefined' ? (
<div>
<p>Welcome to weather app! Enter the city you want the weather of.</p>
</div>
) : (
<div className="weather-data">
<p className="city">{weatherData.name}</p>
<p className="temp">{Math.round(weatherData.main.temp)}℉</p>
<p className="weather">{weatherData.weather[0].main}</p>
</div>
)}
{weatherData.cod === '404' ? <p>City not found.</p> : <></>}
</div>
);
}
export default App;
App.css:
.container {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
padding: 25px;
}
.input {
padding: 15px;
width: 80%;
margin: auto;
border: 1px solid lightgrey;
border-radius: 6px;
font-size: 16px;
}
.input:focus{
outline: none;
}
.weather-data{
margin-top: 30px;
display: flex;
flex-direction: column;
align-items: center;
}
.city{
font-size: 30px;
font-weight: 200;
}
.temp{
font-size: 90px;
padding: 10px;
border: 1px solid lightgray;
border-radius: 12px;
}
.weather{
font-size: 30px;
font-weight: 200;
}
Here:
Open the app in desktop width, notice the div has green background
Reduce browser width to mobile, the div background should change to gray
Again, increase browser width to desktop, notice the gray background remains, instead of green
What should have happened
The background in last step should be green as in the first step, isn't it?
Logging value of isMobile does seem to show it is being updated.
Here is also code:
import React from 'react';
import styled from 'styled-components';
import { useMediaQuery } from 'react-responsive';
let MenuItem = styled.div`
height: 100px;
width: 100px;
border:1px solid red;
background-color: green;
// Select those items which are children of .submenu
.submenu & {
background-color: ${({ isMobile }) => {
return isMobile && 'lightgray';
}};
}
`;
function App() {
const isMobile = useMediaQuery({ query: '(max-width: 524px)' });
return (
<div>
<div className="submenu">
<MenuItem isMobile={isMobile}>test</MenuItem>
</div>
</div>
);
}
export default App;
import React, {useEffect, useState} from 'react';
import styled from 'styled-components';
import {useMediaQuery} from 'react-responsive';
let MenuItem = styled.div`
height: 100px;
width: 100px;
border:1px solid red;
background-color: green;
`;
function App() {
const isMobile = useMediaQuery({query: '(max-width: 524px)'});
const [color, setColor] = useState('green');
useEffect(() => {
if (isMobile) setColor('silver');
else setColor('green');
}, [isMobile])
return (
<div>
<div className="submenu">
<MenuItem style={{background: color}} isMobile={isMobile}>test</MenuItem>
</div>
</div>
);
}
export default App;
You could re-write this as:
const MenuItem = styled.div`
height: 100px;
width: 100px;
border:1px solid red;
background-color: green;
`;
const SubMenu = styled.div`
${MenuItem} {
background-color: ${({ isMobile }) => (isMobile ? `red` : 'lightgray')};
}
`;
function App() {
const isMobile = useMediaQuery({ query: '(max-width: 524px)' });
return (
<>
<SubMenu isMobile={isMobile}>
<MenuItem>MenuItem in SubMenu</MenuItem>
</SubMenu>
<MenuItem>MenuItem</MenuItem>
</>
);
}
Stackblitz
It is the correct answer:
https://stackblitz.com/edit/react-t7gqwx?file=src%2FApp.js,src%2Findex.js
You shouldn't use .submenu &.
Below is my code for a checkout page where i have the CardElement and Iam processing the payment through stripe. It was working fine 2 days back but it has disappeared from the UI all of a sudden. enter image description hereIs there any changes Stripe has made in its CardElement package or Am I missing anything in my code?
import React, { useEffect, useState } from "react";
import Header from "./Header";
import styled from "styled-components";
import { useSelector, useDispatch } from "react-redux";
import { selectUserName, selectUserEmail } from "../features/userSlice";
import {
selectCart,
selectCartTotalAmount,
removeAllItems,
} from "../features/cartSlice";
import Orders from "./Orders";
import { CardElement, useStripe, useElements } from "#stripe/react-stripe-js";
import { useHistory } from "react-router-dom";
import axios from "../axios";
import { db } from "../firebase";
function Checkout() {
const userName = useSelector(selectUserName);
const userEmail = useSelector(selectUserEmail);
const cart = useSelector(selectCart);
const cartTotalAmount = useSelector(selectCartTotalAmount);
const history = useHistory();
const [succeeded, setSucceeded] = useState(false);
const [required, setRequired] = useState("");
const [msg, setMsg] = useState("");
const [processing, setProcessing] = useState("");
const [address, setAddress] = useState("");
const [error, setError] = useState(null);
const [disabled, setDisabled] = useState(true);
const [clientSecret, setClientSecret] = useState(true);
const stripe = useStripe();
const elements = useElements();
const dispatch = useDispatch();
console.log("email", userEmail);
const handlechange = (e) => {
setAddress(e.target.value);
// else {
// setRequired(false);
// }
//localStorage.setItem("address", address);
};
// const goToPreview = () => {
// return <Preview address={address} />;
// };
const validateAddress = () => {};
useEffect(() => {
const getClientSecret = async () => {
const response = await axios({
method: "post",
url: `/payments/create?total=${cartTotalAmount * 100}`,
});
setClientSecret(response.data.clientSecret);
};
getClientSecret();
}, [cart]);
console.log("secret is", clientSecret);
const handleSubmit = async (e) => {
e.preventDefault();
if (address === "") {
setRequired(true);
setMsg("*Address is required!");
} else {
setProcessing(true);
console.log(processing);
const payload = await stripe
.confirmCardPayment(clientSecret, {
payment_method: {
card: elements.getElement(CardElement),
},
})
.then(({ paymentIntent }) => {
db.collection("users")
.doc(userEmail)
.collection("orders")
.doc(paymentIntent.id)
.set({
cart: cart,
amount: paymentIntent.amount,
created: paymentIntent.created,
address: address,
});
setSucceeded(true);
setError(null);
setProcessing(false);
dispatch(removeAllItems());
history.replace("/orders");
});
}
};
const handleCardChange = (e) => {
setDisabled(e.empty);
setError(e.error ? e.error.message : "");
};
return (
<div>
<Header />
<Container>
<Name>
<p>Name : {userName}</p>
</Name>
<Email>
<p>Email : {userEmail}</p>
</Email>
<Address>
<p>Address : </p>
<input type="text" onChange={handlechange} />
</Address>
<small>{msg}</small>
<Order>
<p>Order : </p>
</Order>
{cart.map((item) => (
<OrderCard>
<img src={item.image} />
<p>{item.title}</p>
<small>x</small>
<p>{item.cartQuantity}</p>
</OrderCard>
))}
<Payment>
Payment method :
<form>
<CardElement onChange={handleCardChange} />
</form>
</Payment>
<Total>Total : ${cartTotalAmount}</Total>
<button
onClick={handleSubmit}
disabled={processing || disabled || succeeded}
>
{processing ? "Processing" : "Place your order"}
</button>
{error && <div>{error}</div>}
</Container>
</div>
);
}
export default Checkout;
const Container = styled.div`
padding-top: 70px;
width: 100%;
height: 100vh;
background: linear-gradient(to bottom right, #feedf6, #fcf0e2);
margin: auto;
display: flex;
flex-direction: column;
align-items: flex-start;
justify-content: center;
button {
margin-top: 30px;
margin-bottom: 30px;
margin-left: 40rem;
align-items: center;
}
small {
margin-top: 5px;
margin-left: 20rem;
}
`;
const Name = styled.div`
margin-left: 35rem;
/* margin-right: 10px;
margin-left: 10px; */
`;
const Email = styled.div`
margin-left: 35rem;
/* margin-right: 10px;
margin-left: 10px; */
`;
const Address = styled.div`
display: flex;
justify-content: center;
margin-left: 35rem;
h3 {
margin-right: 10px;
margin-left: 5rem;
}
input {
margin-top: 20px;
margin-left: 5px;
width: 300px;
height: 100px;
}
`;
const Order = styled.div`
margin-left: 35rem;
`;
const OrderCard = styled.div`
display: flex;
justify-content: center;
margin-left: 35rem;
img {
width: 40px;
height: 40px;
}
small {
margin-top: 17px;
margin-left: 10px;
}
p {
margin-left: 20px;
}
`;
const Total = styled.div`
margin-left: 35rem;
`;
const Payment = styled.div`
margin-left: 35rem;
margin-bottom: 20px;
form {
margin-top: 10px;
flex: 0.8;
width: 600px;
height: 40px;
}
`;
I'm trying to set up an image src dynamically in reactJS, I'm using typescript and the code is as follows regarding the image.
This is just a segment of my code demonstrating the logic im trying to follow with updating the weather icon in my app.
Please, if you have other comments don't be shy to share them i'll be more than happy to accept any constructive criticism but mainly please help with the main issue I have here.
const [Icon, setIcon] = useState("");
useEffect(() => {
switch (Icon.substr(0, 2)) {
case "01":
setIcon(sun);
break;
case "50":
setIcon(fog);
break;
case "09":
setIcon(drizzle);
break;
case "11":
setIcon(storm);
break;
case "09" || "13" || "10":
setIcon(rain);
break;
case "13":
setIcon(snow);
break;
case "02" || "03" || "04":
setIcon(clouds);
break;
default:
setIcon(sun);
}
}, [Icon]);
return(<img src={Icon} />)
---FULL CODE ----
import React, { useState, useEffect } from "react";
import styled from "styled-components";
import Header from "../components/Header";
import Footer from "../components/Footer";
import axios from "axios";
import Clock from "react-live-clock";
import Loading from "../components/PageLoader";
import rain from "../images/rain.svg";
import drizzle from "../images/drizzle.svg";
import fog from "../images/fog.svg";
import clouds from "../images/cloud.svg";
import snow from "../images/snowflake.svg";
import storm from "../images/storm.svg";
import sun from "../images/sun.svg";
export default function Home() {
let cityName: string;
const [city, setCity] = useState("amman");
const [Icon, setIcon] = useState("");
const changeC = document.getElementById("C");
const changeF = document.getElementById("F");
const [triggerEffect, setTriggerEffect] = useState(0);
const textChange = (event: any) => {
cityName = event.target.value;
};
const [weatherData, setWeatherData] = useState(Object);
const time = Date();
useEffect(() => {
const fetchData = async () => {
await axios
.get(`http://localhost:5000/app/weather?address=${city}`)
.then((response) => {
setWeatherData(response.data);
setIcon(response.data.weather["0"].icon);
});
};
fetchData();
}, [triggerEffect]);
const [celsius, setCelsius] = useState(true);
const onSubmitText = (event: any) => {
event.preventDefault();
setTriggerEffect(triggerEffect + 1);
if (cityName) {
setCity(cityName);
}
};
function convertCels() {
setCelsius(true);
console.log(changeF + " " + changeC);
if (changeC && changeF) {
changeC.style.color = "#0000ff";
changeF.style.color = "000000";
}
}
function convertFeh() {
setCelsius(false);
console.log(changeF + " " + changeC);
if (changeC && changeF) {
changeC.style.color = "#000000";
changeF.style.color = "0000ff";
}
}
useEffect(() => {
switch (Icon.substr(0, 2)) {
case "01":
setIcon(sun);
break;
case "50":
setIcon(fog);
break;
case "09":
setIcon(drizzle);
break;
case "11":
setIcon(storm);
break;
case "09" || "13" || "10":
setIcon(rain);
break;
case "13":
setIcon(snow);
break;
case "02" || "03" || "04":
setIcon(clouds);
break;
default:
setIcon(sun);
}
}, [Icon]);
return weatherData["name"] && Icon ? (
<MainContainer>
<Header />
<SearchBoxDiv>
<Button type="submit" onClick={onSubmitText}></Button>
<SearchBox
id="text"
type="text"
placeholder="Location"
onChange={textChange}
></SearchBox>
</SearchBoxDiv>
<MainMidContainer>
<SecMidContainer>
<Vector src={Icon} />
</SecMidContainer>
<SecMidContainer>
<CityName>{weatherData["name"]}</CityName>
<Time>
{time.toString().substr(0, 3) + " "}
<Clock format={"HH:mm"} ticking={true} timezone={"Asia/Amman"} />
</Time>
<Condition>{weatherData.weather["0"].description}</Condition>
<Temp id="temp">
{celsius
? parseInt(weatherData.main["temp"]) - 270 + " C"
: parseInt(
parseInt(weatherData.main["temp"]) * (9 / 5) - 459 + "",
) + " F"}
{" "}
<SuperScriptC
onClick={() => {
convertCels();
}}
>
C
</SuperScriptC>
{" "}
<SuperScriptF
id="F"
onClick={() => {
convertFeh();
}}
>
F
</SuperScriptF>
</Temp>
</SecMidContainer>
</MainMidContainer>
<Footer />
</MainContainer>
) : (
<Loading />
);
}
const MainContainer = styled.div`
width: 1400px;
height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: space-between;
`;
const SearchBoxDiv = styled.form`
width: 500px;
height: 55px;
display: flex;
align-self: center;
`;
const SearchBox = styled.input`
width: 497px;
height: 54px;
background: #ffffff;
border: 1px solid #000000;
box-sizing: border-box;
padding: 15px;
`;
const MainMidContainer = styled.div`
width: 900px;
heigh: 330px;
display: flex;
flex-direction: row;
justify-content: space-between;
align-self: center;
`;
const SecMidContainer = styled.div`
width: 40%;
height: 330px;
display: flex;
flex-direction: column;
`;
const Vector = styled.img`
height: 330px;
width: 330px;
`;
const CityName = styled.p`
font-family: Roboto;
font-style: normal;
font-weight: 900;
font-size: 40px;
line-height: 47px;
`;
const Time = styled.p`
font-family: Roboto;
font-style: normal;
font-weight: normal;
font-size: 40px;
line-height: 47px;
`;
const Condition = styled.p`
font-family: Roboto;
font-style: normal;
font-weight: normal;
font-size: 30px;
line-height: 35px;
`;
const Temp = styled.p`
font-family: Roboto;
font-style: normal;
font-weight: normal;
font-size: 100px;
line-height: 117px;
margin-top: 15 %;
`;
const SuperScriptC = styled.sup`
font-family: Roboto;
font-style: normal;
font-weight: normal;
font-size: 40px;
line-height: 47px;
`;
const SuperScriptF = styled.sup`
font-family: Roboto;
font-style: normal;
font-weight: normal;
font-size: 40px;
line-height: 47px;
`;
const Button = styled.input`
background-color: transparent;
color: transparent;
border: none;
`;
Problem: useEffect Dependencies
Your useEffect has Icon as a dependency but it is also setting Icon. So every time that it changes the icon it will run again. There is potential for an infinite loop with this setup. Currently what's happening is that is always ends up on sun from the default case because whenever you set it to something else, it triggers a re-run with the new value.
Solution 1: Better Dependencies
We want to reset the Icon whenever the weatherData changes. Rather than calling setIcon(response.data.weather["0"].icon) (delete that line), we use the icon from the data to trigger the effect. The data is already stored in state as weatherData.
// you need to fix your initial value of weatherData, but I think this will work
const weatherIcon = weatherData?.weather?.[0]?.icon;
useEffect(() => {
switch (weatherIcon.substr(0, 2)) {
case "01":
setIcon(sun);
break;
/* ... */
}
}, [weatherIcon]);
Solution 2: Pure Function
We are just choosing an icon based on a string value in the API response. This does not need to be an effect. We can define a function that finds the icon for a given string and call that function ourselves at the appropriate time. This function also saves you from having to write break a bunch of times because we are calling return.
const findIcon = (weatherIcon: string) => {
switch (weatherIcon.substr(0, 2)) {
case "01":
return sun;
case "50":
return fog;
/* ... */
default:
return sun;
}
}
You would call this function in your fetch effect and delete the icon update effect.
.then((response) => {
setWeatherData(response.data);
setIcon(findIcon(response.data.weather["0"].icon));
});
There are some other issues with your code, but this should fix the icon situation.
Is it possible to display the popup of a vertical submenu "the other way around" ? I have one SubMenu item in a fixed Sidebar at the bottom of my page. It contains links for the users profile and the logout link. But since it is at the bottom of the page the submenu will open and part of it is outside the of the page.
Here is a screenshot of the current situation.
I searched for options the documentation but I couldn't find a suitable solution for the problem. So basically what I want to achieve is growing the popup from bottom to top and not top to bottom.
Here is the source for the Sidebar component. It is quite a early stage so there are still other improvements to the code to do.
import React from 'react';
import { connect } from 'react-redux';
import { Layout, Menu, Icon } from 'antd';
import { withRouter } from 'react-router-dom';
import styled from 'styled-components';
import { toggleSidebar } from '../../actions/ui';
import { logoutUser } from '../../actions/user';
const { Sider } = Layout;
const SubMenu = Menu.SubMenu;
const Logo = styled.div`
height: 32px;
background: rgba(255, 255, 255, 0.2);
margin: 16px;
`;
const UserMenu = styled(SubMenu)`
position: fixed;
text-align: center;
bottom: 0;
cursor: pointer;
height: 48px;
line-height: 48px;
color: #fff;
background: #002140;
z-index: 1;
transition: all 0.2s;
`;
const mapStateToProps = state => ({
ui: state.ui
});
const mapDispatchToProps = dispatch => {
return {
toggleSidebar: () => dispatch(toggleSidebar()),
logoutUser: () => dispatch(logoutUser())
};
};
class Sidebar extends React.Component {
componentDidMount() {}
render() {
const { ui, logoutUser } = this.props;
return (
<Sider
collapsed={ui.sidebarCollapsed}
//onCollapse={toggleSidebar} // toggle is disabled
style={{
overflow: 'auto',
height: '100vh',
position: 'fixed',
left: 0
}}
>
<Logo />
<Menu theme="dark" defaultSelectedKeys={['1']} mode="inline">
<UserMenu
key="sub1"
placement="topLeft"
title={
<span>
<Icon type="user" />
<span>User</span>
</span>
}
>
<Menu.Item onClick={logoutUser} key="3">
Logout
</Menu.Item>
<Menu.Item key="4">Profile</Menu.Item>
</UserMenu>
</Menu>
</Sider>
);
}
}
export default withRouter(
connect(
mapStateToProps,
mapDispatchToProps
)(Sidebar)
);
Is there a way to achieve this ?
Yes it should be possible. <Menu>uses the rc-menu package, and supports all the properties from this package, even if they are not documented in the and.design doc page.
Menu position is guided by the builtinPlacements property, which in turn uses https://github.com/yiminghe/dom-align, which gives you lot of flexibility in how to position the elements.