Skip to content

Commit 81be980

Browse files
dkmytanateweller
andcommitted
Protect: Separate scan results and history DataViews (#40845)
* Init project branch * Protect: Refactor AdminSectionHero (#40516) * Restore history header * Pass status filter presets to consumer * Restore early return * Add plan level restrictions * Init project branch * Protect: Integrate ThreatsDataViews Component (#40076) * Separate scan and history DataViews * Reapply history routes * Add filters * Add toggle group control * Add historic flag * Update stories * Remove unneeded filter handling * Revert unnecessary threats data views updates * Fix import * Include fixer action as label in list view action dropdown * Add field constants * Reorg * Add initialFields prop, update active to current where applicable * Init project branch * Fix exports and imports * Move default values to function params * Fix ts warning * minor adjustment to definition of initial filters * Add tsc-expect-error to dummy arg for minification cases --------- Co-authored-by: Nate Weller <[email protected]> Co-authored-by: Nate Weller <[email protected]>
1 parent 8a6e93e commit 81be980

18 files changed

+390
-357
lines changed

projects/js-packages/components/components/threats-data-views/constants.ts

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,10 @@ import {
77
wordpress as coreIcon,
88
} from '@wordpress/icons';
99

10-
export const THREAT_STATUSES: { value: string; label: string; variant?: 'success' | 'warning' }[] =
11-
[
12-
{ value: 'current', label: __( 'Active', 'jetpack-components' ), variant: 'warning' },
13-
{ value: 'fixed', label: __( 'Fixed', 'jetpack-components' ), variant: 'success' },
14-
{ value: 'ignored', label: __( 'Ignored', 'jetpack-components' ) },
15-
];
10+
export const THREAT_STATUSES: { value: string; label: string; variant?: 'success' }[] = [
11+
{ value: 'fixed', label: __( 'Fixed', 'jetpack-components' ), variant: 'success' },
12+
{ value: 'ignored', label: __( 'Ignored', 'jetpack-components' ) },
13+
];
1614

1715
export const THREAT_TYPES = [
1816
{ value: 'plugins', label: __( 'Plugin', 'jetpack-components' ) },
@@ -45,6 +43,26 @@ export const THREAT_FIELD_FIRST_DETECTED = 'first-detected';
4543
export const THREAT_FIELD_FIXED_ON = 'fixed-on';
4644
export const THREAT_FIELD_AUTO_FIX = 'auto-fix';
4745

46+
export const CURRENT_TABLE_FIELDS = [
47+
THREAT_FIELD_SEVERITY,
48+
THREAT_FIELD_TYPE,
49+
THREAT_FIELD_AUTO_FIX,
50+
];
51+
52+
export const HISTORIC_TABLE_FIELDS = [
53+
THREAT_FIELD_SEVERITY,
54+
THREAT_FIELD_TYPE,
55+
THREAT_FIELD_FIRST_DETECTED,
56+
THREAT_FIELD_FIXED_ON,
57+
];
58+
59+
export const LIST_FIELDS = [
60+
THREAT_FIELD_SEVERITY,
61+
THREAT_FIELD_TYPE,
62+
THREAT_FIELD_EXTENSION,
63+
THREAT_FIELD_SIGNATURE,
64+
];
65+
4866
export const THREAT_ACTION_FIX = 'fix';
4967
export const THREAT_ACTION_IGNORE = 'ignore';
5068
export const THREAT_ACTION_UNIGNORE = 'unignore';

projects/js-packages/components/components/threats-data-views/index.tsx

Lines changed: 66 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { getThreatType, type Threat } from '@automattic/jetpack-scan';
1+
import { getFixerAction, getThreatType, type Threat } from '@automattic/jetpack-scan';
22
import {
33
type Action,
44
type ActionButton,
@@ -19,6 +19,8 @@ import Badge from '../badge';
1919
import ThreatFixerButton from '../threat-fixer-button';
2020
import ThreatSeverityBadge from '../threat-severity-badge';
2121
import {
22+
CURRENT_TABLE_FIELDS,
23+
LIST_FIELDS,
2224
THREAT_ACTION_FIX,
2325
THREAT_ACTION_IGNORE,
2426
THREAT_ACTION_UNIGNORE,
@@ -40,52 +42,62 @@ import {
4042
THREAT_TYPES,
4143
} from './constants';
4244
import styles from './styles.module.scss';
43-
import ThreatsStatusToggleGroupControl from './threats-status-toggle-group-control';
45+
46+
export { HISTORIC_TABLE_FIELDS } from './constants';
4447

4548
/**
4649
* DataViews component for displaying security threats.
4750
*
48-
* @param {object} props - Component props.
49-
* @param {Array} props.data - Threats data.
50-
* @param {Array} props.filters - Initial DataView filters.
51-
* @param {Function} props.onChangeSelection - Callback function run when an item is selected.
52-
* @param {Function} props.onFixThreats - Threat fix action callback.
53-
* @param {Function} props.onIgnoreThreats - Threat ignore action callback.
54-
* @param {Function} props.onUnignoreThreats - Threat unignore action callback.
55-
* @param {Function} props.isThreatEligibleForFix - Function to determine if a threat is eligible for fixing.
56-
* @param {Function} props.isThreatEligibleForIgnore - Function to determine if a threat is eligible for ignoring.
57-
* @param {Function} props.isThreatEligibleForUnignore - Function to determine if a threat is eligible for unignoring.
51+
* @param {object} props - Component props.
52+
* @param {string} props.status - Flag to indicate if the threats are current or historic.
53+
* @param {Array} props.data - Threats data.
54+
* @param {Array} props.initialFilters - Initial DataView filters.
55+
* @param {Array} props.initialFields - Initial DataView fields.
56+
* @param {Function} props.onChangeSelection - Callback function run when an item is selected.
57+
* @param {Function} props.onFixThreats - Threat fix action callback.
58+
* @param {Function} props.onIgnoreThreats - Threat ignore action callback.
59+
* @param {Function} props.onUnignoreThreats - Threat unignore action callback.
60+
* @param {Function} props.isThreatEligibleForFix - Function to determine if a threat is eligible for fixing.
61+
* @param {Function} props.isThreatEligibleForIgnore - Function to determine if a threat is eligible for ignoring.
62+
* @param {Function} props.isThreatEligibleForUnignore - Function to determine if a threat is eligible for unignoring.
63+
* @param {JSX.Element} props.header - Header component.
5864
*
5965
* @return {JSX.Element} The ThreatsDataViews component.
6066
*/
6167
export default function ThreatsDataViews( {
68+
status = 'current',
6269
data,
63-
filters,
70+
initialFields = CURRENT_TABLE_FIELDS,
71+
initialFilters = [],
6472
onChangeSelection,
6573
isThreatEligibleForFix,
6674
isThreatEligibleForIgnore,
6775
isThreatEligibleForUnignore,
6876
onFixThreats,
6977
onIgnoreThreats,
7078
onUnignoreThreats,
79+
header,
7180
}: {
81+
status?: string;
7282
data: Threat[];
73-
filters?: Filter[];
83+
initialFields?: string[];
84+
initialFilters?: Filter[];
7485
onChangeSelection?: ( selectedItemIds: string[] ) => void;
7586
isThreatEligibleForFix?: ( threat: Threat ) => boolean;
7687
isThreatEligibleForIgnore?: ( threat: Threat ) => boolean;
7788
isThreatEligibleForUnignore?: ( threat: Threat ) => boolean;
7889
onFixThreats?: ( threats: Threat[] ) => void;
7990
onIgnoreThreats?: ActionButton< Threat >[ 'callback' ];
8091
onUnignoreThreats?: ActionButton< Threat >[ 'callback' ];
92+
header?: JSX.Element;
8193
} ): JSX.Element {
8294
const baseView = {
8395
sort: {
8496
field: 'severity',
8597
direction: 'desc' as SortDirection,
8698
},
8799
search: '',
88-
filters: filters || [],
100+
filters: initialFilters,
89101
page: 1,
90102
perPage: 20,
91103
};
@@ -100,19 +112,14 @@ export default function ThreatsDataViews( {
100112
const defaultLayouts: SupportedLayouts = {
101113
table: {
102114
...baseView,
103-
fields: [ THREAT_FIELD_SEVERITY, THREAT_FIELD_TYPE, THREAT_FIELD_AUTO_FIX ],
115+
fields: initialFields,
104116
titleField: THREAT_FIELD_TITLE,
105117
descriptionField: THREAT_FIELD_DESCRIPTION,
106118
showMedia: false,
107119
},
108120
list: {
109121
...baseView,
110-
fields: [
111-
THREAT_FIELD_SEVERITY,
112-
THREAT_FIELD_TYPE,
113-
THREAT_FIELD_EXTENSION,
114-
THREAT_FIELD_SIGNATURE,
115-
],
122+
fields: LIST_FIELDS,
116123
titleField: THREAT_FIELD_TITLE,
117124
mediaField: THREAT_FIELD_ICON,
118125
showMedia: true,
@@ -238,28 +245,6 @@ export default function ThreatsDataViews( {
238245
);
239246
},
240247
},
241-
{
242-
id: THREAT_FIELD_STATUS,
243-
label: __( 'Status', 'jetpack-components' ),
244-
elements: THREAT_STATUSES,
245-
getValue( { item }: { item: Threat } ) {
246-
if ( ! item.status ) {
247-
return 'current';
248-
}
249-
return (
250-
THREAT_STATUSES.find( ( { value } ) => value === item.status )?.value ?? item.status
251-
);
252-
},
253-
render( { item }: { item: Threat } ) {
254-
if ( item.status ) {
255-
const status = THREAT_STATUSES.find( ( { value } ) => value === item.status );
256-
if ( status ) {
257-
return <Badge variant={ status?.variant }>{ status.label }</Badge>;
258-
}
259-
}
260-
return <Badge variant="warning">{ __( 'Active', 'jetpack-components' ) }</Badge>;
261-
},
262-
},
263248
{
264249
id: THREAT_FIELD_TYPE,
265250
label: __( 'Type', 'jetpack-components' ),
@@ -300,6 +285,35 @@ export default function ThreatsDataViews( {
300285
return item.extension ? item.extension.slug : '';
301286
},
302287
},
288+
...( 'historic' === status && dataFields.includes( 'status' )
289+
? [
290+
{
291+
id: THREAT_FIELD_STATUS,
292+
label: __( 'Status', 'jetpack-components' ),
293+
elements: THREAT_STATUSES,
294+
getValue( { item }: { item: Threat } ) {
295+
if ( ! item.status ) {
296+
return 'current';
297+
}
298+
return (
299+
THREAT_STATUSES.find( ( { value } ) => value === item.status )?.value ??
300+
item.status
301+
);
302+
},
303+
render( { item }: { item: Threat } ) {
304+
if ( item.status ) {
305+
const threatStatus = THREAT_STATUSES.find(
306+
( { value } ) => value === item.status
307+
);
308+
if ( threatStatus ) {
309+
return <Badge variant={ threatStatus?.variant }>{ threatStatus.label }</Badge>;
310+
}
311+
}
312+
return <Badge variant="warning">{ __( 'Current', 'jetpack-components' ) }</Badge>;
313+
},
314+
},
315+
]
316+
: [] ),
303317
...( dataFields.includes( 'severity' )
304318
? [
305319
{
@@ -366,7 +380,7 @@ export default function ThreatsDataViews( {
366380
},
367381
]
368382
: [] ),
369-
...( dataFields.includes( 'fixable' )
383+
...( 'historic' !== status && dataFields.includes( 'fixable' )
370384
? [
371385
{
372386
id: THREAT_FIELD_AUTO_FIX,
@@ -398,7 +412,7 @@ export default function ThreatsDataViews( {
398412
];
399413

400414
return result;
401-
}, [ dataFields, plugins, themes, signatures, onFixThreats ] );
415+
}, [ dataFields, plugins, themes, signatures, status, onFixThreats ] );
402416

403417
/**
404418
* DataView actions - collection of operations that can be performed upon each record.
@@ -408,10 +422,12 @@ export default function ThreatsDataViews( {
408422
const actions = useMemo( () => {
409423
const result: Action< Threat >[] = [];
410424

411-
if ( dataFields.includes( 'fixable' ) ) {
425+
if ( dataFields.includes( 'fixable' ) && view.type === 'list' ) {
412426
result.push( {
413427
id: THREAT_ACTION_FIX,
414-
label: __( 'Auto-fix', 'jetpack-components' ),
428+
label: items => {
429+
return getFixerAction( items[ 0 ] );
430+
},
415431
isPrimary: true,
416432
callback: onFixThreats,
417433
isEligible( item ) {
@@ -466,6 +482,7 @@ export default function ThreatsDataViews( {
466482

467483
return result;
468484
}, [
485+
view.type,
469486
dataFields,
470487
onFixThreats,
471488
onIgnoreThreats,
@@ -511,13 +528,7 @@ export default function ThreatsDataViews( {
511528
onChangeView={ onChangeView }
512529
paginationInfo={ paginationInfo }
513530
view={ view }
514-
header={
515-
<ThreatsStatusToggleGroupControl
516-
data={ data }
517-
view={ view }
518-
onChangeView={ onChangeView }
519-
/>
520-
}
531+
header={ header }
521532
/>
522533
);
523534
}

0 commit comments

Comments
 (0)