React

by Abdul Ahmad

HTML

<div id="app"></div>

SCSS

body {
  background: #20262E;
  padding: 20px;
  font-family: Helvetica;
  box-sizing: border-box;
}

#app {
  background: #fff;
  border-radius: 4px;
  padding: 20px;
  transition: all 0.2s;
}

.accordion {
  max-width: 500px;
}

.accordion-step {
  border: 1px solid #ddd;
  border-radius: 5px;
  padding: 20px;
  margin-top: 5px;
  
  &:first-child {
    margin-top: 0;
  }
  
  .top-row {
    display: flex;
    justify-content: space-between;
    align-items: center;
  }
  
  .toggle {
    width: 15px;
    height: 15px;
    background: #ddd;
    border-radius: 50%;
    
    &:hover {
      cursor: pointer;
      background: teal;
    }
  }
  
  .title {
    font-weight: bold;
  }
  
  .content {
    margin-top: 10px;
  }
}

React

function SomeForm({ submitCallback }) {
	const handleSubmit = React.useCallback(e => {
  	e.preventDefault();
    submitCallback();
  }, []);

	return (
  	<form onSubmit={handleSubmit}>
  	  <button type='submit'>
  	    submit
  	  </button>
  	</form>
  );
}

function App() {
	return (
  	<Accordion
      initialExpandedItems={[0]}
      steps={[
      	{ title: 'Step 1', content: ({ submitCallback }) => <SomeForm submitCallback={submitCallback} /> },
        { title: 'Step 2', content: 'step 2 content' },
        { title: 'Step 3', content: 'step 3 content' },
      ]}
    />
  );
}

ReactDOM.render((<App />), document.querySelector("#app"));

function AccordionStep({ title, children, expanded, onClickToggle, onComplete, index }) {
	const handleClick = React.useCallback(() => {
  	onClickToggle({ index });
  }, [index, onClickToggle]);

	return (
  	<div className='accordion-step'>
      <div className='top-row'>
        <h3 className='title'>
          { title }
        </h3>
        <div className='toggle' onClick={handleClick} />
      </div>
      {
      	expanded && (
          <div className='content'>
            { children }
          </div>
        )
      }
  	</div>
  );
}

function Accordion({ steps, initialExpandedItems }) {
	const [expandedItems, setExpandedItems] = React.useState([ ...initialExpandedItems ]);
  const [enabledItems, setEnabledItems] = React.useState([...initialExpandedItems]);
  
  const toggle = React.useCallback(({ index } = {}) => {
  	const itemIsEnabled = enabledItems.findIndex(i => i === index) !== -1;
    if (!itemIsEnabled) return;
  	const expandedItemIndex = expandedItems.findIndex(i => i === index);
    let newExpandedItems;
    
    if (expandedItemIndex !== -1) {
    	newExpandedItems = [...expandedItems];
      newExpandedItems.splice(expandedItemIndex, 1);
    } else {
    	newExpandedItems = [...expandedItems, index];
    }
        
    setExpandedItems(newExpandedItems);
  }, [expandedItems]);
  
  const...