Utilize the global theme feature within React Material-UI to create a cohesive

I'm feeling a bit lost when it comes to setting up React Material-UI theme.

Even though I've tried to keep it simple, it's not working out for me as expected.

Here is the code snippet I have:

start.tsx

const theme = createMuiTheme({
    palette: {
        type: 'dark',
        primary: blue,
        secondary: lightGreen
    }
})

ReactDOM.render(
    <ThemeProvider theme={theme}>
        <BrowserRouter>
            <App />
        </BrowserRouter>
    </ThemeProvider>
    ,
    document.getElementById("root")
)

UserInterfaces.scan();

app.tsx

export class App extends React.Component<IProps, IState> {

    constructor(props) {
        super(props);
        this.state = {
            menu: null
        }
    }

    render() {
        if (!this.state.menu) {
            this.login();
            return <div>Loading ... </div>
        } else {
            return <div className="hx-top-frame">
                <div>
                    <MenuBar menuList={this.state.menu} />
                </div>
                <div>
                    Content here
                {/* <Content /> */}
                </div>
            </div>
        }
    }
}

menubar.tsx

export class MenuBar extends React.Component<IMenuProps, IMenuStates> {

    constructor(props) {
        super(props);
        this.state = { expanded: "" };
    }

    private setClose() {
        this.setState({ expanded: "" });
    }

    public render() {
        let menulist: IMenuArray[] = this.props.menuList.map<IMenuArray>(item => {
            return {
                path: item.path,
                icon: item.icon,
                link: Util.hyphenate(item.path)
            }
        })
        return <nav className="hx-menu">
            <Hidden smUp>
                <Drawer variant="temporary" anchor='left' open={this.state.expanded != ""} onClose={this.setClose.bind(this)} className="left-drawer">
                    <SubMenu menu={menulist} />
                </Drawer>
            </Hidden>
            <Hidden xsDown>
                <Drawer variant="permanent" anchor='left' open={this.state.expanded != ""} onClose={this.setClose.bind(this)} className="left-drawer">
                    <SubMenu menu={menulist}></SubMenu>
                </Drawer>
            </Hidden>
        </nav >
    }
}

submenu.tsx

class SubMenu extends React.Component<ISubMenuProps, IMenuStates> {

    constructor(props) {
        super(props);
        this.state = { expanded: "" };
    }

    public render() {
        let submenu: IMenuItems = {};
        let menuitems: IMenuArray[] = [];
        this.props.menu.forEach(menu => {
            let items = menu.path.split("/");
            let parent = items.length > 1;
            let name = items.shift();
            let child = items.join("/");
            if (!parent) {
                menuitems.push({ path: name, icon: menu.icon, link: menu.link });
            } else {
                if (!submenu[name]) submenu[name] = [];
                submenu[name].push({ path: child, icon: menu.icon, link: menu.link });
            }
        })

        return <List>
            {Object.keys(submenu).map(name => {
                let menu = name.split("/").shift();
                return <ListItem button className="hx-submenu" key={"m-" + name}>
                    <span className="hx-nowrap" onClick={() => { this.setState({ expanded: this.state.expanded == name ? "" : name }) }}>
                        <ListItemText primary={name} key={"t-" + name} />{this.state.expanded == name ? <ExpandLess /> : <ExpandMore />}
                    </span>
                    <Collapse in={this.state.expanded == name} timeout="auto" unmountOnExit className="hx-submenu">
                        <SubMenu menu={submenu[name]} />
                    </Collapse>
                </ListItem>
            })}

            {menuitems.map(item => {
                return <ListItem button component={RouterLink} to={item.link} key={"i-" + item.path}>
                    <ListItemText primary={item.path} className="hx-menu" key={"l-" + item.path} />
                </ListItem>
            })}

        </List>
    }
}

And now, my queries are:

  1. Why is the right panel light grey? How can I change it to blue?
  2. I want the drawer to stay open when there is enough window width. However, how do I prevent it from overlapping with the content part? And make it fixed on the left?
  3. How can I ensure the menu expands vertically instead of horizontally?
  4. I don't want to style each component individually. Is it possible to use a global theme throughout the project without customizing components? Even after following the documentation, it doesn't work as expected since my projects are based on classes rather than functions.

Code sandbox link: https://codesandbox.io/embed/theme-test-gwutc

Thank you.

Answer №1

I will provide responses for questions 1 and 4 at this time, as I am not yet aware of the answers for questions 2 and 3.

  1. The panel appears as light gray because it is set to the default color of Paper (Drawer utilizes Paper in its design).
    Paper is not influenced by palette.primary.
    To globally change the paper background color, you can implement the following:

    const theme = createMuiTheme({
      palette: {
        type: "dark",
        primary: blue,
        secondary: lightGreen,
        background: {
          paper: "blue" // drawers (and papers) will be blue due to this setting.
        }
      }
    });
    
  2. You can easily apply a global theme to all material-ui components. You are already achieving this by wrapping your app with ThemeProvider; you just need to adjust your theme accordingly.
    For instance, if you wish to override the default background color of drawers, you can do so as follows (a better alternative to option 1 above, as it does not affect paper components):

    const theme = createMuiTheme({
      overrides: { // you specify that you are overriding default material-ui styles
       MuiDrawer: {
         paper: {
           backgroundColor: 'blue',
         }
       }
      }
    });
    

Answer №2

I successfully managed to find solutions for all the challenges. I followed @ido's instructions for problems Number 1 and 4.

For Problem Number 2, the solution involved adding display:flex and setting a fixed width for the paper, following @ido's advice.

And now, here is the resolution for Problem Number 3.

submenu.tsx

.
.
<li>
    <ListItem button className="hx-submenu" key={"m-" + name}>
        <ListItemText primary={name} key={"t-" + name} onClick={() => {
            this.setState({
                expanded: this.state.expanded == name ? "" : name
            });
        }}
        />
        {this.state.expanded == name ? <ExpandLess /> : <ExpandMore />}
    </ListItem>
    <Collapse
        in={this.state.expanded == name}
        timeout="auto"
        unmountOnExit
        className="hx-submenu"
    >
        <SubMenu menu={submenu[name]} />
    </Collapse>
</li>
.
.

The issue was due to placing <Collapse> within <ListItem>. Consequently, the unnecessary <span> was removed as it served no purpose.

The updated codepen now reflects the corrected version.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Are reflection problems a concern when using type-graphql mutations?

Recently, I've been experimenting with integrating type-graphql into my nodejs project. While implementing @Query methods went smoothly, I'm facing challenges with the following code snippet in combination with Moleculer service. @Mutation() / ...

Is it possible to easily organize a TypeScript dictionary in a straightforward manner?

My typescript dictionary is filled with code. var dictionaryOfScores: {[id: string]: number } = {}; Now that it's populated, I want to sort it based on the value (number). Since the dictionary could be quite large, I'm looking for an in-place ...

NextJS partial render does not have access to context information

Since transitioning to the NextJS app router, I've been encountering an error from Material-UI. Error: MUI: `useColorScheme` must be called under <CssVarsProvider /> This issue arises when using useColorScheme outside of <CssVarsProvider /&g ...

Using React's higher order component (HOC) in TypeScript may trigger warnings when transitioning from non-TypeScript environments

I have a simple HOC component implemented in React with TypeScript. export const withFirebase = <P extends object>( Component: React.ComponentType<P> ) => class WithFirebase extends React.Component<P> { render() { return ...

Tips for modifying only one property after receiving an object from an HTTP GET request

Within my Angular application, I have defined an object structure like this: export class Article { id: number; author: number; title: string; content: string; date: Moment; readingTime: number; draft: boolean; constructor(obj: Partial< ...

Utilize Material-UI to display data retrieved from axios (image may not be visible)

An issue with the display of my code image has arisen. Interestingly, the Axios calls work effectively when I console them. import { Container, Grid, Paper } from '@mui/material'; import { useEffect, useState } from 'react'; import { st ...

Formik Fields with unique key properties

When mapping text fields, I follow this structure: { AddVehicleFields.map(({formikRef, ...input}) => ( <> <TextField key={formikRef} helperText={ getIn(formik.touched, formikRef) ? getIn(formik. ...

Angular 6 Checkbox Selector - Filtering Made Easy

How can I filter a list of JSON objects (Products) by the 'category' variable using checkboxes? An example product object is shown below: { 'bikeId': 6, 'bikeName': 'Kids blue bike', 'bikeCode': ...

Guidance on transferring information from a parent component to an Angular Material table child component

Currently, I am implementing an angular material table with sorting functionality. You can view the example here: Table Sorting Example I intend to transform this into a reusable component so that in the parent component, all I have to do is pass the colu ...

Uploading Images to Imgur with Angular 4

As a newcomer to TypeScript, I am faced with the challenge of uploading an image to the Imgur API using Angular. Currently, my approach involves retrieving the file from a file picker using the following code: let eventObj: MSInputMethodContext = <MSIn ...

Previous states in TypeScript

Just starting out with typescript and trying to work with user files in order to update the state. Currently facing a typescript error that I can't seem to figure out - Error message: Argument of type '(prev: never[]) => any[]' is not as ...

Adjust the border color of Material UI's DatePicker

Hello everyone, I am currently working with a DatePicker component from Material UI. My main goal is to change the border color of this component. I have attempted various methods such as modifying classes, adjusting the theme's primary color, and uti ...

Inject Angular 2 component into designated space

I am working on a website that requires a settings dialog to be loaded in a designated area upon clicking a button. The settings dialog is a component that retrieves data from REST endpoints. I am hesitant to simply insert the component and hide it as I ...

Does Material-UI MenuItem pass arguments to the onClick handler function?

I am currently working with a search.js file and a search-date.js file. Within the search.js file, there is a container called SearchDate which I render. However, I'm puzzled by the behavior of the MenuItem component when it is clicked. The function ...

What could be causing the availability of a response in a service, but showing as undefined in the component?

Currently, I am facing a problem with my service and component setup. While the service can successfully read the response as a JSON object, the component is returning res: undefined. service: constructor( private http: Http, private fbuilder: Fo ...

Determining the length of an array of objects nested within another object

I received a response from the API called res. The response is in the following format: {"plan":[{"name":"ABC"},{"name":"DEF"}]}. I am attempting to save this response in my TypeScript code as shown below: ...

Exploring the method to deactivate and verify a checkbox by searching within an array of values in my TypeScript file

I am working on a project where I have a select field with checkboxes. My goal is to disable and check the checkboxes based on values from a string array. I am using Angular in my .ts file. this.claimNames = any[]; <div class="row container"> ...

Leveraging Vue.js and TypeScript: accessing the type of the child component through refs

In my parent component, I have a child component named with a reference passed to it: <child ref="childRef" /> When trying to execute a function inside the child component from the parent component, I face some challenges: mounted() { ...

Mapping an array based on its individual values

How can I sum values in an array of objects based on a specific condition? [{amount:100, prefix:'a'},{amount:50, prefix:'b'},{amount:70, prefix:'a'},{amount:100, prefix:'b'}] Is there a method to map and calculate t ...

To allow users to sign in, the mutation can be filtered based on the boolean value of `isVerified

How can I filter and only allow users with isVerified === true to sign in? If it's false, the user should not be able to sign in through the mutation. Here is my code for the mutation: signin: async ( _: any, { credentials }: SignInArgs, ...