diff --git a/.phpstorm.meta.php b/.phpstorm.meta.php index f9d561ad97f..ce6a4e617ba 100644 --- a/.phpstorm.meta.php +++ b/.phpstorm.meta.php @@ -50,6 +50,7 @@ 'reader_theme_loader' => \AmpProject\AmpWP\ReaderThemeLoader::class, 'reader_theme_support_features' => \AmpProject\AmpWP\ReaderThemeSupportFeatures::class, 'rest.options_controller' => \AmpProject\AmpWP\OptionsRESTController::class, + 'rest.scannable_urls_controller' => \AmpProject\AmpWP\Validation\ScannableURLsRestController::class, 'rest.validation_counts_controller' => \AmpProject\AmpWP\Validation\ValidationCountsRestController::class, 'sandboxing' => \AmpProject\AmpWP\Sandboxing::class, 'save_post_validation_event' => \AmpProject\AmpWP\Validation\SavePostValidationEvent::class, diff --git a/assets/src/common/helpers/get-plugin-slug-from-file.js b/assets/src/common/helpers/get-plugin-slug-from-file.js new file mode 100644 index 00000000000..bfced3fde52 --- /dev/null +++ b/assets/src/common/helpers/get-plugin-slug-from-file.js @@ -0,0 +1,16 @@ +/** + * Get plugin slug from file path. + * + * If the plugin file is in a directory, then the slug is just the directory name. Otherwise, if the file is not + * inside of a directory and is just a single-file plugin, then the slug is the filename of the PHP file. + * + * If the file path contains a file extension, it will be stripped as well. + * + * See the corresponding PHP logic in `\AmpProject\AmpWP\get_plugin_slug_from_file()`. + * + * @param {string} path Plugin file path. + * @return {string} Plugin slug. + */ +export function getPluginSlugFromFile( path = '' ) { + return path.replace( /\/.*$/, '' ).replace( /\.php$/, '' ); +} diff --git a/assets/src/common/helpers/test/get-plugin-slug-from-file.js b/assets/src/common/helpers/test/get-plugin-slug-from-file.js new file mode 100644 index 00000000000..4d4813448fe --- /dev/null +++ b/assets/src/common/helpers/test/get-plugin-slug-from-file.js @@ -0,0 +1,13 @@ +/** + * Internal dependencies + */ +import { getPluginSlugFromFile } from '../get-plugin-slug-from-file'; + +describe( 'getPluginSlugFromFile', () => { + it( 'should return correct plugin slug', () => { + expect( getPluginSlugFromFile( 'foo' ) ).toBe( 'foo' ); + expect( getPluginSlugFromFile( 'foo.php' ) ).toBe( 'foo' ); + expect( getPluginSlugFromFile( 'foo/bar' ) ).toBe( 'foo' ); + expect( getPluginSlugFromFile( 'foo/baz.php' ) ).toBe( 'foo' ); + } ); +} ); diff --git a/assets/src/components/amp-drawer/index.js b/assets/src/components/amp-drawer/index.js index 7b4118969cc..6b81cd6f71b 100644 --- a/assets/src/components/amp-drawer/index.js +++ b/assets/src/components/amp-drawer/index.js @@ -28,11 +28,22 @@ export const HANDLE_TYPE_RIGHT = 'right'; * @param {any} props.heading Content for the drawer heading. * @param {string} props.id A unique ID for the component. * @param {boolean} props.initialOpen Whether the drawer should be initially open. + * @param {Object} props.labelExtra Optional. Extra content to display on the right side of the option label. * @param {boolean} props.selected Whether to apply the selectable components selected CSS class. * @param {string} props.hiddenTitle A title to go with the button that expands the drawer. * @param {string} props.handleType Display style for the drawer handle. Either 'full-width' or 'right'. */ -export function AMPDrawer( { children = null, className, heading, handleType = HANDLE_TYPE_FULL_WIDTH, id, initialOpen = false, selected = false, hiddenTitle } ) { +export function AMPDrawer( { + children = null, + className, + heading, + handleType = HANDLE_TYPE_FULL_WIDTH, + id, + initialOpen = false, + labelExtra = null, + selected = false, + hiddenTitle, +} ) { const [ opened, setOpened ] = useState( initialOpen ); const [ resetStatus, setResetStatus ] = useState( null ); @@ -94,9 +105,16 @@ export function AMPDrawer( { children = null, className, heading, handleType = H selected={ selected } > { handleType === HANDLE_TYPE_RIGHT && ( -
+ { title }
+
+ { callToAction } +
+ ) } +
+ { slug }
+
+ ) }
+ { status === 'active' ? (
+ <>
+ { author && (
+
+ { sprintf(
+ /* translators: %s is an author name. */
+ __( 'by %s', 'amp' ),
+ author,
+ ) }
+
+ ) }
+ { version && (
+
+ { sprintf(
+ /* translators: %s is a version number. */
+ __( 'Version %s', 'amp' ),
+ version,
+ ) }
+
+ ) }
+ >
+ ) : (
+ - - { ' ' } - - { __( 'Learn more.', 'amp' ) } - -
{ children } + { Array.isArray( details ) && ( ++ + { detailsUrl && ( + <> + { ' ' } + + { __( 'Learn more.', 'amp' ) } + + > + ) } +
+ ) }+ { __( 'Site scan was unsuccessful.', 'amp' ) } +
++ { __( 'You can trigger the site scan again on the AMP Settings page after completing the Wizard.', 'amp' ) } +
+ > + ) } + /> + ); + } + + if ( isDelayedCompleted ) { + return ( ++ { __( 'Site scan is checking if there are AMP compatibility issues with your active theme and plugins. We’ll then recommend how to use the AMP plugin.', 'amp' ) } +
++ { isCompleted + ? __( 'Scan complete', 'amp' ) + : sprintf( + // translators: 1: currently scanned URL index; 2: scannable URLs count; 3: scanned page type. + __( 'Scanning %1$d/%2$d URLs: Checking %3$s…', 'amp' ), + currentlyScannedUrlIndex + 1, + scannableUrls.length, + scannableUrls[ currentlyScannedUrlIndex ]?.label, + ) + } +
+ > + ) } + /> + ); +} + +/** + * Site Scan panel. + * + * @param {Object} props Component props. + * @param {any} props.children Component children. + * @param {any} props.headerContent Component header content. + * @param {string} props.title Component title. + */ +function SiteScanPanel( { + children, + headerContent, + title, +} ) { + return ( ++ { title } +
+AMP experience with different modes and availability of AMP components in the ecosystem.', 'amp' ), + 'https://amp-wp.org/documentation/getting-started/template-modes/', + 'https://amp-wp.org/ecosystem/', + ), + } } /> +