diff --git a/packages/js/product-editor/changelog/add-43707_show_prepublish_checks_section b/packages/js/product-editor/changelog/add-43707_show_prepublish_checks_section new file mode 100644 index 000000000000..6493aa26e357 --- /dev/null +++ b/packages/js/product-editor/changelog/add-43707_show_prepublish_checks_section @@ -0,0 +1,4 @@ +Significance: minor +Type: add + +Add Always show pre-publish checks checkbox #44595 diff --git a/packages/js/product-editor/src/components/header/header.tsx b/packages/js/product-editor/src/components/header/header.tsx index 22e57b2532ab..ec8f2d3b550b 100644 --- a/packages/js/product-editor/src/components/header/header.tsx +++ b/packages/js/product-editor/src/components/header/header.tsx @@ -27,6 +27,7 @@ import { PublishButton } from './publish-button'; import { LoadingState } from './loading-state'; import { Tabs } from '../tabs'; import { HEADER_PINNED_ITEMS_SCOPE, TRACKS_SOURCE } from '../../constants'; +import { useShowPrepublishChecks } from '../../hooks/use-show-prepublish-checks'; export type HeaderProps = { onTabSelect: ( tabId: string | null ) => void; @@ -64,6 +65,8 @@ export function Header( { 'name' ); + const { showPrepublishChecks } = useShowPrepublishChecks(); + const sidebarWidth = useAdminSidebarWidth(); useEffect( () => { @@ -158,7 +161,10 @@ export function Header( { productStatus={ lastPersistedProduct?.status } /> - + diff --git a/packages/js/product-editor/src/components/prepublish-panel/prepublish-panel.tsx b/packages/js/product-editor/src/components/prepublish-panel/prepublish-panel.tsx index e2d0ab88dc67..254950b9a64f 100644 --- a/packages/js/product-editor/src/components/prepublish-panel/prepublish-panel.tsx +++ b/packages/js/product-editor/src/components/prepublish-panel/prepublish-panel.tsx @@ -17,6 +17,7 @@ import { store as productEditorUiStore } from '../../store/product-editor-ui'; import { TRACKS_SOURCE } from '../../constants'; import { VisibilitySection } from './visibility-section'; import { ScheduleSection } from './schedule-section'; +import { ShowPrepublishChecksSection } from './show-prepublish-checks-section'; export function PrepublishPanel( { productType = 'product', @@ -63,9 +64,13 @@ export function PrepublishPanel( {

{ title }

{ description } - - - +
+ + +
+
+ +
); } diff --git a/packages/js/product-editor/src/components/prepublish-panel/show-prepublish-checks-section/index.ts b/packages/js/product-editor/src/components/prepublish-panel/show-prepublish-checks-section/index.ts new file mode 100644 index 000000000000..22473663964f --- /dev/null +++ b/packages/js/product-editor/src/components/prepublish-panel/show-prepublish-checks-section/index.ts @@ -0,0 +1 @@ +export * from './show-prepublish-checks-section'; diff --git a/packages/js/product-editor/src/components/prepublish-panel/show-prepublish-checks-section/show-prepublish-checks-section.tsx b/packages/js/product-editor/src/components/prepublish-panel/show-prepublish-checks-section/show-prepublish-checks-section.tsx new file mode 100644 index 000000000000..6606e420da4b --- /dev/null +++ b/packages/js/product-editor/src/components/prepublish-panel/show-prepublish-checks-section/show-prepublish-checks-section.tsx @@ -0,0 +1,39 @@ +/** + * External dependencies + */ +import { createElement } from '@wordpress/element'; +import { __ } from '@wordpress/i18n'; +import { recordEvent } from '@woocommerce/tracks'; +import { CheckboxControl } from '@wordpress/components'; + +/** + * Internal dependencies + */ +import { TRACKS_SOURCE } from '../../../constants'; +import { useShowPrepublishChecks } from '../../../hooks/use-show-prepublish-checks'; + +export function ShowPrepublishChecksSection() { + const { isResolving, showPrepublishChecks, togglePrepublishChecks } = + useShowPrepublishChecks(); + + if ( isResolving ) { + return null; + } + + return ( +
+ { + togglePrepublishChecks(); + recordEvent( 'product_prepublish_panel', { + source: TRACKS_SOURCE, + action: 'toggle_prepublish_checks', + value: selected, + } ); + } } + /> +
+ ); +} diff --git a/packages/js/product-editor/src/components/prepublish-panel/style.scss b/packages/js/product-editor/src/components/prepublish-panel/style.scss index ee01a544c766..8229395b0f52 100644 --- a/packages/js/product-editor/src/components/prepublish-panel/style.scss +++ b/packages/js/product-editor/src/components/prepublish-panel/style.scss @@ -1,11 +1,19 @@ @import './visibility-section/style.scss'; .woocommerce-product-publish-panel { + bottom: 0; + right: 0; + top: $admin-bar-height-big; + overflow: auto; + position: fixed; background: $white; transform: translateX(+100%); animation: product-publish-panel__slide-in-animation 0.1s forwards; + width: 100%; @include break-medium() { + top: $admin-bar-height; + width: $sidebar-width - $border-width; @include reduce-motion("animation"); body.is-fullscreen-mode & { @@ -43,6 +51,24 @@ margin: 8px 0; } } + + &__content { + // Ensure the pre-publish panel accounts for the header and footer height. + min-height: calc( 100% - #{ $header-height + 200px } ); + } + + &__footer { + padding: 0 $grid-unit-20; + left: 0; + width: 100%; + min-height: $gap-largest + $gap-large; + display: flex; + align-items: center; + + .components-base-control__field { + margin: 0; + } + } } @keyframes product-publish-panel__slide-in-animation { diff --git a/packages/js/product-editor/src/constants.ts b/packages/js/product-editor/src/constants.ts index b4ebb8791801..5ef835e3c201 100644 --- a/packages/js/product-editor/src/constants.ts +++ b/packages/js/product-editor/src/constants.ts @@ -5,6 +5,8 @@ export const NEW_PRODUCT_MANAGEMENT_ENABLED_OPTION_NAME = 'woocommerce_new_product_management_enabled'; export const SINGLE_VARIATION_NOTICE_DISMISSED_OPTION = 'woocommerce_single_variation_notice_dismissed'; +export const SHOW_PREPUBLISH_CHECKS_ENABLED_OPTION_NAME = + 'woocommerce_show_prepublish_checks_enabled'; export const NUMBERS_AND_ALLOWED_CHARS = '[^-0-9%s1%s2]'; export const NUMBERS_AND_DECIMAL_SEPARATOR = '[^-\\d\\%s]+'; diff --git a/packages/js/product-editor/src/hooks/use-show-prepublish-checks.ts b/packages/js/product-editor/src/hooks/use-show-prepublish-checks.ts new file mode 100644 index 000000000000..913b134e8303 --- /dev/null +++ b/packages/js/product-editor/src/hooks/use-show-prepublish-checks.ts @@ -0,0 +1,45 @@ +/** + * External dependencies + */ +import { useSelect, useDispatch } from '@wordpress/data'; +import { OPTIONS_STORE_NAME } from '@woocommerce/data'; + +/** + * Internal dependencies + */ +import { SHOW_PREPUBLISH_CHECKS_ENABLED_OPTION_NAME } from '../constants'; + +export function useShowPrepublishChecks() { + const { updateOptions } = useDispatch( OPTIONS_STORE_NAME ); + + const { isResolving, showPrepublishChecks } = useSelect( ( select ) => { + const { getOption, hasFinishedResolution } = + select( OPTIONS_STORE_NAME ); + + const showPrepublishChecksOption = + getOption( SHOW_PREPUBLISH_CHECKS_ENABLED_OPTION_NAME ) || 'yes'; + + const resolving = ! hasFinishedResolution( 'getOption', [ + SHOW_PREPUBLISH_CHECKS_ENABLED_OPTION_NAME, + ] ); + + return { + isResolving: resolving, + showPrepublishChecks: showPrepublishChecksOption === 'yes', + }; + }, [] ); + + const togglePrepublishChecks = () => { + updateOptions( { + [ SHOW_PREPUBLISH_CHECKS_ENABLED_OPTION_NAME ]: showPrepublishChecks + ? 'no' + : 'yes', + } ); + }; + + return { + isResolving, + showPrepublishChecks, + togglePrepublishChecks, + }; +} diff --git a/plugins/woocommerce-admin/client/customize-store/intro/index.tsx b/plugins/woocommerce-admin/client/customize-store/intro/index.tsx index f4c4fe47a09b..76a58266cdb9 100644 --- a/plugins/woocommerce-admin/client/customize-store/intro/index.tsx +++ b/plugins/woocommerce-admin/client/customize-store/intro/index.tsx @@ -238,6 +238,7 @@ export const Intro: CustomizeStoreComponent = ( { sendEvent, context } ) => { total_palettes={ theme.total_palettes } link_url={ theme?.link_url } is_active={ theme.is_active } + price={ theme.price } onClick={ () => { if ( theme.is_active ) { sendEvent( { diff --git a/plugins/woocommerce-admin/client/customize-store/intro/intro.scss b/plugins/woocommerce-admin/client/customize-store/intro/intro.scss index ff011543d552..e19ce2deb014 100644 --- a/plugins/woocommerce-admin/client/customize-store/intro/intro.scss +++ b/plugins/woocommerce-admin/client/customize-store/intro/intro.scss @@ -278,28 +278,37 @@ } } + .theme-card__free { + color: #1e1e1e; + font-size: 14px; + font-style: normal; + font-weight: 400; + line-height: 16px; /* 114.286% */ + } + .theme-card__active { - border-radius: 100px; background: rgba(56, 88, 233, 0.2); + color: #1d35b4; + } + + .theme-card__paid { + background: #dcdcde; + color: #2c3338; + } + + .theme-card__active, + .theme-card__paid { + border-radius: 100px; padding: 5px 10px; justify-content: flex-end; align-items: center; gap: 10px; - color: #1d35b4; font-size: 12px; font-style: normal; font-weight: 500; line-height: 20px; /* 166.667% */ margin-right: 10px; } - - .theme-card__free { - color: #1e1e1e; - font-size: 14px; - font-style: normal; - font-weight: 400; - line-height: 16px; /* 114.286% */ - } } } diff --git a/plugins/woocommerce-admin/client/customize-store/intro/theme-card.tsx b/plugins/woocommerce-admin/client/customize-store/intro/theme-card.tsx index f60c76fe76e9..e4935cbab83c 100644 --- a/plugins/woocommerce-admin/client/customize-store/intro/theme-card.tsx +++ b/plugins/woocommerce-admin/client/customize-store/intro/theme-card.tsx @@ -19,6 +19,7 @@ export const ThemeCard = ( { total_palettes = 0, link_url = '', is_active = false, + price = 'Free', onClick, }: TypeThemeCard & { onClick: () => void; @@ -49,7 +50,12 @@ export const ThemeCard = ( { { __( 'Active theme', 'woocommerce' ) } ) } - Free + { price !== 'Free' && ( + + { __( 'Paid', 'woocommerce' ) } + + ) } + { price } ); diff --git a/plugins/woocommerce-admin/client/customize-store/intro/types.ts b/plugins/woocommerce-admin/client/customize-store/intro/types.ts index e06b7354efb5..4df236b5181f 100644 --- a/plugins/woocommerce-admin/client/customize-store/intro/types.ts +++ b/plugins/woocommerce-admin/client/customize-store/intro/types.ts @@ -13,6 +13,7 @@ export type ThemeCard = { thumbnail_url: string; is_active: boolean; link_url?: string; + price: string; color_palettes: ColorPalette[]; total_palettes: number; }; diff --git a/plugins/woocommerce/changelog/44811-update-blocks-build-message b/plugins/woocommerce/changelog/44811-update-blocks-build-message new file mode 100644 index 000000000000..028667a1478f --- /dev/null +++ b/plugins/woocommerce/changelog/44811-update-blocks-build-message @@ -0,0 +1,4 @@ +Significance: patch +Type: dev +Comment: Update Blocks build message when asset files are not built. + diff --git a/plugins/woocommerce/changelog/44822-44609-cys-on-core-update-the-themes-list-on-the-intro-screen b/plugins/woocommerce/changelog/44822-44609-cys-on-core-update-the-themes-list-on-the-intro-screen new file mode 100644 index 000000000000..f9ef0d96327c --- /dev/null +++ b/plugins/woocommerce/changelog/44822-44609-cys-on-core-update-the-themes-list-on-the-intro-screen @@ -0,0 +1,4 @@ +Significance: minor +Type: update + +Update the themes list on the Customize Your Store intro screen. \ No newline at end of file diff --git a/plugins/woocommerce/changelog/45096-fix-remove-wp-nightly-test b/plugins/woocommerce/changelog/45096-fix-remove-wp-nightly-test new file mode 100644 index 000000000000..7631d8889179 --- /dev/null +++ b/plugins/woocommerce/changelog/45096-fix-remove-wp-nightly-test @@ -0,0 +1,4 @@ +Significance: patch +Type: tweak +Comment: This PR affects tooling and a change entry is not required. + diff --git a/plugins/woocommerce/changelog/add-43707_show_prepublish_checks_section b/plugins/woocommerce/changelog/add-43707_show_prepublish_checks_section new file mode 100644 index 000000000000..6493aa26e357 --- /dev/null +++ b/plugins/woocommerce/changelog/add-43707_show_prepublish_checks_section @@ -0,0 +1,4 @@ +Significance: minor +Type: add + +Add Always show pre-publish checks checkbox #44595 diff --git a/plugins/woocommerce/changelog/e2e-add-merchant-transform-cart-test b/plugins/woocommerce/changelog/e2e-add-merchant-transform-cart-test new file mode 100644 index 000000000000..31fb8dd2e795 --- /dev/null +++ b/plugins/woocommerce/changelog/e2e-add-merchant-transform-cart-test @@ -0,0 +1,4 @@ +Significance: patch +Type: dev + +E2E tests: add test for transforming classic cart to cart block diff --git a/plugins/woocommerce/changelog/fix-43289-woocommerce-css-watch b/plugins/woocommerce/changelog/fix-43289-woocommerce-css-watch new file mode 100644 index 000000000000..a71f1386a696 --- /dev/null +++ b/plugins/woocommerce/changelog/fix-43289-woocommerce-css-watch @@ -0,0 +1,4 @@ +Significance: patch +Type: dev + +WooCommerce build watching will now also detect CSS file changes. diff --git a/plugins/woocommerce/package.json b/plugins/woocommerce/package.json index 00214fab3071..ce83d3873b22 100644 --- a/plugins/woocommerce/package.json +++ b/plugins/woocommerce/package.json @@ -124,25 +124,6 @@ } } }, - { - "name": "PHP WP: nightly", - "command": "test:php:env", - "changes": [ - "composer.lock", - "includes/**/*.php", - "patterns/**/*.php", - "src/**/*.php", - "templates/**/*.php", - "tests/php/**/*.php", - "tests/unit-tests/**/*.php" - ], - "testEnv": { - "start": "env:test", - "config": { - "wpVersion": "nightly" - } - } - }, { "name": "PHP WP: latest - 1", "command": "test:php:env", @@ -256,6 +237,7 @@ "node_modules/@woocommerce/classic-assets/build", "node_modules/@woocommerce/admin-library/build" ], + "ext": "js,css,php,json", "ignoreRoot": [] }, "wireit": { diff --git a/plugins/woocommerce/src/Admin/API/OnboardingThemes.php b/plugins/woocommerce/src/Admin/API/OnboardingThemes.php index 63564da6d8d3..023ce2ad2a49 100644 --- a/plugins/woocommerce/src/Admin/API/OnboardingThemes.php +++ b/plugins/woocommerce/src/Admin/API/OnboardingThemes.php @@ -249,9 +249,69 @@ public function get_recommended_themes( $request ) { ); } + $core_themes = array( + array( + 'name' => 'Twenty Twenty-Four', + 'price' => 'Free', + 'color_palettes' => array( + array( + 'title' => 'Black and white', + 'primary' => '#FEFBF3', + 'secondary' => '#7F7E7A', + ), + array( + 'title' => 'Brown Sugar', + 'primary' => '#EFEBE0', + 'secondary' => '#AC6239', + ), + array( + 'title' => 'Midnight', + 'primary' => '#161514', + 'secondary' => '#AFADA7', + ), + array( + 'title' => 'Olive', + 'primary' => '#FEFBF3', + 'secondary' => '#7F7E7A', + ), + ), + 'total_palettes' => 0, + 'slug' => 'twentytwentyfour', + 'thumbnail_url' => 'https://i0.wp.com/themes.svn.wordpress.org/twentytwentyfour/1.0/screenshot.png', + 'link_url' => 'https://wordpress.org/themes/twentytwentyfour/', + ), + array( + 'name' => 'Highline', + 'price' => '$79/year', + 'color_palettes' => array(), + 'total_palettes' => 0, + 'slug' => 'highline', + 'thumbnail_url' => 'https://woo.com/wp-content/uploads/2023/12/Featured-image-538x403-1.png', + 'link_url' => 'https://woo.com/products/highline/', + ), + array( + 'name' => 'Luminate', + 'price' => '$79/year', + 'color_palettes' => array(), + 'total_palettes' => 0, + 'slug' => 'luminate', + 'thumbnail_url' => 'https://woo.com/wp-content/uploads/2022/07/Featured-image-538x403-2.png', + 'link_url' => 'https://woo.com/products/luminate/', + ), + array( + 'name' => 'Nokul', + 'price' => '$79/year', + 'color_palettes' => array(), + 'total_palettes' => 0, + 'slug' => 'nokul', + 'thumbnail_url' => 'https://woo.com/wp-content/uploads/2022/11/Product-logo.jpg', + 'link_url' => 'https://woo.com/products/nokul/', + ), + ); + // To be implemented: 1. Fetch themes from the marketplace API. 2. Convert prices to the requested currency. // These are Dotcom themes. - $themes = array( + $default_themes = array( array( 'name' => 'Tsubaki', 'price' => 'Free', @@ -269,7 +329,6 @@ public function get_recommended_themes( $request ) { 'slug' => 'tazza', 'thumbnail_url' => 'https://i0.wp.com/s2.wp.com/wp-content/themes/premium/tazza/screenshot.png', 'link_url' => 'https://wordpress.com/theme/tazza/', - 'total_palettes' => 0, ), array( 'name' => 'Amulet', @@ -333,6 +392,9 @@ public function get_recommended_themes( $request ) { ), ); + $ai_connection_enabled = get_option( 'woocommerce_blocks_allow_ai_connection' ); + $themes = $ai_connection_enabled ? $default_themes : $core_themes; + // To be implemented: Filter themes based on industry. if ( $industry ) { $filtered_themes = array_filter( diff --git a/plugins/woocommerce/src/Admin/API/Options.php b/plugins/woocommerce/src/Admin/API/Options.php index 7f08274cfc1a..1cd59c9bfc04 100644 --- a/plugins/woocommerce/src/Admin/API/Options.php +++ b/plugins/woocommerce/src/Admin/API/Options.php @@ -205,6 +205,7 @@ public static function get_default_option_permissions() { 'woocommerce_product_tour_modal_hidden', 'woocommerce_block_product_tour_shown', 'woocommerce_revenue_report_date_tour_shown', + 'woocommerce_show_prepublish_checks_enabled', 'woocommerce_date_type', 'date_format', 'time_format', diff --git a/plugins/woocommerce/src/Blocks/Domain/Bootstrap.php b/plugins/woocommerce/src/Blocks/Domain/Bootstrap.php index 8dd059564f8c..221f3e880545 100644 --- a/plugins/woocommerce/src/Blocks/Domain/Bootstrap.php +++ b/plugins/woocommerce/src/Blocks/Domain/Bootstrap.php @@ -190,11 +190,12 @@ protected function add_build_notice() { function() { echo '

'; printf( - /* translators: %1$s is the install command, %2$s is the build command, %3$s is the watch command. */ - esc_html__( 'WooCommerce Blocks development mode requires files to be built. From the plugin directory, run %1$s to install dependencies, %2$s to build the files or %3$s to build the files and watch for changes.', 'woocommerce' ), - 'npm install', - 'npm run build', - 'npm start' + /* translators: %1$s is the node install command, %2$s is the install command, %3$s is the build command, %4$s is the watch command. */ + esc_html__( 'WooCommerce Blocks development mode requires files to be built. From the root directory, run %1$s to ensure your node version is aligned, run %2$s to install dependencies, %3$s to build the files or %4$s to build the files and watch for changes.', 'woocommerce' ), + 'nvm use', + 'pnpm install', + 'pnpm --filter="@woocommerce/plugin-woocommerce" build', + 'pnpm --filter="@woocommerce/plugin-woocommerce" watch:build' ); echo '

'; } diff --git a/plugins/woocommerce/tests/e2e-pw/tests/merchant/create-cart-block.spec.js b/plugins/woocommerce/tests/e2e-pw/tests/merchant/create-cart-block.spec.js new file mode 100644 index 000000000000..22dea643514e --- /dev/null +++ b/plugins/woocommerce/tests/e2e-pw/tests/merchant/create-cart-block.spec.js @@ -0,0 +1,72 @@ +const { test, expect } = require( '@playwright/test' ); +const { closeWelcomeModal } = require( '../../utils/editor' ); + +const transformedCartBlockTitle = `Transformed Cart ${ Date.now() }`; +const transformedCartBlockSlug = transformedCartBlockTitle + .replace( / /gi, '-' ) + .toLowerCase(); + +test.describe( 'Transform Classic Cart To Cart Block', () => { + test.use( { storageState: process.env.ADMINSTATE } ); + + test( 'can transform classic cart to cart block', async ( { page } ) => { + // go to create a new page + await page.goto( 'wp-admin/post-new.php?post_type=page' ); + + await closeWelcomeModal( { page } ); + + // fill page title + await page + .getByRole( 'textbox', { name: 'Add title' } ) + .fill( transformedCartBlockTitle ); + + // add classic cart block + await page.getByRole( 'textbox', { name: 'Add title' } ).click(); + await page.getByLabel( 'Add block' ).click(); + await page + .getByPlaceholder( 'Search', { exact: true } ) + .fill( 'classic cart' ); + await page + .getByRole( 'option' ) + .filter( { hasText: 'Classic Cart' } ) + .click(); + + // transform into blocks + await expect( + page.locator( + '.wp-block-woocommerce-classic-shortcode__placeholder-copy' + ) + ).toBeVisible(); + await page + .getByRole( 'button' ) + .filter( { hasText: 'Transform into blocks' } ) + .click(); + await expect( page.getByLabel( 'Dismiss this notice' ) ).toContainText( + 'Classic shortcode transformed to blocks.' + ); + + // save and publish the page + await page + .getByRole( 'button', { name: 'Publish', exact: true } ) + .click(); + await page + .getByRole( 'region', { name: 'Editor publish' } ) + .getByRole( 'button', { name: 'Publish', exact: true } ) + .click(); + await expect( + page.getByText( `${ transformedCartBlockTitle } is now live.` ) + ).toBeVisible(); + + // go to frontend to verify transformed cart block + await page.goto( transformedCartBlockSlug ); + await expect( + page.getByRole( 'heading', { name: transformedCartBlockTitle } ) + ).toBeVisible(); + await expect( + page.getByText( 'Your cart is currently empty!' ) + ).toBeVisible(); + await expect( + page.getByRole( 'link', { name: 'Browse store' } ) + ).toBeVisible(); + } ); +} ); diff --git a/tools/monorepo-utils/.gitignore b/tools/monorepo-utils/.gitignore index e35482ae32a1..56cfb022381a 100644 --- a/tools/monorepo-utils/.gitignore +++ b/tools/monorepo-utils/.gitignore @@ -1,4 +1,5 @@ # We are going to include the bundled version of the tool so that we don't need # to build the tool in order to use it. This saves a lot of time in CI since # we don't need to install any dependencies. -!dist +!dist/index.js +!dist/index.js.LICENSE.txt