Skip to content

Commit

Permalink
Merge branches 'plugins' and 'master' of github.com:PostHog/posthog i…
Browse files Browse the repository at this point in the history
…nto master

* 'plugins' of github.com:PostHog/posthog:
  push up ABC class

* 'master' of github.com:PostHog/posthog:
  Update dockerfile for dev-ing (#1817)
  • Loading branch information
fuziontech committed Oct 7, 2020
2 parents ffb974c + d27b836 commit 5db7fc8
Show file tree
Hide file tree
Showing 19 changed files with 232 additions and 63 deletions.
4 changes: 3 additions & 1 deletion dev.Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ COPY requirements.txt /code/
RUN pip install $(grep -ivE "psycopg2" requirements.txt) --compile\
&& pip install psycopg2-binary

# install dev dependencies
# install dev dependencies
RUN mkdir /code/requirements/
COPY requirements/dev.txt /code/requirements/
RUN pip install -r requirements/dev.txt --compile

COPY package.json /code/
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/toolbar/ToolbarApp.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export function ToolbarApp(props: EditorProps = {}): JSX.Element {

return (
<>
<root.div id="__POSTHOG_TOOLBAR__" ref={shadowRef}>
<root.div id="__POSTHOG_TOOLBAR__" className="ph-no-capture" ref={shadowRef}>
<div id="posthog-toolbar-styles" />
{didRender ? <ToolbarContainer /> : null}
<ToastContainer autoClose={8000} transition={Slide} position="bottom-center" />
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/toolbar/ToolbarContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ function _ToolbarContainer(): JSX.Element {
const { buttonVisible } = useValues(toolbarLogic)

return (
<Fade visible={buttonVisible} className="toolbar-global-fade-container">
<Fade visible={buttonVisible} className="toolbar-global-fade-container ph-no-capture">
<Elements />
<DraggableButton />
</Fade>
Expand Down
7 changes: 6 additions & 1 deletion frontend/src/toolbar/actions/EditAction.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,12 @@ export function EditAction(): JSX.Element {
<StepField field={field} step={step} item="href" label="Link href" />
<StepField field={field} step={step} item="text" label="Text" />
<StepField field={field} step={step} item="selector" label="Selector" />
<StepField field={field} step={step} item="url" label="URL" />
<StepField
field={field}
step={step}
item="url"
label="URL of current page"
/>
</>
) : null}

Expand Down
24 changes: 14 additions & 10 deletions frontend/src/toolbar/actions/SelectorCount.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,24 @@
import React from 'react'
import React, { useMemo } from 'react'
import { querySelectorAllDeep } from '@mariusandra/query-selector-shadow-dom'

interface SelectorCountProps {
selector: string
}

export function SelectorCount({ selector }: SelectorCountProps): JSX.Element {
let selectorError = false
let matches

if (selector) {
try {
matches = document.querySelectorAll(selector).length
} catch {
selectorError = true
const [matches, selectorError] = useMemo(() => {
let selectorError = false
let matches = 0
if (selector) {
try {
matches = querySelectorAllDeep(selector).length
} catch {
selectorError = true
}
}
}
return [matches, selectorError]
}, [selector])

return (
<small style={{ float: 'right', color: selectorError ? 'red' : '' }}>
{selectorError ? 'Invalid selector' : `Matches ${matches} element${matches === 1 ? '' : 's'}`}
Expand Down
7 changes: 5 additions & 2 deletions frontend/src/toolbar/elements/elementsLogic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { ActionStepType, ActionType, ToolbarMode, ToolbarTab } from '~/types'
import { ActionElementWithMetadata, ActionForm, ElementWithMetadata } from '~/toolbar/types'
import { currentPageLogic } from '~/toolbar/stats/currentPageLogic'
import { toolbarLogic } from '~/toolbar/toolbarLogic'
import { collectAllElementsDeep } from '@mariusandra/query-selector-shadow-dom'

type ActionElementMap = Map<HTMLElement, ActionElementWithMetadata[]>
type ElementMap = Map<HTMLElement, ElementWithMetadata>
Expand Down Expand Up @@ -135,9 +136,10 @@ export const elementsLogic = kea<
(s) => [s.displayActionElements, actionsTabLogic.selectors.selectedEditedAction],
(displayActionElements, selectedEditedAction): ElementWithMetadata[] => {
if (displayActionElements && selectedEditedAction?.steps) {
const allElements = collectAllElementsDeep('', document, null)
const steps: ElementWithMetadata[] = []
selectedEditedAction.steps.forEach((step, index) => {
const element = getElementForStep(step)
const element = getElementForStep(step, allElements)
if (element) {
steps.push({
element,
Expand Down Expand Up @@ -187,12 +189,13 @@ export const elementsLogic = kea<
actionsForElementMap: [
(s) => [actionsLogic.selectors.sortedActions, s.rectUpdateCounter, toolbarLogic.selectors.buttonVisible],
(sortedActions): ActionElementMap => {
const allElements = collectAllElementsDeep('', document, null)
const actionsForElementMap = new Map<HTMLElement, ActionElementWithMetadata[]>()
sortedActions.forEach((action, index) => {
action.steps
?.filter((step) => step.event === '$autocapture')
.forEach((step) => {
const element = getElementForStep(step)
const element = getElementForStep(step, allElements)
if (element) {
const rect = getRectForElement(element)
let array = actionsForElementMap.get(element)
Expand Down
9 changes: 7 additions & 2 deletions frontend/src/toolbar/elements/heatmapLogic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { toolbarLogic } from '~/toolbar/toolbarLogic'
import { heatmapLogicType } from 'types/toolbar/elements/heatmapLogicType'
import { CountedHTMLElement, ElementsEventType } from '~/toolbar/types'
import { ActionStepType } from '~/types'
import { collectAllElementsDeep, querySelectorAllDeep } from '@mariusandra/query-selector-shadow-dom'

export const heatmapLogic = kea<heatmapLogicType<ElementsEventType, CountedHTMLElement, ActionStepType>>({
actions: {
Expand Down Expand Up @@ -77,6 +78,8 @@ export const heatmapLogic = kea<heatmapLogicType<ElementsEventType, CountedHTMLE
elements: [
(selectors) => [selectors.events],
(events) => {
// cache all elements in shadow roots
const allElements = collectAllElementsDeep('', document, null)
const elements: CountedHTMLElement[] = []
events.forEach((event) => {
let combinedSelector
Expand All @@ -86,12 +89,14 @@ export const heatmapLogic = kea<heatmapLogicType<ElementsEventType, CountedHTMLE
combinedSelector = lastSelector ? `${selector} > ${lastSelector}` : selector

try {
const domElements = Array.from(document.querySelectorAll(combinedSelector))
const domElements = Array.from(
querySelectorAllDeep(combinedSelector, document, allElements)
) as HTMLElement[]

if (domElements.length === 1) {
const e = event.elements[i]

// element like "svg" as the first one
// element like "svg" (only tag, no class/id/etc) as the first one
if (
i === 0 &&
e.tag_name &&
Expand Down
8 changes: 8 additions & 0 deletions frontend/src/toolbar/styles.scss
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
:host {
// reset fonts
all: initial;
font-family: 'Helvetica Neue', 'Helvetica', 'Arial', sans-serif;
font-size: 14px;
line-height: 1.5;
}

.toolbar-block {
background: white;
color: black;
Expand Down
84 changes: 67 additions & 17 deletions frontend/src/toolbar/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import Simmer from '@mariusandra/simmerjs'
import { cssEscape } from 'lib/utils/cssEscape'
import { ActionStepType, ElementType } from '~/types'
import { ActionStepForm, BoxColor } from '~/toolbar/types'
import { querySelectorAllDeep } from '@mariusandra/query-selector-shadow-dom'

// these plus any element with cursor:pointer will be click targets
const CLICK_TARGET_SELECTOR = `a, button, input, select, textarea, label`
Expand Down Expand Up @@ -31,7 +32,7 @@ export function elementToQuery(element: HTMLElement): string | undefined {
}

// Turn tags into lower cases
return simmer(element)?.replace(/(^[A-Z]+| [A-Z]+)/g, (d: string) => d.toLowerCase())
return simmer(element)?.replace(/(^[A-Z\-]+| [A-Z\-]+)/g, (d: string) => d.toLowerCase())
}

export function elementToActionStep(element: HTMLElement): ActionStepType {
Expand Down Expand Up @@ -81,8 +82,12 @@ export function elementToSelector(element: ElementType): string {
return selector
}

export function getToolbarElement(): HTMLElement | null {
return window.document.getElementById('__POSTHOG_TOOLBAR__') || null
}

export function getShadowRoot(): ShadowRoot | null {
return window.document.getElementById('__POSTHOG_TOOLBAR__')?.shadowRoot || null
return getToolbarElement()?.shadowRoot || null
}

export function getShadowRootPopupContainer(): HTMLElement {
Expand All @@ -93,11 +98,24 @@ export function hasCursorPointer(element: HTMLElement): boolean {
return window.getComputedStyle(element)?.getPropertyValue('cursor') === 'pointer'
}

export function getParent(element: HTMLElement): HTMLElement | null {
const parent = element.parentNode
// 11 = DOCUMENT_FRAGMENT_NODE
if (parent?.nodeType === window.Node.DOCUMENT_FRAGMENT_NODE) {
return (parent as ShadowRoot).host as HTMLElement
}
if (parent?.nodeType === window.Node.ELEMENT_NODE) {
return parent as HTMLElement
}
return null
}

export function trimElement(element: HTMLElement): HTMLElement | null {
if (!element) {
return null
}
if (element && element.getAttribute('id') === '__POSTHOG_TOOLBAR__') {
const toolbarElement = getToolbarElement()
if (toolbarElement && isParentOf(element, toolbarElement)) {
return null
}

Expand All @@ -113,32 +131,39 @@ export function trimElement(element: HTMLElement): HTMLElement | null {
}
}

while (loopElement?.parentElement) {
while (loopElement) {
const parent = getParent(loopElement)
if (!parent) {
return null
}

// return when we find a click target
if (loopElement.matches(CLICK_TARGET_SELECTOR)) {
if (loopElement.matches?.(CLICK_TARGET_SELECTOR)) {
return loopElement
}

const compStyles = window.getComputedStyle(loopElement)
if (compStyles.getPropertyValue('cursor') === 'pointer') {
const parentStyles = loopElement.parentElement ? window.getComputedStyle(loopElement.parentElement) : null
const parentStyles = parent ? window.getComputedStyle(parent) : null
if (!parentStyles || parentStyles.getPropertyValue('cursor') !== 'pointer') {
return loopElement
}
}

loopElement = loopElement.parentElement
loopElement = parent
}

return null
}

export function inBounds(min: number, value: number, max: number): number {
return Math.max(min, Math.min(max, value))
}

export function getAllClickTargets(): HTMLElement[] {
const elements = (document.querySelectorAll(CLICK_TARGET_SELECTOR) as unknown) as HTMLElement[]
export function getAllClickTargets(startNode: Document | HTMLElement | ShadowRoot = document): HTMLElement[] {
const elements = (startNode.querySelectorAll(CLICK_TARGET_SELECTOR) as unknown) as HTMLElement[]

const allElements = [...((document.querySelectorAll('*') as unknown) as HTMLElement[])]
const allElements = [...((startNode.querySelectorAll('*') as unknown) as HTMLElement[])]
const clickTags = CLICK_TARGET_SELECTOR.split(',').map((c) => c.trim())

// loop through all elements and getComputedStyle
Expand All @@ -150,7 +175,13 @@ export function getAllClickTargets(): HTMLElement[] {
return compStyles.getPropertyValue('cursor') === 'pointer'
})

const selectedElements = [...elements, ...pointerElements].map((e) => trimElement(e))
const shadowElements = allElements
.filter((el) => el.shadowRoot && el.getAttribute('id') !== '__POSTHOG_TOOLBAR__')
.map((el: HTMLElement) => (el.shadowRoot ? getAllClickTargets(el.shadowRoot) : []))
.reduce((a, b) => [...a, ...b], [])
const selectedElements = [...elements, ...pointerElements, ...shadowElements]
.map((e) => trimElement(e))
.filter((e) => e)
const uniqueElements = Array.from(new Set(selectedElements)) as HTMLElement[]

return uniqueElements
Expand Down Expand Up @@ -180,13 +211,13 @@ export function isParentOf(element: HTMLElement, possibleParent: HTMLElement): b
if (loopElement !== element && loopElement === possibleParent) {
return true
}
loopElement = loopElement.parentElement
loopElement = getParent(loopElement)
}

return false
}

export function getElementForStep(step: ActionStepForm): HTMLElement | null {
export function getElementForStep(step: ActionStepForm, allElements?: HTMLElement[]): HTMLElement | null {
if (!step) {
return null
}
Expand All @@ -208,7 +239,7 @@ export function getElementForStep(step: ActionStepForm): HTMLElement | null {

let elements = [] as HTMLElement[]
try {
elements = [...((document.querySelectorAll(selector || '*') as unknown) as HTMLElement[])]
elements = [...((querySelectorAllDeep(selector || '*', document, allElements) as unknown) as HTMLElement[])]
} catch (e) {
console.error('Can not use selector:', selector)
throw e
Expand Down Expand Up @@ -268,12 +299,31 @@ export function actionStepToAntdForm(step: ActionStepType, isNew = false): Actio
}

if (isNew) {
const hasSelector = !!step.selector
if (step.tag_name === 'a') {
return { ...step, href_selected: true, selector_selected: true, text_selected: false, url_selected: false }
return {
...step,
href_selected: true,
selector_selected: hasSelector,
text_selected: false,
url_selected: false,
}
} else if (step.tag_name === 'button') {
return { ...step, text_selected: true, selector_selected: true, href_selected: false, url_selected: false }
return {
...step,
text_selected: true,
selector_selected: hasSelector,
href_selected: false,
url_selected: false,
}
} else {
return { ...step, selector_selected: true, text_selected: false, url_selected: false, href_selected: false }
return {
...step,
selector_selected: hasSelector,
text_selected: false,
url_selected: false,
href_selected: false,
}
}
}

Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,9 @@
"dependencies": {
"@babel/core": "^7.10.4",
"@babel/runtime": "^7.10.4",
"@mariusandra/query-selector-shadow-dom": "0.7.2-posthog.2",
"@mariusandra/react-grid-layout": "0.18.3",
"@mariusandra/simmerjs": "0.6.1-posthog.1",
"@mariusandra/simmerjs": "0.7.1-posthog.1",
"@types/react-syntax-highlighter": "^11.0.4",
"@types/zxcvbn": "^4.4.0",
"antd": "^4.1.1",
Expand Down
2 changes: 1 addition & 1 deletion posthog/models/event.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ class Selector(object):

def __init__(self, selector: str):
self.parts = []
tags = re.split(" ", selector)
tags = re.split(" ", selector.strip())
tags.reverse()
for index, tag in enumerate(tags):
if tag == ">":
Expand Down
Loading

0 comments on commit 5db7fc8

Please sign in to comment.