Skip to content

Commit

Permalink
fix: Notebook Sidebar display logic (#17516)
Browse files Browse the repository at this point in the history
  • Loading branch information
benjackwhite authored Sep 19, 2023
1 parent 1fc8877 commit 737f482
Show file tree
Hide file tree
Showing 8 changed files with 68 additions and 31 deletions.
5 changes: 4 additions & 1 deletion frontend/src/lib/lemon-ui/LemonWidget/LemonWidget.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@ export interface LemonWidgetProps {
title: string
collapsible?: boolean
onClose?: () => void
actions?: React.ReactNode
children: React.ReactChild
}

export function LemonWidget({ title, collapsible = true, onClose, children }: LemonWidgetProps): JSX.Element {
export function LemonWidget({ title, collapsible = true, onClose, actions, children }: LemonWidgetProps): JSX.Element {
const [isExpanded, setIsExpanded] = useState<boolean>(true)

return (
Expand All @@ -36,6 +37,8 @@ export function LemonWidget({ title, collapsible = true, onClose, children }: Le
) : (
<span className="flex-1 text-primary-alt px-2">{title}</span>
)}
{actions}

{onClose && <LemonButton status="danger" onClick={onClose} size="small" icon={<IconClose />} />}
</Header>
{isExpanded && <Content>{children}</Content>}
Expand Down
10 changes: 6 additions & 4 deletions frontend/src/scenes/notebooks/Nodes/NodeWrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,8 @@ export function NodeWrapper<T extends CustomNotebookNodeAttributes>({
widgets = [],
}: NodeWrapperProps<T> & NotebookNodeViewProps<T>): JSX.Element {
const mountedNotebookLogic = useMountedLogic(notebookLogic)
const { isEditable, isShowingSidebar } = useValues(mountedNotebookLogic)
const { setIsShowingSidebar } = useActions(mountedNotebookLogic)
const { isEditable, editingNodeId } = useValues(mountedNotebookLogic)
const { setEditingNodeId } = useActions(mountedNotebookLogic)

// nodeId can start null, but should then immediately be generated
const nodeId = attributes.nodeId
Expand Down Expand Up @@ -169,10 +169,12 @@ export function NodeWrapper<T extends CustomNotebookNodeAttributes>({

{widgets.length > 0 ? (
<LemonButton
onClick={() => setIsShowingSidebar(!isShowingSidebar)}
onClick={() =>
setEditingNodeId(editingNodeId === nodeId ? null : nodeId)
}
size="small"
icon={<IconFilter />}
active={isShowingSidebar && selected}
active={editingNodeId === nodeId}
/>
) : null}

Expand Down
1 change: 0 additions & 1 deletion frontend/src/scenes/notebooks/Nodes/NotebookNodeQuery.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,6 @@ export const NotebookNodeQuery = createPostHogWidgetNode<NotebookNodeQueryAttrib
widgets: [
{
key: 'settings',
label: 'Settings',
Component: Settings,
},
],
Expand Down
13 changes: 13 additions & 0 deletions frontend/src/scenes/notebooks/Nodes/notebookNodeLogic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ export const notebookNodeLogic = kea<notebookNodeLogicType>([
setPreviousNode: (node: Node | null) => ({ node }),
setNextNode: (node: Node | null) => ({ node }),
deleteNode: true,
selectNode: true,
}),

connect((props: NotebookNodeLogicProps) => ({
Expand Down Expand Up @@ -138,6 +139,18 @@ export const notebookNodeLogic = kea<notebookNodeLogicType>([
deleteNode: () => {
const logic = values.notebookLogic
logic.values.editor?.deleteRange({ from: props.getPos(), to: props.getPos() + props.node.nodeSize }).run()
if (values.notebookLogic.values.editingNodeId === props.nodeId) {
values.notebookLogic.actions.setEditingNodeId(null)
}
},

selectNode: () => {
const editor = values.notebookLogic.values.editor

if (editor) {
editor.setSelection(props.getPos())
editor.scrollToSelection()
}
},

insertAfterLastNodeOfType: ({ nodeType, content }) => {
Expand Down
11 changes: 10 additions & 1 deletion frontend/src/scenes/notebooks/Notebook/Notebook.scss
Original file line number Diff line number Diff line change
Expand Up @@ -127,13 +127,22 @@
width: 0px;
transition: width var(--notebook-popover-transition-properties);

.NotebookSidebar__padding {
height: 3.5rem;
}

.NotebookSidebar__content {
position: sticky;
align-self: flex-start;
top: 65px;
top: 0px;
width: var(--notebook-sidebar-width);
transform: translateX(-100%);
transition: transform var(--notebook-popover-transition-properties);

.NotebookScene & {
// Account for sticky header
top: 4rem;
}
}

&--showing {
Expand Down
40 changes: 26 additions & 14 deletions frontend/src/scenes/notebooks/Notebook/NotebookSidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@ import { BuiltLogic, useActions, useValues } from 'kea'
import clsx from 'clsx'
import { notebookLogic } from './notebookLogic'
import { notebookNodeLogicType } from '../Nodes/notebookNodeLogicType'
import { LemonButton } from '@posthog/lemon-ui'
import { IconEyeVisible } from 'lib/lemon-ui/icons'

export const NotebookSidebar = (): JSX.Element | null => {
const { selectedNodeLogic, isShowingSidebar, isEditable } = useValues(notebookLogic)
const { setIsShowingSidebar } = useActions(notebookLogic)
const { editingNodeLogic, isShowingSidebar, isEditable } = useValues(notebookLogic)

if (!isEditable) {
return null
Expand All @@ -18,29 +19,40 @@ export const NotebookSidebar = (): JSX.Element | null => {
'NotebookSidebar--showing': isShowingSidebar,
})}
>
<div className="NotebookSidebar__padding" />
<div className="NotebookSidebar__content">
{selectedNodeLogic && isShowingSidebar && (
<Widgets logic={selectedNodeLogic} onClose={() => setIsShowingSidebar(false)} />
)}
{editingNodeLogic && isShowingSidebar && <Widgets logic={editingNodeLogic} />}
</div>
</div>
)
}

export const Widgets = ({
logic,
onClose,
}: {
logic: BuiltLogic<notebookNodeLogicType>
onClose: () => void
}): JSX.Element | null => {
const Widgets = ({ logic }: { logic: BuiltLogic<notebookNodeLogicType> }): JSX.Element | null => {
const { setEditingNodeId } = useActions(notebookLogic)
const { widgets, nodeAttributes } = useValues(logic)
const { updateAttributes } = useActions(logic)
const { updateAttributes, selectNode } = useActions(logic)

return (
<div className="NotebookNodeSettings__widgets space-y-2 w-full">
{widgets.map(({ key, label, Component }) => (
<LemonWidget key={key} title={label} collapsible={false} onClose={onClose}>
<LemonWidget
key={key}
title={label ?? `Editing '${nodeAttributes.title}'`}
collapsible={false}
actions={
<>
<LemonButton
icon={<IconEyeVisible />}
size="small"
status="primary"
onClick={() => selectNode()}
/>
<LemonButton size="small" status="primary" onClick={() => setEditingNodeId(null)}>
Done
</LemonButton>
</>
}
>
<div className="NotebookNodeSettings__widgets__content">
<Component attributes={nodeAttributes} updateAttributes={updateAttributes} />
</div>
Expand Down
17 changes: 8 additions & 9 deletions frontend/src/scenes/notebooks/Notebook/notebookLogic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ export const notebookLogic = kea<notebookLogicType>([
clearLocalContent: true,
loadNotebook: true,
saveNotebook: (notebook: Pick<NotebookType, 'content' | 'title'>) => ({ notebook }),
setSelectedNodeId: (selectedNodeId: string | null) => ({ selectedNodeId }),
setEditingNodeId: (editingNodeId: string | null) => ({ editingNodeId }),
exportJSON: true,
showConflictWarning: true,
onUpdateEditor: true,
Expand Down Expand Up @@ -133,10 +133,10 @@ export const notebookLogic = kea<notebookLogicType>([
loadNotebookSuccess: () => false,
},
],
selectedNodeId: [
editingNodeId: [
null as string | null,
{
setSelectedNodeId: (_, { selectedNodeId }) => selectedNodeId,
setEditingNodeId: (_, { editingNodeId }) => editingNodeId,
},
],
nodeLogics: [
Expand Down Expand Up @@ -170,7 +170,7 @@ export const notebookLogic = kea<notebookLogicType>([
isShowingSidebar: [
false,
{
setSelectedNodeId: (showing, { selectedNodeId }) => (selectedNodeId ? showing : false),
setEditingNodeId: (_, { editingNodeId }) => (editingNodeId ? true : false),
setIsShowingSidebar: (_, { showing }) => showing,
},
],
Expand Down Expand Up @@ -319,10 +319,10 @@ export const notebookLogic = kea<notebookLogicType>([
return 'unsaved'
},
],
selectedNodeLogic: [
(s) => [s.selectedNodeId, s.nodeLogics],
(selectedNodeId, nodeLogics) =>
Object.values(nodeLogics).find((nodeLogic) => nodeLogic.props.nodeId === selectedNodeId),
editingNodeLogic: [
(s) => [s.editingNodeId, s.nodeLogics],
(editingNodeId, nodeLogics) =>
Object.values(nodeLogics).find((nodeLogic) => nodeLogic.props.nodeId === editingNodeId),
],
findNodeLogic: [
(s) => [s.nodeLogics],
Expand Down Expand Up @@ -467,7 +467,6 @@ export const notebookLogic = kea<notebookLogicType>([
onEditorSelectionUpdate: () => {
if (values.editor) {
const node = values.editor.getSelectedNode()
actions.setSelectedNodeId(node?.attrs.nodeId ?? null)

if (node?.attrs.nodeId) {
actions.scrollToSelection()
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/scenes/notebooks/Notebook/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export type NotebookNodeViewProps<T extends CustomNotebookNodeAttributes> = Omit

export type NotebookNodeWidget = {
key: string
label: string
label?: string
// using 'any' here shouldn't be necessary but, I couldn't figure out how to set a generic on the notebookNodeLogic props
Component: ({ attributes, updateAttributes }: NotebookNodeAttributeProperties<any>) => JSX.Element
}
Expand Down

0 comments on commit 737f482

Please sign in to comment.