I have a react component like so
export const SearchBar = ({
searchInput,
updateSearchKeyword,
}: SearchBarProps) => {
const dummy = "";
return (
<div className={`${styles.container} `}>
<input
className={`${styles["search-bar"]} `}
type={"text"}
value={dummy}
title="search"
placeholder="Search..."
onChange={(e) => updateSearchKeyword(e.target.value)}
/>
</div>
);
};
And I want to make come CSS changes when the Input filed has text in it. So, my CSS is like below
.search-bar:valid {
color: red;
}
However, the color is always red! In other words, in put is always 'valid'
I tried
.search-bar[type="text"]:valid {
color: red;
}
and even adding a pattern in the component. It is not working how do I fix this?
PS: I am using modules.css, hence the class names are a bit strange but they are working as expected for other properties.
you only declare
className={`${styles["search-bar"]} `}
you never update this, so yes, are gona be always red.
then, another mistake when you put:
const dummy = "";
value={dummy}
You will be not able to change the value of this input. You have to use like this:
const [dummy, setDummy] = useState('')
and then on the input:
value={dummy}
onChange={(e)=>setDummy(e.target.value)}
but this not solves you problem, but you can:
onChange={(e)=>handleChange(e.target.value)}
and just declare this:
const handleChange = (value) => {
setDummy(value)
if (dummy !== '') {
// here change the style of ur input (the color)
}
}
###################
to change the style u can:
document.getelemntbyId... bla bla bla (booooring)
or BETTER: declare the style on a state like this:
const [myStyle, setMyStyle] = useState(`${styles["search-bar"]}`)
and then when u want to change ur style just actualize using
setMyStyle(`${styles["search-bar-blue"]}`)
Then for the validation you need to declare a css pattern to check if true or false:
<input pattern="[a-z]+" type.../>
On the example is gona validate true ONLY if input are ONLY lowercase letters
Related
I'm implementing a rich text editor to my React web app using Draft.js.
The only options I've kept in the toolbar are inline-styling (bold, italic, underline) and numbered/bulleted lists.
Before implementing the text editor, the texts in my database were simple strings that I could retrieve, display in a styled component and apply CSS to, so the first letter in the text looked like this:
This was my React styled component :
export const Text = styled.p`
&:first-letter {
float: left;
font-family: 'Centaur';
font-size: 3.5rem;
line-height: 0.65;
margin: 0.1em 0.1em 0.1em 0;
}
`
Now that I'm using Draft's text editor, my text is being displayed in a div that has a dangerouslySetInnerHTML property applied to it, so I can't apply CSS to it to edit the style of the first letter.
How can I get my head around this? I know that I can retrieve the editor's plain text with: editorState.getCurrentContent().getPlainText('\u0001')
I was thinking about storing the first letter of the text in the database to wrap it with a CSS class when displaying it, but then I would need to remove that letter from the rest of the text content saved so that the first letter doesn't appear twice, and I don't know how to alter Draft.js text content while retaining the styling.
This is my Draft.js Editor component with a Preview component underneath that displays the formated text:
import { EditorState } from 'draft-js';
import { Editor } from 'react-draft-wysiwyg';
import { convertToHTML } from 'draft-convert';
export default function MyComponent() {
const [editorState, setEditorState] = useState( () => EditorState.createEmpty() );
const [convertedContent, setConvertedContent] = useState('');
const handleChange = () => {
let currentContentAsHTML = convertToHTML(editorState.getCurrentContent());
setConvertedContent(currentContentAsHTML);
}
const createMarkup = (html) => {
return {
__html: html
}
}
return (
<>
<Editor
editorState={editorState}
onEditorStateChange={setEditorState}
toolbar={{options: ['inline', 'list', 'link', 'emoji', 'remove', 'history']}}
onContentStateChange={handleChange}
/>
<Preview dangerouslySetInnerHTML={createMarkup(convertedContent)}></Preview>
</>
)
}
Do you have any suggestion?
Thank you!
I have the following which works fine where I am using in line style to decide the percentage values based on a boolean value.
Is there a way I could prevent using inline styling and instead pass this styling to my scss file and still be able to perform this logic more elegantly?
Working copy with inline styling.
const MyPage = ({isSmall}) => {
return (
<div>
<div style={{flex: `0 0 ${isSmall ? '50%' : '33%'}`}}>
hello
</div>
</div>
);
};
export default MyPage;
Looking to be able to move my css to my scss file and use that as a className instead but still be able to toggle the percentage value based on the boolean isSmall prop value.
Move it to scss file.
.flexer {
flex: '0 0 33%' // some way to make the 33% be dynamic and not hard code like this to be able to switch its value between 50% and 33%
}
Then use this flexer class instead of inline css.
Is this possible? Or any better elegant way to achieve this instead of inline?
You can set boolean state and change class name depends on this state inside the className then you can modifiy it in css
const MyPage = ({isSmall}) => {
const [isSmall,setIsSmall] = useState(false);
return (
<div>
<div className={isBigClass `${isSmall} ? "isSmallClass" : ""}`} >
hello
</div>
</div>
);
};
export default MyPage;
I have a component in React-redux, which has a PagedGrid component (basically a table which renders data row-wise).
<UsersContainer>
<Title>{t('users')}</Title>
<PagedGrid
data-auto-container="user:table"
pageData={user.data}
columns={this.column}
/>
</UsersContainer>
I have created a function for the custom styled component which applies css to the rows of the table inside PagedGrid
const UsersContainer = styled.div`
> table > tbody {
${props => customcss(props)};
}
`;
function customcss({ data = [] }) {
let styles = '';
if (data.length > 0) {
data.forEach((value, index) => {
if (value.mycondition) {
const rowStyle = `& > tr:nth-child(${index + 1}) {
background-color: ${LIGHT_BLUE}
}`;
}
});
}
return css` ${rowStyle} `;
}
Now I want to create a test case using jest to spy on the css of this table and check if the styles are getting applied or not. Can anyone help me on creating a test case for this.
Assuming that you use the #testing-library/react library, you could test your component's style by getting it directly from the html document, and see precisely what style is used for your specific element.
In your example, you can do something like below (assuming that the ${LIGHT_BLUE} value is blue):
import React from 'react';
import { render } from '#testing-library/react';
import UsersContainer from '../UsersContainer';
it('should have the background color set to blue', () => {
const { container, getAllByTestId } = render(
<UsersContainer />
);
// Replace <YOUR_CSS_SELECTOR> by your component's css selector (that can be found in the inspector panel)
let contentDiv = document.querySelector('<YOUR_CSS_SELECTOR>');
let style = window.getComputedStyle(contentDiv[0]);
expect(style.color).toBe('blue');
}
Here, to get the style of your element, I am using the element's CSS Selector. However, it could also work with the element's className, or id directly if it has one, respectively using the methods document.getElementByClassName('YOUR_DIV_CLASS_NAME'), document.getElementId('YOUR_DIV_ID') instead of document.querySelector('<YOUR_CSS_SELECTOR>'). Note that the given name here should be unique, either with the id technique, or the className.
I've found a very simple and efficient tutorial to handle form validation and errors handling with just React and CSS : https://medium.com/#everdimension/how-to-handle-forms-with-just-react-ac066c48bd4f
This solution is working like a charm. But I wanted to reproduce the exact same behavior using the css-in-js library styled-components.
The solution to handle the errors in the tutorial was to switch the class of the FORM and target the pseudo class :invalid of the input child to apply a red border around the inputs. And it works perfectly using just CSS classes. I tried to use the same system with styled-components, but it went wrong.
I did 2 fiddle to show you the difference in behavior
Only CSS => fiddle with css
js
const Form = () => {
const [error, setError] = React.useState(false)
const handleSubmit = React.useCallback((event) => {
event.preventDefault()
if (!event.target.checkValidity()) {
setError(true);
return;
}
setError(false)
console.log('Do something')
}, [error, setError])
return(
<div>
<h1>With pure CSS</h1>
<h2>Good behavior : </h2>
<h2>1 . Press submit => the input goes red </h2>
<h2>2 . Fill one of the input => cleans the red border individually</h2>
<form
onSubmit={handleSubmit}
noValidate
className={error ? "displayErrors" : ""}
>
<input className='input' required></input>
<input className='input' required></input>
<input type="submit" value="Submit!"></input>
</form>
</div>
)
}
ReactDOM.render(<Form />, document.querySelector("#app"))
css
.displayErrors input:invalid {
border-color: red;
}
Using styled-components => Fiddle with styled-components
const StyledInput = styled.input`
margin-right: 5px;
display: table-cell;
margin-bottom: 10px;
outline: none;
`
const StyledForm = styled.form`
&:invalid ${StyledInput} {
border-color: ${props => {
console.log(props.error)
return props.error ? "red" : "none"
}};
}
`;
const Form = () => {
const [error, setError] = React.useState(false)
const handleSubmit = React.useCallback((event) => {
event.preventDefault()
if (!event.target.checkValidity()) {
setError(true);
return;
}
setError(false)
console.log('Do something')
}, [error, setError])
return(
<div>
<h1>With Styled components</h1>
<h2>To reproduce the issue : </h2>
<h2>1 - press submit, the inputs goes red </h2>
<h2>2 - fill one input => ISSUE => the input (which is no more invalid) stays red </h2>
<StyledForm
onSubmit={handleSubmit}
noValidate
error={error}
>
<StyledInput className='input' required></StyledInput>
<StyledInput className='input' required></StyledInput>
<input type="submit" value="Submit!"></input>
</StyledForm>
</div>
)
}
ReactDOM.render(<Form />, document.querySelector("#app"))
The expected behavior on the first fiddle (CSS):
Press submit, the inputs goes red
Fill one input => The red border is cleaned individually (which means one input status doesn't depend on the other)
To reproduce the issue on the second fiddle (Styled-components):
Press submit, the inputs goes red
Fill one input => ISSUE => the input (which is no more invalid) stays red as long as the second input is still invalid (which means the two input are related).
My problem is that in the styled-components version the red borders of the inputs are cleaned when the both of the inputs are valid. But I want the inputs to act individually like in the CSS version.
I know there is 100 of solutions to solve the problem but I really liked the easy and clean solution of the tutorial. I expected to have the same behavior on both sides, could be a bug? Is there something i'm missing? Please help <3
Learning React and trying to cheat off this codepen. I do not understand 2 things.
What is the ... before largebox, flex, and other css classes?
return <div style={{...largebox, ...flex}} key={props.id}
What does the $ do in the css url param? Is it jQuery?
`url(${props.photo})
const FormCard = (props) => (
<div>
{
DATA.map((props) => {
return <div style={{...largebox, ...flex}} key={props.id}>
<div style={{...Photo,backgroundImage: `url(${props.photo})`}}></div>
<div>
<Author author={props.author}/>
<Something bio={props.bio}/>
<AdBox adpic={props.adpic} />
<IconBox />
</div>
</div>
})
}
</div>
)
The three dots '...' are called spread operator, see here https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax
The $ sign is no Jquery but is actually referencing template literals: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals
Hopefully, the docs are enough as I currently can't find [the link] to [that tutorial] I remember...
Spread operator:
const styleA = { color: 'red' };
const styleB = { background: 'blue' };
const styleC = { ...styleA, ...styleB };
// styleC = {color: "red", background: "blue"}
String Template:
const user = 'Bob';
const greetings = `Hello ${user}`;
// greetings = 'Hello Bob'
for your first question we call it Spread Operator in a simple description for below line :
style={{...largebox, ...flex}}
this is mean copy all property of largebox and flex object into a new object and assing it to style.or this line means :
style={{...Photo,backgroundImage:"myurl}"}
create a new object for me with all property of Photo object and also add a property with name backgroundImage to it. so if Photo is equal to {name:'1.jpg'} the new object is equal to
{name:'1.jpg',backgroundImage:"myUrl"}
Now for your second question, this is template literal and allow you to write variables or call functions inside a string. think we have not this so we must write it like :
backgroundImage: "url(" + props.photo +")"
so as you see it is something like concating props.photo and other strings.but with template literals we can wrap string with backticks and then write variable or function of javascript between ${} like below
backgroundImage: `url(${props.photo})`
then ${props.photo} replace with its value.