label-text.js 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", {
  3. value: true
  4. });
  5. exports.findByLabelText = exports.findAllByLabelText = exports.getByLabelText = exports.getAllByLabelText = exports.queryByLabelText = exports.queryAllByLabelText = void 0;
  6. var _config = require("../config");
  7. var _helpers = require("../helpers");
  8. var _labelHelpers = require("../label-helpers");
  9. var _allUtils = require("./all-utils");
  10. function queryAllLabels(container) {
  11. return Array.from(container.querySelectorAll('label,input')).map(node => {
  12. return {
  13. node,
  14. textToMatch: (0, _labelHelpers.getLabelContent)(node)
  15. };
  16. }).filter(({
  17. textToMatch
  18. }) => textToMatch !== null);
  19. }
  20. const queryAllLabelsByText = (container, text, {
  21. exact = true,
  22. trim,
  23. collapseWhitespace,
  24. normalizer
  25. } = {}) => {
  26. const matcher = exact ? _allUtils.matches : _allUtils.fuzzyMatches;
  27. const matchNormalizer = (0, _allUtils.makeNormalizer)({
  28. collapseWhitespace,
  29. trim,
  30. normalizer
  31. });
  32. const textToMatchByLabels = queryAllLabels(container);
  33. return textToMatchByLabels.filter(({
  34. node,
  35. textToMatch
  36. }) => matcher(textToMatch, node, text, matchNormalizer)).map(({
  37. node
  38. }) => node);
  39. };
  40. const queryAllByLabelText = (container, text, {
  41. selector = '*',
  42. exact = true,
  43. collapseWhitespace,
  44. trim,
  45. normalizer
  46. } = {}) => {
  47. (0, _helpers.checkContainerType)(container);
  48. const matcher = exact ? _allUtils.matches : _allUtils.fuzzyMatches;
  49. const matchNormalizer = (0, _allUtils.makeNormalizer)({
  50. collapseWhitespace,
  51. trim,
  52. normalizer
  53. });
  54. const matchingLabelledElements = Array.from(container.querySelectorAll('*')).filter(element => {
  55. return (0, _labelHelpers.getRealLabels)(element).length || element.hasAttribute('aria-labelledby');
  56. }).reduce((labelledElements, labelledElement) => {
  57. const labelList = (0, _labelHelpers.getLabels)(container, labelledElement, {
  58. selector
  59. });
  60. labelList.filter(label => Boolean(label.formControl)).forEach(label => {
  61. if (matcher(label.content, label.formControl, text, matchNormalizer) && label.formControl) labelledElements.push(label.formControl);
  62. });
  63. const labelsValue = labelList.filter(label => Boolean(label.content)).map(label => label.content);
  64. if (matcher(labelsValue.join(' '), labelledElement, text, matchNormalizer)) labelledElements.push(labelledElement);
  65. if (labelsValue.length > 1) {
  66. labelsValue.forEach((labelValue, index) => {
  67. if (matcher(labelValue, labelledElement, text, matchNormalizer)) labelledElements.push(labelledElement);
  68. const labelsFiltered = [...labelsValue];
  69. labelsFiltered.splice(index, 1);
  70. if (labelsFiltered.length > 1) {
  71. if (matcher(labelsFiltered.join(' '), labelledElement, text, matchNormalizer)) labelledElements.push(labelledElement);
  72. }
  73. });
  74. }
  75. return labelledElements;
  76. }, []).concat( // TODO: Remove ignore after `queryAllByAttribute` will be moved to TS
  77. // eslint-disable-next-line @typescript-eslint/ban-ts-comment
  78. // @ts-expect-error
  79. (0, _allUtils.queryAllByAttribute)('aria-label', container, text, {
  80. exact,
  81. normalizer: matchNormalizer
  82. }));
  83. return Array.from(new Set(matchingLabelledElements)).filter(element => element.matches(selector));
  84. }; // the getAll* query would normally look like this:
  85. // const getAllByLabelText = makeGetAllQuery(
  86. // queryAllByLabelText,
  87. // (c, text) => `Unable to find a label with the text of: ${text}`,
  88. // )
  89. // however, we can give a more helpful error message than the generic one,
  90. // so we're writing this one out by hand.
  91. const getAllByLabelText = (container, text, ...rest) => {
  92. const els = queryAllByLabelText(container, text, ...rest);
  93. if (!els.length) {
  94. const labels = queryAllLabelsByText(container, text, ...rest);
  95. if (labels.length) {
  96. const tagNames = labels.map(label => getTagNameOfElementAssociatedWithLabelViaFor(container, label)).filter(tagName => !!tagName);
  97. if (tagNames.length) {
  98. 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);
  99. } else {
  100. 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);
  101. }
  102. } else {
  103. throw (0, _config.getConfig)().getElementError(`Unable to find a label with the text of: ${text}`, container);
  104. }
  105. }
  106. return els;
  107. };
  108. function getTagNameOfElementAssociatedWithLabelViaFor(container, label) {
  109. const htmlFor = label.getAttribute('for');
  110. if (!htmlFor) {
  111. return null;
  112. }
  113. const element = container.querySelector(`[id="${htmlFor}"]`);
  114. return element ? element.tagName.toLowerCase() : null;
  115. } // the reason mentioned above is the same reason we're not using buildQueries
  116. const getMultipleError = (c, text) => `Found multiple elements with the text of: ${text}`;
  117. const queryByLabelText = (0, _allUtils.wrapSingleQueryWithSuggestion)((0, _allUtils.makeSingleQuery)(queryAllByLabelText, getMultipleError), queryAllByLabelText.name, 'query');
  118. exports.queryByLabelText = queryByLabelText;
  119. const getByLabelText = (0, _allUtils.makeSingleQuery)(getAllByLabelText, getMultipleError);
  120. const findAllByLabelText = (0, _allUtils.makeFindQuery)((0, _allUtils.wrapAllByQueryWithSuggestion)(getAllByLabelText, getAllByLabelText.name, 'findAll'));
  121. exports.findAllByLabelText = findAllByLabelText;
  122. const findByLabelText = (0, _allUtils.makeFindQuery)((0, _allUtils.wrapSingleQueryWithSuggestion)(getByLabelText, getAllByLabelText.name, 'find'));
  123. exports.findByLabelText = findByLabelText;
  124. const getAllByLabelTextWithSuggestions = (0, _allUtils.wrapAllByQueryWithSuggestion)(getAllByLabelText, getAllByLabelText.name, 'getAll');
  125. exports.getAllByLabelText = getAllByLabelTextWithSuggestions;
  126. const getByLabelTextWithSuggestions = (0, _allUtils.wrapSingleQueryWithSuggestion)(getByLabelText, getAllByLabelText.name, 'get');
  127. exports.getByLabelText = getByLabelTextWithSuggestions;
  128. const queryAllByLabelTextWithSuggestions = (0, _allUtils.wrapAllByQueryWithSuggestion)(queryAllByLabelText, queryAllByLabelText.name, 'queryAll');
  129. exports.queryAllByLabelText = queryAllByLabelTextWithSuggestions;