Material UI strange blue line when overflow attribute is added - javascript

I'm using Material-UI with my React application. I'm also using styled components and I'm viewing the app in a Chrome browser. The issue I'm having doesn't occur when using a Firefox browser.
When applying the overflow attribute in my styled component, I'm seeing this blue line towards the bottom of the modal. This only appears when I'm playing with the size of my browser window. As I gradually bring my browser window closer to normal size, the line goes away. I'm not sure why this is or what I can do to fix it.
Here is a snippet of my code:
export const ScrollableModal = styled(MUIModal)(() => ({
overflow: 'scroll',
}));
const Modal = ({ title, children, actionsLeft, actionsRight, ...rest }) => {
const wrappedTitle =
typeof title === 'string' ? <Typography>{title}</Typography> : title;
return (
<ScrollableModal {...rest}>
<Container>
I've left the rest out because it's not relevant to my question.
Here is a screenshot of what I'm describing:

I guess that's the outline property what they mentioned in the documentation for simple modal:
Notice that you can disable the outline (often blue or gold) with the outline: 0 CSS property.
First needs to be added to the current style:
const useStyles = makeStyles({
modal: {
textAlign: 'center',
width: '35vw',
backgroundColor: 'white',
opacity: 0.8,
outline: 0, // add / remove
}
});
Then it can be applied on the Container just like the following in the render:
const styles = useStyles();
return <>
<Modal open={true}>
<Container className={styles.modal}>
<p>Simple Modal</p>
</Container>
</Modal>
</>
Result by adding and removing outline property with value 0:
I guess with styled components just create a styled Container with opacity: 0 if you don't want to use makeStlyes for this purpose.
That resolved the issue for me.
I hope that helps!

Related

How would I achieve this scroll background colour change effect?

Basically, assume I have 10 sections. Each have a different colour assigned to them for background colour.
When the user scrolls down from sections 1 through 10, I would like the tag background colour to change accordingly, depending which section is on screen.
Assuming the height of the viewport is 1000px, I would like the function to find out which section is currently at 800px out of 1000px, so the bottom 20%, then find the background color of that section in the bottom 20% and apply it to the tag until the user either scrolls to the next section, or scrolls up and another component takes over the background colour.
I have tried to use IntersectionObservor for this but I don't think it is the best approach for what I want.
Currently, my setup is, I am rendering multiple components after each other, each of them has a data attribute of "data-background={background}"
Then, the observer loops through, adds them all to the observer, and watches to find which one is on screen, but it isn't working completely for what I need.
Is there an easier way to achieve what I am looking for?
Here is the code I have so far
import Page from "../components/common/Page";
import Hero from "../components/molecules/Hero";
import TechStack from "#/components/organisms/TechStack";
import { useEffect } from "react";
const Home = () => {
useEffect(() => {
const observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
console.log("entry", entry);
if (entry.isIntersecting) {
document.body.style.backgroundColor =
entry.target.dataset.background;
}
});
},
{ threshold: [0.20] }
);
// create an array of all the components to be watched
const components = [...document.querySelectorAll("[data-background]")];
components.forEach((component) => {
observer.observe(component);
});
}, []);
return (
<Page seo={{ title: "Starter Kit" }}>
<Hero />
<TechStack background="white"/>
<TechStack background="grey" />
<TechStack background="blue"/>
<TechStack background="green"/>
<TechStack background="grey"/>
<TechStack background="white"/>
</Page>
);
};
export default Home;
You can dynamically add the element to the observer when it mounted, like this
<div ref={(r) => r && observer.observe(r)} />
Here is the example: https://codesandbox.io/s/sleepy-margulis-1f7hz7

Change props depending on breakpoint with SSR support

I'm using material-ui#v5, ie. the alpha branch.
Currently, I have a custom Timeline component which does this:
const CustomTimeline = () => {
const mdDown = useMediaQuery(theme => theme.breakpoints.down("md"));
return (
<Timeline position={mdDown ? "right" : "alternate"}>
{/* some children */}
</Timeline>
);
};
It works mostly as intended, but mobile users may experience layout shift because useMediaQuery is implemented using JS and is client-side only. I would like to seek a CSS implementation equivalence to the above code to work with SSR.
I have thought of the following:
const CustomTimeline = () => {
return (
<Fragment>
<Timeline sx={{ display: { xs: "block", md: "none" } }} position="right">
{/* some children */}
</Timeline>
<Timeline sx={{ display: { xs: "none", md: "block" } }} position="alternate">
{/* some children */}
</Timeline>
</Fragment>
);
};
This will work since the sx prop is converted into emotion styling and embedded in the HTML file, but this will increase the DOM size. Is there a better way to achieve that?
I have experienced the same problem before and I was using Next.js to handle SSR. But it does not matter.
Please first install this package and import it on your root, like App.js
import mediaQuery from 'css-mediaquery';
Then, create this function to pass ThemeProvider of material-ui
const ssrMatchMedia = useCallback(
(query) => {
const deviceType = parser(userAgent).device.type || 'desktop';
return {
matches: mediaQuery.match(query, {
width: deviceType === 'mobile' ? '0px' : '1024px'
})
};
},
[userAgent]
);
You should pass the userAgent!
Then pass ssrMatchMedia to MuiUseMediaQuery
<ThemeProvider
theme={{
...theme,
props: {
...theme.props,
MuiUseMediaQuery: {
ssrMatchMedia
}
}
}}>
This should work. I am not using material-UI v5. Using the old one. MuiUseMediaQuery name might be changed but this approach avoid shifting for me. Let me know if it works.
To avoid first render before useMediaQuery launches
From reactjs docs To fix this, either move that logic to useEffect (if it isn’t necessary for the first render), or delay showing that component until after the client renders (if the HTML looks broken until useLayoutEffect runs).
To exclude a component that needs layout effects from the server-rendered HTML, render it conditionally with showChild && and defer showing it with useEffect(() => { setShowChild(true); }, []). This way, the UI doesn’t appear broken before hydration.

Extending Components in rebass.js?

Beginner here. As of now I can use Box like so:
<Box
p={5}
fontSize={4}
width={[ 1, 1, 1/2 ]}
color='white'
bg='magenta'>
Box
</Box>
and give it certain specified props, as it says on the site:
All margin and padding props
width: responsive width
fontSize: responsive font size
color: text color
bg: background color
flex: CSS flex shorthand property
order: CSS order property
alignSelf: CSS align-self property
Question: What if I need more props?
I know that I can extend rebass components, however, this seems to hard-code certain CSS properties into the component. E.g. If my Box is always purple I can create PurpleBox and save the color there. Or on their website there is this example:
const Container = props =>
<Box
{...props}
mx='auto'
css={{
maxWidth: '1024px'
}}
/>
Fair enough!
What what of I need to create a component, say, PositionedBox which gets an additional position prop for relative or absolute positioning?
I would like to use it like so:
<Box
p={5}
fontSize={4}
width={[ 1, 1, 1/2 ]}
color='white'
bg='magenta'
position='abs'>
Box
</Box>
Is this possible? Not sure how I could accomplish this.
You could make use of styled-components and styled-system
import styled from "styled-components";
import { position } from "styled-system";
// through interpolation
const StyledBox = styled(Box)`
// some properties
${position}
`;
// passing as a single property
const StyledBox = styled(Box)(position);
passing multiple properties
import { compose, property1, property2, ... } from "styled-system";
import StyledBox = styled(Box)(compose(property1, property2, ....));
this would extend all the position properties supported by the styled system.
list of all the out-of-the-box properties supported by styled-system here

Change paper color Material-UI

I'm developing a React project using the material-ui library. I'm currently trying to add a drawer which is working fine for me. However, I'm trying to change the background color of this drawer. I've heard that the way to do this is by changing the color of the drawer's paper. I've tried adding the following tag to my CSS object:
const styles = theme => ({
background:"BLUE"
Then I reference this object in my render function using the classNames library:
render(){
const { classes } = this.props;
return(
<div className={styles.root}>
<CssBaseline />
<Drawer
variant="permanent"
className={classNames(classes.drawer, {
[classes.drawerOpen]: this.state.open,
[classes.drawerClose]: !this.state.open
})}
classes = {{
paper: classNames({
background:classes.background,
[classes.drawerOpen]: this.state.open,
[classes.drawerClose]: !this.state.open
})
}}
However, when I run this on localhost, the paper is still a plain old white. Am I missing something about the classNames library or is a special case of the paper tag? Thanks in advance and let me know if I should supply more info than this.
You have a couple issues in the code shown in your question.
For your styles, you need something more like the following:
const styles = theme => ({
drawerPaper: { background: "blue" }
});
In this case, "drawerPaper" is the key for my class name and then the object to the right contains the CSS properties for that class. When passed into withStyles, this will generate CSS like the following:
<style>
.classname-generated-for-drawerPaper-key: {
background: blue;
}
</style>
You had a class name key of "background" with the string "BLUE" as the CSS properties which will end up with CSS like the following:
<style>
.classname-generated-for-background-key: {
0: B;
1: L;
2: U;
3: E;
}
</style>
which of course is not valid CSS and will have no effect on the paper.
The second issue is in how you specify the classes:
classes = {{
paper: classNames({
background:classes.background,
[classes.drawerOpen]: this.state.open,
[classes.drawerClose]: !this.state.open
})
}}
When you pass an object to classNames, the keys of the object are the class names and the associated value controls (based on whether it is falsey or truthy) whether the class name should be included. With the syntax you used, classes.background will always be truthy which means that the class "background" (rather than the generated class name in classes.background) will be included which will have no effect since a "background" class hasn't been defined.
Instead you should have:
classes = {{
paper: classNames(classes.drawerPaper, {
[classes.drawerOpen]: this.state.open,
[classes.drawerClose]: !this.state.open
})
}}
which will unconditionally include classes.drawerPaper.
Here is a modified version of one of the Drawer demos, but with the background color of the drawer changed to blue: https://codesandbox.io/s/wqlwyk7p4l
If you're using global theme = createTheme( the color of a background paper can be set as following
const theme = createTheme({
palette: {
{
primary: colors.blue,
background: {
default: colors.grey[50],
paper: colors.common.white,
},
// ...

How to scroll to text input on focus when there is an input accessory with React Native

I'm working on a React Native app and many screens has forms with text input fields.
When I press the text input, the keyboard opens. I created a floating InputAccessory component which appears at the top of the keyboard to dismiss it, with the button "Done" on it.
However now that I have this accessory, when I click an input field or press the "Next" button on the keyboard to go to the next field, the ScrollView scrolls to align the bottom of the text input with the top of the keyboard. With this floating accessory it poses problems as you can see below you can't see the content of the text input because of this accessory, and I'd like to have the scrollview scrolling a bit more to display the entire text input.
I could probably do the calculation for this and run the .scrollTo() method from the ScrollView component but this pattern is very common to my entire app and I'm looking for an elegant solution that could be generic enough every single time I import a text input and focus on it.
Do you have any suggestion?
Thanks
I got the same issue before and i have 2 different solutions , Both of them worked for me.
1- Using react-native-keyboard-aware-scroll-view , Note that this library will already contain scrollView so you remove your own scroll view and use
<KeyboardAwareScrollView>
<View>
<TextInput />
</View>
</KeyboardAwareScrollView>
You can also check documentation for more info.
This solution is easier as you don't need to handle anything by yourself, but i think you will have some issues if you want to include scrollView inside it.
2- I once created a component AvoidKeyboard that actually does something similar to your solution, but it used to translate top the whole view with the keyboard height value, this solution worked perfectly also for me.
Implementation
import React, { Component } from 'react';
import { Animated, Easing, Keyboard } from 'react-native';
import PropTypes from 'prop-types';
class AvoidKeyboard extends Component {
constructor(props) {
super(props);
this.state = {
animatedViewHeight: new Animated.Value(0),
viewHeight: 0,
};
this.setViewHeightOnce = this.setViewHeightOnce.bind(this);
this.keyboardWillShow = this.keyboardWillShow.bind(this);
this.keyboardWillHide = this.keyboardWillHide.bind(this);
this.keyboardDidShowListener = Keyboard.addListener('keyboardWillShow', this.keyboardWillShow);
this.keyboardDidHideListener = Keyboard.addListener('keyboardWillHide', this.keyboardWillHide);
}
componentWillUnmount() {
this.keyboardDidShowListener && this.keyboardDidShowListener.remove();
this.keyboardDidHideListener && this.keyboardDidHideListener.remove();
}
setViewHeightOnce(event) {
const { height } = event.nativeEvent.layout;
if (this.state.viewHeight === 0) {
const avoidPaddingBottom = 15;
this.setState({
viewHeight: height + avoidPaddingBottom,
animatedViewHeight: new Animated.Value(height + avoidPaddingBottom),
});
}
}
keyboardWillShow(e) {
const { viewHeight } = this.state;
if (viewHeight) {
requestAnimationFrame(() => { // eslint-disable-line no-undef
Animated.timing(this.state.animatedViewHeight, {
toValue: (viewHeight - e.endCoordinates.height),
duration: 200,
delay: 0,
easing: Easing.inOut(Easing.ease),
}).start();
});
}
}
keyboardWillHide() {
requestAnimationFrame(() => { // eslint-disable-line no-undef
Animated.timing(this.state.animatedViewHeight, {
toValue: this.state.viewHeight,
duration: 200,
delay: 0,
easing: Easing.inOut(Easing.ease),
}).start();
});
}
render() {
let animatedHeight;
const { viewHeight } = this.state;
if (viewHeight > 0) {
animatedHeight = { maxHeight: this.state.animatedViewHeight };
}
return (
<Animated.View
style={[{ flex: 1, justifyContent: 'flex-end' }, animatedHeight]}
onLayout={this.setViewHeightOnce}
>
{this.props.children}
</Animated.View>
);
}
}
AvoidKeyboard.propTypes = {
children: PropTypes.node.isRequired,
};
export default AvoidKeyboard;
Now you just need to wrap your component or screen inside AvoidKeyboard and your screen height will shrink once keyboard is open, and you will be able to scroll the screen
I have had a lot of problems with keyboard in IOS. No KeyboardSpacer, react-native-keyboard-aware-scroll-view and more packages solved it.
Recently I discovered react-native-keyboard-manager and it solved all my problems without one line of code, also in modals and more (I don't have nothing to do with the author, but this package saved me the day). Give it a change.
I found a solution which doesn't involve hacky animation change.
When the keyboard opens, what I decided to do is to add some margin at the bottom of the ScrollView which correspond to the height of the InputAccessory. I then remove this margin when the keyboard closes. It looks like something like this:
import KeyboardListener from 'react-native-keyboard-listener';
...
render() [
<ScrollView
key={1}
style={{ marginBottom: this.state.scrollViewMarginBottom }}
/>,
<InputAccessory key={2} onLayout={...} />,
<KeyboardListener
key={3}
onWillShow={() => this.setState({ scrollViewMarginBottom: inputAccessoryHeight });
onWillHide={() => this.setState({ scrollViewMarginBottom: 0 })
/>
]
I was facing the same issue and reading online I figured out the following solution
For Android
Go to your AndroidManifest.xml and add android:windowSoftInputMode="adjustPan"
<activity
android:name=".MainActivity"
android:windowSoftInputMode="adjustPan">
.....
</activity>
For IOS
Just follow the instructions in this repo.
https://github.com/douglasjunior/react-native-keyboard-manager.
Hope this helps. :)

Categories

Resources