Skip to content

Commit c969a3c

Browse files
committed
Protect Meets Core
Protect: Add Go to Cloud and Scan now button to Protect primary header (#40057) Protect: Update Scan and History headers (#40058) Protect: de-emphasize cloud link by using link variant (#40211) Protect: Add ShieldIcon Component (#40402) Protect: Integrate ThreatsDataViews Component (#40076) Components: Add ScanReport (#40419) Protect: Refactor AdminSectionHero (#40516) Protect: Update Scan History extension types (#40548) Protect: Add Home page (#40317) Protect: Integrate ScanReport (#40420) ScanReport: Fix defaultLayout (#40603) Update onboarding popover placement (#40550) Protect Meets Core: Home Page Scan Report Data Adjustments (#40616) Scan Report: Align Status Icon (#40617) Apply max width to hero content (#40618) Protect: Hide Scan Report When No Data (#40619) Protect: Hide Threats Report When No Data (#40620) Protect: Update Threat Icons (#40621) Protect: fix home page stat card spacing (#40623) ScanReport: Disable hiding relevant fields (#40602) Use error variant (#40832) Protect: Restore HistoryAdminSectionHero (#40551) Protect: Separate scan results and history DataViews (#40845) ThreatsDataViews: Improve responsiveness (#40670) ThreatsDataViews: ThreatModal integration (#40202) Protect Meets Core: Fix "0" rendered by conditional render chain (#40882)
1 parent d3d9f82 commit c969a3c

File tree

150 files changed

+4845
-5684
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

150 files changed

+4845
-5684
lines changed

pnpm-lock.yaml

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Significance: minor
2+
Type: added
3+
4+
Integrates ThreatModal in ThreatsDataViews
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Significance: minor
2+
Type: added
3+
4+
Stat Card: add hideValue prop
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Significance: minor
2+
Type: added
3+
4+
Add ShieldIcon component
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Significance: minor
2+
Type: added
3+
4+
Adds ScanReport component
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { __ } from '@wordpress/i18n';
2+
import {
3+
code as fileIcon,
4+
color as themeIcon,
5+
plugins as pluginIcon,
6+
shield as shieldIcon,
7+
wordpress as coreIcon,
8+
} from '@wordpress/icons';
9+
10+
export const STATUS_TYPES = [
11+
{ value: 'checked', label: __( 'Checked', 'jetpack-components' ) },
12+
{ value: 'unchecked', label: __( 'Unchecked', 'jetpack-components' ) },
13+
{ value: 'threat', label: __( 'Threat', 'jetpack-components' ) },
14+
];
15+
16+
export const TYPES = [
17+
{ value: 'core', label: __( 'WordPress', 'jetpack-components' ) },
18+
{ value: 'plugins', label: __( 'Plugin', 'jetpack-components' ) },
19+
{ value: 'themes', label: __( 'Theme', 'jetpack-components' ) },
20+
{ value: 'files', label: __( 'Files', 'jetpack-components' ) },
21+
];
22+
23+
export const ICONS = {
24+
plugins: pluginIcon,
25+
themes: themeIcon,
26+
core: coreIcon,
27+
files: fileIcon,
28+
default: shieldIcon,
29+
};
30+
31+
export const FIELD_ICON = 'icon';
32+
export const FIELD_TYPE = 'type';
33+
export const FIELD_NAME = 'name';
34+
export const FIELD_STATUS = 'status';
35+
export const FIELD_UPDATE = 'update';
36+
export const FIELD_VERSION = 'version';
Lines changed: 248 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,248 @@
1+
import { type ScanReportExtension } from '@automattic/jetpack-scan';
2+
import { Tooltip } from '@wordpress/components';
3+
import {
4+
type SupportedLayouts,
5+
type View,
6+
type Field,
7+
DataViews,
8+
filterSortAndPaginate,
9+
} from '@wordpress/dataviews';
10+
import { __, _n } from '@wordpress/i18n';
11+
import { Icon } from '@wordpress/icons';
12+
import { useCallback, useMemo, useState } from 'react';
13+
import ShieldIcon from '../shield-icon/index.js';
14+
import {
15+
FIELD_NAME,
16+
FIELD_VERSION,
17+
FIELD_ICON,
18+
FIELD_STATUS,
19+
FIELD_TYPE,
20+
STATUS_TYPES,
21+
TYPES,
22+
ICONS,
23+
} from './constants.js';
24+
import styles from './styles.module.scss';
25+
26+
/**
27+
* DataViews component for displaying a scan report.
28+
*
29+
* @param {object} props - Component props.
30+
* @param {string} props.dataSource - Data source.
31+
* @param {Array} props.data - Scan report data.
32+
* @param {Function} props.onChangeSelection - Callback function run when an item is selected.
33+
*
34+
* @return {JSX.Element} The ScanReport component.
35+
*/
36+
export default function ScanReport( {
37+
dataSource,
38+
data,
39+
onChangeSelection,
40+
}: {
41+
dataSource: string;
42+
data: ScanReportExtension[];
43+
onChangeSelection: ( selected: string[] ) => void;
44+
} ): JSX.Element {
45+
const baseView = {
46+
search: '',
47+
filters: [],
48+
page: 1,
49+
perPage: 20,
50+
};
51+
52+
/**
53+
* DataView default layouts.
54+
*
55+
* This property provides layout information about the view types that are active. If empty, enables all layout types (see “Layout Types”) with empty layout data.
56+
*
57+
* @see https://developer.wordpress.org/block-editor/reference-guides/packages/packages-dataviews/#defaultlayouts-record-string-view
58+
*/
59+
const defaultLayouts: SupportedLayouts = {
60+
table: {
61+
...baseView,
62+
fields: [ FIELD_TYPE, FIELD_NAME, FIELD_VERSION ],
63+
titleField: FIELD_STATUS,
64+
showMedia: false,
65+
},
66+
list: {
67+
...baseView,
68+
fields: [ FIELD_STATUS, FIELD_VERSION, FIELD_TYPE ],
69+
titleField: FIELD_NAME,
70+
mediaField: FIELD_ICON,
71+
showMedia: true,
72+
},
73+
};
74+
75+
/**
76+
* DataView view object - configures how the dataset is visible to the user.
77+
*
78+
* @see https://developer.wordpress.org/block-editor/reference-guides/packages/packages-dataviews/#view-object
79+
*/
80+
const [ view, setView ] = useState< View >( {
81+
type: 'table',
82+
...defaultLayouts.table,
83+
} );
84+
85+
/**
86+
* DataView fields - describes the visible items for each record in the dataset.
87+
*
88+
* @see https://developer.wordpress.org/block-editor/reference-guides/packages/packages-dataviews/#fields-object
89+
*/
90+
const fields = useMemo( () => {
91+
const iconHeight = 20;
92+
const result: Field< ScanReportExtension >[] = [
93+
{
94+
id: FIELD_STATUS,
95+
elements: STATUS_TYPES,
96+
label: __( 'Status', 'jetpack-components' ),
97+
enableHiding: false,
98+
getValue( { item } ) {
99+
if ( item.checked ) {
100+
if ( item.threats.length > 0 ) {
101+
return 'threat';
102+
}
103+
return 'checked';
104+
}
105+
return 'unchecked';
106+
},
107+
render( { item }: { item: ScanReportExtension } ) {
108+
const scanApi = 'scan_api' === dataSource;
109+
let variant: 'info' | 'error' | 'success' = 'info';
110+
let text = __(
111+
'This item was added to your site after the most recent scan. We will check for threats during the next scheduled one.',
112+
'jetpack-components'
113+
);
114+
115+
if ( item.checked ) {
116+
if ( item.threats.length > 0 ) {
117+
variant = 'error';
118+
text = _n(
119+
'Vulnerability detected.',
120+
'Vulnerabilities detected.',
121+
item.threats.length,
122+
'jetpack-components'
123+
);
124+
125+
if ( scanApi ) {
126+
text = _n(
127+
'Threat detected.',
128+
'Threats detected.',
129+
item.threats.length,
130+
'jetpack-components'
131+
);
132+
}
133+
} else {
134+
variant = 'success';
135+
text = __(
136+
'No known vulnerabilities found that affect this version.',
137+
'jetpack-components'
138+
);
139+
140+
if ( scanApi ) {
141+
text = __(
142+
'No known threats found that affect this version.',
143+
'jetpack-components'
144+
);
145+
}
146+
}
147+
}
148+
149+
return (
150+
<Tooltip className={ styles.tooltip } text={ text }>
151+
<div className={ styles.icon }>
152+
<ShieldIcon variant={ variant } height={ iconHeight } />
153+
</div>
154+
</Tooltip>
155+
);
156+
},
157+
},
158+
{
159+
id: FIELD_TYPE,
160+
label: __( 'Type', 'jetpack-components' ),
161+
elements: TYPES,
162+
enableHiding: false,
163+
},
164+
{
165+
id: FIELD_NAME,
166+
label: __( 'Name', 'jetpack-components' ),
167+
enableHiding: false,
168+
enableGlobalSearch: true,
169+
getValue( { item }: { item: ScanReportExtension } ) {
170+
return item.name ? item.name : '';
171+
},
172+
},
173+
{
174+
id: FIELD_VERSION,
175+
label: __( 'Version', 'jetpack-components' ),
176+
enableHiding: false,
177+
enableSorting: false,
178+
enableGlobalSearch: true,
179+
getValue( { item }: { item: ScanReportExtension } ) {
180+
return item.version ? item.version : '';
181+
},
182+
},
183+
...( view.type === 'list'
184+
? [
185+
{
186+
id: FIELD_ICON,
187+
label: __( 'Icon', 'jetpack-components' ),
188+
enableSorting: false,
189+
enableHiding: false,
190+
getValue( { item }: { item: ScanReportExtension } ) {
191+
return ICONS[ item.type ] || '';
192+
},
193+
render( { item }: { item: ScanReportExtension } ) {
194+
return (
195+
<div className={ styles.threat__media }>
196+
<Icon icon={ ICONS[ item.type ] } />
197+
</div>
198+
);
199+
},
200+
},
201+
]
202+
: [] ),
203+
];
204+
205+
return result;
206+
}, [ view, dataSource ] );
207+
208+
/**
209+
* Apply the view settings (i.e. filters, sorting, pagination) to the dataset.
210+
*
211+
* @see https://github.com/WordPress/gutenberg/blob/trunk/packages/dataviews/src/filter-and-sort-data-view.ts
212+
*/
213+
const { data: processedData, paginationInfo } = useMemo( () => {
214+
return filterSortAndPaginate( data, view, fields );
215+
}, [ data, view, fields ] );
216+
217+
/**
218+
* Callback function to update the view state.
219+
*
220+
* @see https://developer.wordpress.org/block-editor/reference-guides/packages/packages-dataviews/#onchangeview-function
221+
*/
222+
const onChangeView = useCallback( ( newView: View ) => {
223+
setView( newView );
224+
}, [] );
225+
226+
/**
227+
* DataView getItemId function - returns the unique ID for each record in the dataset.
228+
*
229+
* @see https://developer.wordpress.org/block-editor/reference-guides/packages/packages-dataviews/#getitemid-function
230+
*/
231+
const getItemId = useCallback(
232+
( item: ScanReportExtension ) => `${ item.type }_${ item.slug }_${ item.version }`,
233+
[]
234+
);
235+
236+
return (
237+
<DataViews
238+
data={ processedData }
239+
defaultLayouts={ defaultLayouts }
240+
fields={ fields }
241+
getItemId={ getItemId }
242+
onChangeSelection={ onChangeSelection }
243+
onChangeView={ onChangeView }
244+
paginationInfo={ paginationInfo }
245+
view={ view }
246+
/>
247+
);
248+
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import ScanReport from '../index.js';
2+
3+
export default {
4+
title: 'JS Packages/Components/Scan Report',
5+
component: ScanReport,
6+
parameters: {
7+
backgrounds: {
8+
default: 'light',
9+
values: [ { name: 'light', value: 'white' } ],
10+
},
11+
},
12+
decorators: [
13+
Story => (
14+
<div style={ { maxWidth: '100%', backgroundColor: 'white' } }>
15+
<Story />
16+
</div>
17+
),
18+
],
19+
};
20+
21+
export const Default = args => <ScanReport { ...args } />;
22+
Default.args = {
23+
dataSource: 'scan_api',
24+
data: [
25+
{
26+
id: 1,
27+
name: 'WordPress',
28+
slug: null,
29+
version: '6.7.1',
30+
threats: [],
31+
checked: true,
32+
type: 'core',
33+
},
34+
{
35+
id: 2,
36+
name: 'Jetpack',
37+
slug: 'jetpack/jetpack.php',
38+
version: '14.1-a.7',
39+
threats: [],
40+
checked: false,
41+
type: 'plugins',
42+
},
43+
{
44+
id: 3,
45+
name: 'Twenty Fifteen',
46+
slug: 'twentyfifteen',
47+
version: '1.1',
48+
threats: [
49+
{
50+
id: 198352527,
51+
signature: 'Vulnerable.WP.Extension',
52+
description: 'Vulnerable WordPress extension',
53+
severity: 3,
54+
},
55+
],
56+
checked: true,
57+
type: 'themes',
58+
},
59+
{
60+
id: 4,
61+
threats: [
62+
{
63+
id: 198352406,
64+
signature: 'EICAR_AV_Test_Suspicious',
65+
title: 'Malicious code found in file: jptt_eicar.php',
66+
severity: 1,
67+
},
68+
{
69+
id: 198352407,
70+
signature: 'EICAR_AV_Test_Suspicious',
71+
title: 'Malicious code found in file: jptt_eicar.php',
72+
severity: 1,
73+
},
74+
],
75+
checked: true,
76+
type: 'files',
77+
},
78+
],
79+
};

0 commit comments

Comments
 (0)