Interface of todo app breaking only in chrome browser, on refresh - css

This is a simple todo app built with react, bulma, and css grid. When I reload the page in chrome the height of the input form changes from 385x27.27 to 385x58.45, and lowers itself to the point of breaking through the bottom boundary of my css grid. I don't think this has much to do with my react code since I tested my app in firefox and edge which did not have this issue.
I also tried clearing my cache, cookies etc. for chrome which did not fix the issue. Surprisingly as soon as you type something into the input box (just typing) it fixes itself to the proper proportions. But as soon as you refresh, it breaks again. I should also note that it's broken on first render of the page (not just refreshing).
I suspect it has to do with my grid and how I wrapped the html elements (I'm still learning), or maybe event.prevent.default() and .reset() inside my react code are interfering with grid? I'm really not sure, and without error messages this has been hard to troubleshoot. I included the full code for my app below and images of the problem.
Page first loaded or refreshed (broken)
Page after typing "p" into the input (correct)
Index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.8.2/css/all.css" integrity="sha384-oS3vJWv+0UjzBfQzYUhtDYW+Pj2yciDJxpsK1OYPAYjqT085Qq/1cq5FLXAZQ7Ay" crossorigin="anonymous">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.7.5/css/bulma.min.css">
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<title>React App</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
</body>
</html>
App.js
import React, { Component } from 'react';
import './App.css';
/* InputTaskForm renders a form, and returns the input to our storeTask method. */
const InputTaskForm = ({ formValidation }) => {
return (
<form name="charlie" className="charlie" onSubmit={formValidation}>
<input name="userinput" type="text" placeholder="Task..." size="35"/>
<button className="button is-info is-small" type="submit">Submit</button>
</form>
);
}
const DisplayTasks = ({ tasksArray, removeTask, crossOutTask }) => {
return (
<div id="orderedList">
<ol>
{/* Create our list items and corresponding buttons by mapping over the tasks array. The array is currently made up of objects with a title and crossedOut key. Therefore keep in mind the task prop is carrying objects, not the task title itself */}
{tasksArray.map((task, index) => (
<li onClick={ () => crossOutTask(index) } key={index} >
{/* Check the crossedOut value of the current task object. If crossedOut is true display it crossed out, else display it uncrossed */}
{ task.crossedOut
? <strike>{task.title}</strike>
: task.title }
<button className="removeButton button is-danger is-small" onClick={ event => removeTask(event, index) } >Remove</button>
</li>
))}
</ol>
</div>
);
};
class App extends Component {
state = {
userinput: '',
tasksarray: [],
}
/* ============================================== #FUNCTIONS ==============================================
=========================================================================================================== */
formValidation = event => { // event prop passed from InputTaskForm component
event.preventDefault(); // prevent form from auto-refreshing on submit
const userInput = event.target.userinput.value // userInput stored
const userInputIsBlank = userInput.trim().length < 1 // trim (remove) prefixed and affixed spaces, then check length
userInputIsBlank
? alert(`Error: invalid submission`)
: this.storeTask(userInput);
};
storeTask = userInput => { // userInput passed from formValidation function
this.setState({
userinput: userInput,
tasksarray: [...this.state.tasksarray, { title: userInput, crossedOut: false } ] //create a copy of tasks array then add a new object into the array filled out with user input
});
document.forms["charlie"].reset();
};
removeTask = (event, index) => { // props passed from DisplayTasks component
event.stopPropagation(); // prevents bubbling to crossOutTask in the DisplayTask component
const removedTaskArray = [...this.state.tasksarray]; //removedTaskArray is just a copy of our current array for the moment
removedTaskArray.splice(index, 1); //here removedTaskArray actually becomes an array w/ the removed task (removed with splice)
this.setState({ tasksarray: removedTaskArray });
};
crossOutTask = index => { // index prop passed from DisplayTasks component
const { tasksarray } = this.state
const selected = tasksarray[index];
this.setState({
tasksarray: [ // change tasksarray state to: [prior slice, change, after slice]
...tasksarray.slice(0, index), // slice off (copies) of array elements prior to index element
Object.assign(selected, {crossedOut: !selected.crossedOut}), // invert the selected line's crossedOut value
...tasksarray.slice(index + 1) // slice off (copies) of array elements after index element
]
});
};
componentDidUpdate() {
console.log(this.state.tasksarray); // debugging :)
};
/* =============================================== #RENDER ================================================
=========================================================================================================== */
render() {
const { tasksarray } = this.state
const { formValidation, storeTask, removeTask, crossOutTask } = this
return (
<div className="grid-container">
<div className="input-tasks-container box">
<h1 className="title is-4">Todo: </h1>
<InputTaskForm
task={storeTask}
formValidation={formValidation} />
</div>
<div className="tasks-grid-container">
<h1 className="Tasks-title title is-4">Tasks</h1>
<h1 className="Tasks-title subtitle is-6">Tip: click on a task to mark it as done</h1>
<DisplayTasks
tasksArray={tasksarray}
removeTask={removeTask}
crossOutTask={crossOutTask} />
</div>
</div>
);
};
};
/* ================================================ #EXPORT ===============================================
=========================================================================================================== */
export default App;
App.css
/* Prevent highlighting when double clicking list items */
#orderedList {
-webkit-user-select: none; /* Chrome all / Safari all */
-moz-user-select: none; /* Firefox all */
-ms-user-select: none; /* IE 10+ */
user-select: none; /* Likely future */
cursor: pointer;
}
.grid-container {
display: grid;
grid-template-columns: 1fr 1fr 425px 1fr 1fr;
grid-template-rows: 200px 1fr;
}
.input-tasks-container {
margin-top: 45px;
text-align: center;
display: grid;
grid-column-start: 3;
}
.Tasks-title {
margin-top: 20px;
}
.tasks-grid-container {
display: grid;
grid-row-start: 2;
grid-column-start: 3;
grid-column-end: 5;
}
.removeButton {
margin-left: 10px
}
button {
cursor: pointer;
}
.charlie {
margin-bottom: 20px;
}
li {
margin-bottom: 10px;
}
ol {
margin-left: 40px;
}

Related

CSS flexbox styles to fill width and height of elements

I am creating a web application for my react-native application. I feel quite comfortable in styling on react-native and was trying to use flexbox on the web also hoping it would allow me to use my react-native knowledge. But the styles behave a little differently on the web and I am having a hard time finding a good resource that explains the basics. I understand the meaning of each flexbox properly but there is a gap in my understanding due to which I am not able to achieve the desired layout.
Please refer to the following code snippet. Link to code sandbox also below the snippet.
import React, { useState } from "react";
import ReactDOM from 'react-dom';
import { Box, Header, Text, Avatar, Menu, List } from "grommet";
import { MoreVertical } from "grommet-icons";
const AlarmDetailsHeader = () => {
return (
<Header className="alarmDetailsHeader" background="brand" pad="small">
<Text>"alarm1"</Text>
<Menu icon={<MoreVertical />} items={[{ label: "logout" }]} />
</Header>
);
};
const AlarmDetails = (props) => {
return (
<Box className="alarmDetails" flex={true}>
<AlarmDetailsHeader />
<Box flex={true} />
</Box>
);
};
const AlarmSummary = (item, index, state) => {
return (
<Box margin="medium">
<Text>{item.name}</Text>
</Box>
);
};
const AlarmListHeader = (props) => (
<Header className="alarmListHeader" background="brand" pad="small" border="right">
<Avatar src="//s.gravatar.com/avatar/b7fb138d53ba0f573212ccce38a7c43b?s=80" />
<Text level={5}>"Alarms</Text>
<Menu
icon={<MoreVertical />}
items={[
{ label: "First Action", onClick: () => {} },
{ label: "Second Action", onClick: () => {} }
]}
/>
</Header>
);
const AlarmList = () => {
const [alarms, setAlarms] = useState([
{
name: "alarm1"
},
{
name: "alarm2"
}
]);
return (
<Box className="alarmList" fill="vertical">
<AlarmListHeader />
{/* <List data={alarms} children={AlarmSummary} /> */}
</Box>
);
};
const App = () => {
return (
<Box className="alarmListWithDetails" direction="row" flex={true}>
<AlarmList />
<AlarmDetails />
</Box>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
rootElement
);
.App {
font-family: sans-serif;
text-align: center;
}
.routerContainer {
display: flex;
height: "100vh";
width: "100vw";
justify-content: center;
}
.alarmListWithDetails {
max-width: "1080px";
}
.alarmList {
background-color: ivory;
width: "320px";
}
.alarmDetails {
background-color: antiquewhite;
}
.alarmListHeader {
height: '80px'
}
.alarmDetailsHeader {
height: '80px'
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="theme-color" content="#000000">
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/grommet/2.16.2/grommet.min.js"></script>
<!--
manifest.json provides metadata used when your web app is added to the
homescreen on Android. See https://developers.google.com/web/fundamentals/engage-and-retain/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json">
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>React App</title>
<style type="text/css">
body {
margin: 0;
}
#root {
height: 100vh;
width: 100vw;
background-color: lightblue;
}
</style>
</head>
<body>
<noscript>
You need to enable JavaScript to run this app.
</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>
https://codesandbox.io/s/alarm-web1-yztws?file=/src/styles.css
I am trying to achieve the below layout which is similar to the Whatsapp Web layout.
This is what I am getting at the moment:
I want to assign a max-width for the main window but that doesn't seem to take effect. I want the sidebar of the left to cover the full height of the window for which I tried using different CSS properties such as height: '100%' or fill: vertical or flex: 1 but that doesn't have any effect either.
Similarly, I want the details panel to take the complete height. I want the height of both the headers to be the same for which I specified height: 80px but that doesn't seem to have any effect either. These things work quite easily on mobile but for some reason, the web doesn't work the same.
I found flexboxfroggy.com and finished all the levels but as I mentioned, I understand the flexbox properties but the web requires some more things than just the flexbox properties to get the layout right.
Your CSS is causing some issues, but essentially you need to look at how parent and children selection works. You need to do a reset at the top level to allow 100% for child elements below it. The 100% should be working but it is set at the HTML/BODY level first. Similar with Flex, it needs to be set at a parent level before the children can display in the manner you set. Then you can use something like flex-direction: column; which will then create a whole column and then any div inside that you set to flex: 1 and you'll get a full height. You can read more here.
I wouldn't recommend using the VH or VW as they only fill the window in its current state. If that window changes then the selection will remain the previous dimension only.
An example from your CSS above is below, I've removed code that isn't working and added some.
html, body {
height: 100%;
}
body {
display: flex;
}
.App {
font-family: sans-serif;
text-align: center;
}
.routerContainer {
justify-content: center;
}
.alarmListWithDetails {
max-width: 1080px;
}
.alarmList {
background-color: ivory;
width: 320px;
}
.alarmDetails {
background-color: antiquewhite;
}
.alarmListHeader {
height: 80px;
}
.alarmDetailsHeader {
height: 80px;
}

React jsx style tag not applied to tags returned by functions

I'm trying to apply styling to tags that are generated from a for loop inside a function. The problem is that styling within a tag doesn't apply to these generated tags. Possibly because they're generated after the styling is applied? I'm not sure. Here's an example:
generateTags = (size) => {
let tags = []
for (var i = 0; i < size; i++) {
tags.push(<img className="image-tag" src={this.state.imagePath} alt="image" key={Math.random() * Math.floor(100000)}/>)
}
return tags
}
render() {
return (
<div className="main">
<div className="left-container">
{this.generateTags(10)}
</div>
<style jsx> {`
.main { <-- This is properly applied
position: relative;
width: 100%;
height: 100%;
}
.image-tag { <-- This doesn't work
position: absolute;
width: 50px;
}
`} </style>
</div>
)
}
The width: 50px is not applied to the image, and nothing I place makes any difference. But when I add styling within the tag like this:
<img className="image-tag" style={{width: "50px"}} src={this.state.imagePath} alt="image" key={Math.random() * Math.floor(100000)}/>
Then the style is applied correctly. Does this mean I can't have css within the style tag if the elements are return from a function?
It looks like you are using Styled JSX. One of the principles of Styled JSX is that the CSS is component specific. Since your <img> tags are being created outside of the render() function where your styles are defined, they are not being applied.
In this instance, I would recommend to instead have a GenerateTags React component, instead of a function. That way, you can generate your tags as needed, as well as apply component specific styling, like so:
GenerateTags = (props) => {
const {size} = props
let tags = []
for (var i = 0; i < size; i++) {
tags.push(i)
}
return(
<>
{tags.map((tag, index) => (
<img className="image-tag" src={this.state.imagePath} alt="image" key={Math.random() * Math.floor(100000)}/>
))}
<style jsx>{`
// This will now work as it is in the same scope as the component
.image-tag {
position: absolute;
width: 50px;
}
`}</style>
</>
)
return tags
}
render() {
return (
<div className="main">
<div className="left-container">
<GenerateTags size={10} />
</div>
<style jsx> {`
.main { <-- This is properly applied
position: relative;
width: 100%;
height: 100%;
}
`} </style>
</div>
)
}
Otherwise, if you wanted these styles to be applied outside of the scope of the component, you could use the global option:
<style jsx global>
...
</style>

how to get a slot content in the css content property of a pseudo ;:before element (or :;after) in a web component?

Well, I think that the question is in the title :
in a web component, with shadowRoot, I want to use a slot text-content inside the content property of a pseudo ::before or ::after element.
This could make me gain much lines.
Would you have an idea, a proposal, a solution ?
You can achieve this with the use of CSS custom properties as the source for the content of ::before and ::after and the slotchange event:
<!DOCTYPE html>
<html lang="en">
<head>
<title>WC</title>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<script>
class XTest extends HTMLElement {
constructor() {
super();
// add shadow dom and insert template content
const shadowRoot = this.attachShadow({ mode: "open" });
const template = document.getElementById("x-test-template");
const templateContent = template.content;
shadowRoot.appendChild(templateContent.cloneNode(true));
}
connectedCallback() {
// get all the named slots
const slots = this.shadowRoot.querySelectorAll("slot[name]");
[...slots].forEach(slot => {
// add a listener to run when the slot changes
slot.addEventListener("slotchange", event => {
// get the slot name
const name = event.target.name;
// get the slot content
const text = event.target.assignedNodes()[0].textContent;
// update the custom property
this.style.setProperty(`--content-${name}`, `'${text}'`);
});
});
}
}
customElements.define("x-test", XTest);
</script>
</head>
<body>
<template id="x-test-template">
<style>
:host {
display: inline-block;
position: relative;
font-size: 2rem;
padding: 2rem;
}
:host::before,
:host::after {
content: "";
position: absolute;
top: 0;
font-size: 1rem;
}
:host::before {
/* set the pseudo selector content to the custom property */
content: var(--content-before, "");
left: 0;
}
:host::after {
/* set the pseudo selector content to the custom property */
content: var(--content-after, "");
right: 0;
}
slot[name] {
display: none;
}
</style>
<!-- slot for ::before -->
<slot name="before"></slot>
<!-- default slot -->
<slot></slot>
<!-- slot for ::after -->
<slot name="after"></slot>
</template>
<x-test>
<span slot="before">B</span>
<span>Hello</span>
<span slot="after">A</span>
</x-test>
</body>
</html>
#lamplightdev
Thank you for your answer.
I was looking for something wich doesn't exist yet.
So I have chose a solution with css var and a setter to set it :
class extends HTMLElement {
set content(val) {
this.setAttribute('data-content',val);
}
constructor() {
...
and, of course :
:host::before {
content: attr(data-content);
...
This seems to be the lighter solution I may imagine.
I'd like to suggest to web's standards developpers to create a new css function : slot(name) witch, with attr(...), var(...) and calc(...), could help the use of pseudo elements inside a web component.
Could someone show me the way to present this proposal ???
I do apologize for my poor english language (I'm french, nobdy's perfect).

React Modals visible for a split-second on page load

I am rendering modals in React.
My index.html looks like this:
<div id="root"></div>
<div id="modal"></div>
And all my modals are rendered (through a portal) as a child of .modal.
Each modal element has the following form:
<div class="modal-background open">
<!-- children -->
</div>
Where the class can be modal-background open or modal-background closed. The entire component is:
interface OwnProps {
children: React.ReactNode
isOpen: boolean
onExit: () => void
}
export class Modal extends React.Component<OwnProps, any> {
_exit = () => this.props.onExit();
_renderModal = () => (
<div className={`modal-background ${this.props.isOpen ? "open" : "closed"}`} onClick={this._exit}>
{this.props.children}
</div>
);
render() {
if (this.props.isOpen) {
document.body.className += " no-scroll";
} else {
document.body.classList.remove("no-scroll");
}
let elem = document.querySelector("#modal");
if (elem == null) {
console.log("Could not render modal.");
return null;
}
return ReactDOM.createPortal(this._renderModal(), elem);
}
}
And the CSS looks like:
.modal-background {
/* Other styling - this a dark backdrop for a modal child */
background-color: rgba(0,0,0,0.2);
transition: opacity 150ms ease-out;
&.closed {
opacity: 0;
pointer-events: none;
}
&.open {
pointer-events: all;
opacity: 1;
&:hover {
cursor: pointer;
}
}
}
So my modal is used like <Modal><CustomModalElement/></Modal>.
When I load the page, my modal elements briefly flash, indicating that they are not hidden on load (but a split-second afterwards).
I can fix this by adding display: none and display: inherit into the css, but then I miss the nice transitions.
Is there a better way to do this?
Not sure you need to do anything else inside your index.html file except
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<div id="modal"></div>
And for your Modal.js, you could try something along these lines:
import React from "react";
import ReactDOM from "react-dom";
const Modal = props => {
return ReactDOM.createPortal(
<div className="ui dimmer modals visible active">
<div className="ui standard modal visible active">
<div className="header">Delete Object</div>
<div className="content">
Are you sure you want to delete this?
</div>
<div className="actions">
<button className="ui primary button">Delete</button>
<button className="ui button">Cancel</button>
</div>
</div>
</div>,
document.querySelector("#modal")
);
};
export default Modal;
and then inside your other component where the user will execute the modal:
import React from "react";
import Modal from "../Modal"; // or wherever your Modal is in the file tree
const ObjectDelete = () => {
return (
<div>
ObjectDelete
<Modal />
</div>
);
};
export default ObjectDelete;
Keep in mind that the example of modal I offer here is not a reusable component.

CSS Help - Remove blank space between Images (<img>)

Right now I am trying to make images stack side by side, vertically and horizontally, so that there is no white spaces between them. This is what my view currently looks like.
Ideally, there would be none of that blank, white space. I have inserted the ReactJS code below with my GifViewer component that holds the . Any ideas on how I can get this to work? And the code will not run due to the axios request, so it's just for visual effect right now.
img {
display: inline !important;
width: 20%;
vertical-align: top;
height: auto;
top: 0;
left: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
class GifViewer extends Component {
render() {
return (
<img
key={this.props.keyyer}
src={`https://i.giphy.com/${this.props.id}.gif`}
/>
);
}
}
class App extends Component {
constructor(props) {
super(props);
this.state = {
searchTerm: "",
counter: 0,
pastSearches: [],
data: null
};
this.handleChange = this.handleChange.bind(this);
}
componentDidUpdate() {
console.log("this.state.data", this.state.data);
}
componentDidMount() {
console.log("Mounted");
}
handleChange(event) {
this.setState({ searchTerm: event.target.value });
axios
.get(
`${BASE_URL}${config.apiKey}${QUERY}${this.state.searchTerm}${LIMITS}`
)
.then(result => {
this.setState({ data: result.data.data });
});
}
render() {
if (this.state.data) {
var GIFS = this.state.data.map(d => (
<GifViewer keyyer={d.embed_url} id={d.id} />
));
}
return (
<div className="container-fluid">
<div className="row py-5 bg-success">
<div className="col-8 offset-2">
<h3 className="text-center">LET'S BREAK THE INTER-WEBZ</h3>
<input
type="text"
className="form-control"
value={this.state.searchTerm}
onChange={this.handleChange}
placeholder="Search"
/>
</div>
</div>
{this.state.data ? GIFS : "Nothing yet"}
</div>
);
}
}
export default App;
My personal favorite way to resolve this is the css property object-fit: cover; (applied to your img rule).
There's a great article on the property here, but the gist (for your purpose) is that you can zoom the image to cover the whole space without stretching or distorting the image.

Resources