Skip to content

Commit

Permalink
Updates
Browse files Browse the repository at this point in the history
  • Loading branch information
dkmyta committed Oct 28, 2024
1 parent 8829ea3 commit 428e35c
Showing 1 changed file with 71 additions and 57 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { ThreatStatus, type Threat } from '@automattic/jetpack-scan';
import {
Button,
__experimentalToggleGroupControl as ToggleGroupControl, // eslint-disable-line @wordpress/no-unsafe-wp-apis
__experimentalToggleGroupControlOption as ToggleGroupControlOption, // eslint-disable-line @wordpress/no-unsafe-wp-apis
} from '@wordpress/components';
Expand All @@ -17,7 +18,6 @@ import {
} from '@wordpress/dataviews';
import { dateI18n } from '@wordpress/date';
import { __, sprintf } from '@wordpress/i18n';
import { tool, unseen, seen } from '@wordpress/icons';
import { Icon } from '@wordpress/icons';
import { useCallback, useMemo, useState } from 'react';
import Badge from '../badge';
Expand Down Expand Up @@ -100,7 +100,6 @@ export function ThreatsStatusToggleGroupControl( {
* @param {object} props - Component props.
* @param {Array} props.data - Threats data.
* @param {Array} props.filters - Initial DataView filters.
* @param {Function} props.onChangeSelection - Callback function run when an item is selected.
* @param {Function} props.onFixThreats - Threat fix action callback.
* @param {Function} props.onIgnoreThreats - Threat ignore action callback.
* @param {Function} props.onUnignoreThreats - Threat unignore action callback.
Expand All @@ -113,7 +112,6 @@ export function ThreatsStatusToggleGroupControl( {
export default function ThreatsDataViews( {
data,
filters,
onChangeSelection,
isThreatEligibleForFix,
isThreatEligibleForIgnore,
isThreatEligibleForUnignore,
Expand All @@ -123,7 +121,6 @@ export default function ThreatsDataViews( {
}: {
data: Threat[];
filters?: Filter[];
onChangeSelection?: ( selectedItemIds: string[] ) => void;
isThreatEligibleForFix?: ( threat: Threat ) => boolean;
isThreatEligibleForIgnore?: ( threat: Threat ) => boolean;
isThreatEligibleForUnignore?: ( threat: Threat ) => boolean;
Expand Down Expand Up @@ -167,6 +164,11 @@ export default function ThreatsDataViews( {
},
};

/**
* DataView selection object - stores the selected item IDs.
*/
const [ selection, setSelection ] = useState< string[] >( [] );

/**
* DataView view object - configures how the dataset is visible to the user.
*
Expand All @@ -177,24 +179,54 @@ export default function ThreatsDataViews( {
...defaultLayouts.table,
} );

/**
* Memoized function to determine if a status filter is selected.
*
* @param {Array} threatStatuses - List of threat statuses.
*/
const isStatusFilterSelected = useMemo(
() => ( threatStatuses: ThreatStatus[] ) =>
view.filters.some(
filter =>
filter.field === 'status' &&
Array.isArray( filter.value ) &&
filter.value.length === threatStatuses.length &&
threatStatuses.every( threatStatus => filter.value.includes( threatStatus ) )
),
[ view.filters ]
);

/**
* Compute values from the provided threats data.
*
* @member {Array} activeThreatIds - List of active threat IDs.
* @member {Array} historicThreatIds - List of historic threat IDs.
* @member {object} extensions - List of unique threat extensions.
* @member {object} signatures - List of unique threat signatures.
* @member {Array} dataFields - List of unique fields.
*/
const {
activeThreatIds,
historicThreatIds,
extensions,
signatures,
dataFields,
}: {
activeThreatIds: string[];
historicThreatIds: string[];
extensions: { value: string; label: string }[];
signatures: { value: string; label: string }[];
dataFields: string[];
} = useMemo( () => {
return data.reduce(
( acc, threat ) => {
// Active/Historic Threat IDs
if ( threat.status === 'current' ) {
acc.activeThreatIds.push( threat.id );
} else {
acc.historicThreatIds.push( threat.id );
}

// Extensions
if ( threat.extension ) {
if ( ! acc.extensions.find( ( { value } ) => value === threat.extension.slug ) ) {
Expand Down Expand Up @@ -224,6 +256,8 @@ export default function ThreatsDataViews( {
return acc;
},
{
activeThreatIds: [],
historicThreatIds: [],
extensions: [],
signatures: [],
dataFields: [],
Expand Down Expand Up @@ -439,19 +473,6 @@ export default function ThreatsDataViews( {
return result;
}, [ extensions, signatures, dataFields, view ] );

const isStatusFilterSelected = ( threatStatuses: ThreatStatus[] ) =>
view.filters.some(
filter =>
filter.field === 'status' &&
Array.isArray( filter.value ) &&
filter.value.length === threatStatuses.length &&
threatStatuses.every( threatStatus => filter.value.includes( threatStatus ) )
);

const isViewingActiveThreats = isStatusFilterSelected( [ 'current' ] );
const isViewingIgnoredThreats = isStatusFilterSelected( [ 'ignored' ] );
const isViewingHistoricThreats = isStatusFilterSelected( [ 'fixed', 'ignored' ] );

/**
* DataView actions - collection of operations that can be performed upon each record.
*
Expand All @@ -463,17 +484,15 @@ export default function ThreatsDataViews( {
if ( dataFields.includes( 'fixable' ) ) {
result.push( {
id: 'fix',
label: __( 'Auto-Fix', 'jetpack' ),
icon: tool,
supportsBulk: isViewingActiveThreats,
label: __( 'Auto-fix', 'jetpack' ),
supportsBulk: true,
callback: items => onFixThreats( items.map( item => item.id ) ),
isEligible( item ) {
if ( ! onFixThreats ) {
// TODO: Ensure we continue to handle this properly?
return false;
}
if ( isThreatEligibleForFix ) {
// TODO: Should not be able to bulk select or individually select in_progress/errored fixers
// TODO: Should not be able to bulk select or individually select threats with in_progress/errored fixers
return isThreatEligibleForFix( item );
}
return !! item.fixable;
Expand All @@ -485,17 +504,12 @@ export default function ThreatsDataViews( {
result.push( {
id: 'ignore',
label: __( 'Ignore', 'jetpack' ),
icon: unseen,
isDestructive: true,
supportsBulk: isViewingActiveThreats,
callback: items => onIgnoreThreats( items.map( item => item.id ) ),
isEligible( item ) {
if ( ! onIgnoreThreats ) {
// TODO: Ensure we continue to handle this properly?
return false;
}
if ( isThreatEligibleForIgnore ) {
// TODO: Should not be able to bulk select or individually select errored fixers
return isThreatEligibleForIgnore( item );
}
return item.status === 'current';
Expand All @@ -507,17 +521,12 @@ export default function ThreatsDataViews( {
result.push( {
id: 'un-ignore',
label: __( 'Unignore', 'jetpack' ),
isDestructive: true,
icon: seen,
supportsBulk: isViewingIgnoredThreats || isViewingHistoricThreats,
callback: items => onUnignoreThreats( items.map( item => item.id ) ),
isEligible( item ) {
if ( ! onUnignoreThreats ) {
// TODO: Ensure we continue to handle this properly?
return false;
}
if ( isThreatEligibleForUnignore ) {
// TODO: Should not be able to bulk select or individually select errored fixers
return isThreatEligibleForUnignore( item );
}
return item.status === 'ignored';
Expand All @@ -528,9 +537,6 @@ export default function ThreatsDataViews( {
return result;
}, [
dataFields,
isViewingActiveThreats,
isViewingIgnoredThreats,
isViewingHistoricThreats,
onFixThreats,
onIgnoreThreats,
onUnignoreThreats,
Expand All @@ -548,6 +554,15 @@ export default function ThreatsDataViews( {
return filterSortAndPaginate( data, view, fields );
}, [ data, view, fields ] );

/**
* Callback function to update the selection state.
*
* @see https://developer.wordpress.org/block-editor/reference-guides/packages/packages-dataviews/#onchangeselection-function
*/
const onChangeSelection = useCallback( ( selectedItemIds: string[] ) => {
setSelection( selectedItemIds );
}, [] );

/**
* Callback function to update the view state.
*
Expand Down Expand Up @@ -595,21 +610,9 @@ export default function ThreatsDataViews( {
[ view ]
);

/**
* Compute the number of active and historic threats.
*/
const activeThreatsCount = useMemo(
() => data.filter( item => item.status === 'current' ).length,
[ data ]
);

/**
* Compute the number of active and historic threats.
*/
const historicThreatsCount = useMemo(
() => data.filter( item => [ 'fixed', 'ignored' ].includes( item.status ) ).length,
[ data ]
);
const handleBulkFixThreatsClick = useCallback( () => {
onFixThreats( selection );
}, [ selection, onFixThreats ] );

return (
<DataViews
Expand All @@ -623,13 +626,24 @@ export default function ThreatsDataViews( {
paginationInfo={ paginationInfo }
view={ view }
header={
<ThreatsStatusToggleGroupControl
activeThreatsCount={ activeThreatsCount }
historicThreatsCount={ historicThreatsCount }
isViewingActiveThreats={ isViewingActiveThreats }
isViewingHistoricThreats={ isViewingHistoricThreats }
onStatusFilterChange={ onStatusFilterChange }
/>
<>
<ThreatsStatusToggleGroupControl
activeThreatsCount={ activeThreatIds.length }
historicThreatsCount={ historicThreatIds.length }
isViewingActiveThreats={ isStatusFilterSelected( [ 'current' ] ) }
isViewingHistoricThreats={ isStatusFilterSelected( [ 'fixed', 'ignored' ] ) }
onStatusFilterChange={ onStatusFilterChange }
/>
{ selection.length > 0 && (
<Button variant={ 'secondary' } onClick={ handleBulkFixThreatsClick }>
{ sprintf(
/* translators: %d: number of selected threats */
__( 'Auto-fix (%d)', 'jetpack' ),
selection.length
) }
</Button>
) }
</>
}
/>
);
Expand Down

0 comments on commit 428e35c

Please sign in to comment.