morphizm/frontend-project-lvl3

View on GitHub
src/view/index.js

Summary

Maintainability
A
2 hrs
Test Coverage
import { watch } from 'melanke-watchjs';
import i18next from 'i18next';

const renderItems = (items) => {
  const result = items.map((item) => {
    const { text, link } = item;
    const html = `
      <li class="list-group-item">
        <a href="${link}" target="_blank">${text}</a>
      </li>
    `;
    return html;
  }).join('');

  return `
    <ul class="list-group list-group-flush">
      ${result}
    </ul>
  `;
};

const renderSpinner = (element) => {
  const div = document.createElement('div');
  div.classList.add('spinner-border');
  div.setAttribute('role', 'status');
  const span = `<span class="sr-only">${i18next.t('loading')}...</span>`;
  div.innerHTML = span;
  element.prepend(div);
};

const removeSpinner = () => {
  const spinnerElement = document.querySelector('.spinner-border');
  if (spinnerElement) {
    spinnerElement.remove();
  }
};

const renderErrorAlert = (element, message) => {
  const errorElement = document.querySelector('[type="error"]');
  if (errorElement) {
    return;
  }
  const div = document.createElement('div');
  div.classList.add('alert', 'alert-info');
  div.setAttribute('role', 'alert');
  div.setAttribute('type', 'error');
  div.innerHTML = message;
  element.prepend(div);
};

const removeErrorAlert = () => {
  const errorElement = document.querySelector('[type="error"]');
  errorElement.remove();
};

const initWatchers = (elements, state) => {
  const {
    content, urlInput, submit, container,
  } = elements;

  watch(state.form, 'valid', () => {
    const { valid } = state.form;
    if (valid) {
      submit.removeAttribute('disabled');
    } else {
      submit.setAttribute('disabled', true);
    }
  });

  watch(state, ['feedList', 'posts'], () => {
    content.innerHTML = '';
    const { feedList, posts } = state;
    feedList.forEach((list) => {
      const { title, description, id } = list;
      const feedPosts = posts.filter((post) => post.feedId === id);
      const div = document.createElement('div');
      div.classList.add('card', 'mt-5');
      const text = `
        <div class="card mb-0">
          <div class="card-header">
            ${title}
          </div>
          <div class="card-body pb-1">
            <p class="card-text">${description}</p>
            ${renderItems(feedPosts)}
          </div>
        </div>
      `;
      div.innerHTML = text;
      content.prepend(div);
    });
  });

  watch(state.form, 'processState', () => {
    const { form: { processState } } = state;
    switch (processState) {
      case 'filling': {
        removeSpinner();
        break;
      }
      case 'pending': {
        renderSpinner(content);
        submit.setAttribute('disabled', true);
        urlInput.value = '';
        break;
      }
      default:
    }
  });

  watch(state, 'errors', () => {
    const { errors: { error, url } } = state;
    const isValidUrl = !url;

    if (error) {
      const message = i18next.t(error);
      renderErrorAlert(container, message);
      setTimeout(removeErrorAlert, 15000);
    }
    if (isValidUrl) {
      urlInput.classList.remove('is-invalid');
    } else {
      urlInput.classList.add('is-invalid');
    }
  });
};

export default initWatchers;