diff --git a/.gitignore b/.gitignore index 497b728ce49..e63d02fa5b4 100644 --- a/.gitignore +++ b/.gitignore @@ -60,4 +60,6 @@ upgrade/ hogvm/typescript/dist .wokeignore plugin-transpiler/dist -.dlt \ No newline at end of file +*-esbuild-meta.json +*-esbuild-bundle-visualization.html +.dlt diff --git a/frontend/__snapshots__/components-html-elements-display--editable-display--dark.png b/frontend/__snapshots__/components-html-elements-display--editable-display--dark.png index f4573ad7274..076a178e2f4 100644 Binary files a/frontend/__snapshots__/components-html-elements-display--editable-display--dark.png and b/frontend/__snapshots__/components-html-elements-display--editable-display--dark.png differ diff --git a/frontend/__snapshots__/components-html-elements-display--editable-display--light.png b/frontend/__snapshots__/components-html-elements-display--editable-display--light.png index b3f041fe952..9b81a5abac8 100644 Binary files a/frontend/__snapshots__/components-html-elements-display--editable-display--light.png and b/frontend/__snapshots__/components-html-elements-display--editable-display--light.png differ diff --git a/frontend/__snapshots__/components-html-elements-display--editable-display-with-preselection--dark.png b/frontend/__snapshots__/components-html-elements-display--editable-display-with-preselection--dark.png index 12b08518ad9..b228c0b33f2 100644 Binary files a/frontend/__snapshots__/components-html-elements-display--editable-display-with-preselection--dark.png and b/frontend/__snapshots__/components-html-elements-display--editable-display-with-preselection--dark.png differ diff --git a/frontend/__snapshots__/components-html-elements-display--editable-display-with-preselection--light.png b/frontend/__snapshots__/components-html-elements-display--editable-display-with-preselection--light.png index 3af5884481d..b0b714fb2b0 100644 Binary files a/frontend/__snapshots__/components-html-elements-display--editable-display-with-preselection--light.png and b/frontend/__snapshots__/components-html-elements-display--editable-display-with-preselection--light.png differ diff --git a/frontend/__snapshots__/components-html-elements-display--with-uniqueness-check--dark.png b/frontend/__snapshots__/components-html-elements-display--with-uniqueness-check--dark.png index 6e0935a6dd3..be40d010138 100644 Binary files a/frontend/__snapshots__/components-html-elements-display--with-uniqueness-check--dark.png and b/frontend/__snapshots__/components-html-elements-display--with-uniqueness-check--dark.png differ diff --git a/frontend/__snapshots__/components-html-elements-display--with-uniqueness-check--light.png b/frontend/__snapshots__/components-html-elements-display--with-uniqueness-check--light.png index ef5c3a8ead8..0f88210edfa 100644 Binary files a/frontend/__snapshots__/components-html-elements-display--with-uniqueness-check--light.png and b/frontend/__snapshots__/components-html-elements-display--with-uniqueness-check--light.png differ diff --git a/frontend/build.mjs b/frontend/build.mjs index 4d616e8ea9e..febe4843ba7 100755 --- a/frontend/build.mjs +++ b/frontend/build.mjs @@ -51,9 +51,43 @@ await buildInParallel( // make sure we don't link to a global window.define banner: { js: 'var posthogToolbar = (function () { var define = undefined;' }, footer: { js: 'return posthogToolbar })();' }, - // This isn't great but we load some static assets at runtime for the toolbar and we can't sub in + // This isn't great, but we load some static assets at runtime for the toolbar, and we can't sub in // a variable at runtime it seems... publicPath: isDev ? '/static/' : 'https://app.posthog.com/static/', + alias: { + 'posthog-js': 'posthog-js-lite', + }, + writeMetaFile: true, + extraPlugins: [ + { + name: 'no-side-effects', + setup(build) { + // sideEffects in package.json lists files that _have_ side effects, + // but we only want to mark lemon-ui as having no side effects, + // so we'd have to list every other file and keep that up to date + // no thanks! + // a glob that negates the path doesn't seem to work + // so based off a comment from the esbuild author here + // https://github.com/evanw/esbuild/issues/1895#issuecomment-1003404929 + // we can add a plugin just for the toolbar build to mark lemon-ui as having no side effects + // that will allow tree-shaking and reduce the toolbar bundle size + // by over 40% at implementation time + build.onResolve({ filter: /^(lib|@posthog)\/lemon-ui/ }, async (args) => { + if (args.pluginData) { + return + } // Ignore this if we called ourselves + + const { path, ...rest } = args + rest.pluginData = true // Avoid infinite recursion + const result = await build.resolve(path, rest) + + result.sideEffects = false + + return result + }) + }, + }, + ], ...common, }, ], diff --git a/frontend/src/lib/components/Cards/TextCard/TextCardModal.tsx b/frontend/src/lib/components/Cards/TextCard/TextCardModal.tsx index c5e0f72a449..cbeab571702 100644 --- a/frontend/src/lib/components/Cards/TextCard/TextCardModal.tsx +++ b/frontend/src/lib/components/Cards/TextCard/TextCardModal.tsx @@ -4,7 +4,7 @@ import { textCardModalLogic } from 'lib/components/Cards/TextCard/textCardModalL import { PayGateMini } from 'lib/components/PayGateMini/PayGateMini' import { LemonButton } from 'lib/lemon-ui/LemonButton' import { LemonModal } from 'lib/lemon-ui/LemonModal' -import { LemonTextAreaMarkdown } from 'lib/lemon-ui/LemonTextArea/LemonTextArea' +import { LemonTextAreaMarkdown } from 'lib/lemon-ui/LemonTextArea' import { userLogic } from 'scenes/userLogic' import { AvailableFeature, DashboardType } from '~/types' diff --git a/frontend/src/lib/components/HTMLElementsDisplay/HTMLElementsDisplay.tsx b/frontend/src/lib/components/HTMLElementsDisplay/HTMLElementsDisplay.tsx index c3a44586e77..9a02c7c5019 100644 --- a/frontend/src/lib/components/HTMLElementsDisplay/HTMLElementsDisplay.tsx +++ b/frontend/src/lib/components/HTMLElementsDisplay/HTMLElementsDisplay.tsx @@ -1,5 +1,4 @@ import { useActions, useValues } from 'kea' -import { CodeSnippet } from 'lib/components/CodeSnippet' import { htmlElementsDisplayLogic } from 'lib/components/HTMLElementsDisplay/htmlElementsDisplayLogic' import { ParsedCSSSelector } from 'lib/components/HTMLElementsDisplay/preselectWithCSS' import { LemonBanner } from 'lib/lemon-ui/LemonBanner' @@ -130,8 +129,11 @@ export function HTMLElementsDisplay({ return (
{editable && !!parsedElements.length && ( -
- Selector: {chosenSelector} +
+
Selector:
+
+
{chosenSelector}
+
)} {checkUniqueness && ( diff --git a/frontend/src/lib/lemon-ui/LemonTextArea/LemonTextArea.stories.tsx b/frontend/src/lib/lemon-ui/LemonTextArea/LemonTextArea.stories.tsx index c0e55962e47..4db7d0ecf3b 100644 --- a/frontend/src/lib/lemon-ui/LemonTextArea/LemonTextArea.stories.tsx +++ b/frontend/src/lib/lemon-ui/LemonTextArea/LemonTextArea.stories.tsx @@ -1,7 +1,8 @@ import { Meta, StoryFn, StoryObj } from '@storybook/react' +import { LemonTextAreaMarkdown as _LemonTextMarkdown } from 'lib/lemon-ui/LemonTextArea/LemonTextAreaMarkdown' import { useState } from 'react' -import { LemonTextArea, LemonTextAreaMarkdown as _LemonTextMarkdown, LemonTextAreaProps } from './LemonTextArea' +import { LemonTextArea, LemonTextAreaProps } from './LemonTextArea' type Story = StoryObj const meta: Meta = { diff --git a/frontend/src/lib/lemon-ui/LemonTextArea/LemonTextArea.tsx b/frontend/src/lib/lemon-ui/LemonTextArea/LemonTextArea.tsx index d2acdd47eef..bc4d03cf125 100644 --- a/frontend/src/lib/lemon-ui/LemonTextArea/LemonTextArea.tsx +++ b/frontend/src/lib/lemon-ui/LemonTextArea/LemonTextArea.tsx @@ -1,20 +1,8 @@ import './LemonTextArea.scss' import clsx from 'clsx' -import { useValues } from 'kea' -import { TextContent } from 'lib/components/Cards/TextCard/TextCard' -import { useUploadFiles } from 'lib/hooks/useUploadFiles' -import { IconMarkdown, IconTools } from 'lib/lemon-ui/icons' -import { LemonFileInput } from 'lib/lemon-ui/LemonFileInput/LemonFileInput' -import { lemonToast } from 'lib/lemon-ui/LemonToast/LemonToast' -import { Link } from 'lib/lemon-ui/Link' -import { Tooltip } from 'lib/lemon-ui/Tooltip' -import posthog from 'posthog-js' -import React, { useRef, useState } from 'react' +import React, { useRef } from 'react' import TextareaAutosize from 'react-textarea-autosize' -import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic' - -import { LemonTabs } from '../LemonTabs' export interface LemonTextAreaProps extends Pick< @@ -74,84 +62,3 @@ export const LemonTextArea = React.forwardRef ) }) - -export const LemonTextAreaMarkdown = React.forwardRef( - function _LemonTextAreaMarkdown({ value, onChange, ...editAreaProps }, ref): JSX.Element { - const { objectStorageAvailable } = useValues(preflightLogic) - - const [isPreviewShown, setIsPreviewShown] = useState(false) - const dropRef = useRef(null) - - const { setFilesToUpload, filesToUpload, uploading } = useUploadFiles({ - onUpload: (url, fileName) => { - onChange?.(value + `\n\n![${fileName}](${url})`) - posthog.capture('markdown image uploaded', { name: fileName }) - }, - onError: (detail) => { - posthog.capture('markdown image upload failed', { error: detail }) - lemonToast.error(`Error uploading image: ${detail}`) - }, - }) - - return ( - setIsPreviewShown(key === 'preview')} - tabs={[ - { - key: 'write', - label: 'Write', - content: ( -
- -
- - Markdown formatting support -
- {objectStorageAvailable ? ( - - ) : ( -
- - - - - Add external images using{' '} - - {' '} - Markdown image links - - . - -
- )} -
- ), - }, - { - key: 'preview', - label: 'Preview', - content: value ? ( - - ) : ( - Nothing to preview - ), - }, - ]} - /> - ) - } -) diff --git a/frontend/src/lib/lemon-ui/LemonTextArea/LemonTextAreaMarkdown.tsx b/frontend/src/lib/lemon-ui/LemonTextArea/LemonTextAreaMarkdown.tsx new file mode 100644 index 00000000000..ac463f2aa3d --- /dev/null +++ b/frontend/src/lib/lemon-ui/LemonTextArea/LemonTextAreaMarkdown.tsx @@ -0,0 +1,94 @@ +import { useValues } from 'kea' +import { TextContent } from 'lib/components/Cards/TextCard/TextCard' +import { useUploadFiles } from 'lib/hooks/useUploadFiles' +import { IconMarkdown, IconTools } from 'lib/lemon-ui/icons' +import { LemonFileInput } from 'lib/lemon-ui/LemonFileInput' +import { LemonTabs } from 'lib/lemon-ui/LemonTabs' +import { LemonTextArea, LemonTextAreaProps } from 'lib/lemon-ui/LemonTextArea/LemonTextArea' +import { lemonToast } from 'lib/lemon-ui/LemonToast' +import { Link } from 'lib/lemon-ui/Link' +import { Tooltip } from 'lib/lemon-ui/Tooltip' +import posthog from 'posthog-js' +import React, { useRef, useState } from 'react' +import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic' + +export const LemonTextAreaMarkdown = React.forwardRef( + function _LemonTextAreaMarkdown({ value, onChange, ...editAreaProps }, ref): JSX.Element { + const { objectStorageAvailable } = useValues(preflightLogic) + + const [isPreviewShown, setIsPreviewShown] = useState(false) + const dropRef = useRef(null) + + const { setFilesToUpload, filesToUpload, uploading } = useUploadFiles({ + onUpload: (url, fileName) => { + onChange?.(value + `\n\n![${fileName}](${url})`) + posthog.capture('markdown image uploaded', { name: fileName }) + }, + onError: (detail) => { + posthog.capture('markdown image upload failed', { error: detail }) + lemonToast.error(`Error uploading image: ${detail}`) + }, + }) + + return ( + setIsPreviewShown(key === 'preview')} + tabs={[ + { + key: 'write', + label: 'Write', + content: ( +
+ +
+ + Markdown formatting support +
+ {objectStorageAvailable ? ( + + ) : ( +
+ + + + + Add external images using{' '} + + {' '} + Markdown image links + + . + +
+ )} +
+ ), + }, + { + key: 'preview', + label: 'Preview', + content: value ? ( + + ) : ( + Nothing to preview + ), + }, + ]} + /> + ) + } +) diff --git a/frontend/src/lib/lemon-ui/LemonTextArea/index.ts b/frontend/src/lib/lemon-ui/LemonTextArea/index.ts index 138d5df39dc..9a82ab79e0d 100644 --- a/frontend/src/lib/lemon-ui/LemonTextArea/index.ts +++ b/frontend/src/lib/lemon-ui/LemonTextArea/index.ts @@ -1,2 +1,3 @@ export type { LemonTextAreaProps } from './LemonTextArea' -export { LemonTextArea, LemonTextAreaMarkdown } from './LemonTextArea' +export { LemonTextArea } from './LemonTextArea' +export { LemonTextAreaMarkdown } from 'lib/lemon-ui/LemonTextArea/LemonTextAreaMarkdown' diff --git a/frontend/src/scenes/UpgradeModal.tsx b/frontend/src/scenes/UpgradeModal.tsx index ea065da4c6a..5c973096eb5 100644 --- a/frontend/src/scenes/UpgradeModal.tsx +++ b/frontend/src/scenes/UpgradeModal.tsx @@ -1,7 +1,7 @@ import { LemonButton, LemonModal } from '@posthog/lemon-ui' import { useActions, useValues } from 'kea' import { capitalizeFirstLetter } from 'lib/utils' -import { posthog } from 'posthog-js' +import posthog from 'posthog-js' import { sceneLogic } from './sceneLogic' import { urls } from './urls' diff --git a/frontend/src/toolbar/stats/HeatmapToolbarMenu.tsx b/frontend/src/toolbar/stats/HeatmapToolbarMenu.tsx index 41f411fb8da..2e552d95914 100644 --- a/frontend/src/toolbar/stats/HeatmapToolbarMenu.tsx +++ b/frontend/src/toolbar/stats/HeatmapToolbarMenu.tsx @@ -1,11 +1,13 @@ import { useActions, useValues } from 'kea' -import { DateFilter } from 'lib/components/DateFilter/DateFilter' +import { CUSTOM_OPTION_KEY } from 'lib/components/DateFilter/types' import { IconSync } from 'lib/lemon-ui/icons' import { LemonButton } from 'lib/lemon-ui/LemonButton' import { LemonInput } from 'lib/lemon-ui/LemonInput' +import { LemonMenu } from 'lib/lemon-ui/LemonMenu' import { LemonSwitch } from 'lib/lemon-ui/LemonSwitch' import { Spinner } from 'lib/lemon-ui/Spinner' import { Tooltip } from 'lib/lemon-ui/Tooltip' +import { dateFilterToText, dateMapping } from 'lib/utils' import { ToolbarMenu } from '~/toolbar/bar/ToolbarMenu' import { elementsLogic } from '~/toolbar/elements/elementsLogic' @@ -21,6 +23,13 @@ export const HeatmapToolbarMenu = (): JSX.Element => { const { setHeatmapFilter, loadMoreElementStats, setMatchLinksByHref } = useActions(heatmapLogic) const { setHighlightElement, setSelectedElement } = useActions(elementsLogic) + const dateItems = dateMapping + .filter((dm) => dm.key !== CUSTOM_OPTION_KEY) + .map((dateOption) => ({ + label: dateOption.key, + onClick: () => setHeatmapFilter({ date_from: dateOption.values[0], date_to: dateOption.values[1] }), + })) + return ( @@ -28,11 +37,11 @@ export const HeatmapToolbarMenu = (): JSX.Element => {
Use * as a wildcard
- setHeatmapFilter({ date_from, date_to })} - /> + + + {dateFilterToText(heatmapFilter.date_from, heatmapFilter.date_to, 'Last 7 days')} + + } diff --git a/frontend/utils.mjs b/frontend/utils.mjs index 2868ca57207..16ad8b984c5 100644 --- a/frontend/utils.mjs +++ b/frontend/utils.mjs @@ -247,7 +247,7 @@ function getBuiltEntryPoints(config, result) { let buildsInProgress = 0 export async function buildOrWatch(config) { - const { absWorkingDir, name, onBuildStart, onBuildComplete, ..._config } = config + const { absWorkingDir, name, onBuildStart, onBuildComplete, writeMetaFile, extraPlugins, ..._config } = config let buildPromise = null let buildAgain = false @@ -311,7 +311,9 @@ export async function buildOrWatch(config) { async function runBuild() { if (!esbuildContext) { - esbuildContext = await context({ ...commonConfig, ..._config }) + const combinedConfig = { ...commonConfig, ..._config } + combinedConfig.plugins = [...commonConfig.plugins, ...(extraPlugins || [])] + esbuildContext = await context(combinedConfig) } buildCount++ @@ -319,6 +321,14 @@ export async function buildOrWatch(config) { log({ name }) try { const buildResult = await esbuildContext.rebuild() + + if (writeMetaFile) { + await fs.writeFile( + `${config.name.toLowerCase().replace(' ', '-')}-esbuild-meta.json`, + JSON.stringify(buildResult.metafile) + ) + } + inputFiles = getInputFiles(buildResult) log({ success: true, name, time }) diff --git a/package.json b/package.json index d1b0d4e33b4..d703c28a69c 100644 --- a/package.json +++ b/package.json @@ -61,7 +61,8 @@ "prepare": "husky install", "mobile-replay:web:schema:build:json": "ts-json-schema-generator -f tsconfig.json --path 'node_modules/@rrweb/types/dist/index.d.ts' --type 'eventWithTime' --expose all --no-top-ref --out ee/frontend/mobile-replay/schema/web/rr-web-schema.json && prettier --write ee/frontend/mobile-replay/schema/web/rr-web-schema.json", "mobile-replay:mobile:schema:build:json": "ts-json-schema-generator -f tsconfig.json --path 'ee/frontend/mobile-replay/mobile.types.ts' --type 'mobileEventWithTime' --expose all --no-top-ref --out ee/frontend/mobile-replay/schema/mobile/rr-mobile-schema.json && prettier --write ee/frontend/mobile-replay/schema/mobile/rr-mobile-schema.json", - "mobile-replay:schema:build:json": "pnpm mobile-replay:web:schema:build:json && pnpm mobile-replay:mobile:schema:build:json" + "mobile-replay:schema:build:json": "pnpm mobile-replay:web:schema:build:json && pnpm mobile-replay:mobile:schema:build:json", + "visualize-toolbar-bundle": "pnpm exec esbuild-visualizer --metadata ./toolbar-esbuild-meta.json --filename=toolbar-esbuild-bundle-visualization.html" }, "dependencies": { "@ant-design/icons": "^4.7.0", @@ -244,6 +245,7 @@ "cypress": "^13.3.0", "cypress-axe": "^1.5.0", "cypress-terminal-report": "^5.3.7", + "esbuild-visualizer": "^0.6.0", "eslint": "^8.52.0", "eslint-config-prettier": "^9.0.0", "eslint-plugin-compat": "^4.2.0", @@ -319,6 +321,10 @@ "eslint -c .eslintrc.js --fix", "prettier --write" ], + "frontend/*.mjs": [ + "eslint -c .eslintrc.js --fix", + "prettier --write" + ], "ee/frontend/**/*.{js,jsx,mjs,ts,tsx}": [ "eslint -c .eslintrc.js --fix", "prettier --write" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0e9a30f1702..0a0678d28ba 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -545,6 +545,9 @@ devDependencies: cypress-terminal-report: specifier: ^5.3.7 version: 5.3.7(cypress@13.3.0) + esbuild-visualizer: + specifier: ^0.6.0 + version: 0.6.0 eslint: specifier: ^8.52.0 version: 8.52.0 @@ -11627,6 +11630,16 @@ packages: sass-embedded: 1.70.0 dev: false + /esbuild-visualizer@0.6.0: + resolution: {integrity: sha512-oNK3JAhC7+re93VTtUdWJKTDVnA2qXPAjCAoaw9OxEFUXztszw3kcaK46u1U790T8FdUBAWv6F9Xt59P8nJCVA==} + engines: {node: '>=18'} + hasBin: true + dependencies: + open: 8.4.2 + picomatch: 2.3.1 + yargs: 17.6.2 + dev: true + /esbuild@0.18.20: resolution: {integrity: sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==} engines: {node: '>=12'}