adobe / helix-rum-enhancer

Add advanced functionality to Helix RUM Collection (client-side)
Apache License 2.0
1 stars 14 forks source link

refactor sourceSelector #215

Closed trieloff closed 2 weeks ago

trieloff commented 3 months ago

I'm rethinking how the sourceSelector should work, and started by writing down the spec

If you want to try this, open dev tools on a random page, paste this

(function() {
  function walk(element, checkFn) {
    if (!element || element === document.body || element === document.documentElement) {
      return undefined;
    }
    const checkValue = checkFn(element);
    return checkValue || walk(element.parentElement, checkFn);
  }

  function isFakeDialog(element) {
    if (element.tagName === 'DIALOG') return true;
    if (element.getAttribute('role') === 'dialog') return true;
    if (element.getAttribute('role') === 'alertdialog') return true;
    if (element.getAttribute('aria-modal') === 'true') return true;
    const computedStyle = window.getComputedStyle(element);
    return (computedStyle && computedStyle.position === 'fixed' && computedStyle.zIndex > 100);
  }

  function isFakeButton(element) {
    if (element.tagName === 'BUTTON') return true;
    if (element.tagName === 'INPUT' && element.getAttribute('type') === 'button') return true;
    if (element.tagName === 'A') {
      const classes = Array.from(element.classList);
      return classes.some((className) => className.match(/button|cta/));
    }
    return element.getAttribute('role') === 'button';
  }

  function getSourceContext(element) {
    if (element.closest('form')) return 'form';
    if (element.closest('.block')) return `.${element.closest('.block').getAttribute('data-block-name')}`;
    if (walk(element, isFakeDialog)) return 'dialog';
    if (element.closest('nav')) return 'nav';
    if (element.closest('header')) return 'header';
    if (element.closest('footer')) return 'footer';
    if (element.closest('aside')) return 'aside';
    return (walk(element, (e) => e.id && `#${e.id}`));
  }

  function getSourceElement(element) {
    if (element.closest('form') && Array.from(element.closest('form').elements).includes(element)) return element.tagName.toLowerCase() + (element.tagName === 'INPUT' ? `[type='${element.getAttribute('type')}']` : '');
    if (walk(element, isFakeButton)) return 'button';
    return element.tagName.toLowerCase().match(/^(a|img|video)$/) && element.tagName.toLowerCase();
  }

  function getSourceIdentifier(element) {
    if (element.id) return `#${element.id}`;
    if (element.getAttribute('data-block-name')) return `.${element.getAttribute('data-block-name')}`;
    return (element.classList.length > 0 && `.${element.classList[0]}`);
  }

  const sourceSelector = (element) => {
    try {
      if (!element || element === document.body || element === document.documentElement) {
        return undefined;
      }
      if (element.getAttribute('data-rum-source')) {
        return element.getAttribute('data-rum-source');
      }
      const context = getSourceContext(element.parentElement) || '';
      const elementName = getSourceElement(element) || '';
      const identifier = getSourceIdentifier(element) || '';
      return `${context} ${elementName}${identifier}`.trim() || `"${element.textContent.substring(0, 10)}"`;
    } catch (error) {
      return null;
    }
  };

  let lastElement = null;

  document.addEventListener('mousemove', (event) => {
    const currentElement = document.elementFromPoint(event.clientX, event.clientY);

    if (currentElement !== lastElement) {
      const source = sourceSelector(currentElement);
      console.log('Source:', source, currentElement);

      // Flash effect
      if (currentElement) {
        currentElement.style.transition = 'background-color 0.5s ease-in-out';
        currentElement.style.backgroundColor = 'yellow';
        setTimeout(() => {
          currentElement.style.backgroundColor = '';
        }, 500);
      }

      lastElement = currentElement;
    }
  });
})();

move the mouse pointer around on the website and compare the output to your expectations

codecov[bot] commented 3 months ago

Codecov Report

All modified and coverable lines are covered by tests :white_check_mark:

Project coverage is 100.00%. Comparing base (944d40d) to head (dca6c1c). Report is 4 commits behind head on main.

Additional details and impacted files ```diff @@ Coverage Diff @@ ## main #215 +/- ## ========================================= Coverage 100.00% 100.00% ========================================= Files 6 6 Lines 524 551 +27 ========================================= + Hits 524 551 +27 ```

:umbrella: View full report in Codecov by Sentry.
:loudspeaker: Have feedback on the report? Share it here.

github-actions[bot] commented 3 months ago

This PR will trigger a minor release when merged.

adobe-bot commented 2 weeks ago

:tada: This issue has been resolved in version 2.21.0 :tada:

The release is available on:

Your semantic-release bot :package::rocket: