Skip to content

Commit

Permalink
feat: Added notebooks to sidebar (#17919)
Browse files Browse the repository at this point in the history
  • Loading branch information
benjackwhite authored Oct 12, 2023
1 parent 2bd2b2c commit a3d822d
Show file tree
Hide file tree
Showing 33 changed files with 137 additions and 119 deletions.
6 changes: 3 additions & 3 deletions cypress/e2e/notebooks-creation-and-deletion.cy.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { randomString } from '../support/random'

function visitNotebooksList(): void {
cy.clickNavMenu('dashboards')
cy.location('pathname').should('include', '/dashboard')
cy.get('h1').should('contain', 'Dashboards & Notebooks')
cy.clickNavMenu('notebooks')
cy.location('pathname').should('include', '/notebooks')
cy.get('h1').should('contain', 'Notebooks')
cy.get('li').contains('Notebooks').should('exist').click()
}

Expand Down
6 changes: 3 additions & 3 deletions cypress/e2e/notebooks.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,12 @@ describe('Notebooks', () => {
}).as('patchNotebook')
})

cy.clickNavMenu('dashboards')
cy.location('pathname').should('include', '/dashboard')
cy.clickNavMenu('notebooks')
cy.location('pathname').should('include', '/notebooks')
})

it('Notebooks are enabled', () => {
cy.get('h1').should('contain', 'Dashboards & Notebooks')
cy.get('h1').should('contain', 'Notebooks')
cy.get('li').contains('Notebooks').should('exist').click()
})

Expand Down
Binary file modified frontend/__snapshots__/lemon-ui-icons--shelf-j.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified frontend/__snapshots__/lemon-ui-icons--shelf-n.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified frontend/__snapshots__/scenes-app-insights--lifecycle.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified frontend/__snapshots__/scenes-app-insights--stickiness.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified frontend/__snapshots__/scenes-app-insights--trends-number.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified frontend/__snapshots__/scenes-app-insights--trends-table.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified frontend/__snapshots__/scenes-app-notebooks--headings.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified frontend/__snapshots__/scenes-app-notebooks--notebooks-list.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
32 changes: 23 additions & 9 deletions frontend/src/layout/navigation/SideBar/SideBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
IconGauge,
IconLive,
IconMessages,
IconNotebook,
IconOpenInApp,
IconPerson,
IconPinOutline,
Expand All @@ -38,7 +39,6 @@ import { AvailableFeature } from '~/types'
import './SideBar.scss'
import { navigationLogic } from '../navigationLogic'
import { FEATURE_FLAGS } from 'lib/constants'
import { featureFlagLogic } from 'lib/logic/featureFlagLogic'
import { groupsModel } from '~/models/groupsModel'
import { userLogic } from 'scenes/userLogic'
import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic'
Expand All @@ -52,13 +52,13 @@ import { Spinner } from 'lib/lemon-ui/Spinner/Spinner'
import { DebugNotice } from 'lib/components/DebugNotice'
import ActivationSidebar from 'lib/components/ActivationSidebar/ActivationSidebar'
import { NotebookPopover } from 'scenes/notebooks/Notebook/NotebookPopover'
import { FlaggedFeature } from 'lib/components/FlaggedFeature'

function Pages(): JSX.Element {
const { currentOrganization } = useValues(organizationLogic)
const { hideSideBarMobile, toggleProjectSwitcher, hideProjectSwitcher } = useActions(navigationLogic)
const { isProjectSwitcherShown } = useValues(navigationLogic)
const { pinnedDashboards } = useValues(dashboardsModel)
const { featureFlags } = useValues(featureFlagLogic)
const { showGroupsOptions } = useValues(groupsModel)
const { hasAvailableFeature } = useValues(userLogic)
const { preflight } = useValues(preflightLogic)
Expand Down Expand Up @@ -150,6 +150,20 @@ function Pages(): JSX.Element {
},
}}
/>
<FlaggedFeature flag="notebooks">
<PageButton
icon={<IconNotebook />}
identifier={Scene.Notebooks}
to={urls.notebooks()}
sideAction={{
icon: <IconPlus />,
to: urls.notebook('new'),
tooltip: 'New notebook',
identifier: Scene.Notebook,
onClick: hideSideBarMobile,
}}
/>
</FlaggedFeature>
<PageButton
icon={<IconBarChart />}
identifier={Scene.SavedInsights}
Expand All @@ -167,6 +181,7 @@ function Pages(): JSX.Element {
<div className="SideBar__heading">Feature Management</div>

<PageButton icon={<IconFlag />} identifier={Scene.FeatureFlags} to={urls.featureFlags()} />

{(hasAvailableFeature(AvailableFeature.EXPERIMENTATION) ||
!preflight?.instance_preferences?.disable_paid_fs) && (
<PageButton icon={<IconExperiment />} identifier={Scene.Experiments} to={urls.experiments()} />
Expand All @@ -186,15 +201,14 @@ function Pages(): JSX.Element {
highlight="beta"
/>

{featureFlags[FEATURE_FLAGS.WEB_ANALYTICS] && (
<FlaggedFeature flag={FEATURE_FLAGS.WEB_ANALYTICS}>
<PageButton
icon={<IconWeb />}
identifier={Scene.WebAnalytics}
to={urls.webAnalytics()}
highlight="alpha"
/>
)}

</FlaggedFeature>
<div className="SideBar__heading">Data</div>

<PageButton
Expand All @@ -214,15 +228,15 @@ function Pages(): JSX.Element {
to={urls.persons()}
title={`Persons${showGroupsOptions ? ' & Groups' : ''}`}
/>
{featureFlags[FEATURE_FLAGS.DATA_WAREHOUSE] && (
<FlaggedFeature flag={FEATURE_FLAGS.DATA_WAREHOUSE}>
<PageButton
icon={<IconDatabase />}
identifier={Scene.DataWarehouse}
title={'Data Warehouse'}
to={urls.dataWarehouse()}
highlight="beta"
/>
)}
</FlaggedFeature>
<PageButton icon={<IconCohort />} identifier={Scene.Cohorts} to={urls.cohorts()} />
<PageButton icon={<IconComment />} identifier={Scene.Annotations} to={urls.annotations()} />
{canViewPlugins(currentOrganization) || Object.keys(frontendApps).length > 0 ? (
Expand All @@ -240,9 +254,9 @@ function Pages(): JSX.Element {
{Object.keys(frontendApps).length > 0 && <SideBarApps />}
</>
) : null}
{featureFlags[FEATURE_FLAGS.FEEDBACK_SCENE] && (
<FlaggedFeature flag={FEATURE_FLAGS.FEEDBACK_SCENE}>
<PageButton icon={<IconMessages />} identifier={Scene.Feedback} to={urls.feedback()} />
)}
</FlaggedFeature>
<div className="SideBar__heading">Configuration</div>

<PageButton
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/layout/navigation/TopBar/NotebookButton.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { IconJournal } from 'lib/lemon-ui/icons'
import { IconNotebook } from 'lib/lemon-ui/icons'
import { useActions, useValues } from 'kea'
import { notebookPopoverLogic } from 'scenes/notebooks/Notebook/notebookPopoverLogic'
import { LemonButton, LemonButtonWithSideActionProps } from '@posthog/lemon-ui'
Expand All @@ -19,7 +19,7 @@ export function NotebookButton(): JSX.Element {

return (
<LemonButton
icon={<IconJournal />}
icon={<IconNotebook />}
type={visibility === 'visible' ? 'primary' : 'tertiary'}
onClick={() => setVisibility(visibility === 'visible' ? 'hidden' : 'visible')}
status="primary-alt"
Expand Down
36 changes: 14 additions & 22 deletions frontend/src/lib/lemon-ui/icons/icons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2383,28 +2383,6 @@ export function IconDatabase(props: LemonIconProps): JSX.Element {
)
}

export function IconJournal(props: LemonIconProps): JSX.Element {
return (
<LemonIconBase {...props}>
<path
fill="currentColor"
d="M17,4V10L15,8L13,10V4H9V20H19V4H17M3,7V5H5V4C5,2.89 5.9,2 7,2H19C20.05,2 21,2.95 21,4V20C21,21.05 20.05,22 19,22H7C5.95,22 5,21.05 5,20V19H3V17H5V13H3V11H5V7H3M5,5V7H7V5H5M5,19H7V17H5V19M5,13H7V11H5V13Z"
/>
</LemonIconBase>
)
}

export function IconJournalPlus(props: LemonIconProps): JSX.Element {
return (
<LemonIconBase {...props}>
<path
fill="currentColor"
d="M17 4V10L15 8L13 10V4H9V20H12.1C12.2 20.7 12.5 21.4 12.8 22H7C5.9 22 5 21 5 20V19H3V17H5V13H3V11H5V7H3V5H5V4C5 2.9 5.9 2 7 2H19C20 2 21 3 21 4V13.8C20.4 13.4 19.7 13.2 19 13.1V4H17M5 19H7V17H5V19M5 13H7V11H5V13M5 7H7V5H5V7M17 15V18H14V20H17V23H19V20H22V18H19V15H17Z"
/>
</LemonIconBase>
)
}

export function IconDragHandle(props: LemonIconProps): JSX.Element {
return (
<LemonIconBase {...props}>
Expand Down Expand Up @@ -2483,3 +2461,17 @@ export function IconTarget(props: LemonIconProps): JSX.Element {
</LemonIconBase>
)
}

export function IconNotebook(props: LemonIconProps): JSX.Element {
return (
<LemonIconBase {...props}>
<path
d="M8.75 3.25H5.25C4.69772 3.25 4.25 3.69772 4.25 4.25V19.75C4.25 20.3023 4.69772 20.75 5.25 20.75H8.75M8.75 3.25H18.75C19.3023 3.25 19.75 3.69772 19.75 4.25V19.75C19.75 20.3023 19.3023 20.75 18.75 20.75H8.75M8.75 3.25V20.75M12.75 7.75H15.75M12.75 11.75H15.75"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
/>
</LemonIconBase>
)
}
6 changes: 5 additions & 1 deletion frontend/src/models/notebooksModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,11 @@ export const openNotebook = async (
if (popoverLogic?.values.visibility === 'visible') {
popoverLogic?.actions.selectNotebook(notebookId, focus)
} else {
router.actions.push(urls.notebook(notebookId))
if (router.values.location.pathname === urls.notebook('new')) {
router.actions.replace(urls.notebook(notebookId))
} else {
router.actions.push(urls.notebook(notebookId))
}
}

const theNotebookLogic = notebookLogic({ shortId: notebookId })
Expand Down
1 change: 1 addition & 0 deletions frontend/src/scenes/appScenes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ export const appScenes: Record<Scene, () => any> = {
[Scene.DebugQuery]: () => import('./debug/DebugScene'),
[Scene.VerifyEmail]: () => import('./authentication/signup/verify-email/VerifyEmail'),
[Scene.Feedback]: () => import('./feedback/Feedback'),
[Scene.Notebooks]: () => import('./notebooks/NotebooksScene'),
[Scene.Notebook]: () => import('./notebooks/NotebookScene'),
[Scene.Products]: () => import('./products/Products'),
[Scene.Onboarding]: () => import('./onboarding/Onboarding'),
Expand Down
60 changes: 11 additions & 49 deletions frontend/src/scenes/dashboard/dashboards/Dashboards.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,6 @@ import { NoDashboards } from 'scenes/dashboard/dashboards/NoDashboards'
import { DashboardsTableContainer } from 'scenes/dashboard/dashboards/DashboardsTable'
import { DashboardTemplatesTable } from 'scenes/dashboard/dashboards/templates/DashboardTemplatesTable'
import { LemonTab, LemonTabs } from 'lib/lemon-ui/LemonTabs'
import { featureFlagLogic } from 'lib/logic/featureFlagLogic'
import { FEATURE_FLAGS } from 'lib/constants'
import { NotebooksTable } from 'scenes/notebooks/NotebooksTable/NotebooksTable'
import { notebooksModel } from '~/models/notebooksModel'
import { LemonTag } from '@posthog/lemon-ui'

export const scene: SceneExport = {
component: Dashboards,
Expand All @@ -30,11 +25,6 @@ export function Dashboards(): JSX.Element {
const { dashboards, currentTab, isFiltering } = useValues(dashboardsLogic)
const { showNewDashboardModal } = useActions(newDashboardLogic)
const { closePrompts } = useActions(inAppPromptLogic)
const { featureFlags } = useValues(featureFlagLogic)
const { notebooksLoading } = useValues(notebooksModel)
const { createNotebook } = useActions(notebooksModel)

const notebooksEnabled = featureFlags[FEATURE_FLAGS.NOTEBOOKS]

const enabledTabs: LemonTab<DashboardsTab>[] = [
{
Expand All @@ -46,58 +36,30 @@ export function Dashboards(): JSX.Element {
label: 'Templates',
},
]
if (notebooksEnabled) {
enabledTabs.splice(1, 0, {
key: DashboardsTab.Notebooks,
label: (
<>
Notebooks
<LemonTag type="warning" className="uppercase ml-2">
Beta
</LemonTag>
</>
),
})
}

return (
<div>
<NewDashboardModal />
<DuplicateDashboardModal />
<DeleteDashboardModal />
<PageHeader
title={'Dashboards' + (notebooksEnabled ? ' & Notebooks' : '')}
title="Dashboards"
buttons={
currentTab === DashboardsTab.Notebooks ? (
<LemonButton
data-attr={'new-notebook'}
onClick={() => {
createNotebook()
}}
type="primary"
disabledReason={notebooksLoading ? 'Loading...' : undefined}
>
New notebook
</LemonButton>
) : (
<LemonButton
data-attr={'new-dashboard'}
onClick={() => {
closePrompts()
showNewDashboardModal()
}}
type="primary"
>
New dashboard
</LemonButton>
)
<LemonButton
data-attr={'new-dashboard'}
onClick={() => {
closePrompts()
showNewDashboardModal()
}}
type="primary"
>
New dashboard
</LemonButton>
}
/>
<LemonTabs activeKey={currentTab} onChange={(newKey) => setCurrentTab(newKey)} tabs={enabledTabs} />
{currentTab === DashboardsTab.Templates ? (
<DashboardTemplatesTable />
) : currentTab === DashboardsTab.Notebooks ? (
<NotebooksTable />
) : dashboardsLoading || dashboards.length > 0 || isFiltering ? (
<DashboardsTableContainer />
) : (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import { DashboardBasicType } from '~/types'

export enum DashboardsTab {
Dashboards = 'dashboards',
Notebooks = 'notebooks',
Templates = 'templates',
}

Expand Down
4 changes: 2 additions & 2 deletions frontend/src/scenes/notebooks/Nodes/NotebookNodeBacklink.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
IconLive,
IconPerson,
IconCohort,
IconJournal,
IconNotebook,
} from 'lib/lemon-ui/icons'
import { TaxonomicFilterGroupType } from 'lib/components/TaxonomicFilter/types'
import { urls } from 'scenes/urls'
Expand All @@ -31,7 +31,7 @@ const ICON_MAP = {
events: <IconLive width="1em" height="1em" />,
persons: <IconPerson />,
cohorts: <IconCohort />,
notebooks: <IconJournal />,
notebooks: <IconNotebook />,
}

const Component = (props: NodeViewProps): JSX.Element => {
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/scenes/notebooks/Notebook/NotebookListMini.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { LemonButton } from '@posthog/lemon-ui'
import { useValues } from 'kea'
import { IconJournal } from 'lib/lemon-ui/icons'
import { IconNotebook } from 'lib/lemon-ui/icons'
import { notebooksModel } from '~/models/notebooksModel'
import { NotebookListItemType } from '~/types'
import { NotebookSelectPopover } from '../NotebookSelectButton/NotebookSelectButton'
Expand All @@ -23,7 +23,7 @@ export function NotebookListMini({ selectedNotebookId }: NotebookListMiniProps):

return (
<NotebookSelectPopover placement="right-start">
<LemonButton size="small" icon={<IconJournal />} status="primary-alt" sideIcon={null}>
<LemonButton size="small" icon={<IconNotebook />} status="primary-alt" sideIcon={null}>
<span className="font-semibold">{selectedTitle || 'Notebooks'}</span>
</LemonButton>
</NotebookSelectPopover>
Expand Down
13 changes: 8 additions & 5 deletions frontend/src/scenes/notebooks/NotebookScene.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import { LOCAL_NOTEBOOK_TEMPLATES } from './NotebookTemplates/notebookTemplates'
import './NotebookScene.scss'
import { featureFlagLogic } from 'lib/logic/featureFlagLogic'
import { FEATURE_FLAGS } from 'lib/constants'
import { NotebookLoadingState } from './Notebook/NotebookLoadingState'

interface NotebookSceneProps {
shortId?: string
Expand All @@ -38,18 +39,16 @@ export const scene: SceneExport = {
}

export function NotebookScene(): JSX.Element {
const { notebookId } = useValues(notebookSceneLogic)
const { notebook, notebookLoading, conflictWarningVisible, showHistory } = useValues(
notebookLogic({ shortId: notebookId })
)
const { notebookId, loading } = useValues(notebookSceneLogic)
const { notebook, conflictWarningVisible, showHistory } = useValues(notebookLogic({ shortId: notebookId }))
const { exportJSON, setShowHistory } = useActions(notebookLogic({ shortId: notebookId }))
const { selectNotebook, setVisibility } = useActions(notebookPopoverLogic)
const { selectedNotebook, visibility } = useValues(notebookPopoverLogic)

const { featureFlags } = useValues(featureFlagLogic)
const buttonSize = featureFlags[FEATURE_FLAGS.POSTHOG_3000] ? 'small' : 'medium'

if (!notebook && !notebookLoading && !conflictWarningVisible) {
if (!notebook && !loading && !conflictWarningVisible) {
return <NotFound object="notebook" />
}

Expand All @@ -74,6 +73,10 @@ export function NotebookScene(): JSX.Element {

const isTemplate = notebook?.is_template

if (notebookId === 'new') {
return <NotebookLoadingState />
}

return (
<div className="NotebookScene">
<div className="flex items-center justify-between border-b py-2 mb-2 sticky top-0 bg-bg-3000 z-10">
Expand Down
Loading

0 comments on commit a3d822d

Please sign in to comment.