I'm starting to understand to work with Material Ui with React, I'm getting difficult to customize the components.
I have this example of the AppBar:
import React from 'react';
import AppBar from 'material-ui/AppBar';
import IconButton from 'material-ui/IconButton';
import NavigationClose from 'material-ui/svg-icons/navigation/close';
import FlatButton from 'material-ui/FlatButton';
const styles = {
title: {
cursor: 'pointer',
},
};
const AppBarExampleIconButton = () => (
<AppBar
title={<span styles={styles.title}>Portofolio</span>}
iconElementRight={<FlatButton label="Save" />} />
);
export default AppBarExampleIconButton;
I can customize the title, but I want to customize the AppBar, in the documentation the Style object Override the inline-styles of the root element. But I'm not understanding out it works, could someone help me?
Depend on of what you try to do you can customize the AppBar in a multiple way. One of them is if you only want to change the color etc to make a theme.js file and import it inside MuiThemeProvider
You do this in the root file of your app. Ex
// Material Setup
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
import getMuiTheme from 'material-ui/styles/getMuiTheme';
// Our Own Theme
import theme from './layout/theme';
const Root = () =>
<MuiThemeProvider muiTheme={getMuiTheme(theme)}>
<YourApp />
</MuiThemeProvider>;
SO if you want to do this inline like you say you make a object inside your styles object who is the css you want to apply to the appbar.
const styles = {
appbar: {
backgroundColor: 'blue'
}
}
And you call it as a props for the AppBar component
<AppBar style={styles.appbar} />
Also if you look at the docs here you can see the title have is own style props for him call titleStyle
Hope that can help you figured out.
Related
I am trying to write the tests for the NavBar component (using react-native-testing-library) that has several buttons that are basically just icons (using ui-kitten for react native). So I can't get these buttons by text (as there is none) but other methods didn't work for me either (like adding accesibilityLabel or testID and then getting by the label text / getting by test ID). Any ideas what I am doing wrong?
// NavBar.tsx
import React from 'react';
import {View, StyleSheet} from 'react-native';
import {HomeBtn, SaveBtn} from '../components/buttons';
import UserSignOut from './UserSignOut';
const NavBar = ({
navigation,
pressHandlers,
}) => {
return (
<View style={styles.navBar}>
<View>
<HomeBtn navigation={navigation} />
<SaveBtn pressHandler={pressHandlers?.saveBtn ?? undefined} />
</View>
<UserSignOut />
</View>
);
};
export default NavBar;
// HomeBtn.tsx
import React from 'react';
import {Button} from '#ui-kitten/components';
import {HomeIcon} from '../shared/icons';
import styles from './Btn.style';
export const HomeBtn = ({navigation}: any) => {
return (
<Button
accesibilityLabel="home button"
style={styles.button}
accessoryLeft={props => HomeIcon(props, styles.icon)}
onPress={() => navigation.navigate('Home')}
/>
);
};
// NavBar.test.tsx
import React from 'react';
import {render, screen} from '#testing-library/react-native';
import * as eva from '#eva-design/eva';
import {RootSiblingParent} from 'react-native-root-siblings';
import {EvaIconsPack} from '#ui-kitten/eva-icons';
import {ApplicationProvider, IconRegistry} from '#ui-kitten/components';
import NavBar from '../../containers/NavBar';
describe('NavBar', () => {
const navBarContainer = (
<RootSiblingParent>
<IconRegistry icons={EvaIconsPack} />
<ApplicationProvider {...eva} theme={eva.light}>
<NavBar />
</ApplicationProvider>
</RootSiblingParent>
);
it('should render the buttons', async () => {
render(navBarContainer);
// this test fails (nothing is found with this accesibility label)
await screen.findByLabelText('home button');
});
});
Query predicate
The recommended solution would be to use:
getByRole('button', { name: "home button" })
As it will require both the button role, as well as check accessibilityLabel with name option.
Alternative, but slightly less expressive way would be to use:
getByLabelText('home button')
This query will only check accessibilityLabel prop, which also should work fine.
Why is query not matching
Since you're asking why the query is not working, that depends on your test setup. It seems that you should be able to use sync getBy* query and do not need to await findBy* query, as the HomeBtn should be rendered without waiting for any async action.
What might prevent that test from working could be incorrect mocking of any of the wrapping components: RootSiblingParent, ApplicationProvider, they might be "consuming" children prop without rendering it. In order to diagnose the issue you can use debug() function from RNTL to inspect the current state of rendered components. You can also run your tests on render(<NavBar />) to verify that.
Does await screen.findByA11yLabel('home button') work? It should match the accessibilityLabel prop.
I am currently trying to render a delete icon on an Material UI component, to be more specific, the MenuItem component. I have created a state field in the component's state and set it to false(this.state.isHovering). The idea is that when I hover over the account item, I want to render the delete icon, and when I hover off, it is supposed to go away. I impleneted these event listeners using onMouseEnter and onMouseLeave and attached them over to the MenuItem. I then made it so that the icon only shows when this.state.isHovering is true. However, when I hover over the MenuItem, it does not render. However, if I just implemented the icon without having the state get involved, it renders. I also console logged in the event handlers to make sure that the event is being registered and it does console log both when hovering in and out. Please see my code. Thanks!
handleMouseEnter = () => {
this.setState({
isHovering: true,
})
}
handleMouseLeave = () => {
this.setState({
isHovering: true,
})
}
I think there are a few issues with your code:
the state is never defined
you don't properly update the state
You don't properly conditionally check/use the state
Lots of bugs in the code you provided (looks like an incomplete copy paste)
You can use reactHooks to help define your state and setState functions
This is my best answer, but the code you provided is missing the account variable and a lot of the referenced functions, so it is incomplete. But it should help with your state issues
import React, { Component, Fragment, useState } from 'react';
import { Typography, withStyles } from '#material-ui/core';
import PropTypes from 'prop-types';
import Paper from '#material-ui/core/Paper';
import Grid from '#material-ui/core/Grid';
import Table from '#material-ui/core/Table';
import TableBody from '#material-ui/core/TableBody';
import TableCell from '#material-ui/core/TableCell';
import MenuItem from '#material-ui/core/MenuItem';
import Icon from '#material-ui/core/Icon';
import AccountTypeIcon from './AccountTypeIcon';
class PortDashSideBar extends Component {
const [isHovering, setIsHovering] = useState(false);
handleMouseEnter = () => {
setIsHovering(true);
}
handleMouseLeave = () => {
setIsHovering(false);
}
return <MenuItem
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
className={account.id === currentAccountId ? classes.selectedAccount : classes.unselectedAccount}
hover
name={account.name}
key={account.id}
onClick={ () => this.setCurrentAccount(account.id) }>
<TableCell className={classes.cellAccountIcon}>
<AccountTypeIcon type={this.getAccountById(account.id).type} />
</TableCell>
<TableCell className={classes.cellName}>
<Typography className={classes.accountName}>
{account.label}
</Typography>
<Typography>
<span>
{this.getHoldingsForAccount(account.id)}
</span>
{
isHovering ? <Icon>delete</Icon> : null
}
</Typography>
</TableCell>
</MenuItem>;
}
I am making a library of react components in storybook (v6.1.14)
I have two different themes, basically and light and dark version.
I wrap all of my stories in the <ThemeProvider> like this:
import React from "react"
import {dark} from "../src/Themes/Theme";
import { ThemeProvider } from "styled-components";
import { GlobalStyles } from "../src/lib/Themes";
// apply our projects theme to our stories
const ThemeDecorator = storyFn => (
<ThemeProvider theme={dark}>
<GlobalStyles />
{storyFn()}
</ThemeProvider>
)
export default ThemeDecorator;
This is applied through the preview.js file like so:
const { addDecorator } = require("#storybook/react");
import ThemeDecorator from "./themeDecorator"
// Add our project theme, add a global stylesheet
addDecorator(ThemeDecorator);
So now everything has the dark theme applied, however, for each component I want to show two stories: One for the light theme and one for the dark theme - how do I make a story take on a different theme to the one globally declared?
I had the same problem as well. From the Storybook docs, I found this answer.
So basically, you can use decorators with your template and apply styling to it.
const Template: Story<LogoProps> = (args) => {
const { version } = args;
return <Logo {...args} version={version} />;
};
export const Light = Template.bind({});
Light.args = {
version: 'light',
};
Light.decorators = [
(LightLogo) => (
<div style={{ backgroundColor: 'black', padding: '5px 7px' }}>
<LightLogo />
</div>
),
];
Material UI version: v0.20.0
I am trying to assign leftAvatar value via CustomAvatar component but it is not aligning as you can see in attached screenshot. Please help.
CustomAvatar: This component is working on some condition bases like if image is available the its
MemberList/index.js
import React, {Component} from 'react';
import {withRouter} from 'react-router-dom';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
import {List, ListItem} from 'material-ui/List';
import IconMenu from 'material-ui/IconMenu';
import MenuItem from 'material-ui/MenuItem';
import CustomAvatar from 'routes/CustomAvatar';
class MemberList extends Component {
render(){<MuiThemeProvider>
<List>
<ListItem
leftAvatar={<CustomAvatar avatarPic={false}/>}
primaryText={"Mike Tailor"}
secondaryText={"This is first text"}
secondaryTextLines={1}
rightIconButton={<IconMenu iconButtonElement={iconButtonElement}>
<MenuItem>Add friend</MenuItem>
<MenuItem>Chat</MenuItem>
</IconMenu>}/>
<ListItem
leftAvatar={<CustomAvatar avatarPic={true}/>}
primaryText={"Kory Becker"}
secondaryText={"This is second text"}
secondaryTextLines={1}
rightIconButton={<IconMenu iconButtonElement={iconButtonElement}>
<MenuItem>Add friend</MenuItem>
<MenuItem>Chat</MenuItem>
</IconMenu>}/>
</List>
</MuiThemeProvider>}
}
export default withRouter(MemberList);
CustomAvatar/index.js
import React, {Component} from 'react';
import {withRouter} from 'react-router-dom';
import Avatar from 'material-ui/Avatar';
class CustomAvatar extends Component {
render(){
if(this.props.avatarPic){
return(<Avatar size={40} src={"http://www.example.com/myimage.png"} />)
}else{
return(<Avatar size={40}>A</Avatar>)
}
}
}
export default withRouter(CustomAvatar);
The cause of your problem
Your problem is caused because the material-ui v0 library expects the leftAvatar prop to be an Avatar component, and so relies on internal values of Avatar behind the scenes. Since your CustomAvatar is not directly an Avatar, material-ui does not find these internals and so the styling does not work.
Specifically, if you take a look at the source of ListItem, you'll notice a pushElement function that takes child components like leftAvatar and assigns styling by setting the style prop:
pushElement(children, element, baseStyles, additionalProps) {
if (element) {
const styles = Object.assign({}, baseStyles, element.props.style);
children.push(
React.cloneElement(element, {
key: children.length,
style: styles,
...additionalProps,
})
);
}
}
Your CustomAvatar makes no use of this style prop, so you never receive the necessary CSS styling. That's the cause of your layout issues.
You have a couple of options to fix this depending on whether you are willing to upgrade to v1 or not.
Code that fixes it
class CustomAvatar extends Component {
render() {
const { showPicture, ...other } = this.props;
if (showPicture) {
return (<Avatar size={40} {...other} src={"http://www.example.com/myimage.png"} />);
} else {
return (<Avatar size={40} {...other}>A</Avatar>);
}
}
}
As discussed above, the pushElement function sets the style prop. Right now, you're not using it, so your Avatars get no styling. The {...other} spreads this prop down into your Avatars so that they get the right styling and your layout works.
But, you should upgrade to v1
v1 should have a stable release in the early quarters of 2018, and it fixes a lot of these kinds of problems. Instead of spending time working through these kinds of issues and working with v0, you should upgrade and learn the new (and, imho, improved) way.
I also have faced same problem you could fix this by wrap your custom compoent into PAPER component of material-ui. Please see code below:
import React, {Component} from 'react';
import {withRouter} from 'react-router-dom';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
import {List, ListItem} from 'material-ui/List';
import IconMenu from 'material-ui/IconMenu';
import MenuItem from 'material-ui/MenuItem';
import CustomAvatar from 'routes/CustomAvatar';
import Paper from 'material-ui/Paper';
class MemberList extends Component {
render(){<MuiThemeProvider>
<List>
<ListItem
leftAvatar={<Paper zDepth={2} circle={true}><CustomAvatar avatarPic={false}/></Paper>}
primaryText={"Mike Tailor"}
secondaryText={"This is first text"}
secondaryTextLines={1}
rightIconButton={<IconMenu iconButtonElement={iconButtonElement}>
<MenuItem>Add friend</MenuItem>
<MenuItem>Chat</MenuItem>
</IconMenu>}/>
<ListItem
leftAvatar={<Paper zDepth={2} circle={true}><CustomAvatar avatarPic={true}/></Paper>}
primaryText={"Kory Becker"}
secondaryText={"This is second text"}
secondaryTextLines={1}
rightIconButton={<IconMenu iconButtonElement={iconButtonElement}>
<MenuItem>Add friend</MenuItem>
<MenuItem>Chat</MenuItem>
</IconMenu>}/>
</List>
</MuiThemeProvider>}
}
export default withRouter(MemberList);
I'm building a page and I want a material-ui element to have a background image using background-image CSS property. I have googled for it of course, and there are solutions but for some reason I can't see that image.
P.S.1: even changing that MUI element to regular hasn't helped me at all.
P.S.2: when I'm using inside container it shows, but that's not what I want.
UPDATE1: Tried adding height and width to container, still no luck...
import React from 'react';
import Paper from 'material-ui/Paper';
import IconButton from 'material-ui/IconButton';
import ActionHome from 'material-ui/svg-icons/action/home';
const styles = {
paperContainer: {
backgroundImage: `url(${"static/src/img/main.jpg"})`
}
};
export default class Home extends React.Component{
render(){
return(
<Paper style={styles.paperContainer}>
</Paper>
)
}
}
You have to import the image as the following, using the relative path.
import React from 'react';
import Paper from 'material-ui/Paper';
import IconButton from 'material-ui/IconButton';
import ActionHome from 'material-ui/svg-icons/action/home';
import Image from '../img/main.jpg'; // Import using relative path
const styles = {
paperContainer: {
backgroundImage: `url(${Image})`
}
};
export default class Home extends React.Component{
render(){
return(
<Paper style={styles.paperContainer}>
Some text to fill the Paper Component
</Paper>
)
}
}
I've found a fix for my case. Actually setting container height in pixels have helped.
Here's the code:
import React from 'react';
const styles = {
paperContainer: {
height: 1356,
backgroundImage: `url(${"static/src/img/main.jpg"})`
}
};
export default class Home extends React.Component {
render() {
return (
<div style={styles.paperContainer}>
</div>
)
}
}
I got this to work for material-ui, where the padding on my parent element was 24px so I added 48px to the width of the background image to make it work...
const styles = {
heroContainer: {
height: 800,
backgroundImage: `url(${"../static/DSC_1037.jpg"})`,
backgroundSize: 'cover',
backgroundPosition: 'center',
width: `calc(100vw + 48px)`,
margin: -24,
padding: 24,
}
};
<Grid
container
direction="column"
justify="flex-end"
alignItems="right"
style={styles.heroContainer} >
<Grid item>Goes here</Grid>
</Grid>
Had the same issues while working with Material UI React and the Create React App. Here is the solution that worked for me. Note that I set up a webpack alias for the relative path
import BackgroundHeader from "assets/img/BlueDiamondBg.png"
const BackgroundHead = {
backgroundImage: 'url('+ BackgroundHeader+')'
}
<div style={BackgroundHead}>
Like Romainwn said, you need to import the image to the file. Make sure you use the relative path to parent, so instead of
static/src/img/main.jpg #looks for static folder from current file location
Do
/static/src/img/main.jpg #looks for file from host directory:
Another hack to do it would be adding an inline style tag to the component:
import React from 'react';
import Paper from 'material-ui/Paper';
import IconButton from 'material-ui/IconButton';
import ActionHome from 'material-ui/svg-icons/action/home';
import Image from '../img/main.jpg'; // Import using relative path
export default class Home extends React.Component{
render(){
return(
<Paper style="background:path/to/your/image;">
</Paper>
)
}
}
You can you sx props in MUI v5
import React from 'react';
import Paper from 'material-ui/Paper';
import Image from '../img/main.jpg';
export default class Home extends React.Component{
render(){
return(
<Paper sx={{ backgroundImage: `url(${Image})` }}>
</Paper>
)
}
}