@elementor/editor-audits 4.4.0-1112 → 4.4.0-1114

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elementor/editor-audits",
3
- "version": "4.4.0-1112",
3
+ "version": "4.4.0-1114",
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-1112",
43
- "@elementor/editor-mcp": "4.4.0-1112",
44
- "@elementor/editor-app-bar": "4.4.0-1112",
45
- "@elementor/editor-elements": "4.4.0-1112",
46
- "@elementor/editor-floating-panels": "4.4.0-1112",
47
- "@elementor/editor-notifications": "4.4.0-1112",
48
- "@elementor/editor-props": "4.4.0-1112",
49
- "@elementor/editor-styles": "4.4.0-1112",
50
- "@elementor/editor-v1-adapters": "4.4.0-1112",
51
- "@elementor/events": "4.4.0-1112",
52
- "@elementor/http-client": "4.4.0-1112",
42
+ "@elementor/editor": "4.4.0-1114",
43
+ "@elementor/editor-mcp": "4.4.0-1114",
44
+ "@elementor/editor-app-bar": "4.4.0-1114",
45
+ "@elementor/editor-elements": "4.4.0-1114",
46
+ "@elementor/editor-floating-panels": "4.4.0-1114",
47
+ "@elementor/editor-notifications": "4.4.0-1114",
48
+ "@elementor/editor-props": "4.4.0-1114",
49
+ "@elementor/editor-styles": "4.4.0-1114",
50
+ "@elementor/editor-v1-adapters": "4.4.0-1114",
51
+ "@elementor/events": "4.4.0-1114",
52
+ "@elementor/http-client": "4.4.0-1114",
53
53
  "@elementor/icons": "~1.77.0",
54
- "@elementor/locations": "4.4.0-1112",
55
- "@elementor/session": "4.4.0-1112",
56
- "@elementor/store": "4.4.0-1112",
54
+ "@elementor/locations": "4.4.0-1114",
55
+ "@elementor/session": "4.4.0-1114",
56
+ "@elementor/store": "4.4.0-1114",
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: isReady ? __( 'Create', 'elementor' ) : undefined,
32
+ ctaLabel: __( 'Create', 'elementor' ),
33
33
  secondaryCtaLabel: __( 'Learn more', 'elementor' ),
34
- secondaryCtaUrl: 'https://go.elementor.com/acc-plg-learn-more',
34
+ secondaryCtaUrl: 'https://elementor.com/help/start-using-ally/#statement',
35
35
  },
36
36
  ],
37
37
  };
@@ -25,6 +25,7 @@ export const audit: Audit = {
25
25
  auditId: audit.id,
26
26
  label: __( 'Cookiez plugin is not installed or active.', 'elementor' ),
27
27
  externalUrl: ctx.pageContext.cookiez_plugin_url,
28
+ ctaLabel: __( 'Set up cookie consent', 'elementor' ),
28
29
  },
29
30
  ],
30
31
  };
@@ -36,6 +36,7 @@ export const audit: Audit = {
36
36
  targetHint: 'element-settings',
37
37
  label: __( 'Image is missing alt text.', 'elementor' ),
38
38
  externalUrl: ctx.pageContext.ally_plugin_url,
39
+ ctaLabel: __( 'Fix with Ally', 'elementor' ),
39
40
  } );
40
41
  }
41
42
  } );
@@ -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
@@ -66,6 +66,7 @@ export const audit: Audit = {
66
66
  kb
67
67
  ),
68
68
  externalUrl: ctx.pageContext.image_optimization_plugin_url,
69
+ ctaLabel: __( 'Optimize all', 'elementor' ),
69
70
  } ) );
70
71
 
71
72
  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 { getCurrentDocumentId } from '@elementor/editor-elements';
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 currentDocumentId = getCurrentDocumentId() ?? 0;
18
- const onRun = () => run( currentDocumentId );
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="audit-panel"
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' || currentDocumentId === 0 }
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 key={ r.audit.id } audit={ r.audit } violations={ r.result.violations } />
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 key={ r.audit.id } audit={ r.audit } />
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 key={ r.audit.id } audit={ r.audit } skipReason={ r.result.reason } />
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 key={ r.audit.id } audit={ r.audit } violations={ r.result.violations } />
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 key={ r.audit.id } audit={ r.audit } />
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 variant={ variant } color="secondary" size="small" onClick={ handleClick }>
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, ShieldCheckIcon } from '@elementor/icons';
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 <ShieldCheckIcon fontSize="inherit" aria-hidden={ true } />;
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 { AlertCircleIcon, BulbIcon, CheckIcon, ChevronDownIcon, EyeIcon, HelpIcon } from '@elementor/icons';
5
- import { Alert, AlertTitle, Box, Collapse, IconButton, Tooltip, Typography } from '@elementor/ui';
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
- export default function ViolationRow( { audit, skipReason, violations }: Props ) {
52
- const [ expanded, setExpanded ] = useState( false );
54
+ function GuidanceAction( { violation }: { violation: AuditViolation } ) {
55
+ const hasPrimaryCta = !! ( violation.ctaLabel && violation.externalUrl );
53
56
 
54
- const toggleExpanded = () => setExpanded( ( value ) => ! value );
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={ toggleExpanded }
102
+ onClick={ onToggleExpand }
69
103
  >
70
- <Typography variant="body2" sx={ { flex: 1 } }>
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={ toggleExpanded }
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
- <Alert
93
- severity="secondary"
94
- sx={ { p: 1 } }
95
- icon={ <AlertCircleIcon fontSize="small" color="secondary" aria-hidden={ true } /> }
96
- >
97
- <AlertTitle>
98
- <Typography variant="caption" component="p" color="text.primary" fontWeight="bold">
99
- { __( "What's the issue", 'elementor' ) }
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
- </AlertTitle>
102
- <Typography variant="caption" component="p" color="text.secondary">
103
- { audit.description }
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, paddingInlineStart: 2 } }>
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: 2,
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
- <EyeIcon
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 { getCurrentDocumentId } from '@elementor/editor-elements';
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 = getCurrentDocumentId() ?? 0;
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 { getElements } from '@elementor/editor-elements';
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
- const tree = buildSnapshotTree( getElements() );
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