2
\$\begingroup\$

I have an app which shows a welcome page with an animal's picture. Also, we can navigate to a second view, where there are two canvas, and a collapsable panel which hosts controls' inputs.

The aim of the application would be to load an image on both canvas, and let a user click on it, and in the second one, the part would be coloured and its name would be written in a textarea.

Currently, I am experimenting with React because I have recently done a project with JS, jQuery, Bootstrap, and I thought it was a mess, because we inlined scripts in HTML.

I will describe how is it organized and then I will ask you for suggest:

index.html:

<!doctype html>

<html lang="es">
<head>
    <meta charset="utf-8">
    <title>Atlas</title>
    <meta name="description" content="Atlas">
    <meta name="author" content="">
</head>

<body>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"
      integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<div id="mainApp"></div>
</body>
</html>

As you can see it is just a boilerplate, and bootstrap.

My index.js React's entry point:

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import '../node_modules/react-dropdown/style.css';
import {Header} from './Header'
import {Main} from './Main'
import {BrowserRouter} from 'react-router-dom'


const App = () => {

    return (
        <div>
            <Header/>
            <Main/>
        </div>
    );
};

ReactDOM.render(
    <BrowserRouter>
        <App/>
    </BrowserRouter>

    , document.getElementById("mainApp"));

As you could see I have followed the pattern of dividing it into Header and Main, to use navigation and react router.

Header,which is a navbar:

import React from 'react';
import {Link} from "react-router-dom";
import {Navbar} from 'react-bootstrap'
import NavDropdown from "react-bootstrap/es/NavDropdown";
import Nav from "react-bootstrap/es/Nav";
import MenuItem from "react-bootstrap/es/MenuItem";

const Header = () => {
    return (
        <Navbar>
            <Nav>

                <NavDropdown eventKey={3} title="Atlas" id="basic-nav-dropdown">
                    <MenuItem eventKey={3.1}><Link to='/'>Inicio</Link></MenuItem>
                    <MenuItem eventKey={3.2}>
                        <li><Link to='/caballo'>Caballo</Link></li>
                    </MenuItem>
                </NavDropdown>
            </Nav>
        </Navbar>
    );
};

export {Header}

Main contains paths and Components to be renderer:

import React from 'react';
import {Inicio} from "./Inicio";
import {Caballo} from "./Caballo";
import Switch from "react-router-dom/es/Switch";
import Route from "react-router-dom/es/Route";

const Main = () => {
    return (
        <main>
            <Switch>
                <Route exact path='/' component={Inicio}/>
                <Route exact path='/caballo' component={Caballo}/>
            </Switch>
        </main>
    );
};

export {Main}

Inicio is the default img of the animal:

import React from 'react';
import './index.css';

const Inicio = () => {
    return (
        <img src="https://misanimales.com/wp-content/uploads/2017/09/cuarto-de-milla.jpg" alt=""/>
    );
};

export {Inicio};

And Caballo is where the most of the App is:

import React from 'react';
import './index.css';
import Dropdown from 'react-dropdown';
import '../node_modules/react-dropdown/style.css';
import {ImageUpload} from "./ImageUpload";
import Panel from "react-bootstrap/es/Panel";
import Button from "react-bootstrap/es/Button";


class Caballo extends React.Component {
    state = {open: true};
    options = [
        'one', 'two', 'three'
    ];

    render() {
        return (
            <main>
                <header>
                    <div style={{display: "flex"}}>
                        <h3>Suba una imagen desde el ordenador, para
                            mostrarla:
                        </h3>
                        <div style={{display: "flex", justifyContent: "flex-end", width: "100%"}}>
                            <Button
                                onClick={() => {
                                    this.setState({open: !this.state.open});
                                    const divCanvas = document.getElementsByClassName("previewComponent");
                                    const innerCanvas = document.getElementsByTagName("canvas");
                                    if (divCanvas && innerCanvas) {
                                        const width = divCanvas[0].offsetWidth;
                                        if (!this.state.open) {
                                            const newWidth = width - 200 + "px";
                                            divCanvas[0].setAttribute("style", `width:${newWidth}`);
                                            divCanvas[1].setAttribute("style", `width:${newWidth}`);
                                            innerCanvas[0].setAttribute("style", `width:${newWidth}`);
                                            innerCanvas[1].setAttribute("style", `width:${newWidth}`);
                                        }
                                        if (this.state.open) {
                                            const newWidth = width + 200 + "px";
                                            divCanvas[0].setAttribute("style", `width:${newWidth}`);
                                            divCanvas[1].setAttribute("style", `width:${newWidth}`);
                                            innerCanvas[0].setAttribute("style", `width:${newWidth}`);
                                            innerCanvas[1].setAttribute("style", `width:${newWidth}`);
                                        }
                                    }
                                }
                                }>
                                Cerrar controles
                            </Button></div>
                    </div>

                </header>
                <article className="single-line-canvas-spaced-controls-row">
                    <section className="single-line-canvas-row">
                        < section className="spaced-column" style={{
                            backgroundColor: 'gray',
                        }}>
                            <ImageUpload/>
                        </section>
                        <section className="spaced-column" style={{
                            backgroundColor: 'yellow',
                        }}>
                            <ImageUpload/>
                        </section>
                    </section>
                    <div className="spaced-column" style={{
                        backgroundColor: "green"
                    }}>
                        <br/>
                        <Panel
                            id="collapsible-panel-example-1"
                            expanded={this.state.open}>
                            <Panel.Collapse>
                                <Panel.Body>
                                    <Dropdown options={this.options} onChange={this._onSelect}
                                              placeholder="Select an option"/>
                                    <div className="row">
                                        <label><input type="checkbox" id="chbox1" value="1"/>Option 1</label>
                                        <label><input type="checkbox" id="chbox2" value="2"/>Option 2</label>
                                    </div>
                                    <div className="row">
                                        <label><input type="checkbox" id="chbox3" value="3"/>Option 3</label>
                                        <label><input type="checkbox" id="chbox4" value="4"/>Option 4</label>
                                    </div>
                                    <div>
                                        <Button>1</Button>
                                        <Button>2</Button>
                                        <Button>3</Button>
                                        <Button>4</Button>
                                    </div>
                                </Panel.Body>
                            </Panel.Collapse>
                        </Panel>
                    </div>

                    <div className="line-break"></div>

                </article>
                <footer><textarea style={{width: "100%"}} value="Estructura:" readOnly name="herarchy"
                                  id="1"></textarea>
                </footer>
            </main>
        )
    }
}

export {Caballo}

And finally, ImageUpload, are the divs and canvas for image preview:

import React from 'react';


class ImageUpload extends React.Component {

    render() {

        const $imagePreview = (
            <div className="previewText">Por favor seleccione una imagen de su ordenador para visualizarla</div>);
        return (
            <div className="previewComponent">
                <div className="imgPreview">
                    {$imagePreview}
                    <canvas></canvas>
                </div>
            </div>
        )
    }
}

export {ImageUpload};

As you see it is ugly, because the code in Caballo is too long.

  1. Control's button's onClick handler is too big: The purpose is to resize canvas when we close controls:

    • Is there an easier way to do this resize?
    • Should this handler be isolated in its file?
    • Is this the correct way to handle canvas' dimensions?
  2. CSS classNames

    First I thought that it is better to describe what it does, for example:

    'single-line-spaced-row'
    

    Then I thought it would be better if it refers to what it is used for:

    'canvas-controls'
    

    The I mixed them both:

    'single-line-canvas-spaced-controls-row'
    

    What approach is the correct one?

  3. HTML/React:

    There is a React component called Header which is the navbar to navigate, and there is a header where I put the title and control's close button.

    How could I improve this naming?

  4. UI's hierarchy:

    I thought it would be correct if I isolate canvas' row as article and each canvas as section to separate them from controls.

    Is there a better way to structure the UI?

  5. Logic is scattered:

    Caballo component is too big: header is for title, and close controls button; article/section are canvas; Panel represents controls' inputs; footer is meant to be the place where dynamically we write the animal's parts' names.

    Could we divide or simplify this Component?

In addition, the default page looks:

enter image description here

Canvas' page with controls opened:

enter image description here

Controls closed:

enter image description here

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.