Styled Components Dropdown props - css

I have a dropdown component with a alert that renders when any other option than 'select' is selected, which works.
I also want to change the background color and color of the dropdown dependant on what option has been selected, so I pass in the value state as a prop.
I can't seem to get the styled component to work. It renders the correct styling at intial render.
Styled component:
export const Wrapper = styled.div<WrapperProps>
#additiondropdown-id {
background: ${(props) =>
props.alert ? 'rgba(19, 135, 172, 0.05)' : '#ffffff'};
color: ${(props) => (props.alert ? `${theme.colors.neutral}` : 'green')};
};
const [alert, setAlert] = useState(true);
const onDropDownChange = (event) => {
const { name, value } = event.target;
onAddressChange({ [name]: value });
value !== 'Select' ? setAlert(false) : setAlert(true);
};
<DropDownWrapper alert>
<DropDown
...
stack overflow, docs

Related

Change css-module dynamically on React

I have a canvas like this in Component
It can change the pointer to crosshair when cursor is entered on Canvas.
import styles from '../css/basic-styles.module.css';
const ImagePreview = () =>{
[mode,setMode] = useState(0);
changeMode(mode){
setMode(mode);
}
return (){
<canvas className={styles.canvas} width=200 height=200></canvas>
}
}
in css
canvas:hover{
/*cursor:pointer;*/
cursor:crosshair;
}
Now I want to change the css dynamically depending on the mode value.
I want to use pointer when mode is 1
I should quite use css? or is there any method to make it work?
Please see the solution below. It is also available in the sandbox.. Ignore the vanilla react solution it included for the snippet runner. Click Run Code Snippet to see preview here.
/* Solution
const ImagePreview = () => {
const [mode, setMode] = useState(0);
return (
<canvas
onMouseEnter={() => setMode(1)}
onMouseLeave={() => setMode(0)}
style={{
backgroundColor: "teal",
cursor: mode ? "crosshair" : "pointer"
}}
width={200}
height={200}
/>
);
};
*/
// This is so it can work in Stackoverflow snippet preview. //
const ImagePreview = () => {
const [mode, setMode] = React.useState(0);
const canvasCfg = {
onMouseEnter: () => setMode(1),
onMouseLeave: () => setMode(0),
style: { backgroundColor: "teal", cursor: mode ? "crosshair" : "pointer"},
width: 200,
height: 200
}
return React.createElement('canvas', canvasCfg)
}
const domContainer = document.querySelector('#app');
const root = ReactDOM.createRoot(domContainer);
root.render(React.createElement(ImagePreview));
<script crossorigin src="https://unpkg.com/react#18/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#18/umd/react-dom.production.min.js"></script>
<div id="app"></div>
create two classes like this
canvas1:hover{
cursor:pointer;
}
canvas2:hover{
cursor:crosshair;
}
and use it conditionally based on mode like
import styles from '../css/basic-styles.module.css';
const ImagePreview = () =>{
[mode,setMode] = useState(0);
changeMode(mode){
setMode(mode);
}
return (){
<canvas className={mode == 1 ? styles.canvas1 : styles.canvas2} width=200 height=200></canvas>
}
}
you can also use classes without :hover , this will also give the desired result because of cursor property
.canvas1{
cursor: pointer;
}
.canvas2{
cursor: crosshair;
}

Use props inside emotion's css string template

I'm in the middle of migrating my design system from styled-components to emotion.
In SC, The following syntax is considered valid:
export interface AvatarProps {
// ...
shape: AvatarShape;
size: AvatarSize;
}
const borderRadiusByShape = css<Pick<AvatarProps, "shape" | "size">>`
border-radius: ${(props) => {
return match(props.shape)
.with("circle", () => `${props.size}px`)
.with("square", () => "0px")
...
.exhaustive();
}}
`;
const StyledAvatar = styled.div<AvatarProps>`
/* ... */
${borderRadiusByShape};
`;
This pattern allows me to reuse my borderRadius across multiple styled.[x] declarations.
Looking at emotion's css function, it looks like emotion doesn't support that syntax. Is there any workaround that won't require me to wrap this functionality with a new component?
Afer a bit of digging, it turns out I can extract the props from css and create a function that accept these props and return the corresponding CSS:
export interface AvatarProps {
// ...
shape: AvatarShape;
size: AvatarSize;
}
const borderRadiusByShape = (props: Pick<AvatarProps, "shape" | "size">) => css`
border-radius: ${match(props.shape)
.with("circle", () => `${props.size}px`)
.with("square", () => "0px")
/* ... */
.exhaustive()};
`;
const StyledAvatar = styled.div<AvatarProps>`
/* ... */
${borderRadiusByShape};
`;

Write JS inside material ui styles

I wanted to put some conditional logic inside mui styles
Currently using inline style, which works
<div className={classes.form} style={{alignItems: ((Code.length>10 || Description.length>100) ? 'start' : 'center')}}>
...
</div>
But want to do the same thing inside mui styles, which gives error
// Code and Description state
...
const useStyles = makeStyles(theme => ({
form: {
alignItems: {(Code.length>10 || Description.length>100) ? 'start' : 'center'}, //Code not defined
}
You can pass a props the the useStyles hook and use it like this:
const classes = useStyles(props);
const useStyles = makeStyles(theme => ({
form: {
alignItems: props => props.Code.length>10 || props.Description.length>100 ? 'start' : 'center',
}
You never mentioned how Code and Description are defined, but you can pass props to makeStyles.
const useStyles = makeStyles(theme => ({
form: {
alignItems: props => (props.Code.length > 10 || props.Description.length > 100) ? 'start' : 'center'
}
}));
// Usage
const classes = useStyles(props);

Rerender Tooltip when Scrolling React

Thanks for your help in advance, I have an issue with a tooltip, it is supposed that I should show the tooltip when a condition is given, but due to the scroll when rerendering the list the validation fails.
Here is working right, the complete list shows the tooltips where it is supposed to be. enter image description here
But then, when I scroll down the view is re-render and the tooltip fails. enter image description here
The idea is that the tooltip (and the underline) should be shown when I have group names too long using this boolean disableHoverListener={textDoesOverflow}, and it is working at the beginning but then ... fails.
Here's the code and the styles.
Please help!!
export const BaseFilteredUsersGroups: React.FC<IFilteredUsersGroups> = (props) => {
const {
userId,
filteredGroupIds = [],
localize,
} = props;
const sizeGroupsRef = React.useRef(null);
const sizeTitleRef = React.useRef(null);
const styles = useStyles();
const usersGroups = useSelector((state: IDuxStore) => {
const groups = filteredGroupIds.map(groupId => select.group.getGroupByGroupId(state, groupId));
return groups.filter(group => group?.memberships?.some(user => user.userId === userId));
});
const labelTitle = localize.formatItems(usersGroups.map(group => group.title));
const textDoesOverflow = sizeGroupsRef?.current?.getBoundingClientRect()?.width >= sizeTitleRef?.current?.getBoundingClientRect()?.width;
const finalStyle = textDoesOverflow ? styles.groupTitle : styles.groupTitleOverflow;
return (<div className={styles.usersGroups} ref={sizeGroupsRef}>
{<Tooltip title={labelTitle} disableHoverListener={textDoesOverflow} placement="top" onScrollCapture={}>
{<span className={finalStyle} ref={sizeTitleRef}>
{labelTitle}
</span>}
</Tooltip>}
</div >);
};
Here the styles:
export const useStyles = makeStyles(theme => {
return createStyles({
usersGroups:{
textOverflow: 'ellipsis',
overflow: 'hidden',
},
groupTitle: {
whiteSpace: 'nowrap',
fontWeight: theme.typography.fontWeightMedium,
color: theme.palette.text.secondary,
},
groupTitleOverflow: {
whiteSpace: 'nowrap',
fontWeight: theme.typography.fontWeightMedium,
color: theme.palette.text.secondary,
textDecorationLine: 'underline',
}
});
});
const textDoesOverflow =
sizeGroupsRef?.current?.getBoundingClientRect()?.width
>= sizeTitleRef?.current?.getBoundingClientRect()?.width;
const finalStyle = textDoesOverflow ? styles.groupTitle : styles.groupTitleOverflow;
The conditional logic here is reversed. Right now if the text width is greater than the sizeTitleRef width it will return groupTitle not groupTitleOverflow. So instead you may want to switch up the ternary operator to this:
const finalStyle = textDoesOverflow ? styles.groupTitleOverflow : styles.groupTitle;

Change style of functional component after click

The AnswerScreen component is loading a set of MyButton components. I want to change the style (basically the backgroundColor of the MyButton that was click. The code doesn't work as intended. I see that on a class component I would be able to do it with this.className but in a functional component I cannot use this (although I have the key). Any tips?
import React from 'react';
import classes from './AnswerScreen.module.css';
import MyButton from '../../../../utils/MyButton';
const AnswerScreen = (props) => {
const buttonClickHandler = (index, value) => {
if (props.myValue !== value) {
classes.AnswerScreenButton = {
fontSize: '3rem',
cursor: 'pointer',
backgroundColor: 'red'
}
}
}
return (
<div className={classes.AnswerScreen}>
{props.buttons.map((value, index) => {
return <MyButton
className={classes.AnswerScreenButton}
clickHandler={() => buttonClickHandler(index, value)}
key={index}
num = {value} />
})}
</div>
);
}
export default AnswerScreen;
I assume you want to keep track of which button was pressed in the end? I suggest you add a state variable to keep track of the selected button. You can use that state variable to set the correct classname of the button. Add the class AnswerScreenButtonSelected that contains css for a selected button. I removed your usage of index, they should be avoided when setting keys in mapped arrays in React.
import React from 'react';
import classes from './AnswerScreen.module.css';
import MyButton from '../../../../utils/MyButton';
const AnswerScreen = (props) => {
const [selectedButton, setSelectedButton] = useState(null);
return (
<div className={classes.AnswerScreen}>
{props.buttons.map( value =>
<MyButton
key={value}
className={selectedButton === value ? classes.AnswerScreenButtonSelected : classes.AnswerScreenButton}
clickHandler={() => setSelectedButton(value)}
num = {value} />
)}
</div>
);
}
export default AnswerScreen;

Resources