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(surveys): Add regex matching option #17738

Merged
merged 6 commits into from
Oct 4, 2023
Merged
Show file tree
Hide file tree
Changes from all 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
45 changes: 36 additions & 9 deletions frontend/src/scenes/surveys/Survey.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
SurveyType,
LinkSurveyQuestion,
RatingSurveyQuestion,
SurveyUrlMatchType,
} from '~/types'
import { FlagSelector } from 'scenes/early-access-features/EarlyAccessFeature'
import { IconCancel, IconDelete, IconPlus, IconPlusMini } from 'lib/lemon-ui/icons'
Expand All @@ -32,7 +33,7 @@ import { SurveyAppearance } from './SurveyAppearance'
import { SurveyAPIEditor } from './SurveyAPIEditor'
import { featureFlagLogic as enabledFeaturesLogic } from 'lib/logic/featureFlagLogic'
import { featureFlagLogic } from 'scenes/feature-flags/featureFlagLogic'
import { defaultSurveyFieldValues, defaultSurveyAppearance, NewSurvey } from './constants'
import { defaultSurveyFieldValues, defaultSurveyAppearance, NewSurvey, SurveyUrlMatchTypeLabels } from './constants'
import { FEATURE_FLAGS } from 'lib/constants'
import { FeatureFlagReleaseConditions } from 'scenes/feature-flags/FeatureFlagReleaseConditions'

Expand Down Expand Up @@ -61,7 +62,8 @@ export function SurveyComponent({ id }: { id?: string } = {}): JSX.Element {
}

export function SurveyForm({ id }: { id: string }): JSX.Element {
const { survey, surveyLoading, isEditingSurvey, hasTargetingFlag } = useValues(surveyLogic)
const { survey, surveyLoading, isEditingSurvey, hasTargetingFlag, urlMatchTypeValidationError } =
useValues(surveyLogic)
const { loadSurvey, editingSurvey, setSurveyValue, setDefaultForQuestionType } = useActions(surveyLogic)
const { featureFlags } = useValues(enabledFeaturesLogic)

Expand Down Expand Up @@ -405,12 +407,31 @@ export function SurveyForm({ id }: { id: string }): JSX.Element {
<Field name="conditions">
{({ value, onChange }) => (
<>
<PureField label="URL contains:">
<LemonInput
value={value?.url}
onChange={(urlVal) => onChange({ ...value, url: urlVal })}
placeholder="ex: https://app.posthog.com"
/>
<PureField
label="URL targeting"
error={urlMatchTypeValidationError}
info="Targeting by regex or exact match requires atleast version 1.82 of posthog-js"
>
<div className="flex flex-row gap-2 items-center">
URL
<LemonSelect
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This won't work for users unless they upgrade to latest posthog-js, so maybe we can include some sort of tooltip or message somewhere about it? 🤔

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah makes sense, added message and validation

image

value={value?.urlMatchType || SurveyUrlMatchType.Contains}
onChange={(matchTypeVal) => {
onChange({ ...value, urlMatchType: matchTypeVal })
}}
data-attr="survey-url-matching-type"
options={Object.keys(SurveyUrlMatchTypeLabels).map((key) => ({
label: SurveyUrlMatchTypeLabels[key],
value: key,
}))}
/>
<LemonInput
value={value?.url}
onChange={(urlVal) => onChange({ ...value, url: urlVal })}
placeholder="ex: https://app.posthog.com"
fullWidth
/>
</div>
</PureField>
<PureField label="Selector matches:">
<LemonInput
Expand Down Expand Up @@ -565,7 +586,13 @@ export function SurveyReleaseSummary({
{survey.conditions?.url && (
<div className="flex flex-col font-medium gap-1">
<div className="flex-row">
<span>URL contains:</span>{' '}
<span>
URL{' '}
{SurveyUrlMatchTypeLabels[
survey.conditions?.urlMatchType || SurveyUrlMatchType.Contains
].slice(2)}
:
</span>{' '}
<span className="simple-tag tag-light-blue text-primary-alt">{survey.conditions.url}</span>
</div>
</div>
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/scenes/surveys/Surveys.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,7 @@ export function Surveys(): JSX.Element {
{
title: 'Question type',
render: function RenderResponses(_, survey) {
return survey.questions.length === 1
return survey.questions?.length === 1
? SurveyQuestionLabel[survey.questions[0].type]
: 'Multiple'
},
Expand Down
8 changes: 7 additions & 1 deletion frontend/src/scenes/surveys/constants.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { FeatureFlagFilters, Survey, SurveyQuestionType, SurveyType } from '~/types'
import { FeatureFlagFilters, Survey, SurveyQuestionType, SurveyType, SurveyUrlMatchType } from '~/types'

export const SURVEY_EVENT_NAME = 'survey sent'
export const SURVEY_RESPONSE_PROPERTY = '$survey_response'
Expand All @@ -11,6 +11,12 @@ export const SurveyQuestionLabel = {
[SurveyQuestionType.MultipleChoice]: 'Multiple choice select',
}

export const SurveyUrlMatchTypeLabels = {
[SurveyUrlMatchType.Contains]: '∋ contains',
[SurveyUrlMatchType.Regex]: '∼ matches regex',
[SurveyUrlMatchType.Exact]: '= equals',
}

export const defaultSurveyAppearance = {
backgroundColor: '#eeeded',
submitButtonText: 'Submit',
Expand Down
16 changes: 16 additions & 0 deletions frontend/src/scenes/surveys/surveyLogic.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
SurveyQuestionBase,
SurveyQuestionType,
SurveyType,
SurveyUrlMatchType,
} from '~/types'
import type { surveyLogicType } from './surveyLogicType'
import { DataTableNode, InsightVizNode, NodeKind } from '~/queries/schema'
Expand Down Expand Up @@ -379,6 +380,19 @@ export const surveyLogic = kea<surveyLogicType>([
return !!survey.targeting_flag || !!survey.targeting_flag_filters
},
],
urlMatchTypeValidationError: [
(s) => [s.survey],
(survey): string | null => {
if (survey.conditions?.urlMatchType === SurveyUrlMatchType.Regex && survey.conditions.url) {
try {
new RegExp(survey.conditions.url)
} catch (e: any) {
return e.message
}
}
return null
},
],
}),
forms(({ actions, props, values }) => ({
survey: {
Expand All @@ -397,6 +411,8 @@ export const surveyLogic = kea<surveyLogicType>([
}
: {}),
})),
// controlled using a PureField in the form
urlMatchType: values.urlMatchTypeValidationError,
}),
submit: async (surveyPayload) => {
let surveyPayloadWithTargetingFlagFilters = surveyPayload
Expand Down
14 changes: 13 additions & 1 deletion frontend/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2090,7 +2090,13 @@ export interface Survey {
linked_flag: FeatureFlagBasicType | null
targeting_flag: FeatureFlagBasicType | null
targeting_flag_filters: Pick<FeatureFlagFilters, 'groups'> | undefined
conditions: { url: string; selector: string; is_headless?: boolean; seenSurveyWaitPeriodInDays?: number } | null
conditions: {
url: string
selector: string
is_headless?: boolean
seenSurveyWaitPeriodInDays?: number
urlMatchType?: SurveyUrlMatchType
} | null
appearance: SurveyAppearance
questions: (BasicSurveyQuestion | LinkSurveyQuestion | RatingSurveyQuestion | MultipleSurveyQuestion)[]
created_at: string
Expand All @@ -2101,6 +2107,12 @@ export interface Survey {
remove_targeting_flag?: boolean
}

export enum SurveyUrlMatchType {
Exact = 'exact',
Contains = 'icontains',
Regex = 'regex',
}

export enum SurveyType {
Popover = 'popover',
Button = 'button',
Expand Down
Loading