Skip to content

Commit 042798e

Browse files
github-actions[bot]adboio
authored andcommitted
test(storybook): update UI snapshots
1 parent 7623a0b commit 042798e

File tree

9 files changed

+489
-63
lines changed

9 files changed

+489
-63
lines changed
-3.29 KB
Loading
-3.24 KB
Loading

frontend/src/scenes/feature-flags/FeatureFlagFeedbackTab.tsx

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,6 @@ export function FeedbackTab({ featureFlag }: { featureFlag: FeatureFlagType }):
3535
<QuickSurveyForm flag={featureFlag} />
3636
</div>
3737
</div>
38-
{/* <pre>{JSON.stringify(featureFlag, null, 2)}</pre> */}
39-
{/* <pre>{JSON.stringify(surveys, null, 2)}</pre> */}
4038
</div>
4139
)
4240
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import { useState } from 'react'
2+
3+
import { IconPlus } from '@posthog/icons'
4+
import { LemonButton } from '@posthog/lemon-ui'
5+
6+
import { TaxonomicFilter } from 'lib/components/TaxonomicFilter/TaxonomicFilter'
7+
import { TaxonomicFilterGroupType } from 'lib/components/TaxonomicFilter/types'
8+
import { Popover } from 'lib/lemon-ui/Popover/Popover'
9+
10+
export interface AddEventButtonProps {
11+
onEventSelect: (eventName: string) => void
12+
excludedEvents?: string[]
13+
}
14+
15+
export function AddEventButton({ onEventSelect, excludedEvents = [] }: AddEventButtonProps): JSX.Element {
16+
const [popoverOpen, setPopoverOpen] = useState(false)
17+
18+
return (
19+
<Popover
20+
overlay={
21+
<TaxonomicFilter
22+
groupType={TaxonomicFilterGroupType.Events}
23+
value=""
24+
onChange={(_, value) => {
25+
if (typeof value === 'string') {
26+
const eventName = value
27+
28+
if (!excludedEvents.includes(eventName)) {
29+
onEventSelect(eventName)
30+
}
31+
setPopoverOpen(false)
32+
}
33+
}}
34+
allowNonCapturedEvents
35+
taxonomicGroupTypes={[TaxonomicFilterGroupType.CustomEvents, TaxonomicFilterGroupType.Events]}
36+
/>
37+
}
38+
visible={popoverOpen}
39+
onClickOutside={() => setPopoverOpen(false)}
40+
placement="bottom-start"
41+
>
42+
<LemonButton
43+
type="secondary"
44+
icon={<IconPlus />}
45+
onClick={() => setPopoverOpen(!popoverOpen)}
46+
size="small"
47+
className="w-fit"
48+
>
49+
Add event
50+
</LemonButton>
51+
</Popover>
52+
)
53+
}
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
import '@testing-library/jest-dom'
2+
import { cleanup, render, screen, waitFor } from '@testing-library/react'
3+
import userEvent from '@testing-library/user-event'
4+
5+
import { useMocks } from '~/mocks/jest'
6+
import { initKeaTests } from '~/test/init'
7+
import { AccessControlLevel, FeatureFlagEvaluationRuntime, type FeatureFlagType } from '~/types'
8+
9+
import { QuickSurveyForm } from './QuickSurveyModal'
10+
11+
jest.mock('scenes/surveys/SurveyAppearancePreview', () => ({
12+
SurveyAppearancePreview: () => <div>Preview</div>,
13+
}))
14+
jest.mock('scenes/surveys/SurveySettings', () => ({
15+
SurveyPopupToggle: () => <div>Survey Toggle</div>,
16+
}))
17+
jest.mock('scenes/surveys/AddEventButton', () => ({
18+
AddEventButton: ({ onEventSelect }: any) => <button onClick={() => onEventSelect('test-event')}>Add event</button>,
19+
}))
20+
21+
const mockFlag: FeatureFlagType = {
22+
id: 1,
23+
key: 'test-flag',
24+
name: 'Test Flag',
25+
filters: { groups: [], multivariate: null, payloads: {} },
26+
deleted: false,
27+
active: true,
28+
created_at: '2023-01-01',
29+
created_by: null,
30+
is_simple_flag: false,
31+
rollout_percentage: null,
32+
ensure_experience_continuity: false,
33+
experiment_set: [],
34+
rollback_conditions: [],
35+
performed_rollback: false,
36+
can_edit: true,
37+
tags: [],
38+
features: [],
39+
usage_dashboard: undefined,
40+
analytics_dashboards: [],
41+
has_enriched_analytics: false,
42+
surveys: [],
43+
updated_at: '2023-01-01',
44+
version: 1,
45+
last_modified_by: null,
46+
evaluation_tags: [],
47+
is_remote_configuration: false,
48+
has_encrypted_payloads: false,
49+
status: 'ACTIVE',
50+
evaluation_runtime: FeatureFlagEvaluationRuntime.ALL,
51+
user_access_level: AccessControlLevel.Admin,
52+
}
53+
54+
describe('QuickSurveyForm', () => {
55+
beforeEach(() => {
56+
initKeaTests()
57+
useMocks({
58+
get: {
59+
'/api/projects/:team_id/property_definitions': { results: [], count: 0 },
60+
'/api/projects/:team_id/surveys': { results: [] },
61+
},
62+
post: {
63+
'/api/projects/:team_id/surveys': () => [200, { id: 'new-survey' }],
64+
},
65+
patch: {
66+
'/api/environments/@current/add_product_intent/': () => [200, {}],
67+
},
68+
})
69+
})
70+
71+
afterEach(() => {
72+
cleanup()
73+
})
74+
75+
it('disables create button when question is empty', async () => {
76+
render(<QuickSurveyForm flag={mockFlag} />)
77+
78+
const questionInput = screen.getByPlaceholderText('What do you think?')
79+
userEvent.clear(questionInput)
80+
81+
expect(screen.getByRole('button', { name: /create & launch/i })).toHaveAttribute('aria-disabled', 'true')
82+
})
83+
84+
it('creates survey with selected events in conditions', async () => {
85+
let capturedRequest: any
86+
useMocks({
87+
post: {
88+
'/api/projects/:team_id/surveys': async (req) => {
89+
capturedRequest = await req.json()
90+
return [200, { id: 'new-survey' }]
91+
},
92+
},
93+
})
94+
95+
render(<QuickSurveyForm flag={mockFlag} />)
96+
97+
// Add an event
98+
await userEvent.click(screen.getByRole('button', { name: /add event/i }))
99+
expect(screen.getByText('test-event')).toBeInTheDocument()
100+
101+
// Create survey
102+
await userEvent.click(screen.getByRole('button', { name: /create & launch/i }))
103+
104+
await waitFor(() => {
105+
expect(capturedRequest).not.toBeUndefined()
106+
expect(capturedRequest.conditions.events.values).toEqual([{ name: 'test-event' }])
107+
expect(capturedRequest.linked_flag_id).toBe(1)
108+
})
109+
})
110+
111+
it('creates survey without events when none selected', async () => {
112+
let capturedRequest: any
113+
useMocks({
114+
post: {
115+
'/api/projects/:team_id/surveys': async (req) => {
116+
capturedRequest = await req.json()
117+
return [200, { id: 'new-survey' }]
118+
},
119+
},
120+
})
121+
122+
render(<QuickSurveyForm flag={mockFlag} />)
123+
124+
await userEvent.click(screen.getByRole('button', { name: /create & launch/i }))
125+
126+
await waitFor(() => {
127+
expect(capturedRequest).not.toBeUndefined()
128+
expect(capturedRequest.conditions.events.values).toEqual([])
129+
})
130+
})
131+
132+
it('includes selected variant in survey conditions', async () => {
133+
const multivariateFlag: FeatureFlagType = {
134+
...mockFlag,
135+
filters: {
136+
groups: [],
137+
multivariate: {
138+
variants: [
139+
{ key: 'control', rollout_percentage: 50 },
140+
{ key: 'test', rollout_percentage: 50 },
141+
],
142+
},
143+
payloads: {},
144+
},
145+
}
146+
147+
let capturedRequest: any
148+
useMocks({
149+
post: {
150+
'/api/projects/:team_id/surveys': async (req) => {
151+
capturedRequest = await req.json()
152+
return [200, { id: 'new-survey' }]
153+
},
154+
},
155+
})
156+
157+
render(<QuickSurveyForm flag={multivariateFlag} />)
158+
159+
// Select the 'test' variant
160+
const testVariantRadio = screen.getByLabelText(/test/i, { selector: 'input[type="radio"]' })
161+
await userEvent.click(testVariantRadio)
162+
163+
await userEvent.click(screen.getByRole('button', { name: /create & launch/i }))
164+
165+
await waitFor(() => {
166+
expect(capturedRequest).not.toBeUndefined()
167+
expect(capturedRequest.conditions.linkedFlagVariant).toBe('test')
168+
})
169+
})
170+
})

frontend/src/scenes/surveys/QuickSurveyModal.tsx

Lines changed: 41 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { useActions, useValues } from 'kea'
22
import { router } from 'kea-router'
33
import { useEffect, useMemo, useState } from 'react'
44

5+
import { IconX } from '@posthog/icons'
56
import { LemonButton, LemonLabel, LemonModal, LemonTextArea, lemonToast } from '@posthog/lemon-ui'
67

78
import api from 'lib/api'
@@ -10,8 +11,9 @@ import { LemonInputSelect } from 'lib/lemon-ui/LemonInputSelect/LemonInputSelect
1011
import { LemonMenuOverlay } from 'lib/lemon-ui/LemonMenu/LemonMenu'
1112
import { eventUsageLogic } from 'lib/utils/eventUsageLogic'
1213
import { addProductIntent } from 'lib/utils/product-intents'
14+
import { AddEventButton } from 'scenes/surveys/AddEventButton'
1315
import { SurveyAppearancePreview } from 'scenes/surveys/SurveyAppearancePreview'
14-
import { SurveySettings } from 'scenes/surveys/SurveySettings'
16+
import { SurveyPopupToggle } from 'scenes/surveys/SurveySettings'
1517
import { NewSurvey, SURVEY_CREATED_SOURCE, defaultSurveyAppearance } from 'scenes/surveys/constants'
1618
import { surveysLogic } from 'scenes/surveys/surveysLogic'
1719
import { urls } from 'scenes/urls'
@@ -30,6 +32,7 @@ export function QuickSurveyForm({ flag, onCancel }: QuickSurveyFormProps): JSX.E
3032
const [question, setQuestion] = useState(`You're trying our latest new feature. What do you think?`)
3133
const [targetVariant, setTargetVariant] = useState<string | null>(null)
3234
const [targetUrl, setTargetUrl] = useState<string>('')
35+
const [selectedEvents, setSelectedEvents] = useState<string[]>([])
3336
const [isCreating, setIsCreating] = useState(false)
3437

3538
const { showSurveysDisabledBanner } = useValues(surveysLogic)
@@ -38,6 +41,8 @@ export function QuickSurveyForm({ flag, onCancel }: QuickSurveyFormProps): JSX.E
3841
const { options } = useValues(propertyDefinitionsModel)
3942
const { loadPropertyValues } = useActions(propertyDefinitionsModel)
4043

44+
const [shouldShowSurveyToggle, _] = useState<boolean>(!!showSurveysDisabledBanner)
45+
4146
const variants = flag.filters?.multivariate?.variants || []
4247
const isMultivariate = variants.length > 1
4348
const urlOptions = options['$current_url']
@@ -58,7 +63,7 @@ export function QuickSurveyForm({ flag, onCancel }: QuickSurveyFormProps): JSX.E
5863
const buildSurveyData = (launch: boolean): Partial<Survey> => {
5964
const randomId = Math.random().toString(36).substring(2, 8)
6065
return {
61-
name: `${flagName} - Quick feedback #${randomId}`,
66+
name: `${flagName}${targetVariant ? ` (${targetVariant})` : ''} - Quick feedback #${randomId}`,
6267
type: SurveyType.Popover,
6368
questions: [
6469
{
@@ -70,17 +75,7 @@ export function QuickSurveyForm({ flag, onCancel }: QuickSurveyFormProps): JSX.E
7075
conditions: {
7176
actions: null,
7277
events: {
73-
values: [
74-
{
75-
name: '$feature_flag_called',
76-
propertyFilters: {
77-
$feature_flag: {
78-
values: [flag.key],
79-
operator: 'exact' as const,
80-
},
81-
},
82-
},
83-
],
78+
values: selectedEvents.map((name) => ({ name })),
8479
},
8580
...(targetVariant ? { linkedFlagVariant: targetVariant } : {}),
8681
...(targetUrl ? { url: targetUrl } : {}),
@@ -99,7 +94,7 @@ export function QuickSurveyForm({ flag, onCancel }: QuickSurveyFormProps): JSX.E
9994
created_at: '',
10095
created_by: null,
10196
}) as NewSurvey,
102-
[question, targetVariant, targetUrl, buildSurveyData]
97+
[question, targetVariant, targetUrl, selectedEvents, flag] // oxlint-disable-line react-hooks/exhaustive-deps
10398
)
10499

105100
const handleCreate = async ({ createType }: { createType: 'launch' | 'edit' | 'draft' }): Promise<void> => {
@@ -122,6 +117,7 @@ export function QuickSurveyForm({ flag, onCancel }: QuickSurveyFormProps): JSX.E
122117
source: SURVEY_CREATED_SOURCE.FEATURE_FLAGS,
123118
created_successfully: true,
124119
quick_survey: true,
120+
create_mode: createType,
125121
},
126122
})
127123

@@ -211,6 +207,35 @@ export function QuickSurveyForm({ flag, onCancel }: QuickSurveyFormProps): JSX.E
211207
data-attr="quick-survey-url-input"
212208
/>
213209
</div>
210+
211+
<div>
212+
<LemonLabel className="mb-2">Trigger on events (optional)</LemonLabel>
213+
{selectedEvents.length > 0 && (
214+
<div className="space-y-2 mb-2">
215+
{selectedEvents.map((eventName) => (
216+
<div
217+
key={eventName}
218+
className="flex items-center justify-between p-2 border rounded bg-bg-light"
219+
>
220+
<span className="text-sm font-medium">{eventName}</span>
221+
<LemonButton
222+
size="xsmall"
223+
icon={<IconX />}
224+
onClick={() =>
225+
setSelectedEvents(selectedEvents.filter((e) => e !== eventName))
226+
}
227+
type="tertiary"
228+
status="alt"
229+
/>
230+
</div>
231+
))}
232+
</div>
233+
)}
234+
<AddEventButton
235+
onEventSelect={(eventName) => setSelectedEvents([...selectedEvents, eventName])}
236+
excludedEvents={selectedEvents}
237+
/>
238+
</div>
214239
</div>
215240

216241
<div>
@@ -221,9 +246,9 @@ export function QuickSurveyForm({ flag, onCancel }: QuickSurveyFormProps): JSX.E
221246
</div>
222247

223248
<div className="mt-6">
224-
{showSurveysDisabledBanner && (
249+
{shouldShowSurveyToggle && (
225250
<div className="mb-4 p-4 border rounded bg-warning-highlight">
226-
<SurveySettings isModal />
251+
<SurveyPopupToggle />
227252
</div>
228253
)}
229254

0 commit comments

Comments
 (0)