diff --git a/frontend/src/layout/navigation-3000/sidepanel/SidePanel.tsx b/frontend/src/layout/navigation-3000/sidepanel/SidePanel.tsx index b892dc496221a..3411db82b1465 100644 --- a/frontend/src/layout/navigation-3000/sidepanel/SidePanel.tsx +++ b/frontend/src/layout/navigation-3000/sidepanel/SidePanel.tsx @@ -1,7 +1,7 @@ import { LemonButton } from '@posthog/lemon-ui' import './SidePanel.scss' import { useActions, useValues } from 'kea' -import { SidePanelTab, sidePanelLogic } from './sidePanelLogic' +import { sidePanelLogic } from './sidePanelLogic' import clsx from 'clsx' import { Resizer } from 'lib/components/Resizer/Resizer' import { useRef } from 'react' @@ -12,6 +12,8 @@ import { SidePanelSupport } from './panels/SidePanelSupport' import { NotebookPanel } from 'scenes/notebooks/NotebookPanel/NotebookPanel' import { SidePanelActivation, SidePanelActivationIcon } from './panels/SidePanelActivation' import { SidePanelSettings } from './panels/SidePanelSettings' +import { SidePanelTab } from '~/types' +import { sidePanelStateLogic } from './sidePanelStateLogic' export const SidePanelTabs: Record = { [SidePanelTab.Notebooks]: { @@ -43,8 +45,9 @@ export const SidePanelTabs: Record
- {Object.entries(SidePanelTabs) - .filter(([tab]) => enabledTabs.includes(tab as SidePanelTab)) - .map(([tab, { label, Icon }]) => ( + {visibleTabs.map((tab: SidePanelTab) => { + const { Icon, label } = SidePanelTabs[tab] + return ( } @@ -98,7 +101,8 @@ export function SidePanel(): JSX.Element | null { > {label} - ))} + ) + })}
diff --git a/frontend/src/layout/navigation-3000/sidepanel/panels/SidePanelSupport.tsx b/frontend/src/layout/navigation-3000/sidepanel/panels/SidePanelSupport.tsx index 4cfa7be0130e0..71344bb84b4a6 100644 --- a/frontend/src/layout/navigation-3000/sidepanel/panels/SidePanelSupport.tsx +++ b/frontend/src/layout/navigation-3000/sidepanel/panels/SidePanelSupport.tsx @@ -1,12 +1,13 @@ import { useActions, useValues } from 'kea' import { SupportForm, SupportFormButtons } from 'lib/components/Support/SupportForm' -import { SidePanelTab, sidePanelLogic } from '../sidePanelLogic' import { supportLogic } from 'lib/components/Support/supportLogic' import { useEffect } from 'react' +import { sidePanelStateLogic } from '../sidePanelStateLogic' +import { SidePanelTab } from '~/types' export const SidePanelSupport = (): JSX.Element => { - const { closeSidePanel } = useActions(sidePanelLogic) - const { selectedTab } = useValues(sidePanelLogic) + const { closeSidePanel } = useActions(sidePanelStateLogic) + const { selectedTab } = useValues(sidePanelStateLogic) const theLogic = supportLogic({ onClose: () => closeSidePanel(SidePanelTab.Feedback) }) const { title } = useValues(theLogic) diff --git a/frontend/src/layout/navigation-3000/sidepanel/panels/sidePanelDocsLogic.ts b/frontend/src/layout/navigation-3000/sidepanel/panels/sidePanelDocsLogic.ts index a74907018531c..ed5ae45ebde09 100644 --- a/frontend/src/layout/navigation-3000/sidepanel/panels/sidePanelDocsLogic.ts +++ b/frontend/src/layout/navigation-3000/sidepanel/panels/sidePanelDocsLogic.ts @@ -1,14 +1,15 @@ import { actions, kea, reducers, path, listeners, connect } from 'kea' -import { SidePanelTab, sidePanelLogic } from '../sidePanelLogic' import type { sidePanelDocsLogicType } from './sidePanelDocsLogicType' +import { sidePanelStateLogic } from '../sidePanelStateLogic' +import { SidePanelTab } from '~/types' const POSTHOG_COM_DOMAIN = 'https://posthog.com' export const sidePanelDocsLogic = kea([ path(['scenes', 'navigation', 'sidepanel', 'sidePanelDocsLogic']), connect({ - actions: [sidePanelLogic, ['openSidePanel', 'closeSidePanel']], + actions: [sidePanelStateLogic, ['openSidePanel', 'closeSidePanel']], }), actions({ diff --git a/frontend/src/layout/navigation-3000/sidepanel/panels/sidePanelSettingsLogic.tsx b/frontend/src/layout/navigation-3000/sidepanel/panels/sidePanelSettingsLogic.tsx index fe8cc899921fb..da07a199f139d 100644 --- a/frontend/src/layout/navigation-3000/sidepanel/panels/sidePanelSettingsLogic.tsx +++ b/frontend/src/layout/navigation-3000/sidepanel/panels/sidePanelSettingsLogic.tsx @@ -1,18 +1,19 @@ import { actions, kea, reducers, path, listeners, connect } from 'kea' -import { SidePanelTab, sidePanelLogic } from '../sidePanelLogic' -import { SettingsLogicProps } from 'scenes/settings/settingsLogic' +import { Settings } from 'scenes/settings/Settings' import { featureFlagLogic } from 'lib/logic/featureFlagLogic' import { FEATURE_FLAGS } from 'lib/constants' import { LemonDialog } from '@posthog/lemon-ui' -import { Settings } from 'scenes/settings/Settings' import type { sidePanelSettingsLogicType } from './sidePanelSettingsLogicType' +import { sidePanelStateLogic } from '../sidePanelStateLogic' +import { SidePanelTab } from '~/types' +import { SettingsLogicProps } from 'scenes/settings/settingsLogic' export const sidePanelSettingsLogic = kea([ path(['scenes', 'navigation', 'sidepanel', 'sidePanelSettingsLogic']), connect({ values: [featureFlagLogic, ['featureFlags']], - actions: [sidePanelLogic, ['openSidePanel', 'closeSidePanel']], + actions: [sidePanelStateLogic, ['openSidePanel', 'closeSidePanel']], }), actions({ diff --git a/frontend/src/layout/navigation-3000/sidepanel/sidePanelLogic.tsx b/frontend/src/layout/navigation-3000/sidepanel/sidePanelLogic.tsx index de1b3afc00805..607e13c2b5fb5 100644 --- a/frontend/src/layout/navigation-3000/sidepanel/sidePanelLogic.tsx +++ b/frontend/src/layout/navigation-3000/sidepanel/sidePanelLogic.tsx @@ -1,47 +1,19 @@ -import { actions, kea, reducers, path, listeners, selectors, connect } from 'kea' +import { kea, path, selectors, connect } from 'kea' import type { sidePanelLogicType } from './sidePanelLogicType' import { featureFlagLogic } from 'lib/logic/featureFlagLogic' import { FEATURE_FLAGS } from 'lib/constants' import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic' - -export enum SidePanelTab { - Notebooks = 'notebook', - Feedback = 'feedback', - Docs = 'docs', - Activation = 'activation', - Settings = 'settings', -} +import { activationLogic } from 'lib/components/ActivationSidebar/activationLogic' +import { SidePanelTab } from '~/types' +import { sidePanelStateLogic } from './sidePanelStateLogic' export const sidePanelLogic = kea([ path(['scenes', 'navigation', 'sidepanel', 'sidePanelLogic']), - actions({ - setSidePanelOpen: (open: boolean) => ({ open }), - openSidePanel: (tab: SidePanelTab) => ({ tab }), - closeSidePanel: (tab?: SidePanelTab) => ({ tab }), - }), - connect({ values: [featureFlagLogic, ['featureFlags'], preflightLogic, ['isCloudOrDev']], }), - reducers(() => ({ - selectedTab: [ - null as SidePanelTab | null, - { persist: true }, - { - openSidePanel: (_, { tab }) => tab, - }, - ], - sidePanelOpen: [ - false, - { persist: true }, - { - setSidePanelOpen: (_, { open }) => open, - }, - ], - })), - selectors({ enabledTabs: [ (s) => [s.featureFlags, s.isCloudOrDev], @@ -60,26 +32,45 @@ export const sidePanelLogic = kea([ tabs.push(SidePanelTab.Docs) } - tabs.push(SidePanelTab.Activation) tabs.push(SidePanelTab.Settings) + tabs.push(SidePanelTab.Activation) return tabs }, ], - }), - listeners(({ actions, values }) => ({ - openSidePanel: () => { - actions.setSidePanelOpen(true) - }, - closeSidePanel: ({ tab }) => { - if (!tab) { - // If we aren't specifiying the tab we always close - actions.setSidePanelOpen(false) - } else if (values.selectedTab === tab) { - // Otherwise we only close it if the tab is the currently open one - actions.setSidePanelOpen(false) - } - }, - })), + visibleTabs: [ + (s) => [ + s.enabledTabs, + sidePanelStateLogic.selectors.selectedTab, + sidePanelStateLogic.selectors.sidePanelOpen, + activationLogic.selectors.isReady, + activationLogic.selectors.hasCompletedAllTasks, + ], + ( + enabledTabs, + selectedTab, + sidePanelOpen, + activationIsReady, + activationHasCompletedAllTasks + ): SidePanelTab[] => { + return enabledTabs.filter((tab: any) => { + if (tab === selectedTab && sidePanelOpen) { + return true + } + + // Hide certain tabs unless they are selected + if ([SidePanelTab.Settings].includes(tab)) { + return false + } + + if (tab === SidePanelTab.Activation && (!activationIsReady || activationHasCompletedAllTasks)) { + return false + } + + return true + }) + }, + ], + }), ]) diff --git a/frontend/src/layout/navigation-3000/sidepanel/sidePanelStateLogic.tsx b/frontend/src/layout/navigation-3000/sidepanel/sidePanelStateLogic.tsx new file mode 100644 index 0000000000000..2c29443facbc6 --- /dev/null +++ b/frontend/src/layout/navigation-3000/sidepanel/sidePanelStateLogic.tsx @@ -0,0 +1,48 @@ +import { actions, kea, listeners, path, reducers } from 'kea' +import { SidePanelTab } from '~/types' + +import type { sidePanelStateLogicType } from './sidePanelStateLogicType' + +// The side panel imports a lot of other components so this allows us to avoid circular dependencies + +export const sidePanelStateLogic = kea([ + path(['scenes', 'navigation', 'sidepanel', 'sidePanelStateLogic']), + actions({ + openSidePanel: (tab: SidePanelTab) => ({ tab }), + closeSidePanel: (tab?: SidePanelTab) => ({ tab }), + setSidePanelOpen: (open: boolean) => ({ open }), + }), + + reducers(() => ({ + selectedTab: [ + null as SidePanelTab | null, + { persist: true }, + { + openSidePanel: (_, { tab }) => tab, + }, + ], + sidePanelOpen: [ + false, + { persist: true }, + { + setSidePanelOpen: (_, { open }) => open, + }, + ], + })), + listeners(({ actions, values }) => ({ + // NOTE: We explicitly reference the actions instead of connecting so that people don't accidentally + // use this logic instead of sidePanelStateLogic + openSidePanel: () => { + actions.setSidePanelOpen(true) + }, + closeSidePanel: ({ tab }) => { + if (!tab) { + // If we aren't specifiying the tab we always close + actions.setSidePanelOpen(false) + } else if (values.selectedTab === tab) { + // Otherwise we only close it if the tab is the currently open one + actions.setSidePanelOpen(false) + } + }, + })), +]) diff --git a/frontend/src/lib/components/Support/supportLogic.ts b/frontend/src/lib/components/Support/supportLogic.ts index 266ead19094db..aae39aeaae649 100644 --- a/frontend/src/lib/components/Support/supportLogic.ts +++ b/frontend/src/lib/components/Support/supportLogic.ts @@ -3,7 +3,7 @@ import { userLogic } from 'scenes/userLogic' import type { supportLogicType } from './supportLogicType' import { forms } from 'kea-forms' -import { Region, TeamType, UserType } from '~/types' +import { Region, SidePanelTab, TeamType, UserType } from '~/types' import { uuid } from 'lib/utils' import posthog from 'posthog-js' import { lemonToast } from 'lib/lemon-ui/lemonToast' @@ -12,7 +12,7 @@ import { captureException } from '@sentry/react' import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic' import { teamLogic } from 'scenes/teamLogic' import * as Sentry from '@sentry/react' -import { SidePanelTab, sidePanelLogic } from '~/layout/navigation-3000/sidepanel/sidePanelLogic' +import { sidePanelStateLogic } from '~/layout/navigation-3000/sidepanel/sidePanelStateLogic' function getSessionReplayLink(): string { const link = posthog @@ -111,7 +111,7 @@ export const supportLogic = kea([ path(['lib', 'components', 'support', 'supportLogic']), connect(() => ({ values: [userLogic, ['user'], preflightLogic, ['preflight']], - actions: [sidePanelLogic, ['openSidePanel', 'closeSidePanel']], + actions: [sidePanelStateLogic, ['openSidePanel']], })), actions(() => ({ closeSupportForm: () => true, diff --git a/frontend/src/lib/lemon-ui/Link/Link.tsx b/frontend/src/lib/lemon-ui/Link/Link.tsx index 0e3bad276c1a4..12c1c9e966a47 100644 --- a/frontend/src/lib/lemon-ui/Link/Link.tsx +++ b/frontend/src/lib/lemon-ui/Link/Link.tsx @@ -5,9 +5,10 @@ import clsx from 'clsx' import './Link.scss' import { IconOpenInNew } from '../icons' import { Tooltip } from '../Tooltip' -import { useNotebookDrag } from 'scenes/notebooks/AddToNotebook/DraggableToNotebook' import { useFeatureFlag } from 'lib/hooks/useFeatureFlag' import { useActions } from 'kea' + +import { useNotebookDrag } from 'scenes/notebooks/AddToNotebook/DraggableToNotebook' import { sidePanelDocsLogic } from '~/layout/navigation-3000/sidepanel/panels/sidePanelDocsLogic' type RoutePart = string | Record diff --git a/frontend/src/scenes/notebooks/NotebookPanel/notebookPanelLogic.ts b/frontend/src/scenes/notebooks/NotebookPanel/notebookPanelLogic.ts index 55c348a66ddd2..0f18ce5290ece 100644 --- a/frontend/src/scenes/notebooks/NotebookPanel/notebookPanelLogic.ts +++ b/frontend/src/scenes/notebooks/NotebookPanel/notebookPanelLogic.ts @@ -4,24 +4,29 @@ import { HTMLProps } from 'react' import { EditorFocusPosition } from '../Notebook/utils' import type { notebookPanelLogicType } from './notebookPanelLogicType' -import { NotebookNodeResource } from '~/types' -import { SidePanelTab, sidePanelLogic } from '~/layout/navigation-3000/sidepanel/sidePanelLogic' +import { NotebookNodeResource, SidePanelTab } from '~/types' import { featureFlagLogic } from 'lib/logic/featureFlagLogic' import { FEATURE_FLAGS } from 'lib/constants' import { notebookPopoverLogic } from './notebookPopoverLogic' +import { sidePanelStateLogic } from '~/layout/navigation-3000/sidepanel/sidePanelStateLogic' export const notebookPanelLogic = kea([ path(['scenes', 'notebooks', 'Notebook', 'notebookPanelLogic']), connect({ values: [ - sidePanelLogic, + sidePanelStateLogic, ['sidePanelOpen', 'selectedTab'], featureFlagLogic, ['featureFlags'], notebookPopoverLogic, ['popoverVisibility'], ], - actions: [sidePanelLogic, ['openSidePanel', 'closeSidePanel'], notebookPopoverLogic, ['setPopoverVisibility']], + actions: [ + sidePanelStateLogic, + ['openSidePanel', 'closeSidePanel'], + notebookPopoverLogic, + ['setPopoverVisibility'], + ], }), actions({ selectNotebook: (id: string, autofocus: EditorFocusPosition | undefined = undefined) => ({ id, autofocus }), diff --git a/frontend/src/types.ts b/frontend/src/types.ts index 31652f6674cd8..b94c51e0aa442 100644 --- a/frontend/src/types.ts +++ b/frontend/src/types.ts @@ -3332,3 +3332,11 @@ export enum SDKTag { } export type SDKInstructionsMap = Partial> + +export enum SidePanelTab { + Notebooks = 'notebook', + Feedback = 'feedback', + Docs = 'docs', + Activation = 'activation', + Settings = 'settings', +}