I'm kind of new to React-Select, and after changing its styles the arrow broke, I've spent 3 hours searching for the arrow placement controls to no avail, I've found how to replace it but it doesn't solve the issue, I'm deciding if I should abandon the lib approach and make the dropdown from scratch since the code looks horrendous, I apologize
This is how it currently looks like:
This is how I'm trying to make it behave like:
import Select from "react-select";
interface DropdownV2 {
color?: string[];
options: string[];
name: string;
}
export const DropdownV2: React.FC<DropdownV2> = ({ color, options, name }) => {
let optionsList: any[] = [];
let counter = 0;
options.map((option) => {
if (color) {
optionsList.push({ label: option, value: option });
} else {
optionsList.push({ label: option, value: option });
}
});
return (
<Select
defaultValue={options[0]}
isMulti={false}
placeholder={"Choose " + name}
styles={customStyles}
options={optionsList}
/>
);
};
const customStyles = {
option: (provided: any, state: { isSelected: any }) => ({
...provided,
borderBottom: "1px solid #e9e9e2",
borderLeft: "1px solid #e9e9e2",
borderRight: "1px solid #e9e9e2",
borderRadius: "4px",
color: state.isSelected ? "#290363" : "#150132",
backgroundColor: state.isSelected ? "#e9e9e2" : "white",
cursor: "pointer",
padding: 20,
}),
control: () => ({
// none of react-select's styles are passed to <Control />
width: 200,
}),
menu: (provided: any) => ({
...provided,
border: "1px solid #e9e9e2",
}),
placeholder: (base: any) => ({
...base,
color: "#150132",
fontFamily: "inter",
fontStyle: "normal",
fontWeight: "400",
fontSize: "14px",
lineHeight: "24px",
boxSizing: "border-box",
border: "1px solid #150132",
borderRadius: "4px",
padding: "12px 18px",
gap: "10px",
}),
singleValue: (provided: any, state: { isDisabled: any }) => {
const opacity = state.isDisabled ? 0.5 : 1;
const transition = "opacity 300ms";
const width = "100%";
const height = "50px";
const textAlign = "left";
const fontFamily = "inter";
const fontStyle = "normal";
const fontWeight = "400";
const fontSize = "14px";
const lineHeight = "24px";
const boxSizing = "border-box";
const display = "block";
const flexDirection = "row";
const justifyContent = "space-between";
const alignItems = "center";
const padding = "12px 18px";
const gap = "10px";
const border = "1px solid #150132";
const borderRadius = "4px";
const flex = "none";
const order = "0";
const alignSelf = "stretch";
const flexGrow = "0";
const backgroundPositionX = "244px";
const marginBottom = "10px";
return {
...provided,
opacity,
transition,
border,
width,
height,
textAlign,
padding,
fontFamily,
fontStyle,
fontWeight,
fontSize,
lineHeight,
boxSizing,
display,
flexDirection,
justifyContent,
alignItems,
gap,
borderRadius,
flex,
order,
alignSelf,
flexGrow,
backgroundPositionX,
marginBottom,
};
},
};
<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>
I provide a custom DropdownIndicator to my react-select.
import React from 'react';
import { components } from 'react-select';
import classnames from 'classnames';
import Icon from '../../../../components/Icon.component';
import styles from '../Select.module.scss';
const DropdownIndicator = ({
className,
selectProps,
...props
}) => (
<components.DropdownIndicator
{...props}
className={classnames(styles.selectIndicator, className)}
selectProps={selectProps}
>
<Icon glyph={`caret-${selectProps.menuIsOpen ? 'up' : 'down'}`} />
</components.DropdownIndicator>
);
You can also change base styling of your component using a custom styles function
export const dropdownIndicatorStyles = (base, state) => {
const { isDisabled } = state;
return {
...base,
...(isDisabled && { display: 'none' }),
};
};
That I then pass it all in to my Select
import DropdownIndicator from './DropdownIndicator';
import {dropdownIndicatorStyles} from './styleFunctions';
// ...
const {components, styles} = useMemo(() => ({
components: {
DropdownIndicator
},
styles: {
dropdownIndicator: dropdownIndicatorStyles
}
}), []);
// ...
<Select components={components} styles={styles} {...otherProps} />
Related
What I Need
I need a way to test if an accordion isCollapsed or not. I tried seeing if there was a way to grab if the maxHeight hasChanged, but from what I have read, it doesn't include measurements within the dom objects for the tests
The Problem
Writing the following test:
import React from 'react';
import { render, screen } from '#testing-library/react';
import userEvent from '#testing-library/user-event';
import Accordion from './Accordion';
import AccordionItem from './AccordionItem';
const accordionTitle = 'Title: Hello World';
const accordionContent = 'Content: Hello World';
function TestContainer() {
return (
<Accordion>
<AccordionItem title={accordionTitle}>
<p>{accordionContent}</p>
</AccordionItem>
</Accordion>
);
}
describe('Accordion', () => {
const userViewing = userEvent.setup();
it.only('expands content on item control click', async () => {
render(<TestContainer />);
expect(await screen.findByText(accordionContent)).not.toBeInTheDocument();
const accordionItem = screen.getByRole('button', { name: accordionTitle });
userViewing.click(accordionItem);
expect(await screen.findByText(accordionContent)).toBeInTheDocument();
});
});
Results in the following Error:
The Reason
I think this is because the content component still exists in the DOM, but is only being hidden by the overflow: hidden; and maxHeight: 0 or heightOfContent(for animation purposes).
The Component Code
Accordion.tsx:
import React, { ReactNode } from 'react';
function Accordion({ children }: PROPS): JSX.Element {
return <div>{children}</div>;
}
interface PROPS {
children: ReactNode;
}
export default Accordion;
AccordionItem.tsx
import React, { ReactNode, useState } from 'react';
import AccordionControlClick from './AccordionControlClick';
import AccordionContent from './AccordionContent';
import { useStyles } from './Styles';
function AccordionItem({ title, children }: PROPS): JSX.Element {
const classes = useStyles();
const [isCollapsed, setIsCollapsed] = useState(true);
return (
<div className={isCollapsed ? classes.accordionItemClosed : classes.accordionItemOpen}>
<AccordionControlClick
title={title}
isCollapsed={isCollapsed}
toggleIsCollapsed={setIsCollapsed}
/>
{children !== null && (
<AccordionContent isCollapsed={isCollapsed}>{children}</AccordionContent>
)}
</div>
);
}
interface PROPS {
title: string;
children?: ReactNode;
}
export default AccordionItem;
AccordionControlClick.tsx
import React from 'react';
import { useStyles } from './Styles';
function AccordionControlClick({ title, isCollapsed, toggleIsCollapsed }: PROPS): JSX.Element {
const classes = useStyles();
return (
<button
className={classes.accordionControlClick}
type="button"
onClick={() => toggleIsCollapsed(!isCollapsed)}
>
<h2>{title}</h2>
<span className={isCollapsed ? classes.iconChevronWrapper : classes.iconChevronWrapperRotate}>
<i class="fa-solid fa-chevron-down" />
</span>
</button>
);
}
interface PROPS {
title: string;
isCollapsed: boolean;
toggleIsCollapsed: (isOpen: boolean) => void;
}
export default AccordionControlClick;
AccordionContent.tsx
import React, { ReactNode, useRef, useLayoutEffect } from 'react';
import { useStyles } from './Styles';
function AccordionContent({ isCollapsed, children }: PROPS): JSX.Element {
// variables
const componentDomRef = useRef<any>(null);
const componentHeight = useRef(0);
const classes = useStyles();
// setup
useLayoutEffect(() => {
componentHeight.current = componentDomRef.current ? componentDomRef.current.scrollHeight : 0;
}, []);
// render
return (
<div
ref={componentDomRef}
className={classes.accordionContent}
style={isCollapsed ? { maxHeight: '0px' } : { maxHeight: `${componentHeight.current}px` }}
>
{children}
</div>
);
}
interface PROPS {
isCollapsed: boolean;
children: ReactNode;
}
export default AccordionContent;
Styles.ts
import { createUseStyles } from 'react-jss';
import { cssColors, cssSpacing } from '../../utils';
const accordionBoxShadow = '2px 3px 8px 1px rgba(0, 0, 0, 0.2)';
const accordionBoxShadowTransition = 'box-shadow 0.3s ease-in-out 0s;';
export const useStyles = createUseStyles({
accordionContent: {
boxSizing: 'border-box',
width: '100%',
padding: `0 ${cssSpacing.m}`,
overflow: 'hidden',
transition: 'max-height 0.3s ease-in-out'
},
accordionControlClick: {
boxSizing: 'border-box',
display: 'flex',
width: '100%',
alignItems: 'center',
padding: `9px ${cssSpacing.m}`,
border: 'none',
borderRadius: '8px',
outline: 'none',
backgroundColor: `${cssColors.backgroundLevel2}`,
cursor: 'pointer'
},
accordionItemClosed: {
boxSizing: 'border-box',
width: '100%',
marginBottom: cssSpacing.l,
border: `2px solid ${cssColors.accordionTitleBorder}`,
borderRadius: '8px',
boxShadow: 'none',
transition: 'none',
'&:hover': {
border: `2px solid ${cssColors.accordionTitleBorder}`,
boxShadow: accordionBoxShadow,
transition: accordionBoxShadowTransition
}
},
accordionItemOpen: {
boxSizing: 'border-box',
width: '100%',
marginBottom: cssSpacing.l,
border: `2px solid ${cssColors.accordionTitleBorder}`,
borderRadius: '8px',
boxShadow: accordionBoxShadow,
transition: accordionBoxShadowTransition,
'&:hover': {
border: `2px solid ${cssColors.accordionTitleBorder}`,
boxShadow: accordionBoxShadow,
transition: accordionBoxShadowTransition
}
},
iconChevronWrapper: {
marginLeft: 'auto',
transform: 'none',
transition: 'transform 300ms ease'
},
iconChevronWrapperRotate: {
marginLeft: 'auto',
transform: 'rotate(180deg)',
transition: 'transform 300ms ease'
}
});
In the AccordionContent.tsx file, I added in the following visibility
style={ isCollapsed ? { maxHeight: '0px', visibility: 'hidden' } : { maxHeight: `${componentHeightRef.current}px`, visibility: 'visible' } }
In the test file I make the following changes to test if the content is ToBeVisible vs toBeInTheDocument:
it.only('expands content on item control click', async () => {
render(<TestContainerOneItem />);
const accordionContentContainer = (await screen.findByText(accordionContent)).parentElement;
await waitFor(() => expect(accordionContentContainer).not.toBeVisible());
const accordionControlButton = screen.getByRole('button', { name: accordionTitle });
await userViewing.click(accordionControlButton);
await waitFor(() => expect(accordionContentContainer).toBeVisible());
});
I am having trouble rendering my react component since I separated my jss file and changed it from makeStyles to withStyles to avoid a hook problem.
I am getting an error message in my jss styling file as a couple of the methods have a 'theme' in the parenthesis for the styling to work off.
How do I go about changing this so that it renders correctly?
accessControl.component.js
import {connect, useSelector} from "react-redux";
import DataTable from "./userListTable.component";
import {Paper} from "#material-ui/core";
function AdminAccessControl(props) {
const { children, value, index, ...other } = props;
// select user object from redux
const user = useSelector(state => state.user);
// select school object from redux
const school = useSelector(state => state.diveSchool);
const classes = useStyles();
const [role, setRole] = useState({
userRole: "",
});
const handleChange = (property) => (e) => {
setRole({
// override the changed property and keep the rest
...role,
[property]: e.target.value,
});
}
return (
<div className={classes.root}>
<StyledTabs
value={value}
onChange={handleChange}
indicatorColor="primary"
textColor="primary"
aria-label="styled tabs example"
centered>
<StyledTab label="User Access Control" />
{/*<DataTable />*/}
<StyledTab label="Scuba School Access Control" />
{/*<DataTable />*/}
</StyledTabs>
<Typography className={classes.padding} />
</div>
);
}
function mapStateToProps(state){
const { user } = state.auth;
const { school } = state.diveSchool;
return {
user,
school,
};
}
export default compose(
connect(
mapStateToProps,
),
withStyles(useStyles)
)(AdminAccessControl);
myJss-style.js
export const useStyles = (theme) => ({
root: {
flexGrow: 1,
},
padding: {
padding: theme.spacing(3),
},
demo1: {
backgroundColor: theme.palette.background.paper,
},
demo2: {
backgroundColor: '#2e1534',
},
});
export const StyledTabs = () => ({
indicator: {
display: 'flex',
justifyContent: 'center',
backgroundColor: 'transparent',
'& > span': {
maxWidth: 40,
width: '100%',
backgroundColor: '#635ee7',
},
},
})((props) => <StyledTabs {...props} TabIndicatorProps={{ children: <span /> }} />);
export const StyledTab = (theme) => ({
root: {
textTransform: 'none',
color: '#fff',
fontWeight: theme.typography.fontWeightRegular,
fontSize: theme.typography.pxToRem(15),
marginRight: theme.spacing(1),
'&:focus': {
opacity: 1,
},
},
})((props) => <StyledTab disableRipple {...props} />);
//Component Style:
const BorderLinearProgress = withStyles(theme => ({
bar: {
borderRadius: 8,
backgroundColor: "red"
}
}))(LinearProgress);
//Component use:
<BorderLinearProgress variant="determinate" value={50} />
I am new to react and material-ui.
In the above code I need to pass or change bar:backgroundColor dynamically.
Please let me know what are the options to do.
Thanks in advance
You can pass your color with the theme variable.
// Passing theme
const useStyles = makeStyles((theme) => ({
bar: props => ({
borderRadius: 8,
backgroundColor: props.color
})
}))
//Using style in component
...
const [progressColor, setProgressColor] = React.useState({ color: 'red' })
const classes = useStyles(progressColor);
// Update color based on your requirements i.e. setProgressColor({color: 'green'}) in some useEffect() when progress crosses some threshold
return (
<LinearProgress color={classes.bar} />
)
...
You can find an example in official docs: https://material-ui.com/styles/basics/#adapting-based-on-props
Below code works fine with dynamic values and colors
const LinearProgressBar: React.FC<ILinearProps> = ({ value, color }) => {
const useStyles = makeStyles({
root: {
height: 10,
borderRadius: 5
},
colorPrimary: {
backgroundColor: '#E9E9E9'
},
bar: {
borderRadius: 5,
backgroundColor: color
}
});
const classes = useStyles();
return (
<LinearProgress
variant="determinate"
value={value}
classes={{
root: classes.root,
colorPrimary: classes.colorPrimary,
bar: classes.bar
}}
/>
);
};
export default LinearProgressBar;
You can do it in two ways:
1). just Write
<LinearProgress style={{backgroundColor: "red"}} variant="determinate" value={50} />
2).
import React from 'react';
import { withStyles } from '#material-ui/core/styles';
const styles = {
LinerProgressColor: {
backgroundColor: 'red',
},
};
function BorderLinearProgress (props) {
return <LinearProgress className={LinerProgressColor} variant="determinate" value={50} />;
}
export default withStyles(styles)(BorderLinearProgress);
I want to make input type file with custom style, I hide input and styling the label using css. There are 2 problems:
I chose file, but coverFileState is undefinded (if I remove css style and choose file then everything is okay).
I want to set file name inside label, but it isn't working. I set text using setCoverUploadText inside handleCoverChange, but no effect.
If you help me with that problem I will really appreciate this, thanks!
const useStyles = makeStyles((theme) => ({
fileInput: {
marginBottom: '1em',
width: '0.1px',
height: '0.1px',
opacity: '0',
overflow: "hidden",
position: "absolute"
},
uploadLabel: {
fontSize: '1rem',
cursor: 'pointer',
color: "gray",
border: "1px solid gray"
}
}));
export default function AddingBook(props: ParamsProps) {
const classes = useStyles();
const coverFile = useRef(null);
const [coverFileState, setCoverFile] = useState<string | Blob>();
const [coverUploadText, setCoverUploadText] = useState("Upload cover photo *");
const handleSubmit = () => {
if (coverFileState === undefined) {
setImageErrorMsg("*Please add cover photo.");
return;
}
api.post('/image/add', JSON.stringify(REST))
.then(res => {
uploadCoverPhoto();
props.close();
}).catch(err => {
const errorMsg = APIServices.onError(err);
showErrorPopup(errorMsg);
})
};
const handleCoverChange = () => {
// #ts-ignore
setCoverFile(coverFile.current.files[0]);
// #ts-ignore
setCoverUploadText(coverFile.current.files[0].name)
};
return (
<>
<div>
<input
id="copy-file-upload"
type="file"
accept="image/*"
ref={coverFile}
onChange={handleCoverChange}
data-testid="inputFile"
className={classes.fileInput}
/>
<label className={classes.uploadLabel} htmlFor="copy-file-upload">{coverUploadText}</label>
</div>
</>
);
Try to replace "className" by "style" for the input file and for the label.
Hope it will helps you
EDIT:
it worked for me when i set the style as :
const useStyles = {
fileInput: {
marginBottom: '1em',
width: '0.1px',
height: '0.1px',
opacity: '0',
overflow: "hidden",
position: "absolute"
},
uploadLabel: {
fontSize: '1rem',
cursor: 'pointer',
color: "gray",
border: "1px solid gray"
}
};
and use it in the return as :
....
<label style={useStyles.uploadLabel} htmlFor="copy-file-upload">{coverUploadText}</label>
....
hi how to change color of arrow icon in react-select
in mouse over in google chrome, I find CSS variable but I cant change color.
this value of CSS css-tlfecz-indicatorContainer.
in my customStyles I wrote this line but not working:
indicatorContainer:base =>({
...base,
color:'#000000'
}),
UPDATE
this is my component I use react-select
import React from 'react';
import Select from 'react-select';
export default function DropDown(props) {
const customStyles = {
control: (base, state) => ({
...base,
background: "#59c5b8",
borderRadius: 0,
}),
menu: base => ({
...base,
// override border radius to match the box
borderRadius: 20,
// kill the gap
marginTop: 0,
}),
menuList: base => ({
...base,
// kill the white space on first and last option
padding: 0
}),
indicatorSeparator: base => ({
...base,
display: 'none'
}),
myDropDown__indicator: base => ({
...base,
'&.myDropDown__dropdown-indicator': {
'&.indicatorContainer': {
color: '#000000'
}
}
}),
'&.indicatorContainer': {
color: '#000000'
}
};
const [selectedOption, setSelectedOption] = React.useState(0);
const handleChange = selectedOption => {
setSelectedOption(selectedOption)
props.parentCallBack(selectedOption)
};
return (
<Select
isSearchable={false}
styles={customStyles}
isOptionDisabled={true}
defaultValue={props.options[0]}
isRtl={true}
onChange={handleChange}
options={props.options}
classNamePrefix='myDropDown'
/>
);
}
Just use customStyles and declare a new colour for dropdownIndicator element:
const customStyles = {
dropdownIndicator: base => ({
...base,
color: "red" // Custom colour
})
};
Here you can find the list of all the elements and here a live example.
Here is how I did it in version 4.3.1
const style = {
dropdownIndicator: (provided) => ({
...provided,
"svg": {
fill: "red"
}
}),
}
return (
<Select options={renderOptions()} styles={style} />
);
This should help:
import React from 'react';
import Select from 'react-select';
export default function DropDown(props) {
const customStyles = {
indicatorsContainer: () => ({
'.myDropDown': {
'&__dropdown-indicator': {
color: 'red' // <--- Color of your choice
}
}
})
};
return (
<Select
styles={customStyles}
classNamePrefix='myDropDown'
/>
);
}
PS Removed non-related code for better understanding. :)