@elementor/editor-audits 4.4.0-beta1 → 4.4.0-beta2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +261 -59
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +247 -45
- package/dist/index.mjs.map +1 -1
- package/package.json +16 -15
- package/src/audits/accessibility-statement.ts +1 -0
- package/src/audits/accessibility-widget.ts +1 -0
- package/src/audits/cookie-policy.ts +1 -0
- package/src/audits/google-consent-mode.ts +1 -0
- package/src/audits/images-alt-text.ts +1 -0
- package/src/audits/images-inefficient-format.ts +1 -0
- package/src/audits/images-too-large.ts +1 -0
- package/src/audits/scan-for-cookies.ts +1 -0
- package/src/components/audit-panel.tsx +6 -1
- package/src/components/fix-violation-with-angie.tsx +10 -1
- package/src/components/promotions.tsx +9 -2
- package/src/components/violation-cta-button.tsx +9 -1
- package/src/components/violation-row.tsx +28 -4
- package/src/constants.ts +13 -0
- package/src/hooks/focus-violation.ts +9 -5
- package/src/hooks/use-audit-report.ts +6 -1
- package/src/hooks/use-track-panel-viewed.ts +31 -0
- package/src/types.ts +1 -0
- package/src/utils/build-run-summary.ts +45 -0
- package/src/utils/tracking-names.ts +12 -0
- package/src/utils/tracking.ts +130 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@elementor/editor-audits",
|
|
3
|
-
"version": "4.4.0-
|
|
3
|
+
"version": "4.4.0-beta2",
|
|
4
4
|
"private": false,
|
|
5
5
|
"author": "Elementor Team",
|
|
6
6
|
"homepage": "https://elementor.com/",
|
|
@@ -39,21 +39,22 @@
|
|
|
39
39
|
"dev": "tsup --config=../../tsup.dev.ts"
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
|
-
"@elementor/editor": "4.4.0-
|
|
43
|
-
"@elementor/editor-mcp": "4.4.0-
|
|
44
|
-
"@elementor/editor-app-bar": "4.4.0-
|
|
45
|
-
"@elementor/editor-
|
|
46
|
-
"@elementor/editor-
|
|
47
|
-
"@elementor/editor-
|
|
48
|
-
"@elementor/editor-
|
|
49
|
-
"@elementor/editor-
|
|
50
|
-
"@elementor/editor-
|
|
51
|
-
"@elementor/
|
|
52
|
-
"@elementor/
|
|
42
|
+
"@elementor/editor": "4.4.0-beta2",
|
|
43
|
+
"@elementor/editor-mcp": "4.4.0-beta2",
|
|
44
|
+
"@elementor/editor-app-bar": "4.4.0-beta2",
|
|
45
|
+
"@elementor/editor-current-user": "4.4.0-beta2",
|
|
46
|
+
"@elementor/editor-elements": "4.4.0-beta2",
|
|
47
|
+
"@elementor/editor-floating-panels": "4.4.0-beta2",
|
|
48
|
+
"@elementor/editor-notifications": "4.4.0-beta2",
|
|
49
|
+
"@elementor/editor-props": "4.4.0-beta2",
|
|
50
|
+
"@elementor/editor-styles": "4.4.0-beta2",
|
|
51
|
+
"@elementor/editor-v1-adapters": "4.4.0-beta2",
|
|
52
|
+
"@elementor/events": "4.4.0-beta2",
|
|
53
|
+
"@elementor/http-client": "4.4.0-beta2",
|
|
53
54
|
"@elementor/icons": "~1.78.1",
|
|
54
|
-
"@elementor/locations": "4.4.0-
|
|
55
|
-
"@elementor/session": "4.4.0-
|
|
56
|
-
"@elementor/store": "4.4.0-
|
|
55
|
+
"@elementor/locations": "4.4.0-beta2",
|
|
56
|
+
"@elementor/session": "4.4.0-beta2",
|
|
57
|
+
"@elementor/store": "4.4.0-beta2",
|
|
57
58
|
"@elementor/ui": "1.41.4"
|
|
58
59
|
},
|
|
59
60
|
"peerDependencies": {
|
|
@@ -34,6 +34,7 @@ export const audit: Audit = {
|
|
|
34
34
|
label: __( 'No accessibility widget is active on this site.', 'elementor' ),
|
|
35
35
|
externalUrl: isReady ? ctx.pageContext.ally_widget_settings_url : ctx.pageContext.ally_plugin_url,
|
|
36
36
|
ctaLabel: __( 'Activate', 'elementor' ),
|
|
37
|
+
appInstalled: isReady,
|
|
37
38
|
},
|
|
38
39
|
],
|
|
39
40
|
};
|
|
@@ -26,6 +26,7 @@ export const audit: Audit = {
|
|
|
26
26
|
label: __( 'Cookiez plugin is not installed or active.', 'elementor' ),
|
|
27
27
|
externalUrl: ctx.pageContext.cookiez_plugin_url,
|
|
28
28
|
ctaLabel: __( 'Set up cookie consent', 'elementor' ),
|
|
29
|
+
appInstalled: ctx.pageContext.cookiez_plugin_active,
|
|
29
30
|
},
|
|
30
31
|
],
|
|
31
32
|
};
|
|
@@ -38,6 +38,7 @@ export const audit: Audit = {
|
|
|
38
38
|
label: __( 'Image is missing alt text.', 'elementor' ),
|
|
39
39
|
externalUrl: isReady ? ctx.pageContext.ally_scan_url : ctx.pageContext.ally_plugin_url,
|
|
40
40
|
ctaLabel: __( 'Fix with Ally', 'elementor' ),
|
|
41
|
+
appInstalled: isReady,
|
|
41
42
|
} );
|
|
42
43
|
}
|
|
43
44
|
} );
|
|
@@ -23,6 +23,7 @@ export const audit: Audit = {
|
|
|
23
23
|
auditId: audit.id,
|
|
24
24
|
label: __( 'This page has not been scanned for cookies yet.', 'elementor' ),
|
|
25
25
|
externalUrl: isReady ? ctx.pageContext.cookiez_scan_url : ctx.pageContext.cookiez_plugin_action_url,
|
|
26
|
+
appInstalled: isReady,
|
|
26
27
|
},
|
|
27
28
|
],
|
|
28
29
|
};
|
|
@@ -6,6 +6,8 @@ import { __ } from '@wordpress/i18n';
|
|
|
6
6
|
|
|
7
7
|
import { AUDIT_PANEL_ID } from '../constants';
|
|
8
8
|
import { useAuditReport } from '../hooks/use-audit-report';
|
|
9
|
+
import { useTrackPanelViewed } from '../hooks/use-track-panel-viewed';
|
|
10
|
+
import { trackPanelClosed } from '../utils/tracking';
|
|
9
11
|
import AuditFeedback from './audit-feedback';
|
|
10
12
|
import ErrorPage from './pages/error-page';
|
|
11
13
|
import LoadingPage from './pages/loading-page';
|
|
@@ -16,8 +18,10 @@ import RescanButton from './rescan-button';
|
|
|
16
18
|
export default function AuditPanel() {
|
|
17
19
|
const { status, report, error, isStale, run } = useAuditReport();
|
|
18
20
|
|
|
21
|
+
useTrackPanelViewed();
|
|
22
|
+
|
|
19
23
|
const hostDocumentId = getHostDocumentId() ?? 0;
|
|
20
|
-
const onRun = () => run( hostDocumentId );
|
|
24
|
+
const onRun = () => run( hostDocumentId, report ? 'rescan' : 'run_audit' );
|
|
21
25
|
const lastScanLabel = report ? new Date( report.runAt ).toLocaleTimeString() : null;
|
|
22
26
|
|
|
23
27
|
return (
|
|
@@ -27,6 +31,7 @@ export default function AuditPanel() {
|
|
|
27
31
|
title={ __( 'Page Audit', 'elementor' ) }
|
|
28
32
|
badge={ __( 'Beta', 'elementor' ) }
|
|
29
33
|
titleVariant="subtitle2"
|
|
34
|
+
onClose={ trackPanelClosed }
|
|
30
35
|
/>
|
|
31
36
|
<FloatingPanelBody
|
|
32
37
|
sx={
|
|
@@ -5,14 +5,17 @@ import { Button, IconButton, Tooltip } from '@elementor/ui';
|
|
|
5
5
|
import { __ } from '@wordpress/i18n';
|
|
6
6
|
|
|
7
7
|
import { ANGIE_FIX_ENTRY_POINT, CREATE_WIDGET_EVENT } from '../constants';
|
|
8
|
+
import { type AuditMeta } from '../types';
|
|
9
|
+
import { trackFindingClicked } from '../utils/tracking';
|
|
8
10
|
|
|
9
11
|
type Props = {
|
|
12
|
+
audit: AuditMeta;
|
|
10
13
|
prompt: string;
|
|
11
14
|
panelZIndex: number;
|
|
12
15
|
variant?: 'icon' | 'button';
|
|
13
16
|
};
|
|
14
17
|
|
|
15
|
-
export default function FixViolationWithAngie( { prompt, panelZIndex, variant = 'icon' }: Props ) {
|
|
18
|
+
export default function FixViolationWithAngie( { audit, prompt, panelZIndex, variant = 'icon' }: Props ) {
|
|
16
19
|
const label = __( 'Fix with Angie', 'elementor' );
|
|
17
20
|
const href = `#angie-prompt=${ encodeURIComponent( prompt ) }`;
|
|
18
21
|
|
|
@@ -22,6 +25,12 @@ export default function FixViolationWithAngie( { prompt, panelZIndex, variant =
|
|
|
22
25
|
|
|
23
26
|
if ( isAngieAvailable() ) {
|
|
24
27
|
sendPromptToAngie( prompt );
|
|
28
|
+
trackFindingClicked( {
|
|
29
|
+
audit,
|
|
30
|
+
locationL2: 'issues',
|
|
31
|
+
actionType: 'fix_with_angie',
|
|
32
|
+
interactionResult: 'angie_prompt_sent',
|
|
33
|
+
} );
|
|
25
34
|
return;
|
|
26
35
|
}
|
|
27
36
|
|
|
@@ -5,6 +5,7 @@ import { __ } from '@wordpress/i18n';
|
|
|
5
5
|
import { PROMOTIONS } from '../register-promotions';
|
|
6
6
|
import { type PageAuditReport } from '../types';
|
|
7
7
|
import { findAuditRun } from '../utils/find-audit-run';
|
|
8
|
+
import { trackFixWithApp } from '../utils/tracking';
|
|
8
9
|
import PromotionCard from './promotion-card';
|
|
9
10
|
|
|
10
11
|
type Props = {
|
|
@@ -58,8 +59,14 @@ export default function Promotions( { report }: Props ) {
|
|
|
58
59
|
ctaLabel={ config.ctaLabel }
|
|
59
60
|
ctaDisabled={ ! ctaUrl }
|
|
60
61
|
onCtaClick={ () => {
|
|
61
|
-
if ( ctaUrl ) {
|
|
62
|
-
|
|
62
|
+
if ( ! ctaUrl ) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
window.open( ctaUrl, '_blank', 'noopener' );
|
|
67
|
+
|
|
68
|
+
if ( run.result.status === 'fail' ) {
|
|
69
|
+
trackFixWithApp( run.audit, run.result.violations[ 0 ], 'overview' );
|
|
63
70
|
}
|
|
64
71
|
} }
|
|
65
72
|
/>
|
|
@@ -7,14 +7,22 @@ type Props = {
|
|
|
7
7
|
externalUrl: string;
|
|
8
8
|
variant?: 'outlined' | 'text';
|
|
9
9
|
withIcon?: boolean;
|
|
10
|
+
onOpen?: () => void;
|
|
10
11
|
};
|
|
11
12
|
|
|
12
|
-
export default function ViolationCtaButton( {
|
|
13
|
+
export default function ViolationCtaButton( {
|
|
14
|
+
ctaLabel,
|
|
15
|
+
externalUrl,
|
|
16
|
+
variant = 'outlined',
|
|
17
|
+
withIcon = false,
|
|
18
|
+
onOpen,
|
|
19
|
+
}: Props ) {
|
|
13
20
|
const handleClick = ( event: React.MouseEvent< HTMLButtonElement > ) => {
|
|
14
21
|
event.stopPropagation();
|
|
15
22
|
event.preventDefault();
|
|
16
23
|
|
|
17
24
|
window.open( externalUrl, '_blank', 'noopener' );
|
|
25
|
+
onOpen?.();
|
|
18
26
|
};
|
|
19
27
|
|
|
20
28
|
return (
|
|
@@ -11,6 +11,7 @@ import { type AuditMeta, type AuditViolation } from '../types';
|
|
|
11
11
|
import { buildAngiePrompt } from '../utils/build-angie-prompt';
|
|
12
12
|
import { isScoredAudit } from '../utils/is-scored-audit';
|
|
13
13
|
import { onKeyboardClick } from '../utils/keyboard-click';
|
|
14
|
+
import { trackFindingClicked, trackFixWithApp } from '../utils/tracking';
|
|
14
15
|
import FixViolationWithAngie from './fix-violation-with-angie';
|
|
15
16
|
import SeverityIcon from './severity-icons';
|
|
16
17
|
import ViolationCtaButton from './violation-cta-button';
|
|
@@ -52,10 +53,12 @@ function StatusIndicator( { audit, violations }: Pick< Props, 'audit' | 'violati
|
|
|
52
53
|
}
|
|
53
54
|
|
|
54
55
|
function GuidanceAction( {
|
|
56
|
+
audit,
|
|
55
57
|
violation,
|
|
56
58
|
sharedAngiePrompt,
|
|
57
59
|
panelZIndex,
|
|
58
60
|
}: {
|
|
61
|
+
audit: AuditMeta;
|
|
59
62
|
violation: AuditViolation;
|
|
60
63
|
sharedAngiePrompt?: string;
|
|
61
64
|
panelZIndex: number;
|
|
@@ -73,6 +76,7 @@ function GuidanceAction( {
|
|
|
73
76
|
ctaLabel={ violation.ctaLabel as string }
|
|
74
77
|
externalUrl={ violation.externalUrl as string }
|
|
75
78
|
withIcon
|
|
79
|
+
onOpen={ () => trackFixWithApp( audit, violation, 'issues' ) }
|
|
76
80
|
/>
|
|
77
81
|
) }
|
|
78
82
|
{ hasPrimaryCta && violation.secondaryCtaLabel && violation.secondaryCtaUrl && (
|
|
@@ -83,7 +87,12 @@ function GuidanceAction( {
|
|
|
83
87
|
/>
|
|
84
88
|
) }
|
|
85
89
|
{ sharedAngiePrompt && (
|
|
86
|
-
<FixViolationWithAngie
|
|
90
|
+
<FixViolationWithAngie
|
|
91
|
+
audit={ audit }
|
|
92
|
+
prompt={ sharedAngiePrompt }
|
|
93
|
+
panelZIndex={ panelZIndex }
|
|
94
|
+
variant="button"
|
|
95
|
+
/>
|
|
87
96
|
) }
|
|
88
97
|
</Box>
|
|
89
98
|
);
|
|
@@ -113,9 +122,22 @@ export default function ViolationRow( { audit, expanded, onToggleExpand, skipRea
|
|
|
113
122
|
const panelZIndex = useFloatingPanelZIndex( AUDIT_PANEL_ID );
|
|
114
123
|
const sharedAngiePrompt = getSharedAngiePrompt( violations );
|
|
115
124
|
|
|
125
|
+
const viewIssue = ( violation: AuditViolation ) => {
|
|
126
|
+
const interactionResult = focusViolation( violation );
|
|
127
|
+
|
|
128
|
+
if ( interactionResult ) {
|
|
129
|
+
trackFindingClicked( {
|
|
130
|
+
audit,
|
|
131
|
+
locationL2: 'issues',
|
|
132
|
+
actionType: 'view_issue',
|
|
133
|
+
interactionResult,
|
|
134
|
+
} );
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
|
|
116
138
|
const handleGuidanceClick = () => {
|
|
117
139
|
if ( isGuidanceFocusable && primaryViolation ) {
|
|
118
|
-
|
|
140
|
+
viewIssue( primaryViolation );
|
|
119
141
|
}
|
|
120
142
|
};
|
|
121
143
|
|
|
@@ -170,6 +192,7 @@ export default function ViolationRow( { audit, expanded, onToggleExpand, skipRea
|
|
|
170
192
|
</Typography>
|
|
171
193
|
{ primaryViolation && (
|
|
172
194
|
<GuidanceAction
|
|
195
|
+
audit={ audit }
|
|
173
196
|
violation={ primaryViolation }
|
|
174
197
|
sharedAngiePrompt={ sharedAngiePrompt }
|
|
175
198
|
panelZIndex={ panelZIndex }
|
|
@@ -194,8 +217,8 @@ export default function ViolationRow( { audit, expanded, onToggleExpand, skipRea
|
|
|
194
217
|
key={ idx }
|
|
195
218
|
role="button"
|
|
196
219
|
tabIndex={ 0 }
|
|
197
|
-
onClick={ () =>
|
|
198
|
-
onKeyDown={ onKeyboardClick( () =>
|
|
220
|
+
onClick={ () => viewIssue( violation ) }
|
|
221
|
+
onKeyDown={ onKeyboardClick( () => viewIssue( violation ) ) }
|
|
199
222
|
sx={ {
|
|
200
223
|
display: 'flex',
|
|
201
224
|
alignItems: 'center',
|
|
@@ -226,6 +249,7 @@ export default function ViolationRow( { audit, expanded, onToggleExpand, skipRea
|
|
|
226
249
|
sx={ { opacity: 0, transition: 'opacity .15s' } }
|
|
227
250
|
>
|
|
228
251
|
<FixViolationWithAngie
|
|
252
|
+
audit={ audit }
|
|
229
253
|
prompt={
|
|
230
254
|
violation.angiePrompt ?? buildAngiePrompt( angiePromptContext )
|
|
231
255
|
}
|
package/src/constants.ts
CHANGED
|
@@ -18,6 +18,19 @@ export const FEEDBACK_SENT_EVENT = 'audit_feedback_sent';
|
|
|
18
18
|
export const FEEDBACK_CANCELLED_EVENT = 'audit_feedback_cancelled';
|
|
19
19
|
export const FEEDBACK_CLOSED_EVENT = 'audit_feedback_closed';
|
|
20
20
|
|
|
21
|
+
export const PANEL_VIEWED_EVENT = 'page_audit_panel_viewed';
|
|
22
|
+
export const PANEL_CLOSED_EVENT = 'page_audit_panel_closed';
|
|
23
|
+
export const RUN_STARTED_EVENT = 'page_audit_run_started';
|
|
24
|
+
export const RUN_COMPLETED_EVENT = 'page_audit_run_completed';
|
|
25
|
+
export const FINDING_CLICKED_EVENT = 'page_audit_finding_clicked';
|
|
26
|
+
|
|
27
|
+
export const TRACKING_WINDOW_NAME = 'page_audit';
|
|
28
|
+
export const TRACKING_TARGET_LOCATION = 'panel';
|
|
29
|
+
export const TRACKING_LOCATION_L1 = 'audit_panel';
|
|
30
|
+
|
|
31
|
+
export const FIRST_OPEN_MESSAGE_KEY = 'page-audit-panel-opened';
|
|
32
|
+
export const AUDIT_ID_PREFIX = 'audits/';
|
|
33
|
+
|
|
21
34
|
export const ALL_CATEGORIES: AuditCategory[] = [
|
|
22
35
|
'best-practices',
|
|
23
36
|
'seo',
|
|
@@ -2,10 +2,11 @@ import { getContainer, selectElement } from '@elementor/editor-elements';
|
|
|
2
2
|
import { __privateOpenRoute as openRoute, __privateRunCommand as runCommand } from '@elementor/editor-v1-adapters';
|
|
3
3
|
|
|
4
4
|
import { type AuditViolation } from '../types';
|
|
5
|
+
import { type ViewIssueResult } from '../utils/tracking';
|
|
5
6
|
|
|
6
7
|
const SCROLL_TO_VIEW_DELAY_MS = 200;
|
|
7
8
|
|
|
8
|
-
export function focusViolation( violation: AuditViolation ):
|
|
9
|
+
export function focusViolation( violation: AuditViolation ): ViewIssueResult | null {
|
|
9
10
|
if ( violation.elementId ) {
|
|
10
11
|
const container = getContainer( violation.elementId );
|
|
11
12
|
|
|
@@ -27,27 +28,30 @@ export function focusViolation( violation: AuditViolation ): void {
|
|
|
27
28
|
runCommand( 'panel/editor/open' );
|
|
28
29
|
}
|
|
29
30
|
|
|
30
|
-
return;
|
|
31
|
+
return container ? 'element_focused' : null;
|
|
31
32
|
}
|
|
32
33
|
|
|
33
34
|
if ( violation.externalUrl ) {
|
|
34
35
|
window.open( violation.externalUrl, '_blank' );
|
|
35
|
-
return;
|
|
36
|
+
return 'external_url_opened';
|
|
36
37
|
}
|
|
37
38
|
|
|
38
39
|
if ( violation.targetHint === 'page-settings' ) {
|
|
39
40
|
openRoute( 'panel/page-settings/settings' );
|
|
40
|
-
return;
|
|
41
|
+
return 'page_settings_opened';
|
|
41
42
|
}
|
|
42
43
|
|
|
43
44
|
if ( violation.targetHint === 'site-settings' ) {
|
|
44
45
|
runCommand( 'panel/global/open' );
|
|
45
|
-
return;
|
|
46
|
+
return 'site_settings_opened';
|
|
46
47
|
}
|
|
47
48
|
|
|
48
49
|
if ( violation.targetHint === 'site-identity-settings' ) {
|
|
49
50
|
void runCommand( 'panel/global/open' ).then( () => {
|
|
50
51
|
openRoute( 'panel/global/settings-site-identity' );
|
|
51
52
|
} );
|
|
53
|
+
return 'site_settings_opened';
|
|
52
54
|
}
|
|
55
|
+
|
|
56
|
+
return null;
|
|
53
57
|
}
|
|
@@ -6,6 +6,7 @@ import { runPageAudit } from '../runner';
|
|
|
6
6
|
import { type GlobalState, selectError, selectIsStale, selectReport, selectStatus, slice } from '../store';
|
|
7
7
|
import { getPersistedReport, persistReport } from '../utils/report-storage';
|
|
8
8
|
import { SessionExpiredError } from '../utils/session-expiration';
|
|
9
|
+
import { type RunTrigger, trackRunCompleted, trackRunFailed, trackRunStarted } from '../utils/tracking';
|
|
9
10
|
|
|
10
11
|
export function useAuditReport() {
|
|
11
12
|
const status = useSelector( ( state: GlobalState ) => selectStatus( state ) );
|
|
@@ -29,20 +30,24 @@ export function useAuditReport() {
|
|
|
29
30
|
}
|
|
30
31
|
}, [ documentId, report, dispatch ] );
|
|
31
32
|
|
|
32
|
-
const run = async ( documentIdToRun: number ) => {
|
|
33
|
+
const run = async ( documentIdToRun: number, trigger: RunTrigger ) => {
|
|
33
34
|
dispatch( slice.actions.runStarted() );
|
|
35
|
+
trackRunStarted( trigger );
|
|
34
36
|
|
|
35
37
|
try {
|
|
36
38
|
const nextReport = await runPageAudit( documentIdToRun );
|
|
37
39
|
dispatch( slice.actions.runSucceeded( nextReport ) );
|
|
38
40
|
persistReport( documentIdToRun, nextReport );
|
|
41
|
+
trackRunCompleted( nextReport );
|
|
39
42
|
} catch ( e ) {
|
|
40
43
|
if ( e instanceof SessionExpiredError ) {
|
|
41
44
|
dispatch( slice.actions.runAborted() );
|
|
45
|
+
trackRunFailed();
|
|
42
46
|
return;
|
|
43
47
|
}
|
|
44
48
|
|
|
45
49
|
dispatch( slice.actions.runFailed( e instanceof Error ? e.message : 'Unknown error' ) );
|
|
50
|
+
trackRunFailed();
|
|
46
51
|
}
|
|
47
52
|
};
|
|
48
53
|
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { useEffect, useRef, useState } from 'react';
|
|
2
|
+
import { useCurrentUser, useSuppressedMessage } from '@elementor/editor-current-user';
|
|
3
|
+
import { getHostDocumentId } from '@elementor/editor-elements';
|
|
4
|
+
import { __useSelector as useSelector } from '@elementor/store';
|
|
5
|
+
|
|
6
|
+
import { FIRST_OPEN_MESSAGE_KEY } from '../constants';
|
|
7
|
+
import { type GlobalState, selectReport } from '../store';
|
|
8
|
+
import { getPersistedReport } from '../utils/report-storage';
|
|
9
|
+
import { trackPanelViewed } from '../utils/tracking';
|
|
10
|
+
|
|
11
|
+
export function useTrackPanelViewed() {
|
|
12
|
+
const { data: currentUser } = useCurrentUser();
|
|
13
|
+
const [ isFirstOpenMessageSuppressed, suppressFirstOpenMessage ] = useSuppressedMessage( FIRST_OPEN_MESSAGE_KEY );
|
|
14
|
+
const report = useSelector( ( state: GlobalState ) => selectReport( state ) );
|
|
15
|
+
const [ hasPriorReport ] = useState( () => {
|
|
16
|
+
const documentId = getHostDocumentId() ?? 0;
|
|
17
|
+
|
|
18
|
+
return Boolean( report ) || Boolean( documentId && getPersistedReport( documentId ) );
|
|
19
|
+
} );
|
|
20
|
+
const hasTracked = useRef( false );
|
|
21
|
+
|
|
22
|
+
useEffect( () => {
|
|
23
|
+
if ( ! currentUser || hasTracked.current ) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
hasTracked.current = true;
|
|
28
|
+
trackPanelViewed( { hasPriorReport, isFirstOpen: ! isFirstOpenMessageSuppressed } );
|
|
29
|
+
suppressFirstOpenMessage();
|
|
30
|
+
}, [ currentUser, hasPriorReport, isFirstOpenMessageSuppressed, suppressFirstOpenMessage ] );
|
|
31
|
+
}
|
package/src/types.ts
CHANGED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { type PageAuditReport } from '../types';
|
|
2
|
+
import { isScoredAudit } from './is-scored-audit';
|
|
3
|
+
import { toCheckName, toTrackingSeverity, type TrackingSeverity } from './tracking-names';
|
|
4
|
+
|
|
5
|
+
type RequiredFix = {
|
|
6
|
+
check: string;
|
|
7
|
+
severity: TrackingSeverity;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export type RunSummary = {
|
|
11
|
+
state: TrackingSeverity | 'success';
|
|
12
|
+
metadata: {
|
|
13
|
+
score: number;
|
|
14
|
+
fix_count: number;
|
|
15
|
+
error_count: number;
|
|
16
|
+
warning_count: number;
|
|
17
|
+
suggestion_count: number;
|
|
18
|
+
required_fixes: RequiredFix[];
|
|
19
|
+
};
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const SEVERITY_PRIORITY: TrackingSeverity[] = [ 'error', 'warning', 'suggestion' ];
|
|
23
|
+
|
|
24
|
+
export function buildRunSummary( report: PageAuditReport ): RunSummary {
|
|
25
|
+
const requiredFixes: RequiredFix[] = report.auditResults
|
|
26
|
+
.filter( ( { audit, result } ) => result.status === 'fail' && isScoredAudit( audit ) )
|
|
27
|
+
.map( ( { audit } ) => ( { check: toCheckName( audit.id ), severity: toTrackingSeverity( audit.severity ) } ) );
|
|
28
|
+
|
|
29
|
+
const countBySeverity = ( severity: TrackingSeverity ) =>
|
|
30
|
+
requiredFixes.filter( ( fix ) => fix.severity === severity ).length;
|
|
31
|
+
|
|
32
|
+
const highestSeverity = SEVERITY_PRIORITY.find( ( severity ) => countBySeverity( severity ) > 0 );
|
|
33
|
+
|
|
34
|
+
return {
|
|
35
|
+
state: highestSeverity ?? 'success',
|
|
36
|
+
metadata: {
|
|
37
|
+
score: report.overall,
|
|
38
|
+
fix_count: requiredFixes.length,
|
|
39
|
+
error_count: countBySeverity( 'error' ),
|
|
40
|
+
warning_count: countBySeverity( 'warning' ),
|
|
41
|
+
suggestion_count: countBySeverity( 'suggestion' ),
|
|
42
|
+
required_fixes: requiredFixes,
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { AUDIT_ID_PREFIX } from '../constants';
|
|
2
|
+
import { type AuditSeverity } from '../types';
|
|
3
|
+
|
|
4
|
+
export type TrackingSeverity = 'error' | 'warning' | 'suggestion';
|
|
5
|
+
|
|
6
|
+
export function toTrackingSeverity( severity: AuditSeverity ): TrackingSeverity {
|
|
7
|
+
return severity === 'info' ? 'suggestion' : severity;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function toCheckName( auditId: string ): string {
|
|
11
|
+
return auditId.replace( AUDIT_ID_PREFIX, '' ).replace( /-/g, '_' );
|
|
12
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { getMixpanel } from '@elementor/events';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
FINDING_CLICKED_EVENT,
|
|
5
|
+
PANEL_CLOSED_EVENT,
|
|
6
|
+
PANEL_VIEWED_EVENT,
|
|
7
|
+
RUN_COMPLETED_EVENT,
|
|
8
|
+
RUN_STARTED_EVENT,
|
|
9
|
+
TRACKING_LOCATION_L1,
|
|
10
|
+
TRACKING_TARGET_LOCATION,
|
|
11
|
+
TRACKING_WINDOW_NAME,
|
|
12
|
+
} from '../constants';
|
|
13
|
+
import { type AuditMeta, type AuditViolation, type PageAuditReport } from '../types';
|
|
14
|
+
import { buildRunSummary } from './build-run-summary';
|
|
15
|
+
import { toCheckName, toTrackingSeverity } from './tracking-names';
|
|
16
|
+
|
|
17
|
+
export type RunTrigger = 'run_audit' | 'rescan';
|
|
18
|
+
|
|
19
|
+
export type ViewIssueResult =
|
|
20
|
+
| 'element_focused'
|
|
21
|
+
| 'page_settings_opened'
|
|
22
|
+
| 'site_settings_opened'
|
|
23
|
+
| 'external_url_opened';
|
|
24
|
+
|
|
25
|
+
export type FindingLocation = 'overview' | 'issues';
|
|
26
|
+
|
|
27
|
+
type FindingAction =
|
|
28
|
+
| { actionType: 'view_issue'; interactionResult: ViewIssueResult }
|
|
29
|
+
| { actionType: 'fix_with_angie'; interactionResult: 'angie_prompt_sent' }
|
|
30
|
+
| { actionType: 'fix_with_app'; interactionResult: 'app_opened'; appInstalled?: boolean };
|
|
31
|
+
|
|
32
|
+
type FindingClick = FindingAction & {
|
|
33
|
+
audit: AuditMeta;
|
|
34
|
+
locationL2: FindingLocation;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const dispatch = ( eventName: string, payload: Record< string, unknown > ) => {
|
|
38
|
+
getMixpanel().dispatchEvent?.( eventName, {
|
|
39
|
+
window_name: TRACKING_WINDOW_NAME,
|
|
40
|
+
target_location: TRACKING_TARGET_LOCATION,
|
|
41
|
+
location_l1: TRACKING_LOCATION_L1,
|
|
42
|
+
...payload,
|
|
43
|
+
} );
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export const trackPanelViewed = ( {
|
|
47
|
+
hasPriorReport,
|
|
48
|
+
isFirstOpen,
|
|
49
|
+
}: {
|
|
50
|
+
hasPriorReport: boolean;
|
|
51
|
+
isFirstOpen: boolean;
|
|
52
|
+
} ) => {
|
|
53
|
+
dispatch( PANEL_VIEWED_EVENT, {
|
|
54
|
+
interaction_type: 'view',
|
|
55
|
+
target_type: 'panel',
|
|
56
|
+
target_name: 'page_audit_panel',
|
|
57
|
+
interaction_result: 'panel_viewed',
|
|
58
|
+
metadata: { has_prior_report: hasPriorReport, is_first_open: isFirstOpen },
|
|
59
|
+
} );
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
export const trackPanelClosed = () => {
|
|
63
|
+
dispatch( PANEL_CLOSED_EVENT, {
|
|
64
|
+
interaction_type: 'click',
|
|
65
|
+
target_type: 'button',
|
|
66
|
+
target_name: 'close',
|
|
67
|
+
interaction_result: 'panel_closed',
|
|
68
|
+
} );
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
export const trackRunStarted = ( trigger: RunTrigger ) => {
|
|
72
|
+
dispatch( RUN_STARTED_EVENT, {
|
|
73
|
+
interaction_type: 'click',
|
|
74
|
+
target_type: 'button',
|
|
75
|
+
target_name: trigger,
|
|
76
|
+
interaction_result: 'run_initiated',
|
|
77
|
+
} );
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
const trackRunOutcome = ( interactionResult: 'run_completed' | 'run_failed', details: Record< string, unknown > ) => {
|
|
81
|
+
dispatch( RUN_COMPLETED_EVENT, {
|
|
82
|
+
interaction_type: 'view',
|
|
83
|
+
target_type: 'report',
|
|
84
|
+
target_name: 'audit_report',
|
|
85
|
+
interaction_result: interactionResult,
|
|
86
|
+
...details,
|
|
87
|
+
} );
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
export const trackRunCompleted = ( report: PageAuditReport ) => {
|
|
91
|
+
const { state, metadata } = buildRunSummary( report );
|
|
92
|
+
|
|
93
|
+
trackRunOutcome( 'run_completed', { state, metadata } );
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
export const trackRunFailed = () => {
|
|
97
|
+
trackRunOutcome( 'run_failed', {} );
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
export const trackFixWithApp = ( audit: AuditMeta, violation: AuditViolation, locationL2: FindingLocation ) => {
|
|
101
|
+
if ( violation.appInstalled === undefined ) {
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
trackFindingClicked( {
|
|
106
|
+
audit,
|
|
107
|
+
locationL2,
|
|
108
|
+
actionType: 'fix_with_app',
|
|
109
|
+
interactionResult: 'app_opened',
|
|
110
|
+
appInstalled: violation.appInstalled,
|
|
111
|
+
} );
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
export const trackFindingClicked = ( click: FindingClick ) => {
|
|
115
|
+
const { audit, locationL2, actionType, interactionResult } = click;
|
|
116
|
+
|
|
117
|
+
dispatch( FINDING_CLICKED_EVENT, {
|
|
118
|
+
interaction_type: 'click',
|
|
119
|
+
target_type: 'row',
|
|
120
|
+
target_name: 'finding_row',
|
|
121
|
+
action_type: actionType,
|
|
122
|
+
target_value: toCheckName( audit.id ),
|
|
123
|
+
interaction_result: interactionResult,
|
|
124
|
+
state: toTrackingSeverity( audit.severity ),
|
|
125
|
+
location_l2: locationL2,
|
|
126
|
+
...( click.actionType === 'fix_with_app' && {
|
|
127
|
+
app_status: click.appInstalled ? 'installed' : 'not_installed',
|
|
128
|
+
} ),
|
|
129
|
+
} );
|
|
130
|
+
};
|