WikiEducationFoundation/WikiEduDashboard

View on GitHub
app/assets/javascripts/components/common/namespace_select.jsx

Summary

Maintainability
B
6 hrs
Test Coverage
A
100%
import React, { useState, useEffect } from 'react';
import { map } from 'lodash-es';
import Select from 'react-select';

import { wikiNamespaceLabel } from '../../utils/wiki_utils';

const projects_namespaces_ids = JSON.parse(ProjectNamespaces);

const NamespaceSelect = (props) => {
  const [selectedNamespaces, setSelectedNamespaces] = useState([]);
  const [options, setOptions] = useState([]);

  useEffect(() => {
    const namespaces = props.namespaces.map((wiki_ns) => {
      const wiki = wiki_ns.split('-')[0];
      const namespace = wiki_ns.split('-')[2];
      const label = wikiNamespaceLabel(wiki, namespace);
      const value = wiki_ns;
      return { label, value };
    });
    setSelectedNamespaces(namespaces);
  }, [props.namespaces]);

  useEffect(() => {
    updateNamespacesFromWikis();
    updateOptionsFromWikis();
  }, [props.wikis]);

  // Filters currently selected namespaces according to tracked wikis
  // There shouldn't be any tracked namespace without corresponding tracked wiki
  const updateNamespacesFromWikis = () => {
    const current_namespaces = props.namespaces;

    const updated_namespaces = props.wikis.map((wiki) => {
      const language = wiki.language || 'www'; // for multilingual wikis, language is null
      const project = wiki.project;
      const domain = `${language}.${project}.org`;
      return current_namespaces.filter((wiki_ns) => {
        return wiki_ns.split('-')[0] === domain;
      });
    }).reduce((a, b) => a.concat(b));
  updateNamespaces(updated_namespaces);
  };

  // Updates options according to tracked wikis
  const updateOptionsFromWikis = () => {
    const updated_options = props.wikis.map((wiki) => {
      const language = wiki.language || 'www';
      const project = wiki.project;
      const domain = `${language}.${project}.org`;
      return projects_namespaces_ids[project].map((ns) => {
        const label = wikiNamespaceLabel(domain, ns);
        const value = `${domain}-namespace-${ns}`;
        return { label, value };
      });
    }).reduce((a, b) => a.concat(b));
    return setOptions(updated_options);
  };

  const handleChange = (selectedOptions) => {
    if (!selectedOptions) return updateNamespaces([]);
    const tracked_namespaces = selectedOptions.map((option) => {
      return option.value;
    });
    updateNamespaces(tracked_namespaces);
  };

  const updateNamespaces = (namespaces) => {
    props.onChange(namespaces);
  };

  if (props.readOnly) {
    const lastIndex = props.namespaces.length - 1;

    const namespaceList = map(props.namespaces, (wiki_ns, index) => {
      const comma = (index !== lastIndex) ? ', ' : '';
      const wiki = wiki_ns.split('-')[0];
      const namespace = wiki_ns.split('-')[2];
      const label = wikiNamespaceLabel(wiki, namespace);
      return <span key={wiki_ns}>{label}{comma}</span>;
    });
    return (
      <>
        {namespaceList}
      </>
    );
  }

  return (
    <>
      <label id="namespace-label" htmlFor="namespace_select" className="text-input-component__label inline-label">
        <strong>
          {I18n.t('courses.namespaces')}:&nbsp;
        </strong>
      </label>
      <div className="tooltip-trigger">
        <img src ="/assets/images/info.svg" alt = "tooltip default logo" />
        <div className="tooltip large dark">
          <p>
            {I18n.t('namespace.tracked_namespaces_info')}
          </p>
        </div>
      </div>
      <Select
        id = "namespace_select"
        value = {selectedNamespaces}
        onChange = {handleChange}
        options = {options}
        styles={props.styles}
        isMulti = {true}
        isClearable={false}
        aria-labelledby="namespace-label"
      />
    </>
  );
};

export default NamespaceSelect;