@elementor/editor-audits 4.3.3 → 4.3.5

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.
Files changed (59) hide show
  1. package/dist/index.d.mts +11 -1
  2. package/dist/index.d.ts +11 -1
  3. package/dist/index.js +1103 -1146
  4. package/dist/index.js.map +1 -1
  5. package/dist/index.mjs +1075 -1108
  6. package/dist/index.mjs.map +1 -1
  7. package/package.json +17 -15
  8. package/src/api/page-context-client.ts +8 -5
  9. package/src/audits/accessibility-statement.ts +39 -0
  10. package/src/audits/accessibility-widget.ts +41 -0
  11. package/src/audits/cookie-policy.ts +1 -0
  12. package/src/audits/google-consent-mode.ts +4 -7
  13. package/src/audits/images-alt-text.ts +6 -4
  14. package/src/audits/images-inefficient-format.ts +66 -0
  15. package/src/audits/images-too-large.ts +23 -10
  16. package/src/audits/nested-boxed-containers.ts +5 -0
  17. package/src/components/audit-feedback.tsx +4 -1
  18. package/src/components/audit-panel.tsx +13 -13
  19. package/src/components/audit-toggle-icon.tsx +22 -0
  20. package/src/components/fix-violation-with-angie.tsx +18 -7
  21. package/src/components/pages/category-page.tsx +27 -3
  22. package/src/components/pages/overview-page.tsx +24 -29
  23. package/src/components/report-shell.tsx +10 -91
  24. package/src/components/rescan-button.tsx +38 -0
  25. package/src/components/severity-icons.tsx +5 -1
  26. package/src/components/severity-summary-cards.tsx +52 -0
  27. package/src/components/violation-cta-button.tsx +11 -2
  28. package/src/components/violation-icons.tsx +2 -2
  29. package/src/components/violation-row.tsx +120 -62
  30. package/src/hooks/use-audit-report.ts +5 -4
  31. package/src/hooks/use-audit-toggle-props.ts +2 -2
  32. package/src/hooks/use-single-expanded-card.ts +11 -0
  33. package/src/init.ts +2 -0
  34. package/src/register-audits.ts +6 -2
  35. package/src/runner.ts +15 -7
  36. package/src/store/selectors.ts +4 -0
  37. package/src/store/slice.ts +10 -1
  38. package/src/sync-report-staleness.ts +14 -0
  39. package/src/types.ts +11 -1
  40. package/src/utils/all-image-sources.ts +17 -0
  41. package/src/utils/alt-text-image-sources.ts +24 -0
  42. package/src/utils/atomic-background-image-sources.ts +56 -0
  43. package/src/utils/atomic-image-sources.ts +66 -0
  44. package/src/utils/audit-status-summary.ts +0 -57
  45. package/src/utils/background-image-sources.ts +32 -0
  46. package/src/utils/image-alt.ts +3 -1
  47. package/src/utils/image-like-sources.ts +5 -14
  48. package/src/utils/image-size-key.ts +8 -0
  49. package/src/utils/page-attachments.ts +7 -6
  50. package/src/utils/scan-accessibility-widgets.ts +7 -0
  51. package/src/audits/too-many-widgets.ts +0 -53
  52. package/src/components/audit-statuses-section.tsx +0 -65
  53. package/src/components/count-summary-circle.tsx +0 -66
  54. package/src/components/pages/all-audits-page.tsx +0 -65
  55. package/src/components/pages/issues-page.tsx +0 -65
  56. package/src/components/score-bar.tsx +0 -56
  57. package/src/components/score-circle.tsx +0 -60
  58. package/src/components/severity-filter-chips.tsx +0 -57
  59. package/src/utils/score-thresholds.ts +0 -23
@@ -1,65 +0,0 @@
1
- import * as React from 'react';
2
- import { Box, Divider, Typography } from '@elementor/ui';
3
- import { __, sprintf } from '@wordpress/i18n';
4
-
5
- import { type PageAuditReport } from '../types';
6
- import {
7
- auditStatusColor,
8
- auditStatusDisplayCounts,
9
- type AuditStatusGroup,
10
- auditStatusLabel,
11
- } from '../utils/audit-status-summary';
12
- import CountSummaryCircle from './count-summary-circle';
13
-
14
- type Props = {
15
- report: PageAuditReport;
16
- onStatusClick: ( status: AuditStatusGroup ) => void;
17
- };
18
-
19
- const STATUS_ARIA_LABELS: Record< AuditStatusGroup, ( count: number ) => string > = {
20
- pass: ( count ) =>
21
- sprintf(
22
- /* translators: %d: number of passed audits. */
23
- __( '%d passed audits, view all', 'elementor' ),
24
- count
25
- ),
26
- fail: ( count ) =>
27
- sprintf(
28
- /* translators: %d: number of failed audit violations. */
29
- __( '%d failed audits, view all', 'elementor' ),
30
- count
31
- ),
32
- skipped: ( count ) =>
33
- sprintf(
34
- /* translators: %d: number of skipped audits. */
35
- __( '%d skipped audits, view all', 'elementor' ),
36
- count
37
- ),
38
- };
39
-
40
- const STATUS_GROUPS = [ 'fail', 'pass', 'skipped' ] as const;
41
-
42
- export default function AuditStatusesSection( { report, onStatusClick }: Props ) {
43
- const statusCounts = auditStatusDisplayCounts( report );
44
-
45
- return (
46
- <>
47
- <Typography variant="subtitle1" fontWeight="bold">
48
- { __( 'Audit statuses', 'elementor' ) }
49
- </Typography>
50
- <Box sx={ { display: 'flex', justifyContent: 'space-around', gap: 2 } }>
51
- { STATUS_GROUPS.map( ( status ) => (
52
- <CountSummaryCircle
53
- key={ status }
54
- ariaLabel={ STATUS_ARIA_LABELS[ status ]( statusCounts[ status ] ) }
55
- color={ auditStatusColor( status ) }
56
- count={ statusCounts[ status ] }
57
- label={ auditStatusLabel( status ) }
58
- onClick={ () => onStatusClick( status ) }
59
- />
60
- ) ) }
61
- </Box>
62
- <Divider />
63
- </>
64
- );
65
- }
@@ -1,66 +0,0 @@
1
- import * as React from 'react';
2
- import { Box, Chip, type ChipProps, type Theme, Typography } from '@elementor/ui';
3
-
4
- type Props = {
5
- ariaLabel: string;
6
- color: ChipProps[ 'color' ];
7
- count: number;
8
- label: string;
9
- onClick: () => void;
10
- };
11
-
12
- const COUNT_SUMMARY_CIRCLE_SIZE = 64;
13
- const COUNT_SUMMARY_BORDER_WIDTH = 4;
14
-
15
- function chipBorderColor( theme: Theme, color: ChipProps[ 'color' ] ): string {
16
- if ( color === 'default' || ! color ) {
17
- return theme.palette.text.secondary;
18
- }
19
-
20
- return theme.palette[ color ].main;
21
- }
22
-
23
- export default function CountSummaryCircle( { ariaLabel, color, count, label, onClick }: Props ) {
24
- return (
25
- <Box
26
- aria-label={ ariaLabel }
27
- component="button"
28
- onClick={ onClick }
29
- type="button"
30
- sx={ {
31
- alignItems: 'center',
32
- background: 'none',
33
- border: 'none',
34
- cursor: 'pointer',
35
- display: 'flex',
36
- flex: 1,
37
- flexDirection: 'column',
38
- gap: 0.5,
39
- padding: 0,
40
- } }
41
- >
42
- <Chip
43
- color={ color }
44
- label={ count }
45
- size="small"
46
- variant="standard"
47
- sx={ ( theme: Theme ) => ( {
48
- border: `${ COUNT_SUMMARY_BORDER_WIDTH }px solid ${ chipBorderColor( theme, color ) }`,
49
- borderRadius: '50%',
50
- height: COUNT_SUMMARY_CIRCLE_SIZE,
51
- maxWidth: COUNT_SUMMARY_CIRCLE_SIZE,
52
- width: COUNT_SUMMARY_CIRCLE_SIZE,
53
- '& .MuiChip-label': {
54
- fontSize: theme.typography.body2.fontSize,
55
- fontWeight: 700,
56
- lineHeight: 1,
57
- padding: 0,
58
- },
59
- } ) }
60
- />
61
- <Typography color="text.secondary" sx={ { textAlign: 'center' } } variant="caption">
62
- { label }
63
- </Typography>
64
- </Box>
65
- );
66
- }
@@ -1,65 +0,0 @@
1
- import * as React from 'react';
2
- import { Box } from '@elementor/ui';
3
- import { __ } from '@wordpress/i18n';
4
-
5
- import { type PageAuditReport } from '../../types';
6
- import {
7
- auditStatusColor,
8
- type AuditStatusGroup,
9
- auditStatusLabel,
10
- partitionAuditResults,
11
- } from '../../utils/audit-status-summary';
12
- import StatusSection from '../status-section';
13
- import SubpageHeader from '../subpage-header';
14
- import ViolationRow from '../violation-row';
15
-
16
- type Props = {
17
- initialExpandedStatus?: AuditStatusGroup;
18
- onBack: () => void;
19
- report: PageAuditReport;
20
- };
21
-
22
- export default function AllAuditsPage( { initialExpandedStatus, onBack, report }: Props ) {
23
- const { failed, passed, skipped, totalViolations } = partitionAuditResults( report );
24
- const expandFail = ! initialExpandedStatus || initialExpandedStatus === 'fail';
25
- const expandPass = initialExpandedStatus === 'pass';
26
- const expandSkipped = initialExpandedStatus === 'skipped';
27
-
28
- return (
29
- <Box key={ initialExpandedStatus ?? 'default' }>
30
- <SubpageHeader title={ __( 'All audits', 'elementor' ) } onBack={ onBack } />
31
- <Box sx={ { p: 1 } }>
32
- <StatusSection
33
- label={ auditStatusLabel( 'fail' ) }
34
- count={ totalViolations }
35
- color={ auditStatusColor( 'fail' ) }
36
- defaultExpanded={ expandFail }
37
- >
38
- { failed.map( ( r ) => (
39
- <ViolationRow key={ r.audit.id } audit={ r.audit } violations={ r.result.violations } />
40
- ) ) }
41
- </StatusSection>
42
- <StatusSection
43
- label={ auditStatusLabel( 'pass' ) }
44
- count={ passed.length }
45
- color={ auditStatusColor( 'pass' ) }
46
- defaultExpanded={ expandPass }
47
- >
48
- { passed.map( ( r ) => (
49
- <ViolationRow key={ r.audit.id } audit={ r.audit } />
50
- ) ) }
51
- </StatusSection>
52
- <StatusSection
53
- label={ auditStatusLabel( 'skipped' ) }
54
- count={ skipped.length }
55
- color={ auditStatusColor( 'skipped' ) }
56
- defaultExpanded={ expandSkipped }
57
- >
58
- { skipped.map( ( r ) => (
59
- <ViolationRow key={ r.audit.id } audit={ r.audit } skipReason={ r.result.reason } />
60
- ) ) }
61
- </StatusSection>
62
- </Box>
63
- </Box>
64
- );
65
- }
@@ -1,65 +0,0 @@
1
- import * as React from 'react';
2
- import { useState } from 'react';
3
- import { Box, Link, Typography } from '@elementor/ui';
4
- import { __ } from '@wordpress/i18n';
5
-
6
- import { ALL_CATEGORIES, CATEGORY_LABELS } from '../../constants';
7
- import { type AuditCategory, type PageAuditReport } from '../../types';
8
- import { type AuditStatusGroup, getPopulatedCategories } from '../../utils/audit-status-summary';
9
- import { countSeverities, type SeverityCounts } from '../../utils/severity-counts';
10
- import AuditStatusesSection from '../audit-statuses-section';
11
- import IssuesCategoryRow from '../issues-category-row';
12
- import SeverityFilterChips, { type SeverityFilter } from '../severity-filter-chips';
13
-
14
- type Props = {
15
- report: PageAuditReport;
16
- onCategoryClick: ( category: AuditCategory ) => void;
17
- onAllAuditsClick: () => void;
18
- onStatusClick: ( status: AuditStatusGroup ) => void;
19
- };
20
-
21
- function filterCounts( counts: SeverityCounts, filter: SeverityFilter ): SeverityCounts {
22
- if ( filter === 'all' ) {
23
- return counts;
24
- }
25
-
26
- return { error: 0, warning: 0, info: 0, [ filter ]: counts[ filter ] };
27
- }
28
-
29
- export default function IssuesPage( { report, onCategoryClick, onAllAuditsClick, onStatusClick }: Props ) {
30
- const [ filter, setFilter ] = useState< SeverityFilter >( 'all' );
31
- const populatedCategories = getPopulatedCategories( report.categories, ALL_CATEGORIES );
32
-
33
- const categoryRows = populatedCategories
34
- .map( ( category ) => ( { category, counts: filterCounts( countSeverities( report, category ), filter ) } ) )
35
- .filter( ( { counts } ) => Object.values( counts ).some( ( count ) => count > 0 ) );
36
-
37
- return (
38
- <Box sx={ { display: 'flex', flexDirection: 'column', gap: 4, p: 2 } }>
39
- <AuditStatusesSection report={ report } onStatusClick={ onStatusClick } />
40
- <Link
41
- component="button"
42
- underline="none"
43
- color="inherit"
44
- onClick={ onAllAuditsClick }
45
- sx={ { textAlign: 'start' } }
46
- >
47
- <Typography variant="subtitle1" component="h2">
48
- { __( 'All issues', 'elementor' ) }
49
- </Typography>
50
- </Link>
51
- <SeverityFilterChips selected={ filter } onChange={ setFilter } />
52
- <Box sx={ { display: 'flex', flexDirection: 'column', gap: 1 } }>
53
- { categoryRows.map( ( { category, counts } ) => (
54
- <IssuesCategoryRow
55
- key={ category }
56
- category={ category }
57
- label={ CATEGORY_LABELS[ category ] }
58
- counts={ counts }
59
- onClick={ () => onCategoryClick( category ) }
60
- />
61
- ) ) }
62
- </Box>
63
- </Box>
64
- );
65
- }
@@ -1,56 +0,0 @@
1
- import * as React from 'react';
2
- import { ChevronRightIcon } from '@elementor/icons';
3
- import { Box, LinearProgress, Rotate, Typography, useTheme } from '@elementor/ui';
4
-
5
- import { onKeyboardClick } from '../utils/keyboard-click';
6
- import { getScoreTier } from '../utils/score-thresholds';
7
-
8
- type Props = {
9
- label: string;
10
- score: number;
11
- onClick?: () => void;
12
- };
13
-
14
- export default function ScoreBar( { label, score, onClick }: Props ) {
15
- const isRtl = 'rtl' === useTheme().direction;
16
-
17
- return (
18
- <Box
19
- role={ onClick ? 'button' : undefined }
20
- tabIndex={ onClick ? 0 : undefined }
21
- onClick={ onClick }
22
- onKeyDown={ onClick ? onKeyboardClick( onClick ) : undefined }
23
- sx={ {
24
- display: 'flex',
25
- alignItems: 'center',
26
- gap: 1,
27
- cursor: onClick ? 'pointer' : 'default',
28
- outline: 'none',
29
- '&:focus-visible': { outline: '2px solid', outlineColor: 'primary.main', borderRadius: 1 },
30
- py: 0.5,
31
- } }
32
- >
33
- <Typography variant="body2" sx={ { minWidth: 96 } }>
34
- { label }
35
- </Typography>
36
- <LinearProgress
37
- variant="determinate"
38
- value={ score }
39
- color={ getScoreTier( score ).color }
40
- sx={ { flex: 1, height: 6, borderRadius: 4, bgcolor: 'action.disabledBackground' } }
41
- />
42
- <Typography
43
- variant="body2"
44
- color="text.primary"
45
- sx={ { minWidth: 24, textAlign: 'right', fontWeight: 900 } }
46
- >
47
- { score }
48
- </Typography>
49
- { onClick && (
50
- <Rotate in={ isRtl }>
51
- <ChevronRightIcon fontSize="small" color="action" />
52
- </Rotate>
53
- ) }
54
- </Box>
55
- );
56
- }
@@ -1,60 +0,0 @@
1
- import * as React from 'react';
2
- import { Box, CircularProgress, Typography } from '@elementor/ui';
3
-
4
- import { getScoreTier, type ScoreColor } from '../utils/score-thresholds';
5
-
6
- type Props = {
7
- color?: ScoreColor;
8
- score: number;
9
- };
10
-
11
- const SCORE_CIRCLE_SIZE = 88;
12
- const SCORE_CIRCLE_THICKNESS = 3;
13
-
14
- export default function ScoreCircle( { color, score }: Props ) {
15
- const progressColor = color ?? getScoreTier( score ).color;
16
-
17
- return (
18
- <Box
19
- role="progressbar"
20
- aria-valuenow={ score }
21
- aria-valuemin={ 0 }
22
- aria-valuemax={ 100 }
23
- sx={ { position: 'relative', display: 'inline-flex' } }
24
- >
25
- <CircularProgress
26
- variant="determinate"
27
- value={ 100 }
28
- size={ SCORE_CIRCLE_SIZE }
29
- thickness={ SCORE_CIRCLE_THICKNESS }
30
- sx={ { color: 'action.disabledBackground' } }
31
- />
32
- <CircularProgress
33
- variant="determinate"
34
- value={ score }
35
- size={ SCORE_CIRCLE_SIZE }
36
- thickness={ SCORE_CIRCLE_THICKNESS }
37
- color={ progressColor }
38
- sx={ { position: 'absolute', left: 0 } }
39
- />
40
- <Box
41
- sx={ {
42
- position: 'absolute',
43
- inset: 0,
44
- display: 'flex',
45
- alignItems: 'center',
46
- justifyContent: 'center',
47
- } }
48
- >
49
- <Typography
50
- variant="h4"
51
- component="span"
52
- color="text.primary"
53
- sx={ { fontWeight: 700, lineHeight: 1 } }
54
- >
55
- { score }
56
- </Typography>
57
- </Box>
58
- </Box>
59
- );
60
- }
@@ -1,57 +0,0 @@
1
- import * as React from 'react';
2
- import { Box, Chip } from '@elementor/ui';
3
- import { __ } from '@wordpress/i18n';
4
-
5
- import { type AuditSeverity } from '../types';
6
- import { ALL_SEVERITIES, severityPluralLabel } from '../utils/severity-counts';
7
- import SeverityIcon from './severity-icons';
8
-
9
- export type SeverityFilter = 'all' | AuditSeverity;
10
-
11
- type Props = {
12
- selected: SeverityFilter;
13
- onChange: ( filter: SeverityFilter ) => void;
14
- };
15
-
16
- export default function SeverityFilterChips( { selected, onChange }: Props ) {
17
- return (
18
- <Box sx={ { display: 'flex', alignItems: 'center', gap: 1 } }>
19
- <FilterChip
20
- label={ __( 'All', 'elementor' ) }
21
- isSelected={ selected === 'all' }
22
- onClick={ () => onChange( 'all' ) }
23
- />
24
- { ALL_SEVERITIES.map( ( severity ) => (
25
- <FilterChip
26
- key={ severity }
27
- label={ severityPluralLabel( severity ) }
28
- icon={ <SeverityIcon severity={ severity } /> }
29
- isSelected={ selected === severity }
30
- onClick={ () => onChange( severity ) }
31
- />
32
- ) ) }
33
- </Box>
34
- );
35
- }
36
-
37
- type FilterChipProps = {
38
- label: string;
39
- icon?: React.ReactElement;
40
- isSelected: boolean;
41
- onClick: () => void;
42
- };
43
-
44
- function FilterChip( { label, icon, isSelected, onClick }: FilterChipProps ) {
45
- return (
46
- <Chip
47
- label={ label }
48
- icon={ icon }
49
- size="small"
50
- variant="filled"
51
- color="default"
52
- onClick={ onClick }
53
- aria-pressed={ isSelected }
54
- sx={ { bgcolor: isSelected ? 'action.selected' : 'transparent' } }
55
- />
56
- );
57
- }
@@ -1,23 +0,0 @@
1
- import { __ } from '@wordpress/i18n';
2
-
3
- export const GOOD_THRESHOLD = 90;
4
- export const OK_THRESHOLD = 50;
5
-
6
- export type ScoreColor = 'success' | 'warning' | 'error';
7
-
8
- export type ScoreTier = {
9
- color: ScoreColor;
10
- label: string;
11
- };
12
-
13
- export function getScoreTier( score: number ): ScoreTier {
14
- if ( score >= GOOD_THRESHOLD ) {
15
- return { color: 'success', label: __( 'Good', 'elementor' ) };
16
- }
17
-
18
- if ( score >= OK_THRESHOLD ) {
19
- return { color: 'warning', label: __( 'Needs work', 'elementor' ) };
20
- }
21
-
22
- return { color: 'error', label: __( 'At risk', 'elementor' ) };
23
- }