Skip to content

Commit

Permalink
feat: simplified filters (#20538)
Browse files Browse the repository at this point in the history
  • Loading branch information
daibhin authored Feb 27, 2024
1 parent fbe120f commit 67538b5
Show file tree
Hide file tree
Showing 26 changed files with 774 additions and 833 deletions.
Binary file modified frontend/__snapshots__/lemon-ui-lemon-collapse--multiple--dark.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-lemon-collapse--multiple--light.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.
11 changes: 10 additions & 1 deletion frontend/src/lib/components/PropertyFilters/PropertyFilters.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import './PropertyFilters.scss'
import { BindLogic, useActions, useValues } from 'kea'
import { TaxonomicPropertyFilter } from 'lib/components/PropertyFilters/components/TaxonomicPropertyFilter'
import { TaxonomicFilterGroupType, TaxonomicFilterProps } from 'lib/components/TaxonomicFilter/types'
import React, { useEffect } from 'react'
import React, { useEffect, useState } from 'react'
import { LogicalRowDivider } from 'scenes/cohorts/CohortFilters/CohortCriteriaRowBuilder'

import { AnyDataNode } from '~/queries/schema'
Expand Down Expand Up @@ -32,6 +32,7 @@ interface PropertyFiltersProps {
hasRowOperator?: boolean
sendAllKeyUpdates?: boolean
allowNew?: boolean
openOnInsert?: boolean
errorMessages?: JSX.Element[] | null
propertyAllowList?: { [key in TaxonomicFilterGroupType]?: string[] }
allowRelativeDateOptions?: boolean
Expand All @@ -56,19 +57,26 @@ export function PropertyFilters({
hasRowOperator = true,
sendAllKeyUpdates = false,
allowNew = true,
openOnInsert = false,
errorMessages = null,
propertyAllowList,
allowRelativeDateOptions,
}: PropertyFiltersProps): JSX.Element {
const logicProps = { propertyFilters, onChange, pageKey, sendAllKeyUpdates }
const { filters, filtersWithNew } = useValues(propertyFilterLogic(logicProps))
const { remove, setFilters } = useActions(propertyFilterLogic(logicProps))
const [allowOpenOnInsert, setAllowOpenOnInsert] = useState<boolean>(false)

// Update the logic's internal filters when the props change
useEffect(() => {
setFilters(propertyFilters ?? [])
}, [propertyFilters])

// do not open on initial render, only open if newly inserted
useEffect(() => {
setAllowOpenOnInsert(true)
}, [])

return (
<div className="PropertyFilters">
{showNestedArrow && !disablePopover && (
Expand Down Expand Up @@ -120,6 +128,7 @@ export function PropertyFilters({
/>
)}
errorMessage={errorMessages && errorMessages[index]}
openOnInsert={allowOpenOnInsert && openOnInsert}
/>
</React.Fragment>
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import clsx from 'clsx'
import { isValidPropertyFilter } from 'lib/components/PropertyFilters/utils'
import { LemonButton } from 'lib/lemon-ui/LemonButton'
import { Popover } from 'lib/lemon-ui/Popover/Popover'
import React, { useState } from 'react'
import React, { useEffect, useState } from 'react'

import { AnyPropertyFilter, PathCleaningFilter } from '~/types'

Expand All @@ -22,6 +22,7 @@ interface FilterRowProps {
disablePopover?: boolean
filterComponent: (onComplete: () => void) => JSX.Element
label: string
openOnInsert?: boolean
onRemove: (index: number) => void
orFiltering?: boolean
errorMessage?: JSX.Element | null
Expand All @@ -35,6 +36,7 @@ export const FilterRow = React.memo(function FilterRow({
showConditionBadge,
totalCount,
disablePopover = false, // use bare PropertyFilter without popover
openOnInsert = false,
filterComponent,
label,
onRemove,
Expand All @@ -43,6 +45,10 @@ export const FilterRow = React.memo(function FilterRow({
}: FilterRowProps) {
const [open, setOpen] = useState(false)

useEffect(() => {
setOpen(openOnInsert)
}, [])

const { key } = item

const handleVisibleChange = (visible: boolean): void => {
Expand Down
3 changes: 1 addition & 2 deletions frontend/src/lib/lemon-ui/LemonCollapse/LemonCollapse.scss
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
flex-direction: column;
align-items: stretch;
overflow: hidden;
background: var(--bg-light);
border: 1px solid var(--border);
border-radius: var(--radius);
}
Expand All @@ -22,6 +21,7 @@
min-height: 2.875rem !important;
padding: 0.5rem 0.75rem !important; // Override reduced side padding
font-weight: 500 !important; // Override status="stealth"'s font-weight
background: var(--bg-light);
border-radius: 0 !important;

&.LemonButton:active {
Expand All @@ -33,7 +33,6 @@
box-sizing: content-box;
height: 0;
overflow: hidden;
background: var(--bg-light);
border-top-width: 1px;
transition: height 200ms ease;
}
Expand Down
20 changes: 17 additions & 3 deletions frontend/src/lib/lemon-ui/LemonCollapse/LemonCollapse.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,20 @@ import { Transition } from 'react-transition-group'
import { ENTERED, ENTERING } from 'react-transition-group/Transition'
import useResizeObserver from 'use-resize-observer'

import { LemonButton } from '../LemonButton'
import { LemonButton, LemonButtonProps } from '../LemonButton'

export interface LemonCollapsePanel<K extends React.Key> {
key: K
header: ReactNode
content: ReactNode
className?: string
}

interface LemonCollapsePropsBase<K extends React.Key> {
/** Panels in order of display. Falsy values mean that the panel isn't rendered. */
panels: (LemonCollapsePanel<K> | null | false)[]
className?: string
size?: LemonButtonProps['size']
}

interface LemonCollapsePropsSingle<K extends React.Key> extends LemonCollapsePropsBase<K> {
Expand All @@ -40,6 +42,7 @@ type LemonCollapseProps<K extends React.Key> = LemonCollapsePropsSingle<K> | Lem
export function LemonCollapse<K extends React.Key>({
panels,
className,
size,
...props
}: LemonCollapseProps<K>): JSX.Element {
let isPanelExpanded: (key: K) => boolean
Expand Down Expand Up @@ -74,6 +77,7 @@ export function LemonCollapse<K extends React.Key>({
<LemonCollapsePanel
key={key}
{...panel}
size={size}
isExpanded={isPanelExpanded(key)}
onChange={(isExanded) => onPanelChange(key, isExanded)}
/>
Expand All @@ -86,10 +90,19 @@ interface LemonCollapsePanelProps {
header: ReactNode
content: ReactNode
isExpanded: boolean
size: LemonButtonProps['size']
onChange: (isExpanded: boolean) => void
className?: string
}

function LemonCollapsePanel({ header, content, isExpanded, onChange }: LemonCollapsePanelProps): JSX.Element {
function LemonCollapsePanel({
header,
content,
isExpanded,
size,
className,
onChange,
}: LemonCollapsePanelProps): JSX.Element {
const { height: contentHeight, ref: contentRef } = useResizeObserver({ box: 'border-box' })

return (
Expand All @@ -98,6 +111,7 @@ function LemonCollapsePanel({ header, content, isExpanded, onChange }: LemonColl
onClick={() => onChange(!isExpanded)}
icon={isExpanded ? <IconCollapse /> : <IconExpand />}
className="LemonCollapsePanel__header"
size={size}
>
{header}
</LemonButton>
Expand All @@ -115,7 +129,7 @@ function LemonCollapsePanel({ header, content, isExpanded, onChange }: LemonColl
}
aria-busy={status.endsWith('ing')}
>
<div className="LemonCollapsePanel__content" ref={contentRef}>
<div className={clsx('LemonCollapsePanel__content', className)} ref={contentRef}>
{content}
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export function InstructionsModal({ onClose, visible, featureFlag }: Instruction

const getCloudPanels = (): JSX.Element => (
<LemonCollapse
className="mt-2"
className="mt-2 bg-bg-light"
defaultActiveKey="1"
panels={[
{
Expand Down
1 change: 1 addition & 0 deletions frontend/src/scenes/experiments/Experiment.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -718,6 +718,7 @@ export function Experiment(): JSX.Element {
{
key: 'experiment-details',
header: 'Experiment details',
className: 'bg-bg-light',
content: (
<div>
<div className={isExperimentRunning ? 'w-1/2' : 'w-full'}>
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/scenes/experiments/SecondaryMetricsResult.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export function SecondaryMetricsResult(): JSX.Element {
<LoadingState />
) : (
<LemonCollapse
className="w-full mt-4"
className="w-full mt-4 bg-bg-light"
defaultActiveKey="secondary-metric-results-0"
panels={
experiment.secondary_metrics?.map((metric, index) => {
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/scenes/groups/Group.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ export function Group(): JSX.Element {
<SessionRecordingsPlaylist
logicKey="groups-recordings"
updateSearchParams
filters={{
advancedFilters={{
events: [
{
type: 'events',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export function InternalMetricsTab(): JSX.Element {
<>
<LemonCollapse
activeKeys={openSections}
className="bg-bg-light"
onChange={(keys) => setOpenSections(keys)}
multiple
panels={[
Expand Down
34 changes: 17 additions & 17 deletions frontend/src/scenes/notebooks/Nodes/NotebookNodePlaylist.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { createPostHogWidgetNode } from 'scenes/notebooks/Nodes/NodeWrapper'
import { FilterType, NotebookNodeType, RecordingFilters, ReplayTabs } from '~/types'
import {
DEFAULT_SIMPLE_RECORDING_FILTERS,
SessionRecordingPlaylistLogicProps,
addedAdvancedFilters,
getDefaultFilters,
sessionRecordingsPlaylistLogic,
} from 'scenes/session-recordings/playlist/sessionRecordingsPlaylistLogic'
import { BuiltLogic, useActions, useValues } from 'kea'
import { useEffect, useMemo, useState } from 'react'
import { useEffect, useMemo } from 'react'
import { urls } from 'scenes/urls'
import { notebookNodeLogic } from './notebookNodeLogic'
import { JSONContent, NotebookNodeProps, NotebookNodeAttributeProperties } from '../Notebook/utils'
Expand All @@ -22,13 +22,14 @@ const Component = ({
attributes,
updateAttributes,
}: NotebookNodeProps<NotebookNodePlaylistAttributes>): JSX.Element => {
const { filters, pinned, nodeId } = attributes
const { filters, simpleFilters, pinned, nodeId } = attributes
const playerKey = `notebook-${nodeId}`

const recordingPlaylistLogicProps: SessionRecordingPlaylistLogicProps = useMemo(
() => ({
logicKey: playerKey,
filters,
advancedFilters: filters,
simpleFilters,
updateSearchParams: false,
autoPlay: false,
onFiltersChange: (newFilters: RecordingFilters) => {
Expand Down Expand Up @@ -117,32 +118,28 @@ export const Settings = ({
attributes,
updateAttributes,
}: NotebookNodeAttributeProperties<NotebookNodePlaylistAttributes>): JSX.Element => {
const { filters } = attributes
const [showAdvancedFilters, setShowAdvancedFilters] = useState(false)
const { filters, simpleFilters } = attributes
const defaultFilters = getDefaultFilters()

const hasAdvancedFilters = useMemo(() => {
const defaultFilters = getDefaultFilters()
return addedAdvancedFilters(filters, defaultFilters)
}, [filters])

return (
<ErrorBoundary>
<SessionRecordingsFilters
filters={{ ...defaultFilters, ...filters }}
setFilters={(filters) => updateAttributes({ filters })}
advancedFilters={{ ...defaultFilters, ...filters }}
simpleFilters={simpleFilters ?? DEFAULT_SIMPLE_RECORDING_FILTERS}
setAdvancedFilters={(filters) => updateAttributes({ filters })}
setSimpleFilters={(simpleFilters) => updateAttributes({ simpleFilters })}
showPropertyFilters
onReset={() => updateAttributes({ filters: undefined })}
hasAdvancedFilters={hasAdvancedFilters}
showAdvancedFilters={showAdvancedFilters}
setShowAdvancedFilters={setShowAdvancedFilters}
onReset={() =>
updateAttributes({ filters: defaultFilters, simpleFilters: DEFAULT_SIMPLE_RECORDING_FILTERS })
}
/>
</ErrorBoundary>
)
}

type NotebookNodePlaylistAttributes = {
filters: RecordingFilters
simpleFilters?: RecordingFilters
pinned?: string[]
}

Expand All @@ -161,6 +158,9 @@ export const NotebookNodePlaylist = createPostHogWidgetNode<NotebookNodePlaylist
filters: {
default: undefined,
},
simpleFilters: {
default: {},
},
pinned: {
default: undefined,
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,35 +9,34 @@ import { MathAvailability } from 'scenes/insights/filters/ActionFilter/ActionFil
import { TestAccountFilter } from 'scenes/insights/filters/TestAccountFilter'

import { groupsModel } from '~/models/groupsModel'
import { EntityTypes, FilterableLogLevel, FilterType, RecordingDurationFilter, RecordingFilters } from '~/types'
import { EntityTypes, FilterableLogLevel, RecordingDurationFilter, RecordingFilters } from '~/types'

import { DurationFilter } from './DurationFilter'

export const AdvancedSessionRecordingsFilters = ({
filters,
setFilters,
localFilters,
setLocalFilters,
showPropertyFilters,
}: {
filters: RecordingFilters
setFilters: (filters: RecordingFilters) => void
localFilters: FilterType
setLocalFilters: (localFilters: FilterType) => void
showPropertyFilters?: boolean
}): JSX.Element => {
const { groupsTaxonomicTypes } = useValues(groupsModel)

return (
<div className="space-y-2">
<div className="space-y-2 bg-light p-3">
<LemonLabel info="Show recordings where all of the events or actions listed below happen.">
Events and actions
</LemonLabel>

<ActionFilter
filters={localFilters}
filters={{ events: filters.events || [], actions: filters.actions || [] }}
setFilters={(payload) => {
setLocalFilters(payload)
setFilters({
events: payload.events || [],
actions: payload.actions || [],
})
}}
typeKey="session-recordings"
mathAvailability={MathAvailability.None}
Expand Down Expand Up @@ -81,6 +80,7 @@ export const AdvancedSessionRecordingsFilters = ({
)}

<LemonLabel>Time and duration</LemonLabel>

<div className="flex flex-wrap gap-2">
<DateFilter
dateFrom={filters.date_from ?? '-7d'}
Expand Down
Loading

0 comments on commit 67538b5

Please sign in to comment.