Pārlūkot izejas kodu

Merge pull request #27829 from overleaf/rh-pug-contact-suggestions

Show subject suggestions list in pug contact form

GitOrigin-RevId: 509506ab1ad629eafcc34df7d841c095321ae72b
roo hutton 1 gadu atpakaļ
vecāks
revīzija
f342d77221

+ 0 - 2
services/web/frontend/js/features/contact-form/index.js

@@ -9,7 +9,6 @@ document
   .forEach(el => {
     el.addEventListener('click', function (e) {
       e.preventDefault()
-      $('[data-ol-contact-form-modal="contact-us"]').modal()
     })
   })
 
@@ -18,7 +17,6 @@ document
   .forEach(el => {
     el.addEventListener('click', function (e) {
       e.preventDefault()
-      $('[data-ol-contact-form-modal="general"]').modal()
     })
   })
 

+ 58 - 10
services/web/frontend/js/features/contact-form/search.js

@@ -1,17 +1,21 @@
 import _ from 'lodash'
+import DOMPurify from 'dompurify'
 import { formatWikiHit, searchWiki } from '../algolia-search/search-wiki'
 import { sendMB } from '../../infrastructure/event-tracking'
 import { materialIcon } from '@/features/utils/material-icon'
 
 export function setupSearch(formEl) {
   const inputEl = formEl.querySelector('[name="subject"]')
-  const resultsEl = formEl.querySelector('[data-ol-search-results]')
+  const resultsContainerEl = formEl.querySelector(
+    '[data-ol-search-results-container]'
+  )
   const wrapperEl = formEl.querySelector('[data-ol-search-results-wrapper]')
 
   let lastValue = ''
   function hideResults() {
     wrapperEl.setAttribute('hidden', '')
   }
+
   function showResults() {
     wrapperEl.removeAttribute('hidden')
   }
@@ -25,32 +29,55 @@ export function setupSearch(formEl) {
       return
     }
 
+    DOMPurify.addHook('uponSanitizeElement', node => {
+      if (node.nodeName === 'EM') {
+        const strong = document.createElement('strong')
+        strong.textContent = node.textContent
+        node.parentNode?.replaceChild(strong, node)
+      }
+    })
+
     try {
       const { hits, nbHits } = await searchWiki(value, {
         hitsPerPage: 3,
         typoTolerance: 'strict',
       })
-      resultsEl.innerText = ''
+
+      resultsContainerEl.innerHTML = ''
 
       for (const hit of hits) {
-        const { url, pageName } = formatWikiHit(hit)
+        const { url, rawPageName, sectionName } = formatWikiHit(hit)
         const liEl = document.createElement('li')
 
         const linkEl = document.createElement('a')
-        linkEl.className = 'contact-suggestion-list-item'
+        linkEl.className = 'dropdown-item'
         linkEl.href = url
         linkEl.target = '_blank'
+        linkEl.rel = 'noopener noreferrer'
+        linkEl.setAttribute('role', 'menuitem')
         liEl.append(linkEl)
 
-        const contentEl = document.createElement('span')
-        contentEl.innerHTML = pageName
-        linkEl.append(contentEl)
+        const contentWrapperEl = document.createElement('div')
+        contentWrapperEl.className = 'dropdown-item-description-container'
+        linkEl.append(contentWrapperEl)
+
+        const pageNameEl = document.createElement('div')
+        pageNameEl.innerHTML = DOMPurify.sanitize(rawPageName)
+        contentWrapperEl.append(pageNameEl)
 
         const iconEl = materialIcon('open_in_new')
         iconEl.classList.add('dropdown-item-trailing-icon')
-        linkEl.append(iconEl)
+        iconEl.setAttribute('aria-hidden', 'true')
+        contentWrapperEl.append(iconEl)
 
-        resultsEl.append(liEl)
+        if (sectionName) {
+          const sectionEl = document.createElement('span')
+          sectionEl.className = 'dropdown-item-description'
+          sectionEl.innerHTML = DOMPurify.sanitize(sectionName)
+          contentWrapperEl.append(sectionEl)
+        }
+
+        resultsContainerEl.append(liEl)
       }
       if (nbHits > 0) {
         showResults()
@@ -61,10 +88,31 @@ export function setupSearch(formEl) {
     } catch (e) {
       hideResults()
     }
+
+    DOMPurify.removeHook('uponSanitizeElement')
   }
 
   inputEl.addEventListener('input', _.debounce(handleChange, 350))
 
-  // display initial results
+  function handleClickOutside(event) {
+    if (!wrapperEl.contains(event.target) && !inputEl.contains(event.target)) {
+      hideResults()
+    }
+  }
+
+  document.addEventListener('click', handleClickOutside)
+
+  function handleKeyDown(event) {
+    if (event.key === 'Escape') {
+      if (!wrapperEl.hasAttribute('hidden')) {
+        hideResults()
+        event.stopPropagation()
+        event.preventDefault()
+      }
+    }
+  }
+
+  formEl.addEventListener('keydown', handleKeyDown)
+
   handleChange()
 }

+ 1 - 0
services/web/frontend/js/marketing.ts

@@ -10,3 +10,4 @@ import './features/multi-submit'
 import './features/cookie-banner'
 import './features/autoplay-video'
 import './features/mathjax'
+import './features/contact-form'

+ 1 - 16
services/web/frontend/stylesheets/modals/contact-us-modal.scss

@@ -15,22 +15,6 @@
   margin-bottom: var(--spacing-05);
 }
 
-.contact-suggestions {
-  @include body-sm;
-
-  margin: 0 calc(-1 * var(--bs-modal-padding)) var(--spacing-05);
-  padding: var(--spacing-05) 0;
-  color: var(--content-secondary);
-  background-color: var(--bg-light-secondary);
-  border-top: solid 1px var(--border-primary-dark);
-  border-bottom: solid 1px var(--border-primary-dark);
-}
-
-.contact-suggestion-label {
-  margin-bottom: var(--spacing-05);
-  padding: 0 var(--spacing-07);
-}
-
 .contact-suggestion-list {
   padding-left: 0;
   list-style: none;
@@ -64,6 +48,7 @@
 }
 
 .contact-suggestions-dropdown {
+  margin-top: $spacing-02;
   width: calc(100% - 2 * var(--bs-modal-padding));
 
   .dropdown-header {