@elementor/editor-audits 4.4.0-1112 → 4.4.0-1113
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.js +218 -168
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +196 -146
- package/dist/index.mjs.map +1 -1
- package/package.json +15 -15
- package/src/audits/accessibility-statement.ts +2 -2
- package/src/audits/cookie-policy.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/components/audit-feedback.tsx +4 -1
- package/src/components/audit-panel.tsx +6 -5
- package/src/components/fix-violation-with-angie.tsx +4 -10
- package/src/components/pages/all-audits-page.tsx +22 -3
- package/src/components/pages/category-page.tsx +27 -3
- package/src/components/violation-cta-button.tsx +10 -2
- package/src/components/violation-icons.tsx +2 -2
- package/src/components/violation-row.tsx +64 -64
- package/src/hooks/use-audit-report.ts +2 -2
- package/src/hooks/use-single-expanded-card.ts +11 -0
- package/src/register-promotions.ts +0 -23
- package/src/runner.ts +6 -2
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-1113",
|
|
4
4
|
"private": false,
|
|
5
5
|
"author": "Elementor Team",
|
|
6
6
|
"homepage": "https://elementor.com/",
|
|
@@ -39,21 +39,21 @@
|
|
|
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-elements": "4.4.0-
|
|
46
|
-
"@elementor/editor-floating-panels": "4.4.0-
|
|
47
|
-
"@elementor/editor-notifications": "4.4.0-
|
|
48
|
-
"@elementor/editor-props": "4.4.0-
|
|
49
|
-
"@elementor/editor-styles": "4.4.0-
|
|
50
|
-
"@elementor/editor-v1-adapters": "4.4.0-
|
|
51
|
-
"@elementor/events": "4.4.0-
|
|
52
|
-
"@elementor/http-client": "4.4.0-
|
|
42
|
+
"@elementor/editor": "4.4.0-1113",
|
|
43
|
+
"@elementor/editor-mcp": "4.4.0-1113",
|
|
44
|
+
"@elementor/editor-app-bar": "4.4.0-1113",
|
|
45
|
+
"@elementor/editor-elements": "4.4.0-1113",
|
|
46
|
+
"@elementor/editor-floating-panels": "4.4.0-1113",
|
|
47
|
+
"@elementor/editor-notifications": "4.4.0-1113",
|
|
48
|
+
"@elementor/editor-props": "4.4.0-1113",
|
|
49
|
+
"@elementor/editor-styles": "4.4.0-1113",
|
|
50
|
+
"@elementor/editor-v1-adapters": "4.4.0-1113",
|
|
51
|
+
"@elementor/events": "4.4.0-1113",
|
|
52
|
+
"@elementor/http-client": "4.4.0-1113",
|
|
53
53
|
"@elementor/icons": "~1.77.0",
|
|
54
|
-
"@elementor/locations": "4.4.0-
|
|
55
|
-
"@elementor/session": "4.4.0-
|
|
56
|
-
"@elementor/store": "4.4.0-
|
|
54
|
+
"@elementor/locations": "4.4.0-1113",
|
|
55
|
+
"@elementor/session": "4.4.0-1113",
|
|
56
|
+
"@elementor/store": "4.4.0-1113",
|
|
57
57
|
"@elementor/ui": "1.37.5"
|
|
58
58
|
},
|
|
59
59
|
"peerDependencies": {
|
|
@@ -29,9 +29,9 @@ export const audit: Audit = {
|
|
|
29
29
|
externalUrl: isReady
|
|
30
30
|
? ctx.pageContext.ally_accessibility_statement_url
|
|
31
31
|
: ctx.pageContext.ally_plugin_url,
|
|
32
|
-
ctaLabel:
|
|
32
|
+
ctaLabel: __( 'Create', 'elementor' ),
|
|
33
33
|
secondaryCtaLabel: __( 'Learn more', 'elementor' ),
|
|
34
|
-
secondaryCtaUrl: 'https://
|
|
34
|
+
secondaryCtaUrl: 'https://elementor.com/help/start-using-ally/#statement',
|
|
35
35
|
},
|
|
36
36
|
],
|
|
37
37
|
};
|
|
@@ -58,6 +58,7 @@ export const audit: Audit = {
|
|
|
58
58
|
targetHint: 'element-settings' as const,
|
|
59
59
|
label: __( "Image isn't served in a modern format like WebP or AVIF.", 'elementor' ),
|
|
60
60
|
externalUrl: ctx.pageContext.image_optimization_plugin_url,
|
|
61
|
+
ctaLabel: __( 'Optimize all', 'elementor' ),
|
|
61
62
|
} ) );
|
|
62
63
|
|
|
63
64
|
return violations.length === 0
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import { useState } from 'react';
|
|
3
|
+
import { useFloatingPanelZIndex } from '@elementor/editor-floating-panels';
|
|
3
4
|
import { notify } from '@elementor/editor-notifications';
|
|
4
5
|
import { isExperimentActive } from '@elementor/editor-v1-adapters';
|
|
5
6
|
import { useMixpanel } from '@elementor/events';
|
|
@@ -21,6 +22,7 @@ import {
|
|
|
21
22
|
import { __ } from '@wordpress/i18n';
|
|
22
23
|
|
|
23
24
|
import {
|
|
25
|
+
AUDIT_PANEL_ID,
|
|
24
26
|
FEEDBACK_CANCELLED_EVENT,
|
|
25
27
|
FEEDBACK_CLICKED_EVENT,
|
|
26
28
|
FEEDBACK_CLOSED_EVENT,
|
|
@@ -58,6 +60,7 @@ export default function AuditFeedback() {
|
|
|
58
60
|
const [ feedbackText, setFeedbackText ] = useState( '' );
|
|
59
61
|
const [ isSubmitting, setIsSubmitting ] = useState( false );
|
|
60
62
|
const { dispatchEvent: trackEvent = ( ...args: unknown[] ) => void args } = useMixpanel();
|
|
63
|
+
const panelZIndex = useFloatingPanelZIndex( AUDIT_PANEL_ID );
|
|
61
64
|
|
|
62
65
|
if ( ! isExperimentActive( FEEDBACK_EXPERIMENT_NAME ) ) {
|
|
63
66
|
return null;
|
|
@@ -117,7 +120,7 @@ export default function AuditFeedback() {
|
|
|
117
120
|
|
|
118
121
|
return (
|
|
119
122
|
<>
|
|
120
|
-
<Tooltip title={ triggerLabel } placement="top">
|
|
123
|
+
<Tooltip title={ triggerLabel } placement="top" PopperProps={ { sx: { zIndex: panelZIndex } } }>
|
|
121
124
|
<IconButton size="small" aria-label={ triggerLabel } onClick={ handleOpen }>
|
|
122
125
|
<MessageLinesIcon fontSize="small" />
|
|
123
126
|
</IconButton>
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import { getHostDocumentId } from '@elementor/editor-elements';
|
|
3
3
|
import { FloatingPanelBody, FloatingPanelFooter, FloatingPanelHeader } from '@elementor/editor-floating-panels';
|
|
4
4
|
import { Box, Button, Typography } from '@elementor/ui';
|
|
5
5
|
import { __ } from '@wordpress/i18n';
|
|
6
6
|
|
|
7
|
+
import { AUDIT_PANEL_ID } from '../constants';
|
|
7
8
|
import { useAuditReport } from '../hooks/use-audit-report';
|
|
8
9
|
import AuditFeedback from './audit-feedback';
|
|
9
10
|
import ErrorPage from './pages/error-page';
|
|
@@ -14,14 +15,14 @@ import ReportShell from './report-shell';
|
|
|
14
15
|
export default function AuditPanel() {
|
|
15
16
|
const { status, report, error, run } = useAuditReport();
|
|
16
17
|
|
|
17
|
-
const
|
|
18
|
-
const onRun = () => run(
|
|
18
|
+
const hostDocumentId = getHostDocumentId() ?? 0;
|
|
19
|
+
const onRun = () => run( hostDocumentId );
|
|
19
20
|
const lastScanLabel = report ? new Date( report.runAt ).toLocaleTimeString() : null;
|
|
20
21
|
|
|
21
22
|
return (
|
|
22
23
|
<>
|
|
23
24
|
<FloatingPanelHeader
|
|
24
|
-
panelId=
|
|
25
|
+
panelId={ AUDIT_PANEL_ID }
|
|
25
26
|
title={ __( 'Page Audit', 'elementor' ) }
|
|
26
27
|
badge={ __( 'Beta', 'elementor' ) }
|
|
27
28
|
titleVariant="subtitle2"
|
|
@@ -51,7 +52,7 @@ export default function AuditPanel() {
|
|
|
51
52
|
variant="contained"
|
|
52
53
|
size="small"
|
|
53
54
|
onClick={ onRun }
|
|
54
|
-
disabled={ status === 'loading' ||
|
|
55
|
+
disabled={ status === 'loading' || hostDocumentId === 0 }
|
|
55
56
|
>
|
|
56
57
|
{ lastScanLabel ? __( 'Re-scan', 'elementor' ) : __( 'Run page audit', 'elementor' ) }
|
|
57
58
|
</Button>
|
|
@@ -8,9 +8,10 @@ import { ANGIE_FIX_ENTRY_POINT, CREATE_WIDGET_EVENT } from '../constants';
|
|
|
8
8
|
|
|
9
9
|
type Props = {
|
|
10
10
|
prompt: string;
|
|
11
|
+
panelZIndex: number;
|
|
11
12
|
};
|
|
12
13
|
|
|
13
|
-
export default function FixViolationWithAngie( { prompt }: Props ) {
|
|
14
|
+
export default function FixViolationWithAngie( { prompt, panelZIndex }: Props ) {
|
|
14
15
|
const label = __( 'Fix with Angie', 'elementor' );
|
|
15
16
|
const href = `#angie-prompt=${ encodeURIComponent( prompt ) }`;
|
|
16
17
|
|
|
@@ -34,15 +35,8 @@ export default function FixViolationWithAngie( { prompt }: Props ) {
|
|
|
34
35
|
};
|
|
35
36
|
|
|
36
37
|
return (
|
|
37
|
-
<Tooltip title={ label }>
|
|
38
|
-
<IconButton
|
|
39
|
-
className="violation-hover-icon"
|
|
40
|
-
component="a"
|
|
41
|
-
href={ href }
|
|
42
|
-
size="small"
|
|
43
|
-
aria-label={ label }
|
|
44
|
-
onClick={ handleClick }
|
|
45
|
-
>
|
|
38
|
+
<Tooltip title={ label } placement="top" PopperProps={ { sx: { zIndex: panelZIndex } } }>
|
|
39
|
+
<IconButton component="a" href={ href } size="small" aria-label={ label } onClick={ handleClick }>
|
|
46
40
|
<AngieIcon fontSize="tiny" />
|
|
47
41
|
</IconButton>
|
|
48
42
|
</Tooltip>
|
|
@@ -2,6 +2,7 @@ import * as React from 'react';
|
|
|
2
2
|
import { Box } from '@elementor/ui';
|
|
3
3
|
import { __ } from '@wordpress/i18n';
|
|
4
4
|
|
|
5
|
+
import { useSingleExpandedCard } from '../../hooks/use-single-expanded-card';
|
|
5
6
|
import { type PageAuditReport } from '../../types';
|
|
6
7
|
import {
|
|
7
8
|
auditStatusColor,
|
|
@@ -24,6 +25,7 @@ export default function AllAuditsPage( { initialExpandedStatus, onBack, report }
|
|
|
24
25
|
const expandFail = ! initialExpandedStatus || initialExpandedStatus === 'fail';
|
|
25
26
|
const expandPass = initialExpandedStatus === 'pass';
|
|
26
27
|
const expandSkipped = initialExpandedStatus === 'skipped';
|
|
28
|
+
const { expandedId, toggle } = useSingleExpandedCard();
|
|
27
29
|
|
|
28
30
|
return (
|
|
29
31
|
<Box key={ initialExpandedStatus ?? 'default' }>
|
|
@@ -36,7 +38,13 @@ export default function AllAuditsPage( { initialExpandedStatus, onBack, report }
|
|
|
36
38
|
defaultExpanded={ expandFail }
|
|
37
39
|
>
|
|
38
40
|
{ failed.map( ( r ) => (
|
|
39
|
-
<ViolationRow
|
|
41
|
+
<ViolationRow
|
|
42
|
+
key={ r.audit.id }
|
|
43
|
+
audit={ r.audit }
|
|
44
|
+
violations={ r.result.violations }
|
|
45
|
+
expanded={ expandedId === r.audit.id }
|
|
46
|
+
onToggleExpand={ () => toggle( r.audit.id ) }
|
|
47
|
+
/>
|
|
40
48
|
) ) }
|
|
41
49
|
</StatusSection>
|
|
42
50
|
<StatusSection
|
|
@@ -46,7 +54,12 @@ export default function AllAuditsPage( { initialExpandedStatus, onBack, report }
|
|
|
46
54
|
defaultExpanded={ expandPass }
|
|
47
55
|
>
|
|
48
56
|
{ passed.map( ( r ) => (
|
|
49
|
-
<ViolationRow
|
|
57
|
+
<ViolationRow
|
|
58
|
+
key={ r.audit.id }
|
|
59
|
+
audit={ r.audit }
|
|
60
|
+
expanded={ expandedId === r.audit.id }
|
|
61
|
+
onToggleExpand={ () => toggle( r.audit.id ) }
|
|
62
|
+
/>
|
|
50
63
|
) ) }
|
|
51
64
|
</StatusSection>
|
|
52
65
|
<StatusSection
|
|
@@ -56,7 +69,13 @@ export default function AllAuditsPage( { initialExpandedStatus, onBack, report }
|
|
|
56
69
|
defaultExpanded={ expandSkipped }
|
|
57
70
|
>
|
|
58
71
|
{ skipped.map( ( r ) => (
|
|
59
|
-
<ViolationRow
|
|
72
|
+
<ViolationRow
|
|
73
|
+
key={ r.audit.id }
|
|
74
|
+
audit={ r.audit }
|
|
75
|
+
skipReason={ r.result.reason }
|
|
76
|
+
expanded={ expandedId === r.audit.id }
|
|
77
|
+
onToggleExpand={ () => toggle( r.audit.id ) }
|
|
78
|
+
/>
|
|
60
79
|
) ) }
|
|
61
80
|
</StatusSection>
|
|
62
81
|
</Box>
|
|
@@ -3,6 +3,7 @@ import { Box } from '@elementor/ui';
|
|
|
3
3
|
import { __ } from '@wordpress/i18n';
|
|
4
4
|
|
|
5
5
|
import { CATEGORY_LABELS } from '../../constants';
|
|
6
|
+
import { useSingleExpandedCard } from '../../hooks/use-single-expanded-card';
|
|
6
7
|
import { type AuditCategory, type PageAuditReport } from '../../types';
|
|
7
8
|
import { partitionAuditResults } from '../../utils/audit-status-summary';
|
|
8
9
|
import { CATEGORY_ICONS } from '../category-icons';
|
|
@@ -18,7 +19,8 @@ type Props = {
|
|
|
18
19
|
|
|
19
20
|
export default function CategoryPage( { category, report, onBack }: Props ) {
|
|
20
21
|
const Icon = CATEGORY_ICONS[ category ];
|
|
21
|
-
const { failed, passed, totalViolations } = partitionAuditResults( report, { category } );
|
|
22
|
+
const { failed, passed, skipped, totalViolations } = partitionAuditResults( report, { category } );
|
|
23
|
+
const { expandedId, toggle } = useSingleExpandedCard();
|
|
22
24
|
|
|
23
25
|
return (
|
|
24
26
|
<>
|
|
@@ -36,12 +38,34 @@ export default function CategoryPage( { category, report, onBack }: Props ) {
|
|
|
36
38
|
defaultExpanded
|
|
37
39
|
>
|
|
38
40
|
{ failed.map( ( r ) => (
|
|
39
|
-
<ViolationRow
|
|
41
|
+
<ViolationRow
|
|
42
|
+
key={ r.audit.id }
|
|
43
|
+
audit={ r.audit }
|
|
44
|
+
violations={ r.result.violations }
|
|
45
|
+
expanded={ expandedId === r.audit.id }
|
|
46
|
+
onToggleExpand={ () => toggle( r.audit.id ) }
|
|
47
|
+
/>
|
|
40
48
|
) ) }
|
|
41
49
|
</StatusSection>
|
|
42
50
|
<StatusSection label={ __( 'Passed audits', 'elementor' ) } count={ passed.length } color="success">
|
|
43
51
|
{ passed.map( ( r ) => (
|
|
44
|
-
<ViolationRow
|
|
52
|
+
<ViolationRow
|
|
53
|
+
key={ r.audit.id }
|
|
54
|
+
audit={ r.audit }
|
|
55
|
+
expanded={ expandedId === r.audit.id }
|
|
56
|
+
onToggleExpand={ () => toggle( r.audit.id ) }
|
|
57
|
+
/>
|
|
58
|
+
) ) }
|
|
59
|
+
</StatusSection>
|
|
60
|
+
<StatusSection label={ __( 'Skipped audits', 'elementor' ) } count={ skipped.length } color="default">
|
|
61
|
+
{ skipped.map( ( r ) => (
|
|
62
|
+
<ViolationRow
|
|
63
|
+
key={ r.audit.id }
|
|
64
|
+
audit={ r.audit }
|
|
65
|
+
skipReason={ r.result.reason }
|
|
66
|
+
expanded={ expandedId === r.audit.id }
|
|
67
|
+
onToggleExpand={ () => toggle( r.audit.id ) }
|
|
68
|
+
/>
|
|
45
69
|
) ) }
|
|
46
70
|
</StatusSection>
|
|
47
71
|
</Box>
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
+
import { WandIcon } from '@elementor/icons';
|
|
2
3
|
import { Button } from '@elementor/ui';
|
|
3
4
|
|
|
4
5
|
type Props = {
|
|
5
6
|
ctaLabel: string;
|
|
6
7
|
externalUrl: string;
|
|
7
8
|
variant?: 'outlined' | 'text';
|
|
9
|
+
withIcon?: boolean;
|
|
8
10
|
};
|
|
9
11
|
|
|
10
|
-
export default function ViolationCtaButton( { ctaLabel, externalUrl, variant = 'outlined' }: Props ) {
|
|
12
|
+
export default function ViolationCtaButton( { ctaLabel, externalUrl, variant = 'outlined', withIcon = false }: Props ) {
|
|
11
13
|
const handleClick = ( event: React.MouseEvent< HTMLButtonElement > ) => {
|
|
12
14
|
event.stopPropagation();
|
|
13
15
|
event.preventDefault();
|
|
@@ -16,7 +18,13 @@ export default function ViolationCtaButton( { ctaLabel, externalUrl, variant = '
|
|
|
16
18
|
};
|
|
17
19
|
|
|
18
20
|
return (
|
|
19
|
-
<Button
|
|
21
|
+
<Button
|
|
22
|
+
variant={ variant }
|
|
23
|
+
color="secondary"
|
|
24
|
+
size="small"
|
|
25
|
+
startIcon={ withIcon ? <WandIcon fontSize="tiny" aria-hidden={ true } /> : undefined }
|
|
26
|
+
onClick={ handleClick }
|
|
27
|
+
>
|
|
20
28
|
{ ctaLabel }
|
|
21
29
|
</Button>
|
|
22
30
|
);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import { FileSettingsIcon, SettingsIcon
|
|
2
|
+
import { FileSettingsIcon, SettingsIcon } from '@elementor/icons';
|
|
3
3
|
import { Box } from '@elementor/ui';
|
|
4
4
|
|
|
5
5
|
import { type AuditViolation } from '../types';
|
|
@@ -29,5 +29,5 @@ export default function ViolationIcon( { violation, widgetIcon }: Props ) {
|
|
|
29
29
|
return <SettingsIcon fontSize="inherit" aria-hidden={ true } />;
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
return <
|
|
32
|
+
return <SettingsIcon fontSize="inherit" aria-hidden={ true } />;
|
|
33
33
|
}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import { useState } from 'react';
|
|
3
2
|
import { getElementIcon, getElementTitle } from '@elementor/editor-elements';
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
3
|
+
import { useFloatingPanelZIndex } from '@elementor/editor-floating-panels';
|
|
4
|
+
import { CheckIcon, ChevronDownIcon, HelpIcon, InfoCircleIcon, WandIcon } from '@elementor/icons';
|
|
5
|
+
import { Alert, Box, Collapse, IconButton, Tooltip, Typography } from '@elementor/ui';
|
|
6
6
|
import { __ } from '@wordpress/i18n';
|
|
7
7
|
|
|
8
|
+
import { AUDIT_PANEL_ID } from '../constants';
|
|
8
9
|
import { focusViolation } from '../hooks/focus-violation';
|
|
9
10
|
import { type AuditMeta, type AuditViolation } from '../types';
|
|
10
11
|
import { buildAngiePrompt } from '../utils/build-angie-prompt';
|
|
@@ -17,6 +18,8 @@ import ViolationIcon from './violation-icons';
|
|
|
17
18
|
|
|
18
19
|
type Props = {
|
|
19
20
|
audit: AuditMeta;
|
|
21
|
+
expanded: boolean;
|
|
22
|
+
onToggleExpand: () => void;
|
|
20
23
|
skipReason?: string;
|
|
21
24
|
violations?: AuditViolation[];
|
|
22
25
|
};
|
|
@@ -35,12 +38,12 @@ function StatusIndicator( { audit, violations }: Pick< Props, 'audit' | 'violati
|
|
|
35
38
|
if ( violations ) {
|
|
36
39
|
return (
|
|
37
40
|
<>
|
|
41
|
+
<SeverityIcon severity={ audit.severity } />
|
|
38
42
|
{ isScoredAudit( audit ) && (
|
|
39
43
|
<Typography variant="caption" color="text.secondary" fontWeight="bold">
|
|
40
44
|
{ violations.length }
|
|
41
45
|
</Typography>
|
|
42
46
|
) }
|
|
43
|
-
<SeverityIcon severity={ audit.severity } />
|
|
44
47
|
</>
|
|
45
48
|
);
|
|
46
49
|
}
|
|
@@ -48,10 +51,41 @@ function StatusIndicator( { audit, violations }: Pick< Props, 'audit' | 'violati
|
|
|
48
51
|
return <CheckIcon fontSize="small" color="success" />;
|
|
49
52
|
}
|
|
50
53
|
|
|
51
|
-
|
|
52
|
-
const
|
|
54
|
+
function GuidanceAction( { violation }: { violation: AuditViolation } ) {
|
|
55
|
+
const hasPrimaryCta = !! ( violation.ctaLabel && violation.externalUrl );
|
|
53
56
|
|
|
54
|
-
|
|
57
|
+
if ( ! hasPrimaryCta ) {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return (
|
|
62
|
+
<Box sx={ { display: 'flex', alignItems: 'center', gap: 1 } }>
|
|
63
|
+
<ViolationCtaButton
|
|
64
|
+
ctaLabel={ violation.ctaLabel as string }
|
|
65
|
+
externalUrl={ violation.externalUrl as string }
|
|
66
|
+
withIcon
|
|
67
|
+
/>
|
|
68
|
+
{ violation.secondaryCtaLabel && violation.secondaryCtaUrl && (
|
|
69
|
+
<ViolationCtaButton
|
|
70
|
+
ctaLabel={ violation.secondaryCtaLabel }
|
|
71
|
+
externalUrl={ violation.secondaryCtaUrl }
|
|
72
|
+
variant="text"
|
|
73
|
+
/>
|
|
74
|
+
) }
|
|
75
|
+
</Box>
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export default function ViolationRow( { audit, expanded, onToggleExpand, skipReason, violations }: Props ) {
|
|
80
|
+
const primaryViolation = violations?.[ 0 ];
|
|
81
|
+
const isGuidanceFocusable = !! primaryViolation;
|
|
82
|
+
const panelZIndex = useFloatingPanelZIndex( AUDIT_PANEL_ID );
|
|
83
|
+
|
|
84
|
+
const handleGuidanceClick = () => {
|
|
85
|
+
if ( isGuidanceFocusable && primaryViolation ) {
|
|
86
|
+
focusViolation( primaryViolation );
|
|
87
|
+
}
|
|
88
|
+
};
|
|
55
89
|
|
|
56
90
|
return (
|
|
57
91
|
<Box sx={ { borderBottom: 1, borderColor: 'divider', paddingBlock: 0.5 } }>
|
|
@@ -65,9 +99,9 @@ export default function ViolationRow( { audit, skipReason, violations }: Props )
|
|
|
65
99
|
gap: 0.5,
|
|
66
100
|
minWidth: 0,
|
|
67
101
|
} }
|
|
68
|
-
onClick={
|
|
102
|
+
onClick={ onToggleExpand }
|
|
69
103
|
>
|
|
70
|
-
<Typography variant="
|
|
104
|
+
<Typography variant="subtitle2" color="text.secondary" sx={ { flex: 1 } }>
|
|
71
105
|
{ audit.title }
|
|
72
106
|
</Typography>
|
|
73
107
|
{ ! skipReason && <StatusIndicator audit={ audit } violations={ violations } /> }
|
|
@@ -76,7 +110,7 @@ export default function ViolationRow( { audit, skipReason, violations }: Props )
|
|
|
76
110
|
<IconButton
|
|
77
111
|
size="small"
|
|
78
112
|
aria-label={ expanded ? __( 'Collapse', 'elementor' ) : __( 'Expand', 'elementor' ) }
|
|
79
|
-
onClick={
|
|
113
|
+
onClick={ onToggleExpand }
|
|
80
114
|
>
|
|
81
115
|
<ChevronDownIcon
|
|
82
116
|
fontSize="small"
|
|
@@ -89,37 +123,25 @@ export default function ViolationRow( { audit, skipReason, violations }: Props )
|
|
|
89
123
|
</Box>
|
|
90
124
|
<Collapse in={ expanded }>
|
|
91
125
|
<Box sx={ { display: 'flex', flexDirection: 'column', gap: 1, paddingBlock: 1 } }>
|
|
92
|
-
<
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
126
|
+
<Tooltip title={ audit.fixHint } placement="top">
|
|
127
|
+
<Alert
|
|
128
|
+
severity="secondary"
|
|
129
|
+
sx={ { p: 1, cursor: isGuidanceFocusable ? 'pointer' : undefined } }
|
|
130
|
+
icon={ <InfoCircleIcon fontSize="small" aria-hidden={ true } /> }
|
|
131
|
+
role={ isGuidanceFocusable ? 'button' : undefined }
|
|
132
|
+
tabIndex={ isGuidanceFocusable ? 0 : undefined }
|
|
133
|
+
onClick={ isGuidanceFocusable ? handleGuidanceClick : undefined }
|
|
134
|
+
onKeyDown={ isGuidanceFocusable ? onKeyboardClick( handleGuidanceClick ) : undefined }
|
|
135
|
+
>
|
|
136
|
+
<Typography variant="caption" component="p" color="text.secondary">
|
|
137
|
+
{ audit.description }
|
|
100
138
|
</Typography>
|
|
101
|
-
</
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
</Typography>
|
|
105
|
-
</Alert>
|
|
106
|
-
<Alert
|
|
107
|
-
severity="info"
|
|
108
|
-
sx={ { p: 1 } }
|
|
109
|
-
icon={ <BulbIcon fontSize="small" color="info" aria-hidden={ true } /> }
|
|
110
|
-
>
|
|
111
|
-
<AlertTitle>
|
|
112
|
-
<Typography variant="caption" component="p" color="text.primary" fontWeight="bold">
|
|
113
|
-
{ __( 'How to resolve', 'elementor' ) }
|
|
114
|
-
</Typography>
|
|
115
|
-
</AlertTitle>
|
|
116
|
-
<Typography variant="caption" component="p" color="text.secondary">
|
|
117
|
-
{ audit.fixHint }
|
|
118
|
-
</Typography>
|
|
119
|
-
</Alert>
|
|
139
|
+
</Alert>
|
|
140
|
+
</Tooltip>
|
|
141
|
+
{ primaryViolation && <GuidanceAction violation={ primaryViolation } /> }
|
|
120
142
|
</Box>
|
|
121
143
|
{ violations && violations.length > 0 && (
|
|
122
|
-
<Box role="list" sx={ { paddingBlockEnd: 1
|
|
144
|
+
<Box role="list" sx={ { paddingBlockEnd: 1 } }>
|
|
123
145
|
{ violations.map( ( violation, idx ) => {
|
|
124
146
|
const widgetIcon = violation.elementId ? getElementIcon( violation.elementId ) : null;
|
|
125
147
|
const elementTitle = violation.elementId ? getElementTitle( violation.elementId ) : null;
|
|
@@ -139,14 +161,10 @@ export default function ViolationRow( { audit, skipReason, violations }: Props )
|
|
|
139
161
|
alignItems: 'center',
|
|
140
162
|
gap: 1,
|
|
141
163
|
paddingBlock: 0.5,
|
|
142
|
-
paddingInline:
|
|
164
|
+
paddingInline: 1,
|
|
143
165
|
borderRadius: 1,
|
|
144
166
|
cursor: 'pointer',
|
|
145
|
-
'& .violation-hover-icon': { opacity: 0, transition: 'opacity .15s' },
|
|
146
167
|
'&:hover': { bgcolor: 'action.hover' },
|
|
147
|
-
'&:hover .violation-hover-icon, &:focus-visible .violation-hover-icon': {
|
|
148
|
-
opacity: 1,
|
|
149
|
-
},
|
|
150
168
|
} }
|
|
151
169
|
>
|
|
152
170
|
<ViolationIcon violation={ violation } widgetIcon={ widgetIcon } />
|
|
@@ -158,31 +176,13 @@ export default function ViolationRow( { audit, skipReason, violations }: Props )
|
|
|
158
176
|
</Typography>
|
|
159
177
|
) }
|
|
160
178
|
</Box>
|
|
161
|
-
{ violation.angieFix
|
|
179
|
+
{ violation.angieFix ? (
|
|
162
180
|
<FixViolationWithAngie
|
|
163
181
|
prompt={ violation.angiePrompt ?? buildAngiePrompt( rowLabel ) }
|
|
182
|
+
panelZIndex={ panelZIndex }
|
|
164
183
|
/>
|
|
165
|
-
) }
|
|
166
|
-
{ violation.ctaLabel && violation.externalUrl ? (
|
|
167
|
-
<Box sx={ { display: 'flex', alignItems: 'center', gap: 0.5 } }>
|
|
168
|
-
<ViolationCtaButton
|
|
169
|
-
ctaLabel={ violation.ctaLabel }
|
|
170
|
-
externalUrl={ violation.externalUrl }
|
|
171
|
-
/>
|
|
172
|
-
{ violation.secondaryCtaLabel && violation.secondaryCtaUrl && (
|
|
173
|
-
<ViolationCtaButton
|
|
174
|
-
ctaLabel={ violation.secondaryCtaLabel }
|
|
175
|
-
externalUrl={ violation.secondaryCtaUrl }
|
|
176
|
-
variant="text"
|
|
177
|
-
/>
|
|
178
|
-
) }
|
|
179
|
-
</Box>
|
|
180
184
|
) : (
|
|
181
|
-
<
|
|
182
|
-
className="violation-hover-icon"
|
|
183
|
-
fontSize="tiny"
|
|
184
|
-
aria-hidden={ true }
|
|
185
|
-
/>
|
|
185
|
+
<WandIcon fontSize="tiny" aria-hidden={ true } />
|
|
186
186
|
) }
|
|
187
187
|
</Box>
|
|
188
188
|
);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { useEffect } from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import { getHostDocumentId } from '@elementor/editor-elements';
|
|
3
3
|
import { __useDispatch as useDispatch, __useSelector as useSelector } from '@elementor/store';
|
|
4
4
|
|
|
5
5
|
import { runPageAudit } from '../runner';
|
|
@@ -12,7 +12,7 @@ export function useAuditReport() {
|
|
|
12
12
|
const report = useSelector( ( state: GlobalState ) => selectReport( state ) );
|
|
13
13
|
const error = useSelector( ( state: GlobalState ) => selectError( state ) );
|
|
14
14
|
const dispatch = useDispatch();
|
|
15
|
-
const documentId =
|
|
15
|
+
const documentId = getHostDocumentId() ?? 0;
|
|
16
16
|
|
|
17
17
|
useEffect( () => {
|
|
18
18
|
if ( ! documentId || report?.documentId === documentId ) {
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { useState } from 'react';
|
|
2
|
+
|
|
3
|
+
export function useSingleExpandedCard( initialId: string | null = null ) {
|
|
4
|
+
const [ expandedId, setExpandedId ] = useState< string | null >( initialId );
|
|
5
|
+
|
|
6
|
+
const toggle = ( id: string ) => {
|
|
7
|
+
setExpandedId( ( current ) => ( current === id ? null : id ) );
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
return { expandedId, toggle };
|
|
11
|
+
}
|
|
@@ -80,27 +80,4 @@ export const PROMOTIONS: PromotionConfig[] = [
|
|
|
80
80
|
},
|
|
81
81
|
getCtaUrl: firstFailExternalUrl,
|
|
82
82
|
},
|
|
83
|
-
{
|
|
84
|
-
auditId: 'audits/images-inefficient-format',
|
|
85
|
-
icon: ShieldHalfFilledIcon,
|
|
86
|
-
ctaLabel: __( 'Optimize all', 'elementor' ),
|
|
87
|
-
formatSubtitle: ( run ) => {
|
|
88
|
-
if ( run.result.status !== 'fail' ) {
|
|
89
|
-
return null;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
const count = run.result.metadata?.inefficientImageFormatCount ?? 0;
|
|
93
|
-
|
|
94
|
-
if ( count === 0 ) {
|
|
95
|
-
return null;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
return sprintf(
|
|
99
|
-
/* translators: %d: number of images served in an inefficient format. */
|
|
100
|
-
_n( '%d image', '%d images', count, 'elementor' ),
|
|
101
|
-
count
|
|
102
|
-
);
|
|
103
|
-
},
|
|
104
|
-
getCtaUrl: firstFailExternalUrl,
|
|
105
|
-
},
|
|
106
83
|
];
|
package/src/runner.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { getHostDocumentElements, getHostDocumentId } from '@elementor/editor-elements';
|
|
2
2
|
|
|
3
3
|
import { fetchPageContext } from './api/page-context-client';
|
|
4
4
|
import { getRegisteredAudits } from './registry';
|
|
@@ -18,7 +18,11 @@ import { readKitSnapshot } from './utils/read-kit-snapshot';
|
|
|
18
18
|
import { buildSnapshotTree } from './utils/v1-snapshot';
|
|
19
19
|
|
|
20
20
|
export async function runPageAudit( documentId: number ): Promise< PageAuditReport > {
|
|
21
|
-
|
|
21
|
+
if ( documentId !== getHostDocumentId() ) {
|
|
22
|
+
throw new Error( 'runPageAudit: documentId must match the host document.' );
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const tree = buildSnapshotTree( getHostDocumentElements() );
|
|
22
26
|
const imageSizeRequests = extractImageSizeRequests( tree );
|
|
23
27
|
const pageContext = await fetchPageContext( documentId, imageSizeRequests );
|
|
24
28
|
|