I have a React component, a button, and I need to set the background-color of a child element to the color of the button. I know that you're not supposed to call this.refs.myElement.getDOMNode() in the render() function, so I'm not sure how I'm supposed to lay this out.
At the moment, my code looks like this:
import React from 'react';
import { Button, Glyphicon } from 'react-bootstrap';
import classnames from 'classnames';
export default class GlyphButton extends Button {
constructor(props) {
super(props);
}
render() {
let {
glyph,
className,
children,
...props
} = this.props;
return (
<Button ref='btn' {...props} className={classnames([className, 'glyph-button'])}>
<Glyphicon glyph={glyph} />
{children}
</Button>
);
}
}
I need to do something like this:
let color = this.refs.btn.style.color;
return (
<Button ref='btn' ...>
<Glyphicon glyph={glyph} style={{backgroundColor: color}} />
{children}
</Button>
);
Unfortunately, this.refs hasn't been populated yet.
In case you're curious, the reason I'm doing this is because I'm using Glyphicon's free version PNGs for some icons, which are all black on a transparent background, and I'm using:
glyphicon.glyphicon-playing-dice:before {
content: "";
width: 20px;
height: 20px;
-webkit-mask-size: 100%;
-webkit-mask-image: url(/img/glyphicons/glyphicons-playing-dice.png);
display: block;
}
to make it act like a font icon. This class will make the element's background-color the color of the displayed icon.
You can set color as a state and change it in componentDidMount stage.
getInitialState: function(){
return {bgColor: ''}
},
componentDidMount: function(){
var color = React.findDOMNode(this.refs.btn).style.color;
this.setState({bgColor : color});
}
Because React recommend us that :
your first inclination is usually going to be to try to use refs to "make things happen" in your app. If this is the case, take a moment and think more critically about where state should be owned in the component hierarchy.
Related
I need some help with this problem I'm having with Material-UI styled components
I'm trying to create a BottomNavigation bar on react using Material-UI v5. I want that the icon of the selected option in the bar shows a specific color, let's say red (#f00) and the not-selected icons show green (#00f), for this I'm using the styled function to generate a custom-themed component for BottomNavigationAction, following the guidelines on the documentation: styled(). The problem: For the selected button, the icon is not grabbing the correct color and is showing the default one. At this point I'm not sure if I'm using the styled function wrong or is something super obvious I'm not seeing. Thanks in advance for any advice!
PS: I don't have enough reputation to post the image directly, sorry for that
The BottomNavigation is defined as follows:
const BottomNav = () => {
return(
<BottomNavigation
showLabels
value={value}
onChange={(event, newValue) => {setValue(newValue)}}
>
<TabBarButton
id='Home'
label='Home'
icon= {home_icon? <AiFillHome size = "30" />: <AiOutlineHome size='30'/> }
onClick={
(value)=>{
iconHandler(value.currentTarget.id)
}
}
/>
<TabBarButton
id='Documentos'
label='Documentos'
icon= {documentos_icon? <RiEditBoxFill size='30'/>: <RiEditBoxLine size='30'/>}
onClick={
(value) =>
iconHandler(value.currentTarget.id)
}
}
/>
</BottomNavigation>
);
}
To define TabBarButton I firstly tried defining the component like this:
import {BottomNavigation, BottomNavigationAction} from "#mui/material";
import { styled} from '#mui/system';
// Styled BottomNavigationAction
const TabBarButton = styled(BottomNavigationAction)({
root: {
color: '#f00',
},
selected: {
color: '#0f0',
}
});
But the rule names: root and selected didn't work, resulting in the default colors being applied:
first-try-bottom-navigation-image
So I changed them to the Global Class instead : BottomNavigationAction CSS :
// Styled BottomNavigationAction
const TabBarButton = styled(BottomNavigationAction)({
color: '#0f0',
'.Mui-selected':{
color: '#f00',
}
});
Which worked with the not-selected icon and the labels:
second-try-bottom-navigation-image
But the selected icon 'Home' is still using the default colors, I tried using a variation of the answer provided on this post Bottom Navigation Material UI Override
// Styled BottomNavigationAction
const TabBarButton = styled(BottomNavigationAction)({
color: '#0f0',
'.Mui-selected, svg':{
color: '#f00',
}
});
But this affects both icons resulting in :
third-try-bottom-navigation-image
I think TabBarButton need to add '&.Mui-selected' selector to have the styles attached to itself correctly, otherwise with '.Mui-selected' the rules only apply to nested elements:
Tested the example on: stackblitz
// Styled BottomNavigationAction
const TabBarButton = styled(BottomNavigationAction)({
color: 'royalblue',
'&.Mui-selected': {
color: 'crimson',
},
});
Two buttons are rendered in my button component. Depending on the label, the button receives the class addButton or clearButton.
Button Component:
import './Button.css'
interface ButtonProps {
lable: string,
disabled: boolean,
onClick: MouseEventHandler<HTMLButtonElement>
}
export const Button: FunctionComponent<ButtonProps> = ({ lable, disabled, onClick}): ReactElement => {
let style: string = lable == 'ADD' ? 'addButton' : 'clearButton';
console.log(lable);
console.log(style);
return(
<div>
<button className= {`button ${style}`} type='button' disabled= { disabled } onClick= { onClick }>
{lable}
</button>
</div>
);
}
In the stylesheet Button.css are the two classes addButton and clearButton. Strangely, only the style class that is in second place is loaded. In this situation, the addButton receives its colour and the clearButton remains colourless. If I place the style class of the clearButton in second place in the stylesheet, it receives its colour and the addButton remains colourless.
Button.css
.clearButton {
background-color: #8c00ff;
}
.addButton {
background-color: #7ce0ff;
}
If I outsource both style classes to separate stylesheets, it works but this is not an elegant solution.
I would also like to understand the problem
I've execute your code, and it works, I think the problem is where you call the button you are not passing props to
change import type of button style
import styles from './Button.css';
return(
<div>
<button className={`button ${label === 'ADD' ? styles.addButton : styles.clearButton}`}>
{lable}
</button>
</div>
);
and reolace background-color to background
.clearButton {
background: #8c00ff;
}
.addButton {
background: #7ce0ff;
}
you can test code in this codesandbox project
I'm experimenting with the windowing technique in Next.js with Antd, and I made this simple app that has this index.tsx page:
import { CSSProperties } from 'react'
import type { NextPage } from 'next'
import Head from 'next/head'
import Image from 'next/image'
import AutoSizer from 'react-virtualized-auto-sizer'
import { FixedSizeList as List } from 'react-window'
import { Card, Tooltip } from 'antd'
import data from '../data'
import styles from '../styles/Home.module.css'
const people = data();
const Row = ({ index, style }: { index: number, style?: CSSProperties }) => (
<div style={style}>
<Card bordered={true}>
<p>{people[index].name}</p>
<Tooltip title={`${Math.floor(people[index].age/15)} in dog years`}>
<p>{people[index].age}</p>
</Tooltip>
</Card>
</div>
)
const Home: NextPage = () => {
return (
<AutoSizer>
{({ height, width }) => (
<List
height={height}
itemCount={people.length}
itemSize={100}
width={width}
>
{Row}
</List>
)}
</AutoSizer>
)
}
export default Home
The FixedSizeList won't work until I add the following style in globals.css:
html, body, div {
height: 100%;
}
However, when I do that, it breaks the Tooltip by Antd. Usually what happens is that when I hover above a relevant element, the tooltip appears for a split second with 100% height and then disappears and it doesn't appear anymore on a page no matter where I hover.
How can I solve this?
I finally found a solution after much agony.
Apparently, Nextjs wraps the entire layout with a div with id __next. That's the outer-most container of the entire page, and its height is not set, so since the content of FixedSizeList is positioned such that it's outside of the regular page flow, the __next div gets a height of 0.
I don't know if there is any better solution, but I simply added this little style in the globals.css file:
div#__next {
height: 100%;
}
That fixed the issue without forcing every other div in the page to have height at 100% (which includes the tooltips and popovers).
I'm coding a React function with parent component containing an array of objects:
let const ingredients = [
{name:"lettuce",color:"green"},
{name:"tomato",color:"red"}
]
...
In a child component, there is a map function that breaks down an array to single items to be displayed in a div.
What is the best practice for defining CSS styling for an object className:"name" to set backgroundColor: {ingredient.color};? I'm trying to avoid manual entry of the entire set of key/values of 'ingredients', to allow updating the object without breaking the code.
I'm currently using inline styling, which I have been advised against. Currently using:
let burg = props.toppings.map((item) => {
const divColor = {backgroundColor: item.color};
return (<div style={divColor}>{item.name}</div>)
Inline style is bad when you have other solution to do what you want. Here, you have a string that is the color (red, green, etc.) so you could write a css class for every color, but that is of course a really bad idea. Inline style is the good way to do it here.
I would suggest setting the class of the div instead of the style. That way you can change the look without resorting to inlining the style.
You could create a css class for lettuce with the background color green, instead of using the item.color you'd set class={ item.name }
You can use this way.
css can be more handy if you use scss
// css
.color-green {
color: green;
}
.color-red {
color: red;
}
import React from "react";
import "./styles.css";
const ingredients = [
{ name: "lettuce", color: "green" },
{ name: "tomato", color: "red" }
];
const Vegetable = ({ color, text }) => {
return (
<p>
this is <span className={`color-${color}`}>{text}</span> color{" "}
</p>
);
};
export default function App() {
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
{ingredients.map((item, index) => {
return <Vegetable key={index} color={item.color} text={item.name} />;
})}
</div>
);
}
A Button component is generally comprised of the Text element wrapped with a TouchableHighlight (or other touchable). I'm trying to create a Button component styled using styled-components, but am having trouble getting my style to respond dynamically to props.
Button Component
Below, I've created a Button component similar to the Adapting based on props example found in the styled-component docs.
import React from 'react';
import { Text, TouchableHighlight } from 'react-native';
import styled from 'styled-components/native';
const colors = {
accent: '#911',
highlight: '#D22',
contrast: '#FFF',
}
const Label = styled.Text`
color: ${props => !props.outline ? colors.contrast : colors.accent};
font-weight: 700;
align-self: center;
padding: 10px;
`
const ButtonContainer = styled.TouchableHighlight`
background-color: ${props => props.outline ? colors.contrast : colors.accent};
width: 80%;
margin-top: 5px;
border-color: ${colors.accent};
border-width: 2px;
`
const Button = (props) => {
return (
<ButtonContainer
onPress={props.onPress}
underlayColor={colors.highlight}
>
<Label>
{props.children}
</Label>
</ButtonContainer>
);
};
export default Button;
Button Usage
After importing it, I'm using the button like this...
<Button
outline
onPress={() => console.log('pressed')}>
Press Me!
</Button>
Expected Result
And so, I would expect my button to look like this...
Actual Result
But instead it looks like this...
What I've done to troubleshoot so far
When I inspect using react-devtools, I can see that the outline prop is being passed down to the Button component.
But the prop is not passed down to any of it's children
The Passed Props part of the docs state, "styled-components pass on all their props", but I guess not all the way down?
My Question
What do I need to change so that I can dynamically style my Button based on it's props?
Here you have:
const Button = (props) => {
return (
<ButtonContainer underlayColor={colors.highlight}>
<Label>
{props.children}
</Label>
</ButtonContainer>
);
};
If ButtonContainer was a normal React component, you wouldn't expect the props passed to Button to be automatically passed to ButtonContainer. You'll have to do <ButtonContainer underlayColor={colors.highlight} {...props} /> to do it.
Actually ButtonContainer is a normal React component, the only difference is you pre-apply some styles using an HOC.
Also if you desugar this to a React.createElement call, you can see there's no way props can be passed automatically, because a Function's arguments don't get passed automatically to the function calls inside it.
const Button = (props) => {
return React.createElement(ButtonContainer, { underlayColor: colors.highlight }, ...);
};
It's nothing specific to styled-components. You just have to pass down the props yourself to ButtonContainer, as well as to Label.
So you'd rewrite your code to:
const Button = (props) => {
return (
<ButtonContainer underlayColor={colors.highlight} onPress={props.onPress} outline={props.outline}>
<Label outline={props.outline}>
{props.children}
</Label>
</ButtonContainer>
);
};
Technically a React component can pass down props to it's children, so ButtonContainer could pass them down to Label using React.Children and React.cloneElement APIs. But ButtonContainer doesn't do that for obvious reasons, e.g. you'd not want underlayColor and onPress to be passed to Label automatically. It would cause a lot of confusing bugs.