label-text.js 6.31 KB
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.queryByLabelText = exports.queryAllByLabelText = exports.getByLabelText = exports.getAllByLabelText = exports.findByLabelText = exports.findAllByLabelText = void 0;

var _config = require("../config");

var _helpers = require("../helpers");

var _labelHelpers = require("../label-helpers");

var _allUtils = require("./all-utils");

function queryAllLabels(container) {
  return Array.from(container.querySelectorAll('label,input')).map(node => {
    return {
      node,
      textToMatch: (0, _labelHelpers.getLabelContent)(node)
    };
  }).filter(({
    textToMatch
  }) => textToMatch !== null);
}

const queryAllLabelsByText = (container, text, {
  exact = true,
  trim,
  collapseWhitespace,
  normalizer
} = {}) => {
  const matcher = exact ? _allUtils.matches : _allUtils.fuzzyMatches;
  const matchNormalizer = (0, _allUtils.makeNormalizer)({
    collapseWhitespace,
    trim,
    normalizer
  });
  const textToMatchByLabels = queryAllLabels(container);
  return textToMatchByLabels.filter(({
    node,
    textToMatch
  }) => matcher(textToMatch, node, text, matchNormalizer)).map(({
    node
  }) => node);
};

const queryAllByLabelText = (container, text, {
  selector = '*',
  exact = true,
  collapseWhitespace,
  trim,
  normalizer
} = {}) => {
  (0, _helpers.checkContainerType)(container);
  const matcher = exact ? _allUtils.matches : _allUtils.fuzzyMatches;
  const matchNormalizer = (0, _allUtils.makeNormalizer)({
    collapseWhitespace,
    trim,
    normalizer
  });
  const matchingLabelledElements = Array.from(container.querySelectorAll('*')).filter(element => {
    return (0, _labelHelpers.getRealLabels)(element).length || element.hasAttribute('aria-labelledby');
  }).reduce((labelledElements, labelledElement) => {
    const labelList = (0, _labelHelpers.getLabels)(container, labelledElement, {
      selector
    });
    labelList.filter(label => Boolean(label.formControl)).forEach(label => {
      if (matcher(label.content, label.formControl, text, matchNormalizer) && label.formControl) labelledElements.push(label.formControl);
    });
    const labelsValue = labelList.filter(label => Boolean(label.content)).map(label => label.content);
    if (matcher(labelsValue.join(' '), labelledElement, text, matchNormalizer)) labelledElements.push(labelledElement);

    if (labelsValue.length > 1) {
      labelsValue.forEach((labelValue, index) => {
        if (matcher(labelValue, labelledElement, text, matchNormalizer)) labelledElements.push(labelledElement);
        const labelsFiltered = [...labelsValue];
        labelsFiltered.splice(index, 1);

        if (labelsFiltered.length > 1) {
          if (matcher(labelsFiltered.join(' '), labelledElement, text, matchNormalizer)) labelledElements.push(labelledElement);
        }
      });
    }

    return labelledElements;
  }, []).concat((0, _allUtils.queryAllByAttribute)('aria-label', container, text, {
    exact,
    normalizer: matchNormalizer
  }));
  return Array.from(new Set(matchingLabelledElements)).filter(element => element.matches(selector));
}; // the getAll* query would normally look like this:
// const getAllByLabelText = makeGetAllQuery(
//   queryAllByLabelText,
//   (c, text) => `Unable to find a label with the text of: ${text}`,
// )
// however, we can give a more helpful error message than the generic one,
// so we're writing this one out by hand.


const getAllByLabelText = (container, text, ...rest) => {
  const els = queryAllByLabelText(container, text, ...rest);

  if (!els.length) {
    const labels = queryAllLabelsByText(container, text, ...rest);

    if (labels.length) {
      const tagNames = labels.map(label => getTagNameOfElementAssociatedWithLabelViaFor(container, label)).filter(tagName => !!tagName);

      if (tagNames.length) {
        throw (0, _config.getConfig)().getElementError(tagNames.map(tagName => `Found a label with the text of: ${text}, however the element associated with this label (<${tagName} />) is non-labellable [https://html.spec.whatwg.org/multipage/forms.html#category-label]. If you really need to label a <${tagName} />, you can use aria-label or aria-labelledby instead.`).join('\n\n'), container);
      } else {
        throw (0, _config.getConfig)().getElementError(`Found a label with the text of: ${text}, however no form control was found associated to that label. Make sure you're using the "for" attribute or "aria-labelledby" attribute correctly.`, container);
      }
    } else {
      throw (0, _config.getConfig)().getElementError(`Unable to find a label with the text of: ${text}`, container);
    }
  }

  return els;
};

function getTagNameOfElementAssociatedWithLabelViaFor(container, label) {
  const htmlFor = label.getAttribute('for');

  if (!htmlFor) {
    return null;
  }

  const element = container.querySelector(`[id="${htmlFor}"]`);
  return element ? element.tagName.toLowerCase() : null;
} // the reason mentioned above is the same reason we're not using buildQueries


const getMultipleError = (c, text) => `Found multiple elements with the text of: ${text}`;

const queryByLabelText = (0, _allUtils.wrapSingleQueryWithSuggestion)((0, _allUtils.makeSingleQuery)(queryAllByLabelText, getMultipleError), queryAllByLabelText.name, 'query');
exports.queryByLabelText = queryByLabelText;
const getByLabelText = (0, _allUtils.makeSingleQuery)(getAllByLabelText, getMultipleError);
const findAllByLabelText = (0, _allUtils.makeFindQuery)((0, _allUtils.wrapAllByQueryWithSuggestion)(getAllByLabelText, getAllByLabelText.name, 'findAll'));
exports.findAllByLabelText = findAllByLabelText;
const findByLabelText = (0, _allUtils.makeFindQuery)((0, _allUtils.wrapSingleQueryWithSuggestion)(getByLabelText, getAllByLabelText.name, 'find'));
exports.findByLabelText = findByLabelText;
const getAllByLabelTextWithSuggestions = (0, _allUtils.wrapAllByQueryWithSuggestion)(getAllByLabelText, getAllByLabelText.name, 'getAll');
exports.getAllByLabelText = getAllByLabelTextWithSuggestions;
const getByLabelTextWithSuggestions = (0, _allUtils.wrapSingleQueryWithSuggestion)(getByLabelText, getAllByLabelText.name, 'get');
exports.getByLabelText = getByLabelTextWithSuggestions;
const queryAllByLabelTextWithSuggestions = (0, _allUtils.wrapAllByQueryWithSuggestion)(queryAllByLabelText, queryAllByLabelText.name, 'queryAll');
exports.queryAllByLabelText = queryAllByLabelTextWithSuggestions;