Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(data exploration): convert funnel correlation feedback #14971

Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
109 changes: 109 additions & 0 deletions frontend/src/scenes/funnels/funnelCorrelationFeedbackLogic.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import posthog from 'posthog-js'
import { expectLogic } from 'kea-test-utils'
import { initKeaTests } from '~/test/init'
import { eventUsageLogic } from 'lib/utils/eventUsageLogic'
import { AvailableFeature, InsightLogicProps, InsightType } from '~/types'
import { useAvailableFeatures } from '~/mocks/features'
import { funnelCorrelationFeedbackLogic } from './funnelCorrelationFeedbackLogic'

describe('funnelCorrelationFeedbackLogic', () => {
let logic: ReturnType<typeof funnelCorrelationFeedbackLogic.build>

beforeEach(() => {
useAvailableFeatures([AvailableFeature.CORRELATION_ANALYSIS])
initKeaTests(false)
})

const defaultProps: InsightLogicProps = {
dashboardItemId: undefined,
cachedInsight: {
short_id: undefined,
filters: {
insight: InsightType.FUNNELS,
actions: [
{ id: '$pageview', order: 0 },
{ id: '$pageview', order: 1 },
],
},
result: [],
},
}

beforeEach(async () => {
logic = funnelCorrelationFeedbackLogic(defaultProps)
logic.mount()
})

it('opens detailed feedback on selecting a valid rating', async () => {
await expectLogic(logic, () => {
logic.actions.setCorrelationFeedbackRating(1)
})
.toMatchValues(logic, {
correlationFeedbackRating: 1,
})
.toDispatchActions(logic, [
(action) =>
action.type === logic.actionTypes.setCorrelationDetailedFeedbackVisible &&
action.payload.visible === true,
])
.toMatchValues(logic, {
correlationDetailedFeedbackVisible: true,
})
})

it('doesnt opens detailed feedback on selecting an invalid rating', async () => {
await expectLogic(logic, () => {
logic.actions.setCorrelationFeedbackRating(0)
})
.toMatchValues(logic, {
correlationFeedbackRating: 0,
})
.toDispatchActions(logic, [
(action) =>
action.type === logic.actionTypes.setCorrelationDetailedFeedbackVisible &&
action.payload.visible === false,
])
.toMatchValues(logic, {
correlationDetailedFeedbackVisible: false,
})
})

it('captures emoji feedback properly', async () => {
jest.spyOn(posthog, 'capture')
await expectLogic(logic, () => {
logic.actions.setCorrelationFeedbackRating(1)
})
.toMatchValues(logic, {
// reset after sending feedback
correlationFeedbackRating: 1,
})
.toDispatchActions(eventUsageLogic, ['reportCorrelationAnalysisFeedback'])

expect(posthog.capture).toBeCalledWith('correlation analysis feedback', { rating: 1 })
})

it('goes away on sending feedback, capturing it properly', async () => {
jest.spyOn(posthog, 'capture')
await expectLogic(logic, () => {
logic.actions.setCorrelationFeedbackRating(2)
logic.actions.setCorrelationDetailedFeedback('tests')
logic.actions.sendCorrelationAnalysisFeedback()
})
.toMatchValues(logic, {
// reset after sending feedback
correlationFeedbackRating: 0,
correlationDetailedFeedback: '',
correlationFeedbackHidden: true,
})
.toDispatchActions(eventUsageLogic, ['reportCorrelationAnalysisDetailedFeedback'])
.toFinishListeners()

await expectLogic(eventUsageLogic).toFinishListeners()

expect(posthog.capture).toBeCalledWith('correlation analysis feedback', { rating: 2 })
expect(posthog.capture).toBeCalledWith('correlation analysis detailed feedback', {
rating: 2,
comments: 'tests',
})
})
})
75 changes: 75 additions & 0 deletions frontend/src/scenes/funnels/funnelCorrelationFeedbackLogic.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { actions, connect, kea, key, listeners, path, props, reducers } from 'kea'
import { lemonToast } from '@posthog/lemon-ui'
import { eventUsageLogic } from 'lib/utils/eventUsageLogic'
import { keyForInsightLogicProps } from 'scenes/insights/sharedUtils'

import type { funnelCorrelationFeedbackLogicType } from './funnelCorrelationFeedbackLogicType'
import { funnelLogic } from './funnelLogic'
import { InsightLogicProps } from '~/types'

export const funnelCorrelationFeedbackLogic = kea<funnelCorrelationFeedbackLogicType>([
props({} as InsightLogicProps),
key(keyForInsightLogicProps('insight_funnel')),
path((key) => ['scenes', 'funnels', 'funnelCorrelationFeedbackLogic', key]),

connect((props: InsightLogicProps) => ({
actions: [funnelLogic(props), ['loadEventCorrelations', 'loadPropertyCorrelations']],
})),

actions({
sendCorrelationAnalysisFeedback: true,
hideCorrelationAnalysisFeedback: true,
setCorrelationFeedbackRating: (rating: number) => ({ rating }),
setCorrelationDetailedFeedback: (comment: string) => ({ comment }),
setCorrelationDetailedFeedbackVisible: (visible: boolean) => ({ visible }),
}),
reducers({
correlationFeedbackHidden: [
true,
{
// don't load the feedback form until after some results were loaded
loadEventCorrelations: () => false,
loadPropertyCorrelations: () => false,
sendCorrelationAnalysisFeedback: () => true,
hideCorrelationAnalysisFeedback: () => true,
},
],
correlationDetailedFeedbackVisible: [
false,
{
setCorrelationDetailedFeedbackVisible: (_, { visible }) => visible,
},
],
correlationFeedbackRating: [
0,
{
setCorrelationFeedbackRating: (_, { rating }) => rating,
},
],
correlationDetailedFeedback: [
'',
{
setCorrelationDetailedFeedback: (_, { comment }) => comment,
},
],
}),
listeners(({ actions, values }) => ({
sendCorrelationAnalysisFeedback: () => {
eventUsageLogic.actions.reportCorrelationAnalysisDetailedFeedback(
values.correlationFeedbackRating,
values.correlationDetailedFeedback
)
actions.setCorrelationFeedbackRating(0)
actions.setCorrelationDetailedFeedback('')
lemonToast.success('Thanks for your feedback! Your comments help us improve')
},
setCorrelationFeedbackRating: ({ rating }) => {
const feedbackBoxVisible = rating > 0
actions.setCorrelationDetailedFeedbackVisible(feedbackBoxVisible)
if (feedbackBoxVisible) {
// Don't send event when resetting reducer
eventUsageLogic.actions.reportCorrelationAnalysisFeedback(rating)
}
},
})),
])
79 changes: 0 additions & 79 deletions frontend/src/scenes/funnels/funnelLogic.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { DEFAULT_EXCLUDED_PERSON_PROPERTIES, funnelLogic } from './funnelLogic'
import { MOCK_DEFAULT_TEAM, MOCK_TEAM_ID } from 'lib/api.mock'
import posthog from 'posthog-js'
import { expectLogic, partial } from 'kea-test-utils'
import { initKeaTests } from '~/test/init'
import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic'
Expand Down Expand Up @@ -1054,84 +1053,6 @@ describe('funnelLogic', () => {
})
})

describe('Correlation Feedback flow', () => {
beforeEach(async () => {
await initFunnelLogic()
})
it('opens detailed feedback on selecting a valid rating', async () => {
await expectLogic(logic, () => {
logic.actions.setCorrelationFeedbackRating(1)
})
.toMatchValues(logic, {
correlationFeedbackRating: 1,
})
.toDispatchActions(logic, [
(action) =>
action.type === logic.actionTypes.setCorrelationDetailedFeedbackVisible &&
action.payload.visible === true,
])
.toMatchValues(logic, {
correlationDetailedFeedbackVisible: true,
})
})

it('doesnt opens detailed feedback on selecting an invalid rating', async () => {
await expectLogic(logic, () => {
logic.actions.setCorrelationFeedbackRating(0)
})
.toMatchValues(logic, {
correlationFeedbackRating: 0,
})
.toDispatchActions(logic, [
(action) =>
action.type === logic.actionTypes.setCorrelationDetailedFeedbackVisible &&
action.payload.visible === false,
])
.toMatchValues(logic, {
correlationDetailedFeedbackVisible: false,
})
})

it('Captures emoji feedback properly', async () => {
jest.spyOn(posthog, 'capture')
await expectLogic(logic, () => {
logic.actions.setCorrelationFeedbackRating(1)
})
.toMatchValues(logic, {
// reset after sending feedback
correlationFeedbackRating: 1,
})
.toDispatchActions(eventUsageLogic, ['reportCorrelationAnalysisFeedback'])

expect(posthog.capture).toBeCalledWith('correlation analysis feedback', { rating: 1 })
})

it('goes away on sending feedback, capturing it properly', async () => {
jest.spyOn(posthog, 'capture')
await expectLogic(logic, () => {
logic.actions.setCorrelationFeedbackRating(2)
logic.actions.setCorrelationDetailedFeedback('tests')
logic.actions.sendCorrelationAnalysisFeedback()
})
.toMatchValues(logic, {
// reset after sending feedback
correlationFeedbackRating: 0,
correlationDetailedFeedback: '',
correlationFeedbackHidden: true,
})
.toDispatchActions(eventUsageLogic, ['reportCorrelationAnalysisDetailedFeedback'])
.toFinishListeners()

await expectLogic(eventUsageLogic).toFinishListeners()

expect(posthog.capture).toBeCalledWith('correlation analysis feedback', { rating: 2 })
expect(posthog.capture).toBeCalledWith('correlation analysis detailed feedback', {
rating: 2,
comments: 'tests',
})
})
})

describe('funnel simple vs. advanced mode', () => {
beforeEach(async () => {
await initFunnelLogic()
Expand Down
50 changes: 0 additions & 50 deletions frontend/src/scenes/funnels/funnelLogic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,12 +170,7 @@ export const funnelLogic = kea<funnelLogicType>({
// Correlation related actions
setCorrelationTypes: (types: FunnelCorrelationType[]) => ({ types }),
setPropertyCorrelationTypes: (types: FunnelCorrelationType[]) => ({ types }),
setCorrelationDetailedFeedback: (comment: string) => ({ comment }),
setCorrelationFeedbackRating: (rating: number) => ({ rating }),
setCorrelationDetailedFeedbackVisible: (visible: boolean) => ({ visible }),
sendCorrelationAnalysisFeedback: true,
hideSkewWarning: true,
hideCorrelationAnalysisFeedback: true,
setFunnelCorrelationDetails: (payload: FunnelCorrelation | null) => ({ payload }),

setPropertyNames: (propertyNames: string[]) => ({ propertyNames }),
Expand Down Expand Up @@ -332,34 +327,6 @@ export const funnelLogic = kea<funnelLogicType>({
hideSkewWarning: () => true,
},
],
correlationFeedbackHidden: [
true,
{
// don't load the feedback form until after some results were loaded
loadEventCorrelations: () => false,
loadPropertyCorrelations: () => false,
sendCorrelationAnalysisFeedback: () => true,
hideCorrelationAnalysisFeedback: () => true,
},
],
correlationDetailedFeedbackVisible: [
false,
{
setCorrelationDetailedFeedbackVisible: (_, { visible }) => visible,
},
],
correlationFeedbackRating: [
0,
{
setCorrelationFeedbackRating: (_, { rating }) => rating,
},
],
correlationDetailedFeedback: [
'',
{
setCorrelationDetailedFeedback: (_, { comment }) => comment,
},
],
eventWithPropertyCorrelations: {
loadEventWithPropertyCorrelationsSuccess: (state, { eventWithPropertyCorrelations }) => {
return {
Expand Down Expand Up @@ -1215,23 +1182,6 @@ export const funnelLogic = kea<funnelLogicType>({
{ property_names: propertyNames.length === values.allProperties.length ? '$all' : propertyNames }
)
},
sendCorrelationAnalysisFeedback: () => {
eventUsageLogic.actions.reportCorrelationAnalysisDetailedFeedback(
values.correlationFeedbackRating,
values.correlationDetailedFeedback
)
actions.setCorrelationFeedbackRating(0)
actions.setCorrelationDetailedFeedback('')
lemonToast.success('Thanks for your feedback! Your comments help us improve')
},
setCorrelationFeedbackRating: ({ rating }) => {
const feedbackBoxVisible = rating > 0
actions.setCorrelationDetailedFeedbackVisible(feedbackBoxVisible)
if (feedbackBoxVisible) {
// Don't send event when resetting reducer
eventUsageLogic.actions.reportCorrelationAnalysisFeedback(rating)
}
},
[visibilitySensorLogic({ id: values.correlationPropKey }).actionTypes.setVisible]: async (
{
visible,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export const FunnelCorrelation = (): JSX.Element | null => {
<FunnelCorrelationSkewWarning />
)}
{!isUsingDataExploration && <FunnelCorrelationTable />}
{!isUsingDataExploration && <FunnelCorrelationFeedbackForm />}
<FunnelCorrelationFeedbackForm />
{!isUsingDataExploration && <FunnelPropertyCorrelationTable />}
</div>
</PayGateMini>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { useRef } from 'react'
import { useActions, useValues } from 'kea'

import { insightLogic } from 'scenes/insights/insightLogic'
import { funnelLogic } from 'scenes/funnels/funnelLogic'
import { funnelCorrelationFeedbackLogic } from 'scenes/funnels/funnelCorrelationFeedbackLogic'

import { LemonButton, LemonTextArea } from '@posthog/lemon-ui'
import { IconClose } from 'lib/lemon-ui/icons'
Expand All @@ -11,14 +11,14 @@ import { CommentOutlined } from '@ant-design/icons'
export const FunnelCorrelationFeedbackForm = (): JSX.Element | null => {
const { insightProps } = useValues(insightLogic)
const { correlationFeedbackHidden, correlationDetailedFeedbackVisible, correlationFeedbackRating } = useValues(
funnelLogic(insightProps)
funnelCorrelationFeedbackLogic(insightProps)
)
const {
sendCorrelationAnalysisFeedback,
hideCorrelationAnalysisFeedback,
setCorrelationFeedbackRating,
setCorrelationDetailedFeedback,
} = useActions(funnelLogic(insightProps))
} = useActions(funnelCorrelationFeedbackLogic(insightProps))

const detailedFeedbackRef = useRef<HTMLTextAreaElement>(null)

Expand Down