-
Notifications
You must be signed in to change notification settings - Fork 382
Improve post Site Scan action items #6698
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
af79d44
62aa5b0
617713a
074f845
fd06053
d24f6b4
3621298
253e5fd
87e609c
1d60b17
310ba0f
21fda0e
cd03b79
5a16e08
0de088d
7114e14
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
/** | ||
* Check if the provided URL is external. | ||
* | ||
* @param {string} url URL to be checked. | ||
* @return {boolean} True if the URL is external, false otherwise. | ||
*/ | ||
export const isExternalUrl = ( url ) => global?.location?.host !== new URL( url ).host; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
/** | ||
* Internal dependencies | ||
*/ | ||
import { isExternalUrl } from '../is-external-url'; | ||
|
||
describe( 'isExternalUrl', () => { | ||
it( 'should return true if of a URL is external and false otherwise', () => { | ||
expect( isExternalUrl( 'https://example.com/' ) ).toBe( true ); | ||
expect( isExternalUrl( 'https://localhost/' ) ).toBe( false ); | ||
} ); | ||
} ); |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,6 +3,7 @@ | |
*/ | ||
import { createContext, useEffect, useState, useRef, useCallback, useContext } from '@wordpress/element'; | ||
import apiFetch from '@wordpress/api-fetch'; | ||
import { addQueryArgs } from '@wordpress/url'; | ||
|
||
/** | ||
* External dependencies | ||
|
@@ -38,7 +39,9 @@ function waitASecond() { | |
*/ | ||
export function OptionsContextProvider( { children, optionsRestPath, populateDefaultValues, hasErrorBoundary = false, delaySave = false } ) { | ||
const [ updates, setUpdates ] = useState( {} ); | ||
const [ fetchingPluginSuppression, setFetchingPluginSuppression ] = useState( false ); | ||
const [ fetchingOptions, setFetchingOptions ] = useState( null ); | ||
const [ savedOptions, setSavedOptions ] = useState( {} ); | ||
const [ savingOptions, setSavingOptions ] = useState( false ); | ||
const [ didSaveOptions, setDidSaveOptions ] = useState( false ); | ||
const [ originalOptions, setOriginalOptions ] = useState( {} ); | ||
|
@@ -55,6 +58,52 @@ export function OptionsContextProvider( { children, optionsRestPath, populateDef | |
hasUnmounted.current = true; | ||
}, [] ); | ||
|
||
/** | ||
* Fetches plugin suppression data. | ||
*/ | ||
const refetchPluginSuppression = useCallback( () => { | ||
if ( error || fetchingPluginSuppression ) { | ||
return; | ||
} | ||
|
||
/** | ||
* Fetches suppression data from the REST endpoint. | ||
*/ | ||
( async () => { | ||
setFetchingPluginSuppression( true ); | ||
|
||
try { | ||
const fetchedPluginSuppression = await apiFetch( { | ||
path: addQueryArgs( optionsRestPath, { | ||
_fields: [ 'suppressed_plugins', 'suppressible_plugins' ], | ||
} ), | ||
} ); | ||
|
||
if ( true === hasUnmounted.current ) { | ||
return; | ||
} | ||
|
||
setOriginalOptions( { | ||
...originalOptions, | ||
...fetchedPluginSuppression, | ||
} ); | ||
Comment on lines
+86
to
+89
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is great. I was thinking that this could have the effect of overriding a user's unsaved changes to plugin suppression, but this won't be the case the original options are updated, not the modified otpions. |
||
} catch ( e ) { | ||
if ( true === hasUnmounted.current ) { | ||
return; | ||
} | ||
|
||
setError( e ); | ||
|
||
if ( hasErrorBoundary ) { | ||
setAsyncError( e ); | ||
} | ||
return; | ||
} | ||
|
||
setFetchingPluginSuppression( false ); | ||
} )(); | ||
}, [ error, fetchingPluginSuppression, hasErrorBoundary, optionsRestPath, originalOptions, setAsyncError, setError ] ); | ||
|
||
/** | ||
* Fetches options. | ||
*/ | ||
|
@@ -129,7 +178,7 @@ export function OptionsContextProvider( { children, optionsRestPath, populateDef | |
|
||
// Ensure this promise lasts at least a second so that the "Saving Options" load screen is | ||
// visible long enough for the user to see it is happening. | ||
const [ savedOptions ] = await Promise.all( | ||
const [ retrievedOptions ] = await Promise.all( | ||
[ | ||
apiFetch( | ||
{ | ||
|
@@ -146,7 +195,7 @@ export function OptionsContextProvider( { children, optionsRestPath, populateDef | |
return; | ||
} | ||
|
||
setOriginalOptions( savedOptions ); | ||
setOriginalOptions( retrievedOptions ); | ||
setError( null ); | ||
} catch ( e ) { | ||
if ( true === hasUnmounted.current ) { | ||
|
@@ -164,6 +213,7 @@ export function OptionsContextProvider( { children, optionsRestPath, populateDef | |
} | ||
|
||
setModifiedOptions( { ...modifiedOptions, ...updates } ); | ||
setSavedOptions( updates ); | ||
|
||
setUpdates( {} ); | ||
setDidSaveOptions( true ); | ||
|
@@ -198,10 +248,12 @@ export function OptionsContextProvider( { children, optionsRestPath, populateDef | |
updates, | ||
originalOptions, | ||
saveOptions, | ||
savedOptions, | ||
savingOptions, | ||
unsetOption, | ||
updateOptions, | ||
readerModeWasOverridden, | ||
refetchPluginSuppression, | ||
setReaderModeWasOverridden, | ||
modifiedOptions, | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,29 +3,38 @@ | |
*/ | ||
import PropTypes from 'prop-types'; | ||
import classnames from 'classnames'; | ||
import { AMP_COMPATIBLE_PLUGINS_URL } from 'amp-settings'; // From WP inline script. | ||
|
||
/** | ||
* WordPress dependencies | ||
*/ | ||
import { __ } from '@wordpress/i18n'; | ||
import { useMemo } from '@wordpress/element'; | ||
import { createInterpolateElement, useMemo } from '@wordpress/element'; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fancy. |
||
import { ExternalLink } from '@wordpress/components'; | ||
|
||
/** | ||
* Internal dependencies | ||
*/ | ||
import { useNormalizedPluginsData } from '../plugins-context-provider/use-normalized-plugins-data'; | ||
import { IconLaptopPlug } from '../svg/laptop-plug'; | ||
import { isExternalUrl } from '../../common/helpers/is-external-url'; | ||
import { SiteScanSourcesList } from './site-scan-sources-list'; | ||
import { SiteScanResults } from './index'; | ||
|
||
/** | ||
* Render a list of plugins that cause AMP Incompatibility. | ||
* | ||
* @param {Object} props Component props. | ||
* @param {string} props.className Component class name. | ||
* @param {string[]} props.slugs List of plugins slugs. | ||
* @param {Object} props Component props. | ||
* @param {string} props.className Component class name. | ||
* @param {boolean} props.showHelpText Show additional help text above the issues list. | ||
* @param {string[]} props.slugs List of plugins slugs. | ||
*/ | ||
export function PluginsWithAmpIncompatibility( { className, slugs = [], ...props } ) { | ||
export function PluginsWithAmpIncompatibility( { | ||
className, | ||
showHelpText = false, | ||
slugs = [], | ||
...props | ||
} ) { | ||
const pluginsData = useNormalizedPluginsData(); | ||
const sources = useMemo( () => slugs?.map( ( slug ) => pluginsData?.[ slug ] ?? { | ||
slug, | ||
|
@@ -40,6 +49,26 @@ export function PluginsWithAmpIncompatibility( { className, slugs = [], ...props | |
className={ classnames( 'site-scan-results--plugins', className ) } | ||
{ ...props } | ||
> | ||
{ showHelpText && ( | ||
<p> | ||
{ createInterpolateElement( | ||
__( 'Because of plugin issue(s) detected, you may want to <a>review and suppress plugins</a>.', 'amp' ), | ||
{ | ||
// eslint-disable-next-line jsx-a11y/anchor-has-content -- Anchor has content defined in the translated string. | ||
a: <a href="#plugin-suppression" />, | ||
}, | ||
) } | ||
{ AMP_COMPATIBLE_PLUGINS_URL ? createInterpolateElement( | ||
` ${ __( 'You may also want to browse alternative <a>AMP compatible plugins</a>.', 'amp' ) }`, | ||
{ | ||
a: isExternalUrl( AMP_COMPATIBLE_PLUGINS_URL ) | ||
? <ExternalLink href={ AMP_COMPATIBLE_PLUGINS_URL } /> | ||
// eslint-disable-next-line jsx-a11y/anchor-has-content -- Anchor has content defined in the translated string. | ||
: <a href={ AMP_COMPATIBLE_PLUGINS_URL } />, | ||
}, | ||
) : '' } | ||
</p> | ||
) } | ||
<SiteScanSourcesList | ||
sources={ sources } | ||
inactiveSourceNotice={ __( 'This plugin has been deactivated since last site scan.' ) } | ||
|
@@ -51,5 +80,6 @@ export function PluginsWithAmpIncompatibility( { className, slugs = [], ...props | |
|
||
PluginsWithAmpIncompatibility.propTypes = { | ||
className: PropTypes.string, | ||
showHelpText: PropTypes.bool, | ||
slugs: PropTypes.arrayOf( PropTypes.string ).isRequired, | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
❤️