Skip to content

Commit

Permalink
fix(3000): Top bar title fixes (#19291)
Browse files Browse the repository at this point in the history
* Fix recording playlist's beforeUnload

* Fix missing insight derived name

* Allow saving unnamed action

* Fix EditableField autofocus

* Update query snapshots

* Update query snapshots

---------

Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
  • Loading branch information
Twixes and github-actions[bot] authored Dec 13, 2023
1 parent 371c2e5 commit ccbe3a1
Show file tree
Hide file tree
Showing 7 changed files with 75 additions and 40 deletions.
1 change: 1 addition & 0 deletions frontend/src/layout/navigation-3000/components/TopBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,7 @@ function Here({ breadcrumb }: HereProps): JSX.Element {
placeholder="Unnamed"
compactButtons="xsmall"
editingIndication="underlined"
autoFocus
/>
) : (
<span>{breadcrumb.name}</span>
Expand Down
71 changes: 43 additions & 28 deletions frontend/src/lib/components/EditableField/EditableField.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import './EditableField.scss'

import { useMergeRefs } from '@floating-ui/react'
import clsx from 'clsx'
import { IconEdit, IconMarkdown } from 'lib/lemon-ui/icons'
import { LemonButton } from 'lib/lemon-ui/LemonButton'
import { LemonMarkdown } from 'lib/lemon-ui/LemonMarkdown'
import { Tooltip } from 'lib/lemon-ui/Tooltip'
import { pluralize } from 'lib/utils'
import React, { useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react'
import React, { useEffect, useLayoutEffect, useRef, useState } from 'react'
import TextareaAutosize from 'react-textarea-autosize'

export interface EditableFieldProps {
Expand Down Expand Up @@ -51,7 +52,7 @@ export function EditableField({
placeholder,
minLength,
maxLength,
autoFocus = true,
autoFocus = false,
multiline = false,
markdown = false,
compactButtons = false,
Expand All @@ -65,16 +66,30 @@ export function EditableField({
saveButtonText = 'Save',
notice,
}: EditableFieldProps): JSX.Element {
const [localIsEditing, setLocalIsEditing] = useState(false)
const [localIsEditing, setLocalIsEditing] = useState(mode === 'edit')
const [localTentativeValue, setLocalTentativeValue] = useState(value)
const inputRef = useRef<HTMLInputElement | HTMLTextAreaElement>()
const previousIsEditing = useRef<boolean>()

useEffect(() => {
setLocalTentativeValue(value)
}, [value])

useEffect(() => {
setLocalIsEditing(mode === 'edit')
}, [mode])

useEffect(() => {
// We always want to focus when switching to edit mode, but can't use autoFocus, because we don't want this to
// happen when the component is _initially_ rendered in edit mode.
if (inputRef.current && previousIsEditing.current === false && localIsEditing === true) {
const endOfInput = inputRef.current.value.length
inputRef.current.setSelectionRange(endOfInput, endOfInput)
inputRef.current.focus()
}
previousIsEditing.current = localIsEditing
}, [localIsEditing])

const isSaveable = !minLength || localTentativeValue.length >= minLength

const mouseDownOnCancelButton = (e: React.MouseEvent): void => {
Expand Down Expand Up @@ -150,6 +165,7 @@ export function EditableField({
minLength={minLength}
maxLength={maxLength}
autoFocus={autoFocus}
ref={inputRef as React.RefObject<HTMLTextAreaElement>}
/>
) : (
<AutosizeInput
Expand All @@ -165,6 +181,7 @@ export function EditableField({
minLength={minLength}
maxLength={maxLength}
autoFocus={autoFocus}
ref={inputRef as React.RefObject<HTMLInputElement>}
/>
)}
{(!mode || !!onModeToggle) && (
Expand Down Expand Up @@ -242,17 +259,7 @@ export function EditableField({
)
}

const AutosizeInput = ({
name,
value,
onChange,
placeholder,
onBlur,
onKeyDown,
minLength,
maxLength,
autoFocus,
}: {
interface AutosizeInputProps {
name: string
value: string
onChange: (e: React.ChangeEvent<HTMLInputElement>) => void
Expand All @@ -262,11 +269,18 @@ const AutosizeInput = ({
minLength?: number
maxLength?: number
autoFocus?: boolean
}): JSX.Element => {
}

const AutosizeInput = React.forwardRef<HTMLInputElement, AutosizeInputProps>(function AutosizeInput(
{ name, value, onChange, placeholder, onBlur, onKeyDown, minLength, maxLength, autoFocus },
ref
) {
const [inputWidth, setInputWidth] = useState<number | string>(1)
const inputRef = useRef<HTMLInputElement>(null)
const [inputStyles, setInputStyles] = useState<CSSStyleDeclaration>()
const sizerRef = useRef<HTMLDivElement>(null)
const placeHolderSizerRef = useRef<HTMLDivElement>(null)
const inputRef = useRef<HTMLInputElement>(null)
const mergedRefs = useMergeRefs([ref, inputRef])

const copyStyles = (styles: CSSStyleDeclaration, node: HTMLDivElement): void => {
node.style.fontSize = styles.fontSize
Expand All @@ -277,21 +291,22 @@ const AutosizeInput = ({
node.style.textTransform = styles.textTransform
}

const inputStyles = useMemo(() => {
return inputRef.current ? window.getComputedStyle(inputRef.current) : null
}, [inputRef.current])

useLayoutEffect(() => {
if (inputStyles && placeHolderSizerRef.current) {
copyStyles(inputStyles, placeHolderSizerRef.current)
if (inputRef.current) {
setInputStyles(getComputedStyle(inputRef.current))
}
}, [placeHolderSizerRef, placeHolderSizerRef])
}, [inputRef.current])

useLayoutEffect(() => {
if (inputStyles && sizerRef.current) {
copyStyles(inputStyles, sizerRef.current)
if (inputStyles) {
if (sizerRef.current) {
copyStyles(inputStyles, sizerRef.current)
}
if (placeHolderSizerRef.current) {
copyStyles(inputStyles, placeHolderSizerRef.current)
}
}
}, [inputStyles, sizerRef])
}, [inputStyles])

useLayoutEffect(() => {
if (!sizerRef.current || !placeHolderSizerRef.current) {
Expand Down Expand Up @@ -320,7 +335,7 @@ const AutosizeInput = ({
minLength={minLength}
maxLength={maxLength}
autoFocus={autoFocus}
ref={inputRef}
ref={mergedRefs}
/* eslint-disable-next-line react/forbid-dom-props */
style={{ boxSizing: 'content-box', width: `${inputWidth}px` }}
/>
Expand All @@ -332,4 +347,4 @@ const AutosizeInput = ({
</div>
</div>
)
}
})
3 changes: 0 additions & 3 deletions frontend/src/scenes/actions/actionEditLogic.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,6 @@ export const actionEditLogic = kea<actionEditLogicType>([
forms(({ actions, props }) => ({
action: {
defaults: { ...props.action } as ActionEditType,
errors: ({ name }) => ({
name: !name ? 'You need to set a name' : null,
}),
submit: (action) => {
actions.saveAction(action)
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export function ActionsTable(): JSX.Element {
return (
<>
<Link data-attr={'action-link-' + index} to={urls.action(action.id)} className="row-name">
{name || <i>Unnamed action</i>}
{name || <i>Unnamed</i>}
</Link>
{action.description && (
<LemonMarkdown className="row-description" lowKeyHeadings>
Expand Down
25 changes: 22 additions & 3 deletions frontend/src/scenes/insights/insightSceneLogic.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,17 @@ import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic'
import { sceneLogic } from 'scenes/sceneLogic'
import { Scene } from 'scenes/sceneTypes'
import { teamLogic } from 'scenes/teamLogic'
import { mathsLogic } from 'scenes/trends/mathsLogic'
import { urls } from 'scenes/urls'

import { cohortsModel } from '~/models/cohortsModel'
import { groupsModel } from '~/models/groupsModel'
import { Breadcrumb, FilterType, InsightShortId, InsightType, ItemMode } from '~/types'

import { insightDataLogic } from './insightDataLogic'
import { insightDataLogicType } from './insightDataLogicType'
import type { insightSceneLogicType } from './insightSceneLogicType'
import { summarizeInsight } from './summarizeInsight'

export const insightSceneLogic = kea<insightSceneLogicType>([
path(['scenes', 'insights', 'insightSceneLogic']),
Expand Down Expand Up @@ -85,18 +89,33 @@ export const insightSceneLogic = kea<insightSceneLogicType>([
}),
selectors(() => ({
insightSelector: [(s) => [s.insightLogicRef], (insightLogicRef) => insightLogicRef?.logic.selectors.insight],
filtersSelector: [(s) => [s.insightLogicRef], (insightLogicRef) => insightLogicRef?.logic.selectors.filters],
insight: [(s) => [(state, props) => s.insightSelector?.(state, props)?.(state, props)], (insight) => insight],
filters: [(s) => [(state, props) => s.filtersSelector?.(state, props)?.(state, props)], (filters) => filters],
breadcrumbs: [
(s) => [s.insight, s.insightLogicRef],
(insight, insightLogicRef): Breadcrumb[] => [
(s) => [
s.insightLogicRef,
s.insight,
s.filters,
groupsModel.selectors.aggregationLabel,
cohortsModel.selectors.cohortsById,
mathsLogic.selectors.mathDefinitions,
],
(insightLogicRef, insight, filters, aggregationLabel, cohortsById, mathDefinitions): Breadcrumb[] => [
{
key: Scene.SavedInsights,
name: 'Product analytics',
path: urls.savedInsights(),
},
{
key: insight?.short_id || 'new',
name: insight?.name || insight?.derived_name || 'Unnamed',
name:
insight?.name ||
summarizeInsight(insight?.query, filters, {
aggregationLabel,
cohortsById,
mathDefinitions,
}),
onRename: async (name: string) => {
await insightLogicRef?.logic.asyncActions.setInsightMetadata({ name })
},
Expand Down
5 changes: 2 additions & 3 deletions frontend/src/scenes/insights/summarizeInsight.ts
Original file line number Diff line number Diff line change
Expand Up @@ -343,15 +343,14 @@ export interface SummaryContext {

export function summarizeInsight(
query: Node | undefined | null,
filters: Partial<FilterType>,
filters: Partial<FilterType> | undefined | null,
context: SummaryContext
): string {
const hasFilters = Object.keys(filters || {}).length > 0
return isInsightVizNode(query)
? summarizeInsightQuery(query.source, context)
: !!query && !isInsightVizNode(query)
? summarizeQuery(query)
: hasFilters
: filters && Object.keys(filters).length > 0
? summarizeInsightFilters(filters, context)
: ''
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { actions, afterMount, connect, kea, key, listeners, path, props, reducer
import { loaders } from 'kea-loaders'
import { beforeUnload, router } from 'kea-router'
import api from 'lib/api'
import { sceneLogic } from 'scenes/sceneLogic'
import { Scene } from 'scenes/sceneTypes'
import {
deletePlaylist,
Expand All @@ -30,7 +31,7 @@ export const sessionRecordingsPlaylistSceneLogic = kea<sessionRecordingsPlaylist
props({} as SessionRecordingsPlaylistLogicProps),
key((props) => props.shortId),
connect({
values: [cohortsModel, ['cohortsById']],
values: [cohortsModel, ['cohortsById'], sceneLogic, ['activeScene']],
}),
actions({
updatePlaylist: (properties?: Partial<SessionRecordingPlaylistType>, silent = false) => ({
Expand Down Expand Up @@ -126,7 +127,10 @@ export const sessionRecordingsPlaylistSceneLogic = kea<sessionRecordingsPlaylist
})),

beforeUnload(({ values, actions }) => ({
enabled: (newLocation) => values.hasChanges && newLocation?.pathname !== router.values.location.pathname,
enabled: (newLocation) =>
values.activeScene === Scene.ReplayPlaylist &&
values.hasChanges &&
newLocation?.pathname !== router.values.location.pathname,
message: 'Leave playlist?\nChanges you made will be discarded.',
onConfirm: () => {
actions.setFilters(values.playlist?.filters || null)
Expand Down

0 comments on commit ccbe3a1

Please sign in to comment.