JSFiddle - React, Tailwind, and code Playground

[ASSIGNMENT] Close Frontend Application Quiz

by yelton

HTML

<script src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<div id="root"></div>

CSS

body {
  font-family: -apple-system,BlinkMacSystemFont,Segoe UI,Ubuntu,Helvetica,Arial,sans-serif;
}

.List {
  margin: 16px;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  grid-auto-rows: 60px;
  grid-gap: 16px;
}

.List__item {
  line-height: 60px;
  text-align: center;
  color: white;
  text-shadow: 1px 1px rgba(0, 0, 0, 0.5);
  box-shadow: 0px 1px 1px 0px rgba(0, 0, 0, 0.05);
}
.List__item:hover {
  box-shadow: 0 0 0 3px rgba(0, 0, 0, 0.1);
}
.List__item--active, .List__item--active:hover {
  box-shadow: 0 0 0 3px rgba(0, 0, 0, 0.2);
}

/* Taken from https://clrs.cc/ */

.List__item--navy {
  background-color: #001f3f;
}

.List__item--blue {
  background-color: #0074d9;
}

.List__item--aqua {
  background-color: #7fdbff;
}

.List__item--teal {
  background-color: #39cccc;
}

.List__item--olive {
  background-color: #3d9970;
}

.List__item--green {
  background-color: #2ecc40;
}

.List__item--lime {
  background-color: #01ff70;
}

.List__item--yellow {
  background-color: #ffdc00;
}

.List__item--orange {
  background-color: #ff851b;
}

.List__item--red {
  background-color: #ff4136;
}

.List__item--maroon {
  background-color: #85144b;
}

.List__item--fuchsia {
  background-color: #f012be;
}

.List__item--purple {
  background-color: #b10dc9;
}

.List__item--black {
  background-color: #111111;
}

.List__item--gray {
  background-color: #aaaaaa;
}

.List__item--silver {
  background-color: #dddddd;
}


/* Selected items */
.List__selection {
  margin: 16px;
  font-size: 12px;
}

React

// Implement a feature to allow item selection with the following requirements:
// 1. Clicking an item selects/unselects it.
// 2. Multiple items can be selected at a time.
// 3. Make sure to AVOID UNNECESSARY RE-RENDERS (performance).
// 4. Currently selected items should be visually highlighted.
// 5. Currently selected items' names should be shown at the top of the page.

class List extends React.Component {
	constructor (props) {
  	super(props);
    
    // Note: Used Set to store selected items as it has better time complexity
    this.state = {
    	selected: new Set(),
    }
  }
  
  onSelect(item) {
	  // console.log(item);
  	const { selected } = this.state;
    if(selected.has(item.name)) {
	    selected.delete(item.name);
    } else {
    	selected.add(item.name);
    }
    this.setState({ selected });
  }
  
  renderSelectedItems() {
    const items = [];
    
    // Note: We could add the item name as a component,
    // but I preferred to use comma separated list in a 
    // single line to prevent shifting the grid on every de/selection.
    // This way if you turn on paint-flashing, you can notice the diff rerender
		for(const item of this.state.selected) {
      items.push(item);
    }
    return (
    	<div className="List__selection">
        { items.length ? <b>Selected items: </b> : null }
        { items.join(', ') }
      </div>
    );
  }
  
  // Note: We could use classnames for better implementation of dynamic classname
  render() {
    const { items } = this.props;
    const { selected } = this.state;
  	return (
    	<div>
        { this.renderSelectedItems() }
        <ul className="List">
          {items.map(item => (
            <li 
              onClick={this.onSelect.bind(this, item)} 
              key={item.name} 
              className={`List__item List__item--${item.color}${selected.has(item.name) ? ' List__item--active' : ''}`}>
              {item.name}
            </li>
          ))}
        </ul>
      </div>
    )
 ...