Slider inside of Modal is not working - React + Material UI - javascript

I am trying to attach a custom slider component in an MUI Modal component.
My slider is working pretty good on a storybook, this is the behavior as expected:
But when I add it into the Material UI modal it this is the behavior:
I really don't know what could be happening... I've tried making my custom modal (without MUI), using another slider library and they all behave the same.
I am getting this warning when I try to move my slider on the modal:
Slider.js:770 [Violation] Added non-passive event listener to a scroll-blocking 'touchstart' event.
Consider marking the event handler as 'passive' to make the page more responsive.
See https://www.chromestatus.com/feature/5745543795965952
This is my slider code (which, to make emphasis, is working perfectly outside of the modal:
import React from "react";
import {
styled,
Grid,
Slider as MUISlider,
InputBase,
Tooltip,
} from "#material-ui/core";
const CustomSlider = styled(MUISlider)(({ theme }) => ({
color: theme.palette.secondary.light,
width: 86,
}));
const GasInput = styled(InputBase)(({ theme }) => ({
color: theme.palette.secondary.light,
width: 48,
height: 32,
border: "1px solid #ECEFF3",
borderRadius: 4,
background: "#FAFCFF",
fontSize: 12,
boxSizing: "border-box",
padding: 12,
}));
const SliderContainer = styled(Grid)({
width: 200,
height: 20,
marginTop: -10,
});
const Input = styled(Grid)({
paddingLeft: 8,
});
export interface SliderProps {
value: number;
min: number;
max: number;
onChangeValue: (value: number) => void;
}
interface Props {
children: React.ReactElement;
open: boolean;
value: number;
}
function ValueLabelComponent(props: Props) {
const { children, open, value } = props;
return (
<Tooltip open={open} enterTouchDelay={0} placement="top" title={value}>
{children}
</Tooltip>
);
}
export function Slider({ min, max, value, onChangeValue }: SliderProps) {
const handleSliderChange = (
_: React.ChangeEvent<unknown>,
value: number | number[]
) => {
onChangeValue(Number(value));
};
const handleInputChange = (event: React.ChangeEvent<HTMLInputElement>) => {
onChangeValue(parseInt(event.target.value, 10));
};
return (
<SliderContainer
container
direction="row"
alignItems="center"
justify="flex-end"
>
<Grid item>
<CustomSlider
ValueLabelComponent={ValueLabelComponent}
min={min}
max={max}
value={value}
onChange={handleSliderChange}
/>
</Grid>
<Input item>
<GasInput type="number" value={value} onChange={handleInputChange} />
</Input>
</SliderContainer>
);
}

I created an example using your code. Everything appears to work as expected. Compare your local code to that.

Thanks to #jack.benson answer, I was able to find out what was really going on (Really appreciated sir).
I created a modal component which abstracted the main things of the modal I will use through the entire app:
import React from "react";
import { Modal as MUIModal, Box, styled } from "#material-ui/core";
interface ModalProps {
width: number;
height: number;
children: React.ReactNode;
open: boolean;
}
export function Modal({ width, height, children, open }: ModalProps) {
const ModalContainer = styled(MUIModal)({
height,
width,
margin: "auto",
borderRadius: 12,
});
const ModalBox = styled(Box)({
height,
width,
background: "#FFFFFF",
borderRadius: 12,
outline: "none",
});
return (
<ModalContainer open={open}>
<ModalBox>{children}</ModalBox>
</ModalContainer>
);
}
As you can see, I am using the styled function in order to style my components. And that's what was giving me the problem. I don't know why is the reason, but if I move from styled to makeStyles it will work perfectly, this is the new code of my modal component:
import React from "react";
import { Modal as MUIModal, Box, makeStyles } from "#material-ui/core";
interface ModalProps {
width: number;
height: number;
children: React.ReactNode;
open: boolean;
}
const useStyles = ({ height, width }: Partial<ModalProps>) => makeStyles({
root: {
height: `${height}px`,
width: `${width}px`,
margin: "auto",
borderRadius: 12,
},
box: {
height: `${height}px`,
width: `${width}px`,
background: "#FFFFFF",
borderRadius: 12,
outline: 0,
}
});
export function Modal({ width, height, children, open }: ModalProps) {
const classes = useStyles({ width, height })()
return (
<MUIModal className={classes.root} open={open}>
<Box className={classes.box}>{children}</Box>
</MUIModal>
);
}

Related

React Material UI Slider [duplicate]

This question already has answers here:
How to Change Material-UI Slider Color
(2 answers)
Closed 1 year ago.
I am using the react material UI slider and want to customize the color of the pointer from default value blue to pink? I have tried modifying the thumb and finger in withStyles object.But it doesn't work.
https://material-ui.com/components/slider/
I want to customize the color of the slider pointer.
The is makeStyles component that material UI library offers that helps overriding custom styles to styled components of material UI. Here is a little snapshot that might help you:
import { makeStyles } from '#material-ui/core/styles';
import Slider from '#material-ui/core/Slider';
const useStyles = makeStyles({
root: {
width: 250,
},
sliderColor: {
color: 'red'
}
});
export default function InputSlider() {
const classes = useStyles();
return (
<div className={classes.root}>
<Grid container spacing={2} alignItems="center"
<Grid item xs>
<Slider
value={typeof value === 'number' ? value : 0}
onChange={handleSliderChange}
aria-labelledby="input-slider"
className={classes.sliderColor}
/>
</Grid>
</Grid>
</div>
);
}
create a custom component like this:
const PrettoSlider = withStyles({
root: {
color: '#52af77',
height: 8,
},
thumb: {
height: 24,
width: 24,
backgroundColor: '#fff',
border: '2px solid currentColor',
marginTop: -8,
marginLeft: -12,
'&:focus, &:hover, &$active': {
boxShadow: 'inherit',
},
},
active: {},
valueLabel: {
left: 'calc(-50% + 4px)',
},
track: {
height: 8,
borderRadius: 4,
},
rail: {
height: 8,
borderRadius: 4,
},
})(Slider);
and call this in your render like:
return {
...
<PrettoSlider valueLabelDisplay="auto" aria-label="pretto slider" defaultValue={20} />
...
}
by https://material-ui.com/components/slider/ Customed sliders

Changing an object value from a Class

I have a js file handling my css where I am trying to change the value of an object, but the value stays the same.
let inputBoxInner = {
width: "80%",
marginTop: 5,
alignItems: "center",
color: "#397185",
cursor: "text",
height: 36,
border: "1px solid #80cfc6",
visibility: "visible",
opacity: 0.2,
setOpacity: function (e) {
this.opacity = e
};
};
module.exports = {
inputBoxInner
};
import React, {Component} from "react";
import {inputBoxInner} from "../css/style.js";
export default class Input extends Component {
state = {
borderOpacity: 1,
id: ""
};
return(
<div
className="input"
onClick={(e) => {
inputBoxInner.setOpacity(this.state.borderOpacity);
this.setState({id: e.target.className});
}}
style={inputBoxInner}
/>
);
};
I assume the "this.opacity" is only returning a reference and not modifying the actual object and I am unsure of how to make this object mutable.
How would I go about changing this value?
You should save a clicked state in the state and set opacity depending on it.
state = {
borderOpacity: 1,
id: "",
isClicked: false
};
return(
<div
className="input"
onClick={(e) => { this.setState({id: e.target.className, isClicked: true }); }}
style={{...inputBoxInner, opacity: this.state.isClicked ?
this.state.borderOpacity : inputBoxInner.opacity}}
/>
);

React Material UI Tooltip / Popper remove unwanted transparency / opacity

I'm trying to use the Tooltip component of the React Material UI Library somewhere in my project. The code for the same is like this:
<WhiteOnDarkGreyTooltipWithControls
disableTouchListener
disableFocusListener
title={
<Text selectable={false} style={[styles.ratedIndicatorTextColor]}>
{"Game Completion Rate"}
</Text>
}
placement={"bottom"}
disablePortal={true}
>
<Text selectable={false}>GCR</Text>
</WhiteOnDarkGreyTooltipWithControls>;
Where WhiteOnDarkGreyTooltipWithControls looks like this:
import withStyles from "#material-ui/core/styles/withStyles";
import Tooltip from "#material-ui/core/Tooltip";
import React from "react";
export const WhiteOnDarkGreyTooltip = withStyles({
tooltip: {
color: "E0E0E0",
backgroundColor: "#404040",
borderRadius: 2,
maxWidth: 200,
textAlign: "center",
fontWeight: 900,
padding: 8,
marginTop: 5,
opacity: 1
},
popper: {
opacity: 1
}
})(Tooltip);
export class WhiteOnDarkGreyTooltipWithControls extends React.Component {
state = { open: this.props.open };
render = () => (
<WhiteOnDarkGreyTooltip
TransitionComponent={({ children }) => children}
{...this.props}
enterDelay={0}
open={this.state.open}
PopperProps={{
placement: "bottom",
disablePortal: !!this.props.disablePortal,
modifiers: [
{
preventOverflow: {
enabled: false,
boundariesElement: "scrollParent"
}
}
]
}}
/>
);
open = () => this.setState({ open: true });
close = () => this.setState({ open: false });
}
I want my tooltips to have an opaque, black background with white text on top. Elsewhere in the project the above configuration works fine, but particularly in the above usage, some transparency is being added:
How can I disable any opacity being set by default in the Component in the Material UI library?

How to add dynamic content to React Material UI expansion panels whilst keeping only one active tab at at time functionality

I have WebGL/React project that generates a list of users from a mock data on click.
I want this content to appear in an accordion and, as I've had good experience with material ui before I thought I'd use their expansion panel.
It works well straight from the demo page, however if I wanted to map over my user data base and populate the expansion panels with this instead, it appears to get rid of the handy functionality.
I would like the first expansion panel open by default and then as you click on one panel, any other panels close, which is the default behaviour from the example.
When I pass in props
<ExpansionPanel
square
expanded={expanded === "panel1"}
onChange={handleChange("panel1")}
>
<ExpansionPanelSummary
aria-controls="panel1d-content"
id="panel1d-header"
>
{props.country}
</ExpansionPanelSummary>
<ExpansionPanelDetails>{props.children}</ExpansionPanelDetails>
</ExpansionPanel>
</div>
then use it here....
{users &&
users.map(user => (
<Accordion title={user.name} key={user.name}>
<div className="overlay-container">
<div className="overlay overlay-anim">
<div className="overlay-content-container">
<div className="name-container">
<h1 key={user.id} className="user_name">
{user.name}
</h1>
</div>
</div>
</div>
</div>
</Accordion>
))}
It opens all the panels by default and doesn't close the others when one is active. (again not real data and the gif makes it a little buggy but I can't generate an example as the code base is too huge).
Would anyone have some ideas or examples of how to achieve this?
EDIT
as per below suggestion have added added an id into the mapping function and adapted the expansion component, unfortunately getting the same effect/issues
const [expanded, setExpanded] = React.useState("panel" + props.id);
const handleChange = panel => (event, newExpanded) => {
setExpanded(newExpanded ? panel : false);
};
return (
<div>
<ExpansionPanel
expanded={expanded === "panel" + props.i}
onChange={handleChange("panel" + props.i)}
>
<ExpansionPanelSummary
aria-controls={"panel" + props.id + "d" + "-content"}
id={"panel" + props.id + "d" + "-header"}
>
{props.country}
</ExpansionPanelSummary>
<ExpansionPanelDetails>{props.children}</ExpansionPanelDetails>
</ExpansionPanel>
</div>
);
}```
Hard to say what you are doing with the ExpansionPanel, but it would appear you have same
<ExpansionPanel expanded={expanded === 'panel1'} onChange={handleChange('panel1')}>
for everyone of them, you need to have unique names (panel1, panel2, panel3) for them.
EDIT:
You could add iterator into the map function:
users.map((user, i) => (
and have i passed to the ExpansionPanel as prop for
<ExpansionPanel expanded={expanded === 'panel' + props.i} onChange={handleChange('panel' + props.i)}>
EDIT #2: updated answer with working code and reason why yours didn't work.
Main function, note that u should add the useState here and give them to the CustomizedExpansionPanels child.
example.jsx
import React, { useState } from 'react'
import { withStyles } from '#material-ui/core/styles'
import CustomizedExpansionPanels from './TestTab.jsx'
const styles = (theme) => ({
/* ... your styles... */
})
const users = [
{ name: '5001', color: 'green', type: 'None' },
{ name: '5002', color: 'blue', type: 'Glazed' },
{ name: '5003', color: 'red', type: 'Chocolate' },
{ name: '5004', color: 'orange', type: 'Maple' }
]
function Example(props) {
const [expanded, setExpanded] = useState('panel_0') // change 0 to the number u want to be open by default
return (
<div>
{users.map((user, i) => CustomizedExpansionPanels(user, i, expanded, setExpanded))}
<div/>
)
export default withStyles(styles, { withTheme: true })(Example)
TestTab.jsx
import React from 'react'
import { withStyles } from '#material-ui/core/styles'
import MuiExpansionPanel from '#material-ui/core/ExpansionPanel'
import MuiExpansionPanelSummary from '#material-ui/core/ExpansionPanelSummary'
import MuiExpansionPanelDetails from '#material-ui/core/ExpansionPanelDetails'
import Typography from '#material-ui/core/Typography'
const ExpansionPanel = withStyles({
root: {
border: '1px solid rgba(0, 0, 0, .125)',
boxShadow: 'none',
'&:not(:last-child)': {
borderBottom: 0,
},
'&:before': {
display: 'none',
},
'&$expanded': {
margin: 'auto',
},
},
expanded: {},
})(MuiExpansionPanel)
const ExpansionPanelDetails = withStyles((theme) => ({
root: {
padding: theme.spacing(2),
},
}))(MuiExpansionPanelDetails)
const ExpansionPanelSummary = withStyles({
root: {
backgroundColor: 'rgba(0, 0, 0, .03)',
borderBottom: '1px solid rgba(0, 0, 0, .125)',
marginBottom: -1,
minHeight: 56,
'&$expanded': {
minHeight: 56,
},
},
content: {
'&$expanded': {
margin: '12px 0',
},
},
expanded: {},
})(MuiExpansionPanelSummary)
export default function CustomizedExpansionPanels(user, id, expanded, setExpanded) {
const handleChange = (panel) => (event, newExpanded) => {
setExpanded(newExpanded ? panel : false)
}
const { name, color, type } = user
return (
<div>
<ExpansionPanel square expanded={expanded === `panel_${id}`} onChange={handleChange(`panel_${id}`)}>
<ExpansionPanelSummary aria-controls={`panel_${id}d-content`} id={`panel_${id}d-header`}>
<Typography style={{ color }}>{`Collapsible Group Item #${id}`}</Typography>
</ExpansionPanelSummary>
<ExpansionPanelDetails>
<Typography>
{`name: ${name} type: ${type}`}
</Typography>
</ExpansionPanelDetails>
</ExpansionPanel>
</div>
)
}
Hard to say for sure, but it looks like you are looping the creation of one and same panel where the extended hook exists in each one of them and has the value id of it's own panel => not related to the other panels opening and closing.
You need to create each ExpansionPanel with it's own variables and have 1 hook to control them all.

Trying to fix tslint error with React + TypeScript project

I'm working on a React + TypeScript huge application. I'm building new component feature. It's a button that opens a modal with some filter options. I have included <i> tag for an SVG Icon from here.
When I peek problem using my Visual Studio, I get this error message:
Type '{ children: string; class: string; }' is not assignable to type 'DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>'.
Property 'class' does not exist on type 'DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>'.ts(2322)
How can I fix this? I'm pretty new to React, TypeScript & the project itself. The <i> tags are in the toggleArrow() function
FilterOptions Component:
import './FilterOptions.scss';
import Button from '#material-ui/core/Button';
import Modal from '#material-ui/core/Modal';
import { withStyles } from '#material-ui/core/styles';
import Typography from '#material-ui/core/Typography';
import PropTypes, { any } from 'prop-types';
import * as React from 'react';
import FilterCheckboxes from '../FilterCheckboxes/FilterCheckboxes';
import FilterSliders from '../FilterSliders/FilterSliders';
const getModalStyle = (): any => {
const top = 50;
const left = 50;
return {
top: `${top}%`,
left: `${left}%`,
transform: `translate(-${top}%, -${left}%)`,
};
};
const styles = (theme): any => ({
paper: {
position: 'absolute',
width: theme.spacing.unit * 70,
backgroundColor: theme.palette.background.paper,
boxShadow: theme.shadows[5],
padding: theme.spacing.unit * 4,
outline: 'none',
spacing: theme.spacing,
},
buttonMargin: {
marginRight: '20px',
},
});
class FilterOptions extends React.Component {
public static propTypes: { classes: PropTypes.Validator<object>; };
public state = {
open: false,
};
public handleOpen = (): void => {
this.setState({ open: true });
};
public handleClose = (): void => {
this.setState({ open: false });
};
// function to toggle arrow
public toggleArrow = (): any => {
return this.state.open ? (
<i class='material-icons'>keyboard_arrow_down</i>
) : (
<i class='material-icons'>keyboard_arrow_right</i>
);
};
public render() {
const { classes }: any = this.props;
return (
<React.Fragment>
<Button
className={`filters__button ${classes.buttonMargin}`}
color='primary'
onClick={this.handleOpen}
>
<i class='material-icons'>
<span>filter_list</span>
</i>
<span className='filters__button-message'>Filters</span>
{this.toggleArrow()}
</Button>
<Modal
aria-labelledby='simple-modal-title'
aria-describedby='simple-modal-description'
open={this.state.open}
>
<div style={getModalStyle()} className={classes.paper}>
<Typography variant='h6' id='modal-title'>
Text in a modal
</Typography>
<Typography variant='subheading'>
Status
</Typography>
<FilterCheckboxes />
<Typography>Submitted</Typography>
<FilterSliders />
<Typography>Not Submitted</Typography>
<FilterSliders />
<Typography>MARQ</Typography>
<FilterSliders />
<Button onClick={this.handleClose}>Close</Button>
</div>
</Modal>
</React.Fragment>
);
}
}
FilterOptions.propTypes = {
classes: PropTypes.object.isRequired,
};
// We need an intermediary variable for handling the recursive nesting.
const SimpleModalWrapped = withStyles(styles)(FilterOptions);
export default SimpleModalWrapped;
You've typed it as an html element, which means you can only assign properties that are in that interface. It should be className, not class

Categories

Resources