diff --git a/.github/workflows/ci-frontend.yml b/.github/workflows/ci-frontend.yml
index 8d7ef2d216a..10b89d5b5be 100644
--- a/.github/workflows/ci-frontend.yml
+++ b/.github/workflows/ci-frontend.yml
@@ -30,7 +30,17 @@ jobs:
uses: actions/setup-node@v3
with:
node-version: 18
- cache: pnpm
+
+ - name: Get pnpm cache directory path
+ id: pnpm-cache-dir
+ run: echo "PNPM_STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT
+
+ - uses: actions/cache@v3
+ id: pnpm-cache
+ with:
+ path: ${{ steps.pnpm-cache-dir.outputs.PNPM_STORE_PATH }}
+ key: ${{ runner.os }}-pnpm-cypress-${{ hashFiles('pnpm-lock.yaml') }}
+ restore-keys: ${{ runner.os }}-pnpm-cypress-
- name: Install package.json dependencies with pnpm
run: pnpm install --frozen-lockfile
diff --git a/frontend/__snapshots__/lemon-ui-lemon-file-input--default.png b/frontend/__snapshots__/lemon-ui-lemon-file-input--default.png
index 0204ecc04cd..8f354b12f2b 100644
Binary files a/frontend/__snapshots__/lemon-ui-lemon-file-input--default.png and b/frontend/__snapshots__/lemon-ui-lemon-file-input--default.png differ
diff --git a/frontend/__snapshots__/lemon-ui-lemon-text-area--lemon-text-markdown.png b/frontend/__snapshots__/lemon-ui-lemon-text-area--lemon-text-markdown.png
index fe295d1272f..184490f81c8 100644
Binary files a/frontend/__snapshots__/lemon-ui-lemon-text-area--lemon-text-markdown.png and b/frontend/__snapshots__/lemon-ui-lemon-text-area--lemon-text-markdown.png differ
diff --git a/frontend/src/layout/navigation/SideBar/SideBar.tsx b/frontend/src/layout/navigation/SideBar/SideBar.tsx
index 5ec1c5285fc..c3bf0ac1ce5 100644
--- a/frontend/src/layout/navigation/SideBar/SideBar.tsx
+++ b/frontend/src/layout/navigation/SideBar/SideBar.tsx
@@ -282,7 +282,7 @@ export function SideBar({ children }: { children: React.ReactNode }): JSX.Elemen
function AppUrls({ setIsToolbarLaunchShown }: { setIsToolbarLaunchShown: (state: boolean) => void }): JSX.Element {
const { authorizedUrls, launchUrl, suggestionsLoading } = useValues(
- authorizedUrlListLogic({ type: AuthorizedUrlListType.TOOLBAR_URLS })
+ authorizedUrlListLogic({ type: AuthorizedUrlListType.TOOLBAR_URLS, actionId: null })
)
return (
diff --git a/frontend/src/lib/components/AuthorizedUrlList/AuthorizedUrlList.tsx b/frontend/src/lib/components/AuthorizedUrlList/AuthorizedUrlList.tsx
index f48a1d86753..e227641bc69 100644
--- a/frontend/src/lib/components/AuthorizedUrlList/AuthorizedUrlList.tsx
+++ b/frontend/src/lib/components/AuthorizedUrlList/AuthorizedUrlList.tsx
@@ -2,11 +2,7 @@ import clsx from 'clsx'
import { useActions, useValues } from 'kea'
import { LemonTag } from 'lib/lemon-ui/LemonTag/LemonTag'
import { LemonButton } from 'lib/lemon-ui/LemonButton'
-import {
- AuthorizedUrlListType as AuthorizedUrlListType,
- authorizedUrlListLogic,
- AuthorizedUrlListProps,
-} from './authorizedUrlListLogic'
+import { AuthorizedUrlListType as AuthorizedUrlListType, authorizedUrlListLogic } from './authorizedUrlListLogic'
import { IconDelete, IconEdit, IconOpenInApp, IconPlus } from 'lib/lemon-ui/icons'
import { Spinner } from 'lib/lemon-ui/Spinner/Spinner'
import { Form } from 'kea-forms'
@@ -48,7 +44,7 @@ function EmptyState({
}
function AuthorizedUrlForm({ actionId, type }: AuthorizedUrlListProps): JSX.Element {
- const logic = authorizedUrlListLogic({ actionId, type })
+ const logic = authorizedUrlListLogic({ actionId: actionId ?? null, type })
const { isProposedUrlSubmitting } = useValues(logic)
const { cancelProposingUrl } = useActions(logic)
return (
@@ -78,12 +74,17 @@ function AuthorizedUrlForm({ actionId, type }: AuthorizedUrlListProps): JSX.Elem
)
}
+export interface AuthorizedUrlListProps {
+ actionId?: number
+ type: AuthorizedUrlListType
+}
+
export function AuthorizedUrlList({
actionId,
type,
addText = 'Add',
}: AuthorizedUrlListProps & { addText?: string }): JSX.Element {
- const logic = authorizedUrlListLogic({ actionId, type })
+ const logic = authorizedUrlListLogic({ actionId: actionId ?? null, type })
const {
urlsKeyed,
suggestionsLoading,
diff --git a/frontend/src/lib/components/AuthorizedUrlList/authorizedUrlListLogic.test.ts b/frontend/src/lib/components/AuthorizedUrlList/authorizedUrlListLogic.test.ts
index bfef90a9d4f..1d35529e7ae 100644
--- a/frontend/src/lib/components/AuthorizedUrlList/authorizedUrlListLogic.test.ts
+++ b/frontend/src/lib/components/AuthorizedUrlList/authorizedUrlListLogic.test.ts
@@ -28,6 +28,7 @@ describe('the authorized urls list logic', () => {
initKeaTests()
logic = authorizedUrlListLogic({
type: AuthorizedUrlListType.TOOLBAR_URLS,
+ actionId: null,
})
logic.mount()
})
@@ -106,6 +107,7 @@ describe('the authorized urls list logic', () => {
beforeEach(() => {
logic = authorizedUrlListLogic({
type: AuthorizedUrlListType.RECORDING_DOMAINS,
+ actionId: null,
})
logic.mount()
})
diff --git a/frontend/src/lib/components/AuthorizedUrlList/authorizedUrlListLogic.ts b/frontend/src/lib/components/AuthorizedUrlList/authorizedUrlListLogic.ts
index 034d822f678..a9bd8d0fb24 100644
--- a/frontend/src/lib/components/AuthorizedUrlList/authorizedUrlListLogic.ts
+++ b/frontend/src/lib/components/AuthorizedUrlList/authorizedUrlListLogic.ts
@@ -62,19 +62,18 @@ export const validateProposedURL = (
}
/** defaultIntent: whether to launch with empty intent (i.e. toolbar mode is default) */
-export function appEditorUrl(appUrl?: string, actionId?: number, defaultIntent?: boolean): string {
- // See
- // https://github.com/PostHog/posthog-js/blob/f7119c7542c940354719a9ba8120a08ba25b5ae8/src/extensions/toolbar.ts#L52
- // for where these params are passed.
- const params: ToolbarParams = {
+export function appEditorUrl(appUrl: string, actionId?: number | null, defaultIntent?: boolean): string {
+ // See https://github.com/PostHog/posthog-js/blob/f7119c/src/extensions/toolbar.ts#L52 for where these params
+ // are passed. `appUrl` is an extra `redirect_to_site` param.
+ const params: ToolbarParams & { appUrl: string } = {
userIntent: defaultIntent ? undefined : actionId ? 'edit-action' : 'add-action',
// Make sure to pass the app url, otherwise the api_host will be used by
// the toolbar, which isn't correct when used behind a reverse proxy as
// we require e.g. SSO login to the app, which will not work when placed
// behind a proxy unless we register each domain with the OAuth2 client.
apiURL: window.location.origin,
+ appUrl,
...(actionId ? { actionId } : {}),
- ...(appUrl ? { appUrl } : {}),
}
return '/api/user/redirect_to_site/' + encodeParams(params, '?')
}
@@ -87,14 +86,14 @@ export interface KeyedAppUrl {
originalIndex: number
}
-export interface AuthorizedUrlListProps {
- actionId?: number
+export interface AuthorizedUrlListLogicProps {
+ actionId: number | null
type: AuthorizedUrlListType
}
export const authorizedUrlListLogic = kea
([
path((key) => ['lib', 'components', 'AuthorizedUrlList', 'authorizedUrlListLogic', key]),
key((props) => `${props.type}-${props.actionId}`),
- props({} as AuthorizedUrlListProps),
+ props({} as AuthorizedUrlListLogicProps),
connect({
values: [teamLogic, ['currentTeam', 'currentTeamId']],
actions: [teamLogic, ['updateCurrentTeam']],
@@ -280,7 +279,7 @@ export const authorizedUrlListLogic = kea([
actions.resetProposedUrl()
},
})),
- selectors(({ props }) => ({
+ selectors({
urlToEdit: [
(s) => [s.authorizedUrls, s.editUrlIndex],
(authorizedUrls, editUrlIndex) => {
@@ -319,10 +318,10 @@ export const authorizedUrlListLogic = kea([
.map((result) => result.item)
},
],
- launchUrl: [() => [], () => (url: string) => appEditorUrl(url, props.actionId, !props.actionId)],
+ launchUrl: [(_, p) => [p.actionId], (actionId) => (url: string) => appEditorUrl(url, actionId, !actionId)],
isAddUrlFormVisible: [(s) => [s.editUrlIndex], (editUrlIndex) => editUrlIndex === -1],
- onlyAllowDomains: [() => [], () => props.type === AuthorizedUrlListType.RECORDING_DOMAINS],
- })),
+ onlyAllowDomains: [(_, p) => [p.type], (type) => type === AuthorizedUrlListType.RECORDING_DOMAINS],
+ }),
urlToAction(({ actions }) => ({
[urls.toolbarLaunch()]: (_, searchParams) => {
if (searchParams.addNew) {
diff --git a/frontend/src/lib/components/HelpButton/HelpButton.tsx b/frontend/src/lib/components/HelpButton/HelpButton.tsx
index f036e31955f..0d19537ad86 100644
--- a/frontend/src/lib/components/HelpButton/HelpButton.tsx
+++ b/frontend/src/lib/components/HelpButton/HelpButton.tsx
@@ -3,27 +3,26 @@ import { kea, useActions, useValues } from 'kea'
import { eventUsageLogic } from 'lib/utils/eventUsageLogic'
import { HelpType } from '~/types'
import type { helpButtonLogicType } from './HelpButtonType'
-import { Popover } from 'lib/lemon-ui/Popover/Popover'
-import { LemonButton } from 'lib/lemon-ui/LemonButton'
import {
IconArrowDropDown,
IconArticle,
IconHelpOutline,
- IconMail,
IconQuestionAnswer,
IconMessages,
IconFlare,
- IconTrendingUp,
IconLive,
+ IconSupport,
+ IconFeedback,
+ IconBugReport,
} from 'lib/lemon-ui/icons'
import clsx from 'clsx'
import { Placement } from '@floating-ui/react'
import { DefaultAction, inAppPromptLogic } from 'lib/logic/inAppPrompt/inAppPromptLogic'
import { hedgehogbuddyLogic } from '../HedgehogBuddy/hedgehogbuddyLogic'
import { HedgehogBuddyWithLogic } from '../HedgehogBuddy/HedgehogBuddy'
-import { navigationLogic } from '~/layout/navigation/navigationLogic'
import { supportLogic } from '../Support/supportLogic'
-import SupportForm from '../Support/SupportForm'
+import { SupportModal } from '../Support/SupportModal'
+import { LemonMenu } from 'lib/lemon-ui/LemonMenu'
const HELP_UTM_TAGS = '?utm_medium=in-product&utm_campaign=help-button-top'
@@ -88,111 +87,103 @@ export function HelpButton({
const { isPromptVisible } = useValues(inAppPromptLogic)
const { hedgehogModeEnabled } = useValues(hedgehogbuddyLogic)
const { setHedgehogModeEnabled } = useActions(hedgehogbuddyLogic)
- const { toggleActivationSideBar } = useActions(navigationLogic)
const { openSupportForm } = useActions(supportLogic)
return (
<>
-
- {!contactOnly && (
- }
- status="stealth"
- fullWidth
- onClick={() => {
+ ,
+ label: "What's new?",
+ onClick: () => {
reportHelpButtonUsed(HelpType.Updates)
hideHelp()
- }}
- to={`https://posthog.com/blog/categories/posthog-news`}
- targetBlank
- >
- What's new?
-
- )}
- }
- status="stealth"
- fullWidth
- onClick={() => {
- reportHelpButtonUsed(HelpType.SupportForm)
- openSupportForm()
- hideHelp()
- }}
- >
- Report bug / get support
-
- {!contactOnly && (
- }
- status="stealth"
- fullWidth
- onClick={() => {
+ },
+ to: 'https://posthog.com/blog/categories/posthog-news',
+ targetBlank: true,
+ },
+ ],
+ },
+ {
+ items: [
+ {
+ label: 'Ask on the forum',
+ icon: ,
+ onClick: () => {
+ reportHelpButtonUsed(HelpType.Slack)
+ hideHelp()
+ },
+ to: `https://posthog.com/questions${HELP_UTM_TAGS}`,
+ targetBlank: true,
+ },
+ {
+ label: 'Report a bug',
+ icon: ,
+ onClick: () => {
+ reportHelpButtonUsed(HelpType.SupportForm)
+ openSupportForm('bug')
+ hideHelp()
+ },
+ },
+ {
+ label: 'Give feedback',
+ icon: ,
+ onClick: () => {
+ reportHelpButtonUsed(HelpType.SupportForm)
+ openSupportForm('feedback')
+ hideHelp()
+ },
+ },
+ {
+ label: 'Get support',
+ icon: ,
+ onClick: () => {
+ reportHelpButtonUsed(HelpType.SupportForm)
+ openSupportForm('support')
+ hideHelp()
+ },
+ },
+ ],
+ },
+ !contactOnly && {
+ items: [
+ {
+ label: 'Read the docs',
+ icon: ,
+ onClick: () => {
reportHelpButtonUsed(HelpType.Docs)
hideHelp()
- }}
- to={`https://posthog.com/docs${HELP_UTM_TAGS}`}
- targetBlank
- >
- Read the docs
-
- )}
- }
- status="stealth"
- fullWidth
- onClick={() => {
- reportHelpButtonUsed(HelpType.Slack)
- hideHelp()
- }}
- to={`https://posthog.com/questions${HELP_UTM_TAGS}`}
- targetBlank
- >
- Ask a question on our forum
-
- }
- status="stealth"
- fullWidth
- onClick={() => {
- toggleActivationSideBar()
- hideHelp()
- }}
- >
- Open Quick Start
-
- {validProductTourSequences.length > 0 && (
- }
- status="stealth"
- fullWidth
- onClick={() => {
+ },
+ to: `https://posthog.com/docs${HELP_UTM_TAGS}`,
+ targetBlank: true,
+ },
+ validProductTourSequences.length > 0 && {
+ label: isPromptVisible ? 'Stop tutorial' : 'Explain this page',
+ icon: ,
+ onClick: () => {
if (isPromptVisible) {
promptAction(DefaultAction.SKIP)
} else {
runFirstValidSequence({ runDismissedOrCompleted: true })
}
hideHelp()
- }}
- >
- {isPromptVisible ? 'Stop tutorial' : 'Explain this page'}
-
- )}
- }
- status="stealth"
- fullWidth
- onClick={() => {
- setHedgehogModeEnabled(!hedgehogModeEnabled)
- hideHelp()
- }}
- >
- {hedgehogModeEnabled ? 'Disable' : 'Enable'} Hedgehog Mode
-
- >
- }
- onClickOutside={hideHelp}
+ },
+ },
+ {
+ label: `${hedgehogModeEnabled ? 'Disable' : 'Enable'} hedgehog mode`,
+ icon: ,
+ onClick: () => {
+ setHedgehogModeEnabled(!hedgehogModeEnabled)
+ hideHelp()
+ },
+ },
+ ],
+ },
+ ]}
+ onVisibilityChange={(visible) => !visible && hideHelp()}
visible={isHelpVisible}
placement={placement}
actionable
@@ -205,9 +196,9 @@ export function HelpButton({
>
)}
-
+