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(Experiments): Allow users to edit and preview no-code experiments from experiment page. #25877

Merged
merged 25 commits into from
Nov 4, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
6881ac4
Allow toolbar to open an experiment pre-selected
Phanatic Oct 29, 2024
0cabe7b
add story for web experiment page
Phanatic Oct 29, 2024
ccfaac3
Update query snapshots
github-actions[bot] Oct 29, 2024
beee629
Update frontend/src/scenes/experiments/WebExperimentImplementationDet…
Phanatic Oct 31, 2024
8d7a850
remove flashing errors on Save
Phanatic Oct 31, 2024
a973ed8
Apply text transforms last
Phanatic Oct 31, 2024
edc7025
Merge branch 'master' into exp-show-previews-link
Phanatic Oct 31, 2024
9a0dfa1
Update UI snapshots for `chromium` (2)
github-actions[bot] Oct 31, 2024
a69fe2b
Update UI snapshots for `chromium` (1)
github-actions[bot] Oct 31, 2024
8be14f6
Add test
Phanatic Oct 31, 2024
16c85f9
Update UI snapshots for `chromium` (1)
github-actions[bot] Oct 31, 2024
9579fba
Merge branch 'master' into exp-show-previews-link
Phanatic Oct 31, 2024
aaa022d
Fix and add test for experimentId in user.py
Phanatic Nov 1, 2024
172b90c
fix build errors
Phanatic Nov 1, 2024
79d705c
Merge branch 'master' into exp-show-previews-link
Phanatic Nov 1, 2024
b4dbb4d
remake undone changes
Phanatic Nov 1, 2024
e3a8858
Update UI snapshots for `chromium` (1)
github-actions[bot] Nov 1, 2024
b372228
reuse ExperimentIdType
Phanatic Nov 1, 2024
bcc918b
feat(data-warehouse): Added Chargebee data source (#25818)
rossgray Nov 1, 2024
954ed48
Merge branch 'master' into exp-show-previews-link
Phanatic Nov 1, 2024
6a917dc
Update UI snapshots for `chromium` (1)
github-actions[bot] Nov 1, 2024
3005098
fix issue with Add Authorized URL failing
Phanatic Nov 1, 2024
38e8cd1
Merge branch 'master' into exp-show-previews-link
Phanatic Nov 4, 2024
4ef53b7
Merge branch 'master' into exp-show-previews-link
Phanatic Nov 4, 2024
29fac72
lint sources
Phanatic Nov 4, 2024
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
2 changes: 2 additions & 0 deletions ee/clickhouse/views/experiments.py
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,7 @@ class Meta:
"created_by",
"created_at",
"updated_at",
"type",
]
read_only_fields = [
"id",
Expand All @@ -193,6 +194,7 @@ class Meta:
"feature_flag",
"exposure_cohort",
"holdout",
"type",
]

def validate_parameters(self, value):
Expand Down
5 changes: 3 additions & 2 deletions frontend/src/layout/navigation-3000/sidebars/toolbar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,13 @@
path(['layout', 'navigation-3000', 'sidebars', 'toolbarSidebarLogic']),
connect(() => ({
values: [
authorizedUrlListLogic({ actionId: null, type: AuthorizedUrlListType.TOOLBAR_URLS }),
authorizedUrlListLogic({ actionId: null, experimentId: null, type: AuthorizedUrlListType.TOOLBAR_URLS }),

Check failure on line 32 in frontend/src/layout/navigation-3000/sidebars/toolbar.ts

View workflow job for this annotation

GitHub Actions / Code quality checks

Argument of type '{ actionId: null; experimentId: null; type: AuthorizedUrlListType.TOOLBAR_URLS; }' is not assignable to parameter of type 'AnyComponent | AuthorizedUrlListLogicProps'.
['urlsKeyed', 'suggestionsLoading', 'launchUrl'],
sceneLogic,
['activeScene', 'sceneParams'],
],
actions: [
authorizedUrlListLogic({ actionId: null, type: AuthorizedUrlListType.TOOLBAR_URLS }),
authorizedUrlListLogic({ actionId: null, experimentId: null, type: AuthorizedUrlListType.TOOLBAR_URLS }),

Check failure on line 38 in frontend/src/layout/navigation-3000/sidebars/toolbar.ts

View workflow job for this annotation

GitHub Actions / Code quality checks

Argument of type '{ actionId: null; experimentId: null; type: AuthorizedUrlListType.TOOLBAR_URLS; }' is not assignable to parameter of type 'AnyComponent | AuthorizedUrlListLogicProps'.
['addUrl', 'removeUrl', 'updateUrl'],
],
})),
Expand All @@ -48,10 +48,11 @@
noun: 'site',
loading: suggestionsLoading,
onAdd: async (url) => {
await authorizedUrlListLogic({

Check failure on line 51 in frontend/src/layout/navigation-3000/sidebars/toolbar.ts

View workflow job for this annotation

GitHub Actions / Code quality checks

Argument of type '{ actionId: null; experimentId: null; type: AuthorizedUrlListType.TOOLBAR_URLS; }' is not assignable to parameter of type 'AnyComponent | AuthorizedUrlListLogicProps'.
actionId: null,
experimentId: null,
type: AuthorizedUrlListType.TOOLBAR_URLS,
}).asyncActions.addUrl(url)

Check failure on line 55 in frontend/src/layout/navigation-3000/sidebars/toolbar.ts

View workflow job for this annotation

GitHub Actions / Code quality checks

Property 'asyncActions' does not exist on type 'FunctionComponent<{}> | BuiltLogic<authorizedUrlListLogicType>'.
},
validateName: (url) => {
const { currentTeam } = teamLogic.values
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,10 @@
) : null
}

function AuthorizedUrlForm({ actionId, type }: AuthorizedUrlListProps): JSX.Element {
const logic = authorizedUrlListLogic({ actionId: actionId ?? null, type })
function AuthorizedUrlForm({ actionId, experimentId, type }: AuthorizedUrlListProps): JSX.Element {
const logic = authorizedUrlListLogic({ actionId: actionId ?? null, experimentId: experimentId ?? null, type })

Check failure on line 49 in frontend/src/lib/components/AuthorizedUrlList/AuthorizedUrlList.tsx

View workflow job for this annotation

GitHub Actions / Code quality checks

Argument of type '{ actionId: number | null; experimentId: number | "new" | null; type: AuthorizedUrlListType; }' is not assignable to parameter of type 'AnyComponent | AuthorizedUrlListLogicProps'.
const { isProposedUrlSubmitting } = useValues(logic)

Check failure on line 50 in frontend/src/lib/components/AuthorizedUrlList/AuthorizedUrlList.tsx

View workflow job for this annotation

GitHub Actions / Code quality checks

Argument of type 'FunctionComponent<{}> | BuiltLogic<authorizedUrlListLogicType>' is not assignable to parameter of type 'BuiltLogic<authorizedUrlListLogicType> | LogicWrapper<authorizedUrlListLogicType>'.
const { cancelProposingUrl } = useActions(logic)

Check failure on line 51 in frontend/src/lib/components/AuthorizedUrlList/AuthorizedUrlList.tsx

View workflow job for this annotation

GitHub Actions / Code quality checks

Argument of type 'FunctionComponent<{}> | BuiltLogic<authorizedUrlListLogicType>' is not assignable to parameter of type 'BuiltLogic<authorizedUrlListLogicType> | LogicWrapper<authorizedUrlListLogicType>'.
return (
<Form
logic={authorizedUrlListLogic}
Expand Down Expand Up @@ -78,15 +78,24 @@

export interface AuthorizedUrlListProps {
actionId?: number
experimentId?: number | 'new'
query?: string
type: AuthorizedUrlListType
}

export function AuthorizedUrlList({
actionId,
experimentId,
query,
type,
addText = 'Add',
}: AuthorizedUrlListProps & { addText?: string }): JSX.Element {
const logic = authorizedUrlListLogic({ actionId: actionId ?? null, type })
const logic = authorizedUrlListLogic({
experimentId: experimentId ?? null,
actionId: actionId ?? null,
type,
query,
})
const {
urlsKeyed,
suggestionsLoading,
Expand Down Expand Up @@ -162,7 +171,7 @@
type === AuthorizedUrlListType.TOOLBAR_URLS
? launchUrl(keyedURL.url)
: // other urls are simply opened directly
keyedURL.url
keyedURL.url + query
}
targetBlank
tooltip={
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,10 @@
},
})
initKeaTests()
logic = authorizedUrlListLogic({

Check failure on line 35 in frontend/src/lib/components/AuthorizedUrlList/authorizedUrlListLogic.test.ts

View workflow job for this annotation

GitHub Actions / Code quality checks

Type 'FunctionComponent<{}> | BuiltLogic<authorizedUrlListLogicType>' is not assignable to type 'BuiltLogic<authorizedUrlListLogicType>'.

Check failure on line 35 in frontend/src/lib/components/AuthorizedUrlList/authorizedUrlListLogic.test.ts

View workflow job for this annotation

GitHub Actions / Code quality checks

Argument of type '{ type: AuthorizedUrlListType.TOOLBAR_URLS; actionId: null; experimentId: null; }' is not assignable to parameter of type 'AnyComponent | AuthorizedUrlListLogicProps'.
type: AuthorizedUrlListType.TOOLBAR_URLS,
actionId: null,
experimentId: null,
})
logic.mount()
})
Expand Down Expand Up @@ -116,9 +117,10 @@
})
describe('recording domain type', () => {
beforeEach(() => {
logic = authorizedUrlListLogic({

Check failure on line 120 in frontend/src/lib/components/AuthorizedUrlList/authorizedUrlListLogic.test.ts

View workflow job for this annotation

GitHub Actions / Code quality checks

Type 'FunctionComponent<{}> | BuiltLogic<authorizedUrlListLogicType>' is not assignable to type 'BuiltLogic<authorizedUrlListLogicType>'.
type: AuthorizedUrlListType.RECORDING_DOMAINS,
actionId: null,
experimentId: null,
})
logic.mount()
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ export interface ProposeNewUrlFormType {
export enum AuthorizedUrlListType {
TOOLBAR_URLS = 'TOOLBAR_URLS',
RECORDING_DOMAINS = 'RECORDING_DOMAINS',
WEB_EXPERIMENTS = 'WEB_EXPERIMENTS',
}

/**
Expand Down Expand Up @@ -90,19 +91,22 @@ export const validateProposedUrl = (
/** defaultIntent: whether to launch with empty intent (i.e. toolbar mode is default) */
export function appEditorUrl(
appUrl: string,
options?: { actionId?: number | null; userIntent?: ToolbarUserIntent }
options?: { actionId?: number | null; experimentId?: number | null | 'new'; userIntent?: ToolbarUserIntent }
): string {
// See https://github.com/PostHog/posthog-js/blob/f7119c/src/extensions/toolbar.ts#L52 for where these params
// are passed. `appUrl` is an extra `redirect_to_site` param.
const params: ToolbarParams & { appUrl: string } = {
userIntent: options?.userIntent ?? (options?.actionId ? 'edit-action' : 'add-action'),
userIntent:
options?.userIntent ??
(options?.actionId ? 'edit-action' : options?.experimentId ? 'edit-experiment' : 'add-action'),
// Make sure to pass the app url, otherwise the api_host will be used by
// the toolbar, which isn't correct when used behind a reverse proxy as
// we require e.g. SSO login to the app, which will not work when placed
// behind a proxy unless we register each domain with the OAuth2 client.
apiURL: apiHostOrigin(),
appUrl,
...(options?.actionId ? { actionId: options.actionId } : {}),
...(options?.experimentId ? { experimentId: options.experimentId } : {}),
}
return '/api/user/redirect_to_site/' + encodeParams(params, '?')
}
Expand Down Expand Up @@ -164,7 +168,9 @@ export interface KeyedAppUrl {

export interface AuthorizedUrlListLogicProps {
actionId: number | null
experimentId: number | null | 'new'
type: AuthorizedUrlListType
query: string | undefined
}
export const authorizedUrlListLogic = kea<authorizedUrlListLogicType>([
path((key) => ['lib', 'components', 'AuthorizedUrlList', 'authorizedUrlListLogic', key]),
Expand Down Expand Up @@ -372,11 +378,18 @@ export const authorizedUrlListLogic = kea<authorizedUrlListLogicType>([
},
],
launchUrl: [
(_, p) => [p.actionId],
(actionId) => (url: string) =>
appEditorUrl(url, {
(_, p) => [p.actionId, p.experimentId],
(actionId, experimentId) => (url: string) => {
if (experimentId) {
return appEditorUrl(url, {
experimentId,
})
}

return appEditorUrl(url, {
actionId,
}),
})
},
],
isAddUrlFormVisible: [(s) => [s.editUrlIndex], (editUrlIndex) => editUrlIndex === -1],
onlyAllowDomains: [(_, p) => [p.type], (type) => type === AuthorizedUrlListType.RECORDING_DOMAINS],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,13 @@ export const iframedToolbarBrowserLogic = kea<iframedToolbarBrowserLogicType>([

connect({
values: [
authorizedUrlListLogic({ actionId: null, type: AuthorizedUrlListType.TOOLBAR_URLS }),
authorizedUrlListLogic({ actionId: null, experimentId: null, type: AuthorizedUrlListType.TOOLBAR_URLS }),
['urlsKeyed', 'checkUrlIsAuthorized'],
teamLogic,
['currentTeam'],
],
actions: [
authorizedUrlListLogic({ actionId: null, type: AuthorizedUrlListType.TOOLBAR_URLS }),
authorizedUrlListLogic({ actionId: null, experimentId: null, type: AuthorizedUrlListType.TOOLBAR_URLS }),
['addUrl'],
teamLogic,
['updateCurrentTeamSuccess'],
Expand Down
125 changes: 125 additions & 0 deletions frontend/src/scenes/experiments/Experiment.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,128 @@ const MOCK_TREND_EXPERIMENT: Experiment = {
updated_at: '2022-03-15T21:31:00.192917Z',
}

const MOCK_WEB_EXPERIMENT_MANY_VARIANTS: Experiment = {
id: 4,
name: 'web-experiment',
type: 'web',
start_date: '2023-02-11T10:37:17.634000Z',
end_date: null,
feature_flag_key: 'web-experiment',
feature_flag: {
id: 1,
team_id: 1,
name: 'Web Experiment on Hawaii.com',
key: 'web-experiment',
active: false,
deleted: false,
ensure_experience_continuity: false,
filters: {
groups: [
{
properties: [
{
key: 'company_name',
type: PropertyFilterType.Person,
value: 'awesome',
operator: PropertyOperator.IContains,
},
],
variant: null,
rollout_percentage: undefined,
},
],
payloads: {},
multivariate: {
variants: [
{
key: 'control',
rollout_percentage: 16,
},
{
key: 'test_1',
rollout_percentage: 16,
},
{
key: 'test_2',
rollout_percentage: 16,
},
{
key: 'test_3',
rollout_percentage: 16,
},
{
key: 'test_4',
rollout_percentage: 16,
},
{
key: 'test_5',
rollout_percentage: 20,
},
],
},
},
},
parameters: {
feature_flag_variants: [
{
key: 'control',
rollout_percentage: 16,
},
{
key: 'test_1',
rollout_percentage: 16,
},
{
key: 'test_2',
rollout_percentage: 16,
},
{
key: 'test_3',
rollout_percentage: 16,
},
{
key: 'test_4',
rollout_percentage: 16,
},
{
key: 'test_5',
rollout_percentage: 20,
},
],
recommended_sample_size: 0,
recommended_running_time: 28.3,
},
secondary_metrics: [],
filters: {
events: [
{
id: '$pageview',
math: 'avg_count_per_actor',
name: '$pageview',
type: 'events',
order: 0,
},
],
actions: [],
date_to: '2023-05-19T23:59',
insight: InsightType.TRENDS,
interval: 'day',
date_from: '2023-05-05T11:36',
filter_test_accounts: false,
},
archived: false,
created_by: {
id: 1,
uuid: '01881f35-b41a-0000-1d94-331938392cac',
distinct_id: 'Xr1OY26ZsDh9ZbvA212ggq4l0Hf0dmEUjT33zvRPKrX',
first_name: 'SS',
email: '[email protected]',
is_email_verified: false,
},
created_at: '2022-03-15T21:31:00.192917Z',
updated_at: '2022-03-15T21:31:00.192917Z',
}

const MOCK_TREND_EXPERIMENT_MANY_VARIANTS: Experiment = {
id: 3,
name: 'aloha',
Expand Down Expand Up @@ -1236,13 +1358,16 @@ const meta: Meta = {
MOCK_FUNNEL_EXPERIMENT,
MOCK_TREND_EXPERIMENT,
MOCK_TREND_EXPERIMENT_MANY_VARIANTS,
MOCK_WEB_EXPERIMENT_MANY_VARIANTS,
]),
'/api/projects/:team_id/experiments/1/': MOCK_FUNNEL_EXPERIMENT,
'/api/projects/:team_id/experiments/1/results/': MOCK_EXPERIMENT_RESULTS,
'/api/projects/:team_id/experiments/2/': MOCK_TREND_EXPERIMENT,
'/api/projects/:team_id/experiments/2/results/': MOCK_TREND_EXPERIMENT_RESULTS,
'/api/projects/:team_id/experiments/3/': MOCK_TREND_EXPERIMENT_MANY_VARIANTS,
'/api/projects/:team_id/experiments/3/results/': MOCK_TREND_EXPERIMENT_MANY_VARIANTS_RESULTS,
'/api/projects/:team_id/experiments/4/': MOCK_WEB_EXPERIMENT_MANY_VARIANTS,
'/api/projects/:team_id/experiments/4/results/': MOCK_TREND_EXPERIMENT_MANY_VARIANTS_RESULTS,
},
}),
],
Expand Down
Loading
Loading