rokumatsumoto/boyutluseyler

View on GitHub
app/javascript/pages/shared/username_validator.js

Summary

Maintainability
A
2 hrs
Test Coverage
/* eslint-disable consistent-return, class-methods-use-this */

import axios from 'lib/utils/axios_utils';
import Noty from 'noty';

const UNAVAILABLE_MESSAGE_SELECTOR = '.username .validation-error';
const SUCCESS_MESSAGE_SELECTOR = '.username .validation-success';
const PENDING_MESSAGE_SELECTOR = '.username .validation-pending';
const YOURS_MESSAGE_SELECTOR = '.username .validation-yours';

const INVALID_INPUT_CLASS = 'is-invalid';
const SUCCESS_INPUT_CLASS = 'is-valid';

const INVALID_FORM_GROUP_CLASS = 'form-group-invalid';

export default class UsernameValidator {
  constructor() {
    this.inputElement = document.getElementById('user_username');
    if (this.inputElement != null) {
      // for Turbolinks
      this.state = {
        available: true,
        valid: false,
        pending: false,
        empty: true,
        yours: false,
      };
      this.inputElement.addEventListener('blur', this.usernameBlur.bind(this), false);

      this.form = this.inputElement.closest('form');
      this.form.addEventListener('submit', this.formSubmit.bind(this), false);
    }
  }

  usernameBlur() {
    const username = this.inputElement.value;
    // look parent node (div) for valid state
    // for presence, length and format validations we use client_side_validations gem
    this.state.valid = !this.inputElement.parentNode.classList.contains(INVALID_FORM_GROUP_CLASS);
    this.state.empty = !username.length;
    this.state.yours =
      this.inputElement.attributes.data_username !== undefined
        ? this.inputElement.attributes.data_username.value === this.inputElement.value
        : false;
    this.state.available = true;

    if (this.state.empty || !this.state.valid) {
      this.clearFieldValidationStateMessage();
    }

    if (this.state.yours) {
      this.renderState();
    }

    if (this.state.valid && !this.state.empty && !this.state.yours) {
      return this.validateUsername(username);
    }
  }

  formSubmit(event) {
    if (!this.state.available) {
      event.preventDefault();
      event.stopPropagation();
    }
  }

  renderState() {
    // Clear all state
    this.clearFieldValidationState();

    if (this.state.yours) {
      return this.setYoursState();
    }

    if (this.state.valid && this.state.available) {
      return this.setSuccessState();
    }

    if (this.state.empty) {
      return this.clearFieldValidationState();
    }

    if (this.state.pending) {
      return this.setPendingState();
    }

    if (this.state.valid && !this.state.yours && !this.state.available) {
      return this.setUnavailableState();
    }
  }

  validateUsername(username) {
    if (this.state.valid) {
      this.state.pending = true;
      this.state.available = false;
      this.renderState();
      axios
        .get(`/exists/${username}`)
        .then(({ data }) => this.setAvailabilityState(data.exists))
        .catch(error => {
          new Noty({
            type: 'error',
            layout: 'top',
            text: error.response.data.message,
          }).show();
        });
    }
  }

  setAvailabilityState(usernameTaken) {
    if (usernameTaken) {
      this.state.available = false;
    } else {
      this.state.available = true;
    }
    this.state.pending = false;
    this.renderState();
  }

  clearFieldValidationStateMessage() {
    $(this.inputElement.closest('div'))
      .children('div[class^="validation-"]')
      .hide();
  }

  clearFieldValidationState() {
    this.clearFieldValidationStateMessage();
    this.inputElement.classList.remove(INVALID_INPUT_CLASS, SUCCESS_INPUT_CLASS);
  }

  addSuccessClass() {
    this.inputElement.classList.add(SUCCESS_INPUT_CLASS);
    this.inputElement.classList.remove(INVALID_INPUT_CLASS);
  }

  setYoursState() {
    const $usernameYoursMessage = document.querySelector(YOURS_MESSAGE_SELECTOR);
    this.addSuccessClass();
    $usernameYoursMessage.style.display = 'block';
  }

  setSuccessState() {
    const $usernameSuccessMessage = document.querySelector(SUCCESS_MESSAGE_SELECTOR);
    this.addSuccessClass();
    $usernameSuccessMessage.style.display = 'block';
  }

  setPendingState() {
    const $usernamePendingMessage = document.querySelector(PENDING_MESSAGE_SELECTOR);
    if (this.state.pending) {
      $usernamePendingMessage.style.display = 'block';
    } else {
      $usernamePendingMessage.style.display = 'none';
    }
  }

  setUnavailableState() {
    const $usernameUnavailableMessage = document.querySelector(UNAVAILABLE_MESSAGE_SELECTOR);
    this.inputElement.classList.add(INVALID_INPUT_CLASS);
    this.inputElement.classList.remove(SUCCESS_INPUT_CLASS);
    $usernameUnavailableMessage.style.display = 'block';
  }
}