Replacing the clearIndicator X with custom text in React-Select - reactjs

I need to remove the clearIndicator's default X in the select component on MultiSelect and replace it with custom text. Is there a way to do this without losing the ability to remove the selected options (as happens with isClearable={false})?
Code:
export const MultiSelect = () => {
const [selected, setSelected] = useState([]);
const options = [
{ value: '1', label: 'Label1' },
{ value: '2', label: 'Label2' },
{ value: '3', label: 'Label3' },
];
const customStyles = {
control: (prevStyle, { isFocused }) => ({
...prevStyle,
backgroundColor: 'rgba(248, 251, 251, 1)',
boxShadow: 'none',
borderColor: isFocused ? 'black' : 'grey',
':hover': {
borderColor: isFocused ? 'black' : 'grey',
},
}),
clearIndicator: (prevStyle) => ({
...prevStyle,
color: 'rgba(0, 0, 0, 0.4)',
':hover': {
color: 'rgba(0, 0, 0, 0.4)',
},
}),
};
return (
<ReactSelect
ref={reactSelectRef}
placeholder={placeholder}
instanceId={`multiselect-${id}`}
styles={customStyles}
isOptionSelected={isMulti && isOptionSelected}
options={getOptions()}
value={getValue()}
onChange={isMulti ? onChangeHandler : onChange}
hideSelectedOptions={false}
closeMenuOnSelect={!isMulti}
formatGroupLabel={formatGroupLabel}
isMulti={isMulti}
/>
);

React Select has an option of passing in your own custom Components Docs
Would look something like this
<Select
//You can pass in any component as the ClearIndidcator and do whatever customizations you want
components={{ ClearIndicator: () => <div>Clear</div> }}
{...props}
/>

Related

Struggling to correctly update and re-render chart on react-chartjs-2

I am building an app for gym-goers to record and track their progress. For each exercise they input, it should render a chart showing their previous track record. This is working fine.
Users can then add another entry to this track record, but it does not update the chart unless you refresh the page. I can't work out why or how to fix it.
There are a number of different components involved - a parent Exercise.js one, then an ExerciseFooter.js one, which contains the buttons to adjust the target or add a new entry to the exercise, and then AddHistory.js and SetTarget.js components which contain modals and the logic to update the exercise via Redux and MongoDB.
A minimal version of the Exercise.js page is here (I've collapsed the stuff that's mainly styling into single lines as much as possible):
import React, { useState, useEffect } from "react";
import { ExerciseFooter } from "./ExerciseFooter";
import { Line } from "react-chartjs-2";
import { useLocation } from "react-router-dom";
import { useSelector } from "react-redux";
export const Exercise = (props) => {
const location = useLocation();
const users = useSelector((state) => state.auth);
const localUser = JSON.parse(localStorage.getItem("profile"));
const [user, setUser] = useState("");
const [exerciseProp, setExerciseProp] = useState({
history: [""],
target: 0,
});
useEffect(() => {
localUser &&
localUser?.result &&
users.length > 0 &&
setUser(
users.filter(
(filteredUser) => filteredUser._id == props.match.params.userId
)[0]
);
if (!localUser) setUser("");
setExerciseProp(
user?.exercises?.filter(
(exercise) => exercise._id == props.match.params.exerciseId
)[0]
);
}, [users, location]);
//styling for chart
const [barData, setBarData] = useState({
labels: [""],
datasets: [
{ label: "Target", fill: false, radius: 0, data: [""], borderColor: ["rgba(35, 53, 89)"], borderWidth: [3], },
{ label: "You did", data: [""], tension: 0.3, borderColor: ["white"], backgroundColor: ["white"], borderWidth: 3, },
],
});
//updating chart data
var weightArr = [];
var dateArr = [];
var targetArr = [];
if (exerciseProp) {
exerciseProp.history.map((hist) =>
weightArr.push(parseInt(hist.weight) || 0)
);
exerciseProp.history.map((hist) => dateArr.push(hist.date));
for (let i = 0; i < exerciseProp.history.length; i++) {
targetArr.push(exerciseProp.target);
}
}
useEffect(() => {
if (exerciseProp) {
setBarData({
labels: dateArr,
datasets: [
{
label: "Target",
fill: false,
radius: 0,
data: targetArr,
borderColor: ["rgba(35, 53, 89)"], borderWidth: [3],
},
{
label: "Weight",
data: weightArr,
tension: 0.3, borderColor: ["white"], backgroundColor: ["white"], borderWidth: 3,
},
],
});
}
}, [users]);
//render chart ones exerciseProp is populated
if (exerciseProp) {
return (
<div style={{ marginTop: "200px" }}>
<Line
data={barData}
options={{ plugins: { title: { display: false, }, legend: { display: false, }, },
scales: { x: { grid: { color: "white", font: { family: "Dongle", size: 20, }, }, ticks: { color: "white", font: { family: "Dongle", size: 20, }, }, }, y: { grid: { color: "white", }, ticks: { color: "white", font: { family: "Dongle", size: 20, }, }, }, }, }}
/>
{exerciseProp && <ExerciseFooter user={user} exercise={exerciseProp} />}
</div>
);
} else {
return <>Loading...</>;
}
};
I've tried doing a few different things but nothing has worked. I tried adding an 'update' state variable which was updated by a function passed down to the the various dispatches, and then added it to the dependencies of the useEffects, but that didn't seem to make any difference.
Any help much appreciated! As I say, if I just force a refresh then it works fine but know that's bad practice so trying to work out why it isn't re-rendering correctly.
Thanks!
You just have to enable redraw prop
like this
<Line
redraw={true}
data={barData}
options={{ plugins: { title: { display: false, }, legend: { display: false, }, },
scales: { x: { grid: { color: "white", font: { family: "Dongle", size: 20, }, }, ticks: { color: "white", font: { family: "Dongle", size: 20, }, }, }, y: { grid: { color: "white", }, ticks: { color: "white", font: { family: "Dongle", size: 20, }, }, }, }, }}/>
this all you have to do
redraw={true}

react-select styling issues when resizing for height and width

I am trying to make a react-select component but I keep running into an issue where if I change the high and width of the original react-select it throws everything else of center.
Here is the original react-select box code:
import React from 'react'
import Select from 'react-select'
const options = [
{ value: 'item-1', label: 'item-1' },
{ value: 'item-2', label: 'item-2' },
{ value: 'item-3', label: 'item-3' },
{ value: 'item-4', label: 'item-4' }
]
export default function Example(){
return (
<Select options={options}
closeMenuOnSelect={true}
placeholder="Placeholder"
/>
)}
and a picture:
original react-select image
this is size of react-select box I want:
height: 20,
width: 118.5
modified react-select for correct height and width
as you can see it throws off the placement of the input box, placeholder, and icons.
Here is the code for the above image:
import React from 'react'
import Select from 'react-select'
const options = [
{ value: 'item-1', label: 'item-1' },
{ value: 'item-2', label: 'item-2' },
{ value: 'item-3', label: 'item-3' },
{ value: 'item-4', label: 'item-4' }
]
const customStyles = {
control: base => ({
...base,
height: 20,
minHeight: 20,
width: 118.5,
}),
}
export default function Example(){
return (
<Select options={options}
styles={customStyles}
closeMenuOnSelect={true}
placeholder="Placeholder"
/>
)}
and this is how I have been trying to modify the component. This has gotten me somewhat close to the desired outcome but the input box sizing and icon placements are still off and sized weird:
import React from 'react'
import Select from 'react-select'
const options = [
{ value: 'item-1', label: 'item-1' },
{ value: 'item-2', label: 'item-2' },
{ value: 'item-3', label: 'item-3' },
{ value: 'item-4', label: 'item-4' }
]
const customStyles = {
control: base => ({
...base,
height: 20,
minHeight: 20,
width: 118.5,
}),
valueContainer: base => ({
...base,
height: 20,
minHeight: 20,
width:20,
alignItems: 'left',
}),
indicatorsContainer: base => ({
...base,
height: 20,
minHeight: 20,
alignItems: 'center',
}),
}
export default function Example(){
return (
<Select options={options}
styles={customStyles}
closeMenuOnSelect={true}
placeholder="Placeholder"
/>
)}
react-select what I have been able to achieve with the posted code image 1
react-select what I have been able to achieve with the posted code image 2
I have been at this for hours and I just cannot seem to get everything to fit nice and neat into the react-select box when I resize it. Any help would be greatly appreciated.

How to select all options in react select?

I am not able to implement select all option for react select.
Below is my code
Here I am using react-select with multi select option.
when I click on select all option it should select all options in the dropdown and save it in a state variable.
import React from "react";
import Select,{components} from 'react-select';
import '../App.css'
const options = [
{ value: '*', label: 'Select All' },
{ value: 'ocean', label: 'Ocean', color: '#00B8D9', isFixed: true },
{ value: 'blue', label: 'Blue', color: '#0052CC', isDisabled: true },
{ value: 'purple', label: 'Purple', color: '#5243AA' },
{ value: 'red', label: 'Red', color: '#FF5630', isFixed: true },
{ value: 'orange', label: 'Orange', color: '#FF8B00' },
{ value: 'yellow', label: 'Yellow', color: '#FFC400' },
{ value: 'green', label: 'Green', color: '#36B37E' },
{ value: 'forest', label: 'Forest', color: '#00875A' },
{ value: 'slate', label: 'Slate', color: '#253858' },
{ value: 'silver', label: 'Silver', color: '#666666' },
];
export default function ReactSelect() {
const [value,setValue]=React.useState([])
const handleChange = (val) => {
if(val && val.length && val[0].value==='*'){
let arr=options;
arr.splice(0,0);
setValue([...arr])
}
else{
setValue( [...val] );}
}
return (
<div id="select">
<h1>Hello StackBlitz!</h1>
<p>Start editing to see some magic happen </p>
<Select
onChange={handleChange}
isMulti
name="colors"
options={options}
className="basic-multi-select"
classNamePrefix="select"
closeMenuOnSelect={false}
hideSelectedOptions={false}
components={{ ValueContainer }}
value={value}
/>
<button onClick={()=>console.log(value)}>CLick</button>
</div>
);
}
const ValueContainer = ({ children, ...props }) => {
let [values, input] = children;
if (Array.isArray(values)) {
const val = (i= Number) => values[i].props.children;
const { length } = values;
switch (length) {
case 1:
values = `${val(0)} `;
break;
default:
const otherCount = length - 1;
values = `${val(0)}+ ${otherCount} `;
break;
}
}
return (
<components.ValueContainer {...props}>
{values}
{input}
</components.ValueContainer>
);
};
I tried to implement with select all option but its not working. Is there any inbuilt facility for it or any other select library which has multi select option with select all facility.

React select multi select one option not clearable

I am using react-select in my project. I have it for multiple select and it looks like this:
and it works fine. The problem is I would like to have one option already selected and it would be not clearable so it will not have "X" near it
I just need it for one option, all others have to be normally in the options and clearable.
How can I achieve that? Is it a special prop added to options or can I check them some way that if option name is commercial it will not have possibility to clear and would be selected on initial
react-select has a fixed options example on the docs but I found this solution is much cleaner. You can remove MultiValueRemove component (the delete button) based on the option value:
const MultiValueRemove = (props) => {
if (props.data.isFixed) {
return null;
}
return <components.MultiValueRemove {...props} />;
};
export default () => {
return (
<Select
isMulti
defaultValue={[colourOptions[0], colourOptions[1]]}
isClearable={false}
options={colourOptions}
components={{ MultiValueRemove }}
/>
);
};
The select above will remove the delete button of any option that has the isFixed property set to true (the first 2 options below).
export const colourOptions = [
{ value: 'ocean', label: 'Ocean', color: '#00B8D9', isFixed: true },
{ value: 'red', label: 'Red', color: '#FF5630', isFixed: true },
{ value: 'purple', label: 'Purple', color: '#5243AA' },
{ value: 'orange', label: 'Orange', color: '#FF8B00' },
{ value: 'yellow', label: 'Yellow', color: '#FFC400' },
{ value: 'green', label: 'Green', color: '#36B37E' },
{ value: 'forest', label: 'Forest', color: '#00875A' },
{ value: 'slate', label: 'Slate', color: '#253858' },
{ value: 'silver', label: 'Silver', color: '#666666' },
];
Live Demo
You can remove that by using isClearable props of react-select like below
Consider your options array have fixed boolean set to true
<Select
// other props
isClearable={options.some(v => !v.isFixed)}
/>
And you can change you multiValueRemove in styles const like this
const styles = {
// other styles here
multiValueRemove: (base, state) => {
return state.data.isFixed ? { ...base, display: 'none' } : base;
},
};
You can find more info in Fixed option section of https://react-select.com/home#fixed-options
Try this:
export const CreatingSelect: FC<CreatingSelectProps> = (props) => {
const { className, components, ...restProps } = props;
const selectClassName = cn('select', className);
const MultiValueRemove = (props: PropsWithChildren<any>) => {
return (
<div className={props.innerProps.className} onClick={props.innerProps.onClick}>
<SvgIcon name={iconNames.cross} />
</div>
);
};
return (
<SelectStyled
styles={customStyles}
className={selectClassName}
classNamePrefix='select'
components={{ ...components, MultiValueRemove }}
{...restProps}
/>
);
};

React Material UI TextField Styles Not Working

I'm trying to style the TextField API provided by Material UI (found here), however, for some reason, the styling is not being applied to the component. When I render it on a webpage, it's shown as in its default form. I would greatly appreciate any help on this. Here is my code.
interface CustomEmptyFieldProps {
usernameOrPass: string,
}
const baseMuiOutlineInputSizeAndPosition = {
borderRadius: 8,
width: '328px',
height: '40px',
};
const baseTextFieldSizeAndPosition = () => (
{
"& label:not(.Mui-focused)": { // Label in center of TextInput
marginTop: '-8px',
},
"& label:.Mui-shrink": { // Label in center of TextInput
marginTop:'-8px',
},
width: '328px',
height: '40px'
}
);
const useTextFieldStyles = (isTypedIn: boolean) => (
makeStyles({
"& label.Mui-focused, label:not(.Mui-focused)": { color: TextFieldColours.error.label },
"& .MuiOutlinedInput-root": {
"& fieldset": { borderColor: TextFieldColours.error.border, },
"&:hover fieldset": { borderColor: TextFieldColours.error.border, },
"&.Mui-focused fieldset": { borderColor: TextFieldColours.error.border },
...baseMuiOutlineInputSizeAndPosition,
},
...baseTextFieldSizeAndPosition,
})
);
const EmptyTextField = (props: CustomEmptyFieldProps) => {
const {
usernameOrPass,
} = props;
const inputLabel = "VolunteerHub " + usernameOrPass;
const errorMessage = "Please enter your VolunteerHub " + usernameOrPass;
const textFieldStyles = useTextFieldStyles(false);
return (
<div>
<TextField
classes={{ root: textFieldStyles, }}
placeholder={inputLabel}
id="outlined-error-helper-text"
defaultValue=""
helperText={errorMessage}
variant="outlined"
/>
</div >
);
}
Not sure about the way you declare your useTextFieldStyles. Here is how I would usually do:
const useTextFieldStyles = makeStyles(() => ({
root: {
"& label.Mui-focused, label:not(.Mui-focused)": {
color: TextFieldColours.error.label
},
"& .MuiOutlinedInput-root": {
"& fieldset": { borderColor: TextFieldColours.error.border },
"&:hover fieldset": { borderColor: TextFieldColours.error.border },
"&.Mui-focused fieldset": {
borderColor: TextFieldColours.error.border
},
...baseMuiOutlineInputSizeAndPosition
},
...baseTextFieldSizeAndPosition
}
}));
Working sample: https://codesandbox.io/s/runtime-sky-x14vr?file=/src/App.tsx:647-1173

Resources