Reference

Reference Components

Components

OtherEvergreenPublic

Playground

import React, { useEffect, useRef, useState } from 'react'
import { connect } from 'react-redux'

import { store } from '../../store/index'
import './Playground.css'
import PolicyAccordion from '../policy-accordion/PolicyAccordion'

function mapStateToProps(state) {

}

function mapDispatchToProps(dispatch) {
  return {
  }
}

function Playground(props) {
  const { listDataProps = `{}` } = props // props from aem/html

  const { componentPath, templatePattern, templateConfigurationPathPattern, liveusageQueryPath, basePagePath, wcmmode, currentResourcePath, fiddleCss, notes, defaultTemplateRegExp, defaultImportContentRegExp } = listDataProps

  const [loadingIframes, setLoadingIframes] = useState(true)

  const iframeContentRef = useRef(null)

  const [urlDialogComponent, setUrlDialogComponent] = useState(null)
  const [urlContentPage, setUrlContentPage] = useState(null)

  const [iframeContentDisableMode, setIframeContentDisableMode] = useState(true)
  const [selectedTemplatePath, setSelectedTemplatePath] = useState(null)
  const [componentFullPath, setComponentFullPath] = useState(null)
  const [fullBasePagePath, setFullBasePagePath] = useState(null)

  const [selectTemplateOptions, setSelectTemplateOptions] = useState([])

  // import content
  const [selectPreContentOptions, setSelectPreContentOptions] = useState([])
  const [selectedPreContent, setSelectedPreContent] = useState('')

  const [fiddleValue, setFiddleVaue] = useState(fiddleCss)
  const [notesValue, setNotesVaue] = useState(notes)
  const [loadedAxeCore, setLoadedAxeCore] = useState(false)
  const [axeCoreViolations, setAxeCoreViolations] = useState([])

  const [demoControlWindow, setDemoControlWindow] = useState('')
  const [tabWindow, setTabWindow] = useState('playground')

  const [aemPoliciesList, setAemPoliciesList] = useState([])
  const [aemLiveUsageList, setAemLiveUsageList] = useState([])
  const [aemLiveUsageListSize, setAemLiveUsageListSize] = useState(0)

  const [saveMessage, setSaveMessage] = useState('')
  useEffect(async () => {
    // get template selected by default
    let initSelectedTemplatePath = getUrlParameter('t') // shared link parameter

    // will display default or shared selectedTemplatePath.
    // Called first to get default precontent, and then pass it to getTemplates to create the page.
    let currentStateInfo = await getPreContent(initSelectedTemplatePath)
    await getTemplates(currentStateInfo)

    // wait for templates to load and then search for precontent
    getPolicies()
  }, []) // called only once

  async function getTemplates(currentStateInfo) {
    const details = {
      tp: templatePattern,
      tcp: templateConfigurationPathPattern,
      defaultTemplateRegExp,
      ws: getUrlParameter('ws'),
    }

    let formBody = []
    for (let property in details) {
      if (details[property]) {
        let encodedKey = encodeURIComponent(property)
        let encodedValue = encodeURIComponent(details[property])
        formBody.push(`${encodedKey}=${encodedValue}`)
      }
    }
    formBody = formBody.join('&')

    return await fetch('/bin/aemcatalog.playgroundtemplates.json', {
      headers: {
        'Accept': 'application/json',
        'Content-Type': 'application/x-www-form-urlencoded',
        'CSRF-Token': await getCsrfToken(),
      },
      method: 'POST',
      body: formBody,
    })
      .then(response => response.json())
      .then((templates) => {
        if (templates && templates.aemTemplateList) {
          setSelectTemplateOptions([{ name: 'Select', path: '', nameHint: '' }, ...templates.aemTemplateList])
          let currentInitSelectedTemplatePath = null
          if (currentStateInfo && currentStateInfo.initSelectedTemplatePath) { // check if its first load, select the default template
            currentInitSelectedTemplatePath = currentStateInfo.initSelectedTemplatePath
          }
          else if (templates.initSelectedTemplate && templates.initSelectedTemplate.path) {
            currentInitSelectedTemplatePath = templates.initSelectedTemplate.path
          }
          handleTemplateChange(currentInitSelectedTemplatePath, currentStateInfo.selectedPreContent) // send default init content
          return { initSelectedTemplatePath: currentInitSelectedTemplatePath }
        }
        else {
          setSelectTemplateOptions([])
          console.log('No template options loaded.')
          return { initSelectedTemplatePath: null }
        }
      })
      .catch(err => console.log(err))
  }

  async function getPreContent(initSelectedTemplatePath) {
    let returnValue = { selectedPreContent: null, initSelectedTemplatePath }

    const details = {
      defaultImportContentRegExp,
      ws: getUrlParameter('ws'),
    }

    let formBody = []
    for (let property in details) {
      if (details[property]) {
        let encodedKey = encodeURIComponent(property)
        let encodedValue = encodeURIComponent(details[property])
        formBody.push(`${encodedKey}=${encodedValue}`)
      }
    }
    formBody = formBody.join('&')

    return await fetch(`/bin/aemcatalogprecontent.list.json${componentPath}`, {
      headers: {
        'Accept': 'application/json',
        'Content-Type': 'application/x-www-form-urlencoded',
        'CSRF-Token': await getCsrfToken(),
      },
      method: 'POST',
      body: formBody,
    })
      .then(response => response.json())
      .then((pages) => {
        if (pages && pages.aemPreconfiguredContentList) {
          setSelectPreContentOptions([{ title: 'Select', fullPagePath: '', fullComponentPath: '' }, ...pages.aemPreconfiguredContentList])

          if (pages.initSelectedPreconfiguredContent) { // set default pre content
            setSelectedPreContent(pages.initSelectedPreconfiguredContent.fullPagePath)
            returnValue.selectedPreContent = pages.initSelectedPreconfiguredContent.fullPagePath // set default default precontent
          }
          else {
            setSelectedPreContent('')
          }
        }
        else {
          setSelectPreContentOptions([])
          setSelectedPreContent('')
          console.log('No template options loaded.')
        }
        return returnValue
      })
      .catch(err => console.log(err))
  }

  function setIframeDialogUrl(componentPath, resourcePath) {
    let url = `/bin/aemdialog.html/mnt/override${componentPath}/_cq_dialog.html${resourcePath}?page=true` + `&time=${Date.now()}` // timestamp force refresh
    setLoadingIframes(true)
    setUrlDialogComponent(url)
  }

  function setIframeContentUlr(pagePath, disableMode) {
    console.log(`pagePath:${pagePath} disableMode:${disableMode}`)
    let url = `${pagePath}.html`
    if (disableMode === true) {
      url = `${url}?wcmmode=disabled` + `&time=${Date.now()}` // timestamp force refresh
    }
    else { // author;
      url = `/editor.html${url}?time=${Date.now()}`
    }
    setLoadingIframes(true)
    setUrlContentPage(url)
  }

  function overrideDialog(iframeTarget) {
    const iframeDialog = iframeTarget.target
    const iframeWinDialog = iframeDialog.contentWindow || iframeDialog
    const iframeDocDialog = iframeDialog.contentDocument || iframeDialog.document
    // var action = iframeDocDialog.querySelector("form").getAttribute("action");
    console.log(`urlDialogComponent${urlDialogComponent}`)
    let iframeFrom = iframeDocDialog.querySelector('form')
    if (iframeFrom) {
      iframeFrom.setAttribute('data-cq-dialog-pageeditor', urlDialogComponent)
    }
  }

  async function handleTemplateChange(templatePath, selectedPreContent) {
    // let templatePath = event.target.value;
    // call servlet
    let result = await createPage (componentPath, templatePath, selectedPreContent)
    let _componentFullPath = result.componentFullPath
    let _fullBasePagePath = result.fullBasePagePath

    setSelectedTemplatePath(templatePath)
    setComponentFullPath(_componentFullPath)
    setFullBasePagePath(_fullBasePagePath)

    if (templatePath) {
      console.log(`handleTemplateChange:${templatePath}`)
      setIframeDialogUrl(componentPath, _componentFullPath)
      setIframeContentUlr(_fullBasePagePath, iframeContentDisableMode)
    }
  }

  async function handlePreContentChange(event) {
    console.log(`change clearing..${event.target.value}`)
    let pagePath = event.target.value
    setSelectedPreContent(pagePath)
  }

  function getUrlParameter(parameterName) {
    const search = window.location.search
    const params = new URLSearchParams(search)
    const wsValue = params.get(parameterName)
    if (wsValue) {
      return wsValue
    }
    return ''
  }

  async function createPage(componentPath, templatePath, selectedPreContent) {
    const details = {
      selectedPreContent,
      t: templatePath,
      ws: getUrlParameter('ws'),
      crp: currentResourcePath,
    }

    let formBody = []
    for (let property in details) {
      if (details[property]) {
        let encodedKey = encodeURIComponent(property)
        let encodedValue = encodeURIComponent(details[property])
        formBody.push(`${encodedKey}=${encodedValue}`)
      }
    }
    formBody = formBody.join('&')

    let response = fetch(`/bin/aemcatalogpage.getpage.html${componentPath}`, {
      headers: {
        'Accept': 'application/json',
        'Content-Type': 'application/x-www-form-urlencoded',
        'CSRF-Token': await getCsrfToken(),
      },
      method: 'POST',
      body: formBody,
    }).then(response => response.json()).catch((err) => {
      console.log(err)
    })

    return await response
  }

  async function savePage(e) {
    if (!fullBasePagePath) {
      return
    }

    const details = {
      template: selectedTemplatePath,
      componentfullpath: componentFullPath,
      page: fullBasePagePath,
      componentpath: componentPath,
      pagename: 'base',
      ws: getUrlParameter('ws'),
    }

    let formBody = []
    for (let property in details) {
      if (details[property]) {
        let encodedKey = encodeURIComponent(property)
        let encodedValue = encodeURIComponent(details[property])
        formBody.push(`${encodedKey}=${encodedValue}`)
      }
    }
    formBody = formBody.join('&')

    fetch('/bin/aemcatalogsave.save.json', {
      headers: {
        'Accept': 'application/json',
        'Content-Type': 'application/x-www-form-urlencoded',
        'CSRF-Token': await getCsrfToken(),
      },
      method: 'POST',
      body: formBody,
    })
      .then(response => response.json())
      .then((response) => {
        getPreContent()
        alert('Saved!')
      })
      .catch((err) => {
        console.log(err)
      })
  }

  async function clearAllPages(e) {
    const details = {
      template: selectedTemplatePath,
      componentfullpath: componentFullPath,
      page: fullBasePagePath,
      componentpath: componentPath,
      ws: getUrlParameter('ws'),
    }

    let formBody = []
    for (let property in details) {
      if (details[property]) {
        let encodedKey = encodeURIComponent(property)
        let encodedValue = encodeURIComponent(details[property])
        formBody.push(`${encodedKey}=${encodedValue}`)
      }
    }
    formBody = formBody.join('&')

    fetch('/bin/aemcatalogsave.clearall.json', {
      headers: {
        'Accept': 'application/json',
        'Content-Type': 'application/x-www-form-urlencoded',
        'CSRF-Token': await getCsrfToken(),
      },
      method: 'POST',
      body: formBody,
    })
      .then(response => response.json())
      .then((response) => {
        getPreContent()
      })
      .catch((err) => {
        console.log(err)
      })
  }

  async function clearPage(e) {
    const details = {
      template: selectedTemplatePath,
      componentfullpath: componentFullPath,
      page: fullBasePagePath,
      componentpath: componentPath,
      precontentPath: selectedPreContent,
      ws: getUrlParameter('ws'),
    }

    let formBody = []
    for (let property in details) {
      if (details[property]) {
        let encodedKey = encodeURIComponent(property)
        let encodedValue = encodeURIComponent(details[property])
        formBody.push(`${encodedKey}=${encodedValue}`)
      }
    }
    formBody = formBody.join('&')

    fetch('/bin/aemcatalogsave.clearpage.json', {
      headers: {
        'Accept': 'application/json',
        'Content-Type': 'application/x-www-form-urlencoded',
        'CSRF-Token': await getCsrfToken(),
      },
      method: 'POST',
      body: formBody,
    })
      .then(response => response.json())
      .then((response) => {
        getPreContent()
      })
      .catch((err) => {
        console.log(err)
      })
  }

  async function importContent(event) {
    /// ////// /* Post import content*/
    const details = {
      precontent: selectedPreContent,
      template: selectedTemplatePath,
      componentfullpath: componentFullPath,
      page: fullBasePagePath,
      componentpath: componentPath,
      ws: getUrlParameter('ws'),
    }

    let formBody = []
    for (let property in details) {
      if (details[property]) {
        let encodedKey = encodeURIComponent(property)
        let encodedValue = encodeURIComponent(details[property])
        formBody.push(`${encodedKey}=${encodedValue}`)
      }
    }
    formBody = formBody.join('&')

    fetch('/bin/aemcatalogprecontent.update.json', {
      headers: {
        'Accept': 'application/json',
        'Content-Type': 'application/x-www-form-urlencoded',
        'CSRF-Token': await getCsrfToken(),
      },
      method: 'POST',
      body: formBody,
    })
      .then(response => response.json())
      .then((response) => {
        if (response.fullComponentPath) {
          setIframeDialogUrl(componentPath, response.fullComponentPath) // componentPath  componentFullPath
          setIframeContentUlr(response.fullPagePath, iframeContentDisableMode) // fullBasePagePath  toogleDisableMode
        }
      })
      .catch((err) => {
        console.log(err)
      })
  }

  async function getCsrfToken() {
    const response = await fetch('/libs/granite/csrf/token.json')
    const json = await response.json()
    return json.token
  }

  function toggleDisableMode() {
    let _togleDisableMode = !iframeContentDisableMode
    setIframeContentDisableMode(_togleDisableMode)
    updateModifiedPage(_togleDisableMode)
  }

  async function updateModifiedPage(_togleDisableMode) {
    if (!_togleDisableMode) { // change to author, nothing has been updated yet.
      setIframeDialogUrl(componentPath, componentFullPath)
      setIframeContentUlr(fullBasePagePath, _togleDisableMode)
      return
    }

    const details = {
      template: selectedTemplatePath,
      componentfullpath: componentFullPath,
      page: fullBasePagePath,
      componentpath: componentPath,
      pagename: 'base',
      ws: getUrlParameter('ws'),
    }

    let formBody = []
    for (let property in details) {
      if (details[property]) {
        let encodedKey = encodeURIComponent(property)
        let encodedValue = encodeURIComponent(details[property])
        formBody.push(`${encodedKey}=${encodedValue}`)
      }
    }
    formBody = formBody.join('&')

    fetch('/bin/aemcatalogsave.update.json', {
      headers: {
        'Accept': 'application/json',
        'Content-Type': 'application/x-www-form-urlencoded',
        'CSRF-Token': await getCsrfToken(),
      },
      method: 'POST',
      body: formBody,
    })
      .then(response => response.json())
      .then((response) => {
        let _componentFullPath = response.fullComponentPath
        setComponentFullPath(_componentFullPath)
        setIframeDialogUrl(componentPath, _componentFullPath)
        setIframeContentUlr(fullBasePagePath, _togleDisableMode)
      })
      .catch((err) => {
        console.log(err)
      })
  }

  function redirectNavigation(iframeTarget) {
    // prevent navigationa away from current url, just works on publish
    // on author it does not work as editor has iframe
    const currentIframe = iframeTarget.target
    const currentIframeWin = currentIframe.contentWindow || currentIframe
    const currentPath = currentIframeWin.location.pathname + currentIframeWin.location.search
    if (currentPath && urlContentPage && currentPath != urlContentPage) {
      console.log('Not the same Path')
      currentIframe.src = urlContentPage
    }
  }

  function disableAuthorPanel(iframeTarget) {
    const currentIframe = iframeTarget.target
    const currentIframeDoc = currentIframe.contentDocument || currentIframe.document
    let paneHeader = currentIframeDoc.querySelector('.editor-GlobalBar.editor-panel-header')
    if (paneHeader) {
      // paneHeader.style.pointerEvents = 'none';
      paneHeader.style.opacity = '0.2'
    }
  }

  async function clearWorkspace() {
    await fetch('/bin/aemplayground.clear.html', {
      headers: {
        'Accept': 'application/json',
        'Content-Type': 'application/x-www-form-urlencoded',
        'CSRF-Token': await getCsrfToken(),
      },
      method: 'GET',
    })
    window.location.reload()
  }

  function applyCss() {
    console.log('apply css')
    if (iframeContentRef.current) {
      const currentIframeDoc = iframeContentRef.current.contentDocument || iframeContentRef.current.document

      let existingElement = currentIframeDoc.head.querySelector('#xpfiddle-css')
      if (existingElement) {
        existingElement.textContent = fiddleValue
      }
      else {
        let style = currentIframeDoc.createElement('style')
        style.type = 'text/css'
        style.setAttribute('id', 'xpfiddle-css')
        style.textContent = fiddleValue
        currentIframeDoc.head.appendChild(style)
      }
    }
  }

  async function saveCss() {
    const details = {
      fv: fiddleValue,
      crp: currentResourcePath,
      t: selectedTemplatePath,
      ws: getUrlParameter('ws'),
    }

    let formBody = []
    for (let property in details) {
      if (details[property]) {
        let encodedKey = encodeURIComponent(property)
        let encodedValue = encodeURIComponent(details[property])
        formBody.push(`${encodedKey}=${encodedValue}`)
      }
    }
    formBody = formBody.join('&')

    fetch(`/bin/aemcatalogpage.fiddlecss.json${componentPath}`, {
      headers: {
        'Accept': 'application/json',
        'Content-Type': 'application/x-www-form-urlencoded',
        'CSRF-Token': await getCsrfToken(),
      },
      method: 'POST',
      body: formBody,
    })
      .then(response => response.json())
      .then((templates) => {

      })
      .catch(err => console.log(err))
  }

  async function saveNotes() {
    const details = {
      nv: notesValue,
      crp: currentResourcePath,
      t: selectedTemplatePath,
      ws: getUrlParameter('ws'),
    }

    let formBody = []
    for (let property in details) {
      if (details[property]) {
        let encodedKey = encodeURIComponent(property)
        let encodedValue = encodeURIComponent(details[property])
        formBody.push(`${encodedKey}=${encodedValue}`)
      }
    }
    formBody = formBody.join('&')

    fetch(`/bin/aemcatalogpage.savenotes.json${componentPath}`, {
      headers: {
        'Accept': 'application/json',
        'Content-Type': 'application/x-www-form-urlencoded',
        'CSRF-Token': await getCsrfToken(),
      },
      method: 'POST',
      body: formBody,
    })
      .then(response => response.json())
      .then((templates) => {
        setSaveMessage('Saved')
        setTimeout(() => { setSaveMessage('') }, 1500)
      })
      .catch(err => console.log(err))
  }

  async function shareLink() {
    const response = await fetch(`/bin/aemcatalogpage.getid.html${componentPath}?` + `ws=${encodeURIComponent(getUrlParameter('ws'))}`)
    const responseValue = await response.json()

    if (responseValue.indentifier) {
      let ccp = (new URLSearchParams(window.location.search)).get('ccp')
      let path = window.location.href.split('?')[0]

      let selectedTemplatePathParam = ''
      if (selectedTemplatePath) {
        selectedTemplatePathParam = `&t=${selectedTemplatePath}`
      }
      let ccpParam = ''
      if (ccp) {
        ccpParam = `&ccp=${ccp}`
      }
      let urlClippboard = `${path}?` + `ws=${encodeURIComponent(responseValue.indentifier)}${ccpParam}${selectedTemplatePathParam}`
      await navigator.clipboard.writeText(urlClippboard)
      alert(`Copied to clipboard:  ${urlClippboard}`)
    }
  }

  function timeout(delay) {
    return new Promise(res => setTimeout(res, delay))
  }
  async function loadAxeCore() {
    setLoadedAxeCore(true)
    if (!loadedAxeCore) {
      const currentIframeDoc = iframeContentRef.current.contentDocument || iframeContentRef.current.document
      const iframeWinDialog = iframeContentRef.current.contentWindow || iframeContentRef.current.window

      let scriptAxe = currentIframeDoc.createElement('script')
      scriptAxe.type = 'text/javascript'
      scriptAxe.src = '/etc.clientlibs/aem-component-catalog/clientlibs/clientlib-axe.js'
      await currentIframeDoc.head.append(scriptAxe)
      await timeout(1000)
      let results = await iframeWinDialog.axe.run()

      if (results.violations.length > 0) {
        setAxeCoreViolations(results.violations)
      }
      else {
        setAxeCoreViolations([])
      }

      results.violations.forEach(
        (item, index) => {
          item.nodes.forEach(
            (node, index) => {
              if (node && node.target) {
                let elementNode = currentIframeDoc.querySelector(node.target[0])
                if (elementNode) {
                  // elementNode.style.border ="3px dotted green";
                  elementNode.style.outline = 'dotted green'
                }
              }
            },
          )
        },
      )
    }
  }

  async function getPolicies() {
    let formBody = []
    if (componentPath) {
      formBody.push(`${encodeURIComponent('c')}=${encodeURIComponent(componentPath)}`)
    }

    formBody = formBody.join('&')

    fetch('/bin/aempolicies.json', {
      headers: {
        'Accept': 'application/json',
        'Content-Type': 'application/x-www-form-urlencoded',
        'CSRF-Token': await getCsrfToken(),
      },
      method: 'POST',
      body: formBody,
    })
      .then(response => response.json())
      .then((components) => {
        if (components && components.aemPolicyDTOList) {
          setAemPoliciesList(components.aemPolicyDTOList)
        }
        else {
          console.log('No component options loaded.')
          setAemPoliciesList([])
        }
        // setLoading(false);
      })
      .catch((err) => {
        console.log(err)
        // setLoading(false);
        setAemPoliciesList([])
      })
  }

  async function getLiveUsage() {
    const details = {
      t: selectedTemplatePath,
      bp: liveusageQueryPath,
      c: componentPath,
      ws: getUrlParameter('ws'),
    }

    let formBody = []
    for (let property in details) {
      if (details[property]) {
        let encodedKey = encodeURIComponent(property)
        let encodedValue = encodeURIComponent(details[property])
        formBody.push(`${encodedKey}=${encodedValue}`)
      }
    }
    formBody = formBody.join('&')

    fetch('/bin/aemusage.components.json', {
      headers: {
        'Accept': 'application/json',
        'Content-Type': 'application/x-www-form-urlencoded',
        'CSRF-Token': await getCsrfToken(),
      },
      method: 'POST',
      body: formBody,
    })
      .then(response => response.json())
      .then((components) => {
        if (components && components.aemLiveUsageItemDTOList) {
          setAemLiveUsageList(components.aemLiveUsageItemDTOList)
          setAemLiveUsageListSize(components.size)
        }
        else {
          console.log('No component options loaded.')
        }
        // setLoading(false);
      })
      .catch((err) => {
        console.log(err)
        // setLoading(false);
        setAemLiveUsageList([])
        setAemLiveUsageListSize(0)
      })
  }

  return (
    <div className="responsivegrid aem-GridColumn aem-GridColumn--default--12">
      <div id="container-aacc1dda19" className="cmp-container container">
        <div className="aem-Grid aem-Grid--12 aem-Grid--default--12">
          <ul className="nav nav-tabs">
            <li className="nav-item">
              <a className={`nav-link ${tabWindow === 'playground' ? 'active' : ''}`} aria-current="page" onClick={(e) => { setTabWindow('playground') }}>Playground</a>
            </li>
            <li className="nav-item">
              <a className={`nav-link ${tabWindow === 'liveusage' ? 'active' : ''}`} onClick={(e) => { setTabWindow('liveusage'); getLiveUsage() }}>Live Usage Report</a>
            </li>
            <li className="nav-item">
              <a className={`nav-link ${tabWindow === 'policies' ? 'active' : ''}`} onClick={(e) => { setTabWindow('policies') }}>Policies</a>
            </li>
          </ul>
          {/* JSX playground */}
          <div className={`playground-react aem-GridColumn aem-GridColumn--default--12 ${tabWindow === 'playground' ? 'display-tab' : 'hide-tab'}`}>
            <div className="playground">
              <div className="playground-controls row mt-4">
                <div className="col-lg-9">
                  <h3>Workspace Content Controls</h3>
                  <div className="row">
                    <div className="col-lg-6 template">
                      <label htmlFor="template-select" className="form-label">
                        Select a page
                        template
                      </label>

                      <select
                        className="template-select form-select"
                        id="template-select"
                        value={selectedTemplatePath}
                        onChange={e => handleTemplateChange(e.target.value, null)}
                      >
                        {selectTemplateOptions.map((option, index) => (
                          <option key={index} value={option.path}>
                            {option.nameHint}
                            {' '}
                            {option.title}
                            {' '}
                            {index === 0 && 'Select'}
                            {' '}
                            {index !== 0 && `(${option.name})`}
                          </option>
                        ),
                        )}
                      </select>

                    </div>
                    <div className="col-lg-6 playground-import">
                      {selectedTemplatePath && (
                        <>
                          <label htmlFor="precontent-select" className="form-label">
                            Select content
                            for the component
                          </label>
                          <div className="precontent input-group">

                            <select
                              className="precontent-select form-select"
                              id="precontent-select"
                              value={selectedPreContent}
                              onChange={e => handlePreContentChange(e)}
                            >
                              {selectPreContentOptions.map((option, index) => (
                                <option key={index} value={option.fullPagePath}>
                                  {option.title}
                                  {' '}
                                  {index !== 0 && `(${option.name})`}
                                </option>
                              ),
                              )}
                            </select>
                            <button className="btn btn-outline-secondary" type="button" onClick={e => importContent(e)}>Import Content</button>
                          </div>
                        </>
                      )}
                    </div>
                  </div>
                </div>

                { wcmmode == 'EDIT' && selectedTemplatePath
                  && (
                    <div className="col-lg-2 offset-lg-1 admin-controls">
                      <h3>Admin</h3>
                      <div className="form-check form-switch">
                        <input
                          disabled={!fullBasePagePath}
                          className="form-check-input"
                          type="checkbox"
                          role="switch"
                          id="authorSwitchCheck"
                          onClick={(e) => {
                            toggleDisableMode()
                          }}
                        />
                        <label className="form-check-label" htmlFor="authorSwitchCheck">
                          {!iframeContentDisableMode && ' Disable Author View'}
                          {' '}
                          {iframeContentDisableMode && 'Enable Author View'}
                        </label>
                      </div>
                      <button
                        className="btn btn-sm btn-link"
                        disabled={!fullBasePagePath || iframeContentDisableMode}
                        onClick={e => savePage(e)}
                      >
                        <span
                          className="material-symbols-rounded"
                        >
                          save
                        </span>
                        {' '}
                        Save Demo Content
                      </button>

                      {selectedPreContent && (
                        <button
                          className="btn btn-sm btn-link"
                          onClick={e => clearPage(e)}
                        >
                          {' '}
                          <span
                            className="material-symbols-rounded"
                          >
                            close
                          </span>
                          Clear Page Demo Content
                        </button>
                      )}

                      <button
                        className="btn btn-sm btn-link"
                        onClick={e => clearAllPages(e)}
                      >
                        {' '}
                        <span
                          className="material-symbols-rounded"
                        >
                          close
                        </span>
                        Clear All Demo Content
                      </button>

                    </div>
                  )}

              </div>
              <div className="row mt-5 playground-container">
                <div className="col-lg-2">
                  <h3>Demo Controls -</h3>

                  {selectedTemplatePath
                    && (
                      <>
                        <button
                          className="btn btn-sm btn-link show-control"
                          onClick={(e) => {
                            setDemoControlWindow('dialog')
                          }}
                          data-whichcontrol="component-dialog"
                        >
                          <span
                            className="material-symbols-rounded"
                          >
                            tune
                          </span>
                          {' '}
                          Edit Component
                        </button>
                        <button
                          className="btn btn-sm btn-link show-control"
                          onClick={(e) => {
                            setDemoControlWindow('css')
                          }}
                          data-whichcontrol="fiddler-dialog"
                        >
                          <span
                            className="material-symbols-rounded"
                          >
                            palette
                          </span>
                          {' '}
                          Component CSS
                        </button>
                        <button
                          onClick={(e) => {
                            loadAxeCore(e)
                            setDemoControlWindow('axe')
                          }}
                          className="btn btn-sm btn-link btn-axe show-control"
                          data-whichcontrol="axe-results"
                        >
                          <span
                            className="material-symbols-rounded"
                          >
                            accessibility_new
                          </span>
                          {' '}
                          Axe
                          Accessibility
                        </button>
                      </>
                    )}

                  <button
                    className="btn btn-sm btn-link"
                    onClick={(e) => {
                      setDemoControlWindow('notes')
                    }}
                    data-whichcontrol="fiddler-dialog"
                  >
                    <span
                      className="material-symbols-rounded"
                    >
                      tune
                    </span>
                    {' '}
                    Notes &emsp;&emsp; &emsp;&emsp;
                  </button>

                  <button onClick={e => shareLink(e)} className="btn btn-sm btn-link">
                    <span
                      className="material-symbols-rounded"
                    >
                      link
                    </span>
                    {' '}
                    Share Link
                  </button>
                  <button
                    className="btn btn-sm btn-link clear"
                    onClick={e => clearWorkspace(e)}
                  >
                    <span
                      className="material-symbols-rounded"
                    >
                      close
                    </span>
                    {' '}
                    Clear Workspace
                  </button>
                </div>
                <div className="col-lg-10">
                  <iframe
                    id="contentIFrame"
                    className={`content-iframe ${iframeContentDisableMode ? 'disable-mode ' : 'author-mode '}${loadingIframes ? 'loading ' : ' '}`}
                    height="800"
                    src={urlContentPage}
                    title="description"
                    ref={iframeContentRef}
                    onLoad={(iframeTarget) => {
                      redirectNavigation(iframeTarget)
                      disableAuthorPanel(iframeTarget)
                      setLoadedAxeCore(false)

                      setLoadingIframes(false)
                    }}
                  >
                  </iframe>
                </div>

                <div
                  className={`demo-control component-dialog ${demoControlWindow === 'dialog' ? 'active ' : ' '}`}
                  id="component-dialog"
                >
                  <button
                    className="btn btn-sm btn-link close-control"
                    onClick={e => setDemoControlWindow('')}
                  >
                    <span
                      className="material-symbols-rounded"
                    >
                      close
                    </span>
                  </button>

                  <iframe
                    id="dialogIFrame"
                    height="800"
                    className={`dialog-iframe ${loadingIframes ? 'loading ' : ' '}${iframeContentDisableMode ? 'disable-mode ' : 'author-mode '}`}
                    src={urlDialogComponent}
                    title="description"
                    onLoad={(iframeTarget) => {
                      if (!loadingIframes) { // if it is not true it was trigger by submit,  //if loading is true then it was trigger by a component action
                        iframeContentRef.current.contentWindow.location.reload() // no need to timeout since its after the it loads
                      }
                      setLoadingIframes(false)
                      overrideDialog(iframeTarget)
                    }}
                  >
                  </iframe>
                </div>

                <div
                  className={`demo-control fiddler-dialog ${demoControlWindow === 'css' ? 'active ' : ' '}`}
                  id="fiddler-dialog"
                >
                  <button
                    className="btn btn-sm btn-link close-control"
                    onClick={e => setDemoControlWindow('')}
                  >
                    <span
                      className="material-symbols-rounded"
                    >
                      close
                    </span>
                  </button>
                  <h4>Add custom CSS to the component</h4>
                  <textarea
                    id="fiddler-text-area"
                    name="textarea"
                    className="mb-3"
                    value={fiddleValue}
                    onInput={e => setFiddleVaue(e.target.value)}
                  >
                  </textarea>

                  <button
                    className="btn btn-outline-secondary"
                    onClick={(e) => {
                      applyCss(e)
                      saveCss(e)
                    }}
                  >
                    Apply css
                  </button>
                </div>

                <div
                  className={`demo-control fiddler-dialog ${demoControlWindow === 'notes' ? 'active ' : ' '}`}
                  id="fiddler-dialog"
                >
                  <button
                    className="btn btn-sm btn-link close-control"
                    onClick={e => setDemoControlWindow('')}
                  >
                    <span
                      className="material-symbols-rounded"
                    >
                      close
                    </span>
                  </button>
                  <h4>Notes</h4>
                  <textarea
                    id="fiddler-text-area"
                    name="textarea"
                    className="mb-3"
                    value={notesValue}
                    onInput={e => setNotesVaue(e.target.value)}
                  >
                  </textarea>

                  <button
                    className="btn btn-outline-secondary"
                    onClick={(e) => {
                      saveNotes(e)
                    }}
                  >
                    Save
                  </button>
                  {saveMessage
                    && (
                      <div className="alert alert-secondary mt-1 mb-1" role="alert">
                        {saveMessage}
                      </div>
                    )}

                </div>

                <div
                  className={`demo-control axe-results ${demoControlWindow === 'axe' ? 'active ' : ' '}`}
                  id="axe-results"
                >
                  <button
                    className="btn btn-sm btn-link close-control"
                    onClick={e => setDemoControlWindow('')}
                  >
                    <span
                      className="material-symbols-rounded"
                    >
                      close
                    </span>
                  </button>
                  <h4>Accessibility Scan Results</h4>

                  {axeCoreViolations && loadedAxeCore
                    && (
                      <div>
                        {axeCoreViolations.map((group, index) => (

                          <div>
                            <h5 className="title">{group.id}</h5>
                            <span className="title">{group.description}</span>
                            <ul>
                              {group.nodes.map((node, index) => (
                                <li key={index}>
                                  <span>{node.target}</span>
                                  <span>{node.failureSummary}</span>
                                </li>
                              ))}
                            </ul>
                          </div>

                        ),
                        )}
                      </div>
                    )}
                </div>

              </div>
            </div>
          </div>
          {/* End JSX playground */}
          {/* JSX policies */}

          <div className={`playground-react aem-GridColumn aem-GridColumn--default--12 ${tabWindow === 'policies' ? 'display-tab' : 'hide-tab'}`}>

            {aemPoliciesList
              && (
                <ul className="policies-list">
                  {aemPoliciesList.map((item, index) => (
                    <PolicyAccordion index={index} item={item} />
                  ),
                  )}
                </ul>
              )}
          </div>
          {/* End JSX policies */}
          {/* JSX live usage */}
          <div
            className={`playground-react aem-GridColumn aem-GridColumn--default--12 ${tabWindow === 'liveusage' ? 'display-tab' : 'hide-tab'}`}
          >
            <table className="table liveusage-list">
              <thead>
                <tr>
                  <th scope="col">Count</th>
                  <th scope="col">Template</th>
                </tr>
              </thead>
              <tbody>
                <tr>
                  <td>{aemLiveUsageListSize}</td>
                  <td>{selectedTemplatePath}</td>
                </tr>
              </tbody>
            </table>
            <table className="table liveusage-list">
              <thead>
                <tr>
                  <th scope="col">Title</th>
                  <th scope="col">Page or Fragment Path</th>
                  <th scope="col">Component Path</th>
                </tr>
              </thead>

              {aemLiveUsageList
                && (
                  <tbody>
                    {aemLiveUsageList.map((item, index) => (
                      <tr key={index}>
                        <td>{item.title}</td>
                        <td>{item.pagePath}</td>
                        <td>{item.resourcePath}</td>
                      </tr>
                    ),
                    )}
                  </tbody>
                )}

            </table>

          </div>
          {/* End JSX live usage */}
        </div>
      </div>
    </div>
  )
}

const ConnectedPlayground = connect(
  mapStateToProps,
  mapDispatchToProps,
)(Playground)
export { ConnectedPlayground as Playground }

.playground-container iframe {
    animation: fadeIn 0.2s forwards;
}

.playground-container iframe.loading {
    background-color: #ececec;
    animation: none;
    opacity:0.8;
}


@keyframes fadeIn {
    0% { opacity:0.1; }
    80% { opacity:0.2; }
    100% { opacity:1; }
}

.playground-container  #contentIFrame {
    height: 800px;
}

.playground-container  #contentIFrame.disable-mode {
    /*pointer-events: none;
    cursor: pointer;*/
}

.playground-container  #dialogIFrame.author-mode  {
    pointer-events: none;
    opacity: 0.2;
    cursor: url('data:image/jpeg;base64,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'), auto;
}

.playground-container textarea {
    height: 70%;
}


.playground-react .hide-tab {
    display: none;
}

@media(min-width: 992px) {
    .container,.container-lg,.container-md,.container-sm {
        max-width:80pc
    }
}

@media(min-width: 1200px) {
    .container,.container-lg,.container-md,.container-sm,.container-xl {
        max-width:1100pt
    }
}

@media(min-width: 1400px) {
    .container,.container-lg,.container-md,.container-sm,.container-xl,.container-xxl {
        max-width:1290pt
    }
}