Skip to content

Commit

Permalink
Components: Add ScanReport (#40419)
Browse files Browse the repository at this point in the history
  • Loading branch information
dkmyta authored Dec 5, 2024
1 parent 1118627 commit 5ba7582
Show file tree
Hide file tree
Showing 10 changed files with 360 additions and 13 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: minor
Type: added

Adds ScanReport component
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { __ } from '@wordpress/i18n';
import {
code as fileIcon,
color as themeIcon,
plugins as pluginIcon,
shield as shieldIcon,
wordpress as coreIcon,
} from '@wordpress/icons';

export const TYPES = [
{ value: 'core', label: __( 'WordPress', 'jetpack-components' ) },
{ value: 'plugins', label: __( 'Plugin', 'jetpack-components' ) },
{ value: 'themes', label: __( 'Theme', 'jetpack-components' ) },
{ value: 'files', label: __( 'Files', 'jetpack-components' ) },
];

export const ICONS = {
plugins: pluginIcon,
themes: themeIcon,
core: coreIcon,
files: fileIcon,
default: shieldIcon,
};

export const FIELD_ICON = 'icon';
export const FIELD_TYPE = 'type';
export const FIELD_NAME = 'name';
export const FIELD_STATUS = 'status';
export const FIELD_UPDATE = 'update';
export const FIELD_VERSION = 'version';
197 changes: 197 additions & 0 deletions projects/js-packages/components/components/scan-report/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
import { type ScanReportExtension } from '@automattic/jetpack-scan';
import { Tooltip } from '@wordpress/components';
import {
type SupportedLayouts,
type View,
type Field,
DataViews,
filterSortAndPaginate,
} from '@wordpress/dataviews';
import { __ } from '@wordpress/i18n';
import { Icon } from '@wordpress/icons';
import { useCallback, useMemo, useState } from 'react';
import ShieldIcon from '../shield-icon';
import {
FIELD_NAME,
FIELD_VERSION,
FIELD_ICON,
FIELD_STATUS,
FIELD_TYPE,
TYPES,
ICONS,
} from './constants';
import styles from './styles.module.scss';

/**
* DataViews component for displaying a scan report.
*
* @param {object} props - Component props.
* @param {Array} props.data - Scan report data.
* @param {Function} props.onChangeSelection - Callback function run when an item is selected.
*
* @return {JSX.Element} The ScanReport component.
*/
export default function ScanReport( { data, onChangeSelection } ): JSX.Element {
const baseView = {
search: '',
filters: [],
page: 1,
perPage: 20,
};

/**
* DataView default layouts.
*
* 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.
*
* @see https://developer.wordpress.org/block-editor/reference-guides/packages/packages-dataviews/#defaultlayouts-record-string-view
*/
const defaultLayouts: SupportedLayouts = {
table: {
...baseView,
fields: [ FIELD_STATUS, FIELD_TYPE, FIELD_NAME, FIELD_VERSION ],
layout: {
primaryField: FIELD_STATUS,
},
},
list: {
...baseView,
fields: [ FIELD_STATUS, FIELD_VERSION ],
layout: {
primaryField: FIELD_NAME,
mediaField: FIELD_ICON,
},
},
};

/**
* DataView view object - configures how the dataset is visible to the user.
*
* @see https://developer.wordpress.org/block-editor/reference-guides/packages/packages-dataviews/#view-object
*/
const [ view, setView ] = useState< View >( {
type: 'table',
...defaultLayouts.table,
} );

/**
* DataView fields - describes the visible items for each record in the dataset.
*
* @see https://developer.wordpress.org/block-editor/reference-guides/packages/packages-dataviews/#fields-object
*/
const fields = useMemo( () => {
const iconHeight = 20;
const result: Field< ScanReportExtension >[] = [
{
id: FIELD_STATUS,
label: __( 'Status', 'jetpack-components' ),
render( { item }: { item: ScanReportExtension } ) {
let variant: 'info' | 'warning' | 'success' = 'info';
let text = __(
'This item was added to your site after the most recent scan. We will check for threats during the next scheduled one.',
'jetpack-components'
);

if ( item.checked ) {
if ( item.threats.length > 0 ) {
variant = 'warning';
text = __( 'Threat detected.', 'jetpack-components' );
} else {
variant = 'success';
text = __( 'No known threats found that affect this version.', 'jetpack-components' );
}
}

return (
<Tooltip className={ styles.tooltip } text={ text }>
<div className={ styles.icon }>
<ShieldIcon variant={ variant } height={ iconHeight } />
</div>
</Tooltip>
);
},
},
{
id: FIELD_TYPE,
label: __( 'Type', 'jetpack-components' ),
elements: TYPES,
},
{
id: FIELD_NAME,
label: __( 'Name', 'jetpack-components' ),
enableGlobalSearch: true,
getValue( { item }: { item: ScanReportExtension } ) {
return item.name ? item.name : '';
},
},
{
id: FIELD_VERSION,
label: __( 'Version', 'jetpack-components' ),
enableGlobalSearch: true,
getValue( { item }: { item: ScanReportExtension } ) {
return item.version ? item.version : '';
},
},
...( view.type === 'list'
? [
{
id: FIELD_ICON,
label: __( 'Icon', 'jetpack-components' ),
enableSorting: false,
enableHiding: false,
getValue( { item }: { item: ScanReportExtension } ) {
return ICONS[ item.type ] || '';
},
render( { item }: { item: ScanReportExtension } ) {
return (
<div className={ styles.threat__media }>
<Icon icon={ ICONS[ item.type ] } />
</div>
);
},
},
]
: [] ),
];

return result;
}, [ view ] );

/**
* Apply the view settings (i.e. filters, sorting, pagination) to the dataset.
*
* @see https://github.com/WordPress/gutenberg/blob/trunk/packages/dataviews/src/filter-and-sort-data-view.ts
*/
const { data: processedData, paginationInfo } = useMemo( () => {
return filterSortAndPaginate( data, view, fields );
}, [ data, view, fields ] );

/**
* Callback function to update the view state.
*
* @see https://developer.wordpress.org/block-editor/reference-guides/packages/packages-dataviews/#onchangeview-function
*/
const onChangeView = useCallback( ( newView: View ) => {
setView( newView );
}, [] );

/**
* DataView getItemId function - returns the unique ID for each record in the dataset.
*
* @see https://developer.wordpress.org/block-editor/reference-guides/packages/packages-dataviews/#getitemid-function
*/
const getItemId = useCallback( ( item: ScanReportExtension ) => item.id.toString(), [] );

return (
<DataViews
data={ processedData }
defaultLayouts={ defaultLayouts }
fields={ fields }
getItemId={ getItemId }
onChangeSelection={ onChangeSelection }
onChangeView={ onChangeView }
paginationInfo={ paginationInfo }
view={ view }
/>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import ScanReport from '..';

export default {
title: 'JS Packages/Components/Scan Report',
component: ScanReport,
parameters: {
backgrounds: {
default: 'light',
values: [ { name: 'light', value: 'white' } ],
},
},
decorators: [
Story => (
<div style={ { maxWidth: '100%', backgroundColor: 'white' } }>
<Story />
</div>
),
],
};

export const Default = args => <ScanReport { ...args } />;
Default.args = {
data: [
{
id: 1,
name: 'WordPress',
slug: null,
version: '6.7.1',
threats: [],
checked: true,
type: 'core',
},
{
id: 2,
name: 'Jetpack',
slug: 'jetpack/jetpack.php',
version: '14.1-a.7',
threats: [],
checked: false,
type: 'plugins',
},
{
id: 3,
name: 'Twenty Fifteen',
slug: 'twentyfifteen',
version: '1.1',
threats: [
{
id: 198352527,
signature: 'Vulnerable.WP.Extension',
description: 'Vulnerable WordPress extension',
severity: 3,
},
],
checked: true,
type: 'themes',
},
{
id: 4,
threats: [
{
id: 198352406,
signature: 'EICAR_AV_Test_Suspicious',
title: 'Malicious code found in file: jptt_eicar.php',
severity: 1,
},
],
checked: true,
type: 'files',
},
],
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
@import '@wordpress/dataviews/build-style/style.css';

.threat__media {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
background-color: #EDFFEE;
border-color: #EDFFEE;

svg {
fill: var( --jp-black );
}
}

.tooltip {
max-width: 240px;
border-radius: 4px;
text-align: left;
}
14 changes: 9 additions & 5 deletions projects/js-packages/components/components/shield-icon/index.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import React from 'react';

const COLORS = {
error: '#D63638',
warning: '#F0B849',
success: '#069E08',
default: '#1d2327',
info: '#A7AAAD',
success: '#069E08',
warning: '#F0B849',
error: '#D63638',
};

/**
Expand Down Expand Up @@ -32,11 +33,11 @@ export default function ShieldIcon( {
}: {
className?: string;
contrast?: string;
fill?: 'default' | 'success' | 'warning' | 'error' | string;
fill?: 'default' | 'info' | 'success' | 'warning' | 'error' | string;
height?: number;
icon?: 'success' | 'error';
outline?: boolean;
variant: 'default' | 'success' | 'warning' | 'error';
variant: 'default' | 'info' | 'success' | 'warning' | 'error';
} ): JSX.Element {
const shieldFill = COLORS[ fill ] || fill || COLORS[ variant ];
const iconFill = outline ? shieldFill : contrast;
Expand All @@ -60,6 +61,9 @@ export default function ShieldIcon( {
}
fill={ shieldFill }
/>
{ 'info' === iconVariant && (
<path d="M33.7 69H41.5V45.6H33.7V69ZM33.7 37.8H41.5V30H33.7V37.8Z" fill={ iconFill } />
) }
{ 'success' === iconVariant && (
<path
fillRule="evenodd"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,13 @@ export default {
control: {
type: 'select',
},
options: [ 'default', 'success', 'warning', 'error' ],
options: [ 'default', 'info', 'success', 'warning', 'error' ],
},
icon: {
control: {
type: 'select',
},
options: [ 'success', 'error' ],
options: [ 'info', 'success', 'error' ],
},
fill: {
control: 'color',
Expand All @@ -39,12 +39,14 @@ export const Variants = () => {
<div style={ { display: 'flex', flexDirection: 'column', gap: '8px' } }>
<div style={ { display: 'flex', gap: '8px' } }>
<ShieldIcon variant="default" />
<ShieldIcon variant="info" />
<ShieldIcon variant="success" />
<ShieldIcon variant="warning" />
<ShieldIcon variant="error" />
</div>
<div style={ { display: 'flex', gap: '8px' } }>
<ShieldIcon variant="default" outline />
<ShieldIcon variant="info" outline />
<ShieldIcon variant="success" outline />
<ShieldIcon variant="warning" outline />
<ShieldIcon variant="error" outline />
Expand Down
Loading

0 comments on commit 5ba7582

Please sign in to comment.